msbuild call target on all the project references - msbuild

I have a requirement to call targets on each of the 'ProjectReferences' for the following hierarchical project structure.
project A -> project B -> project C
Each project has a target called 'customTarget'. How can define the configuration such that when I call 'customTarget' on project 'A', it triggers 'customTarget' on project 'B' and which indirectly triggers target on 'C'?
Expected target order: C -> B -> A .
Question: How can I configure the above and also make it generic for all project references.
[UPDATE]
I was able to create the chain of target calls using
<MSBUILD Projects="#(ProjectReference)" Targets="customTarget" />
But this requires me to add the target specification in each project in the project chain even if it's not doing anything.
Would there be any better solution it approach the above ?

You can put your extension target inside a Directory.Build.props file:
<Project>
<Target Name="customTarget" DependsOnTargets="PrepareProjectReferences">
<MSBUILD Projects="#(ProjectReferenceWithConfiguration)"
Targets="customTarget"
SkipNonexistentTargets="true" />
</Target>
</Project>
In VS 2017 / MSBuild 15 and higher it will be auto-imported into all projects file below the folder hierarchy.

Related

Run target once for built project

I have a msbuild script that compiles the files and then copies them to a different directory. To ensure only the freshly built files are in the destination, I want to delete the destination folder before.
I tried running it before the Build target like this
<Target Name="RemoveRelease64" BeforeTargets="Build" Condition="'$(Platform)' == 'x64' and '$(Configuration)' == 'Release'">
<RemoveDir Directories="$(SolutionDir)bin64" />
</Target>
But this is called for every project that my main project depends on. When starting a build for Project A, it first builds Project B, C and D. For every built project the target is called and the folder deleted. As such it deletes the folder too often.
Is there a way to call a target only once for the project that the build got started for?
Is there a way to call a target only once for the project that the
build got started for?
I think you use Directory.Build.props or Directory.Build.targets as the msbuild script and write the RemoveRelease64 in that file.
If you put the file in the solution folder, as its scope, it will works for the projects from current folder and any sub folders.
So as a suggestion, if you want the msbuild script to only work for project A rather than any referenced projects B,C,D, you should put the msbuild script only in the project folder of A where A.csproj file exists .
Suggestion
1) If you use Directory.Build.props or Directory.Build.targets, delete it at the solution folder or any parent directories, add it only in the folder of Project A where A.csproj file exists.
2) Besides, you could abandon using Directory.Build.props/targets file. Instead, you could rename it as RemoveRelease64.targets and please remember not to use those two names.
Use msbuild import node in A.csproj file and then it will only executes for Project A rather than B,C,D:
Add this in A.csproj file and remove any Directory.Build.props or Directory.Build.targets in solution folders and project folders.
<Import Project="xxx\RemoveRelease64.targets" />

Building a Target inside a solution using MSBuild

I have a solution with two default projects inside (VisualBasic console applications):
Solution1\
Solution1.sln
ConsoleApplication1\ConsoleApplication1.vbproj
ConsoleApplication2\ConsoleApplication2.vbproj
I have added a custom target to ConsoleApplication1:
...
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
<Target Name="HelloWorld">
<Message Text="Hello Build!"></Message>
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
...
</Project>
I would like to set up a script to execute a normal build on both projects, then call my custom target.
According to: http://msdn.microsoft.com/en-us/library/ms171486.aspx, I should be able to call the specific target with the following command (ignoring the initial build of the projects for now):
msbuild C:\Path\To\Solution1\Solution1.sln /t:ConsoleApplication1:HelloWorld
Instead, I get the following error message:
error MSB4057: The target "ConsoleApplication1:HelloWorld" does not exist in the project. [E:\Path\To\Solution1\Solution1.sln]
I presume I have the syntax incorrect, what would the correct syntax be to accomplish this?
The following commands work as expected:
msbuild C:\Path\To\Solution1\Solution1.sln
msbuild C:\Path\To\Solution1\ConsoleApplication1\ConsoleApplication1.vbproj /t:HelloWorld
Have you considered just adding your custom target action to the AfterBuild target in the desired msbuild proj file? It's a different approach, but would eliminate the need for the explicit invocation of the custom target and still be project file specific.

How make MSBuild build custom target specified in csproj building sln?

