2

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="&lt;# /* AUTOGENERATED FILE */ #&gt;" Overwrite="true" Encoding="Unicode" />

        <!-- T4Toolbox -->
        <WriteLinesToFile File="@(TargetReferenceFile)" Lines="&lt;#@ include file=&quot;T4Toolbox.tt&quot; #&gt;" Overwrite="false" Encoding="Unicode" />
        <WriteLinesToFile File="@(TargetReferenceFile)" Lines="&lt;#@ assembly name=&quot;&quot; completion=&quot;T4Toolbox.dll&quot; #&gt;" Overwrite="false" Encoding="Unicode" />

        <!-- Visual Studio IDE -->
        <WriteLinesToFile File="@(TargetReferenceFile)" Lines="&lt;#@ assembly name=&quot;EnvDTE, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a&quot; #&gt;" Overwrite="false" Encoding="Unicode" />
        <WriteLinesToFile File="@(TargetReferenceFile)" Lines="&lt;#@ assembly name=&quot;EnvDTE80, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a&quot; #&gt;" Overwrite="false" Encoding="Unicode" />
        <WriteLinesToFile File="@(TargetReferenceFile)" Lines="&lt;#@ assembly name=&quot;VSLangProj, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a&quot; #&gt;" Overwrite="false" Encoding="Unicode" />
        <WriteLinesToFile File="@(TargetReferenceFile)" Lines="&lt;#@ assembly name=&quot;Microsoft.VisualStudio.Shell.Interop.8.0, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a&quot; #&gt;" Overwrite="false" Encoding="Unicode" />
        <WriteLinesToFile File="@(TargetReferenceFile)" Lines="&lt;#@ assembly name=&quot;Microsoft.VisualStudio.TextTemplating.Interfaces.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL&quot; #&gt;" Overwrite="false" Encoding="Unicode" />
        <WriteLinesToFile File="@(TargetReferenceFile)" Lines="&lt;#@ assembly name=&quot;Microsoft.VisualStudio.TextTemplating.12.0, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL&quot; #&gt;" Overwrite="false" Encoding="Unicode" />

        <!-- With HintPath -->
        <WriteLinesToFile File="@(TargetReferenceFile)" Lines="&lt;#@ assembly name=&quot;%24%28ProjectDir%29%(Reference.HintPath)&quot; #&gt;" Overwrite="false" Encoding="Unicode" Condition="'%(Reference.HintPath)' != '' And $([System.String]::new('%(Reference.HintPath)').Contains('T4Toolbox.dll')) == 'false'" />

        <!-- Without HintPath -->
        <WriteLinesToFile File="@(TargetReferenceFile)" Lines="&lt;#@ assembly name=&quot;%(Reference.Identity)&quot; #&gt;" Overwrite="false" Encoding="Unicode" Condition="'%(Reference.HintPath)' == ''" />

        <!-- ProjectReference -->
        <WriteLinesToFile File="@(TargetReferenceFile)" Lines="&lt;#@ assembly name=&quot;%24%28ProjectDir%29%(ProjectReference.RelativeDir)bin\$(Configuration)\%(ProjectReference.Name).dll&quot; #&gt;" Overwrite="false" Encoding="Unicode" Condition="'%(ProjectReference.Name)' != ''" />

        <!-- Includes -->
        <WriteLinesToFile File="@(IncludesFile)" Lines="&lt;# /* AUTOGENERATED FILE */ #&gt;" Overwrite="true" Encoding="Unicode" />
        <WriteLinesToFile File="@(IncludesFile)" Lines="&lt;#@ include file=&quot;..\Includes\%(Includes.Filename)%(Includes.Extension)&quot; #&gt;" Overwrite="false" Encoding="Unicode" />

        <Message Text="Generating %(TargetReferenceFile.FullPath) &amp;%(IncludesFile.FullPath)" Importance="high" />
    </Target>
</Project>
Community
  • 1
  • 1
Mike-E
  • 2,477
  • 3
  • 22
  • 34

1 Answers1

0

You could use a PowerShell script (install.ps1) to run a build after the NuGet package is installed.

The PowerShell script could use the Visual Studio API to do the build. Something similar to the following would build the project:

param($installPath, $toolsPath, $package, $project)

$config = $dte.Solution.SolutionBuild.ActiveConfiguration.Name
$dte.Solution.SolutionBuild.BuildProject($config, $project.UniqueName, $false)

To build a specific MSBuild target is more tricky. You could call MSBuild.exe directly. This is something the PostSharp NuGet package does. Or possibly you could use the MSBuild API to build a particular target. Something like:

Add-Type -AssemblyName 'Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

# Grab the loaded MSBuild project for the project
$buildProject = [Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.GetLoadedProjects($project.FullName) | Select-Object -First 1

$buildProject.Build("YourTargetName")

Note that I have not tested these PowerShell scripts. They are meant to give you a pointer to the correct implementation.

Matt Ward
  • 47,057
  • 5
  • 93
  • 94
  • OK great, thank you for your answer @matt-ward (I'm a newb and tried to mention your name here, but it appears to fail). I will look into the PostSharp NuGet package you mention above and see if I can get a target to build. I will update this thread with any questions I have or the final solution that I land on. – Mike-E Jan 27 '15 at 19:32
  • Thanks again Matt, I have updated my original question with my final answer. Incidentally, you inadvertently led me to the fundamental answer of my core problem, in that I am looking to use AOP with Xamarin. I looked into PostSharp, and they just happened to have announced Xamarin support for their new preview just a few days ago. Couldn't have been better timing. I will be ditching T4 to go this route, but will be leaving this question/answer here in case someone might benefit from it. – Mike-E Jan 30 '15 at 14:45