Get the correct $(VisualStudioVersion) for VS 2017 inside a MSBuild file - visual-studio

I have a MSBuild file to publish a solution (created in VS 2013 and ported to VS 2017) to a remote server. The offending line is this one:
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
When I run the command:
"C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\msbuild" WebSite1_Web_configs.build /p:Configuration=Integracion;Platform="AnyCPU" /p:VisualStudioVersion=15.0
After some compilation, it ends up with this error:
Error MSB4062 The "TransformXml" task could not be loaded from the assembly C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\VisualStudio\v12.0\Web\Microsoft.Web.Publishing.Tasks.dll
Of course it can't be loaded, because the version should be 15.0, not 12.0.
Things I have tried:
Adding the /p:VisualStudioVersion=15.0 to the command where I call MSBuild. It doesn't work.
Changing, inside the *.csproj file, the element:
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
For:
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
It doesn't work.
So, how can I get the correct VS version with $(VisualStudioVersion) inside the MSBuild file without having to hardcode it?

Ok, the problem was that I had <Import> elements at the end of the *.build file. One of those imports had part of the path hardcoded:
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v12.0\WebApplications\Microsoft.WebApplication.targets" />
I changed it to:
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\WebApplications\Microsoft.WebApplication.targets" />
I executed MSBuild with the /p:VisualStudioVersion=15.0 parameter and it worked.
Please, if you have the same problem, check if you have imports with hardcoded paths.

On Visual Studio 2017, instead of edits the .csproj file, you can simple go to Visual Studio Package Manager Console (Tools -> NuGet Package Manager -> Package Manager Console) and install Web.Targets:
Install-Package MSBuild.Microsoft.VisualStudio.Web.targets

Related

MSBuild project in Visual Studio

I Have a question about msbuild integration with Visual Studio 2017 Preview.
I have a custom *.csproj:
<Project>
<Target Name="Build">
<Message Text="Hello World" Importance="High"/>
</Target>
</Project>
I want to add this project to Visual Studio, but i have an error:
Project file is incomplete. Expected imports are missing
What i need to add to my custom *.csproj to get my project working in Visual Studio?
First, I suggest you to create a project from Visual Studio or from dotnet cli, and then add your custom target to csproj file.
Anyway, I think that the minimal csproj file looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
</PropertyGroup>
</Project>
This is the template from dotnet cli, of course, it depends on your project type (Console, ClassLibrary)

Visual Studio 2017 extension - VSToolsPath not working

