I am creating a NuGet package, and have a .targets file that gets installed into the target .csproj file when the package is installed. This works perfectly as expected, with the .targets file having a Target element that executes before the Build and TransformOnBuild targets.
However, what I want to do now is to execute this Target after the package is installed. That is, I do not want the user to have to manually build their .csproj after they install my package. I want the package installation to run this defined Target after it installs the package into the target project.
The reason for this is that the Target autogenerates a few files in the target project (and are based on properties found within the target project), and I want these files to be generated with initial data upon installation and not wait until the user builds that project to create the data.
FWIW, the closest question I found that comes close to this is this one: Configure NuGet package to add a build event when installed, but this is for the build pipeline. I am wanting to initiate a build (my Target in the .targets file) after the NuGet project installation.
EDIT:
Thanks to @matt-ward I was able to land on this final answer. I am sharing the full answer here. It recursively goes through the project to find any .tt files, and sets their Custom Tool to blank, and then compiles the project to generate the needed artifacts. Here is the install.ps1 in full:
# Runs every time a package is installed in a project
param($installPath, $toolsPath, $package, $project)
# $installPath is the path to the folder where the package is installed.
# $toolsPath is the path to the tools directory in the folder where the package is installed.
# $package is a reference to the package object.
# $project is a reference to the project the package was installed to.
function AdjustCustomTool( $folder )
{
foreach ( $i in $folder.ProjectItems )
{
switch ( $i.Kind )
{
"{6BB5F8EF-4483-11D3-8BCF-00C04F8EC28C}" # Folder
{
AdjustCustomTool( $i );
break;
}
"{6BB5F8EE-4483-11D3-8BCF-00C04F8EC28C}" # File
{
switch ( [System.IO.Path]::GetExtension( $i.Name ) )
{
".tt"
{
$i.Properties.Item("CustomTool").Value = "";
break;
}
}
}
}
}
}
$templates = $project.ProjectItems.Item( "DragonSpark.Templates" );
AdjustCustomTool( $templates );
# Need to load MSBuild assembly if it's not loaded yet.
Add-Type -AssemblyName 'Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
# Grab the loaded MSBuild project for the project
$project = [Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.GetLoadedProjects($project.FullName) | Select-Object -First 1
$project.Build( "GenerateTemplateReferences" );
FWIW, here is my .targets file:
<Error Condition="'@(TargetReferenceFile -> Count())' == 0" Text="No DragonSpark.Templates\Generated\References.tt file found" />
<Error Condition="'@(IncludesFile -> Count())' == 0" Text="No DragonSpark.Templates\Generated\Includes.tt file found" />
<WriteLinesToFile File="@(TargetReferenceFile)" Lines="<# /* AUTOGENERATED FILE */ #>" Overwrite="true" Encoding="Unicode" />
<!-- T4Toolbox -->
<WriteLinesToFile File="@(TargetReferenceFile)" Lines="<#@ include file="T4Toolbox.tt" #>" Overwrite="false" Encoding="Unicode" />
<WriteLinesToFile File="@(TargetReferenceFile)" Lines="<#@ assembly name="" completion="T4Toolbox.dll" #>" Overwrite="false" Encoding="Unicode" />
<!-- Visual Studio IDE -->
<WriteLinesToFile File="@(TargetReferenceFile)" Lines="<#@ assembly name="EnvDTE, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" #>" Overwrite="false" Encoding="Unicode" />
<WriteLinesToFile File="@(TargetReferenceFile)" Lines="<#@ assembly name="EnvDTE80, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" #>" Overwrite="false" Encoding="Unicode" />
<WriteLinesToFile File="@(TargetReferenceFile)" Lines="<#@ assembly name="VSLangProj, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" #>" Overwrite="false" Encoding="Unicode" />
<WriteLinesToFile File="@(TargetReferenceFile)" Lines="<#@ assembly name="Microsoft.VisualStudio.Shell.Interop.8.0, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" #>" Overwrite="false" Encoding="Unicode" />
<WriteLinesToFile File="@(TargetReferenceFile)" Lines="<#@ assembly name="Microsoft.VisualStudio.TextTemplating.Interfaces.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" #>" Overwrite="false" Encoding="Unicode" />
<WriteLinesToFile File="@(TargetReferenceFile)" Lines="<#@ assembly name="Microsoft.VisualStudio.TextTemplating.12.0, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" #>" Overwrite="false" Encoding="Unicode" />
<!-- With HintPath -->
<WriteLinesToFile File="@(TargetReferenceFile)" Lines="<#@ assembly name="%24%28ProjectDir%29%(Reference.HintPath)" #>" Overwrite="false" Encoding="Unicode" Condition="'%(Reference.HintPath)' != '' And $([System.String]::new('%(Reference.HintPath)').Contains('T4Toolbox.dll')) == 'false'" />
<!-- Without HintPath -->
<WriteLinesToFile File="@(TargetReferenceFile)" Lines="<#@ assembly name="%(Reference.Identity)" #>" Overwrite="false" Encoding="Unicode" Condition="'%(Reference.HintPath)' == ''" />
<!-- ProjectReference -->
<WriteLinesToFile File="@(TargetReferenceFile)" Lines="<#@ assembly name="%24%28ProjectDir%29%(ProjectReference.RelativeDir)bin\$(Configuration)\%(ProjectReference.Name).dll" #>" Overwrite="false" Encoding="Unicode" Condition="'%(ProjectReference.Name)' != ''" />
<!-- Includes -->
<WriteLinesToFile File="@(IncludesFile)" Lines="<# /* AUTOGENERATED FILE */ #>" Overwrite="true" Encoding="Unicode" />
<WriteLinesToFile File="@(IncludesFile)" Lines="<#@ include file="..\Includes\%(Includes.Filename)%(Includes.Extension)" #>" Overwrite="false" Encoding="Unicode" />
<Message Text="Generating %(TargetReferenceFile.FullPath) &%(IncludesFile.FullPath)" Importance="high" />
</Target>
</Project>