Sundew.Generator is code generator aiming to provide an alternative or replacement for T4 templates.
Generators are simple C# console applications that execute themselves after being built.
The concept separates generators from models and output, meaning it is possible to have several generators operate on the same input (models) and have the result placed in one or more outputs (writers).
- Create a new project (Console application).
- Add the Sundew.Generator or Sundew.Generator.Code NuGet package.
- Implement a generator
- Setup
- Implement the main method, something like this:
public static Task Main()
{
return GeneratorFacade.RunAsync(new MySetupsFactory());
}
- Build (and it will run)
A generator can be added by implementing the generic Sundew.Generator.IGenerator<in TSetup, in TGeneratorSetup, in TTarget, in TModel, TRun, out TOutput>
interface.
The interface take quite a few generic parameters, which the developer can decide and requires the implementation of two methods.
-
Prepare must return a number IRuns, which will cause the Generate method to be called for each run. Typically prepare will return a single run.
-
Generate can output anything as long as the writer accepts the output, but typically this would be text, such as TextOutput.
Sundew.Generator does not support any form of preprocessed text templates like T4. For readability string interpolation combined with methods may be used.
public interface IGenerator<in TSetup, in TGeneratorSetup, in TTarget, in TModel, TRun, out TOutput> : IGenerator
where TTarget : ITarget
where TRun : IRun
{
IReadOnlyList<TRun> Prepare(TSetup setup, TGeneratorSetup generatorSetup, TTarget target, TModel model, string modelPath);
TOutput Generate(TSetup setup, TGeneratorSetup generatorSetup, TTarget target, TModel model, TRun run, long index);
}
The setup is used to configure models, generators and writers.
The Sundew.Generator.Setup
allows setting a ModelSetup, one of more GeneratorSetups and one or more WriterSetups.
A model setup allows to specify a ModelProvider and the ModelType with the Sundew.Generator.Input.ModelSetup
class. If neither a ModelProvider or ModelType is specified an Sundew.Generator.ModelProviders.EmptyModelProvider<object>
will be used.
If only a model type is specified Sundew.Generator.ModelProviders.JsonModelProvider<TModel>
will be used.
Sundew.Generator ships with three model providers out the box:
Sundew.Generator.ModelProviders.XmlModelProvider<TModel>
Sundew.Generator.ModelProviders.JsonModelProvider<TModel>
Sundew.Generator.ModelProviders.EmptyModelProvider<TModel>
XmlModelProvider
and JsonModelProvider
can use an Sundew.Generator.Input.FolderModelSetup
to specify folder and file pattern to search for model files.
EmptyModelProvider
provides a single model through its default constructor.
To implement a model provider use the Sundew.Generator.Input.IModelProvider<in TSetup, in TModelSetup, TModel>
interface.
The interface consists of a single async method that results a list of models. GetModelsAsync takes two parameters, the setup with which the generation was setup and a model setup.
public interface IModelProvider<in TSetup, in TModelSetup, TModel> : IModelProvider
where TModelSetup : class
where TModel : class
{
Task<IReadOnlyList<IModelInfo<TModel>>> GetModelsAsync(TSetup setup, TModelSetup modelSetup);
}
Sundew.Generator.GeneratorSetup
specify which generators to run and also allow to add generator specific settings.
Additionally, they may also specify any number of WriterSetups that will only be used for this particular generator and a boolean indicating whether the global WriterSetups should skip the generator (default false).
Writer setups help configure what happens with a generator's output. Sundew.Generator.Output.WriterSetup
provides a target (any string) and a writer.
There is also the Sundew.Generator.Output.FileWriterSetup
which can be used to specify a filename suffix and extension.
-
Sundew.Generator.Writers.TextFileWriter
writes text output of each run to a file and uses theWriterSetup.Target
to specify the folder to write to. -
Sundew.Generator.Code.ProjectTextFileWriter
is available in the Sundew.Generator.Code NuGet package and can be used with the providedSundew.Generator.Code.CodeSetup
to generate C# code. It expects theWriterSetup.Target
to be csproj or vbproj to determine the default namespace and root folder to store generated files.
Similar to generators and model providers, a custom writer can be added be implementing the Sundew.Generator.Output.IWriter<in TWriterSetup, TTarget, in TRun, in TOutput>
interface.
This is by far the most complex interface because it can manage the output of a generator.
-
GetTargetAsync runs in the very beginning of the generation process and provides a target for the generators prepare method. For example the
Sundew.Generator.Writers.TextFileWriter
builds the target folder path. - PrepareTargetAsync runs after the generation process.
- ApplyContentToTargetAsync runs for each of the generated outputs of the generator and allows to gather the output or persist it.
- CompleteTargetAsync runs once as a last step in the generation process and may be used to persist gathered output if necessary.
public interface IWriter<in TWriterSetup, TTarget, in TRun, in TOutput> : IWriter
where TWriterSetup : IWriterSetup
where TTarget : ITarget
where TRun : IRun
{
Task<TTarget> GetTargetAsync(TWriterSetup writerSetup);
Task PrepareTargetAsync(TTarget target, TWriterSetup writerSetup);
Task<string> ApplyContentToTargetAsync(TTarget target, TRun run, TWriterSetup writerSetup, TOutput output);
Task CompleteTargetAsync(ITargetCompletionTracker targetCompletionTracker);
}
With SDK-style projects files can be included with a wildcard an ItemGroup, while the WriterSetup can be configured to output the generated files to a .generated folder.
<ItemGroup>
<Compile Include=".generated\**\*.cs" />
</ItemGroup>
It might also be suitable to add the .generated folder to .gitignore to avoid checking in generated files.
.generated/
In Visual Studio the Build Dependencies feature can be used to ensure that the generator runs before any consuming project without creating a project reference.
Right click the solution in Solution Explorer - Build Dependencies - Build Dependencies to set up the build order.
The number times each generator runs is determined the following way:
#WriterSetups * #Models * #Runs
Sundew.Generator support using json files to setup up the generator.
Use the RunAsync overload on the GeneratorFacade and specify the directory and file pattern to search for setup files.
GeneratorFacade.RunAsync(string directory, string pattern, GeneratorOptions generatorOptions = null)
In a json setup file a "Type"
property may be specified with the fully qualified name to tell the deserializer, which type to use for Setup, GeneratorSetup, ModelSetup and WriterSetup.
MIT