8

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

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

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

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

我如何调整我的构建过程来实现这一点?

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

4

4 回答 4

12

最后,解决方案结合了Sayed Ibrahim Hashimi 的博客条目和 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 失败会发生什么,它仍然调用我们的目标吗?我想我们会及时发现:)

于 2012-08-08T17:04:27.727 回答
7

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

几个月前,我写了一篇博文 MSBuild 如何在 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 的全部内容。

构建项目

<?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>

在上面的构建目标中,我使用 MSBuild 任务来构建 ProjA 和 ProjB。如您所见,我正在传递指向extend-corecompile.proj 的属性CustomBeforeMicrosoftCommonTargets=$(FileToInject)。通过在构建 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>

此项目文件使用我之前的博客文章中概述的技术,仅在执行 CoreCompile 时执行 MyCustomTarget。

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

于 2012-07-28T23:32:56.313 回答
0

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

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

请参阅此问题:如何使用 MSBuild 更改 AssemblyProduct、AssemblyTitle?

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

  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
于 2012-07-26T11:47:11.763 回答
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"
于 2012-07-26T12:05:52.040 回答