MSBuild doesn't show entry target message - msbuild

I have the following dummy build script files:
Common.targets
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Target Name="TargetA">
<Message Text="This is TargetA"/>
</Target>
<Target Name="TargetB">
<Message Text="This is TargetB"/>
</Target>
</Project>
EntryPoint.proj
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Import Project="Common.targets"/>
<Target Name="EntryPointTarget" DependsOnTargets="TargetA">
<!--But why this message is not shown during build?-->
<Message Text="This is Entry Point Target"/>
</Target>
</Project>
Why is EntryPointTarget message not shown during build?

If you do not specify a target on the commandline and no DefaultTarget is specified then msbuild executes the first target it sees, TargetA in this case. If you switch TargetA and TargetB you'll see TargetB being executed first. If you remove the import and make EntryPointTarget not depend on any other targets EntryPointTarget will be executed. Those aren't proper fixes obviously, so either:
specify the target explicitly call msbuild EntryPoint.proj /t:EntryPointTarget
make it determinstic what gets executed by adding the DefaultTargets=EntryPointTarget attribute to the Project tag, then you can just call msbuild EntryPoint.proj and it will execute EntryPoint.

Related

VS2019 add custom script to publishing process

How to perform own program before/after FolderPublish event in VS2019?
Not interesting at all how to add custom build script, it working and can be adding manually or by VS2019 project editor.
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
<PropertyGroup>
<PreBuildEvent>echo $(PublishDir)</PreBuildEvent>
</PropertyGroup>
</Project>
My question exactly about processing project publishing event, I try to use
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
<Target Name="ActionsBeforePublish" BeforeTargets="BeforePublish">
<Exec Command="echo YES" />
</Target>
<Target Name="ActionsAfterPublish" AfterTargets="AfterPublish">
<Exec Command="echo $(PublishDir)" />
</Target>
</Project>
but its not working. I don't see "YES" in publishing output window.

Second executing CustomBuild item always logs "All outputs are up-to-date"

