问题描述
我有一个 VS2010 解决方案文件,其中包含 20 多个项目,其中一些项目依赖于解决方案中的其他项目.
I have a VS2010 solution file with over 20 projects in it, and some of the projects have dependencies on other projects from within the solution.
我也为不同的目的设置了多个构建配置,但我已经精简了要构建的项目,只包含最少数量的项目.
I also have multiple build configurations set up for different purposes, but I've trimmed down the projects to be built to just include the bare minimum number of projects.
例如,我有三个库(A、B 和 C)、一个网站项目和一个网站部署项目(VS2010).该网站引用了库 A 和 B,而 B 又引用了 C.我的构建配置只检查了网站和部署项目.当我从解决方案的属性中检查项目依赖项时,网站正确列出了库,并且 B 显示了 C,正如预期的那样.
For example, I have three libraries (A, B, and C), a website project, and a website deployment project (VS2010). The website has references to libraries A and B, and B in turn has a reference to C. My build configuration only has the website and the deployment project checked. When I check the project dependencies from the solution's properties, the website correctly lists the libraries, and B shows C, as expected.
当我在 VS2010 中针对我的构建配置运行构建时,它完全正常,但是当我在指定我的配置的解决方案上运行 MSBuild 时(如下所示),它只会导致一堆错误.
When I run the build against my build config from within VS2010, it works completely fine, but when I run MSBuild on the solution specifying my configuration (as follows), it just results in a bunch of errors.
msbuild.exe mysolution.sln/t:Build/p:configuration=MyConfig
这是我得到的错误示例:
Here's an example of the errors I get:
ServicesIService.cs(11,63):错误 CS0246:找不到类型或命名空间名称优先级"(您是否缺少 using 指令或程序集引用?)
我注意到在我的构建服务器 (TeamCity v7.1.2) 上发生了这种情况,但我可以在多台机器上重现它,并且我将其缩小为 MSBuild 本身的问题.
I noticed this happening on my build server (TeamCity v7.1.2), but I can reproduce it on multiple machines, and I narrowed it down to an issue with MSBuild, itself.
它只在我安装 .NET 4.5(和 2 个安全补丁)后才开始发生,所以我卸载了它,重新安装了 .NET 4.0(带补丁),因为它也被删除了,然后尝试了相同的命令,它工作得很好.
It only started happening after I installed .NET 4.5 (and 2 security patches), so I uninstalled it, reinstalled .NET 4.0 (with patches) since it was also removed, then tried the same command, and it worked just fine.
这让我相信使用 .NET 4.5 的 MSBuild 中的某些内容发生了更改或损坏,但他们的文档中似乎没有提及这种更改.
This leads me to believe that something was changed or broken in MSBuild with .NET 4.5, but nothing in their documentation seems to talk about this kind of change.
MSBuild 4.5 文档:http://msdn.microsoft.com/en-us/library/hh162058.aspx
MSBuild 4.5 documentation: http://msdn.microsoft.com/en-us/library/hh162058.aspx
我什至尝试将 BuildProjectDependencies=true
传递给 MSBuild,结果显示它跳过了其他项目,因为它们没有在配置管理器中被选中,这是正确且有意的.
I've even tried passing BuildProjectDependencies=true
to MSBuild, and it comes up stating that it skipped the other projects because they were not selected in the configuration manager, which is correct and intentional.
我让它与 MSBuild 4.5 一起工作的唯一方法是返回并选择被跳过的项目,但由于实际解决方案的依赖链有点复杂,我不想不得不每次我们使用新项目或依赖项更新解决方案时,尝试手动管理配置.它应该是自动的.
The only way I got it to work with MSBuild 4.5 was to go back and select the projects that were being skipped, but since the actual solution is a bit more complex with the dependency chain, I don't want to have to try to manage the configurations manually each time we update the solution with new projects or dependencies. It SHOULD be automatic.
对我正在做的事情有什么想法吗?
Any ideas with what I'm doing?
推荐答案
我想我会更新我以前的答案,因为我花了很多时间和精力来创建自己的解决方案来解决这个问题.解决方法比简单地解决问题要全面一些,但我已尝试既消除问题又使自己免受未来此类冲击的影响.
I thought I'd update my previous answer, as I've spent a lot of time and effort creating my own workaround to this problem. The work around is a bit more comprehensive than simply living with the problem, but I've attempted to both eliminate the issue and insulate ourselves against future shocks like this.
MSBuild 已被降级,无法使用解决方案、配置或其他方式.MSBuild 只是被要求单独编译项目.发生这种情况的顺序由 Powershell 脚本计算,该脚本会解析我们的解决方案和项目,以制定出最佳的即时构建执行计划.
MSBuild has been demoted from working with solutions, configurations or otherwise. MSBuild is just asked to compile projects in isolation. The order this happens is calculated by a Powershell script that parses ours solutions and projects to work out the best Just-In-Time build execution plan.
这个的关键(我认为你可能会觉得有帮助)是以下片段:
Key to this (and I think you might find helpful) are the following snippets:
确定我的解决方案
我有一个包含我平台中所有解决方案的列表,并且我基本上对每个解决方案进行了迭代.
I have a list of all the solutions in my platform, and I essentially iterate over each of these.
$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 文件.
I have some logic that helps to translate this into actual physical paths, but its very bespoke to our needs, so I won't list it here. Sufficed to say, from this list, I can find the .sln file I need to parse.
解析项目的解决方案文件
对于每个解决方案,我都会读取 .sln 文件并尝试提取其中包含的所有项目,我将需要稍后构建这些项目.
With each solution, I read the .sln file and attempt to extract all the projects contained within that I will need to build later.
所以首先,确定我的所有项目
So firstly, identify all projects in my
$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
然后将其转换为一个不错的项目列表,我可以稍后对其进行迭代.
And then translate this into a nice list of projects that I can iterate over later.
$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 项目
从我对解决方案文件的解析来看,我现在有一个每个解决方案的项目列表,其中重要的是包含从解决方案根目录查找项目的相关文件路径.
From my parsing of the solution file, I now have a list of projects per solution, which crucially contains the relative File Path from the solution root to find the project.
$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 }
}
}
而且,既然我们正在做,也可以获取一些基本的输出信息
这在迭代我要构建的项目列表时很方便,因为知道将输出推送到哪里,或者从哪里找到依赖项的输出.
This is handy when it comes to iterating my list of projects to build, as know where to push the output, or where to find the output of a dependent.
$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
最后
我们只需要确保我们没有任何重复,所以我只记录这个特定代码项目的不同依赖:
We just need to make sure we don't have any duplicates, so I record just the distinct dependencies of this particular code project:
$dependendents = @();
if ($references -ne $null)
{
$buildAction.project.dependencies += $references | where {(!([string]::IsNullOrEmpty($_))) -and ($_ -match "OurCompanyPrefix.(.*)")} | % { $_.ToLower()} | Select -unique
}
我希望这能为您提供足够的信息来解析您的 SLN 和 PROJ 文件.您将如何选择捕获和存储这些信息,我认为完全取决于您.
I would hope this provides you with enough information for parsing your SLN and PROJ files. How you would choose to capture and store this information I think would depend entirely up to you.
我正在写一篇关于此的深入博客文章,其中将包含我在上面逃避的所有装饰和框架.该帖子尚未准备好,但我将从较早的帖子中链接到它:http://automagik.piximo.me/2013/02/just-in-time-compilation.html - 由于微软的这一变化几乎使这项工作脱轨!
I'm in the middle of writing quite an in-depth blog post about this, which will contain all the trimmings and framework I've eluded to above. The post isn't ready yet, but I will be linking to it from an earlier post : http://automagik.piximo.me/2013/02/just-in-time-compilation.html - Since this change by Microsoft nearly derailed this work!
干杯.
这篇关于MSBuild 4.5 忽略项目依赖项的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!