Use 32bit "Program Files" directory in msbuild - msbuild

In 64 bit versions of windows, 32 bit software is installed in "c:\program files (x86)". This means you cannot use $(programfiles) to get the path to (32 bit) software. So I need a $(ProgramFiles32) to overcome this in my MSBuild project. I don't want to change the project depending on the os it is running on.
I have a solution that I will post, but maybe there is a easier/better way.

In MSBuild 4.0+, there's a $(MSBuildProgramFiles32) property for it, which you can confidently employ directly (especially if you're prepared to put a ToolsVersion="4.0" at the top of the file to guarantee it's going to be available and Fail Fast if it's not).
If you're not and need something that can Do The Right Thing even when executed in an MSBuild 2.0 or later environment (i.e., back to VS 2005 environments), the complete solution is:
<PropertyGroup>
<!--MSBuild 4.0 property-->
<ProgramFiles32>$(MSBuildProgramFiles32)</ProgramFiles32>
<!--Use OS env var as a fallback:- 32 bit MSBuild 2.0/3.5 on x64 will use this-->
<ProgramFiles32 Condition=" '' == '$(ProgramFiles32)'">$(ProgramFiles%28x86%29)</ProgramFiles32>
<!-- Handle MSBuild 2.0/3.5 running in 64 bit mode - neither of the above env vars are available. http://stackoverflow.com/questions/336633
NB this trick (Adding a literal " (x86)" to the 64 bit Program Files path) may or may not work on all versions/locales of Windows -->
<ProgramFiles32 Condition ="'$(ProgramFiles32)'=='' AND 'AMD64' == '$(PROCESSOR_ARCHITECTURE)'">$(ProgramFiles) (x86)</ProgramFiles32>
<!--Catch-all - handles .NET 2.0/3.5 non-AMD64 and .NET 2.0 on x86 -->
<ProgramFiles32 Condition=" '' == '$(ProgramFiles32)' ">$(ProgramFiles)</ProgramFiles32>
</PropertyGroup>
Unfortunately Progressive enhancement / polyfill overriding of the MSBuild reserved property name MSBuildProgramFiles32 via either a <PropertyGroup> or <CreateProperty> is rejected by MSBuild 4.0+ so it can't be made tidier and still support .NET 2.0.

My solution is to look whether "c:\program files (x86)" exists, if it exists, asume this is a 64 bit os. Otherwise use the normal program files directory:
<PropertyGroup>
<ProgramFiles32 Condition="Exists('$(PROGRAMFILES) (x86)')">$(PROGRAMFILES) (x86)</ProgramFiles32>
<ProgramFiles32 Condition="$(ProgramFiles32) == ''">$(PROGRAMFILES)</ProgramFiles32>
</PropertyGroup>
I can use it like this
<Exec WorkingDirectory="src\app1" Command='"$(ProgramFiles32)\doxygen\bin\doxygen" Doxyfile' />

In MSBuild 4.0, $(MSBuildProgramFiles32) will give you the 32-bit Program Files directory.

Try "$(MSBuildExtensionsPath32)\.."

I think a slighly more reliable way is to grab the Environment variable "ProgramFiles(x86)". In a 64 bit process on Windows this will point to the 32 bit program files directory. It will be empty on a 32 bit version of windows and I believe on a wow64 process
I ran into virtually same problem recently with some PowerShell scripts. I wrote a blog entry on how a worked around the program files directory issue. Different language obviously but it may help you out.
http://blogs.msdn.com/jaredpar/archive/2008/10/21/program-files-i-just-want-the-32-bit-version.aspx

I stumbled across this question trying to find a generic way in MSbuild to see if it was a 32- or 64-bit os. In case someone else also find this, I used the following:
<PropertyGroup>
<OSBits Condition="$(ProgramW6432) != ''">x64</OSBits>
<OSBits Condition="$(OSBits) == ''">x32</OSBits>
</PropertyGroup>
Apparently %ProgramW6432% is only set on 64-bit systems.

If you run the 32-bit version of the Visual Studio tools (especially in VS2012, there are like 3 different command prompts you can choose from), $(ProgramFiles) points to "Program Files (x86)"

