mono / t4

T4 text templating engine
Other
393 stars 102 forks source link

Difficulties referencing custom assemblies #105

Open qubitz opened 3 years ago

qubitz commented 3 years ago

I'm struggling to reference a class that I have defined. I created a step-by-step example of what I'm trying to do:

mkdir t4test
cd t4test
dotnet new classlib
echo "namespace Program { public class Custom { public int Field; } }" > Custom.cs
dotnet build 
dotnet install --global dotnet-t4
echo "<#@ template language=`"C#`"#>" > Test.tt
echo "<#@ output extension=`".cs`"#>" >> Test.tt
echo "<# var t = typeof(Custom); #>" >> Test.tt  # Reference to Custom class
t4 -r ./bin/Debug/net5.0/t4test.dll ./Test.tt

Resulting output:

System.IO.FileLoadException: The given assembly name or codebase was invalid. (0x80131047)
   at System.Reflection.AssemblyName.nInit()
   at System.Reflection.AssemblyName..ctor(String assemblyName)
   at Mono.TextTemplating.TemplateGenerator.ResolveAssemblyReference(String assemblyReference) in D:\a\t4\t4\Mono.TextTemplating\Mono.TextTemplating\TemplateGenerator.cs:line 221
   at Mono.TextTemplating.TemplateGenerator.Microsoft.VisualStudio.TextTemplating.ITextTemplatingEngineHost.ResolveAssemblyReference(String assemblyReference) in D:\a\t4\t4\Mono.TextTemplating\Mono.TextTemplating\TemplateGenerator.cs:line 400
   at Mono.TextTemplating.TemplatingEngine.ProcessReferences(ITextTemplatingEngineHost host, ParsedTemplate pt, TemplateSettings settings) in D:\a\t4\t4\Mono.TextTemplating\Mono.TextTemplating\TemplatingEngine.cs:line 301
   at Mono.TextTemplating.TemplatingEngine.CompileTemplateInternal(ParsedTemplate pt, String content, ITextTemplatingEngineHost host, TemplateSettings settings) in D:\a\t4\t4\Mono.TextTemplating\Mono.TextTemplating\TemplatingEngine.cs:line 198
   at Mono.TextTemplating.TemplatingEngine.CompileTemplate(ParsedTemplate pt, String content, ITextTemplatingEngineHost host, TemplateSettings settings) in D:\a\t4\t4\Mono.TextTemplating\Mono.TextTemplating\TemplatingEngine.cs:line 174
   at Mono.TextTemplating.ToolTemplateGenerator.ProcessTemplate(ParsedTemplate pt, String inputFile, String inputContent, String& outputFile, TemplateSettings settings) in D:\a\t4\t4\dotnet-t4\ToolTemplateGenerator.cs:line 64
   at Mono.TextTemplating.TextTransform.MainInternal(String[] args) in D:\a\t4\t4\dotnet-t4\TextTransform.cs:line 218
   at Mono.TextTemplating.TextTransform.Main(String[] args) in D:\a\t4\t4\dotnet-t4\TextTransform.cs:line 42

PowerShell v5.1.18362 dotnet v5.0.201 t4 v2.2.1

What am I doing wrong to reference my Custom class? This is probably a common use case, so I suspect that I'm missing something obvious.

MoFtZ commented 3 years ago

@qubitz I was able to get it to work by using an absolute path to the assembly: t4 -r C:/t4test/bin/Debug/net5.0/t4test.dll ./Test.tt

After that, it complains that it cannot find Custom, which can be fixed by importing the namespace.

echo "<#@ template language=`"C#`"#>" > Test.tt
echo "<#@ output extension=`".cs`"#>" >> Test.tt
echo "<#@ import namespace=`"Program`" #>" >> Test.tt
echo "<# var t = typeof(Custom); #>" >> Test.tt
qubitz commented 3 years ago

Omg, thank you @MoFtZ! That worked for me. I'm starting to understand the cli help now. These are equivalent:

t4 -r C:\t4test\bin\Debug\net5.0\t4test.dll .\Test.tt
t4 -P C:\t4test\bin\Debug\net5.0\ -r t4test.dll .\Test.tt

Also <#@ assembly name="t4test.dll"#> works with t4 -P C:\t4test\bin\Debug\net5.0\ .\Test.tt

The current help page is a tad vague when it mentions "framework and the include folders" in -r. Something similar to "specified assembly and include folders" would be preferable.

 -r=<assembly>              Name or path of an <assembly> reference.
                               Assemblies will be resolved from the framework
                               and the include folders
 -I=<directory>             Search <directory> when resolving file includes
 -P=<directory>             Search <directory> when resolving assembly
                               references

I might submit a PR for this if I get some time this week. Thanks again.

mhutch commented 3 years ago

Reopening because this definitely seems like a bug that relative assembly paths don't work.

I think this partly might be an artifact of modeling the ResolveAssemblyFile mechanism in the old VS T4 API. I'd love to modernize the whole thing and get rid of the whole concept of include/search folders.

Offhand, it might be difficult to make sure that assembly references in tt files resolve relative to those files while assembly references on the CLI resolve relative to the working directory.

qubitz commented 3 years ago

Ah yes @mhutch, I believe supporting relative assembly paths would be extremely helpful in standardizing setup across different users with the same files. It's also quite intuitive to new users like me who didn't know that t4 templates stand alone from files around them in the same csproj file.

davidreis97 commented 3 years ago

Hi, are there any plans to move forward with this? It'd be extremely useful in maintaining compatibility with .tt files from the .NET Framework era.

Would you feel comfortable with reviewing a PR for this? I'd be happy to contribute.

mhutch commented 3 years ago

@davidreis97 absolutely! Pull requests are very welcome. It might be a while till I can review it but I will make time to do so 🙂

I would recommend basing the PR on the build-targets branch rather than master. That's effectively been my dev branch for a while now - there are a bunch of other fixes in addition to the MSBuild targets. I was hoping to get some feedback on the targets (and a better package name) before merging it into master, so it's been sitting on the back burner, but it's pretty much done.

mhutch commented 3 years ago

Also, I'm more than happy to review draft PRs, answer questions such as why things are designed/implemented the way they are currently, discuss possible approaches for implementing/fixing things, and so on 🙂