Visual Studio 2013 and MSBuild command line switches - visual-studio

Is there a way to specify msbuild switches like /p:option=value within Visual Studio 2013?

Formalizing my comment from above.
A possible solution may be to have a targets file (e.g. VisualStudioOverrides.targets) that defines the properties to be overridden and their values. Then, if this file exists then the csproj file will import it, thus override the relevant properties.
Such an import statement should be placed at the bottom of the csproj file (after the properties to be overridden were defined) and would look something like:
<Import Project="$(MSBuildProjectDirectory)\VisualStudioOverrides.targets" Condition="Exists('$(MSBuildProjectDirectory)\VisualStudioOverrides.targets')" />

Related

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.

Visual Studio 2013 and Qt5: QTDIR set too late

Everything works with Qt in MSVC2013, except one thing: the DLLs are not found, because $(QTDIR) is not defined, when the local debugging environment is set.
My debugging environment settings:
PATH=$(QTDIR)\bin%3b$(PATH)
My .user file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LocalDebuggerEnvironment>PATH=$(QTDIR)\bin%3b$(PATH)</LocalDebuggerEnvironment>
<QTDIR>C:\Qt\Qt5.4.1\5.4\msvc2013</QTDIR>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LocalDebuggerEnvironment>PATH="$(QTDIR)\bin%3b$(PATH)</LocalDebuggerEnvironment>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LocalDebuggerEnvironment>PATH=$(QTDIR)\bin%3b"$(QTDIR)\bin%3b$(PATH)</LocalDebuggerEnvironment>
<QTDIR>C:\Qt\Qt5.4.1\5.4\msvc2013</QTDIR>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LocalDebuggerEnvironment>PATH="$(QTDIR)\bin%3b$(PATH)</LocalDebuggerEnvironment>
</PropertyGroup>
</Project>
I don't know why, but the $(QTDIR) variable is not available to LocalDebuggerEnvironment.
It works with following debugging environment settings:
PATH=C:\Qt\Qt5.4.1\5.4\msvc2013\bin%3b$(PATH)
Is it possible to make Visual Studio handle this properly or do I have to enter the path manually?
It seems Visual Studio parses the lines from top to bottom, so with your code...
<LocalDebuggerEnvironment>PATH=$(QTDIR)\bin%3b$(PATH)</LocalDebuggerEnvironment>
<QTDIR>C:\Qt\Qt5.4.1\5.4\msvc2013</QTDIR>
...the variable $(QTDIR) is defined in the second line, and is cannot be used in the first line.
Simply switch the order so that the variable is defined before it is used:
<QTDIR>C:\Qt\Qt5.4.1\5.4\msvc2013</QTDIR>
<LocalDebuggerEnvironment>PATH=$(QTDIR)\bin%3b$(PATH)</LocalDebuggerEnvironment>
Note: Visual Studio only reads the .user file at startup, so you need to (re-)start Visual Studio after editing the file.
Thank you for your discussion of this issue. I can report that it still is extant in Qt 5.8 / QT Extension for Windows Version 2.1.1 / Visual Studio 2015. The symptoms are that entering "$(QTDIR)\bin" (the location of the Qt DLLs) as a PATH folder spec in the VStudio Project property Debugger...Environment appears to work, but actually does not.
By "appears to work" I mean that if you click on the option to examine the Debugger Environment property, and the raw form of the Environment property looks like "PATH=$(QTDIR)\bin;...;$(PATH)" the VStudio editing dialog box will helpfully inform you that that PATH string evaluates to (e.g.) "PATH=D:\TechApps\Qt\5.8\msvc2015_64b\bin;..."--just as it should if the QTDIR macro was really present and had the correct value. This is cruelly deceptive!
Because, by "really doesn't work" I mean that the PATH as actually prepared for use when launching your Qt-dependent application from within VStudio doesn't see this macro. Your beautiful PATH string is reduced to "\bin;...". You can confirm this by temporarily copying your Qt DLLs from $(QTDIR)\bin into the folder of your application executable. Your application will launch correctly. And if you examine your PATH from within your program (using e.g. getenv("PATH") in c++, you will see that your program's PATH string is missing QTDIR. (That is, it is what it would be if QTDIR were the empty string.)
FWIW, another symptom of this problem is that if you examine the VStudio list of macros, $(QTDIR) is not in the list.
The solution described above (rearranging the elements in ...vcxproj.user) appears to solve the problem. When you move the definition(s) of QTDIR earlier in that file, the PATH string actually available to the application is correct, and $(QTDIR) is present in the list of macros known to Visual Studio.
HOWEVER, I don't know whether changing the "Qt VS Tools" options, or the "Qt Project Settings," in VStudio causes the .user file to be incorrectly overwritten again.
Changing (or re-selecting) the Qt Version in the solution (Change Solutions Qt Version) fixed the issue for me.
It took many seconds to re-initialize the projects and even then I don't believe the .user file updated until I did a build of the project.

Override One Property in a .targets file

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.

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" />

Visual Studio 2010 and protobuf-csharp-port

We're using Jon Skeet's proto-csharp-port, and I'm running into some difficulties when mixing it with ReSharper in Visual Studio 2010.
We generate the .cs files via a custom MSBuild target, hooked up as follows:
<Target Name="BeforeBuild" DependsOnTargets="CompileProtos" />
The CompileProtos target runs ProtoGen and then adds the generated .cs files to the #(Compile) item group, by using CreateItem. This looks in a defined directory and compiles every .proto file it finds, so they're not listed in the project.
Where it falls down is that ReSharper doesn't recognise the content of the .cs files (because they're not in the project and might not exist yet), so I can't get the solution analysis light to go green.
If I add the .cs files to the project, then I get a build failure, because the .cs file has been added to the Compile item group twice.
I know that Marc's protobuf-net has Visual Studio 2008 goodness in it, and I'm looking for something similar, but for Jon's protobuf-csharp-port and for Visual Studio 2010.
Ideally, I'd like to be able to add the .proto files to the project, have them built correctly, and have Visual Studio and ReSharper know about the generated .cs files, so that IntelliSense and solution analysis work properly.
I'm guessing that something like how .xsd files can implicitly generate .cs files would do the trick.
I've attempted to get this working by implementing a custom tool for code generation, but I've run into a seemingly insurmountable hurdle:
protoc takes a directory full of .proto files and generates a .protobin file. This is then fed to ProtoGen which spits out a .cs file for each protocol definition. Unfortunately, it appears that the .protobin file needs to contain all of the definitions, otherwise you get Error: Unable to resolve all dependencies.
Since the custom tool model in Visual Studio assumes a single input file and a single output file (i.e. foo.proto -> foo.cs), it doesn't look like this can be made to work.
At least, not without finding some way to include all of foo.proto's imported .proto files in foo.protobin, anyway.
I solved it by removing the CreateItem from the CompileProtos target, and by defining it as a proper ItemGroup:
<ItemGroup>
<Protocols Include="$(ProtocolsPath)\*.proto"/>
</ItemGroup>
<ItemGroup>
<Compile Include="#(Protocols -> '%(Filename).cs')"/>
</ItemGroup>
This means that Visual Studio (and ReSharper) pick up the .cs files correctly, once they've been built, and ReSharper's full solution analysis stops complaining.
Unfortunately, Visual Studio has a habit of expanding the ItemGroup into individual Compile entries, but I can check for that before checking anything in.

Resources