10

我有一个 VS2010 解决方案文件,其中包含 20 多个项目,其中一些项目依赖于解决方案中的其他项目。

我还为不同的目的设置了多个构建配置,但我已经减少了要构建的项目,只包含最少数量的项目。

例如,我有三个库(A、B 和 C)、一个网站项目和一个网站部署项目(VS2010)。该网站引用了库 A 和 B,而 B 又引用了 C。我的构建配置仅检查了网站和部署项目。当我从解决方案的属性中检查项目依赖项时,网站正确列出了库,并且 B 按预期显示了 C。

当我在 VS2010 中针对我的构建配置运行构建时,它完全可以正常工作,但是当我在指定我的配置的解决方案上运行 MSBuild 时(如下所示),它只会导致一堆错误。

msbuild.exe mysolution.sln /t:Build /p:configuration=MyConfig

这是我得到的错误示例:

Services\IService.cs(11,63): error CS0246: The type or namespace name 'Priority' could not be found (are you missing a using directive or an assembly reference?)

我注意到这发生在我的构建服务器(TeamCity v7.1.2)上,但我可以在多台机器上重现它,我将其缩小到 MSBuild 本身的问题。

它只是在我安装 .NET 4.5(和 2 个安全补丁)后才开始发生,所以我卸载了它,重新安装了 .NET 4.0(带有补丁),因为它也被删除了,然后尝试了相同的命令,它工作得很好。

这让我相信 .NET 4.5 在 MSBuild 中发生了一些变化或损坏,但他们的文档中似乎没有任何内容谈论这种变化。

MSBuild 4.5 文档:http: //msdn.microsoft.com/en-us/library/hh162058.aspx

我什至尝试过传递BuildProjectDependencies=true给 MSBuild,它指出它跳过了其他项目,因为它们没有在配置管理器中被选中,这是正确且有意的。

我让它与 MSBuild 4.5 一起工作的唯一方法是返回并选择被跳过的项目,但由于依赖链的实际解决方案有点复杂,我不想尝试管理每次我们使用新项目或依赖项更新解决方案时手动配置。它应该是自动的。

我在做什么有什么想法吗?

4

6 回答 6

4

如果您仍然没有解决这个问题,让我们尝试一些盲拍:

  1. Msbuild 已确认错误,它有时会根据所遵循的依赖项生成错误的构建顺序。尝试构建的不是整个解决方案,而是您想要构建的确切项目msbuild.exe YourWebsiteProject.csproj /t:Clean;Build /p:configuration=MyConfig。问题仍然存在吗?

  2. 确保 YourWebsiteProject 和您的库(B 和 C)具有正确的引用 - 在项目上,而不是在另一个项目文件夹中的 dll 上(修复该问题的最简单方法 - 从 B 引用中删除对 C 并重新添加它,只需确保您是添加项目引用而不是浏览到 bin\Debug 以获得非常 C.dll)。问题还在吗?

如果您可以提供详细甚至诊断性的 msbuild 日志(在开关 /ds /v:diag 之后添加到您的 msbuild 命令行,然后在某处共享 teamcity 完整构建日志或将命令行日志通过管道传输到文件)或一些我可以重现的示例项目集这种行为 - 它可以帮助解决问题。

于 2012-12-09T06:19:20.170 回答
4

我想我会更新我之前的答案,因为我花了很多时间和精力来创建自己的解决方法来解决这个问题。解决方法比简单地解决问题要全面一些,但我试图既消除问题又使自己免受未来此类冲击的影响。

MSBuild 已从使用解决方案、配置或其他方式降级。MSBuild 只是被要求单独编译项目。发生这种情况的顺序由 Powershell 脚本计算,该脚本解析我们的解决方案和项目,以制定出最佳的即时构建执行计划。

关键(我认为您可能会发现有帮助)是以下片段:

确定我的解决方案

我在我的平台上有一个所有解决方案的列表,并且我基本上迭代了其中的每一个。

$buildPlan = (
@{
    solutions = (
        @{
            name      = "DataStorage"
            namespace = "Platform.Databases"
        },
        @{
            name      = "CoreFramework"
        },
        @{
            namespace = "Platform.Server"
            name      = "Application1"
        },
        @{
            namespace = "Platform.Server"
            name      = "Application2"
        },
        @{
            namespace = "Platform.Client"
            name      = "Application1"
        }
     )
})

