确定MSBuild CoreCompile是否将运行并调用自定义目标

时间:2012-07-26 10:38:33

标签: c# msbuild

这似乎是一件显而易见的事情,但是我已经把大部分头发都拉出来试图在网上找到任何例子或自己做。

我有一个包含19个项目的c#解决方案和一个运行构建脚本来驱动MSBuild的Jenkins构建服务器。 MSBuild当然会根据输入与输出确定需要编译和不需要编译的内容。

我正在尝试创建一个自定义目标来有条件地更新MSBuild将要编译的那些项目的AssemblyInfo.cs以增加文件版本。当然,我想让这些项目不是单独编译的。

我知道如何在每次运行的CoreBuild之前注入一个目标,所以如果有一些变量,我可以测试一下是否会发生可以工作的编译。我也知道如何确定编译是否运行,因此有条件地进行一些可能但不理想的后期处理。

如何调整构建过程以实现此目的?

由于似乎没有直接回答这个问题,有没有人知道如何执行与MSBuild相同的逻辑来确定哪些项目需要重建?

4 个答案:

答案 0 :(得分:11)

最后,解决方案是Sayed Ibrahim Hashimi's blog entry和MSDN论坛条目'Execute target when (core)compile will execute'中的信息的组合。

我基本上采用Sayed的注入方法让我的目标在所有项目上运行'extend-corecompile.proj',而不必编辑每个proj文件,但用'CoreCompileDependsOn'的覆盖替换它的内容,指向自定义目标采用与'CoreCompile'目标相同的输入和输出。最终结果是一个目标,只有在“CoreCompile”运行时才会在构建脚本中集中管理时运行。

感谢所有人的输入,这是我在'extend-corecompile.proj'中使用的框架代码:

<!--The following property group adds our custom post-target to the post compile call list -->
<PropertyGroup>
    <TargetsTriggeredByCompilation>
        $(TargetsTriggeredByCompilation);
        CustomPostTarget
    </TargetsTriggeredByCompilation>
</PropertyGroup>

<!--The following property group adds our custom pre-target to CoreCompileDependsOn to ensure it is called before CoreCompile -->
<PropertyGroup>
    <CoreCompileDependsOn>
        $(CoreCompileDependsOn);
        CustomPreTarget
    </CoreCompileDependsOn>
</PropertyGroup>

<!-- The following custom pre-target has the same inputs and outputs as CoreCompile so that it will only run when CoreCompile runs.
    Because we have injected this file and Targets are resolved in sequence we know this Target will fire before CoreCompile.-->
<Target Name="CustomPreTarget" 
    Inputs="$(MSBuildAllProjects);
            @(Compile);                               
            @(_CoreCompileResourceInputs);
            $(ApplicationIcon);
            $(AssemblyOriginatorKeyFile);
            @(ReferencePath);
            @(CompiledLicenseFile);
            @(EmbeddedDocumentation); 
            $(Win32Resource);
            $(Win32Manifest);
            @(CustomAdditionalCompileInputs)"
    Outputs="@(DocFileItem);
             @(IntermediateAssembly);
             @(_DebugSymbolsIntermediatePath);                 
             $(NonExistentFile);
             @(CustomAdditionalCompileOutputs)">
    <!--Do pre-compilation processing here-->
</Target>

<!--This target will be called by CoreCompile-->
<Target Name="CustomPostTarget" >
    <!--Do post-compilation processing here-->
</Target>

不确定如果CoreCompile失败会发生什么,它仍然会调用我们的目标吗?我想我们会及时发现:)

答案 1 :(得分:6)

我刚刚在http://sedodream.com/2012/07/28/MSBuildHowToExecuteATargetAfterCoreCompilePart2.aspx上写了这个问题的答案,但我已在下面粘贴了解决方案。

几个月前,我写了一篇博客文章MSBuild how to execute a target after CoreCompile,其中描述了如果执行CoreCompile目标时如何执行目标,如果跳过CoreCompile,那么你的另一个目标也是如此。我在上一篇文章中概述的方法的缺点是它需要您编辑.csproj / .vbproj / etc文件本身。因此,如果您有一个构建多个项目的场景,则必须编辑所有项目文件。在这篇文章中,我将描述如何执行相同的自定义,而无需编辑项目文件本身。

在我们找到针对这个特定案例的解决方案之前,让我描述一下C#和VB项目所具有的可扩展性钩子。构建C#和VB项目的大多数逻辑都是在C:\ Windows \ Microsoft.NET \ Framework \ v4.0.30319 \ Microsoft.Common.targets的MSBuild目标文件中捕获的。如果您查看该文件,您会在顶部看到一个类似下面的导入。

<Import Project="$(CustomBeforeMicrosoftCommonTargets)" Condition="'$(CustomBeforeMicrosoftCommonTargets)' != '' and Exists('$(CustomBeforeMicrosoftCommonTargets)')"/>

