How to run MSBuild Target BEFORE compilation but only when compilation will happen
Asked Answered
E

2

8

I have a C# library project that hase some dependencies that a created in "BeforeBuild" with PowerShell.

MSBuild seems to execute target "BeforeBuild" each time, also if the library project itself does not need to build.

I'd like to configure the build process to only run the PowerShell script if the library needs to (re)build.

Is there a way to do this?

I tried to use targets "BeforeBuild" and "BeforeCompile". But it seems to execute every time.

Euripus answered 5/8, 2019 at 6:26 Comment(1)
Hi friend, any update for this issue?Prado
D
3

You can't really predict whether compilation will happen, as you can't know what targets may or may not do after your target runs. But you may be able to predict whether your task needs to run based on the current state of the build directory. This is how you'd implement Incremental building in MsBuild, which sounds like what you're really after.

All targets have an optional Inputs and Outputs parameter. These two are used to calculate whether any of the inputs have changed since the last build and based on that MsBuild decides whether to run your target or whether it can skip it.

This requires some knowledge, based on the current state of the ItemGroups and Properties in your build to predict the outcomes of your target.

When Inputs and Outputs aren't specified, MsBuild can't figure out whether or not your target will impact anything, Thus it will run it to be sure.

The docs provide a good explanation on how to enable incremental building of your custom targets.

To predict the outputs you'd need to write a transformation of some kind. Or, you'd write out some kind of marker file (like codeanalysis.lastsucceeded) and use that to compare against. The incremental build will ONLY look at the Last Changed Date of all inputs and compares those against all outputs. So you need to express the need to run or not as an operation on that data.

Basic structure:

<target name="RunMyPowerShell" 
    beforetargets="Compile" Inputs="@(Content)" 
    Outputs"@(Content->'%(Filename).translated.content')">
    ...
    <exec Command="powershell .\mypsfile.ps1" />
    ...
</targets>

As an alternative for BeforeTargets="Build", you can overwrite the BuildDependsOn property:

<PropertyGroup>
    <CompileDependsOn>
        RunMyPowerShell;
        $(CompileDependsOn)
    </CompileDependsOn>
</PropertyGroup>

If you haven't dug into this area before, all the intricacies of MsBuild can be overwhelming and to make this work, you need to master quite a few things. It may als require changes to what the PowerShell script does to make its output predictable enough for MsBuild to do its magic.

Alternatively, you can add a condition on your target based on some expression, but given the way build files are parsed, computed and executed, it can be pretty hard to figure out how to do that correctly.

There can be massive performance gains if you get these things right.

See also:

Durante answered 8/8, 2019 at 10:57 Comment(0)
P
0

MSBuild seems to execute target "BeforeBuild" each time, also as if the library project itself does not need to build.

What do you mean the msbuild execute the target each time also if the library project itself does not need to build? As I know, BeforeBuild is one necessary target during the build process, so if you build one project, it will always call this target. If the library project doesn't need to build, what's the reason you call msbuild to build it? More details could be better :)

I'd like to configure the build process to only run the PowerShell script if the library needs to (re)build.

Is there a way to do this?

I tried to use targets "BeforeBuild" and "BeforeCompile". But it seems to execute every time.

BeforeBuild and BeforeCompile are predefined targets in msbuild system. You can overwrite them to customize your build steps, but you can't avoid running them during build process. So they'll execute every time if the build starts, it's expected behavior by design.

According to your description, you may overwrite these targets and run the power shell script in them. That's why in your machine it will always call the ps script.

Here's a workaround if you build the project by msbuild.exe instead of VS IDE:

Assuming you have overwritten BeforeTarget in your project file. You can add a condition to determine if to run the custom BeforeTarget(call ps script) or original BeforeTarget.

Define a property named RunPS, and add a condition to the BeforeTarget you use in this way:

  <PropertyGroup>
    <RunPS>false</RunPS>
  </PropertyGroup>

  <Target Name="BeforeBuild" Condition="$(RunPS)=='true'">
   <!--Run the ps script here-->  
  </Target>

Then you can control if use the default BeforeTarget or custom one by setting the value of RunPS.

msbuild xx.csproj /p:RunPS=true to run ps script during build process, msbuild xx.csproj to run original BeforeTarget

And if you build the project in VS IDE,you can create new Configuration(CustomDebug) copied from Debug or Release, and set Condition="$(Configuration)=='CustomDebug'" to the target.

Update:

For the workaround which works in VS and command-line:

See this document, in VS we can create new configurations easily by copying settings from Debug and Release Configurations.

1.In my opinion, for your library project, you have normal Debug and Release Configurations, you can follow tips in the document to create corresponding custom configurations. Copy the setting from Debug and create a new configuration named PSDebug, copy the setting from Release and create a new configuration named PSRelease.

2.Then edit the xx.csproj, add a condition in this format:

  <Target Name="BeforeBuild" Condition="'$(Configuration)'=='PSDebug' OR '$(Configuration)'=='PSRelease'">
    <!--Run the ps script here-->
    <Message Text="showsth" Importance="high" />
  </Target>

Then in VS IDE, if you build with normal Configuration Debug or Release, it will use default BeforeBuild target. And only when you build with custom PSDebug or PSRelease, it will call your custom BeforeBuild target.In VS, you can easily switch between those configurations by this box:

enter image description here

And for command-line, it also works. If you use command like msbuild xx.csproj /p:Configuration=Debug, it use predefined BeforeBuild target to do this, and if you use command like msbuild xx.csproj /p:Configuration=PSDebug, it will call your custom target and run the PS script.

Prado answered 6/8, 2019 at 7:31 Comment(2)
Thank you for your suggestion! I understand that this behaviour is "by design"! But I cannot use your solution because it need to work in VS too.Euripus
@IngoKarstein See my update, since it's by design behavior, we have no VS option to directly control it. But vs allows us to create new configuration from Debug and Release, so we can achieve the behavior by customizing the project file. Hope it helps:)Prado

© 2022 - 2024 — McMap. All rights reserved.