-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Description
If you create this very simple T4 file:
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Xml.XDocument" #>
<#=typeof(System.Xml.Linq.XElement)#>
and try to run it:
TextTransformCore.exe .\Simple.ttyou get this error:
Error: C:\temp\texttransformcore\Simple.tt(4,124) : error CS1069: Compiling transformation: CS1069: The type name 'XElement' could not be found in the namespace 'System.Xml.Linq'. This type has been forwarded to assembly 'System.Private.Xml.Linq, Version=8.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51' Consider adding a reference to that assembly.
It's true that the runtime assemblies for .NET 8.0 do happen to define XElement in System.Private.Xml.Linq, but that's not the case for the reference assemblies. At build time it should be sufficient to have a reference just to System.Xml.XDocument; the fact that the .NET 8.0 runtime happens to implement that somewhere else is an implementation detail, something that could change from one version of .NET to the next, and not something that you're supposed to be making assumptions about at build time.
For this particular example, I can work around this by adding the reference it says it wants:
<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Xml.XDocument" #>
<#@ assembly name="System.Private.Xml.Linq" #>
<#=typeof(System.Xml.Linq.XElement)#>
However, this is a fragile solution (because it bakes in an assumption about internal implementation choices made by the .NET runtime) and should be unnecessary.
Within the .NET tool chain, the distinction between reference assemblies and runtime assemblies is typically important, and the norm is for reference assemblies to be supplied at compile time, and runtime assemblies to be used at runtime. Failure to do this causes this kind of problem with assemblies built into the .NET runtime. It will also cause problems for other libraries that use a similar distinction. (E.g., I'm the lead maintainer of Rx.NET (https://github.com/dotnet/reactive) and we have some components that have slightly different runtime and ref assembly surface areas to support certain backwards compatibility scenarios. These libraries might well be essentially unusable with TextTransformCore . Mind you, since those are distributed via NuGet, there's also the larger problem that T4 doesn't provide a way to add a NuGet reference.)
As far as I can tell, TextTransformCore simply doesn't make this distinction. From what I've been able to tell, at some point during its internal initialization, it rewrites assembly directives of this kind by calling AssemblyContext.LoadFromAssemblyName, which gives you the runtime assembly, and it then supplies the resulting path to the compiler. In effect, the tool appears to act as if runtime assemblies are always suitable for use at build time. But as System.Xml.XDocument shows, this is definitely not true.