I've been trying to add a second CustomBuild step to a content project using MSBuild targets. This first on (which compiles GLSL to SPIR-V) works fine with the CustomBuild item. The second one I tried however, always logs "All outputs are up-to-date". I'm quite new to MSBuild so I'm sure I'm doing something stupid. Any general input on my script is also welcome :)
I've tried converting the second CustomBuild item to a Exec item with practically the same command which worked fine. As far as I know this will not give me access to the tracker functionality. I've also tried using a different TrackerLogDirectory but that seemed to have no effect.
I've also checked the input files and they are being passed correctly.
This is the first (working) targets file:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)spv.xml" />
<AvailableItemName Include="SPIRVShader">
<Targets>CompileGlslShaders</Targets>
</AvailableItemName>
</ItemGroup>
<Target Name="CompileGlslShaders" Condition="'#(SPIRVShader)' != ''" BeforeTargets="FinalizeBuildStatus">
<MakeDir Directories="$(OutDir)Shaders;$(IntDir)$(ProjectName).tlog"/>
<ItemGroup>
<SPIRVShader>
<Outputs>$(OutDir)Shaders\%(Filename)%(Extension).spv</Outputs>
<Command>glslangValidator -V -o "$(OutDir)Shaders\%(Filename)%(Extension).spv" "%(FullPath)"</Command>
</SPIRVShader>
</ItemGroup>
<CustomBuild
Sources="#(SPIRVShader)"
MinimalRebuildFromTracking="True"
TrackerLogDirectory="$(IntDir)$(ProjectName).tlog\"
ErrorListRegex="(?'CATEGORY'ERROR|WARNING): (?'FILENAME'.+):(?'LINE'\d+): (?'TEXT'.*)"/>
</Target>
</Project>
And this is the second one:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)pum.xml" />
<AvailableItemName Include="PuModel">
<Targets>CompilePlutoniumModels</Targets>
</AvailableItemName>
</ItemGroup>
<Target Name="CompilePlutoniumModels" Condition="'#(PuModel)' != ''" BeforeTargets="FinalizeBuildStatus">
<MakeDir Directories="$(OutDir)Models;$(IntDir)$(ProjectName).tlog"/>
<ItemGroup>
<CompileModels>
<Outputs>$(OutDir)Models\%(Filename).pum</Outputs>
<Command>"$(SolutionDir)..\..\bin_$(PlatformTarget)_$(Configuration)_ContentCompiler\ContentCompiler" -o "$(OutDir)Models\%(Filename).pum" "%(FullPath)"</Command>
</CompileModels>
</ItemGroup>
<CustomBuild
Sources="#(CompileModels)"
MinimalRebuildFromTracking="True"
TrackerLogDirectory="$(IntDir)$(ProjectName).tlog\"
ErrorListRegex="(?'CATEGORY'ERROR|WARNING): (?'FILENAME'.+):(?'LINE'\d+): (?'TEXT'.*)"/>
</Target>
</Project>
I would expect the CustomBuild to work the same way as the first one and make new tlog files to check. Currently it just executes the MakeDir task and then says: "All outputs are up-to-date".
Edit:
After some more testing I found out that the second CustomBuild task doesn't even want to run if I disable the first one. The target still gets called in both scenarios ("CompilePlutoniumModels called!" is being logged). But even a simple echo doesn't want to log on its own:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)pum.xml" />
<AvailableItemName Include="PuModel">
<Targets>CompilePlutoniumModels</Targets>
</AvailableItemName>
</ItemGroup>
<Target Name="CompilePlutoniumModels" Condition="'#(PuModel)' != ''" BeforeTargets="FinalizeBuildStatus">
<MakeDir Directories="$(OutDir)Models;$(IntDir)$(ProjectName)_$(MSBuildThisFileName).tlog"/>
<Message Importance="high" Text="CompilePlutoniumModels called!"/>
<ItemGroup>
<CompileModels>
<Command>echo CompileModels called!</Command>
</CompileModels>
</ItemGroup>
<CustomBuild
Sources="#(CompileModels)"
MinimalRebuildFromTracking="True"
TrackerLogDirectory="$(IntDir)$(ProjectName)_$(MSBuildThisFileName).tlog\"/>
</Target>
</Project>
I thought that the Sources property in the CustomBuild task would be a link to the item that would contain the build step. I figured out that this needs to have the same name as the ItemType or ContentType. So I changed my second targets file to:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PropertyPageSchema Include="$(MSBuildThisFileDirectory)pum.xml" />
<AvailableItemName Include="PuModel">
<Targets>CompilePlutoniumModels</Targets>
</AvailableItemName>
</ItemGroup>
<Target Name="CompilePlutoniumModels" Condition="'#(PuModel)' != ''" BeforeTargets="FinalizeBuildStatus">
<MakeDir Directories="$(OutDir)Models;$(IntDir)$(ProjectName)_$(MSBuildThisFileName).tlog"/>
<ItemGroup>
<PuModel>
<Outputs>$(OutDir)Models\%(Filename).pum</Outputs>
<Command>call "$(SolutionDir)..\..\bin_$(PlatformTarget)_$(Configuration)_ContentCompiler\ContentCompiler" -o "$(OutDir)Models\%(Filename).pum" "%(FullPath)"</Command>
</PuModel>
</ItemGroup>
<CustomBuild
Sources="#(PuModel)"
MinimalRebuildFromTracking="True"
TrackerLogDirectory="$(IntDir)$(ProjectName)_$(MSBuildThisFileName).tlog\"/>
</Target>
</Project>

MSBuild project to test a C++ program

I have a .vcxproj file that compiles a C++ program. I would like to create a second MSBuild project file that tests the program by running it, but only if the program has been rebuilt since the last successful test. How can I access the "TargetPath" of the program from the second project file?
If I could access TargetPath as an "item" from the .vcxproj file, then the the tester project file will look like this:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Build" Inputs="#(TargetPath)" Outputs="#(TargetPath->'%(filename).test-passed)'">
<Exec Command="#(TargetPath)" />
<Touch Files="#(TargetPath->'%(filename).test-passed)'" />
</Target>
</Project>
I would like to execute the test using a separate project file from the compilation of the program, to make it easier to choose between build-and-test or build-and-debug within Visual Studio, without multiplying the build configurations.
It is possible to run a native program compiled by a separate .vcxproj using the MSBuild task. Use the <Output> element to create an Item with the "TargetOutputs" from the C++ application build. However, if you are building a "native" program, "TargetOutputs" is normally blank. In this case, use the "GetNativeTargetPath" target to get the output path. The following project .vcxproj file works with Visual Studio. It builds test_build.vcxproj. The test_build.exe file is run, if it has changed since the last successful run.
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{80DB0D71-72E0-4FB1-B53F-EFB858A1D5A8}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>nordic_test_run</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<ConfigurationType>Application</ConfigurationType>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ItemGroup>
<ProjectReference Include="test_build.vcxproj" />
</ItemGroup>
<Target Name="BuildExecutable">
<MSBuild Projects="#(ProjectReference)" Targets="Build" BuildInParallel="True" />
<MSBuild Projects="#(ProjectReference)" Targets="GetNativeTargetPath" BuildInParallel="True">
<Output TaskParameter="TargetOutputs" ItemName="NativeTests" />
</MSBuild>
</Target>
<Target Name="Build" DependsOnTargets="BuildExecutable" Inputs="#(NativeTests)" Outputs="#(NativeTests->'%(filename).test-passed')">
<Exec Command="#(NativeTests)" />
<Touch Files="#(TestTargets->'%(filename).test-passed')" />
</Target>
</Project>