I am facing an issue with MSBuild I can't overcome it by myself. As a result I rely on community's wisdom.
The real situation I'm having troubles with
I have a soluiton file containing several projects with dependencies to other projects in same solution. I'd like to append a custom target to one of the project's csproj file and build it from the command line. It will allow me to make all the necessary output binaries for this project for further processing during the building of the custom target. But the main thing is that I can't figure out how to do it, googling doesn't help either.
Simplification
To make thing simplier I decided to make a new C# console project, add a simple custom target to the project's file and try to make it build. Still no success! Here what I've done so far:
Created a solution app with a default console project coreapp. This gaves me at least two files:
app.sln
coreapp\coreapp.csproj
Modified coreapp.csproj with addition of my custom target inside of the Project tag
<Target Name="SampleTarget">
<Message Text="This is a SampleTarget" />
</Target>
Run on the command line the following command
%windir%\Microsoft.NET\framework\v3.5\msbuild.exe app.sln /t:coreapp:SampleTarget
or even
%windir%\Microsoft.NET\framework\v3.5\msbuild.exe app.sln /t:coreapp.csproj:SampleTarget
Results
No luck, facing the error
MSB4057: The target "coreapp.csproj:SampleTarget" does not exist in the project.
I suspect that MSBuild thinks somehting fundamentally different from what I want it to think...
BEsides that, I also tried to set on the same command line the environment variable MSBuildEmitSolution=1 to force msbuild dump a temporary solution file it creates while processing the solution. In this file, indeed, no such target. However I guess it isn't the reason because I asked msbuild to build coreapp.proj where target SampleTarget really resides.
The question is how to build SampleTarget in this simplified scenario using solution file since potencially it can contain dependencies for the project containing this SampleTarget target?
I'd be greatful for any sort of help or firection for further investigation!
Instead of inserting a custom target in your project file, you could try creating a new standalone msbuild file, which would:
build the solution file (which builds projects)
defines your extra target
Call it app-custom-Debug.msbuild , for example.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WorkingFolder>$(MSBuildProjectDirectory)</WorkingFolder>
<Configuration>Debug</Configuration>
<SolutionFile>app.sln</SolutionFile>
</PropertyGroup>
<Target Name="Build" DependsOnTargets="Compile" />
<Target Name="Compile">
<Message Text="=== COMPILING $(Configuration) configuration ===" />
<MSBuild Projects="$(SolutionFile)"
Properties="Configuration=$(Configuration)" />
</Target>
<Target Name="SampleTarget">
<Message Text="This is a SampleTarget" />
</Target>
</Project>
Then you execute:
msbuild.exe app-custom-Debug.msbuild /t:SampleTarget
One option is to tie your SampleTarget to the standard Build targets via overriding the appropriate DependsOn property. In this case you could tell BeforeBuild that it DependsOn SampleTarget or you do the same thing with AfterBuild. This will ensure that MSBuild processes your target prior to the standard target indicated.

AfterPublish target not working

