Visual studio 如何在项目中强制文件,使其始终复制到输出目录

Visual studio 如何在项目中强制文件,使其始终复制到输出目录,visual-studio,msbuild,csproj,octopus-deploy,roundhouse,Visual Studio,Msbuild,Csproj,Octopus Deploy,Roundhouse,我正在使用VisualStudioC库项目来包含作为部署工件所需的静态资源。在我的例子中,使用RoundhousE和Octopus组合运行的SQL文件部署。按照惯例,项目中的所有文件都必须设置其属性,以便生成操作为Content,复制到输出目录为Copy always 如果团队中有人添加了一个文件,但忘记设置这些属性,我们会看到部署错误。这通常是在内部环境中获得的,但我希望找到一种方法在CI构建中实施这一点 那么,有没有一种方法可以使生成失败,或者在生成过程中更好地使用MS build任务覆盖这

我正在使用VisualStudioC库项目来包含作为部署工件所需的静态资源。在我的例子中,使用RoundhousE和Octopus组合运行的SQL文件部署。按照惯例,项目中的所有文件都必须设置其属性,以便生成操作为Content,复制到输出目录为Copy always

如果团队中有人添加了一个文件,但忘记设置这些属性,我们会看到部署错误。这通常是在内部环境中获得的,但我希望找到一种方法在CI构建中实施这一点


那么,有没有一种方法可以使生成失败,或者在生成过程中更好地使用MS build任务覆盖这些属性?我处理这件事的方法不对吗?欢迎任何建议。

您必须解析项目文件并检查内容,而不必将CopyToOutputDirectory设置为Always,我怀疑还有其他方法

这可以使用您想要的任何脚本语言来完成,或者您甚至可以编写一个使用Microsoft.Build.Evaluation命名空间中的类的小型C工具。下面是一个可能的PowerShell实现—最困难的部分是正确处理正则表达式。第一个检查没有任何元数据的内容,第二个检查CopyToOutputDirectory不以A开头的内容,我认为应该始终是,不知道如何匹配整个单词

FindBadContentNodes.ps1:

param([String]$inputDir)

Function FindBadContent()
{
  $lines = Get-Content $input
  $text = [string]::Join( "`n", $lines )
  if( $text -match "<Content Include.*/>" -Or
      $text -match "<Content Include.*`n\s*<CopyToOutputDirectory>[^A]\w*<.*" )
  {
    "Found file with bad content node"
    exit 1
  }
}

Get-ChildItem -Recurse -Include *.csproj -Path $inputDir | FindBadContent
从MsBuild调用此函数:

<Target Name="FindBadContentNodes">
  <Exec Command="Powershell FindBadContentNodes.ps1 -inputDir path\to\sourceDir"/>
</Target>
请注意,您提到了这些属性,或者最好在生成过程中重写这些属性。我不想使用这样的解决方案:您只是在隐藏问题并依靠CI生成正确的构建,因此使用just-VS的本地构建将不同。Imo使构建失败会更好,特别是因为大多数CI系统都有一种通知负责开发的开发人员的方法,所以应该快速应用修复


另一种可能是让CI应用修复程序,然后提交更改,这样至少每个人都有正确的版本。

您必须解析项目文件并检查内容,而不将CopyToOutputDirectory设置为Always,我怀疑还有其他方法

这可以使用您想要的任何脚本语言来完成,或者您甚至可以编写一个使用Microsoft.Build.Evaluation命名空间中的类的小型C工具。下面是一个可能的PowerShell实现—最困难的部分是正确处理正则表达式。第一个检查没有任何元数据的内容,第二个检查CopyToOutputDirectory不以A开头的内容,我认为应该始终是,不知道如何匹配整个单词

FindBadContentNodes.ps1:

param([String]$inputDir)

Function FindBadContent()
{
  $lines = Get-Content $input
  $text = [string]::Join( "`n", $lines )
  if( $text -match "<Content Include.*/>" -Or
      $text -match "<Content Include.*`n\s*<CopyToOutputDirectory>[^A]\w*<.*" )
  {
    "Found file with bad content node"
    exit 1
  }
}

