Search code examples
powershellnugetnuget-packageenvdtecsproj

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


I'm writing a nuget package for a windows service, the idea is that my colleagues can create a windows service install the package and all of the default logging settings, entlib libraries and other house keep tasks will be set up for them.

I've gotten pretty much everything to work except for one thing which is driving me nuts.

In the content folder of my nuget directory I have Service.cs and Service.Designer.cs these are added to the target csproj but they are not being related.

When I look at the csproj file I see:

<Compile Include="Service.cs">
  <SubType>Component</SubType>
</Compile>
<Compile Include="Service.Designer.cs" />

But I want to see:

<Compile Include="Service.cs">
  <SubType>Component</SubType>
</Compile>
<Compile Include="Service.Designer.cs">
  <DependentUpon>Service.cs</DependentUpon>
</Compile>

Any ideas, pretty sure it will involve the install.ps script but my powershell skills are non existant?

As an aside, can nuget be used to delete/overwrite files? So far it just seems to skip.


Solution

  • 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.com Hope this saves someone out there some time.