Related

.NET Core RuntimeIdentifier vs TargetFramework

Can someone explain the purpose of this two in csproj file (VS2017):
<TargetFramework>netstandard1.6</TargetFramework>
<RuntimeIdentifier>win7</RuntimeIdentifier>
I just migrated from VS2015 and now can't publish my web api because it looks I should use only one target framework. In addition I can't specify multiple RIDs.
All these changed things make me frustrated. Nothing works from scratch, should overcome something over and over.
I just want developing my web-api on windows, run xUnit tests here and then deploy web-api to run on linux (ubuntu) server.
What I should put in both parameters in csproj ?
Links with good explanation is highly appreciated.
Update1
I have web api with referenced .net core libraries. Everything where migrated from VS2015. Now in root project I have
<TargetFrameworks>netcoreapp1.1;net461</TargetFrameworks>. When I publish via VS2017 I got error:
C:\Program
Files\dotnet\sdk\1.0.3\Sdks\Microsoft.NET.Sdk\buildCrossTargeting\Microsoft.NET.Sdk.targets(31,5):
error : The 'Publish' target is not supported without specifying a
target framework. The current project targets multiple frameworks,
please specify the framework for the published application.
But I have specified target framework in publish as netcoreapp1.1.
OK. Then I updated my csproj with <PropertyGroup Condition="$(TargetFramework)'=='netcoreapp1.1'">
<RuntimeIdentifier>ubuntu.16.10-x64</RuntimeIdentifier>
</PropertyGroup> as suggested below.
But now I even can't build app, get error:
5>C:\Program Files (x86)\Microsoft Visual
Studio\2017\Enterprise\MSBuild\Sdks\Microsoft.NET.Sdk\build\Microsoft.NET.Sdk.targets(92,5):
error : Assets file '\obj\project.assets.json' doesn't
have a target for '.NETCoreApp,Version=v1.1/ubuntu.16.10-x64'. Ensure
you have restored this project for TargetFramework='netcoreapp1.1' and
RuntimeIdentifier='ubuntu.16.10-x64'.
I just want develop with VS2017 at windows 8.1/windows7 and deploy to ubuntu 16.10.
What I'm doing wrong ?
Update2
I have 8 projects in solution. 3 of them are xUnit tests. Thus we have 5 projects. 4 of these 5 are class libraries and 1 is my web-app.
All 4 class libraries have this:
<TargetFrameworks>netstandard1.6;net461</TargetFrameworks>
<ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
My web app:
<TargetFrameworks>netcoreapp1.1;net461</TargetFrameworks>
<ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
How to publish my web-app ?
The <TargetFramework> (or <TargetFrameworks> when you want have multiple targets, such as net451, one or multiple netstandard1.x etc). Per <TargetFramework> / <TargetFrameworks> entry one set of assemblies will be created and located inside bin\Debug\<targetframeworkid>).
This is useful, when you want to use a different library in .NET Core (because the library you used only works with full .NET Framework such as 4.5.1) or remove this feature from i.e. .NET Core because it's unsupported.
It is used for both, building and NuGet restore. i.e. you can't use a net451 only library in a .NET Core project (netstandard 1.1 for example - but you can use netstandard1.1 in a net451 project).
<RuntimeIdentifier> / <RuntimeIdentifiers> on the other side is used for NuGet mainly. It tells NuGet which packages you need. For example if you want to target Linux, Mac and Windows, certain assemblies require native libraries (such as encryption. On windows CryptoAPI will be used, but on Linux and Mac you need OpenSSL). This includes non-managed dlls and *.so (Linux) files.
i.e. <RuntimeIdentifiers>win7-x64;win7-x86;ubuntu.16.10-x64</RuntimeIdentifiers> will make nuget restore packages for win7 (both x64 and x86) versions and x64 only for ubuntu. This is required, because when you work on windows you need to download these native libraries too so you deploy/package them with dotnet publish.
Here's a little catch though: When you have a full .NET Framework reference in <TargetFramework> or <TargetFrameworks>, then you must specify a single <RuntimeIdentifier> (singular, not plural <RuntimeIdentifiers>), otherwise you will get an error.
For example:
<PropertyGroup>
<TargetFrameworks>netstandard1.0;net451</TargetFrameworks>
<RuntimeIdentifiers>win7-x64;win7-x86;ubuntu.16.10-x64</RuntimeIdentifiers>
</PropertyGroup>
<!-- This entry will only be used for the .NET Framework 4.5.1 output -->
<PropertyGroup Condition="'$(TargetFramework)' == 'net451'">
<RuntimeIdentifier>win7-x64</RuntimeIdentifier>
</PropertyGroup>
RID is short for Runtime IDentifier. RIDs are used to identify target
operating systems where an application or asset (that is, assembly)
will run. They look like this: "ubuntu.14.04-x64", "win7-x64",
"osx.10.11-x64". For the packages with native dependencies, it will
designate on which platforms the package can be restored.
More in docs
First change to proper RID from win7 to win7-x64 or win7-x86. Next add other RID like ubuntu. For example:
<PropertyGroup>
<TargetFramework>netstandard1.6</TargetFramework>
<RuntimeIdentifier>win7-x64;ubuntu.16.10-x64</RuntimeIdentifier>
</PropertyGroup>
Target framework looking good. For more read docs

