MSBuild 4.5忽略了项目依赖性

时间:2012-12-06 21:10:22

标签: msbuild .net-4.5

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

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

例如,我有三个库(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一起使用的唯一方法是返回并选择被跳过的项目,但由于实际的解决方案对依赖链有点复杂,我不想要每次我们使用新项目或依赖项更新解决方案时,都会尝试手动管理配置。它应该是自动的。

我正在做什么的想法?

6 个答案:

答案 0 :(得分:4)

如果您仍未解决此问题,请尝试一些盲目的镜头:

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

  2. 确保YourWebsiteProject和你的libs(B和C)有正确的引用 - 在项目上,而不是在另一个项目文件夹中的dll上(最简单的方法来修复它 - 从B引用中删除C并再次重新添加它) ,只需确保您正在添加项目引用而不是浏览到bin \ Debug for very C.dll)。问题还存在吗?

  3. 如果你能提供详细甚至诊断的msbuild日志(在switch / ds / v:diag之后添加到你的msbuild命令行,然后在某处共享teamcity完整构建日志或管道命令行日志到文件)或者某些示例项目设置在哪里我可以重现这种行为 - 它可以帮助解决问题。

答案 1 :(得分: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 - 由于微软的这一改变几乎使这项工作脱轨!

干杯。

答案 2 :(得分:3)

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

(即使我警告他们更改会破坏构建,但Microsoft可能会对Connect bug I filed against MSBuild 4.0做出此更改。)

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

<PropertyGroup>

答案 3 :(得分:2)

我遇到了完全相同的问题。我设法找到的这两种解决方法从长远来看都是不可接受的,但是它们确实克服了使用新的4.5堆栈使我们的构建过程老化的初始问题。

  1. 使用文件引用交换项目引用
  2. 创建复合构建配置
  3. 我选择了#2,因为文件引用意味着开发人员会失去实时智能感知等。

    复合配置就是这样:

    • 发布服务器 - &gt; 所有服务器项目
    • 发布消费者 - &gt; “发布服务器”+客户项目

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

    两者都很难看,但至少它让我脱离了紧张的地方。

    马特

答案 4 :(得分:0)

我明白这个问题有多种原因。我在这里尝试了大部分解决方案。我无法更改我们的构建服务器以使用PS脚本,以便解决方案。没有什么我可以尝试的。

最后,我删除了我的解决方案并开始了一个新的解决方案。新的解决方案奏效了。用工作溶液对破碎的溶液进行分析后,我发现原来的解决方案是缺少线条。无法编译的每个依赖项都缺少此行:

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

注1:GUID将从依赖变为依赖。

注2:您可以在&#34; GlobalSection(ProjectConfigurationPlatforms)= postSolution&#34;下找到类似这样的行。解决方案文件的一部分。

我的build.proj文件说build&#34; Release&#34;使用&#34;任何CPU&#34;平台。因为MSBuild无法找到此行,所以它没有构建此依赖项。这会导致&#34; 错误CS0246:无法找到类型或命名空间&#34;消息。

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

答案 5 :(得分:0)

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