Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

T4ParameterValues Support #11

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions T4.Build/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Ignore any immediate `tools` directories, as they are likely someone building and testing locally from another project
/tools/*
58 changes: 53 additions & 5 deletions T4.Build/BuildTemplateGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.CodeDom.Compiler;
using System.IO;
using System.Text;
using Mono.TextTemplating;

namespace T4.Build
Expand All @@ -11,13 +12,20 @@ class BuildTemplateGenerator : TemplateGenerator
List<string> includedFiles = new List<string>();
string lockPath;
ParsedTemplate parsedTemplate;
readonly Func<string, string> preprocessor = null;

public BuildTemplateGenerator(string template)
{
TemplateFile = template;
Engine.UseInProcessCompiler();
}

public BuildTemplateGenerator(string template, Func<string, string> preprocessor)
: this(template)
{
this.preprocessor = preprocessor;
}

public string[] IncludedFiles
{
get
Expand Down Expand Up @@ -98,24 +106,64 @@ public bool ProcessTemplate(bool skipUpToDate, out bool skipped)
{
skipped = false;

if (string.IsNullOrEmpty(OutputFile))
var outputFile = OutputFile;
if (string.IsNullOrWhiteSpace(outputFile))
{
return false;
}

if (skipUpToDate)
{
var templateInfo = new FileInfo(TemplateFile);
var outputInfo = new FileInfo(OutputFile);
var outputInfo = new FileInfo(outputFile);
if (outputInfo.Exists
&& templateInfo.LastWriteTime.Ticks < outputInfo.LastWriteTime.Ticks
&& includedFiles.TrueForAll(x => (new FileInfo(x)).LastWriteTime.Ticks < outputInfo.LastWriteTime.Ticks)
&& templateInfo.LastWriteTime.Ticks < outputInfo.LastWriteTime.Ticks
&& includedFiles.TrueForAll(x => (new FileInfo(x)).LastWriteTime.Ticks < outputInfo.LastWriteTime.Ticks)
)
{
skipped = true;
return true;
}
}
return ProcessTemplate(TemplateFile, OutputFile);

string content;
try
{
content = File.ReadAllText(TemplateFile);
}
catch (IOException ex)
{
Errors.Clear();
AddError($"Could not read input file '{TemplateFile}':\n{ex}");
return false;
}

if (!(preprocessor is null))
{
content = preprocessor(content);
}
ProcessTemplate(TemplateFile, content, ref outputFile, out var output);

try
{
if (!Errors.HasErrors)
{
File.WriteAllText(outputFile, output, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
}
}
catch (IOException ex)
{
AddError($"Could not write output file '{outputFile}':\n{ex}");
}

return !Errors.HasErrors;
}

CompilerError AddError(string error)
{
var err = new CompilerError { ErrorText = error };
Errors.Add(err);
return err;
}
}
}
51 changes: 47 additions & 4 deletions T4.Build/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.CommandLine;
using System.CommandLine.Invocation;
using System.Diagnostics;
Expand Down Expand Up @@ -30,7 +31,12 @@ static int Main(string[] args)
transformCommand.Add(skipOption);
var parallelOption = new Option<bool>(new[] { "--parallel", "-p" }, "Transform templates in parallel");
transformCommand.Add(parallelOption);
transformCommand.Handler = CommandHandler.Create<FileInfo[], int, bool, bool>(Transform);
var variableOptions = new Option<string[]>(new[] { "--variable", "-v" }, "T4ParameterValues variable to replace within the template; syntax is \"Include=Value\"")
{
AllowMultipleArgumentsPerToken = false,
};
transformCommand.Add(variableOptions);
transformCommand.Handler = CommandHandler.Create<FileInfo[], int, bool, bool, string[]>(Transform);
rootCommand.Add(transformCommand);

var cleanCommand = new Command("clean", "List the output files");
Expand All @@ -41,7 +47,7 @@ static int Main(string[] args)
return rootCommand.InvokeAsync(args).Result;
}

static int Transform(FileInfo[] templates, int lockTimeout, bool skipUpToDate, bool parallel)
static int Transform(FileInfo[] templates, int lockTimeout, bool skipUpToDate, bool parallel, string[] variable)
{
using (var locker = new Lock($"Global\\T4.Build.transform.{ComputeHash(templates)}.lock", lockTimeout))
{
Expand All @@ -50,12 +56,13 @@ static int Transform(FileInfo[] templates, int lockTimeout, bool skipUpToDate, b
var didSomeWork = false;
var stopwatch = new Stopwatch();

var t4ParameterValues = ProcessT4ParameterValues(variable);
stopwatch.Start();
Parallel.ForEach(templates,
new ParallelOptions { MaxDegreeOfParallelism = parallel ? -1 : 1 },
t =>
{
var generator = new BuildTemplateGenerator(t.ToString());
var generator = new BuildTemplateGenerator(t.ToString(), templateContent => PreprocessTemplate(templateContent, t4ParameterValues));
try
{
bool skipped;
Expand Down Expand Up @@ -175,6 +182,42 @@ static void LogErrors(CompilerErrorCollection errors)

Console.ForegroundColor = oldColor;
}

static IReadOnlyDictionary<string, string> ProcessT4ParameterValues(string[] variableSets)
{
if (variableSets?.Any() != true)
{
return null;
}

var t4ParameterValues = new Dictionary<string, string>(variableSets.Length);
foreach (var variableSet in variableSets)
{
var pair = variableSet.Split('=', 2, StringSplitOptions.None);
t4ParameterValues.Add($"$({pair[0]})", pair[1]);
}

return t4ParameterValues;
}

static string PreprocessTemplate(string templateContent, IReadOnlyDictionary<string, string> t4ParameterValues)
{
if (string.IsNullOrWhiteSpace(templateContent) || t4ParameterValues?.Keys.Any() != true)
{
return templateContent;
}

var replacementBuilder = (StringBuilder)null;
foreach (var t4ParameterValuesPair in t4ParameterValues)
{
if (replacementBuilder is null && templateContent.IndexOf(t4ParameterValuesPair.Key) > -1)
{
replacementBuilder = new StringBuilder(templateContent);
}
replacementBuilder?.Replace(t4ParameterValuesPair.Key, t4ParameterValuesPair.Value);
}

return replacementBuilder is null ? templateContent : replacementBuilder.ToString();
}
}
}

18 changes: 16 additions & 2 deletions T4.Build/build/common.targets
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

<Target Name="TextTemplateCreateTransformList">
<ItemGroup>
<T4Build_TransformFiles Include="@(None)" Condition=" '%(None.Generator)' == 'TextTemplatingFileGenerator' " />
<T4Build_TransformFiles Include="@(Compile);@(None);@(Content);@(EmbeddedResource)" Condition=" '%(*.Generator)' == 'TextTemplatingFileGenerator' " />
<T4Build_TransformFiles Include="@(TextTemplateTransformFiles)" />
</ItemGroup>
<ItemGroup Condition=" '$(TextTemplateTransformAll)' == 'true' ">
Expand All @@ -31,10 +31,24 @@
</ItemGroup>
</Target>

<Target Name="TextTemplateTransform" DependsOnTargets="TextTemplateCreateTransformList">
<Target Name="TextTemplateCreateParameterValueList" Inputs="@(T4ParameterValues)" Outputs="%(T4ParameterValues.Identity)">
<PropertyGroup>
<T4Build_ParameterIdentity>%(T4ParameterValues.Identity)</T4Build_ParameterIdentity>
<T4Build_ParameterValue>%(T4ParameterValues.Value)</T4Build_ParameterValue>
</PropertyGroup>
<PropertyGroup>
<T4Build_ParameterValue>$(T4Build_ParameterValue.Replace('"', '""'))</T4Build_ParameterValue>
</PropertyGroup>
<ItemGroup>
<T4Build_ParameterValueList Include="--variable &quot;$(T4Build_ParameterIdentity)=$(T4Build_ParameterValue)&quot;" Condition="!$([System.String]::IsNullOrWhiteSpace('$(T4Build_ParameterIdentity)'))" />
</ItemGroup>
</Target>

<Target Name="TextTemplateTransform" DependsOnTargets="TextTemplateCreateTransformList;TextTemplateCreateParameterValueList">
<ItemGroup>
<T4Build_TransformOptions Include="--skip-up-to-date" Condition=" '$(TextTemplateTransformSkipUpToDate)' == 'true' " />
<T4Build_TransformOptions Include="--parallel" Condition=" '$(TextTemplateTransformParallel)' == 'true' " />
<T4Build_TransformOptions Include="@(T4Build_ParameterValueList, ' ')" />
</ItemGroup>
<Exec Command="$(T4Build_Command) transform @(T4Build_TransformOptions, ' ') @(T4Build_TransformFiles, ' ')" Condition=" '@(T4Build_TransformFiles)' != '' " ConsoleToMSBuild="true" StandardOutputImportance="normal">
<Output TaskParameter="ConsoleOutput" ItemName="T4Build_GeneratedFiles" />
Expand Down