bloop icon indicating copy to clipboard operation
bloop copied to clipboard

feat: Add command template to source generators

Open Mee-Tree opened this issue 9 months ago • 2 comments

This PR is an attempt to provide a more flexible configuration for source generator commands through templating.

It should be fully backward compatible with existing source generator configurations.

Implementation Details

  • Support for the following template variables: ${inputs}, ${output}, and ${unmanaged}
  • Variables as standalone arguments expand into multiple arguments (e.g. ${inputs} -> "test1.in" "test2.in")
  • Variables within arguments are joined (e.g. prefix${inputs}suffix -> "prefixtest1.in test2.insuffix")
  • Unknown variables expand to empty strings with a warning message, this behaviour allows for future extension of the variable set
  • Double dollar signs escape template variables (e.g. $${inputs} -> "${inputs}")

This should come in handy for https://github.com/VirtusLab/scala-cli/pull/3583 to eliminate the need for "wrappers" that only reorder the arguments.

Mee-Tree avatar Mar 23 '25 20:03 Mee-Tree

Thanks for the contribution! Do you have an example of how would a template look like?

tgodzik avatar Mar 25 '25 18:03 tgodzik

Thanks for the contribution! Do you have an example of how would a template look like?

Hi! For example, if we have a Config.SourceGenerator like this (some types are simplified for the sake of readability):

Configuration
SourceGenerator(
  sourcesGlobs = SourcesGlobs(
    directory = "/path/to/input", 
    walkDepth = None,
    includes = "glob:*.in",
    excludes = Nil
  ),
  outputDirectory = "/path/to/output",
  command = Nil,
  commandTemplate = List(
    "python3", 
    "-c", 
    "import sys; print(*sys.argv[1:], sep=\"\\n\")", 
    "${inputs}", 
    "$${inputs}", 
    "pref${inputs}suf", 
    "smth", 
    "${unmanaged}", 
    "pref${unmanaged}suf", 
    "${nonexistent}", 
    "$$${output}"
  )
)

The command would be expanded into:

Command
Seq(
 "python3", 
  "-c", 
  "import sys; print(*sys.argv[1:], sep=\"\\n\")", 
  "/path/to/input/test1.in", 
  "/path/to/input/test2.in", 
  "${inputs}", 
  "pref/path/to/input/test1.in /path/to/input/test2.insuf", 
  "smth", 
  "prefsuf", 
  "$/path/to/output"
)

Which, when run, would output (line numbers added for reference):

Output
1 | Warning: Couldn't find substitution for `nonexistent`, consider escaping it with a $.
2 | /path/to/input/test1.in
3 | /path/to/input/test2.in
4 | ${inputs}
5 | pref/path/to/input/test1.in /path/to/input/test2.insuf
6 | smth
7 | prefsuf
8 | $/path/to/output

Let's go through each line:

  • ${inputs} expands to three separate arguments (lines 2-3)
  • $${inputs} expands to ${inputs} (line 4)
  • pref${inputs}suf expands to one argument (line 5)
  • smth is not interpreted in any way and is passed as is (line 6)
  • ${unmanaged} expands to an empty list of arguments because it's Nil
  • pref${unmanaged}suf expands to one argument prefsuf (line 7)
  • ${nonexistent} expands to an empty list of arguments too, but also prints a warning because there's no such variable (line 1)
  • $$${output} expands to one argument containing $ at the beginning (line 8)

Please let me know if you have any further questions.

Mee-Tree avatar Mar 28 '25 23:03 Mee-Tree