I'm trying to create a msbuild csproj where I can invoke dotnet run myproject
and get bin/obj folders deleted after execution. However, the CustomAfterRun
target (below) does not get executed after the Run target.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.cs" />
</ItemGroup>
<Target Name="CustomAfterRun" AfterTargets="Run">
<Message Importance="high" Text="[msbuild] Cleaning bin and obj..."/>
<RemoveDir Directories="$(TargetDir)" />
<RemoveDir Directories="$(ProjectDir)$(BaseIntermediateOutputPath)" />
</Target>
</Project>
If I create a target CustomAfterBuild
(with AfterTargets="Build"
), it works fine (it gets executed after the build when I run dotnet build myproj
).
Is this a bug or am I doing something wrong?
And the crazy part: if inside CustomAfterBuild
I invoke <CallTarget Targets="Run" />
then after the build my program is invoked, and in this case CustomAfterRun
runs fine !! The downside of this method is that the program output is only visible if I enable detailed verbosity level, and also after CustomAfterRun
runs (deleting bin/obj folders) it will also try to run the program AGAIN (meaning that the Run target is invoked TWICE?!) giving me an error: System.ComponentModel.Win32Exception (2): The system cannot find the file specified.
.
EDIT: As an alternative I also tried overriding Run target, but that didn't work at all:
<Project>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.cs" />
</ItemGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
<Target Name="Run" >
<Message Importance="high" Text="[msbuild] overriden..."/>
</Target>
</Project>
Turns out I had a wrong assumption about dotnet run
.
There's dotnet msbuild
which is basically a facade to invoke msbuild. dotnet build
is just a shortcut to dotnet msbuild -restore
, so it will restore nuget packages and build (default target). dotnet clean
also invokes msbuild (clean target).
In a similar sense I was expecting dotnet run
to use the Run
target from msbuild, however it does NOT. It invokes dotnet msbuild
(if required), but it will run the output WITHOUT using msbuild.
If I run msbuild -target:Run
the post-run events work fine!
So one possible solution would be to use plain msbuild.
E.g.: dotnet msbuild /t:Restore;Build;Run;Clean MyProj.csproj
),
However, another behavior which was annoying me is that when the program is launched through msbuild I don't get colors (if any) in console output. This is probably because msbuild uses console-redirection to run my program. dotnet run
fortunately doesn't have this behavior, so my final solution will be:
dotnet run & dotnet clean
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.cs" />
</ItemGroup>
<!-- Clean is not so perfect, clear leftovers -->
<Target Name="CleanBinObj" AfterTargets="Clean">
<RemoveDir Directories="$(ProjectDir)$(BaseOutputPath)" />
<RemoveDir Directories="$(ProjectDir)$(BaseIntermediateOutputPath)" />
</Target>
<!-- If someone uses msbuild to launch, force a clean.. -->
<Target Name="CustomAfterRun" AfterTargets="Run">
<CallTarget Targets="Clean" />
</Target>
</Project>
Or for my specific case where I'll keep multiple individual cs programs in the same folder (each one with their own csproj) and I want to override bin/obj folders with an individual (temporary) folder for my file:
<Project>
<PropertyGroup>
<TempDir>$(MSBuildProjectName).cs.tmp\</TempDir>
<BaseOutputPath>$(TempDir)</BaseOutputPath>
<BaseIntermediateOutputPath>$(TempDir)</BaseIntermediateOutputPath>
</PropertyGroup>
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<EnableDefaultItems>false</EnableDefaultItems>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildProjectName).cs" />
</ItemGroup>
<!-- Clean is not so perfect, clear leftovers -->
<Target Name="CleanBinObj" AfterTargets="Clean">
<Message Importance="high" Text="Cleaning $(TempDir) (bin/obj)..."/>
<RemoveDir Directories="$(TempDir)" />
</Target>
<!-- If someone uses msbuild to launch, force a clean.. -->
<Target Name="CustomAfterRun" AfterTargets="Run">
<CallTarget Targets="Clean" />
</Target>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project>