How can I get nuget (powershell) to insert <DependentUpon> elements in the target csproj file How can I get nuget (powershell) to insert <DependentUpon> elements in the target csproj file powershell powershell

How can I get nuget (powershell) to insert <DependentUpon> elements in the target csproj file


Well after around 2 days of powershell and msbuild hell I finally got a working solution. See below. A word of warning calling project.Save() is crucial - without this you will get a "conflicting file modification detected" warning. If you call project.Save() first you will be merely asked to reload the solution once you are done.

param($installPath, $toolsPath, $package, $project)#save the project file first - this commits the changes made by nuget before this     script runs.$project.Save()#Load the csproj file into an xml object$xml = [XML] (gc $project.FullName)#grab the namespace from the project element so your xpath works.$nsmgr = New-Object System.Xml.XmlNamespaceManager -ArgumentList $xml.NameTable$nsmgr.AddNamespace('a',$xml.Project.GetAttribute("xmlns"))#link the service designer to the service.cs$node = $xml.Project.SelectSingleNode("//a:Compile[@Include='Service.Designer.cs']", $nsmgr)$depUpon = $xml.CreateElement("DependentUpon", $xml.Project.GetAttribute("xmlns"))$depUpon.InnerXml = "Service.cs"$node.AppendChild($depUpon)#link the settings file to the settings.designer.cs $settings = $xml.Project.SelectSingleNode("//a:None[@Include='ServiceSettings.settings']", $nsmgr)$generator = $xml.CreateElement("Generator", $xml.Project.GetAttribute("xmlns"))$generator.InnerXml = "SettingsSingleFileGenerator"$settings.AppendChild($generator)$LastGenOutput = $xml.CreateElement("LastGenOutput", $xml.Project.GetAttribute("xmlns"))$LastGenOutput.InnerXml = "ServiceSettings.Designer.cs"$settings.AppendChild($LastGenOutput)#set the settings designer to be autogen$settingsDesigner = $xml.Project.SelectSingleNode("//a:Compile[@Include='ServiceSettings.Designer.cs']", $nsmgr)$autoGen = $xml.CreateElement("AutoGen", $xml.Project.GetAttribute("xmlns"))$autoGen.InnerXml = "True"$DesignTimeSharedInput = $xml.CreateElement("DesignTimeSharedInput", $xml.Project.GetAttribute("xmlns"))$DesignTimeSharedInput.InnerXml = "True"$AGDependentUpon = $xml.CreateElement("DependentUpon", $xml.Project.GetAttribute("xmlns"))$AGDependentUpon.InnerXml = "ServiceSettings.settings"$settingsDesigner.AppendChild($autoGen)$settingsDesigner.AppendChild($DesignTimeSharedInput)$settingsDesigner.AppendChild($AGDependentUpon)#fix up the project installer.    $projectInstallerRes = $xml.Project.SelectSingleNode("//a:EmbeddedResource[@Include='ProjectInstaller.resx']", $nsmgr)$projectInstallerResDepUpon = $xml.CreateElement("DependentUpon", $xml.Project.GetAttribute("xmlns"))$projectInstallerResDepUpon.InnerXml = "ProjectInstaller.cs"$projectInstallerRes.AppendChild($projectInstallerResDepUpon)$projectInstallerDesigner = $xml.Project.SelectSingleNode("//a:Compile[@Include='ProjectInstaller.Designer.cs']", $nsmgr)$projectInstallerDesignerDepUpon = $xml.CreateElement("DependentUpon", $xml.Project.GetAttribute("xmlns"))$projectInstallerDesignerDepUpon.InnerXml = "ProjectInstaller.cs"$projectInstallerDesigner.AppendChild($projectInstallerDesignerDepUpon)#delete the bundled program.cs file.$prog = $xml.Project.SelectSingleNode("//a:Compile[@Include='Program.cs']", $nsmgr)$prog.SelectSingleNode("..").RemoveChild($prog)#delete the bundled service1 file$oldServiceFile = $xml.Project.SelectSingleNode("//a:Compile[@Include='Service1.cs']", $nsmgr)$oldServiceFile.SelectSingleNode("..").RemoveChild($oldServiceFile)$oldServiceDesignerFile = $xml.Project.SelectSingleNode("//a:Compile[@Include='Service1.Designer.cs']", $nsmgr)$oldServiceDesignerFile.SelectSingleNode("..").RemoveChild($oldServiceDesignerFile)#save the changes.$xml.Save($project.FullName)

Shameless self plugging: I'll do a full write up of the solution and issues I encountered on my blog cianm.comHope this saves someone out there some time.


I think an easier way to do this (and possibly more supported) is through the $project variable that points to the DTE project.

Note: I'm coming into this a year later, but facing a similar problem. I found this answer and started using it, but I had trouble with the Save call at the end of the PowerShell script - if I had one package getting installed that depended on the package with the PowerShell script, the Save call "broke things" so the other packages couldn't properly install. Anyway, I'm now using NuGet 2.5, but DTE hasn't changed with any significance since VS 2003 so this should work even in the older NuGet you were using.

param($installPath, $toolsPath, $package, $project)# 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.# The Service.cs will show with a sub-item if it's already got the designer# file set. In the package upgrade scenario, you don't want to re-set all# this, so skip it if it's set.$service = $project.ProjectItems | Where-Object { $_.Properties.Item("Filename").Value -eq "Service.cs" -and  $_.ProjectItems.Count -eq 0 }if($service -eq $null){    # Upgrade scenario - user has moved/removed the Service.cs    # or it already has the sub-items set.    return}$designer = $project.ProjectItems | Where-Object { $_.Properties.Item("Filename").Value -eq "Service.Designer.cs" }if($designer -eq $null){    # Upgrade scenario - user has moved/removed the Service.Desginer.cs.    return}# Here's where you set the designer to be a dependent file of# the primary code file.$service.ProjectItems.AddFromFile($designer.Properties.Item("FullPath").Value)

Hopefully that helps folks in the future looking to accomplish something similar. Doing it this way, you avoid the problem with dependent package installs failing because the project is getting saved in the middle of package install.


Here's a short example using Build.Evaluation that is part of Visual Studio 2010+

$buildProject = @([Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.GetLoadedProjects($project.FullName))[0]$toEdit = $buildProject.Xml.Items | where-object { $_.Include -eq "Service.Designer.cs" }$toEdit.AddMetaData("DependentUpon", "Service.cs")# ... for the other files$project.Save()