Dynamically create a property in msbuild to be used in a calltarget subtarget

How do I create a property in msbuild so that I can use it in a CallTarget directive?
Essentially I am trying to call a target 'subroutine' where the properties act as parameters.
I tried making a toy csproj file which attempts to create a property, and then calls a target which echos it. It echos null.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Test">
<CreateProperty Value="AAA">
<Output TaskParameter="Value" PropertyName="Foo" />
</CreateProperty>
<CallTarget Targets="Test2" />
</Target>
<Target Name="Test2">
<Message Text="Target Test2: Foo=$(Foo)" />
</Target>
</Project>
Running msbuild TestProj.csproj /t:Test outputs:
Test:
Target Test: Foo=AAA
Test2:
Target Test2: Foo=
I guess the problem is I'm thinking about msbuild in an imperative fashion (which is apparently a common mistake), so I'm hoping someone can correct what appears to be a very fundamental misunderstanding in how msbuild works.
You can use the target property DependsOnTarget to get the property passed from task to task.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Test">
<CreateProperty Value="AAA">
<Output TaskParameter="Value" PropertyName="Foo" />
</CreateProperty>
</Target>
<Target Name="Test2" DependsOnTargets="Test">
<Message Text="Target Test2: Foo=$(Foo)" />
</Target>
</Project>
The just call the second target.
Holy crap. This is apparently a bug in msbuild?
Overwrite properties with MSBuild
http://weblogs.asp.net/bhouse/archive/2006/03/20/440648.aspx
edit: Or this is a feature? https://stackoverflow.com/a/7539455

How do I stop Item Batching from executing a batch when there are zero items?

Execute this with msbuild:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Main" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Colors Include="Blue">
<Shade>Dark</Shade>
</Colors>
</ItemGroup>
<Target Name="Main">
<Message Text="Color: %(Colors.Shade) %(Colors.Identity)"/>
</Target>
</Project>
And it outputs:
Color: Dark Blue
All well, and good, but delete the color and use this:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Main" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
</ItemGroup>
<Target Name="Main">
<Message Text="Color: %(Colors.Shade) %(Colors.Identity)"/>
</Target>
</Project>
And it outputs:
Color:
Why is one batch of the Message task being executed when there are no items in the group? I would have expected for zero items, the batch would execute zero times and I would not see "Color:" followed by nothing in the output.
Am I doing something wrong or is there a workaround for this?
Thanks.
Update:
I've found you can do:
<Message Condition="'#(Colors)'!=''" Text="Color: %(Colors.Shade) %(Colors.Identity)"/>
But, if feels unsatisfactory to have to explicitly write code for the case when there are no items every time batching is used.
My 2 cents :
In your Message Task, there is information from Batching and static information ("Colors :"). I think MsBuild prints the static information and then batch over the values of your Colors Item. The probleme is that you don't have any data in your collection (it is even undeclared), I suppose MsBuild interpretates this as an empty list, which, when you try to print it, print the empty string ''.
If you remove the static content ("Colors :" and the whitespace before identity), you won't have anything displayed.
A solution for printing with batching only if the items collection is not empty would be either :
Check if the collection is empty
<Message Condition="'#(Colors)'!=''" Text="Color: %(Colors.Shade) %(Colors.Identity)"/>
Use MSBuild transforms
<Message Text="#(Colors->'Color : %(Shade) %(Identity)')"/>
Just wanted to add an alternative solution for this as well. If you can change your batching to Target batching, instead of Task batching, you can add your Condition statement to the Target.
I added the Target batching here:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Main" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
</ItemGroup>
<Target Name="Main" Outputs="%(Colors.Identity)">
<Message Text="Color: %(Colors.Shade) %(Colors.Identity)"/>
</Target>
</Project>
...and that can be conditionally made to only execute when something exists in the Colors item group:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Main" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
</ItemGroup>
<Target Condition="'#(Colors)'!=''" Name="Main" Outputs="%(Colors.Identity)">
<Message Text="Color: %(Colors.Shade) %(Colors.Identity)"/>
</Target>
</Project>