Override One Property in a .targets file - visual-studio

Background: I have several solutions with roughly 300 C++ projects across them, most of them shared. We are using Visual Studio 2013 and have a build script that compiles all of the projects in the correct order, ensuring dependencies are resolved ahead of time. Our development/engineering team builds all of the code through the build script and then attempts to debug using Visual Studio 2013.
Issue: The "build then debug" process results in Visual Studio telling us that the Projects are out of date. This stems from the ProjectEvaluationFingerprint property (in Line 39 Microsoft.CppBuild.targets) including a $(SolutionDir) in the output file. The recommended fix from Microsoft suggests removing the $(SolutionDir) from the file. As our developers tends to transition back and forth between projects, I do not want to manually change this .targets file on every developer's machine (and remember to change it back when they leave the project). I would like to override the property in the .vcxproj by using a .targets file explicitly for this.
The property in Microsoft.CppBuild.targets looks like:
<!-- Global up-to-date check support -->
<PropertyGroup>
<ProjectEvaluationFingerprint>$(Configuration)|$(Platform)|$(SolutionDir)|$(ProjectEvaluationFingerprint)</ProjectEvaluationFingerprint>
</PropertyGroup>
Generally, I have been following Microsoft's How to: Use the Same Target in Multiple Project Files. I have created a .targets file (test.targets) that contains the following code (note the TEST text was to test evaluation of the property in both the build script and building the project in Visual Studio):
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectEvaluationFingerprint>$(Configuration)|$(Platform)|TEST|$(ProjectEvaluationFingerprint)</ProjectEvaluationFingerprint>
</PropertyGroup>
I then import it using the following line in the .vcxproj
<Import Project="..\..\Config\VSPropertySheets\test.targets" />
The project.lastbuildstate file now reads:
#TargetFrameworkVersion=v4.0:PlatformToolSet=v120_xp:EnableManagedIncrementalBuild=false:VCToolArchitecture=Native32Bit
Debug|Win32|D:\views\devbranch\Products\SLN\|Debug|Win32|TEST|
It is appending the new ProjectEvaluationFingerprint to the existing one, so it is not overriding (I can understand this to a degree, but I'm no MSBuild expert).
Question: How can I override this one property using a .targets file? Do I need to use a replaceregexp task or do I have an easier option?

You can override this property, but you have to be careful about two things:
the new setting you want is this:
<ProjectEvaluationFingerprint>$(Configuration)|$(Platform)|TEST/ProjectEvaluationFingerprint>
Note the removal of $(ProjectEvaluationFingerprint), which would contain the previous value of this tag
the location where you put the import is important: you will want to put it at the very end of your project (i.e. after the Microsoft.CppBuild.targets import).
Concretely:
use_custom_fingerprint.targets
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectEvaluationFingerprint>$(Configuration)|$(Platform)</ProjectEvaluationFingerprint>
</PropertyGroup>
</Project>
project.vcxproj
<Project ...>
...
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
<Import Project="use_custom_fingerprint.targets" />
</Project>
Note that I also tried the extension .props and this worked just the same.
Note: The new import after importing Microsoft.CppBuild.targets.$(Platform).user.props is not sufficient, it must be after Microsoft.CppBuild.targets.
Disclaimer: tried in Visual Studio 2015

I have the same problem. I was able to progress a step further than you, but I still haven't a full solution.
The reason why you have now the old fingerprint appended to the new one without solution dir is your line
<ProjectEvaluationFingerprint>$(Configuration)|$(Platform)|TEST|$(ProjectEvaluationFingerprint)</ProjectEvaluationFingerprint>
The
$(ProjectEvaluationFingerprint)
Holds the old fingerprint, so just remove this part from the value for ProjectEvaluationFingerprint and your lastbuildstate will have the desired value.
Sadly now (at least for me) Visual Studio always thinks the fingerprint is wrong and will re-link the project with every compile, not only when switching sln file.
I removed the line from the props sheet and the up-to-date check works again as expected as long as solution directory doesn't change. I then modified the Microsoft.CppBuild.targets directly and this works: No more "not up-to-date" projects, even when switching solution directory.

Related

Visual Studio - Automatically include new files in project

I have a web app that generates some configuration files that I need to include into my VS-project. I know this can be done manually and also that there is a wildcard-solution (Auto include new files created outside Visual Studio) but I'm not really pleased with this.
I need the files to be included without having to reload the project, and also VS sometimes changes the wildcard configuration and reference the individual files.
I'm thinking that there might be some plugin etc that could to this? Something with a file system watcher that includes the files?
Or does anyone know how to prevent VS from changing the wildcard-config?
Edit: I think that the changed in the csproj are triggered when you ie add a new file to the project. Then VS removes the folder** and adds a direct reference to all the files included and to the new file.
Edit 2: Seems like this works until you remove a file from the solution explorer, that's when VS creates all these "hard" references to a file.
I came up with a solution that seems to work, that is importing an external project, in my YadaYada.Web.csproj:
<Import Project="..\CustomBuild.targets" />
And then in CustomBuild.targets
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Content Include="$(MSBuildProjectDirectory)\MyConfigurationFolder\**" />
</ItemGroup>
</Project>
This will included the files in my builds but not in the VS-ui which is probably fine for me.
Thanks!

Set MSBuild default parameters when building locally

Is there a way to set a default set of parameters for MSBuild to use every time i build locally using Ctrl + Shift + B. I want to append a parameter like /p:MyParameter to the MSBuild call.
Thanks in advance!
I am not sure what parameters you want to pass and how will those be used by your project solution. But if those parameters are ought to be used by your project solution then you can create a PropertyGroup element and use it within your project at the time when VS builds your solution. Refer this tutorial
I assume the scenario is the following: you want to use some MsBuild variable values when debugging on your local machine, but don't want to use them in production.
Add the following directive to the Visual Studio project or .targets file:
<Import Project="User.targets" Condition="Exists('User.targets')"/>
Create the User.targets file - example:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MyProperty>Value</MyProperty>
</PropertyGroup>
</Project>
Exclude this file from Git/Mercurial repository.
Close and restart Visual Studio.
Warning: when you change the referenced file, the changes may not take effect immediately; for them to take effect, either close and restart Visual Studio, or change the time of last change ("touch") on the solution file to force Visual Studio to reload it.

Visual Studio and Imported MSBuild properties in vcxproj files

I was trying to factor out some properties in some C++ projects in a Visual Studio 2013 Update 4 solution into separate property files which I then Import into the vcxproj files. After doing so I noticed that the properties no longer seem to be reflected in the properties editor GUI. For instance if I Import this from some file
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
rather than have it defined directly in the vcxproj file the "Character Set" item in the properties GUI appears as blank. However the C++ Command Line in the properties GUI does contain '/D "_UNICODE" /D "UNICODE"' so it would appear that the property is being noticed and taking effect.
So is this just a GUI thing or will doing something like this cause stuff to not to build correctly? My guess is that Visual Studio looks for elements like '' but only does so directly in the vcxproj file but not in anything it Imports.
Visual Studio 2010, 2012, 2013, 2015, 2017 and 2019 creates list of visualized and editable options only from items in project file itself.
Items from included files are used only as default values for configuration options. Values from included files are processed and used, even if they are not visualized in GUI.
To see something in GUI, you have to add at least empty corresponding item in .vcxproj file.
For example, to see main configuration, you need to add empty "PropertyGroup"
with label "Configuration" (for each project configuration) to .vcxproj file:
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
</PropertyGroup>
To see name, location of output files and other general properties, add empty "PropertyGroup" without label:
<PropertyGroup />
To see C/C++ compiler, linker and lib settings, add "ItemDefinitionGroup" with "ClCompile", "Link" and "Lib" items to .vcxproj file:
<ItemDefinitionGroup>
<ClCompile />
<Link />
<Lib />
</ItemDefinitionGroup>
If you add everything, mentioned here, to your .vcxproj file, you will see all C/C++ settings in GUI and you will be able to change them.
If you are using 'propertysheets' properly you can see them in the Property Manager window in VS, nicely and hierarchically ordered, including the options contained in them. Which is really very convenient.
I guess you now manually modified the vcxproj and added an Import. While that works as well (as you can see is reflected in the commandline), it doesn't play together well with VS. So, don't do that but revert your change and head over to the property manager and add some property files there. VS will create the Import statements for you in the correct place, and should display all properties as usual.

Does Visual Studio support adding MSBuild tasks to projects?

I'm trying to add some simple MSBuild tasks to a Visual Studio project (VS 2012 Express) - specifically, to create a subdirectory then copy some files to a subdirectory of the output directory ready for packaging.
I see that VS supports custom build steps, which are command-line invocations. However, since VS is based on MSBuild it should be possible to add these directly as MSBuild tasks like the Copy Task in the AfterBuild pre-defined target.
What I can't find is any way to actually add such tasks within the framework of Visual Studio. The documentation only talks about it from an MSBuild perspective, not how it works within Visual Studio's UI. It also doesn't seem to discuss the properties that refer to build output etc there; presumably they're just those used by msbuild its self.
Is there support for MSBuild task management in Visual Studio's UI and it's just crippled out of my Express edition? Or do I have to go hack the project file XML to add MSBuild tasks? Is that supported and the way it's supposed to be done?
I'm used to working with Eclipse and Ant or Maven, where all this is supported within the IDE, though of course you can hack the XML directly. Finding no UI at all for MSBuild task management in Visual Studio is quite confusing. Am I missing the obvious or crippled by using the freebie edition?
For C++ projects, you can use the property
<CppCleanDependsOn>DeleteOutputs;$(CppCleanDependsOn)</CppCleanDependsOn>
instead of defining the BeforeClean target like you did.
From what I read, CallTarget is to be avoided. In your example, you should use DependsOnTargets to do that, as you see in many dummy targets in the MS supplied files. The analogous mechanism of a function where a target just "calls" other targets is done with DependsOnTargets. The flow is not really the same as procedural programming.
Intellisense: I never use it. Is that true for conditional AdditionalIncludeDirectories in the props file only? Go ahead and edit the entry in the proj file where the IDE put it, if you edit the property in the IDE with just one configuration chosen.
(After a bunch more reading I found out how this works):
Visual Studio doesn't seem to expose advanced MSBuild project editing, even though modern vcxproj files are just MSBuild project files with a bunch of extra labeled properties and other entries for Visual Studio IDE specifics. So you have to hack the project XML.
To make it cleaner, only add one line to your actual vcxproj file - an include of a .targets file that contains the rest of your build customisations. e.g, just before the end of the project file, insert:
<Import Project="pg_sysdatetime.targets" />
</Build>
Now create your .targets file with the same structure as any other MSBuild project. Here's mine from the project I've been working on:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- MSBuild extension targets for Visual Studio build -->
<PropertyGroup>
<DistDir>pg_sysdatetime_pg$(PGMAJORVERSION)-$(Configuration)-$(Platform)</DistDir>
</PropertyGroup>
<ItemGroup>
<DocFiles Include="README.md;LICENSE"/>
<ExtensionSourceFiles Include="pg_sysdatetime--1.0.sql;pg_sysdatetime.control"/>
<ExtensionDll Include="$(TargetDir)\pg_sysdatetime.dll"/>
</ItemGroup>
<Target Name="CopyOutputs">
<Message Text="Copying build product to $(DistDir)" Importance="high" />
<Copy
SourceFiles="#(DocFiles)"
DestinationFolder="$(DistDir)"
/>
<Copy
SourceFiles="#(ExtensionDll)"
DestinationFolder="$(DistDir)\lib"
/>
<Copy
SourceFiles="#(ExtensionSourceFiles)"
DestinationFolder="$(DistDir)\share\extension"
/>
</Target>
<Target Name="DeleteOutputs">
<Message Text="Deleting $(DistDir)" Importance="normal" />
<Delete Files="$(DistDir)"/>
</Target>
<!-- Attach to Visual Studio build hooks -->
<Target Name="BeforeClean">
<CallTarget Targets="DeleteOutputs"/>
</Target>
<Target Name="AfterBuild">
<CallTarget Targets="CopyOutputs"/>
</Target>
</Project>
This can contain whatver MSBuild tasks you want, grouped into targets. It can also have property groups, item groups, and whatever else MSBuild supports.
To integrate into Visual Studio you add specially named targets that invoke what you want. Here you can see I've defined the BeforeClean and AfterBuild targets. You can get the supported targets from the VS integration docs.
Now, when I build or rebuild, a new directory containing the product DLL and a bunch of static files is automatically created, ready to zip up. If I wanted I could add the Nuget package for MSBuild Community Extensions and use the Zip task to bundle the whole thing into a zip file at the end too.
BTW, while you can define properties in your .targets files it's better to define them in property sheets instead. That way they're visible in the UI.
I'm using VS2010 Pro, and it doesn't expose the AfterBuild target, at least in C++ projects which is what I'm doing. As you see, it does have the "Events", which according to what I've read are for backward compatibility with converted projects from VSBuild. I agree, a MSBuild task rather than a command script is the way to go.
Forget the UI. It's made to support free editing of the XML files, and continue using the UI too as it respects what you had in there and uses labels for its own stuff so it can find it to update it.
But to keep it neat, you could use a property page; a stand-alone XML file with *.props name, and put what you want in it. Then add that props file to the projects using the UI. You won't hand-edit the project file that the UI is maintaining, and it won't touch the props file unless you go through the property manager view and open it explicitly.
Oh, I also recall seeing additional standard targets something like Package and Publish. Maybe those are not used on your project type, but you could use those entry points anyway.

MSBuild Inheriting Platform Toolset

I am attempting to overhaul my project's build processes. We have ~330 Visual C++ projects that we have upgraded in the last year from Visual Studio 2005 to Visual Studio 2013. I would like to take advantage of MSBuild to improve our build time over our very serial build scripts that we have now. I have completed a rough first pass and dropped the build times for a Release build from ~2 hours to ~20 minutes. In the process of doing this, I am consolidating a lot of common project settings into a .props file . In doing so, I have hit a stumbling block.
I wish to inherit the Platform Toolset from one VSProps file to all of the projects that include it. At the top of the new .props file I created, I put the following:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="Configuration">
<PlatformToolSet>v120</PlatformToolSet>
</PropertyGroup>
<PropertyGroup Label="UserMacros" />
I then removed the corresponding <PlatformToolSet>v120</PlatformToolset> from the individual project files.
Alas, things have started to go downhill. The projects (in Visual Studio 2013) now say in the Solution Explorer something like CoreGeometry (Visual Studio 2010) and the projects themselves seem to want to reference the v100 platform toolset. When I build, it then complains at me:
C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120\Microsoft.CppBuild.targets(362,5): warning MSB8003: Could not find WindowsSDKDir variable from the registry. TargetFrameworkVersion or PlatformToolset may be set to an invalid version number.
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120\Microsoft.CppCommon.targets(341,5): error MSB6006: "CL.exe" exited with code -1073741515.
The only way I have been able to work around this is to manually set the PlatformToolset on the .vcxproj themselves, which is not terrible, I just am a bit annoyed that every other property seems to inherit, but the PlatformToolset does not.
My question is thus:
Can I use a .props file to inherit a common PlatformToolSet into a .vcxproj that does not specify a platform toolset?
A second question: Should I even be messing with the Platform ToolSet in this manner or am I setting myself up at risk for a maintenance nightmare later?
It is very good practice to extract common settings to a separate .props file and <Import> that from all projects. I am doing the same with my projects, including configuring PlatformToolset property in .props file, and I have no problems building it this way.
Few points related to this:
There is nothing special about PlatformToolset property, or any other property for that matter. Configuring properties inside .props file is identical to setting it inside .vcxproj file directly (however see my point below about ordering). Of course, there are some built-in properties, which you cannot configure at all, but those are always read-only properties.
The only case where you would not be able to override a property, if it the property value is passed directly from command line for the build (e.g. msbuild mysolution.sln /p:Platform=x86 will have everything built with Platform property set to x86 and overrides in projects won't take effect).
There is a difference between msbuild engine interpreting your projects and Visual Studio showing settings for the project. In some cases you might find that after refactoring .vcxproj files some standard project configuration dialogs not showing information you configured in .props file. To alleviate this, make sure that your <Import> command for .props file is always able to locate the .props file, by setting absolute path to .props file. Second, ensure you specify Label attribute for the <PropertyGroup> element in your configuration file like it was specified in your .vcxproj file.
Finally, make sure your <Import> element is in the right place. Usually you want it to be the very first Import, before you import standard .targets and .props, like Microsoft.Cpp.defaults.props, etc. The reason is msbuild works by performing sequential scans through the statements, so order of instructions matter.
To make #3 and #4 easier, here is a trick to specify absolute path to the .props file. Assume that your solution name is MySolution.sln and custom props file is MyCustomProps.props, placed in the same directory where solution is:
<PropertyGroup>
<RootFolder>$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory),MySolution.sln))</RootFolder>
</PropertyGroup>
<Import Project="$(RootFolder)\MyCustomProps.props" />

Resources