I'm updating an old Visual Studio extension for VS 2017. It compiles fine from Visual Studio and msbuild in debug and release on my local computer.
This is the msbuild command line I am using:
msbuild VxCop.sln /p:ToolsHome=C:\ProgramData\chocolatey\bin /p:Configuration=Release /p:Platform="Any CPU"
However, on the build machine (TFS Build 2010) calling msbuild.exe with the same command line it fails with this error
In order to fix this I am trying to specify VSToolsPath. I've tried various things such as altering the VSToolsPath entry in the .csproj (which seems to not be taken into account since doing this had no effect) and also passing it on the command line:
msbuild VxCop.sln /p:ToolsHome=C:\ProgramData\chocolatey\bin /p:Configuration=Release /p:Platform="Any CPU" /p:VSToolsPath=Packages\Microsoft.VSSDK.BuildTools.15.1.192\tools\
This causes a very strange error:
CopyFilesToOutputDirectory:
Copying file from "obj\Release\SymCop.dll" to "bin\Release\SymCop.dll".
SymCop -> H:\src\tools\VisualStudioExtensions\Main\VxCop\source\SymCop\bin\Release\SymCop.dll
Copying file from "obj\Release\SymCop.pdb" to "bin\Release\SymCop.pdb".
Done Building Project "H:\src\tools\VisualStudioExtensions\Main\VxCop\source\SymCop\SymCop.csproj" (default targets).
Done Building Project "H:\src\tools\VisualStudioExtensions\Main\VxCop\VxCop.sln" (Build target(s)) -- FAILED.
Done Building Project "H:\src\tools\VisualStudioExtensions\Main\VxCop\build.proj" (default targets) -- FAILED.
Build FAILED.
0 Warning(s)
0 Error(s)
The actual extension project isn't appearing in the log at all, and there's no, y'know, errors. But the build returns as failed, the return code is non-zero, and the vsix project seems to not be built (its output is missing)
Hopefully someone has some suggestions
Thanks
Edit:
For those reading this in the future, the problem seemed to be that there was an <Import> further down in the same file which didn't care about my update to $(VSToolsPath).
Changing that import fixed it:
<Import Project="$(SolutionDir)\packages\Microsoft.VSSDK.BuildTools.15.1.192\tools\VSSDK\Microsoft.VsSDK.targets"
/>
Visual Studio 2017 extension - VSToolsPath not working
I got the same result as you based on your scripts. After installed the NuGet package Microsoft.VSSDK.BuildTools to the project, the Microsoft.VSSDK.BuildTools.props will be imported in to project file, open the project file, you can find below Import:
<Import Project="..\packages\Microsoft.VSSDK.BuildTools.15.1.192\build\Microsoft.VSSDK.BuildTools.props" Condition="Exists('..\packages\Microsoft.VSSDK.BuildTools.15.1.192\build\Microsoft.VSSDK.BuildTools.props')" />
Then open this props file, you can notice below scripts snippet:
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="VSSDK_NuGet_Configuration">
<ThisPackageDirectory>$(MSBuildThisFileDirectory)..\</ThisPackageDirectory>
<VSToolsPath>$(ThisPackageDirectory)\tools</VSToolsPath>
<VsSDKInstall>$(VSToolsPath)\VSSDK</VsSDKInstall>
<VsSDKIncludes>$(VsSDKInstall)\inc</VsSDKIncludes>
<VsSDKToolsPath>$(VsSDKInstall)\bin</VsSDKToolsPath>
</PropertyGroup>
</Project>
In this case, NuGet package override the value VSToolsPath with $(ThisPackageDirectory)\tools. So MSBuild will skip set the value setting in the next step in the project file:
<PropertyGroup>
<MinimumVisualStudioVersion>15.0</MinimumVisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
Because NuGet have already set the value $(VSToolsPath), the value of Condition="'$(VSToolsPath)' == ''" would be False. In addition, you can add a target to check if the value is set, like:
<Target Name="CheckVSToolsPath" BeforeTargets="Build">
<Message Text="$(VSToolsPath)"></Message>
</Target>
You will find this value is set to:
C:\Users\Admin\Documents\Visual Studio 2017\Projects\VSIXProject2\packages\Microsoft.VSSDK.BuildTools.15.1.192\build\..\\tools
Summary above, the value of VSToolsPath was imported correctly, we do not need to passing it on the command line.
After in-depth investigation, I found the reason for the previous error "MSB4226: The imported project "(...)\VSSDK\Microsoft.VsSDK.targets" was not found." is that the MSBuild property of "VisualStudioVersion" not be set on the build server.
See below link for detail info Building a VSIX extension with the Visual Studio 2017 Build Tools:
something that a machine with the full Visual Studio 2017 does and that a machine with the Build Tools 2017 does if you open a developer command prompt. Since I was not using it, I passed it as a parameter to the MSBuild script. It can be defined too inside the .csproj file, something that previous Visual Studio versions did automatically but recent versions don’t.
So to resolve the error "MSBuild4226", you should pass the visual studio version on command line:
msbuild VxCop.sln /p:ToolsHome=C:\ProgramData\chocolatey\bin /p:Configuration=Release /p:Platform="Any CPU" /p:VisualStudioVersion=15.0
After using this command line, the error MSBuild 4226 was resolved.
Hope this helps.
I resolved this problem in VS 2019 by https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#generatepathproperty
<ItemGroup>
<PackageReference Include="Microsoft.VSSDK.BuildTools" Version="16.10.1055">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<Import Project="$(PkgMicrosoft_VSSDK_BuildTools)\tools\vssdk\Microsoft.VsSDK.targets" />

Visual Studio 2017 not loading AjaxMin MSBuild Task from specified path