World's simplest task (see below) is not being executed after I publish my web application project. Any idea why?
<Target Name="AfterPublish">
<Copy SourceFiles="C:\A.txt" DestinationFiles="C:\B.txt" />
</Target>
Note: The following applies to VS2010 and publishing web-application projects with the "Web Deploy" publish method selected in the 'Build/Publish {projectname}' dialog.
Julien Hoarau's correct in that "Publish" is NOT the name of the msbuild target invoked in the above case; the actual target name is "MSDeployPublish".
Therefore, you have to define a "Target" element whose "AfterTarget" attribute's value is set to "MSDeployPublish" - the "Name" attribute's value does not matter (as long as it's unique among target names).
Here's how to do it:
Open the project file (e.g. *.csproj) in a text/XML editor and, just before the closing </Project> tag, add a <Target Name="CustomPostPublishAction" AfterTargets="MSDeployPublish"> element; pick a name of your choice for "CustomPostPublishAction".
Add a so-called Task child element that performs the desired action; for instance, to add a command to be passed to cmd.exe, use an <Exec Command="..." /> element.
Example:
<Target Name="CustomPostPublishActions" AfterTargets="MSDeployPublish" >
<Exec Command="echo Post-PUBLISH event: Active configuration is: $(ConfigurationName)" />
</Target>
Note:
In command strings, use XML entity(?) references in place of characters that would break XML parsing, e.g. "&gt" in place of "<".
For documentation of the <Target> element in general, see http://msdn.microsoft.com/en-us/library/t50z2hka.aspx
Task-elements reference here: http://msdn.microsoft.com/en-us/library/7z253716.aspx
In general, if you need to determine the name of the msbuild.exe target that is actually invoked by Visual Studio 2010, do the following:
Go to Tools/Options..., Project and Solutions/Build and Run, select 'Detailed' (or, for even more information, 'Diagnostic') from the dropdown list labeled 'MSBuild project build output verbosity.
After running the build/publish action, e.g. Build/Publish, examine the Output window for the last occurrence of the string "Done building target" to determine the top-level target that was invoked.
Visual Studio 2013. Publish Web application to file system.
<Target Name="Moose" AfterTargets="GatherAllFilesToPublish" >
<Message Importance="high" Text="***Moooooooooooooooose***$(WPPAllFilesInSingleFolder)***$(TargetDir)" />
</Target>
Note: Make sure that build logging is set to at least to Detailed. Look for it under Tools -> Options -> Projects and Solutinos -> Build and Run -> MSBuild output verbosity. Diagnostic is also fine if you want to investigate which build target was last run before actual publish.
This seems to work in Visual Studio 2019
<Target Name="MyCustomTarget" AfterTargets="Publish">
<Copy SourceFiles="C:\A.txt" DestinationFiles="C:\B.txt" />
</Target>
You must define override the target at the end of your file, after <Import ... />
Launch MSBuild with detailed verbosity to see why your target is ignored :
msbuild project.csproj /t:Target_to_Launch /v:d
AfterPublish is called after Publish target, but Publish is not the target called when you publish a web application. Publish is the target for publishing ClickOnce application.
You'll have to find the target used when you call Publish in Visual Studio, it could be Package, WebPublish...
I'm a little lazy right now to figure out the mess of targets to find the right one for a file-based publish (which you might be interested in). What you can do in the meantime is defining an AfterBuild target in the *.pubxml file.
<Target Name="AfterBuild">
...
I recommend also turning off the property "DeleteExistingFiles" because if you copy files into the directories being published, it does a clean somewhere during the publishing process.
<DeleteExistingFiles>False</DeleteExistingFiles>

Make a target run once at the Solution level in MSBuild

I need a set of tasks that need to be executed exactly once for the entire solution. This will run tasks that will modify each project to run a separate set of tasks for each project. We had done this earlier using a separate project to the solution which had the solution level tasks, but we want to move away from that. Has anyone done this or does anyone have any suggestions on how to implement this?
Since Solution files are not in MSBuild format they are not easily extended or customized. If you want more control over the build process you would have to create a "driver" msbuild file which would replace your solution file. Inside this driver file you would build all the projects that you needed and perform some additional tasks. You would do this using the MSBuild task. Here is a sample showing how to build more than 1 project.
<Project ...>
<ItemGroup>
<Projects Include="proj01.csproj"/>
<Projects Include="proj02.csproj"/>
<Projects Include="proj03.csproj"/>
</ItemGroup>
<Target Name="BuildAll">
<MSBuild Projects="#(Projects)" BuildInParallel="true" />
</Target>
</Project>
So in your case you would just execute the tasks before you build the projects. Also note that I specified the value true for the BuildInParallel indicating that MSBuild can try and build more than one project at once.
An alternative solution is to have a single target that dispatches to an MSBuild invoked target with as many Global properties removed as possible. My team have a target in the InitialTargets of a Directory.Build.props Import'ed props file - something like:
<Target Name="Prebuild">
<MSBuild Projects="$(MSBuildThisFileFullPath)"
Targets="PrebuildWorker"
RemoveProperties="Configuration;Platform;TargetFramework;BuildProjectReferences" />
</Target>
Since MSBuild appears to synchronize parallel builds on the {project file, global properties, target} set, then by removing all of the properties you can synchronize the build and run it once for all projects being built. The downside: you have to maintain the RemoveProperties attribute - MSBuild doesn't have a way to remove all global properties. If something in the build issues a new MSBuild task with a custom property specified, then you'll get a second instance of the Target invoked.
And - of course - your build will be synchronized on this target. You could try hooking the target up by setting, say, CompileDependsOn to depend on the Prebuild target, to allow independent progress in the build. But to have the target run early and ubiquitously using InitialTargets seems like the better option.