Limit custom MSBuild target to the file(s) selected in Visual Studio - visual-studio

I have a small custom MSBuild target:
<ItemGroup>
<Foo Include="file1.foo"/>
<Foo Include="file2.foo"/>
<Foo Include="file3.foo"/>
...
</ItemGroup>
<Target Name="Foo2Bar" Inputs="foo.config;#(Foo)" Outputs="%(Foo.Filename).bar">
<Exec Command="path\to\script #(Foo) -o %(Foo.Filename).bar"/>
</Target>
Now I want to be able to to select one of the *.foo-files in Visual Studio and trigger processing just this one (e.g. by pressing CtrlF7 or right clicking in the Solution Explorer -> Compile). It seems that this sets a property $(SelectedFiles), but I don't find any usable tutorial or example how to make this work...

Limit custom MSBuild target to the file(s) selected in Visual Studio
If I understand you correctly, you want to select one of *.foo file to the target, if yes, you can pass the list around as a property, so we need to convert item into a property:
<Target Name="BuildMigrationZip">
<PropertyGroup>
<FooProperty>#(Foo)</FooProperty>
</PropertyGroup>
<MSBuild Projects="$(MSBuildThisFile)" Targets="Foo2Bar"
Properties="FilesToFoo=$(FooProperty)" />
</Target>
Then when we build the this file with MSBuild command line, we could pass the property FooProperty:
msbuild.exe "YourCustomTargetFile" /p:FooProperty=file1.foo
You can check this thread for some more details info.
If I understand you incorrect, please let me know for free, I will keep follow ASAP.
Hope this helps.

Related

Visual Studio constantly triggers MSBuild Targets

I want to execute a text template before my MSBuild project in Visual Studio. I have added the following to my project file:
<Target Name="TransformOnBuild" BeforeTargets="ResolveProjectReferences">
<PropertyGroup>
<_TransformExe>$(MSBuildExtensionsPath)\..\Common7\IDE\TextTransform.exe</_TransformExe>
<_TextTransform>$(ProjectDir)AssemblyInfo.tt</_TextTransform>
<_TextTransformResult>$(ProjectDir)AssemblyInfo.cs</_TextTransformResult>
</PropertyGroup>
<Exec Command="del "$(_TextTransformResult)"" />
<Exec Command=""$(_TransformExe)" "$(_TextTransform)" -out "$(_TextTransformResult)"" />
</Target>
This simply deletes my AssemblyInfo.cs and regenerates it from AssemblyInfo.tt.
I use BeforeTargets="ResolveProjectReferences" because I need this file regenerated before any of the referenced projects get built.
Basically, this already works but I have noticed something strange: When I have this in my project file while Visual Studio is open, the AssemblyInfo.cs file constantly dissappears and then reappears. To me it looks like VS repeatedly executes my build target in the background. Of course I don't want it to behave like this. I want it to regenerate the file only when I start a build.
Is there any way to achieve my goal without generating constant CPU load and annoying file-wobbling in the explorer? Maybe a different base target than ResolveProjectReferences?
I use Visual Studio Professional 2022, Version 17.2.6
Update based on latest comments.
You could also try Condition="'$(DesignTimeBuild)' != 'true'".
Details/Background.
If you can live withit never being run inside Visual Studio, you can add this condition to the target element:
Condition="'$(BuildingInsideVisualStudio)' != 'true'"
Otherwise you can try this:
<Target Name="TransformOnBuild" BeforeTargets="ResolveProjectReferences"
Inputs="$(MSBuildProjectDirectory)AssemblyInfo.tt"
Outputs="$(MSBuildProjectDirectory)AssemblyInfo.cs">
<PropertyGroup>
<!-- ... -->
</Target>
You can learn more about Inputs/Outputs here. Basically, in this case, it means that the target will only be run, when AssemblyInfo.tt is newer than AssemblyInfo.cs.
Note that VS (for intellisence, etc.) will run targets in the background.

How to pass parameter to MSBuild from Visual Studio?

My issue is simple (or it is supposed to be so)
I want to pass some parameter to MSBuild From Visual Studio, Not from the command line
Put it in another way, How can I execute the following command when I click Build project from Visual Studio
msbuild myproject.csproj -p:CustomParameter=ItsValue
Thank you
On Visual Studio, you only have to modify the proj file of the project. And this special and simple assignment is only a unique way of msbuild command line.
So you have to right-click on your project on the Solution Explorer-->Unload Project-->then right-click on the unload project-->Edit Project File,
add this at the bottom of the proj file:
<PropertyGroup>
<CustomParameter>ItsValue</CustomParameter>
</PropertyGroup>
Every time you want to change the custom value, you have to modify the property on the proj file.
This is the only way and actually, it is indeed not as convenient as the command line.
============================================
Update 1
1) add a new file called test.props file on the project folder
then add these content on the on the test.props file:
<Project>
<PropertyGroup>
<CustomParameter>ItsValue</CustomParameter>
</PropertyGroup>
</Project>
2) modify your proj file with this:
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="test.props"/>
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<Target Name="test123" AfterTargets="Build">
<Message Importance="high" Text="$(CustomParameter)"></Message>
</Target>
</Project>
3) every time you should open test.props file, change the value of CustomParameter,save, click Build button, you will get what you want.
So far, this is by far the easiest way.