I'm attempting update an existing application so that it will load in Visual Studio 2017 (Enterprise - V15.2 (26430.12)) from Visual Studio 2015 (Enterprise) and having issues with the AjaxMin build task.
In Visual Studio 2015, the AjaxMin build task is found in C:\Program Files (x86)\MSBuild\Microsoft\MicrosoftAjax. Visual Studio 2017 looks for it in C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\Microsoft. Given that all developers on the team may not have VS2017 Enterprise and we've had issues in the past with developers installing the wrong version, I'd like to have it use the AjaxMin NuGet package referenced in the solution.
There are 3 projects in the solution that use the AjaxMin task. I've updated all of them to have the following import:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
...
<Import Project="$(MSBuildProjectDirectory)\..\..\..\packages\AjaxMin.5.14.5506.26202\tools\net40\AjaxMin.targets" />
<Target Name="BeforeBuild">
<ItemGroup>
<JS Include="..." Exclude="..." />
</ItemGroup>
<ItemGroup>
<CSS Include="..." Exclude="..." />
</ItemGroup>
<AjaxMin Switches="..." JsSourceFiles="#(JS)" JsCombinedFileName="..." CssSourceFiles="#(CSS)" CssCombinedFileName="..." />
</Target>
...
</Project>
However, when the build happens, I receive an error saying it can't find the AjaxMin task in the default build extensions directory:
The "AjaxMin" task could not be loaded from the assembly C:\Program
Files (x86)\Microsoft Visual
Studio\2017\Enterprise\MSBuild\Microsoft\MicrosoftAjax\AjaxMinTask.dll.
Could not load file or assembly 'file:///C:\Program Files
(x86)\Microsoft Visual
Studio\2017\Enterprise\MSBuild\Microsoft\MicrosoftAjax\AjaxMinTask.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.
I've used the MSBuild Message Task to output the path and verified that it is correct by navigating to it from the command prompt. I also tried using the UsingTask with no luck there.
Why is Visual Studio not looking for the AjaxMin DLL in the location I specified?
Update:
I moved the import tag for AjaxMin to the top (right under the project tag) and it works now. I don't understand why.
I resolved this issue by installing the .Net 3.5 framework
The issue turned out being another import for another third party build task. It appears that build task alters the build process such that the AjaxMin build task won't work after it. Having the AjaxMin build task after the Microsoft.CSharp.Targets import but before the other third party build task did the trick.
I moved the import tag for AjaxMin to the top (right under the project tag) and it works now. I don't understand why.
If I understand your question correctly, that is because you have two reference sources for the AjaxMinTask.dll, one is the default assembly source:C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\Microsoft\MicrosoftAjax, another is the import reference, you will notice that the AjaxMinTask.dll is used in the AjaxMin.targets:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask AssemblyFile="AjaxMinTask.dll" TaskName="AjaxMin" />
<UsingTask AssemblyFile="AjaxMinTask.dll" TaskName="AjaxMinBundleTask" />
<UsingTask AssemblyFile="AjaxMinTask.dll" TaskName="AjaxMinManifestTask" />
<UsingTask AssemblyFile="AjaxMinTask.dll" TaskName="AjaxMinManifestCleanTask" />
...
</Project>
So when set the import reference behind your task, MSBuild will use the default assembly source (under the VS2017 installation directory), when you set the import reference before you task, the AjaxMin.targets will rewrite the path of reference source.

Create proj file that loads csproj files into solution explorer when opened with visual studio

I am building a project file for our application that I am going to execute from our build machine. I was wondering if it is possible for me to open the project file and get the same view visual studio gives me of the solution when I open a solution file.
So here is my Contosa.proj file so far.
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)'=='' ">Debug</Configuration>
<RootNamespace>Contosa</RootNamespace>
<AssemblyName>Contosa</AssemblyName>
</PropertyGroup>
<ItemGroup>
<Projects Include="C:\Users\localuser\Documents\Perforce\Contosa\Branches\Working23\UI\Desktop\ContosaClient\ContosaClient.csproj" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="Build">
<PropertyGroup>
<Contosa>$(MSBuildProjectDirectory)\UI\Desktop\ContosaClient\ContosaClient.csproj</Contosa>
</PropertyGroup>
<MSBuild Projects="$(Contosa)"
Properties="Configuration=QA;
VisualStudioVersion=12.0;
DevEnvDir=C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\;
SolutionDir=$(MSBuildProjectDirectory)\"/>
</Target>
</Project>
Currently double clicking this file with visual studio as its associated application will open the Contosa.proj for text editing. I would really like it to be possible to associate my Contosa.proj file with visual studio like our Contosa.sln file is. So when developers open it with visual studio they get the same view that you get from the Contosa.sln. I don't understand what parts of a csproj or a sln file make them open as projects or solutions in visual studio.
UPDATE 1
I am looking to do what this user Replace .sln with MSBuild and wrap contained projects into targets did but I want the project file to be able to opened by the user like a solution file. I want the Projects I include to be loaded into the solution explorer.
When visual studio installs, it configures explorer to launch visual studio when a .csproj is doubleclicked (How can I set a file association from a custom file type to a program). Your windows, and your developers windows, don't know what a .proj is. So rename it .csproj or distribute a .sln which references .proj

Where is WindowsSDK_IncludePath defined?

