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.
Related
I am setting up a new TeamCity build agent. Several build configurations that run perfectly well on the existing agent are failing on the new agent, with errors like this (whitespace added for clarity):
[22:14:32][someproject.vbproj]
C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\bin\Microsoft.VisualBasic.CurrentVersion.targets(322, 5):
error MSB4019: The imported project
"C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\bin\Roslyn\Microsoft.VisualBasic.Core.targets"
was not found.
Confirm that the path in the <Import> declaration is correct, and that the file exists on disk.
Note the path on line three after "The imported project". The error is correct; the folder does exist but the file does not exist there. However the Microsoft.VisualBasic.Core.targets file does exist at a slightly different path
C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\Roslyn\Microsoft.VisualBasic.Core.targets
Is there an easy fix for this, perhaps a registry entry telling MSBuild where to look?
Cause
On your machine, there are two copies of VS 2017 installed, Professional edition and the raw Build Tools.
You didn't install VB bits for Professional, while did that for Build Tools.
TeamCity prefers MSBuild from Professional edition.
Combine all above, the issue happened.
Fix
You should either force TeamCity to use MSBuild from Build Tools, or add VB to your VS Pro installation.
Reference
https://blog.lextudio.com/the-rough-history-of-msbuild-cc72a217fa98
I have added a Build Step in TeamCity 8.0.6 (build 27767) to execute FxCop against a specific c# project assembly.
When running FxCop from within VS 2013 with the MinimumRecommendedRules set I get no errors or warnings (I fixed them all).
Now I want to wire this up in TeamCity. To limit the rules I am specifying a command line property as follows:
/ruleSet:=MinimumRecommendedRules.ruleset /rulesetdirectory:'FxCop\Rule Sets'
(The rulesetdirectory parameter points to a location in my source tree which contains the full contents of the standard Rule Sets folder)
However this does not have the desired behaviour, the FULL rule set is run and I get an error as follows:
FxCop warning: Keyword=CA0063 Kind=Engine Type=Microsoft.FxCop.Sdk.FxCopException * Failed to load rule set file 'MinimumRecommendedRules.ruleset' or one of its dependent rule set files.
And this is the TeamCity generated command line:
[17:32:29]Starting: "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\FxCopCmd.exe" /forceoutput /ignoregeneratedcode /ruleSet:=MinimumRecommendedRules.ruleset "/rulesetdirectory:'FxCop\Rule Sets'" /f:dal\bin\release\MyDAL.dll /out:C:\TeamCity\buildAgent\temp\buildTmp\fxcop-output-1891867450083417003\fxcop-result.xml
Can anyone spot what is wrong?
I figured it out so hopefully this is helpful to someone else.
the /ruleSet: parameter needed to have the full (relative or absolute) path to the ruleset file as follows:
/ruleSet:"=%teamcity.build.workingDir%\FxCop\RuleSets\MinimumRecommendedRules.ruleset"
(Note: the double quotes are to handle the possibility that the teamcity.build.workingDir might contain spaces)
Secondly, the second command line parameter /rulesetdirectory: is no longer necessary. I suspect this second parameter does not actually work.
Please also note, my working command line parameter here points at my MinimumRecommendedRules.ruleset file in my source code. It could just as easily have been copied into the standard Rule Sets folder under "c:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Static Analysis Tools\Rule Sets".
And finally, as my projects are all VS 2013 and TeamCity only recognises up as far as v10 I also set "Specifiy installation root" to "c:\Program Files (x86)\Microsoft Visual Studio 12.0\Team Tools\Static Analysis Tools\FxCop" explicitly to ensure that the latest version of FxCop was being used and would not choke on .Net 4.5 code. (Note: I have VS 2013 installed on the build server because of C++ project build requirements, I'm not happy about this but it turned out to be convenient for me in dealing with the FxCop set up. There are other ways to put the FxCop stuff on your build server without installing VS 2013)
I have a old solution which worked fine with VS 2012 and now I migrate it to VS 2013.
When I try to build a configuration which involve web.config replacement i got this error:
Error 1 The "TransformXml" task could not be loaded from the assembly C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v11.0\Web\Microsoft.Web.Publishing.Tasks.dll. Could not load file or assembly 'file:///C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v11.0\Web\Microsoft.Web.Publishing.Tasks.dll' or one of its dependencies. The system cannot find the file specified. Confirm that the declaration is correct, that the assembly and all its dependencies are available, and that the task contains a public class that implements Microsoft.Build.Framework.ITask.
What it mean is that there no build tools in my v11.0 which is right. Because I use Visual Studio 2013. (the build tool are in a folder called v12.0) One option is to just copy my build tool to the right place but im searching for a real solution.
I found in my .csproj file these config:
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
But im not really understand why it then v11.0 could be. I have tried multiple things to change these like set VisualStudioVersion explicit but it changed nothing.
So my question: where the heck come these path from and where can I configure it?
Since vs2012 MS tries to keep sln file untouched if there is no reason to upgrade it (to allow opening sln in the old VS).
If you’ve upgrade from vs2012 to vs2013 there can be chance you are opening solution in vs2012 (it happen to me in many updated solutions).
Try to check beginning of the sln file
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
or:
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2012
If you wish to change it by VS, open sln in vs2013 and then select solution root node in solution explorer and the File > Save .sln (Ctrl+S)
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.
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)"