我有一些逻辑可以帮助将其转化为实际的物理路径,但它非常符合我们的需求,所以我不会在这里列出。可以说,从这个列表中,我可以找到我需要解析的 .sln 文件。

解析项目的解决方案文件

对于每个解决方案,我都会阅读 .sln 文件并尝试提取其中包含的所有项目,我将在以后需要构建这些项目。

所以首先,确定我的所有项目

$solutionContent = Get-Content $solutionFile

$buildConfigurations += Get-Content $solutionFile | Select-String  "{([a-fA-F0-9]{8}-([a-fA-F0-9]{4}-){3}[a-fA-F0-9]{12})}\.(Release.*)\|Any CPU\.Build" | % {
        New-Object PSObject -Property @{
            Name = $_.matches[0].groups[3].value.replace("Release ","");
            Guid = $_.matches[0].groups[1].value
          }

    }  | Sort-Object Name,Guid -unique

然后将其翻译成一个很好的项目列表,我可以稍后对其进行迭代。

$projectDefinitions = $solutionContent | 
      Select-String 'Project\(' |
        ForEach-Object {
          $projectParts = $_ -Split '[,=]' | ForEach-Object { $_.Trim('[ "{}]') };
          $configs = ($buildConfigurations | where  {$_.Guid -eq $projectParts[3]} | Select-Object Name)

          foreach ($config in $configs)
          {
              $santisiedConfig = if ([string]::IsNullOrEmpty($config.Name)){"Release"}else{$config.Name}
              if ($projectParts[1] -match "OurCompanyPrefix.")
              {
                  New-Object PSObject -Property @{
                    Name = $projectParts[1];
                    File = $projectParts[2];
                    Guid = $projectParts[3];
                    Config =  $santisiedConfig
                  }
              }
          }
    } 

加载 Visual Studio 项目

从我对解决方案文件的解析中,我现在有一个每个解决方案的项目列表,其中关键包含从解决方案根目录找到项目的相对文件路径。

$projectDefinition = [xml](Get-Content $csProjectFileName)
$ns = @{ e = "http://schemas.microsoft.com/developer/msbuild/2003" }
$references = @();

1) 识别外部项目参考

$references += Select-Xml -Xml $projectDefinition -XPath "//e:Project/e:ItemGroup/e:Reference" -Namespace $ns | % {$_.Node} | where {$_.Include -match "OurCompanyPrefix" -and $_.HintPath -notmatch "packages"}  | % {$_.Include}

2) 识别内部项目参考

$references += Select-Xml -Xml $projectDefinition -XPath "//e:Project/e:ItemGroup/e:ProjectReference" -Namespace $ns | % { $_.Node.Name }

3) 遵循“构建后”事件作为外部参考

$references += Select-Xml -Xml $projectDefinition -XPath "//e:Project/e:PropertyGroup/e:PostBuildEvent" -Namespace $ns | where {(!([String]::IsNullOrEmpty($_.Node.InnerText)))} | % {

            $postBuildEvents = $_.Node.InnerText.Split("`n")
            $projectsReferencedInPostBuildEvents = $postBuildEvents | Select-String "\(SolutionDir\)((\w|\.)*)" | % {$_.Matches[0].Groups[1].Value}
            if ($projectsReferencedInPostBuildEvents -ne $null)
            {
                Write-Output $projectsReferencedInPostBuildEvents | % { $matchedProject = $_; ($releaseConfiguation | ? {$_.File -match $matchedProject}).Name }  
            }

        }

而且,既然我们在这里,也可以获得一些基本的输出信息

在迭代我要构建的项目列表时,这很方便,因为知道在哪里推送输出,或者在哪里找到依赖项的输出。

$assemblyName = (Select-Xml -Xml $projectDefinition -XPath "//e:Project/e:PropertyGroup/e:AssemblyName" -Namespace $ns).Node.InnerText
$outputPath  = (Select-Xml -Xml $projectDefinition -XPath "//e:Project/e:PropertyGroup[contains(@Condition,'Release|')]/e:OutputPath" -Namespace $ns).Node.InnerText