What is default msbuild platform

How does msbuild chose a platform if it is not specified? It seems to me that for some solutions it selects "Mixed Platforms" for others "x86".
I switch on the diagnostics level of logging and the only thing I can see is that "Initial Properties" at the beginning contain e.g. "Platform = Mixed Platforms" without any explanation why.
To preempt some answers, I know that I can override the platform manually. That is not an issue. I need to know what msbuild does when it is NOT specified.
This may help: I was researching this and finally tracked down the default platform for my install, by looking in Microsoft.Cpp.Default.props (line 21 in this version of Visual Studio), which lives in Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120:
<Platform Condition="'$(Platform)' == ''">Win32</Platform>
This means that under VS12 (Visual Studio 2013) MSBuild will pick Win32 as the platform if no other platform is explicitly specified. As noted in some other questions, setting an environment variable named Platform will change the default to the value you set.
Important note: If you invoke MSBuild on a Visual Studio solution file (*.sln) rather than a project file, and you don't specify a platform in the MSBuild arguments, then it appears that MSBuild will choose the platform automatically based on the first entry under the SolutionConfigurationPlatforms global section in the solution file. I haven't found this documented anywhere but from experimentation it appears to be the case. This means that editing your project file and providing a different default Platform property (as described above), MSBuild will ignore this default, because it will have chosen the platform already before it even starts looking at the project. Invoking MSBuild directly on the project file seems to bypass this behavior.
MSBuild does not choose but whatever MSBuild project it is building may default certain properties. I am assuming that your question relates to how MSBuild builds a solution file.
msbuild.exe "somesolution.sln" /t:Build
You need to look at the projects that make up the solution, in there you will see the properties that are set. For example you will probably see the following at the top of the project file:
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
...
</PropertyGroup>
This shows a PropertyGroup containing amongst others two properties, Configuration and Platform. Their values are set based on a Condition. The condition says says that if no value has been set for the property Configuration it should default to 'Debug'. Likewise if nothing is set for Platform it should default to AnyCPU.
You may also see a Conditional PropertyGroup:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
...
</PropertyGroup>
What this condition says is that if the property Configuration and Platform match Debug and AnyCPU then it should apply all of the properties contained within.
A point to note is that the property names are just an arbitrary name and the values are just strings. However when building .Net projects there is a convention to which these properties and their values are a part. To see what the default values are you do not need to open each project in a text editor. You can go into Visual Studio and look at the solution configuration.

Why is the 64-bit MSBuild loading 32-bit extensions?