Determine whether it's a build or rebuild in .cmd script called in prelink step inside Visual Studio

How can a .cmd script run from within a Visual Studio (2005, 2008, 2010, 2012 and 2013 respectively) project's pre-link stage determine whether this is a full rebuild (Build.RebuildSolution/Build.RebuildOnlyProject) or "ordinary" build (Build.BuildSolution/Build.BuildOnlyProject)?
This is an external script (LuaJIT, if you must know) and I don't want to rebuild the library every single build of the project. Instead I'd like to limit the complete rebuild to situations where I choose exactly that option.
How can a .cmd script run from within a Visual Studio (2005, 2008, 2010, 2012 and 2013 respectively) project's pre-link stage determine whether this is a full rebuild ... or "ordinary" build ... ?
I do not know if the exact thing that you are asking can be done - perhaps someone else knows how to do it. I will, however, suggest an alternate approach.
My approach is to remove the build of the Lua library from the pre-link step to a separate Visual Studio NMake project. If you create an NMake project, you will be able to know which type of build (build or rebuild) is occurring.
Note that later versions of Visual Studio simply refer to the project type as "Make". For discussion purposes here, I will refer to the project type as "NMake". I believe this is just a naming difference, and that the underlying build project remains the same between the two versions.
As a simple test, I created two Visual Studio applications: 1) an NMake project that calls a batch file to create a static library, and 2) a console application that consumes the library from step 1.
The NMake Project
In Visual Studio, if you create a new NMake project, you will see a dialog that allows you to provide MS-DOS commands:
As you can see, there are commands for: Build, Clean, Rebuild, and others. I don't have a screen shot of the above dialog with my commands, but here is my NMake project's properties:
My Build command just checks for the existence of the output file (lua.lib). If it does not exist, then it calls the rebuild.bat batch file. My Rebuild command always calls the batch file. My Clean command just deletes the output. I am not really sure what the Output command is used for, but I just filled in the path to the build output (lua.lib).
Now if you do a build, the lua.lib file will only be created if it is not there. If it is already there, nothing is done. If you do a rebuild, then a new lua.lib file is created.
The Console Application
In my console application, I added a reference to the NMake project - this way the NMake project is built prior to the console application. Here is the console application's reference page:
I also added the lua.lib file as an input during the application's link stage:
When the console application is built (during a build), it will build the NMake project if needed, and use the output (lua.lib) during the linker stage. When the console application is rebuilt (during a rebuild), it will also rebuild the NMake project.
Other Thoughts
My screen shots above only show the debug version of the properties. Your projects will have to account for the release version. There probably is a VS macro to handle this, but I am not sure since it has been ages since I've done anything with C/ C++.
In my testing above I use a single build batch file for both the build and rebuild. Obviously, you could do the same or you could use different batch files.
It may be a bit of a hack, but in .csproj file there are sections
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
You can set an variable from BeforeBuild and retrieve it from cmd script. Later on reset this variable in AfterBuild and you should be good to go.
Ok, this is going to be a long one.
First of all - do not take my code 'as is' - it is terrible one with lots of hacks, I had no idea msbuild is so broken by default (it seems at work I have access to waaaay more commands that make life easier). And another thing - it seems vcxproj is broken at some poin - I was not able to integrate the way I wanted with only BeforeRebuild and AfterRebuild targets - I had to redefine hole Rebuild target (it is located in C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets)
So, the idea is the following: when a Rebuild is happening we create an anchor. Then, during PreLink stage we execute cmd which is able to use created anchor. If the anchor is in place - we deal with Rebuild, if there is no anchor - it is a simple Build. After Rebuild is done - we delete the anchor.
modifications in vcxproj file:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
....
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
....
<PreLinkEventUseInBuild>true</PreLinkEventUseInBuild>
....
</PropertyGroup>
....
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
.....
<PreLinkEvent>
<Command>D:\PreLink\b.cmd</Command>
</PreLinkEvent>
.....
</ItemDefinitionGroup>
.....
<Target Name="BeforeRebuild">
<Exec Command="echo 2 > D:\PreLink\2.txt" />
</Target>
<Target Name="AfterRebuild">
<Exec Command="del D:\PreLink\2.txt" />
</Target>
<!-- This was copied from MS file -->
<PropertyGroup>
<_ProjectDefaultTargets Condition="'$(MSBuildProjectDefaultTargets)' != ''">$(MSBuildProjectDefaultTargets)</_ProjectDefaultTargets>
<_ProjectDefaultTargets Condition="'$(MSBuildProjectDefaultTargets)' == ''">Build</_ProjectDefaultTargets>
<RebuildDependsOn>
BeforeRebuild;
Clean;
$(_ProjectDefaultTargets);
AfterRebuild;
</RebuildDependsOn>
<RebuildDependsOn Condition=" '$(MSBuildProjectDefaultTargets)' == 'Rebuild' " >
BeforeRebuild;
Clean;
Build;
AfterRebuild;
</RebuildDependsOn>
</PropertyGroup>
<Target
Name="Rebuild"
Condition=" '$(_InvalidConfigurationWarning)' != 'true' "
DependsOnTargets="$(RebuildDependsOn)"
Returns="$(TargetPath)"/>
<!-- End of copy -->
</Project>
And the cmd looks like this:
if exist 2.txt (
echo Rebuild818181
) else (
echo Build12312312
)
The output from Output window:
1>Task "Exec" (TaskId:41)
1> Task Parameter:Command=D:\PreLink\b.cmd
1> :VCEnd (TaskId:41)
1> Build12312312 (TaskId:41)
Things to improve:
Use normal variables instead of external file (it seems MsBuild extension pack should do it)
Probably find a way to override only BeforeRebuild and AfterRebuild instead of the hole Rebuild part
It is much easier. Just add the following target to your build file or visual Studio Project
<Target Name="AfterRebuild">
<Message Text="AFTER REBUILD" Importance="High" />
<!--
Do whatever Needs to be done on Rebuild - as the message shows in VS Output
window it is only executed when an explicit rebuild is triggered
-->
</Target>
If you want a two step solution use this as a template:
<PropertyGroup>
<IsRebuild>false</IsRebuild>
</PropertyGroup>
<Target Name="BeforeRebuild">
<Message Text="BEFORE REBUILD" Importance="High" />
<PropertyGroup>
<IsRebuild>true</IsRebuild>
</PropertyGroup>
</Target>
<Target Name="BeforeBuild">
<Message Text="BEFORE BUILD: IsRebuild: $(IsRebuild)" Importance="High" />
</Target>

