diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92738f6..6f33d16 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,10 +8,16 @@ on: jobs: scalafmt: - name: Formatting + name: Check formatting runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Setup Java (temurin@8) + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 8 + cache: sbt - uses: coursier/cache-action@v6 - name: Check formatting run: sbt -v "scalafmtSbtCheck" "scalafmtCheckAll" @@ -20,20 +26,53 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - name: Setup Java (temurin@8) + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 8 + cache: sbt - uses: coursier/cache-action@v6 - run: sbt docs/run plugin-tests: name: Compiler and SBT plugin tests - runs-on: ubuntu-latest + strategy: + matrix: + os: [ ubuntu-latest ] + scala: + - 2.12.13 + - 2.12.14 + - 2.12.15 + - 2.12.16 + - 2.12.17 + - 2.12.18 + - 2.12.19 + - 2.13.5 + - 2.13.6 + - 2.13.7 + - 2.13.8 + - 2.13.9 + - 2.13.10 + - 2.13.11 + - 2.13.12 + - 2.13.13 + - 2.13.14 + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 with: submodules: true + - name: Setup Java (temurin@8) + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 8 + cache: sbt - uses: coursier/cache-action@v6 - name: Compiler plugin tests - run: sbt -v "+plugin/test" + run: sbt -v '++ ${{ matrix.scala }}' plugin/test - name: SBT plugin tests - run: sbt -v "profilingSbtPlugin/scripted" + run: sbt -v '++ ${{ matrix.scala }}' profilingSbtPlugin/scripted intergration-tests: name: Integration tests runs-on: ubuntu-latest @@ -41,6 +80,12 @@ jobs: - uses: actions/checkout@v4 with: submodules: true + - name: Setup Java (temurin@8) + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: 8 + cache: sbt - uses: coursier/cache-action@v6 - name: Running tests run: sbt -v "showScalaInstances" "integrations/testOnly integration better-files wartremover" diff --git a/.scalafmt.conf b/.scalafmt.conf index ea2121a..dfc7bed 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version = "3.8.0" +version = "3.8.2" runner.dialect = Scala213 maxColumn = 100 docstrings.style = Asterisk diff --git a/build.sbt b/build.sbt index fbca99a..5a6490b 100644 --- a/build.sbt +++ b/build.sbt @@ -24,8 +24,20 @@ lazy val root = project (integrations / watchSources).value ) -val bin212 = Seq("2.12.18", "2.12.17", "2.12.16", "2.12.15", "2.12.14", "2.12.13") -val bin213 = Seq("2.13.12", "2.13.11", "2.13.10", "2.13.9", "2.13.8", "2.13.7", "2.13.6", "2.13.5") +val bin212 = Seq("2.12.19", "2.12.18", "2.12.17", "2.12.16", "2.12.15", "2.12.14", "2.12.13") +val bin213 = + Seq( + "2.13.14", + "2.13.13", + "2.13.12", + "2.13.11", + "2.13.10", + "2.13.9", + "2.13.8", + "2.13.7", + "2.13.6", + "2.13.5" + ) // Copied from // https://github.com/scalameta/scalameta/blob/370e304b0d10db1dd65fc79a5abc1f39004aeffd/build.sbt#L724-L737 @@ -36,7 +48,7 @@ lazy val fullCrossVersionSettings = Seq( // NOTE: SBT 1.x provides cross-version support for Scala sources // (https://www.scala-sbt.org/1.x/docs/Cross-Build.html#Scala-version+specific+source+directory). // Unfortunately, it only includes directories like "scala_2.12" or "scala_2.13", - // not "scala_2.12.18" or "scala_2.13.12" that we need. + // not "scala_2.12.19" or "scala_2.13.14" that we need. // That's why we have to work around here. val base = (Compile / sourceDirectory).value val versionDir = scalaVersion.value.replaceAll("-.*", "") @@ -57,7 +69,7 @@ lazy val profiledb = project libraryDependencies += "com.thesamet.scalapb" %% "scalapb-runtime" % scalapbVersion % "protobuf", Compile / managedSourceDirectories += target.value / "protobuf-generated", - Compile / PB.targets in Compile := Seq( + Compile / PB.targets := Seq( scalapb.gen() -> (target.value / "protobuf-generated") ), buildInfoPackage := "scalac.profiling.internal.build", @@ -77,7 +89,7 @@ lazy val plugin = project fullCrossVersionSettings, name := "scalac-profiling", libraryDependencies ++= List( - "com.lihaoyi" %% "pprint" % "0.8.1", + "com.lihaoyi" %% "pprint" % "0.9.0", scalaOrganization.value % "scala-compiler" % scalaVersion.value ), libraryDependencies ++= List( diff --git a/docs/user-guide/installation.md b/docs/user-guide/installation.md index e15d22b..88726a7 100644 --- a/docs/user-guide/installation.md +++ b/docs/user-guide/installation.md @@ -47,13 +47,14 @@ lazy val myproject = project There are several compiler plugin options to enable to enrichment of analysis capabilities. All the following options are prepended by the `-P:scalac-profiling:`. -| Name | Description | -|:-----------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `generate-global-flamegraph` | Creates a global flamegraph of implicit searches for all compilation units. Use the `-P:scalac-profiling:sourceroot` option to manage the root directory, otherwise, a working directory (defined by the `user.dir` property) will be picked. | -| `generate-macro-flamegraph` | Generate a flamegraph for macro expansions. The flamegraph for implicit searches is enabled by default. | -| `generate-profiledb` | Generate profiledb. | -| `print-failed-implicit-macro-candidates` | Print trees of all failed implicit searches that triggered a macro expansion. | -| `print-search-result` | Print the result retrieved by an implicit search. Example: `-P:scalac-profiling:print-search-result:$MACRO_ID`. | -| `show-concrete-implicit-tparams` | Use more concrete type parameters in the implicit search flamegraph. Note that it may change the shape of the flamegraph. | -| `show-profiles` | Show implicit searches and macro expansions by type and call-site. | -| `sourceroot` | Tell the plugin what is the source directory of the project. Example: `-P:scalac-profiling:sourceroot:$PROJECT_BASE_DIR`. | +| Name | Description | +|:-----------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `generate-global-flamegraph` | Creates a global flamegraph of implicit searches for all compilation units. Use the `-P:scalac-profiling:cross-target` option to manage the target directory for the resulting flamegraph file, otherwise, the SBT target directory will be picked. | +| `generate-macro-flamegraph` | Generate a flamegraph for macro expansions. The flamegraph for implicit searches is enabled by default. | +| `generate-profiledb` | Generate profiledb. | +| `print-failed-implicit-macro-candidates` | Print trees of all failed implicit searches that triggered a macro expansion. | +| `print-search-result` | Print the result retrieved by an implicit search. Example: `-P:scalac-profiling:print-search-result:$MACRO_ID`. | +| `show-concrete-implicit-tparams` | Use more concrete type parameters in the implicit search flamegraph. Note that it may change the shape of the flamegraph. | +| `show-profiles` | Show implicit searches and macro expansions by type and call-site. | +| `sourceroot` | Tell the plugin what is the source directory of the project. Example: `-P:scalac-profiling:sourceroot:$PROJECT_BASE_DIR`. | +| `cross-target` | Tell the plugin what is the cross target directory of the project. Example: `-P:scalac-profiling:cross-target:$PROJECT_TARGET`. | diff --git a/plugin/src/main/scala-2.12.19/tools/SettingsOps.scala b/plugin/src/main/scala-2.12.19/tools/SettingsOps.scala new file mode 100644 index 0000000..aadf96d --- /dev/null +++ b/plugin/src/main/scala-2.12.19/tools/SettingsOps.scala @@ -0,0 +1,8 @@ +package ch.epfl.scala.profilers.tools + +import scala.tools.nsc.Global + +object SettingsOps { + def areStatisticsEnabled(g: Global): Boolean = + g.settings.areStatisticsEnabled +} diff --git a/plugin/src/main/scala-2.13.13/ch/epfl/scala/profilers/tools/SettingsOps.scala b/plugin/src/main/scala-2.13.13/ch/epfl/scala/profilers/tools/SettingsOps.scala new file mode 100644 index 0000000..aadf96d --- /dev/null +++ b/plugin/src/main/scala-2.13.13/ch/epfl/scala/profilers/tools/SettingsOps.scala @@ -0,0 +1,8 @@ +package ch.epfl.scala.profilers.tools + +import scala.tools.nsc.Global + +object SettingsOps { + def areStatisticsEnabled(g: Global): Boolean = + g.settings.areStatisticsEnabled +} diff --git a/plugin/src/main/scala-2.13.14/ch/epfl/scala/profilers/tools/SettingsOps.scala b/plugin/src/main/scala-2.13.14/ch/epfl/scala/profilers/tools/SettingsOps.scala new file mode 100644 index 0000000..aadf96d --- /dev/null +++ b/plugin/src/main/scala-2.13.14/ch/epfl/scala/profilers/tools/SettingsOps.scala @@ -0,0 +1,8 @@ +package ch.epfl.scala.profilers.tools + +import scala.tools.nsc.Global + +object SettingsOps { + def areStatisticsEnabled(g: Global): Boolean = + g.settings.areStatisticsEnabled +} diff --git a/plugin/src/main/scala/ch/epfl/scala/PluginConfig.scala b/plugin/src/main/scala/ch/epfl/scala/PluginConfig.scala index cc0d824..d17511a 100644 --- a/plugin/src/main/scala/ch/epfl/scala/PluginConfig.scala +++ b/plugin/src/main/scala/ch/epfl/scala/PluginConfig.scala @@ -6,6 +6,7 @@ final case class PluginConfig( showProfiles: Boolean, generateDb: Boolean, sourceRoot: AbsolutePath, + crossTarget: AbsolutePath, printSearchIds: Set[Int], generateMacroFlamegraph: Boolean, generateGlobalFlamegraph: Boolean, diff --git a/plugin/src/main/scala/ch/epfl/scala/ProfilingPlugin.scala b/plugin/src/main/scala/ch/epfl/scala/ProfilingPlugin.scala index e2ef32d..17ef20f 100644 --- a/plugin/src/main/scala/ch/epfl/scala/ProfilingPlugin.scala +++ b/plugin/src/main/scala/ch/epfl/scala/ProfilingPlugin.scala @@ -32,6 +32,7 @@ class ProfilingPlugin(val global: Global) extends Plugin { self => private final lazy val ShowProfiles = "show-profiles" private final lazy val SourceRoot = "sourceroot" + private final lazy val CrossTarget = "cross-target" private final lazy val PrintSearchResult = "print-search-result" private final lazy val GenerateMacroFlamegraph = "generate-macro-flamegraph" private final lazy val GenerateGlobalFlamegraph = "generate-global-flamegraph" @@ -40,6 +41,7 @@ class ProfilingPlugin(val global: Global) extends Plugin { self => private final lazy val ShowConcreteImplicitTparams = "show-concrete-implicit-tparams" private final lazy val PrintSearchRegex = s"$PrintSearchResult:(.*)".r private final lazy val SourceRootRegex = s"$SourceRoot:(.*)".r + private final lazy val CrossTargetRegex = s"$CrossTarget:(.*)".r def findOption(name: String, pattern: Regex): Option[String] = { super.options.find(_.startsWith(name)).flatMap { @@ -55,18 +57,39 @@ class ProfilingPlugin(val global: Global) extends Plugin { self => } } - private final lazy val config = PluginConfig( - showProfiles = super.options.contains(ShowProfiles), - generateDb = super.options.contains(GenerateProfileDb), - sourceRoot = findOption(SourceRoot, SourceRootRegex) + private final lazy val config = { + val sourceRoot = findOption(SourceRoot, SourceRootRegex) .map(AbsolutePath.apply) - .getOrElse(AbsolutePath.workingDirectory), - printSearchIds = findSearchIds(findOption(PrintSearchResult, PrintSearchRegex)), - generateMacroFlamegraph = super.options.contains(GenerateMacroFlamegraph), - generateGlobalFlamegraph = super.options.contains(GenerateGlobalFlamegraph), - printFailedMacroImplicits = super.options.contains(PrintFailedMacroImplicits), - concreteTypeParamsInImplicits = super.options.contains(ShowConcreteImplicitTparams) - ) + .getOrElse(AbsolutePath.workingDirectory) + val crossTarget = findOption(CrossTarget, CrossTargetRegex) + .map(AbsolutePath.apply) + .getOrElse { + val scalaDir = + if (ScalaSettingsOps.isScala212) + "scala-2.12" + else if (ScalaSettingsOps.isScala213) + "scala-2.13" + else + sys.error( + s"Currently, only Scala 2.12 and 2.13 are supported, " + + s"but [${global.settings.source.value}] has been spotted" + ) + + sourceRoot.resolve(RelativePath(s"target/$scalaDir")) + } + + PluginConfig( + showProfiles = super.options.contains(ShowProfiles), + generateDb = super.options.contains(GenerateProfileDb), + sourceRoot = sourceRoot, + crossTarget = crossTarget, + printSearchIds = findSearchIds(findOption(PrintSearchResult, PrintSearchRegex)), + generateMacroFlamegraph = super.options.contains(GenerateMacroFlamegraph), + generateGlobalFlamegraph = super.options.contains(GenerateGlobalFlamegraph), + printFailedMacroImplicits = super.options.contains(PrintFailedMacroImplicits), + concreteTypeParamsInImplicits = super.options.contains(ShowConcreteImplicitTparams) + ) + } private lazy val logger = new Logger(global) @@ -77,7 +100,7 @@ class ProfilingPlugin(val global: Global) extends Plugin { self => // format: off override val optionsHelp: Option[String] = Some( s""" - |-P:$name:${pad20(GenerateGlobalFlamegraph)} Creates a global flamegraph of implicit searches for all compilation units. Use the `-P:$name:$SourceRoot` option to manage the root directory, otherwise, a working directory (defined by the `user.dir` property) will be picked. + |-P:$name:${pad20(GenerateGlobalFlamegraph)} Creates a global flamegraph of implicit searches for all compilation units. Use the `-P:$name:$CrossTarget` option to manage the target directory for the resulting flamegraph file, otherwise, the SBT target directory will be picked. |-P:$name:${pad20(GenerateMacroFlamegraph)} Generates a flamegraph for macro expansions. The flamegraph for implicit searches is enabled by default. |-P:$name:${pad20(GenerateProfileDb)} Generates profiledb (will be removed later). |-P:$name:${pad20(PrintFailedMacroImplicits)} Prints trees of all failed implicit searches that triggered a macro expansion. @@ -85,7 +108,8 @@ class ProfilingPlugin(val global: Global) extends Plugin { self => |-P:$name:${pad20(ShowConcreteImplicitTparams)} Shows types in flamegraphs of implicits with concrete type params. |-P:$name:${pad20(ShowProfiles)} Logs profile information for every call-site. |-P:$name:${pad20(SourceRoot)}:_ Sets the source root for this project. - """.stripMargin + |-P:$name:${pad20(CrossTarget)}:_ Sets the cross target for this project. + """.stripMargin ) // format: on lazy val implementation = new ProfilingImpl(ProfilingPlugin.this.global, config, logger) @@ -111,20 +135,9 @@ class ProfilingPlugin(val global: Global) extends Plugin { self => private def reportStatistics(graphsPath: AbsolutePath): Unit = { val globalDir = if (config.generateGlobalFlamegraph) { - val scalaDir = - if (ScalaSettingsOps.isScala212) - "scala-2.12" - else if (ScalaSettingsOps.isScala213) - "scala-2.13" - else - sys.error( - s"Currently, only Scala 2.12 and 2.13 are supported, " + - s"but [${global.settings.source.value}] has been spotted" - ) - val globalDir = ProfileDbPath.toGraphsProfilePath( - config.sourceRoot.resolve(RelativePath(s"target/$scalaDir/classes")) + config.crossTarget.resolve(RelativePath("classes")) ) Some(globalDir) diff --git a/project/BuildPlugin.scala b/project/BuildPlugin.scala index fb9448f..2f240af 100644 --- a/project/BuildPlugin.scala +++ b/project/BuildPlugin.scala @@ -75,7 +75,7 @@ object BuildKeys { val targetDir = (Compile / Keys.target).value val compiledClassesDir = targetDir / s"scala-$scalaBinVersion/classes" val testClassesDir = targetDir / s"scala-$scalaBinVersion/test-classes" - val libraryJar = Keys.scalaInstance.value.libraryJar.getAbsolutePath + val libraryJar = Keys.scalaInstance.value.libraryJars.head.getAbsolutePath val deps = (Compile / Keys.libraryDependencies).value.mkString(":") val classpath = s"$compiledClassesDir:$testClassesDir:$libraryJar:$deps" val resourceDir = (Compile / Keys.resourceManaged).value @@ -109,7 +109,7 @@ object BuildKeys { //////////////////////////////////////////////////////////////////////////////// def inProject(ref: Reference)(ss: Seq[Setting[_]]): Seq[Setting[_]] = - sbt.inScope(sbt.ThisScope.in(project = ref))(ss) + sbt.inScope(sbt.ThisScope.copy(project = Select(ref)))(ss) def inProjectRefs(refs: Seq[Reference])(ss: Setting[_]*): Seq[Setting[_]] = refs.flatMap(inProject(_)(ss)) @@ -258,7 +258,7 @@ object BuildImplementation { Keys.organization := "ch.epfl.scala", Keys.resolvers += Resolver.jcenterRepo, Keys.updateOptions := Keys.updateOptions.value.withCachedResolution(true), - Keys.scalaVersion := "2.12.18", + Keys.scalaVersion := "2.12.19", sbt.nio.Keys.watchTriggeredMessage := Watch.clearScreenOnTrigger, BuildKeys.enableStatistics := true, BuildKeys.showScalaInstances := BuildDefaults.showScalaInstances.value diff --git a/project/build.properties b/project/build.properties index abbbce5..04267b1 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.9.8 +sbt.version=1.9.9 diff --git a/project/build.sbt b/project/build.sbt index 87e9ea2..a980f79 100644 --- a/project/build.sbt +++ b/project/build.sbt @@ -3,16 +3,16 @@ lazy val root = project .settings( addSbtPlugin("com.github.sbt" % "sbt-git" % "2.0.1"), addSbtPlugin("com.thesamet" % "sbt-protoc" % "1.0.7"), - addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.5"), + addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.2.0"), addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.5.12"), addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2"), addSbtPlugin("com.github.sbt" % "sbt-ghpages" % "0.8.0"), - addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.5.2"), - addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0"), + addSbtPlugin("org.scalameta" % "sbt-mdoc" % "2.5.4"), + addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.12.0"), // // Let's add our sbt plugin to the sbt too ;) // unmanagedSourceDirectories in Compile ++= { // val pluginMainDir = baseDirectory.value.getParentFile / "sbt-plugin" / "src" / "main" // List(pluginMainDir / "scala", pluginMainDir / s"scala-sbt-${Keys.sbtBinaryVersion.value}") // }, - libraryDependencies += "com.thesamet.scalapb" %% "compilerplugin" % "0.11.15" + libraryDependencies += "com.thesamet.scalapb" %% "compilerplugin" % "0.11.17" ) diff --git a/project/project/build.properties b/project/project/build.properties index abbbce5..04267b1 100644 --- a/project/project/build.properties +++ b/project/project/build.properties @@ -1 +1 @@ -sbt.version=1.9.8 +sbt.version=1.9.9