如何在项目中强制文件,以便它们始终复制到输出目录

时间:2014-03-09 23:20:26

标签: visual-studio msbuild csproj octopus-deploy roundhouse

我正在使用visual studio c#库项目来包含作为部署工件所需的静态资源。 (在我的例子中,使用RoundhousE和Octopus部署的组合运行的SQL文件)。按照惯例,项目中的所有文件都必须设置其属性,以便“构建操作”为“内容”,“复制到输出目录”为“始终复制”。

如果团队中有人添加了文件但忘记设置这些属性,我们会看到部署错误。这通常是在内部环境中获取的,但我希望找到一种方法在CI构建中强制执行此操作。

那么有没有办法让构建失败或者更好的是在使用MS Build任务构建期间仍覆盖这些属性?我是以错误的方式解决这个问题吗?欢迎任何建议。

3 个答案:

答案 0 :(得分:2)

您将不得不解析项目文件并检查Content 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来生成正确的构建,因此使用VS的本地构建将不一样。 Imo使构建失败更好,特别是因为大多数CI系统都有办法通知负责的开发人员,因此应该快速应用修复。

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

答案 1 :(得分:2)

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

因此可以对.sql文件(以及他们希望以这种方式设置的其他文件)执行相同的操作。快速搜索让我想到了这一点:http://blog.andreloker.de/post/2010/07/02/Visual-Studio-default-build-action-for-non-default-file-types.aspx

相关部分:

  

可以在。中配置文件类型的默认构建操作   注册表中。但是,我们不是手动黑客入侵注册表,而是使用   更好的方法: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#项目”。可以在此处找到相当全面的项目类型指南列表。虽然它没有明确涵盖Visual Studio 2010,但Guids也适用于当前版本。顺便说一句,我们可以在这里使用C#作为项目类型,因为基于C#的MVC项目实际上是C#项目(和Web应用程序项目)。对于Visual Basic,您将改为使用“{F184B08F-C81C-45F6-A57F-5ABD9991F28F}”。

     

$ RootKey $是对Visual的真实注册表项的抽象   Studio将配置存储在:   HKEY_CURRENT_USER \ Software \ Microsoft \ VisualStudio \ 10.0_Config(注意:   不要尝试手动编辑此键下的任何内容   Visual Studio随时覆盖。

     

其余部分应该是自解释的:此选项设置默认值   将.spark文件的操作构建为“内容”,因此包含这些文件   在出版过程中。

     

现在你需要做的就是将这段文字放入一个文件中   扩展名为pkgdef,把它放在某处   %PROGRAMFILES(x86)%\ Microsoft Visual Studio   10.0 \ Common7 \ IDE \ Extensions(在64位系统上)或%PROGRAMFILES(x86)%\ Microsoft Visual Studio   10.0 \ Common7 \ IDE \ Extensions(在32位系统上)和Visual Studio将在下次启动时自动加载和应用设置。至   撤消更改,只需删除文件。

     

最后,我附上了一堆用于的pkgdef文件   定义C#和“内容”默认构建操作的生产   VB分别为.spark,.brail,.brailjs和.less文件投影。   下载它们,将它们保存在Extensions文件夹中的某个位置即可   很高兴。

作者还说,他建立了一个实用工具来帮助你完成所有这些工作:

http://tools.andreloker.de/dbag

答案 2 :(得分:0)

扩展@stijn答案,而不是使用正则表达式,使用本机xml解析要容易得多。

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

aws route53 list-resource-record-sets \
  --hosted-zone-id $zoneId \
  --query 'ResourceRecordSets[?Name==`abcd.example.com.`]'

并使用参数:

对其进行调用
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

我最终使用了一个预构建事件,然后将此ps1文件放在我的解决方案目录中,这样我就可以将其用于多个项目。

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

示例构建输出:

echo "Build Dir: %cd%"
echo "Sol Dir: $(SolutionDir)"
echo "Proj Dir: '$(ProjectDir)"
echo.
Powershell -NoProfile -Command "& '$(SolutionDir)\FindBadContentNodes.ps1' -Path '$(ProjectDir)' -IncludeMatch '^Upgrade.*\.(sql|xml)$'"