MSBuild - Execute Custom Targets w/o modifying individual projects - msbuild

I have custom MSBuild Tasks to execute after the : AfterBuild event for each project in a solution.
I don't want to modify each Project file as:
Visual Studio wipes out all the Custom Changes done to the project file once i modify the project in Visual Studio (VS 2012 Ultimate), say add or remove a file/reference.
I don't want to use "CustomAfterMicrosoftCommonTargets" as mentioned here as there is no way to pass this command line argument while building from Visual Studio :
msbuild.exe app.proj /property:CustomAfterMicrosoftCommonTargets=custom.target
I found a solution here, but I didn't quite get it.
Can anybody please elaborate on it or help me figure out a better solution?
Update 5/12/2014:
I figured out that Visual Studio doesn't wipe out the custom changes if I am running the Visual Studio in the Administrator mode.
I can now think of having a Custom import file that has got the required overrides, but still I have to do this for each project in the solution. If somebody adds a new project, they have to remember to add this customization. I don't like this, but probably I can live with for now.
I tried to use the "CustomAfterMicrosoftCommonTargets" approach, but I was not able to set this property from the Pre-build event of Visual Studio, even running as Administrator didn't help.
I was trying to set an environment variable with same name from the Pre-Build event, but I never got the new value while MSBuild executes.
Thanks!

Finally I found an option where in I don't have to edit the individual project file.
The idea is to invoke your custom common targets file in the "AfterBuild" event that Visual Studio exposes.
IF "$(BuildingInsideVisualStudio)"=="true" (
$(MSBuildBinPath)\msbuild.exe "$(ProjectDir)CustomMSBuild.targets" /p:Configuration="$(Configuration)"/property:"ProjectUnderCompilation=$(MSBuildThisFileDirectory)$(MSBuildThisFile)"
)
So I am passing the project under compilation as a property and import that project file.
If I throw an exception in the custom task, it appears as as Compilation error on the parent project.
This worked amazingly and I am able to perform any validations on the project that was passed.
The only downside I see is that I am spawning another MSBuild.exe and I don't see any impact of that in the compilation time as of now.
Please let me know your thoughts on this implementation.
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="MyTarget">
<UsingTask AssemblyFile="$(ProjectDir)\bin\TaskLibrary.dll" TaskName="CheckProjectReferences" />
<PropertyGroup>
<ProjectUnderCompilation></ProjectUnderCompilation>
</PropertyGroup>
<Target Name="MyTarget">
<Message Text="Inside MyTarget" Importance="High" />
<CheckProjectReferences/>
</Target>
<Import Project="$(ProjectUnderCompilation)" />
</Project>

Related

How to change msbuild working directory in TFS 2013 workflow

I have a TFS 2013 build xaml workflow, that eventually calls the Microsoft.TeamFoundation.Build.Workflow.Activities.MSBuild activity once for each solution that I want to build. When msbuild.exe is called, it's working directory is the working directory of the current solution being built. I can see this through the 'MSBuildStartupDirectory' property when running msbuild with a 'diagnostic' verbosity.
Unfortunately, I need the working of msbuild.exe to be somewhere else when msbuild.exe starts. This is because I use the MSBuild SonarQube runner that imposes constraints on the directory from which msbuild is called.
I have looked at the 'msbuild' activity and there is no way to control the working directory. Is there another way to control the working directory of this activity?
Its been a while since I edited a build process template but I believe you could use an activity that just executes a command in CMD and provide the full MSBuild command. I'm sure there are tons of variables you will need to setup for this to work.
Instead of editing the build process template have you considered using a PowerShell script in the Post-build script to execute SonarQube?
I still haven't found any way to control the working directory of msbuild. But since I know that the working directory will be the directory of the project being built by msbuild, I created a new proj file at the root of my workspace (where my working directory has to be) and only build this new proj file from my workflow. This new proj file then builds all my other solutions. That way, my working directory is the same for all the solutions being built.
Here is an example of my top level proj file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<ItemGroup>
<Solutions Include="**\*.sln"/>
</ItemGroup>
<Target Name="Build">
<MSBuild Projects="#(Solutions)" Targets="Build"/>
</Target>
</Project>
But beware that doing this may affect the output directory (OutDir) given to each solution. So you may want to do something like this:
<MSBuild Projects="#(Solutions)" Targets="Build" Properties="OutDir=$(OutDir)..\%(Solutions.Filename)"/>

How to exclude some stylecop rule in visual studio 2013

