我正在为一个Windows服务编写一个nuget包,我的想法是我的同事可以创建一个Windows服务安装包,并为他们设置所有默认的日志记录设置,entlib库和其他的house keep任务
除了一件令我疯狂的事情外,我已经完成了一切工作。
在我的nuget目录的content文件夹中,我有Service.cs和Service.Designer.cs这些被添加到目标csproj但它们没有相关。
当我查看csproj文件时,我看到:
<Compile Include="Service.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Service.Designer.cs" />
但我希望看到:
<Compile Include="Service.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Service.Designer.cs">
<DependentUpon>Service.cs</DependentUpon>
</Compile>
任何想法,非常确定它会涉及install.ps脚本,但我的powershell技能是不存在的?
顺便说一下,可以用nuget来删除/覆盖文件吗?到目前为止它似乎只是跳过了。
答案 0 :(得分:11)
经过2天左右的powershell和msbuild,我终于得到了一个有效的解决方案。见下文。调用project.Save()的警告是至关重要的 - 如果没有这个,你将得到一个“检测到冲突的文件修改”警告。如果你先调用project.Save(),那么只要你完成就会被要求重新加载解决方案。
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)
无耻的自我堵塞:I'll do a full write up of the solution and issues I encountered on my blog cianm.com 希望这能节省一些人的时间。
答案 1 :(得分:6)
我认为更简单的方法(可能更多支持)是通过指向DTE项目的$project
变量。
注意:一年后我会进入这个阶段,但面临类似的问题。我找到了这个答案并开始使用它,但是我在PowerShell脚本末尾的Save调用时遇到了问题 - 如果我安装了一个依赖于PowerShell脚本的软件包,那么保存调用会“破坏”所以其他软件包无法正确安装。无论如何,我现在正在使用NuGet 2.5,但是自从VS 2003以来DTE没有任何重大改变,所以即使在你使用的旧版NuGet中也应如此。
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)
希望这有助于未来的人们寻求完成类似的事情。这样做可以避免依赖软件包安装失败的问题,因为项目是在软件包安装过程中保存的。
答案 2 :(得分:1)
这是一个使用Build.Evaluation的简短示例,它是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()