MSBuild目标,BeforeCompile,在加载项目文件时触发

时间:2015-06-26 18:48:57

标签: visual-studio-2010 visual-studio msbuild msbuild-task

这是一个与我的整体问题相关的小问题。这就是我想要实现的目标。

我创建了一个修改源文件的任务。每当我编译和构建时,我想用修改过的cs文件进行编译。我试图制作一个我从prebuild事件调用的控制台应用程序,但我发现只编译了一些修改过的文件。即使我没有在编辑器中打开它们。然后我尝试使用自定义任务,该任务在我的csproj文件中使用以下xml:

<Target Name="BeforeCompile">
    <MyCustomTask />
</Target>

我也用它来得到相同的结果:

<PropertyGroup>
    <CompileDependsOn>
        $(CompileDependsOn);
        MyCustomTarget;
    </CompileDependsOn>
</PropertyGroup>
<Target Name="MyCustomTarget">
    <MyCustomTask />
</Target>

所以这就是我想要做的,但我发现无论何时加载项目文件,它都会触发我的目标。我认为这只会在我的代码编译时运行。显然,在项目文件加载时,它正在编译或编译相关目标。

理想情况下,我希望只有在我明确构建项目时(即手动构建或开始调试时)才会触发此目标。为实现这一目标,我能做些什么?

2 个答案:

答案 0 :(得分:4)

从文档中,这些目标始终作为加载Visual Studio的一部分执行。这也包括这些目标所依赖的任何依赖。

  

设计时目标执行

     

Visual Studio在加载项目时尝试执行具有特定名称的目标。这些目标包括编译 ResolveAssemblyReferences ResolveCOMReferences GetFrameworkPaths CopyRunEnvironmentFiles 。 Visual Studio运行这些目标,以便可以初始化编译器以提供IntelliSense,可以初始化调试器,并且可以解析在解决方案资源管理器中显示的引用。如果这些目标不存在,项目将正确加载和构建,但Visual Studio中的设计时体验将无法完全发挥作用。

     

<子> Source

在比较运行msbuild /v:diag /t:compile vs msbuild /v:diag /t:build时执行的目标时,您会看到ResGen和其他一些目标被跳过。试图捎带其中一个可能会为你做到这一点。

还要记住有关Visual Studio Hosting过程及其对动态更改文件的影响:

Visual Studio编译器可能正在使用它的缓存版本的文件。这将导致问题,要么在obj文件夹下显式创建文件并将它们动态地包含在MsBuild中,这样Visual Studio就不会使用它的文件内存实例。通过从ItemGroup中删除源文件并添加您自己生成的副本来执行此操作,您需要从自定义目标执行此操作:

<ItemGroup>
   <Compile Remove="ThefileYourWantGone.cs" />
   <Compile Include="$(BaseIntermediateOutputPath)\ThefileYourWantGone.g.cs/>
</ItemGroup>

您可以使用转换表达式,而不是对其进行硬编码:

<ItemGroup>
   <Compile Include="ThefileYourWantGone.cs">
      <IsRegenerated>true</IsRegenerated>
   </Compile>
</ItemGroup>

<ItemGroup>
   <RegeneratedCompile
        Include="@(Compile)"
        Condition="'%(Compile.IsRegenerated)' == 'true'"
   />
</ItemGroup>

<YourCustomtaskThatOutputs Inputs="@(RegeneratedCompile)" Outputs="@(RegeneratedCompile-> '$(BaseIntermediateOutputPath)\%(relativedir)%(filename).g.%(extension)')" />

<ItemGroup>
   <Compile Remove="@(RegeneratedCompile)" />
   <Compile Include="@(RegeneratedCompile-> '$(BaseIntermediateOutputPath)\%(relativedir)%(filename).g.%(extension)')" />
</ItemGroup>

替代

禁用主机编译器

disable the HostCompiler添加

<UseHostCompilerIfAvailable>FALSE</UseHostCompilerIfAvailable>

到第一个属性组(不应该有条件)使Visual Studio始终使用磁盘版本(会稍微减慢Visual Studio的构建速度)。

让您的任务识别Visual Studio

或者让您的构建任务知道IVsMSBuildTaskFileManager并在更新文件时告诉它。您需要注册your build task in the registry of Visual Studio to flag it as "safe to load"

答案 1 :(得分:2)

Edit: This does what I describe it to do. Although it doesn't seem that it uses the modified files in the compile process. This is still good reference if someone needs to run a task early on in the entire build process but doesn't want them executing when the project file is loaded. I will come back and edit if I find a solution for my specific problem (which is still relevant to this one!)


Original Answer:

So I found the answer while playing around with information from jessehouwing's answer. Here is the code I used and I will follow with an explanation.

<PropertyGroup>
    <ResolveAssemblyReferencesDependsOn>
        SetFirstTimeLoading;
        $(ResolveAssemblyReferencesDependsOn);
    </ResolveAssemblyReferencesDependsOn>
    <CompileDependsOn>
        RunCustomTask;
        $(CompileDependsOn);
    </CompileDependsOn>
</PropertyGroup>

<Target Name="SetFirstTimeLoading">
    <PropertyGroup>
        <FirstTimeLoading Condition=" '$(FirstTimeLoading)' == '' ">false</FirstTimeLoading>
    </PropertyGroup>
</Target>

<UsingTask TaskName="MyCustomTask" AssemblyFile="path\to\task" />
<Target Name="RunCustomTask" Condition=" '$(FirstTimeLoading)' == 'false' ">
    <MyCustomTask/>
</Target>

I have two targets here. The bottom one is the task I need to run. The second is setting a custom property, SetFirstTimeLoading, to false.

RunCustomTask will run before compiling because it is listed in the CompileDependsOn.

SetFirstTimeLoading will run after Compile but also before ResolveAssemblyReferences because it is listed in the ResolveAssemblyReferencesDependsOn.

So because of this, I can have a conditional in my RunCustomTask target that will see if the FirstTimeLoading property has been set to false. This is set the first time the project file is loaded. So any subsequent time the my tasks are run (whenever I build in visual studio), RunCustomTask's conditional will always evaluate to true and will run MyCustomTask.