我正在研究的Build framework提供了许多默认步骤,以便用户只需提供描述其构建过程的配置文件。然后,框架将根据可用信息调用正确的构建步骤。
标准方法是使用顶级项目文件导入所有其他必要文件。但是这不起作用,因为在流程开始时我们不知道我们需要哪些导入,因此我们必须在需要时动态导入项目文件。对此的解决方案是创建一个ItemGroup
,其中包含要执行的脚本的文件路径,然后执行它们,例如:
<!-- Simplified version of the approach ... -->
<ItemGroup>
<ThingsToBuild Include="a" />
<ThingsToBuild Include="b" />
</ItemGroup>
<MsBuild Projects="@(ThingsToBuild)" />
我目前正在尝试解决的主要问题是某些步骤会收集后续步骤中使用的信息,例如版本或VCS信息。稍后,此信息可通过ItemGroup
替换模板参数。由于这些ItemGroups
在构建过程中发生了变化,因此MsBuild不会重新加载以前导入的项目文件,因此任何具有ItemGroup信息的项目文件在更改后都不会重新加载。所以我们可以:
每次我们需要时,根据存储在某处的信息生成项目。注意,有时信息是直接获得的,例如,存储在属性中的文件路径,有时它更复杂,例如从文件中获取的版本号或VCS信息。
<PropertyGroup>
<MyProperty>At $TIME$ the version is: $VERSION$.</MyProperty>
</PropertyGroup>
<Target Name="a">
<!--
Use ItemGroup if you want to add straight forward items
and the item group is used in the same target
-->
<ItemGroup>
<MyItem Include="a" />
</ItemGroup>
<!--
Use CreateItem if you need to to nasty manipulation or
the ItemGroup is used in another target
-->
<CreateItem Include="$(MyProperty.Replace(',', ';').ToLower().Split(';'))">
<Output ItemName="MyItem" TaskParameter="Include" />
</CreateItem>
<!-- Use the MyItem items here -->
<TemplateText Template="$(MyProperty)" Tokens="@(MyItem)">
<Output PropertyName="MyTranslatedProperty" TaskParameter="Result" />
</TemplateText>
</Target>
在信息可用时生成包含ItemGroup
的文件,并在每次需要时导入这些文件
第一种方法更简单,因为您可以将所有项目加载到同一个MsBuild实例中。然而它也慢得多,因为我们每次需要时都必须生成项目组,结果很多次。
由于MsBuild处理项目文件导入的方式,第二种方法要求将每个构建步骤加载到MsBuild的独立实例中,否则永远不会重新加载更新的文件。
即使这样,由于我们使用项目组的次数很多,第二种方法比第一种方法快四倍。
然而,它不够快,为此我想回到单个MsBuild实例,因为应用程序加载时间可能非常高,具体取决于硬件功能。但是,为了加载ItemGroups
工作,我需要在它自己的小'bubble'/ appdomain / (某物)中执行每个项目。
我尝试过使用ProjectCollection和Project类,但显然您无法在正在运行的版本中调用Project.Build()。这会导致InvalidOperationException。
所以现在问题是“重新加载”包含项目组的项目文件的最佳方法是什么,以便每个步骤都获得最新版本,而不是之前加载的版本
----编辑----
可以在sample repository中找到行为的示例。