diff --git a/frontend/src/main/scala/bloop/engine/SourceGenerator.scala b/frontend/src/main/scala/bloop/engine/SourceGenerator.scala index 83d7ebd255..3f835d1fd9 100644 --- a/frontend/src/main/scala/bloop/engine/SourceGenerator.scala +++ b/frontend/src/main/scala/bloop/engine/SourceGenerator.scala @@ -4,6 +4,7 @@ import java.nio.file.FileSystems import scala.collection.mutable import scala.util.control.NoStackTrace +import scala.util.matching.Regex import bloop.cli.CommonOptions import bloop.config.Config @@ -21,7 +22,8 @@ final case class SourceGenerator( sourcesGlobs: List[SourcesGlobs], outputDirectory: AbsolutePath, unmangedInputs: List[AbsolutePath], - command: List[String] + command: List[String], + commandTemplate: Option[List[String]] ) { /** @@ -66,7 +68,14 @@ final case class SourceGenerator( logger: Logger, opts: CommonOptions ): Task[SourceGenerator.Run] = { - val cmd = (command :+ outputDirectory.syntax) ++ inputs.keys.map(_.syntax) + val cmd = + buildCommand( + outputDirectory.syntax, + inputs.keys.map(_.syntax).toSeq, + unmangedInputs.keys.map(_.syntax).toSeq, + logger + ) + logger.debug { () => cmd.mkString(s"Running source generator:${System.lineSeparator()}$$ ", " ", "") } @@ -79,6 +88,28 @@ final case class SourceGenerator( } } + private def buildCommand( + outputDirectory: String, + inputs: Seq[String], + unmangedInputs: Seq[String], + logger: Logger + ): Seq[String] = + commandTemplate match { + case None => + (command :+ outputDirectory) ++ inputs + case Some(cmd) => + val substs = Map[String, Seq[String]]( + SourceGenerator.Arg.Output -> Seq(outputDirectory), + SourceGenerator.Arg.Inputs -> inputs, + SourceGenerator.Arg.UnmanagedInputs -> unmangedInputs + ).withDefault { name => + logger.warn(s"Couldn't find substitution for `$name`, consider escaping it with a $$.") + Seq.empty[String] + } + + cmd.flatMap(SourceGenerator.Arg.substitute(substs)(_)) + } + private def needsUpdate(previous: SourceGenerator.Run): Task[SourceGenerator.Changes] = { previous match { case SourceGenerator.NoRun => @@ -141,6 +172,33 @@ object SourceGenerator { unamanagedInputs: Map[AbsolutePath, Int] ) extends Changes + private object Arg { + private val Single: Regex = """((?:\$)+)\{([a-zA-Z]+)\}""".r + private val Anywhere: Regex = Single.unanchored + + // TODO: make these configurable in some way? + val Inputs = "inputs" + val Output = "output" + val UnmanagedInputs = "unmanaged" + + def substitute(substs: Map[String, Seq[String]])(s: String): Seq[String] = + s match { + case Single("$", name) => substs(name) + case _ => + Seq(Anywhere.replaceAllIn(s, m => Regex.quoteReplacement(replace(m, substs)))) + } + + private def replace(mtch: Regex.Match, substs: Map[String, Seq[String]]): String = { + val dollars = mtch.group(1).size + val name = mtch.group(2) + val value = + if (dollars % 2 == 0) s"{$name}" + else substs(name).mkString(" ") + + s"${"$" * (dollars / 2)}$value" + } + } + def fromConfig(cwd: AbsolutePath, generator: Config.SourceGenerator): SourceGenerator = { val sourcesGlobs = generator.sourcesGlobs.map { case Config.SourcesGlobs(directory, depth, includes, excludes) => @@ -159,7 +217,9 @@ object SourceGenerator { sourcesGlobs, AbsolutePath(generator.outputDirectory), generator.unmanagedInputs.map(AbsolutePath.apply), - generator.command + generator.command, + // TODO: change to `generator.commandTemplate` after PR to bloop-config is merged + None ) } diff --git a/frontend/src/test/scala/bloop/SourceGeneratorSpec.scala b/frontend/src/test/scala/bloop/SourceGeneratorSpec.scala index a0f5425228..b3cc0683ec 100644 --- a/frontend/src/test/scala/bloop/SourceGeneratorSpec.scala +++ b/frontend/src/test/scala/bloop/SourceGeneratorSpec.scala @@ -9,6 +9,7 @@ import bloop.logging.RecordingLogger import bloop.util.TestProject import bloop.util.TestUtil +// TODO: add tests after PR to bloop-config is merged object SourceGeneratorSpec extends bloop.testing.BaseSuite { val generator = TestUtil.generator diff --git a/project/Dependencies.scala b/project/Dependencies.scala index fca4946a26..dffaa18406 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -43,6 +43,7 @@ object Dependencies { val asmVersion = "9.7.1" val ztExecVersion = "1.12" val debugAdapterVersion = "4.2.4" + // TODO: update after PR to bloop-config is merged val bloopConfigVersion = "2.3.2" val semanticdbVersion = "4.9.9" val millVersion = "0.12.7"