I'm attempting to build a project using MSBuild (v4.0) on a 64-bit machine. For some reason, MSBuild is attempting to load a 32-bit extension, and I cannot figure out why. I've reduced the problem to the smallest set in order to demonstrate the issue.
Using the following MSBuild project file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Target Name="test">
<Message Text="bin path: $(MSBuildBinPath)" />
<Message Text="extensions path: $(MSBuildExtensionsPath)" />
<Message Text="extensions path (x86): $(MSBuildExtensionsPath32)" />
<Message Text="extensions path (x64): $(MSBuildExtensionsPath64)" />
</Target>
</Project>
I get this output:
Microsoft (R) Build Engine Version 4.0.30319.1
[Microsoft .NET Framework, Version 4.0.30319.1]
Copyright (C) Microsoft Corporation 2007. All rights reserved.
Build started 8/27/2010 9:56:35 AM.
Project "D:\5\test.proj" on node 1 (default targets).
test:
bin path: C:\Windows\Microsoft.NET\Framework64\v4.0.30319
extensions path: C:\Program Files (x86)\MSBuild
extensions path (x86): C:\Program Files (x86)\MSBuild
extensions path (x64): C:\Program Files\MSBuild
Done Building Project "D:\5\test.proj" (default targets).
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.03
MSBuild obviously knows about the 32bit and 64bit extensions path, and from the binary path it seems clear that I'm running the 64-bit MSBuild.exe, but for some reason it believes that extensions should be loaded from Program Files (x86) instead of Program Files. This is causing me trouble, as I have an extension that I need loaded, that MUST be loaded correctly in a 32bit/64bit process, and it will not load (MSBuild is attempting to load the 32bit version in a 64bit process).
Why?
I filed a bug on Microsoft Connect, and it was closed as "By Design", with this explanation:
You're exactly right -- this has changed, and strictly speaking, it's wrong now. However, this was a conscious decision. The reason it was changed was that very many extensions (such as .targets files) installed by other products are only installed in the 32 bit program files location. They did not anticipate 64 bit scenarios, but generally would work just fine in 64 bit MSBuild. When a user runs 64 bit MSBuild, which is quite common now because it's the default for Team Build 2010, MSBuildExtensionsPath would have in the past resolved to the 64 bit Program Files as you expect. However this meant that all those .targets files were not longer found and the build failed. It was not practical to get all those products to fix their setup authoring, especially since it had already shipped to customers. So we made the change to make MSBuildExetnsionsPath always point to the 32 bit location. Almost nobody seems to really want the 64 bit location, and those people can change to MSBuildExtensionsPath64. It was really a question of the least bad option here.
I accept the evidence, but I disagree with the conclusion. I believe that authors of broken installers deserve to have their extensions not work on 64-bit machines.

Find out the "Bit"ness of the current OS in MSBuild