Get-ChildItem -Recurse -Include *.csproj -Path $inputDir | FindBadContent
从MsBuild调用此函数:

<Target Name="FindBadContentNodes">
  <Exec Command="Powershell FindBadContentNodes.ps1 -inputDir path\to\sourceDir"/>
</Target>
请注意,您提到了这些属性,或者最好在生成过程中重写这些属性。我不想使用这样的解决方案:您只是在隐藏问题并依靠CI生成正确的构建,因此使用just-VS的本地构建将不同。Imo使构建失败会更好,特别是因为大多数CI系统都有一种通知负责开发的开发人员的方法,所以应该快速应用修复


另一种可能是让CI应用修复程序,然后提交更改,这样至少每个人都有正确的版本。

IIRC在Visual Studio中有一种方法可以设置文件扩展名,以在默认情况下执行某些操作,就像.config文件总是设置为内容并复制到输出目录一样

因此,可以对.sql文件和其他文件执行相同的操作,这些文件都希望以这种方式进行设置。快速搜索使我想到:

有关部分:

可以在中配置文件类型的默认生成操作 登记处。但是,我们没有手动入侵注册表,而是使用 更好的方法:pkgdef文件一篇关于pkgdef的好文章 文件夹。实际上,pkdef是与.reg类似的配置文件 定义注册表项和值的文件,这些注册表项和值将自动 合并到真实注册表中的正确位置。如果pkgfile 如果删除,更改将自动撤消。这样,你就可以放心了 在不破坏任何东西的情况下修改注册表–或 至少,修复损坏很容易

最后,这里是一个如何更改默认构建操作的示例 文件类型的类型:

1:[$RootKey$\Projects{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\FileExtensions.spark]

2:DefaultBuildAction=Content键中的Guid引用项目类型。在这种情况下,“{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}”表示“C项目”。这里可以找到相当全面的项目类型GUID列表。尽管它没有明确地涵盖VisualStudio2010,但GUI也适用于当前版本。顺便说一下,我们可以在这里使用C作为项目类型,因为基于C的MVC项目实际上是C项目和web应用程序项目。对于Visual Basic,您需要 请改用“{F184B08F-C81C-45F6-A57F-5ABD9991F28F}”

$RootKey$是可视的真实注册表项的抽象 Studio将配置存储在以下位置: HKEY\ U当前\用户\软件\ Microsoft\VisualStudio\10.0\配置说明: 请勿尝试手动编辑此键下的任何内容,因为它可能是 由Visual Studio随时覆盖

其余的应该是不言自明的:此选项设置默认值 将.spark文件的生成操作设置为“内容”,以便包含这些文件 在出版过程中

您现在需要做的就是将这段文本放入一个包含 扩展名pkgdef,放在下面的某个地方 %PROGRAMFILESx86%\Microsoft Visual Studio 64位系统或%PROGRAMFILESx86%\Microsoft Visual Studio上的10.0\Common7\IDE\Extensions 32位系统和Visual Studio上的10.0\Common7\IDE\扩展将在下次启动时自动加载和应用设置。到 撤消更改,只需删除文件

最后,我附加了一组在中使用的pkgdef文件 为C和C定义“内容”默认生成操作的产品 VB分别为.spark、.brail、.brailjs和.less文件编程。 下载它们,将它们保存在Extensions文件夹中的某个位置,然后 很好

作者还说,他构建了一个实用程序来帮助您完成所有这些:


IIRC在VisualStudio中有一种方法可以设置文件扩展名,以便在默认情况下执行某些操作,就像.config文件总是设置为“内容”并复制到输出目录一样

因此,可以对.sql文件和其他文件执行相同的操作,这些文件都希望以这种方式进行设置。快速搜索使我想到:

有关部分:

可以在中配置文件类型的默认生成操作 登记处。但是,我们没有手动入侵注册表,而是使用 更好的方法:pkgdef文件一篇关于pkgdef的好文章 文件夹。实际上,pkdef是与.reg类似的配置文件 定义注册表项和值的文件,这些注册表项和值将自动 合并到真实注册表中的正确位置。如果pkgfile 如果删除,更改将自动撤消。这样,你就可以放心了 在不破坏任何东西的情况下修改注册表–或 至少,修复损坏很容易

最后,这里是一个如何更改默认构建操作的示例 文件类型的类型:

1:[$RootKey$\Projects{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\FileExtensions.spark]

2:DefaultBuildAction=Content键中的Guid引用项目类型。在这种情况下,“{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}”表示“C项目”。这里可以找到相当全面的项目类型GUID列表。尽管它没有明确地涵盖VisualStudio2010,但GUI也适用于当前版本。顺便说一下,我们可以在这里使用C作为项目类型,因为基于C的MVC项目实际上是C项目和web应用程序项目。对于Visual Basic,您应该使用“{F184B08F-C81C-45F6-A57F-5ABD9991F28F}”

$RootKey$是可视的真实注册表项的抽象 Studio将配置存储在以下位置: HKEY\ U当前\用户\软件\ Microsoft\VisualStudio\10.0\配置说明: 请勿尝试手动编辑此键下的任何内容,因为它可能是 由Visual Studio随时覆盖

其余的应该是不言自明的:此选项设置默认值 将.spark文件的生成操作设置为“内容”,以便包含这些文件 在出版过程中

您现在需要做的就是将这段文本放入一个包含 扩展名pkgdef,放在下面的某个地方 %PROGRAMFILESx86%\Microsoft Visual Studio 64位系统或%PROGRAMFILESx86%\Microsoft Visual Studio上的10.0\Common7\IDE\Extensions 32位系统和Visual Studio上的10.0\Common7\IDE\扩展将在下次启动时自动加载和应用设置。到 撤消更改,只需删除文件

最后,我附加了一组在中使用的pkgdef文件 为C和C定义“内容”默认生成操作的产品 VB分别为.spark、.brail、.brailjs和.less文件编程。 下载它们,将它们保存在Extensions文件夹中的某个位置,然后 很好

作者还说,他构建了一个实用程序来帮助您完成所有这些:


扩展@stijn answer,而不是使用正则表达式,使用原生xml解析要容易得多

这是我建议的文件,它还支持自定义仅在文件名上使用正则表达式来评估哪些文件的功能

param([String]$Path, [string]$IncludeMatch, [switch]$AllowPreserve)

Function Test-BadContentExists
{
    param (
        [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [Alias("FullName")]
        [string[]]$Path,
        [string]$IncludeMatch,
        [switch]$AllowPreserve
        )
  [xml]$proj = Get-Content -Path $Path
  $ContentNodes = ($proj | Select-Xml "//Content|//n:Content" -Namespace @{n='http://schemas.microsoft.com/developer/msbuild/2003'}).Node
  if (![string]::IsNullOrEmpty($IncludeMatch)) {
    $ContentNodes = $ContentNodes | Where-Object -Property Include -Match $IncludeMatch
  }
  #remove the always nodes
  $ContentNodes = $ContentNodes | Where-Object -Property CopyToOutputDirectory -ne 'Always'
  #optionally remove the preserve nodes
  if ($AllowPreserve) {
    $ContentNodes = $ContentNodes | Where-Object -Property CopyToOutputDirectory -ne 'PreserveNewest'
  }
  if($ContentNodes)
  {
    write-output "Found file with bad content node:"
    write-output ($ContentNodes | Select-Object Include,CopyToOutputDirectory | sort Include | Out-String)

    exit 1
  }
}

[hashtable]$Options = $PSBoundParameters
[void]$Options.Remove("Path")
Get-ChildItem -Recurse -Include *.csproj -Path $Path | Test-BadContentExists @Options
并使用参数调用它:

<Target Name="FindBadContentNodes">
  <Exec Command="Powershell FindBadContentNodes.ps1 -inputDir path\to\sourceDir -IncludeMatch '^Upgrade.*\.(sql|xml)$'"/>
</Target>
生成输出示例:

1>  "Build Dir: C:\Source\RPS\MRM BI\MRMBI-Setup\MRMBI-Schema\bin\Debug"
1>  "Sol Dir: C:\Source\RPS\MRM BI\MRMBI-Setup\"
1>  "Proj Dir: 'C:\Source\RPS\MRM BI\MRMBI-Setup\MRMBI-Schema\"
1>  
1>  Found file with bad content node:
1>  
1>  Include                          CopyToOutputDirectory
1>  -------                          ----------------------
1>  Upgrades\V17.09\myfile1.sql                          
1>  Upgrades\V20.05\myfile2.sql      PreserveNewest       
1>  

扩展@stijn answer,而不是使用正则表达式,使用原生xml解析要容易得多

这是我建议的文件,它还支持自定义仅在文件名上使用正则表达式来评估哪些文件的功能

param([String]$Path, [string]$IncludeMatch, [switch]$AllowPreserve)

Function Test-BadContentExists
{
    param (
        [parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
        [Alias("FullName")]
        [string[]]$Path,
        [string]$IncludeMatch,
        [switch]$AllowPreserve
        )
  [xml]$proj = Get-Content -Path $Path
  $ContentNodes = ($proj | Select-Xml "//Content|//n:Content" -Namespace @{n='http://schemas.microsoft.com/developer/msbuild/2003'}).Node
  if (![string]::IsNullOrEmpty($IncludeMatch)) {
    $ContentNodes = $ContentNodes | Where-Object -Property Include -Match $IncludeMatch
  }
  #remove the always nodes
  $ContentNodes = $ContentNodes | Where-Object -Property CopyToOutputDirectory -ne 'Always'
  #optionally remove the preserve nodes
  if ($AllowPreserve) {
    $ContentNodes = $ContentNodes | Where-Object -Property CopyToOutputDirectory -ne 'PreserveNewest'
  }
  if($ContentNodes)
  {
    write-output "Found file with bad content node:"
    write-output ($ContentNodes | Select-Object Include,CopyToOutputDirectory | sort Include | Out-String)

    exit 1
  }
}

[hashtable]$Options = $PSBoundParameters
[void]$Options.Remove("Path")
Get-ChildItem -Recurse -Include *.csproj -Path $Path | Test-BadContentExists @Options
和ca 使用以下参数对其进行排序:

<Target Name="FindBadContentNodes">
  <Exec Command="Powershell FindBadContentNodes.ps1 -inputDir path\to\sourceDir -IncludeMatch '^Upgrade.*\.(sql|xml)$'"/>
</Target>
生成输出示例:

1>  "Build Dir: C:\Source\RPS\MRM BI\MRMBI-Setup\MRMBI-Schema\bin\Debug"
1>  "Sol Dir: C:\Source\RPS\MRM BI\MRMBI-Setup\"
1>  "Proj Dir: 'C:\Source\RPS\MRM BI\MRMBI-Setup\MRMBI-Schema\"
1>  
1>  Found file with bad content node:
1>  
1>  Include                          CopyToOutputDirectory
1>  -------                          ----------------------
1>  Upgrades\V17.09\myfile1.sql                          
1>  Upgrades\V20.05\myfile2.sql      PreserveNewest       
1>  

谢谢,这看起来很有趣。我以前没听说过这个,但一定会看一看!B.T.W感谢您在RoundhousE上所做的工作-它对我们非常有效,并且使部署数据库更改变得轻而易举。@RobBird RH是我最喜欢的创作之一。:谢谢,这看起来很有趣。我以前没听说过这个,但一定会看一看!B.T.W感谢您在RoundhousE上所做的工作-它对我们非常有效,并且使部署数据库更改变得轻而易举。@RobBird RH是我最喜欢的创作之一。: