NuGet / Home

Repo for NuGet Client issues
Other
1.49k stars 251 forks source link

Question, how can be NuGet PackageReference assemblies be imported in a T4 Text Template #6997

Open alexdrl opened 6 years ago

alexdrl commented 6 years ago

Details about Problem

NuGet product used (NuGet.exe | VS UI | Package Manager Console | dotnet.exe): Launching the restore from msbuild

NuGet version (x.x.x.xxx): Included in VS

dotnet.exe --version (if appropriate):

VS version (if appropriate): VS 15.7.3

OS version (i.e. win10 v1607 (14393.321)): Windows Server 2016

Detailed repro steps so we can see the same problem

I have the same problem as this issue in SO, I want to reference some NuGet DLLs from a T4. Using <#@ assembly name="$(ProjectDir)$(OutDir)DllName" #> is not ideal as the references need to be copied in the $(OutDir) folder before the text template is executed, so the build fails because the text template is executed. Is there any MSBuild variable or similar which I can use to get a NuGet PackageReference Id related folder?

Thank you.

rohit21agrawal commented 6 years ago

@alexdrl not sure if i understand your question correctly, but the *.g.props file in the obj folder contains certain properties that might be of interest to you:


<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
    <RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
    <RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
    <ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">E:\temp\packversion\obj\project.assets.json</ProjectAssetsFile>
    <NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
    <NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\username\.nuget\packages\;C:\Program Files (x86)\Microsoft SDKs\NuGetPackagesFallback\;E:\DevTools\dotnet-sdk-latest-win-x64\sdk\NuGetFallbackFolder</NuGetPackageFolders>
    <NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
    <NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">4.7.0</NuGetToolVersion>
  </PropertyGroup>
  <PropertyGroup>
    <MSBuildAllProjects>$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
  </PropertyGroup>
</Project>
alexdrl commented 6 years ago

@rohit21agrawal The problem is that I would need to do: <#@ assembly name="$(NuGetPackageRoot)\newtonsoft.json\11.0.2\lib\netstandard2.0\Newtonsoft.Json.dll" #>

And I would like to do something like: <#@ assembly name="$(ResolvedPackageReference('Newtonsoft.Json'))" #>, something that I think that could be not posible, but I have not read something in the documentation about that.

Note that Newtonsoft.Json is a PackageReference in the Project that hosts the T4 template.

alexdrl commented 6 years ago

Could be relate to something like https://github.com/NuGet/Home/issues/6949

Fresa commented 6 years ago

What is sad is that the T4 text templating engine does not automatically include project referenced assemblies within the same project as the .tt file(s) exist, as it does for some "standard assemblies".

https://msdn.microsoft.com/en-us/library/gg586946.aspx

StingyJack commented 6 years ago

@alexdrl - T4 templates can access the ambient DTE, and use MSBuild properties.

excerpt mostly from here, that file has a method for setting up an alternate AssemblyResolve event handler that is used in the T4. It works, but its not the cleanest looking code.

<#@ template debug="true" hostspecific="true" language="C#" #>
<#@ assembly Name="EnvDTE" #>
<#@ assembly name="$(SolutionDir)\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll"#>

var regenHelper = new CodeGenHelpers(Host);
var solutionFilePath = regenHelper.GetDte().FullName;
var solutionFile = regenHelper.GetDte().FileName
var solutionFolder = solutionFilePath.Replace(solutionFile, string.Empty);
var packagesFolder = System.IO.Path.Combine(solutionFolder, "packages");

//whatever you want to do with these paths. 

public class CodeGenHelpers
{
    private ITextTemplatingEngineHost _Host;
    private static DTE _Dte;

    public CodeGenHelpers(ITextTemplatingEngineHost host)
    {
        _Host = host;
    }
    public DTE GetDte()
    {
        if (_Dte == null)
        {
            var serviceProvider = _Host as IServiceProvider;
            _Dte = serviceProvider.GetService(typeof(SDTE)) as DTE;
        }
        return _Dte;
    }
}
alexdrl commented 6 years ago

@StingyJack This answer is likely working with the old packages.config, but I will try with something like @rohit21agrawal suggested.

StingyJack commented 6 years ago

This isn't specific to either packages type, it's how to get to the current project and solution via T4.