I have a build script that needs to hard code a path to an executable. The path is:
C:\Program Files\Microsoft Visual Studio 9.0\SmartDevices\SDK\SDKTools\cabwiz.exe
This has worked fine, but now I am running on a 64 bit OS (but my coworker and build server are on 32 bit still).
I need the path to be this for me:
C:\Program Files (x86)\Microsoft Visual Studio 9.0\SmartDevices\SDK\SDKTools\cabwiz.exe
But use the normal path for the others.
Here is how I set it up:
<PropertyGroup>
<CabWiz>"C:\Program Files\Microsoft Visual Studio 9.0\SmartDevices\SDK\SDKTools\cabwiz.exe"</CabWiz>
</PropertyGroup>
Is there a condition I can put on that so that I can set it if the OS (not the current build configuration) is 64 bit?
There is a registry key that will tell you the bit-edness of the current OS. Here are the properties I use in my MSBuild files:
<PropertyGroup>
<MachineProcessorArchitecture>$(registry:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment#PROCESSOR_ARCHITECTURE)</MachineProcessorArchitecture>
<Is32Bit>False</Is32Bit>
<Is32Bit Condition="'$(MachineProcessorArchitecture)' == 'x86'">True</Is32Bit>
<Is64Bit>False</Is64Bit>
<Is64Bit Condition="'$(MachineProcessorArchitecture)' == 'AMD64'">True</Is64Bit>
</PropertyGroup>
You're using the bitness to try and guess the correct Program Files folder, but there's no guarantee that it's on the C drive, or even called "Program Files". You would be better using the $(MSBuildProgramFiles32) property (in MSBuild 4.0).
On a 64-bit OS, the following variables are defined:
ProgramFiles=C:\Program Files
ProgramFiles(x86)=C:\Program Files (x86)
So just test for ProgramFiles(x86) and if it's empty, use ProgramFiles.
If you're always running the 32-bit version of MSBuild, regardless of the platform, then it's easy: just substitute '$(ProgramFiles)' for 'C:\Program Files'. Whether on a 32-bit or 64-bit OS, '$(ProgramFiles)' should expand to the correct folder location (the location of all 32-bit programs).
If you're running the 64-bit version of MSBuild on 64-bit platforms (which is unlikely), then it gets a bit trickier. The '%ProgramFiles(x86)%' environment variable would seem to be what you want, but good luck dealing with those parentheses. Easier would probably be to use the '%PROCESSOR_ARCHITECTURE%' environment variable in a condition.

How do I specify the platform for MSBuild?

I am trying to use MSBuild to build a solution with a specified target platform (I need both binaries, x86 and x64). This is how I tried it:
C:\WINDOWS\Microsoft.NET\Framework\v3.5>MsBuild SolutionPath\Solution.sln /t:Rebuild /p:Configuration=Release /p:Platform="x86"
However the build always fails if the platform is different from "Any CPU". What am I doing wrong?
This is the while output MSBuild prints:
C:\WINDOWS\Microsoft.NET\Framework\v3.5>MsBuild
SolutionPath\Solution.sln /t:Rebuild
/p:Configuration=Release
/p:Platform="x86" Microsoft (R) Build
Engine Version 3.5.30729.1 [Microsoft
.NET Framework, Version
2.0.50727.3082] Copyright (C) Microsoft Corporation 2007. All rights
reserved.
Build started 1.7.2010 8:28:10.
Project "SolutionPath\Solution.sln" on
node 0 (Rebuild targe t(s)).
SolutionPath\Solution.sln : error
MSB4126: The specified sol ution
configuration "Release|x86" is
invalid. Please specify a valid
solution c onfiguration using the
Configuration and Platform properties
(e.g. MSBuild.exe Solution.sln
/p:Configuration=Debug
/p:Platform="Any CPU") or leave those
prope rties blank to use the default
solution configuration. Done Building
Project "SolutionPath\Solution.sln"
(Rebuild t arget(s)) -- FAILED.
Build FAILED.
"SolutionPath\Solution.sln" (Rebuild
target) (1) ->
(ValidateSolutionConfiguration target)
-> SolutionPath\Solution.sln : error MSB4126: The specified s olution
configuration "Release|x86" is
invalid. Please specify a valid
solution configuration using the
Configuration and Platform properties
(e.g. MSBuild.ex e Solution.sln
/p:Configuration=Debug
/p:Platform="Any CPU") or leave those
pro perties blank to use the default
solution configuration.
0 Warning(s)
1 Error(s)
Time Elapsed 00:00:00.03
If I try to build it for x86/x64 with devenv it works perfectly, however I am trying to set up a build server without installing all the necessary versions of Visual Studio. By the way, if there is a better free tool (that supports .NET framework 4) out there, I'd love to hear about it.
In MSBuild or Teamcity use command line
MSBuild yourproject.sln /property:Configuration=Release /property:Platform=x64
or use shorter form:
MSBuild yourproject.sln /p:Configuration=Release /p:Platform=x64
However you need to set up platform in your project anyway, see the answer by Julien Hoarau.
If you want to build your solution for x86 and x64, your solution must be configured for both platforms. Actually you just have an Any CPU configuration.
How to check the available configuration for a project
To check the available configuration for a given project, open the project file (*.csproj for example) and look for a PropertyGroup with the right Condition.
If you want to build in Release mode for x86, you must have something like this in your project file:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
...
</PropertyGroup>
How to create and edit the configuration in Visual Studio
(source: microsoft.com)
(source: msdn.com)
(source: msdn.com)
How to create and edit the configuration (on MSDN)
If you're trying to do this from the command line, you may be encountering an issue where a machine-wide environment variable 'Platform' is being set for you and working against you. I can reproduce this if I use the VS2012 Command window instead of a regular windows Command window.
At the command prompt type:
set platform
In a VS2012 Command window, I have a value of 'X64' preset. That seems to interfere with whatever is in my solution file.
In a regular Command window, the 'set' command results in a "variable not defined" message...which is good.
If the result of your 'set' command above returns no environment variable value, you should be good to go.
Hopefully this helps someone out there.
For platform I was specifying "Any CPU", changed it to "AnyCPU" and that fixed the problem.
msbuild C:\Users\Project\Project.publishproj /p:Platform="AnyCPU" /p:DeployOnBuild=true /p:PublishProfile=local /p:Configuration=Debug
If you look at your .csproj file you'll see the correct platform name to use.
For VS2017 and 2019... with the modern core library SDK project files, the platform can be changed during the build process. Here's an example to change to the anycpu platform, just before the built-in CoreCompile task runs:
<Project Sdk="Microsoft.NET.Sdk" >
<Target Name="SwitchToAnyCpu" BeforeTargets="CoreCompile" >
<Message Text="Current Platform=$(Platform)" />
<Message Text="Current PlatformTarget=$(PlatformName)" />
<PropertyGroup>
<Platform>anycpu</Platform>
<PlatformTarget>anycpu</PlatformTarget>
</PropertyGroup>
<Message Text="New Platform=$(Platform)" />
<Message Text="New PlatformTarget=$(PlatformTarget)" />
</Target>
</Project>
In my case, I'm building an FPGA with BeforeTargets and AfterTargets tasks, but compiling a C# app in the main CoreCompile. (partly as I may want some sort of command-line app, and partly because I could not figure out how to omit or override CoreCompile)
To build for multiple, concurrent binaries such as x86 and x64: either a separate, manual build task would be needed or two separate project files with the respective <PlatformTarget>x86</PlatformTarget> and <PlatformTarget>x64</PlatformTarget> settings in the example, above.
When you define different build configurations in your visual studio solution for your projects using a tool like ConfigurationTransform, you may want your Teamcity build, to build you a specified build configuration. You may have build configurations e.g., Debug, Release, Dev, UAT, Prod etc defined. This means, you will have MSBuild Configuration transformation setup for the different configurations. These different configurations are usually used when you have different configurations, e.g. different database connection strings, for the different environment. This is very common because you would have a different database for your production environment from your playground development environment.
They say a picture is worth a thousand words, please see the image below how you would specify multiple build configurations in Teamcity.
In the commandline input text box, specify as below
/p:OutputPath=Publish;Configuration=Dev
Here, I have specified two commandline build configurations/arguments OutputPath and build Configuration with values Publish and Dev respectively, but it could have been, UAT or Prod configuration. If you want more, simply separate them by semi-colon,;
There is an odd case I got in VS2017, about the space between ‘Any’ and 'CPU'.
this is not about using command prompt.
If you have a build project file, which could call other solution files. You can try to add the space between Any and CPU, like this (the Platform property value):
<MSBuild Projects="#(SolutionToBuild2)" Properties ="Configuration=$(ProjectConfiguration);Platform=Any CPU;Rerun=$(MsBuildReRun);" />
Before I fix this build issue, it is like this (ProjectPlatform is a global variable, was set to 'AnyCPU'):
<MSBuild Projects="#(SolutionToBuild1)" Properties ="Configuration=$(ProjectConfiguration);Platform=$(ProjectPlatform);Rerun=$(MsBuildReRun);" />
Also, we have a lot projects being called using $ (ProjectPlatform), which is 'AnyCPU' and work fine. If we open proj file, we can see lines liket this and it make sense.
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
So my conclusion is,
'AnyCPU' works for calling project files, but not for calling solution files,
for calling solution files, using 'Any CPU' (add the space.)
For now, I am not sure if it is a bug of VS project file or MSBuild.
I am using VS2017 with VS2017 build tools installed.
In Visual Studio 2019, version 16.8.4, you can just add
<Prefer32Bit>false</Prefer32Bit>