How can I force the C++ platform in MSBuild? - c++

I have a program which must build as 32 bit. It gets shipped with an x64 application. As a result, there is an installer which gets a bitness.
The installer is built with a wixproj that must be built with /p:Platform=x64 -- but the vcxproj needs to build as x86.
I tried forcing Platform to be x86 or Win32 by setting it explicitly:
<PropertyGroup>
<Platform>Win32</Platform>
</PropertyGroup>
but it appears that the command line switch that got passed to the wixproj "wins" when building.
Is there some way that the project file can force Platform to be Win32 no matter what is specified on the command line?
(for csproj I was able to do this:
<PropertyGroup>
<PlatformTarget>AnyCPU</PlatformTarget>
</PropertyGroup>
but that doesn't have any effect on C++ it seems)

Finally figured this out. MSBuild has a setting TreatAsLocalProperty that allows a project file to override any variable, which goes into the Project node at the beginning of the file.
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build"
ToolsVersion="4.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
TreatAsLocalProperty="Platform"> <!-- !!! -->
<PropertyGroup>
<Platform>Win32</Platform>
</PropertyGroup>
<!-- Now Platform is Win32 no matter what! -->
</Project>

Related

How to change %(DisableSpecificWarnings) in Visual Studio 2019

After upgrading to VS 2019 I'm trying to make the C++ warnings useful again by disabling some that I don't care about, for example C26812
I know I could go to every project's property page and add this string to "Disable Specific Warnings" but that would be too much duplication (I've got a lot of projects). So instead I'm trying to change the DisableSpecificWarnings variable which is common to all Projects: 4996;6031;%(DisableSpecificWarnings)
How and where can I change this global variable in Visual Studio 2019 ?
For anyone interested, I ended up creating one GLOBAL.props file which is shared across projects.
To add this sheet, select project go to "Property Manager", select the Project and do "Add Existing Property Sheet". For example DisableSpecificWarnings used by all sub-projects is defined here. For further information on Compile and Link properties, see MSBuild documentation:
Clcompile: https://learn.microsoft.com/en-us/visualstudio/msbuild/cl-task?view=vs-2019
Link: https://learn.microsoft.com/en-us/visualstudio/msbuild/link-task?view=vs-2019
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup>
<ClCompile>
<DisableSpecificWarnings>4675;4541;4702;4267;4996;26812;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
</ItemDefinitionGroup>
<ItemGroup />
</Project>
Project defaults come from several .props files, organized by scope, language and platform. The location (and even presence) of those .props files has changed between versions, and can depend on the history of past versions installed prior to VS 2019.
One (safer) way to identify the default .props being actually used is to create a new C++ project and look at the <Import Project ... /> lines in the generated .vcxproj file. For example, I am getting the following on my machine, in increasing order of specificity.
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
The last one is supposed to be the most specific, carrying the per-user per-platform settings. It appears to no longer be created with fresh VS 2019 installs, but it is inherited from prior versions and is still recognized when present (see Microsoft.Cpp.Win32.user.props file missing for example).
To see what "$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" expands to, open the project settings and pretend to change any of editable paths in the configuration, then click Edit in the dropdown menu and paste the .props path in the edit box. The actual path will show right below it, for example C:\Users\<user name>\AppData\Local\Microsoft\MSBuild\v4.0\Microsoft.Cpp.Win32.user.props.
Find the file on disk, or create it if it doesn't exist already, and insert the following under Project/ItemDefinitionGroup/ClCompile.
<DisableSpecificWarnings>26812;%(DisableSpecificWarnings)</DisableSpecificWarnings>
If the .props file did not exist and you had to create it from scratch, the complete file would be:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemDefinitionGroup>
<ClCompile>
<DisableSpecificWarnings>26812;%(DisableSpecificWarnings)</DisableSpecificWarnings>
</ClCompile>
</ItemDefinitionGroup>
</Project>
In order to modify the per-machine defaults, rather than the per-user ones, follow the same steps but use one of the $(VCTargets) files instead.

VS2017 blocking on non-existing object files when debugging with pdb file