How do I use an MSBuild file from Visual Studio 2012?

I have a simple MSBuild file that I'm learning with.
Here it is:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Clean" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{D5A16164-962E-4A6D-9382-240F31AB6C50}</ProjectGuid>
</PropertyGroup>
<Target Name="Clean">
<ItemGroup>
<BinFiles Include="bin\*.*" />
<fff Include="f\*.*" />
</ItemGroup>
<Delete Files="#(BinFiles)" />
<Delete Files="#(fff)" />
</Target>
</Project>
Now I want to include this in a Visual Studio solution and be able to run the "clean" target from Visual Studio 2012. I tried naming it testproject.msbuildproj like the internet seems to suggest "works", but it doesn't work. When I run the clean command I just get "unexpected error".
If I rename the project to testproject.csproj, it does some unintuitive things like creating compilation directories, but it does actually run my clean command properly. However, this is undesireable because it creates obj and bin/x86/debug type directories. It also looks goofy in Visual Studio because it still gives the References drop down.
How can I use just a plain vanilla MSBuild project from Visual Studio without random errors or false assumptions?
Note I only am having a problem with this from Visual Studio. Using msbuild from the command line it works perfectly
Visual Studio creates bin / obj folders when it opens csproj file. When you click Build / Rebuild / Clean it just uses appropriate targets from the project file.
You cannot stop VS from creating these folders, but you can ask it to create them in say temp folder by setting appropriate properties - refer this MSDN article for details.
So the steps are to rename your project to csproj, and add the following lines into project:
<PropertyGroup>
<OutputPath>$(Temp)\bin</OutputPath>
<IntermediateOutputPath>$(Temp)\obj</IntermediateOutputPath>
</PropertyGroup>
I usually use a bit different approach to work with MSBUILD files from VS:
I use regular csproj file with removed Import ... CSharp.targets part as pure container for my Build projects.
I add actual build files with targets and logic, and all properties, necessary artifacts like XSLT etc using "Include into project", so I can manage hierarchy and change any file from within VS.Net.
I redefine Build / Rebuild targets in csproj file for whatever I need, for example Build may contain minimum output, and while rebuild diagnostic one.
Like this:
<Target Name="Build">
<Exec Command="%windir%\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe Builds\build.proj /t:Build /v:m" />
</Target>
<Target Name="Rebuild">
<Exec Command="%windir%\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe Builds\build.proj /t:Build /v:d" />
</Target>

