T4 template processing task added

This commit is contained in:
Mike 2019-02-11 22:42:31 -08:00
parent 3952da3a60
commit 4e5f94d27a
7 changed files with 215 additions and 3 deletions

View File

@ -21,7 +21,9 @@ Start new FNA projects with Nez quickly and easily with handy setup scripts, a v
2. Run `./getFNA.sh` (macOS) to download the latest Nez, FNA and fnalibs to the directory. You can run this script again if you want to update either FNA or the fnalibs at a later point. Nez is setup as a submodule so you can update it in the normal fashion.
3. Open the newly-created and named `PROJECT_NAME_YOU_CHOSE.code-workspace` file (or open the project folder in Visual Studio Code or the top-level sln in Visual Studio)
That's it! Now you're ready to build and run the base project! When developing raw content (files not processed by the Pipeline tool) should be placed in the `Content` folder and anything that needs processing should go in the `CompiledContent` folder and added to the Pipeline tool.
That's it! Now you're ready to build and run the base project. When developing raw content (files not processed by the Pipeline tool) should be placed in the `Content` folder and anything that needs processing should go in the `CompiledContent` folder and added to the Pipeline tool.
If you want to see the output of `Debug.*` calls in the VS Code Debug Console, you have to install a listener by adding this somewhere in your code (Game1 is a good spot): `System.Diagnostics.Debug.Listeners.Add(new System.Diagnostics.TextWriterTraceListener(System.Console.Out));`
## Build Tasks ##
@ -33,6 +35,7 @@ That's it! Now you're ready to build and run the base project! When developing r
- **Build Content:** Runs good old MGCB.exe on the Content.mgcb file
- **Force Build Content:** Force builds the content (MGCB.exe -r)
- **Open Pipeline Tool:** Opens the MonoGame Pipeline tool
- **Process T4 Templates:** Processes any T4 templates found in the `T4Templates` folder. Note that the install script will attempt to install the t4 command line program which requires the `dotnet` command line program to be installed. The install command it will run is `dotnet tool install -g dotnet-t4`.
## License and Credits ##

View File

@ -94,6 +94,10 @@ if [[ $shouldDownloadLibs =~ ^[Yy]$ ]]; then
fi
# install t4 engine
dotnet tool install -g dotnet-t4
# Rename project
read -p "Enter the project name to use for your folder and csproj file or 'exit' to quit: " newProjectName
if [[ $newProjectName = 'exit' ]]; then

16
project_name/.vscode/processT4Templates.sh vendored Executable file
View File

@ -0,0 +1,16 @@
#!/bin/bash
# buildEffects
# Compiles all .fx files found in the project's Content directory.
# Intended for usage with VS Code Build Tasks tooling.
# You may need to change the path to fxc.exe depending on your installation.
printf "Starting T4 processing...\n"
for file in `find ./T4Templates/** -name "*.tt"` ;
do
# Build the template
t4 -r=System.dll -r=mscorlib.dll -r=netstandard.dll -r=System.IO.FileSystem.dll -r=System.Linq.dll -r=System.Text.RegularExpressions.dll `dirname $file`/`basename $file` -o `dirname $file`/Output/`basename $file .tt`.cs
echo "Built `basename $file`"
done

View File

@ -120,5 +120,13 @@
"command": "export MONOGAME_PIPELINE_PROJECT=${workspaceFolder}/CompiledContent/Content.mgcb && /Applications/Pipeline.app/Contents/MacOS/Pipeline",
"problemMatcher": "$msCompile"
},
{
"label": "Process T4 Templates",
"type": "shell",
"command": "${workspaceFolder}/.vscode/processT4Templates.sh",
"group": "build",
"problemMatcher": "$msCompile",
},
]
}

View File