I'm using visual studio 2013.
I install stylecop using NuGet Package follow these steps:
https://www.nuget.org/packages/StyleCop.MSBuild/
Previously when I using visual studio 2010, I usually put my custom rules set called Setting.StyleCop file to my solution or project.
How should I implement my custom rules set in VS 2013?
Settings.StyleCop still works as it did previously.
The StyleCop.MsBuild package performs a couple of actions:
1. Stores an instance of StyleCop.exe in the packages directory, this makes it portable between machines, build servers etc.. Namely \packages\StyleCop.MSBuild.4.7.49.1\tools this is pretty much the same you would get with installing it, but its not in program files.
2. Makes changes to the csproj, namely:
<Import Project="..\..\packages\StyleCop.MSBuild.4.7.49.1\build\StyleCop.MSBuild.Targets" Condition="Exists('..\..\packages\StyleCop.MSBuild.4.7.49.1\build\StyleCop.MSBuild.Targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\StyleCop.MSBuild.4.7.49.1\build\StyleCop.MSBuild.Targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\StyleCop.MSBuild.4.7.49.1\build\StyleCop.MSBuild.Targets'))" />
</Target>
All this basically does is point at the targets file so that it runs stylecop and adds in some requires for the package to be there before building.
What you need to do to use StyleCop.Settings
Copy it into the root of your solution, or the project file, then it should get picked up as usual. The easiest way is just to copy it from \packages\StyleCop.MSBuild.4.7.49.1\tools

In MSBUILD, how can you specify a condition that check whether command line or VS launched it?

I have a csproj that I would like to have trigger the opening of a particular file in Visual Studio, only if the target was executed from within Visual Studio, but not from the MSBUILD command line. How do I do this?
Quote from MSDN page:
When building inside Visual Studio, the property $(BuildingInsideVisualStudio) is set to true. This can be used in your project or .targets files to cause the build to behave differently.
Example how it could be used in your .*proj or .targets file:
<PropertyGroup>
<MyProperty Condition="'$(BuildingInsideVisualStudio)' == 'true'">This build is done by VS</MyProperty>
<MyProperty Condition="'$(BuildingInsideVisualStudio)' != 'true'">This build is done from command line of by TFS</MyProperty>
</PropertyGroup>
Add a property to the .csproj project file, example:
<PropertyGroup>
<FromMSBuild>false</FromMSBuild>
</PropertyGroup>
Then in the task you want to run, put a condition that evaluates that property. For example, i f you want to open notepad.exe whenever the build is executed from command line and NOT visual studio:
<Target Name="BeforeBuild">
<Exec Command="C:\Windows\Notepad.exe" Condition="$(FromMSBuild)" />
</Target>
Of course, this is dependent on setting the $(FromMSBuild) property correctly when you run the build via command line, like so:
MSBuild myProject.csproj /p:FromMSBuild=true
If I understand you correctly, you want to open a file when building in visual studio but not from command line with MSBuild?
If that is the case, specify a PreBuild or PostBuild in Visual Studio.
Right click on the project in the solution explorer and select Properties
Select the Events tab
Add either a Pre or Post Build event to open the desired file

Using a project file as a parameter in MSBuild with Hudson

I'm currently using the Hudson build system with MSBuild steps. As part of the build, I have a project file with various targets in, one of which is to start a build with visual studio. However, I need to pass through a seperate project file to this target in order for it to build, but I keep getting the exception 'MSBUILD : error MSB1008: Only one project can be specified.'
I believe this is because the system is unable to calculate which project is supposed to be the parameter, and which the top-level target? If so, is there anyway to resolve this.
Here is a snippet of the target project file:
<Target Name="VisualStudioTask">
<!-- Required Properties:
$(BuildType)
$(ConfigurationSetup)
$(Solution)-->
<Exec Command="C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv.exe $(BuildType) $(ConfigurationSetup) $(Solution)" />
</Target>
The MSBuild step calling this looks like this:
/t:VisualStudioTask -p:BuildType="/Build" p:ConfigurationSetup="Release" -p:Solution="%22..\MyProject.vcproj%22"
Many thanks
Chris
I have figured it out, the problem was that I'd left out a '-' when declaring the 'ConfigurationSetup' parameter, so if you look in my original example it has this:
/t:VisualStudioTask -p:BuildType="/Build" p:ConfigurationSetup="Release" -p:Solution="%22..\MyProject.vcproj%22"
When it should have this..
/t:VisualStudioTask -p:BuildType="/Build" -p:ConfigurationSetup="Release" -p:Solution="%22..\MyProject.vcproj%22"

How do I add an MSBuild .proj file to my solution?

Does anyone know how to add a an MSBuild .proj file to my solution?
I was just given existing code from a vendor with a solution that references an MSBuild .proj file as one of its projects. When I open the solution, the project shows as (unavailable). It appears that I need to install some sort of project template to get this project to open correctly. I installed the Codeplex MSBuild Template, but this doesn't appear to be it.
Any ideas?
If you don't need IDE support, it's possible to do this using MSBuild solution extension targets.
Create a file named "before.SolutionName.sln.targets" with the following code:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ProjectReference Include="CustomProject\CustomProject.proj">
<AdditionalProperties>Configuration=$(Configuration); Platform=AnyCPU</AdditionalProperties>
<Configuration>$(Configuration)</Configuration>
<Platform>AnyCPU</Platform>
</ProjectReference>
</ItemGroup>
</Project>
When your solution is built at command line by MSBuild (ie/ build server) the custom MSBuild project will be pulled into the temporary in-memory project file that MSBuild converts the solution into.
I actually got it to work! I re-started Visual Studio and still saw that the projects were unavailable after installing the MSBuild Template mentioned above. I had to manually reload the projects. That fixed the issue.