当它的必要,我不介意的东西偶尔重复,但在我的MSBuild真的不知道该怎么过避免重复.它通常意义上不提供"功能"; 一个目标只能被调用一次,甚至通过CallTarget,<Import>只能在Project关卡上工作.
这是一个我试图去"重复"的具体例子:
<Target Name="Tgt1">
<PropertyGroup><Conf1>Twiddle</Conf1><Conf2>Thing</Conf2></PropertyGroup>
<PropertyGroup><xxxxxxxxxxExePath>$(xxxxxxxBuildRoot)\$(Conf1)Console-xxxxxxxxed</xxxxxxxxorExePath></PropertyGroup>
<MSBuild Projects="$(BuildSingleProj)" Targets="Build;Merge"
Properties="Configuration=$(Conf1)$(Conf2);Platform=$(Platform);CompiledFileName=$(CompiledFileName);ProjectName=$(ProjectName);SolutionFile=$(SolutionFile);Root=$(Root);Caller=$(MSBuildProjectFullPath)"/>
<MakeDir Directories="$(xxxxxxxxorExePath)" />
<WriteLinesToFile File="$(xxxxxxxxorExePath)\xxxxxxx.IsPortable.txt" />
<WriteLinesToFile File="$(xxxxxxxxorExePath)\xxxxxxx.Global.Settings.xml" Lines="@(xxxxxxxLicense)" Overwrite="true" />
<Exec Command='$(xxxxxxxxorExePath)\xxxxxxx.exe -a "$(xxxxxxxBuildRoot)\$(Conf1)$(Conf2)-Merged\xxxxxxx.exe" "$(xxxxxxxBuildRoot)\$(Conf1)$(Conf2)-xxxxxxxxed\xxxxxxx.exe"'/>
</Target>
Run Code Online (Sandbox Code Playgroud)
我有四个这样的目标,Tgt1,Tgt2,Tgt3,Tgt4.在只有这四个目标之间的区别就是第一行,定义一个Conf1和Conf2.
我所知道的唯一或多或少可行的重复数据删除思想是将共享代码移动到新目标并通过MSBuild任务调用它.遗憾的是,这需要手动传入loooooong属性字符串,并且此任务使用了相当多的(我计算了11个属性和1个项目组).
另一个要求是我可以使用这些目标的任意子集来调用脚本,例如\t:Tgt2,Tgt3.
是否有任何明智的替代方法只需复制/粘贴这一块代码 - 这不涉及复制庞大的属性列表而不是?
这是使用批处理的完美场景.
您需要使用适当的元数据创建自定义项目,然后创建单个目标以引用新项目.
你可以将每个Item包装在它自己的目标中,如下所示:
<Target Name="Tgt1">
<ItemGroup>
<BuildConfig Include="Tgt1">
<Conf1>Twiddle</Conf1>
<Conf2>Thing</Conf2>
</BuildConfig>
</ItemGroup>
</Target>
<Target Name="Tgt2">
<ItemGroup>
<BuildConfig Include="Tgt2">
<Conf1>Twaddle</Conf1>
<Conf2>Thing 1</Conf2>
</BuildConfig>
</ItemGroup>
</Target>
<Target Name="Tgt3">
<ItemGroup>
<BuildConfig Include="Tgt3">
<Conf1>Tulip</Conf1>
<Conf2>Thing 2</Conf2>
</BuildConfig>
</ItemGroup>
</Target>
Run Code Online (Sandbox Code Playgroud)
然后,您需要一个核心目标来调用它将执行所有工作,如下所示:
<Target Name="CoreBuild" Outputs="%(BuildConfig.Identity)">
<Message Text="Name : %(BuildConfig.Identity)" />
<Message Text="Conf1 : %(BuildConfig.Conf1)" />
<Message Text="Conf2 : %(BuildConfig.Conf2)" />
</Target>
Run Code Online (Sandbox Code Playgroud)
添加Outputs="%(BuildConfig.Identity)"到目标将确保您在目标级别而不是在任务级别进行批处理.
只要最后一个目标是您的核心目标,您就可以通过传递任意目标组合从msbuild执行此操作.例如,执行此命令MSBuild.exe test.msbulid /t:Tgt1,Tgt3,CoreBuild将为您提供以下输出:
Name : Tgt1
Conf1 : Twiddle
Conf2 : Thing
Name : Tgt3
Conf1 : Tulip
Conf2 : Thing 2
Run Code Online (Sandbox Code Playgroud)
DRY不是MSBuild的宗旨.有人说,在任何情况下重复自己是不好的,当它是合理可避免的.Aaron给出的关于批处理的答案是一个很好的答案.这是防止重复的一种方法.
我想指出的一件事是,在更高的层次上,您似乎将MSBuild视为一种过程语言(即具有可以调用的函数和不可调用的函数).虽然MSBuild比声明更具说明性.如果你正在创建MSBuild脚本并且你有心态'创建函数X以便我可以在Y点调用它,那么你就进入了一个痛苦的世界.相反,您应该将MSBuild视为阶段.例如; 收集文件,编译,发布等等.当你以这种方式想到它时,它总是有意义的是为什么在执行一次之后跳过目标(你在试验中显然已经观察过).
在我和MSBuild一起工作之后,我已经发现它可以真正成为PITA以通用/超级可重用的方式做事.这是可以做到的,但我会保留这种类型的努力为您肯定知道.targets文件将被重新使用许多次.现在有一天,而不是经历,我更务实,我在完全黑客的脚本之间的某个地方和我以前的方式做事.我有一组我重复使用的脚本,但除了那些我尝试并保持简单.其中一个重要原因是有很多人如何了解MSBuild的基础知识,但很少有人对它有深入的了解.创建好的通用脚本需要对MSBuild有深入的了解,所以当你离开项目时,进入你的人不知道你在做什么(如果你是承包商,也许很好吗?大声笑).
在任何情况下,我都有很多关于批处理的资源:http://sedotech.com/Resources#Batching.