@ -0,0 +1,141 @@
<#@ template language="C#" hostSpecific="true" #>
<#@ assembly name="System.Core.dll" #>
<#@ assembly name="System.dll" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Globalization" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<# var sourceFolders = new string[] { "CompiledContent/bin/DesktopGL", "Content" }; #>
namespace Nez
{
class Content
{
<#
// loop through all of our sourceFolders
foreach( var sourceFolder in sourceFolders )
{
var directories = Directory.GetDirectories( sourceFolder );
// loop through all the directories in our sourceFolder
foreach( var dir in directories )
{
var dirName = new DirectoryInfo( dir ).Name.ToLower();
if( dirName == "bin" || dirName == "obj" || dirName == "content" )
continue;
// dont delve into directories that don't contan xnb files
var xnbFiles = Directory.GetFiles( dir, "*.xnb", SearchOption.AllDirectories );
if( xnbFiles.Length == 0 )
continue;
// start off the recursive directory printing
printDirectoryClass( dir, 2, sourceFolder );
}
// handle any files in the root sourceFolder
printContentFiles( sourceFolder, 2, sourceFolder );
}
#>
}
}
<#+
// C# reserved keywords
private System.Collections.Generic.List<string> keywords = new System.Collections.Generic.List<string>
{
"abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const", "continue", "decimal", "default", "delegate",
"do", "double", "else", "enum", "event", "explicit", "extern", "false", "finally", "fixed", "float", "for", "foreach", "goto", "if", "implicit", "in",
"int", "interface", "internal", "is", "lock", "long", "namespace", "new", "null", "object", "operator", "out", "override", "params", "private",
"protected", "public", "readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc", "static", "string", "struct", "switch", "this",
"throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using", "virtual", "void", "volatile", "while"
};
// recursively creates a class for each directory
void printDirectoryClass( string dir, int depth, string sourceFolder )
{
var dirInfo = new DirectoryInfo( dir );
var firstIndent = new string( ' ', depth * 5 );
var className = generateClassName( dirInfo.Name, true );
WriteLine( "{0}public static class {1}\n{2}{{", firstIndent, className, firstIndent );
// handle subdirectories
foreach( var subdir in Directory.GetDirectories( dir ) )
printDirectoryClass( subdir, depth + 1, sourceFolder );
// handle files
printContentFiles( dir, depth + 1, sourceFolder );
WriteLine( "{0}}}\n", firstIndent );
}
// prints a const string for each file in the directory
void printContentFiles( string dir, int depth, string sourceFolder )
{
var firstIndent = new string( '\t', depth );
var files = Directory.EnumerateFiles( dir )
.Where( s => s.EndsWith( ".xnb" ) || s.EndsWith( ".png" ) || s.EndsWith( ".ogg" ) || s.EndsWith( ".wav" ) || s.EndsWith( ".fxb" ) );
foreach( var file in files )
{
// clear out all of the path up to the sourceFolder so we get just the relative path to the Content folder
var finalPath = file.Substring( file.IndexOf( sourceFolder ) + sourceFolder.Length );
var fileInfo = new FileInfo( file );
var fileName = fileInfo.Name.Replace( ".xnb", string.Empty );
var className = generateClassName( fileName, false );
if( finalPath[0] == '/' || finalPath[0] == '\\' )
finalPath = finalPath.Substring( 1 );
// if file name is reserved insert a leading '@'
if( keywords.Contains( className ) )
className = className.Insert( 0, "@" );
WriteLine( "{0}public const string {1} = @\"{2}\";", firstIndent, className, finalPath );
}
}
string stripInvalidPathChars( string input )
{
var invalidChars = Path.GetInvalidPathChars();
return new string( input.Where( m => !invalidChars.Contains( m ) ).ToArray<char>() );
}
string stripInvalidFilenameChars( string input )
{
var invalidChars = Path.GetInvalidFileNameChars();
return new string( input.Where( m => !invalidChars.Contains( m ) ).ToArray<char>() );
}
// attempts to generate a proper path name
string generateClassName( string className, bool uppercaseFirstChar )
{
// handle upper or lower casing the first char in the className
if( uppercaseFirstChar && char.IsLower( className[0] ) )
className = char.ToUpper( className[0] ) + className.Substring( 1 );
else if( !uppercaseFirstChar && char.IsUpper( className[0] ) )
className = char.ToLower( className[0] ) + className.Substring( 1 );
// remove invalid characters
var regex = new Regex( @"[^\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}\p{Nl}\p{Mn}\p{Mc}\p{Cf}\p{Pc}\p{Lm}]" );
className = regex.Replace( className, "" );
// class name doesn't begin with a letter, insert an underscore
if( !char.IsLetter( className, 0 ) )
className = className.Insert( 0, "_" );
return className.Replace( " ", string.Empty );
}
#>

View File

@ -0,0 +1,40 @@
<#@ template language="C#" hostSpecific="true" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Collections.Generic" #>
<# var enumTypes = new string[] { "System.StringSplitOptions" }; #>
using System.Collections.Generic;
namespace Nez
{
<#
// loop through all of our enumTypes and generate a comparer
foreach( var enumType in enumTypes )
{
var classPrefix = char.ToUpper( enumType[0] ) + enumType.Substring( 1 ).Replace( ".", string.Empty );
if( enumType.IndexOf( "." ) > 0 )
{
var enumTypeSansNS = enumType.Substring( enumType.IndexOf( "." ) + 1 );
classPrefix = char.ToUpper( enumTypeSansNS[0] ) + enumTypeSansNS.Substring( 1 ).Replace( ".", string.Empty );
}
WriteLine( "\tpublic class {0}Comparer : IEqualityComparer<{1}>", classPrefix, enumType );
WriteLine( "\t{" );
WriteLine( "\t\tstatic public readonly {0}Comparer default{0}Comparer = new {0}Comparer();\n", classPrefix );
WriteLine( "\t\tpublic bool Equals( {0} x, {0} y )", enumType );
WriteLine( "\t\t{" );
WriteLine( "\t\t\treturn x == y;" );
WriteLine( "\t\t}\n\n" );
WriteLine( "\t\tpublic int GetHashCode( {0} b )", enumType );
WriteLine( "\t\t{" );
WriteLine( "\t\t\treturn (int)b;" );
WriteLine( "\t\t}" );
WriteLine( "\t}\n\n" );
}
#>
}

View File

@ -6,13 +6,13 @@
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<PlatformTarget>AnyCPU</PlatformTarget>
<AssemblyName>project_name</AssemblyName>
<MonoGamePlatform>DesktopGL</MonoGamePlatform>
<MonoGamePlatform>DesktopGL</MonoGamePlatform>
</PropertyGroup>
<!-- Reference FNA project -->
<ItemGroup>
<ProjectReference Include="../FNA/FNA.csproj" />
<ProjectReference Include="../Nez.FNA/Nez.FNA/Nez.FNA.csproj" />
<ProjectReference Include="../Nez.FNA/Nez.FNA/Nez.FNA.csproj" />
</ItemGroup>
<!-- Include the Content directory (except for .fx files, since we use .fxb at runtime) -->