We are in the process of switching Visual C++ projects to the vc141 toolchain (VS 2017). We have encountered a problem where Visual Studio is unable to use a .pdb file whose source .obj files don't exist anymore (for example because they have been compiled on a build server).
Let's take a very simple executable project:
#include <iostream>
int main() {
std::cout << "Hello world\n";
std::cin.ignore();
}
The .vcxproj file is all default, except for <GenerateDebugInformation>true</GenerateDebugInformation> to generate the pdb file.
Reproduction steps are, always using VS2017:
Compiling the project
Placing a breakpoint inside main
Removing the intermediate Debug/ directory containing the .obj files
Disabling build-on-run through the configuration manager (so it won't recreate them)
Starting a debug session
This works fine with the vc100 (VS 2010) toolchain, and the breakpoint works, but it immediately triggers the following error with vc141:
Error: Unable to open file
<path>\Debug\main.obj. Error code = 0x80070003.
This very generic error code corresponds indeed to FACILITY_WIN32/ERROR_PATH_NOT_FOUND. The path to main.obj can be found inside both versions of the .pdb file, thus it is unclear to us why VS suddenly breaks down when it doesn't find it.
The "Modules" view shows that the .pdb file seems to be loaded correctly. Additionally, the breakpoint's tooltip shows the following error:
The breakpoint will not currently be hit. Unexpected symbol reader error while processing MyUser_141.exe.
What could be a solution or a workaround for this problem, given that we cannot debug on the machine that compiles the binaries in our real-case application?
Here is the full .vcxproj file:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<RootNamespace>MyUser_141</RootNamespace>
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
<ProjectGuid>{90982029-29B8-4C9B-AFB7-B8F555F15C1E}</ProjectGuid>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="main.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
Further research:
We tried some other toolchain versions. The bug is not present in v14.0 (VS 2015), but is present as soon as 14.11 (VS2017 15.3).
Using v141_xp, which as far as we can tell uses the same toolchain but older system libraries, works.
To fix this, make the following change in the property pages of the project(s) that build your executable(s) and DLL(s):
Configuration Properties -> Linker -> Debugging -> Generate Debug Info -> Generate Debug Information optimized for sharing and publishing
Static libraries, having no link step, don't need this. .EXEs and DLLs do.
When you specify /DEBUG with no additional options, the linker defaults to /DEBUG:FULL for command line and makefile builds, for release builds in the Visual Studio IDE, and for both debug and release builds in Visual Studio 2015 and earlier versions. Beginning in Visual Studio 2017, the build system in the IDE defaults to /DEBUG:FASTLINK when you specify the /DEBUG option for debug builds. Other defaults are unchanged to maintain backward compatibility.
Source : https://developercommunity.visualstudio.com/content/problem/259283/error-unable-to-open-file-mainobj-error-code-0x800.html

How to pass additional compiler/linker options in msbuild command line? [duplicate]

Is it possible to pass options to linker via comamnd line of msbuild? For example I want to set VC linker option /PROFILE. How to do it without changing of C++ project file?
PS: Visual Studio Express 2012.
Inside the projectfile the linker options are set in an ItemGroup so you cannot simply add or override this from the commandline. Instead you'll have to make msbuild include them which can only be done by importing another msbuild file. This functionality is supported: if you set the ForceImportBeforeCppTargets on the commandline, msbuild will import the file it points to.
Practically: create this file, let's call it c:\props\profile.props
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemDefinitionGroup>
<Link>
<Profile>true</Profile>
</Link>
</ItemDefinitionGroup>
</Project>
Then build your (unmodified) project like this:
msbuild myProject.vcxproj /p:ForceImportBeforeCppTargets=c:\props\profile.props

Building a multi-arch installer with msbuild

I'm building a multi-architecture installer for a C++ program with NSIS. I'm using Visual Studio 2010. All is well except I don't know how to make the installer depend on the build for all architectures.
I have created a project to run makensis as a build step, and configured it to depend on all other projects in the solution. I'm currently building for Win32 and X86_64 architectures. The NSIS project is only built as a part of X86_64 configuration. But it packs files built in both X86_64 and Win32 configurations. Here lies the problem. If I build Win32 and then immediately X86_64, all is well. If I start with X86_64, the build fails because it can't find Win32 files. Worse, if I change some source code and rebuild only X86_64, the installer project will happily pick up out-of-date Win32 files without any indication of a problem.
Can I force a Win32 build from an X86_64 build, or do anything else to make this work?
I'm a Unix type, Windows is an alien world to me.
Any
As for "foolproof" solutions, if I understand you correctly:
You have solution containing multiple projects (lets say Test.sln),
You want to build this solution for several platforms,
... and use the MakeNSIS tool (I have no idea what that is) to create an installer packaging binaries built for all the platforms.
Please correct me if I am wrong. So, to achieve this task:
I would completely drop the project you introduced (the one running MakeNSIS),
Then would create Test.msbuild file such as the one below,
Notice the <Exec> element, that is the place where you want to run you MakeNSIS,
Then simply run the msbuild as msbuild Test.msbuild,
Using this solution you would have all the projects from Test.sln first built for Win32, then for x64, and MakeNSIS would only be run afterwards.
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Build" DependsOnTargets="Build_Win32;Build_x64">
<!-- Run whatever command you like, such as MakeNSIS .. ? -->
<Exec Command="notepad.exe" />
</Target>
<Target Name ="Build_Win32">
<MSBuild Projects="Test.sln" Properties="Configuration=Release;Platform=Win32" />
</Target>
<Target Name ="Build_x64">
<MSBuild Projects="Test.sln" Properties="Configuration=Release;Platform=x64" />
</Target>
</Project>
Please provide clarification to your actual question if the above isn't what you asked for.
EDIT:
After clarifying your request in the comment, I would propose following solution. I like the above solution with Test.msbuild more, but here you go:
Add new project BuildInstaller into your solution,
In Configuration Manager uncheck the checkbox "Build" for this new project for all combinations of Configuration/Platform,
Still in Configuration Manager, create new configuration, lets say Installers,
For this new configuration, uncheck the "Build" checkbox for all the projects from the solution, except for the BuildInstaller,
Now open the BuildInstaller.vcxproj in text editor and append the following snippet right before the closing </Project> tag:
<ItemGroup>
<ProjectsToBuild Include="..\**\*.vcxproj" Exclude="..\**\BuildInstaller.vcxproj"/>
</ItemGroup>
<Target Name="Build" DependsOnTargets="Build_Win32;Build_x64">
<!-- Run whatever command you like, such as MakeNSIS .. ? -->
<Exec Command="notepad.exe" />
</Target>
<Target Name="Build_Win32">
<MSBuild Projects="#(ProjectsToBuild)" Properties="Configuration=Release;Platform=Win32" />
</Target>
<Target Name="Build_x64">
<MSBuild Projects="#(ProjectsToBuild)" Properties="Configuration=Release;Platform=x64" />
</Target>
This way you effectively override the default build target,
So now:
Everytime you build for Release/Debug configuration, installer won't be built, that is preferred from many reasons,
Everytime you build for Installers configuration, your new BuildInstaller.vcxproj will take over, will build both win32 and x64 binaries and in the end will run the custom command line executable. Of course binaries will be built using Release configuration which should be desired.
Initially I thought I could drop the <ItemGroup> element and use Projects="..\Test.sln" instead of Projects="#(ProjectsToBuild)" as there should be no circular dependency (BuildInstaller.vcxproj is not built for Release) but the build took forever so there had to be some problem, weird...
Does this satisfy your needs?
I think you need to build Win32 configuration first and then 64bit configuration.
The makensis project should be built after both are finished (successfully!).
For example it is possible to call it from Post Build event (for 64bit configuration) or as separate project.
I am not sure whether your makensis project is based on Visual & Installer (http://www.visual-installer.com - sorry for little self promo :) or it is pure (text - batch) project included in VS Solution.
In both cases the Configuration manager in VS allows you to define the build order. The makensis project should be always the last so it can find all dependencies from previous configurations.
Also it is good to use relative path in makensis project - something like ${TARGET_PATH} which will be defined for each configuration with different value.

Batch compiling only one project in a solution

Say I have a solution that contains three projects
SolutionA
ProjectA
ProjectB
ProjectC
The whole solution is set to build as AnyCPU. However, ProjectB is a managed C++ project and I would like it (and only it) to build both Win32 and x64. I am using TFS to perform these builds nightly. Is there any good way to accomplish this?
The .sln file itself cannot build same project in two configurations or platforms at the same time. One possible workaround is to create a custom project, that would be invoked by .sln and would call in its turn a projectA twice -- with two different values of Platform parameter. The custom project file would look like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
</PropertyGroup>
<Target Name="build">
<MSBuild Project="ProjectA.vcxproj" Properties="Platform=Win32;Configuration=$(Configuration)" />
<MSBuild Project="ProjectA.vcxproj" Properties="Platform=x64;Configuration=$(Configuration)" />
</Target>
</Project>
For this to work you need to do the following. 1. In the solution disable build of ProjectA (or remove it from .sln). 2. Add your custom project to the .sln.
Also, remember, you cannot pass explicit /p:Platform option to the