@@ -4,6 +4,7 @@ import java.nio.file.FileSystems
44
55import scala .collection .mutable
66import scala .util .control .NoStackTrace
7+ import scala .util .matching .Regex
78
89import bloop .cli .CommonOptions
910import bloop .config .Config
@@ -21,7 +22,8 @@ final case class SourceGenerator(
2122 sourcesGlobs : List [SourcesGlobs ],
2223 outputDirectory : AbsolutePath ,
2324 unmangedInputs : List [AbsolutePath ],
24- command : List [String ]
25+ command : List [String ],
26+ commandTemplate : Option [List [String ]]
2527) {
2628
2729 /**
@@ -66,7 +68,14 @@ final case class SourceGenerator(
6668 logger : Logger ,
6769 opts : CommonOptions
6870 ): Task [SourceGenerator .Run ] = {
69- val cmd = (command :+ outputDirectory.syntax) ++ inputs.keys.map(_.syntax)
71+ val cmd =
72+ buildCommand(
73+ outputDirectory.syntax,
74+ inputs.keys.map(_.syntax).toSeq,
75+ unmangedInputs.keys.map(_.syntax).toSeq,
76+ logger
77+ )
78+
7079 logger.debug { () =>
7180 cmd.mkString(s " Running source generator: ${System .lineSeparator()}$$ " , " " , " " )
7281 }
@@ -79,6 +88,28 @@ final case class SourceGenerator(
7988 }
8089 }
8190
91+ private def buildCommand (
92+ outputDirectory : String ,
93+ inputs : Seq [String ],
94+ unmangedInputs : Seq [String ],
95+ logger : Logger
96+ ): Seq [String ] =
97+ commandTemplate match {
98+ case None =>
99+ (command :+ outputDirectory) ++ inputs
100+ case Some (cmd) =>
101+ val substs = Map [String , Seq [String ]](
102+ SourceGenerator .Arg .Output -> Seq (outputDirectory),
103+ SourceGenerator .Arg .Inputs -> inputs,
104+ SourceGenerator .Arg .UnmanagedInputs -> unmangedInputs
105+ ).withDefault { name =>
106+ logger.warn(s " Couldn't find substitution for ` $name`, consider escaping it with a $$ . " )
107+ Seq .empty[String ]
108+ }
109+
110+ cmd.flatMap(SourceGenerator .Arg .substitute(substs)(_))
111+ }
112+
82113 private def needsUpdate (previous : SourceGenerator .Run ): Task [SourceGenerator .Changes ] = {
83114 previous match {
84115 case SourceGenerator .NoRun =>
@@ -141,6 +172,33 @@ object SourceGenerator {
141172 unamanagedInputs : Map [AbsolutePath , Int ]
142173 ) extends Changes
143174
175+ private object Arg {
176+ private val Single : Regex = """ ((?:\$)+)\{([a-zA-Z]+)\}""" .r
177+ private val Anywhere : Regex = Single .unanchored
178+
179+ // TODO: make these configurable in some way?
180+ val Inputs = " inputs"
181+ val Output = " output"
182+ val UnmanagedInputs = " unmanaged"
183+
184+ def substitute (substs : Map [String , Seq [String ]])(s : String ): Seq [String ] =
185+ s match {
186+ case Single (" $" , name) => substs(name)
187+ case _ =>
188+ Seq (Anywhere .replaceAllIn(s, m => Regex .quoteReplacement(replace(m, substs))))
189+ }
190+
191+ private def replace (mtch : Regex .Match , substs : Map [String , Seq [String ]]): String = {
192+ val dollars = mtch.group(1 ).size
193+ val name = mtch.group(2 )
194+ val value =
195+ if (dollars % 2 == 0 ) s " { $name} "
196+ else substs(name).mkString(" " )
197+
198+ s " ${" $" * (dollars / 2 )}$value"
199+ }
200+ }
201+
144202 def fromConfig (cwd : AbsolutePath , generator : Config .SourceGenerator ): SourceGenerator = {
145203 val sourcesGlobs = generator.sourcesGlobs.map {
146204 case Config .SourcesGlobs (directory, depth, includes, excludes) =>
@@ -159,7 +217,9 @@ object SourceGenerator {
159217 sourcesGlobs,
160218 AbsolutePath (generator.outputDirectory),
161219 generator.unmanagedInputs.map(AbsolutePath .apply),
162- generator.command
220+ generator.command,
221+ // TODO: change to `generator.commandTemplate` after PR to bloop-config is merged
222+ None
163223 )
164224 }
165225
0 commit comments