The macro $(WindowsSDK_IncludePath) has the values shown in the picture.
I'd like to know where those values are defined, they must be defined in some files.
The picture was taken from Visual Studio 2013.
In my case, the WindowsSDK_IncludePath variable was correctly defined and in fact it worked perfectly in Visual Studio 2013. So I even uninstalled and reinstalled VS2015. Then I found thanks to this link that the preset macro variables can be modified by specific user settings. These user settings are stored in C:\Users\<user>\AppData\Local\Microsoft\MSBuild\v4.0\Microsoft.Cpp.Win32.user.props which in my case read as follows:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets">
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<IncludePath>$(VC_IncludePath);$(WindowsSDK_IncludePath);C:\Program Files (x86)\CodeSynthesis XSD 4.0\include;</IncludePath>
<LibraryPath>$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);C:\Program Files (x86)\CodeSynthesis XSD 4.0\lib\vc-12.0;</LibraryPath>
<ExecutablePath>C:\Program Files (x86)\CodeSynthesis XSD 4.0\bin;$(VC_ExecutablePath_x86);$(WindowsSDK_ExecutablePath);$(VS_ExecutablePath);$(MSBuild_ExecutablePath);$(SystemRoot)\SysWow64;$(FxCopDir);$(PATH);</ExecutablePath>
</PropertyGroup>
<ItemDefinitionGroup />
<ItemGroup />
</Project>
Somehow the <IncludePath> sentence made it impossible for VS2015 to find the correct value. In my laptop computer where everything worked correctly, the file was basically empty:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets">
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
</PropertyGroup>
<ItemDefinitionGroup />
<ItemGroup />
</Project>
After setting my file just like the one in my laptop, everything worked normally. Of course I lost the execution preference of CodeSynthesis XSD but right now I am not working on any project that use it. I will continue experimenting with different variants of this file.
For me it was the file Microsoft.Cpp.Common.props in folder C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140, where I had to change the hardcoded Windows 10 SDK version from 10.0.10240.0 to 10.0.10586.0.
<!-- 10.0.10240.0 is the hardcoded checked-in version of uCRT that we use in case we target 8.1 SDK -->
<TargetUniversalCRTVersion Condition="'$(TargetUniversalCRTVersion)' == '' and ('$(TargetPlatformVersion)' == '8.1' or '$(DefineWindowsSDK_71A)' == 'true')">10.0.10586.0</TargetUniversalCRTVersion>
I'm using VS2015 on Windows 10 and wasn't able to compile against Windows 8.1 SDK because of missing include files. Installing the standalone Windows 10 SDK didn't help either (because it doesn't contain ucrt files for 10.0.10240 like ctype.h a.s.o.).
I see the data in file sdk.props in folder C:\Program Files (x86)\Windows Kits\8.0\build\CommonConfiguration\Neutral
<PropertyGroup>
<WindowsSdkDir Condition="'$(WindowsSdkDir)' == ''">$([MSBUILD]::GetDirectoryNameOfFileAbove('$(MSBUILDTHISFILEDIRECTORY)', 'sdkmanifest.xml'))</WindowsSdkDir>
</PropertyGroup>
<PropertyGroup> <WindowsSDK_IncludePath>$(WindowsSdkDir)Include\um;$(WindowsSdkDir)Include\shared;$(WindowsSdkDir)Include\winrt;</WindowsSDK_IncludePath>
</PropertyGroup>
I use a Win8 + VS2012, so it should be in folder 8.1 for your VS2013 + 8.1 SDK.
I find Reason that : Windows Registry
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots]
"KitsRoot10"="C:\\Program Files\\Windows Kits\\10\\"
but in Actually
"KitsRoot10"="C:\\Program Files (x86)\\Windows Kits\\10\\"
"AppVerifier64BitAutomationRoot"="C:\\Program Files\\Application Verifier\\"
"KitsRoot81"="C:\\Program Files (x86)\\Windows Kits\\8.1\\"
Search for *.props in C:\Program Files (x86)\Windows Kits\
In my case the file causing the problem was UAP.props. Editing the file and changing 4.7.1 to 4.6.1 solved the issue.
<PropertyGroup>
<WindowsSdkDir Condition="'$(WindowsSdkDir)' == ''">$([MSBUILD]::GetDirectoryNameOfFileAbove('$(MSBUILDTHISFILEDIRECTORY)', 'sdkmanifest.xml'))\</WindowsSdkDir>
<DotNetSdkRoot Condition="'$(DotNetSdkRoot)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\NETFXSDK\4.7.1#KitsInstallationFolder)</DotNetSdkRoot>
<DotNetSdkRoot Condition="'$(DotNetSdkRoot)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SDKs\NETFXSDK\4.7.1#KitsInstallationFolder)</DotNetSdkRoot>
</PropertyGroup>

Resources