Setting DependentUpon File Properties on NuGet Package Install
In working on some NuGet packages, one thing I wanted to do was set up some configuration files in preparation for SlowCheetah integration. Instead of seeing a folder structure like this in the project…
- ~/Config
- MyConfig.config
- MyConfig.Debug.config
- MyConfig.Release.config
I wanted to see the file dependencies set up like you usually get with Web.config:
- ~/Config
- MyConfig.config
- MyConfig.Debug.config
- MyConfig.Release.config
- MyConfig.config
That’s not really a straightforward thing to do, as it turns out.
Luckily, NuGet provides your package the ability to have a PowerShell script run at install time, and part of what it passes you is a reference to the EnvDTE project into which the package is being installed.
EnvDTE is the way you automate Visual Studio for things like custom tools and add-ins. I’ve messed around with EnvDTE before (though lately I prefer using CodeRush for my automation tasks) so this wasn’t too hard to get back into. Here’s the script for Install.ps1:
param($installPath, $toolsPath, $package, $project)
# Sets the configuration files to have dependent transforms (.Debug/.Release).
# Selections of items in the project are done with Where-Object rather than direct
# access into the ProjectItems collection because if the object is moved or doesn't
# exist then Where-Object will give us a null response rather than the error that
# DTE will give us.
$configFolder = $project.ProjectItems | Where-Object { $_.Properties.Item("Filename").Value -eq "Config" -and $_.ProjectItems.Count -gt 0 }
if($configFolder -eq $null)
{
# Upgrade scenario - user has moved/removed the Config folder
# or has moved/removed the configuration files out of the folder.
return
}
$baseConfig = $configFolder.ProjectItems | Where-Object { $_.Properties.Item("Filename").Value -eq "MyConfig.config" -and $_.ProjectItems.Count -eq 0 }
if($baseConfig -eq $null)
{
# Upgrade scenario - user has moved/removed the MyConfig.config file
# or it already has the dependent items set.
return
}
# Config file exists, so update the properties.
$baseConfig.Properties.Item("SubType").Value = "Designer"
$debugConfig = $configFolder.ProjectItems | Where-Object { $_.Properties.Item("Filename").Value -eq "MyConfig.Debug.config" }
if($debugConfig -eq $null)
{
# Upgrade scenario - user has moved/removed the MyConfig.Debug.config file
# or it's already set as a dependent item. (Dependent items show up as children
# of the file on which they depend, not as a child of the folder.)
return
}
# Handle the update for MyConfig.Debug.config - set it as BuildAction = None
# and move it to be a dependency of MyConfig.config.
$debugConfig.Properties.Item("ItemType").Value = "None"
$baseConfig.ProjectItems.AddFromFile($debugConfig.Properties.Item("FullPath").Value)
$releaseConfig = $configFolder.ProjectItems | Where-Object { $_.Properties.Item("Filename").Value -eq "MyConfig.Release.config" }
if($releaseConfig -eq $null)
{
# Upgrade scenario - user has moved/removed the MyConfig.Release.config file
# or it's already set as a dependent item. (Dependent items show up as children
# of the file on which they depend, not as a child of the folder.)
return
}
# Handle the update for MyConfig.Release.config - set it as BuildAction = None
# and move it to be a dependency of MyConfig.config.
$releaseConfig.Properties.Item("ItemType").Value = "None"
$baseConfig.ProjectItems.AddFromFile($releaseConfig.Properties.Item("FullPath").Value)
What this does is switch this .csproj snippet…
<ItemGroup>
<Content Include="MyConfig.config" />
<Content Include="MyConfig.Debug.config" />
<Content Include="MyConfig.Release.config" />
<ItemGroup>
Into this:
<ItemGroup>
<Content Include="MyConfig.config">
<SubType>Designer</SubType>
</Content>
<None Include="MyConfig.Debug.config">
<DependentUpon>MyConfig.config</DependentUpon>
</None>
<None Include="MyConfig.Release.config">
<DependentUpon>MyConfig.config</DependentUpon>
</None>
<ItemGroup>
What I’ve not yet figured out is how to get a new custom element
<TransformOnBuild>true</TransformOnBuild>
to show up on the
MyConfig.config element. From this article on
MSDN, it
appears there’s a much more involved bit of work to do and I’m not sure
that I have access to all the requisite DTE objects from inside the
script.