【问题标题】:How can I get nuget (powershell) to insert <DependentUpon> elements in the target csproj file如何让 nuget (powershell) 在目标 csproj 文件中插入 <DependentUpon> 元素
【发布时间】:2012-05-31 23:23:05
【问题描述】:

我正在为 Windows 服务编写一个 nuget 包,其想法是我的同事可以创建一个 Windows 服务安装该包,并为他们设置所有默认日志记录设置、entlib 库和其他家务管理任务.

除了一件让我发疯的事情之外,我几乎已经完成了所有工作。

在我的 nuget 目录的内容文件夹中,我有 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 来删除/覆盖文件吗?到目前为止,它似乎只是跳过。

【问题讨论】:

  • 我问这应该成为您问题的一部分。我还建议针对最新版本的 NuGet 进行测试,即1.8

标签: powershell nuget nuget-package envdte csproj


【解决方案1】:

经过大约 2 天的 powershell 和 msbuild hell 后,我终于找到了一个可行的解决方案。见下文。一个调用 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 希望这可以节省一些时间。

【讨论】:

    【解决方案2】:

    我认为更简单的方法(并且可能更受支持)是通过指向 DTE 项目的 $project 变量。

    注意:我会在一年后进入这个领域,但面临着类似的问题。我找到了这个答案并开始使用它,但我在 PowerShell 脚本末尾的 Save 调用遇到了问题 - 如果我安装了一个依赖于 PowerShell 脚本的包的包,则 Save 调用“破坏”所以其他软件包无法正确安装。无论如何,我现在使用的是 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)
    

    希望这能帮助人们在未来完成类似的事情。这样做可以避免依赖包安装失败的问题,因为项目在包安装过程中被保存。

    【讨论】:

      【解决方案3】:

      这是一个使用 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()
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-07-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-08
        • 2011-08-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多