最后

我们只需要确保我们没有任何重复项,所以我只记录这个特定代码项目的不同依赖项:

$dependendents = @();
if ($references -ne $null)
{
    $buildAction.project.dependencies += $references | where {(!([string]::IsNullOrEmpty($_))) -and ($_ -match "OurCompanyPrefix\.(.*)")} | % { $_.ToLower()} | Select -unique
}    

我希望这可以为您提供足够的信息来解析您的 SLN 和 PROJ 文件。我认为您将如何选择捕获和存储这些信息完全取决于您。

我正在写一篇关于此的相当深入的博客文章,其中将包含我上面没有提到的所有修饰和框架。这篇文章还没有准备好,但我会从之前的一篇文章中链接到它:http: //automagik.piximo.me/2013/02/just-in-time-compilation.html - 因为微软的这个改变几乎使这项工作出轨!

干杯。

于 2013-05-08T08:55:55.783 回答
3

在 .NET 4.5 中, C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets 中OnlyReferenceAndBuildProjectsEnabledInSolutionConfiguration属性的默认值已从 更改falsetrue。顾名思义,此属性会导致 MSBuild 忽略对从构建配置中排除的项目的引用。

(微软可能会做出此更改以响应我针对 MSBuild 4.0 提交的 Connect 错误,尽管我警告他们更改会破坏构建。)

解决方法很简单:将属性设置回每个项目false的第一<PropertyGroup>部分:

<PropertyGroup>
    ...
    <OnlyReferenceAndBuildProjectsEnabledInSolutionConfiguration>false</OnlyReferenceAndBuildProjectsEnabledInSolutionConfiguration>
</PropertyGroup>
于 2014-08-05T16:46:29.137 回答
2

我遇到了完全相同的问题。从长远来看,我设法找到的两种变通方法都是不可接受的,但它们确实克服了最初的问题,即我们的构建过程使用新的 4.5 堆栈时会变旧。

  1. 将项目引用与文件引用交换
  2. 创建复合构建配置

我选择了#2,因为文件引用意味着开发人员会失去实时智能感知等。

复合配置很简单:

  • 发布服务器 ->所有服务器项目
  • 发布消费者-> “发布服务器”+客户端项目

问题似乎是,如果一个项目未包含在当前/活动构建配置中,它将不会将其作为引用的依赖项包含在内。因此,通过将依赖项添加到配置中,项目至少可以编译。

两者都很丑,但至少它让我摆脱了困境。

马特

于 2013-03-08T09:13:48.150 回答
0

我发现 MSBuild 正在从解决方案构建项目,它们在 .sln 文件中声明的顺序。因此,如果您使用文本编辑器重新排序它们,您可以修复 MSBuild 的顺序。

于 2017-04-06T06:46:29.377 回答
0

我觉得这个问题有多种原因。我在这里尝试了大多数解决方案。我无法更改我们的构建服务器以使用 PS 脚本,因此该解决方案已失效。我无法尝试任何工作。

最后,我删除了我的解决方案并开始了一个新的解决方案。新的解决方案奏效了。在将损坏的解决方案与工作解决方案进行比较后,我发现原始解决方案缺少行。每个无法编译的依赖项都缺少这一行:

{A93FB559-F0DB-4F4D-9569-E676F59D6168}.Release|Any CPU.Build.0 = Release|Any CPU

注意 1:GUID 将从依赖项更改为依赖项。

注意 2:您可以在解决方案文件的“GlobalSection(ProjectConfigurationPlatforms) = postSolution”部分下找到类似这样的行。

我的 build.proj 文件说使用“Any CPU”平台构建“Release”。因为 MSBuild 找不到这一行,所以它没有构建这个依赖项。这会导致“错误 CS0246:找不到类型或命名空间”消息。

如果您好奇,有人将此解决方案设置为“x86”平台(这对我们来说是错误的)。我将其更改为“任何 CPU”(以及其他一些更改)。Visual Studio 没有将相应的行添加到解决方案文件中。在 IDE 中一切正常,但 MSBuild 开始抛出错误。

于 2016-03-02T14:59:52.000 回答