在尚未构建csproj时抑制AfterBuild目标

时间:2009-02-05 14:55:49

标签: c# msbuild

我在MSBuild中有一个后期构建目标来复制一些构建输出。

此链接作为AfterBuild目标的依赖关系(由Microsoft.CSharp.targets公开):

<Target Name="AfterBuild" DependsOnTargets="InstallUtil;CopyPostBuildFiles" />

如果构建实际上没有重新构建,有没有办法避免复制文件?

例如,当MSBuild依赖关系分析声明项目不需要构建因为它的源文件都没有更新时,它不会构建,但仍然执行我的复制目标。有什么方法可以阻止这种情况吗?

7 个答案:

答案 0 :(得分:8)

我只是用Google搜索,发现了这个岁月的答案。我找到了一种方法,通过从核心目标中窃取一个想法来更轻松地做到这一点。覆盖BeforeBuild和AfterBuild,并执行以下操作:

  <Target Name="BeforeBuild">
    <PropertyGroup>
      <MyBeforeCompileTimestamp>%(IntermediateAssembly.ModifiedTime)
                 </MyBeforeCompileTimestamp>
    </PropertyGroup>
  </Target>

  <Target Name="AfterBuild">
    <CallTarget Condition="$(MyBeforeCompileTimestamp) !=   
          %(IntermediateAssembly.ModifiedTime)" Targets="MyTarget" /> 
  </Target>

答案 1 :(得分:7)

由于您要覆盖AfterBuild目标,因此它将始终在构建发生后执行。这是重建或正常构建的情况。如果要在重建(而不是构建)之后执行某些操作,则应该扩展Rebuild目标的依赖项属性。因此,在重建发生后注入目标的情况下,项目文件应如下所示:

<Project ...>
   <!-- some content here -->

   <Import Project="... Microsoft.Csharp.targets" />


    <PropertyGroup>
        <RebuildDependsOn>
            $(RebuildDependsOn);
            InstallUtil;
            CopyPostBuildFiles
        </RebuildDependsOn>
    </PropertyGroup>
</Project>

此方法扩展了Rebuild目标用于声明其所依赖的目标的属性。我在文章Inside MSBuild中对此进行了详细介绍,请参阅扩展构建过程部分。

如果您可以指定哪些文件将“触发”更新发生,那么AfterBuild目标可能会实现您要完成的任务。换句话说,您可以在目标中指定一组“输入”,并将一组“输出”指定为两个文件。如果所有输出都是在所有输入之后创建的,则目标将被考虑为最新并跳过。这个概念被称为增量构建,我在文章MSBuild Best Practices Part 2中介绍了它。另外,我在我的书Inside the Microsoft Build Engine: Using MSBuild and Team Foundation Build中详细说明了这一点。

编辑:添加BuildDependsOn示例

如果希望目标仅在实际构建文件时执行,而不是仅在执行重建目标时执行。然后,您应该将项目创建为如下所示:

<Project ...>
   <!-- some content here -->

   <Import Project="... Microsoft.Csharp.targets" />


    <PropertyGroup>
        <BuildDependsOn>
            $(BuildDependsOn);
            CustomAfterBuild;
        </BuildDependsOn>
    </PropertyGroup>
   <Target Name="CustomAfterBuild" Inputs="$(MSBuildAllProjects);
            @(Compile);                               
            @(_CoreCompileResourceInputs);
            $(ApplicationIcon);
            $(AssemblyOriginatorKeyFile);
            @(ReferencePath);
            @(CompiledLicenseFile);
            @(EmbeddedDocumentation); 
            $(Win32Resource);
            $(Win32Manifest);
            @(CustomAdditionalCompileInputs)"
    Outputs="@(DocFileItem);
             @(IntermediateAssembly);
             @(_DebugSymbolsIntermediatePath);                 
             $(NonExistentFile);
             @(CustomAdditionalCompileOutputs)">

   <!-- Content here -->

    </Target>

</Project>