如果属性不为空且文件存在,则此语句将导入文件(位于CustomBeforeMicrosoftCommonTargets的值)。 CustomBeforeMicrosoftCommonTargets的默认值为C:\ Program Files(x86)\ MSBuild \ v4.0 \ Custom.Before.Microsoft.Common.targets。因此,如果您在该位置删除MSBuild文件,它将修改在该计算机上构建的每个C#/ VB项目的构建过程。或者,如果您不想(或不能由于ACL),则可以将文件放在其他位置,然后通过重写CustomBeforeMicrosoftCommonTargets属性来指定其位置。这是我将采取的方法。我创建了一个示例解决方案,它包含两个项目ProjA和ProjB。我还有一个构建脚本build.proj,用于自动化构建。以下是build.proj的全部内容。

build.proj

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <FileToInject Condition=" '$(FileToInject)'=='' ">$(MSBuildThisFileDirectory)extend-corecompile.proj</FileToInject>
  </PropertyGroup>

  <ItemGroup>
    <ProjectsToBuild Include="ProjA\ProjA.csproj"/>
    <ProjectsToBuild Include="ProjB\ProjB.csproj"/>
  </ItemGroup>

  <Target Name="Build">
    <MSBuild Projects="@(ProjectsToBuild)"
             Properties="CustomBeforeMicrosoftCommonTargets=$(FileToInject)" />   
  </Target>

  <Target Name="Clean">
    <MSBuild Projects="@(ProjectsToBuild)" Targets="Clean"/>
  </Target>

  <Target Name="Rebuild" DependsOnTargets="Clean;Build"/>

</Project>

在上面的Build目标中,我使用MSBuild任务来构建ProjA和ProjB。正如您所看到的,我正在传递属性CustomBeforeMicrosoftCommonTargets = $(FileToInject),它指向extend-corecompile.proj。通过在构建ProjA和ProjB时传递此属性,它将自动导入构建过程的extend-corecompile.proj文件。您可以在下面看到extend-corecompile.proj的内容。

延伸-corecompile.proj

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <TargetsTriggeredByCompilation>
      $(TargetsTriggeredByCompilation);
      MyCustomTarget
    </TargetsTriggeredByCompilation>
  </PropertyGroup>

  <Target Name="MyCustomTarget">
    <Message Text="MyCustomTarget called" Importance ="high"/>
  </Target>

</Project>

此项目文件使用my previous blog post中概述的技术仅在执行CoreCompile时执行MyCustomTarget。

注意:您可以在https://github.com/sayedihashimi/sayed-samples/tree/master/ExtBuildMultiple获取此示例的最新版本。

答案 2 :(得分:0)

即使您获得了需要编译的项目列表,如果更新其中一个项目的assemblyinfo.cs,也可能会导致更改触发另一个项目的编译。

因此,最简单的方法是根据源代码控制版本号生成所有AssemblyInfo.cs文件。您甚至可以获得每个项目目录的最新版本号,有效地了解该项目的“最后”修改时间。

请参阅此问题:How can I change AssemblyProduct, AssemblyTitle using MSBuild?

根据您的评论,您是否查看过BeforeBuild和AfterBuild目标(在csproj文件的末尾):

  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>

答案 3 :(得分:0)

或者,您可以使用由所有项目引用的单个自动生成的VersionInfo.cs文件。要使用此技术,请从项目的AssemblyInfo.cs文件中删除版本,公司信息等属性(是的,这很痛苦,但您只需执行此操作一次),并使用批处理命令吐出一个VersionInfo.cs文件基于模板。要在Visual Studio中引用公共文件,请从项目上下文菜单中选择“添加现有项”,在文件浏览器中导航到VersionInfo.cs文件后,单击“添加”旁边的下拉箭头,然后选择“添加为”。链接。

以下是我使用的一个示例。此脚本已签入我们的SCC系统,并在构建开始时执行,为脚本提供%BUILD_NUMBER%。

SET BUILD=%1

@echo using System.Reflection; > "%~p0Version.cs"
@echo [assembly: AssemblyCompany("MyCompany, Inc.")] >> "%~p0Version.cs"
@echo [assembly: AssemblyProduct("MyProduct")] >> "%~p0Version.cs"
@echo [assembly: AssemblyCopyright("Copyright © 2012 MyCompany, Inc.")] >> "%~p0Version.cs"
@echo [assembly: AssemblyTrademark("")]@echo [assembly: AssemblyVersion("1.0.%BUILD%.0")] >> "%~p0Version.cs"

@echo [assembly: AssemblyFileVersion("1.0.%BUILD%.0")] >> "%~p0Version.cs"

@echo ^<Include xmlns="http://schemas.microsoft.com/wix/2006/wi"^> > "%~p0Version.wxi"
@echo   ^<?define VersionBuild="%BUILD%"?^> >> "%~p0Version.wxi"
@echo ^</Include^> >> "%~p0\Version.wxi"