How to turn off caching of build definitions in Visual studio

In project file I import my own target file
<Import Project="Build\CopyDependencies.target" />
and later I call target from that target file
<CallTarget Targets="CopyDependencies" UseResultsCache="false" />
If I edit CopyDependencies.target file I have to reload whole solution and only then changes to CopyDependencies.target take effect. I believe it is some sort of build definitions caching in Visual Studio? If it is, maybe it can be turned off?
Thanks #KazR
Here is a smaller Solution that you can insert into your .csproj file
<Target Name="AfterBuild">
<PropertyGroup>
<TempProjectFile>Build.$([System.Guid]::NewGuid()).proj</TempProjectFile>
</PropertyGroup>
<Copy SourceFiles="Build.proj" DestinationFiles="$(TempProjectFile)" />
<MSBuild Projects="$(TempProjectFile)" />
<ItemGroup>
<TempProjectFiles Include="Build.????????-????-????-????-????????????.proj"/>
</ItemGroup>
<Delete Files="#(TempProjectFiles)" />
</Target>
Problem solved
I don't know how you would disable the VS cache, however I may have a workaround that would allow you to edit the build target without having to reload the solution.
You could use the MSBuild task in your proj file to call a wrapper target that copies your CopyDependencies.target file to CopyDependencies.[RandomNumber].target, then invokes your CopyDependencies target in the newly created file, and finally deletes it.
This would force VS to reload the target on each invocation as the filename is different.
Here's an example:
myProject.proj
Add this to the AfterBuild target:
<MSBuild Projects="Wrapper.target" Targets="MyWrappedTarget" UnloadProjectsOnCompletion="true"/>
Wrapper.target
Here we have the target that will - at build time - copy the real target file and invoke the desired build target within it (I've used an inline c# task which is only available in MSBuild 4.0):
<UsingTask TaskName="RandomNumber" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<Number ParameterType="System.Int32" Output="true"/>
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<!-- CDATA -->
Random rndGenerator = new Random();
Number = rndGenerator.Next(Int32.MaxValue);
<!-- CDATA -->
</Code>
</Task>
</UsingTask>
<Target Name="MyWrappedTarget">
<Message Text="MyWrappedTarget target called"/>
<RandomNumber>
<Output TaskParameter="Number" PropertyName="FileNumber"/>
</RandomNumber>
<PropertyGroup>
<CopiedTarget>inner.test.$(FileNumber).target</CopiedTarget>
</PropertyGroup>
<Copy SourceFiles="inner.test.target" DestinationFiles="$(CopiedTarget)"/>
<MSBuild Projects="$(CopiedTarget)" Targets="_innerTestTarget"/>
<Delete Files="$(CopiedTarget)"/>
</Target>
inner.test.target
This contains the real build target you want to execute, in this example it's a simple file copy.
<Target Name="_innerTestTarget">
<Message Text="This is a inner test text message"/>
<Copy SourceFiles="x.txt" DestinationFiles="x1.txt"/>
</Target>
This isn't production ready, but hopefully illustrates my point.
With this (slightly convoluted) process in place, you can change the inner.test.target file without having to reload the solution in VS.
Here's a solution that doesn't require any MSBuild scripting at all.
I noticed that unloading and reloading a project doesn't get around the cache, but closing and reopening the solution does. In addition, Visual Studio will prompt you to reload the solution if it notices the .sln file has changed. And finally, this superuser question explains how to touch a file in Windows.
Putting these together, I added a Visual Studio external tool to touch the current solution file. Here's how:
Select TOOLS > External Tools ...
Click the Add button to add a new tool.
Set properties as follows:
Title: Reload Solution
Command: cmd.exe
Arguments: /c copy "$(SolutionFileName)"+>nul
Initial directory: $(SolutionDir)
and turn on Use Output window
Click OK to close the External Tools window
Now if you have made changes to your MSBuild files, just select TOOLS > Reload Solution and all your build files will be reloaded.
I'm using Windows 7 64-bit and Visual Studio 2012 Express for Windows Desktop.
I have a different solution, not involving temporary files:
Include.targets file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Foobar">
<Copy SourceFiles="test.source" DestinationFiles="testFoobar.dest" />
</Target>
</Project>
Project file:
....
<Target Name="BeforeBuild">
<Exec Command="$(MSBuildToolsPath)\MSBuild.exe Include.targets /t:Foobar" ContinueOnError="false" />
</Target>
....
in this case VS does not recognize the MSBuild command, and does not cache the file.
happy coding!
Before running MSBuild I run this to clear the download cache:
call "%VS120COMNTOOLS%vsvars32.bat"
echo Clear download cache
gacutil -cdl

Resources