我刚刚复制了Microsoft.CSharp.targets中的 CoreCompile 目标的输入和输出(我假设您在这里使用C#)并将其粘贴到此处。这意味着只要执行 CoreCompile 目标,就会跳过目标。此外,由于我扩展了BuildDependsOn,我们知道无论何时构建项目,MSBuild都会尝试执行它。

答案 2 :(得分:6)

如何使用输入和输出目标的属性,就像在编译目标中完成的那样。

<PropertyGroup>
  <PostBuildDir>CopiedFilesDirectory</PostBuildDir>
</PropertyGroup>

<Target Name="AfterBuild" DependsOnTargets="InstallUtil;CopyPostBuildFiles" />

<Target Name="CopyPostBuildFiles"
        Inputs="@(PostBuildFiles)"
        Outputs="@(PostBuildFiles -> '$(PostBuildDir)%(Filename)%(Extension)'">
    <Copy SourceFiles="@(PostBuildFiles)"
          DestinationFolder="PostBuildDir"/>
</Target>

您可以在 CopyPostBuildFiles InstallUtil 上使用,或直接在 AfterBuild 上使用。

有关Targets Inputs Outputs

的更多信息,请参阅此页面

答案 3 :(得分:0)

构建目标定义是:

<Target Name = "Build" DependsOnTargets="BeforeBuild;CoreBuild;AfterBuild"/>

我认为如果您的文件没有更改,CoreBuild就不会执行,因此您可以尝试使用AfterCompile 而不是AfterBuild。

<Target Name="AfterCompile" DependsOnTargets="InstallUtil;CopyPostBuildFiles" />

答案 4 :(得分:0)

这是怎么回事。

  
      
  • BuildBreak 。当a时设置为true   发生编译错误,否则   假。可用于构建中断   目标是确定它们是否存在   在编译错误或执行后执行   正常执行。
  •   
     

http://blogs.msdn.com/aaronhallberg/archive/2008/02/12/team-build-2008-property-reference.aspx

我没试过,但听起来很有希望。

答案 5 :(得分:0)

当且仅当重建了项目目标文件时,才会调用AfterBuild目标:

<Target Name="AfterBuild"
    DependsOnTargets="InstallUtil;CopyPostBuildFiles"
    Inputs="$(TargetPath)"
    Outputs="$(DeployPath)\$(TargetFileName)" />

这假定您已定义了一个属性$(DeployPath),用于标识复制目标输出文件的文件夹。

答案 6 :(得分:-1)

我对MSBuild知之甚少,但对于Visual Studio for C#使用的MSBuild项目,有一个简单的选择:使用Post-build构建事件而不是AfterBuild目标。

您可以通过Visual Studio项目属性对话框在第三个“构建事件”选项卡上设置构建后构建事件。在某些情况下,可能需要在此对话框中输入一些命令,然后在确定其工作原理后直接编辑.csproj文件。

请记住在对话框中选择“运行此构建后事件:构建更新项目输出时” - 这是获取OP请求的功能的关键。

就像我说的,我对MSBuild知之甚少,而且Post-build构建事件可能不适用于AfterBuild目标可以做的一些事情。但是我用它来复制文件和运行BAT脚本,为此它工作正常。

编辑:

我将添加一些关于如何在我的C#项目中使用后期构建事件的注释。

为了分隔不同的功能区域,我通常创建一个名为PostBuildEvent.bat的BAT脚本,并将其放在与.csproj文件相同的文件夹中。然后我的构建后事件只包含两行:

cd $(ProjectDir)
PostBuildEvent.bat

然后我将我想要的命令放在PostBuildEvent.bat文件中。这是一个例子:

copy "..\..\..\..\..\Shared Bin\Merlinia.CommonClasses.NamedPipesNative.dll" bin
copy "..\..\..\..\..\Shared Bin\Merlinia.CommonClasses.NamedPipesNative.pdb" bin

cd Packed-NonObfuscated
call PackForWindowsSystem32-NonObfuscated.bat
cd ..\

cd Packed-Obfuscated
call PackForWindowsSystem32-Obfuscated.bat
cd ..\

pause

请记住,要从BAT脚本调用BAT脚本,请明确指定“call”。另请注意使用“暂停” - 这样可以通过双击BAT文件来测试脚本,然后您可以在cmd窗口中看到任何错误消息。当脚本通过MSBuild运行时,忽略“暂停”。