It's been a while since I last used T4 and this is probably a silly question...
Is it possible to reference an arbitrary assembly from a template?
Example:
I have a class that I'd like to use in Project X
Project X.Test references X and contains the .tt file
I assume the following should work
<## assembly name="X" #>
But I get the following error on save:
Compiling transformation: Metadata
file 'X' could not be found
What am I doing wrong?
(In case anyone's interested: I'm trying to automatically generate a particular type of tests based on some metadata that I get from X)
Update: it looks like VS2010 has broken the assembly resolution behavior that I was expecting. From Link:
T4's assembly set is completely
separated from the containing
project's assembly set to avoid
picking up the wrong assemblies when a
project targets previous framework
versions. Project assemblies are no
longer used to resolve template
assembly directives.
Are there any workarounds, besides using absolute paths?
You can use VS macro variables such as $(SolutionDir) in your reference as of VS2010
e.g.
<## assembly name="$(SolutionDir)\Project1\bin\debug\Foo.dll" #>
You can also check here on SO: Can't reference an assembly in a T4 template
#GarethJ gives a good answer but for all the methods of referencing an assembly from a T4 template try this: T4 Template error - Assembly Directive cannot locate referenced assembly in Visual Studio 2010 project.
And if you like the VS Macro solution then you can find 'em all here: Macros for Build Commands and Properties
Related
I'm a little confused about compatibilities between netstandard, netframework, netX and how mono fits into the picture...
I understand that Rider's T4 engine runs on mono, but does that mean I can't use net5.0 assemblies in my T4 templates?
Currently, I have a net5.0 project, referencing net5.0 nuget assemblies.
In my T4 templates, I'm referencing the assembly DLLs in bin/Debug/net5.0 with `<# assembly name="...">
When I run the T4 templates in-proc in a net5.0 Console Application via Mono.TextTemplating.TemplateGenerator.TemplateGenerator, then the templates work.
However, if I right-click a template in the Solution explorer and select 'Run Template', I get a list of errors that core System.* libs are missing, e.g.
Generate.tt(21, 25): [CS0012] The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
Is this a bug in Rider?
Due to implementation details, Rider compiles T4 files targeting a version of .NET Framework, that's why it has problems with files referencing net5.0 assemblies. As far as I understand what I see in Mono.TextTemplating, they target the runtime they are launched in instead. To solve your problem I'd suggest trying Mono.TextTemplating as a command-line tool - it might be able to compile files with net5.0 references. To integrate that external tool into Rider, you can go to File > Settings > Tools > Custom Tools, disable Bundled T4 Template Executor and add a custom tool for Mono.TextTemplating CLT.
To answer your question: yes, it probably can be called a bug
I'm trying to use types from my own project in a T4 template, and so I want to use the VolatileAssembly directive so I don't have to keep restarting Visual Studio:
<## VolatileAssembly processor="T4Toolbox.VolatileAssemblyProcessor" name="C:\Josh\Archimetrics\Archimetrics.Consulting\Suntex\SuntexFirstInMathTools\bin\Debug\SuntexFirstInMathTools.exe" #>
But I keep getting the following error:
A processor named 'T4Toolbox.VolatileAssemblyProcessor' could not be found for the directive named 'VolatileAssembly'. The transformation will not be run.
The following Exception was thrown:
System.IO.FileNotFoundException: Failed to resolve type for directive processor
T4Toolbox.VolatileAssemblyProcessor.
at Microsoft.VisualStudio.TextTemplating.VSHost.TextTemplatingService.ResolveDirectiveProcessor(String processorName)
at Microsoft.VisualStudio.TextTemplating.Engine.ProcessCustomDirectives(ITextTemplatingEngineHost host, TemplateProcessingSession session, IEnumerable`1 directivesToBeProcessed)
I've installed the T4 toolbox and restarted, but for some reason it will not recognize the VolatileAssemblyProcessor.
How can I fix this?
They are also harder to use than templates because code they generate
is invisible to you by default. In addition, the VolatileAssembly is
no longer necessary because in Visual Studio 2012 the assembly
directive shadow-copies the assembly files before loading them
- the developers page -
So I think you do not need this directive anymore. Only use <## assembly name="YourPathOrName" #> will fix your problem.
I was able to get my T4 file to work properly when I run it within visual studio but it errors out when automating it to happen during the build process automatically.
The problem is that in order to reference an assembly that is in the same solution but another project I added this line:
<## assembly name="$(SolutionDir)\My.Core\bin\Debug\My.Core.dll" #>
In order to just run a single T4 template on build, I created a post build-event with the following command:
"%PROGRAMFILES(x86)%\Common Files\microsoft shared\TextTemplating\10.0\TextTransform.exe" $(ProjectDir)\Features\Admin\app\Abilities.tt
But since the host is not the IDE environment at this point it throws an error because it is treating the $(SolutionDir) a literally so it can't find the assembly reference for My.Core.dll.
So my question is, how can I reference that assembly within the T4 template so that it works with the build process I have and ideally still be able to right click and run the transformation manually, but that isn't as important.
I should also note that using the full path is not an option since there are multiple developers and the source code will be living in potentially different directories on each developer machine.
Here's one. I have a bunch of T4s in one project / solution. This is a framework with support code and T4 templates.
In a different solution, I want to use this framework, but have the support classes / T4s remain in the original solution.
In the new solution I link to the support code and T4s (add existing / link). Now in the new solution I have a T4 which needs to include the linked T4. It has something like this:
<## template language="C#" debug="false" hostspecific="true"#>
<## include file="..\Models\DALContextGenerator.tt"#>
<## output extension=".cs"#><#
Generate("..\Models\Model1.edmx");
>
In this case, DALContextGenerator.tt is in this solution, but is linked to the real DALContextGenerator.tt in a different solution. WHen I run the T4 I get an error ("Failed to resolve include text"). If I reference the physical location it is fine.
Any ideas?
Thanks
Ray
As far as I know, the T4 engine uses the template file as a root and is unaware of the Visual Studio Solution and Solution items. If you're using a Visual Studio link to a file somewhere else this information is only stored in the project file. The T4 engine looks up the include-path relative to the T4 file. That's why referencing the Visual Studio Link relatively fails. But referencing the include file either with its absolute path or a relative one pointing to the physical file succeeds.
Here are some ideas how to address your problem, but there is no "smooth" solution I can think of:
Use a hard link between the original include file and a file located next to the template file (command line: mklink /H source target)
if you are using a source control system (like svn) you can work with external directories without duplicating your originals
Have a (meta-)T4 template that generates the actual T4 template with the proper paths based on the information you get from Env.DTE Visual Studio Model
Old but relevant question, I think the same as this other thread, where I posted a reply, using expansions $(ProjectDir) and $(SolutionDir): https://stackoverflow.com/a/42785952/1948625
I have the following code in a tester class in my main assembly, PocoGenerator. This assembly is supposed to use a T4 template to generate POCO's based on L2S entities in a referenced assembly (a project reference), DataObjects.
var assemblyName = "DataObjects";
var dataObjects = AppDomain.CurrentDomain.Load(new AssemblyName(assemblyName));
Try as I may, I cannot get T4 to find the DataObjects assembly. I have tried various forms of assembly directives, like:
<## assembly name="DataObjects" #>
<## assembly name="DataObjects, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" #>
to no avail. The code above works in the tester class, but not in the template. What am I doing wrong?
ADDED:
I have resolved this issue by using the absolute path to the assembly in bot places I reference it, the directive as well as the class feature block, i.e.
<## assembly name="C:\Development\PocoGenerator\DataObjects\bin\Debug\DataObjects.dll" #>
and
var sourceAssembly = Assembly.LoadFile(#"C:\Development\PocoGenerator\DataObjects\bin\Debug\DataObjects.dll");
But I really don't like this, as I would like to use this template in various projects, and I just plain hate duplication, especially of magic strings.
<## assembly name="$(ProjectDir)bin\Debug\ProofOfConcept.dll" #>
Happy Coding!
To reference assembly in T4 template in VS2010 you have some options:
GAC your assemblies and use Namespace Reference or Fully Qualified Type Name
Use a hard-coded Fully Qualified UNC path
Copy assembly to Visual Studio "Public Assemblies Folder" and use Namespace Reference or Fully Qualified Type Name.
Use or Define a Windows Environment Variable to build a Fully Qualified UNC path.
Use a Visual Studio Macro to build a Fully Qualified UNC path.
I would suggest that you put a referenced assembly in your Public Assemblies Folder, another, maybe even better solution would be to hard code the path of your referenced assemblies.
Very nice post on this topic: T4 Template error - Assembly Directive cannot locate referenced assembly in Visual Studio 2010 project.
Basically MS decided to the the braking change, that the project referenced assemblies are not referenced by T4 engine, too.
T4's assembly set is completely separated from the containing project's assembly set to avoid picking up the wrong assemblies when a project targets previous framework versions. Project assemblies are no longer used to resolve template assembly directives.
More on that: What's new in T4 in Visual Studio 2010
I had a similar problem when I tried to include Less Css for .NET in my Web project.
I've ended up with copying the assembly in the root folder of my project and including it as a reference in the project itself. Then, I've added the following lines in the .tt file:
<## assembly name="dotless.Core.dll" #>
<## import namespace="dotless.Core" #>
<## import namespace="dotless.Core.configuration" #>
I'm sure that something similar should work with your assembly as well...
I've found there are a number of cases in creating and using the gax toolkit and packages where the build is perfectly happy with the way references are structured but the runtime gets all bothered because it can't find what it's looking for - this usually occurs when the main assembly references an assembly that uses gax elements and then that assembly in turn references another assembly that the main does not.
try directly including the assembly in question in your main assembly - and consider that you may need to write post build instructions to move it to an 'expected' location - while a nusiance, it should beat having to hardwire the path.
YMMV