Rake task for MSBuild finds different version than my VS - visual-studio

I'm having some trouble getting a rake build script to work locally (it runs fine on our CI server), because it for some reason uses an incorrect version of msbuild. I can also build it fine from inside Visual Studio 2013.
The following existed in my web project's .csproj file when I started working on this:
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)'==''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<!--<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />-->
<!-- Above commented out because it fails - keep reading for why... -->
I added the following target for debugging purposes:
<Target Name="ShowVersions">
<Message Text="VS version: $(VisualStudioVersion)" />
<Message Text="VSToolsPath: $(VSToolsPath)" />
</Target>
and the accompanying rake task:
msbuild :debug_versions do |msb|
msb.solution = "src/NCVIB.WebMVC/NCVIB.WebMVC.csproj"
msb.targets :ShowVersions
end
Now, if I run the ShowVersions target from my terminal directly using MSBuild MyWebProject.csproj /t:ShowVersions it correctly identifies my VS version as 12.0. If I run rake debug_versions, it says the VS version is 11.0, and consequentially the import of WebApplications.targets fails (the path doesn't exist).
How do I make the rake task pick up the same VS version as plain msbuild does?

Related

Why does overriding the Build target in msbuild work for C++ projects but fail for C# projects?

I am overriding the Build target like this in my file OverrideBuild.targets:
<Target Name="OriginalBuild" DependsOnTargets="$(BuildDependsOn)">
<Message Text="Finished running target OriginalBuild" Importance="High" />
</Target>
<Target Name="Build" >
<CheckArtifacts ProjectGuid = "$(ProjectGuid)" SolutionPath = "$(SolutionPath)" >
<Output PropertyName = "ArtifactsHaveChanged" TaskParameter = "Result" />
</CheckArtifacts>
<Message Text="ArtifactsHaveChanged = $(ArtifactsHaveChanged)" Importance="high" />
<!-- if the artifacts.props file has not just been updated then we can run the original build target -->
<Message Condition="'$(ArtifactsHaveChanged)' == 'false'" Text="Running target OriginalBuild" Importance="High" />
<CallTarget Condition="'$(ArtifactsHaveChanged)' == 'false'" Targets="OriginalBuild" />
<!-- Otherwise we need to run a new msbuild to avoid using an out-of-date cached version of the artifacts.props file.
To force the msbuild process not to use the cached values from this process we must pass at least one property.
-->
<Message Condition="'$(ArtifactsHaveChanged)' == 'true'" Text="Running target OriginalBuild in nested msbuild" Importance="High" />
<MSBuild Condition="'$(ArtifactsHaveChanged)' == 'true'" Targets="OriginalBuild"
Projects="$(MSBuildProjectFullPath)" Properties="InNestedMsbuild=true" />
<!-- Visual Studio doesn't pick up on the modified artifacts.props file unless we force it to reload the solution -->
<Touch Condition="'$(ArtifactsHaveChanged)' == 'true' and '$(BuildingInsideVisualStudio)' == 'true'" Files = "$(SolutionPath)" />
<Message Text="Finished running build target override" Importance="High" />
</Target>
and each of my .vcxproj or .csproj files includes this file at the end:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\OverrideBuild.targets" />
</Project>
This works as I want it to for the C++ projects but fails with the C# projects. When building a C# project via msbuild it fails because the command line to the C# compiler is missing reference arguments for local assemblies. For example, a C# file that has a line like this at the top of the file:
using My.Utils.Common;
fails with the following error message:
error CS0234: The type or namespace name 'Common' does not exist in the namespace 'My.Utils' (are you missing an assembly reference?)
And looking at the compiler command used it is missing this line:
/reference:C:\Code\scratch\Build\My.Utils.Common\Bin\Release\My.Utils.Common.dll
That missing line is present when I comment out my override of the Build target. And weirdly enough it will build fine from within Visual Studio even with my Build override in place. It only fails when building using msbuild from the command line and only for C# projects.
I thought that the way I had overriden the Build target would be completely transparent but apparently it isn't. Can anybody shed some light on what is going wrong ?
It seems that when project A depends on project B with a project reference, the outputs of the Build target of B are used to deduce what should be passed as a reference to the compiler when building A. This is presumably somewhere in the ResolveAssemblyReferences logic.
Therefore to get your replacement Build target working, you need to make its outputs match those of the standard Build.
Here is how you can achieve this:
<Target
Name="Build"
Condition=" '$(_InvalidConfigurationWarning)' != 'true' "
DependsOnTargets="GetTargetPathWithTargetPlatformMoniker"
Returns="#(TargetPathWithTargetPlatformMoniker)" >
</Target>
Here Returns="#(TargetPathWithTargetPlatformMoniker)" is what the Returns of the standard Build in the SDK is. But the item array #(TargetPathWithTargetPlatformMoniker) is initially empty, so you need to run the Target GetTargetPathWithTargetPlatformMoniker to populate it before hand.
These are implementation details of the build system, so they may vary by SDK version, but you can always inspect the logic in C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin\Microsoft.Common.CurrentVersion.target or equivalent.
Note that this cannot be used directly with C++ projects, their default Build target is a bit different. You may need to vary by the project type to support both. The Condition on a Target does not stop it from overwriting the existing one, it only stops it from executing, so if you need a target overwrite to differ, you need to put the alternatives in files and import them conditionally. I don't know of a more convenient way, but that at least works.
Why does overriding the Build target in msbuild work for C++ projects but fail for C# projects?
After test your sample, I found the error not comes from the overriden the Build target, it should be related to the project type which you referenced.
Because I have tried comment the import line in the HelloWorld project file:
<Import Project="..\..\OverrideBuild.targets" />
Then MSBuild command line still throw that error.
Besides, I found your referenced project HelloWorldHelper is a Console application project, which output type is Class library.
To resolve this issue, I have created a new Class library instead of Console application, then build it from MSBuild command line, it works fine.
So, please try to convert your referenced project to Class library.
Hope this helps.

Get AssemblyVersion number from AssemblyInfo.cs in Command Line

I am trying to automate a job in Jenkins to build and deploy a visual studio solution. We can already get Jenkins to build the project. I have created a batch file that Jenkins runs after the project build that deploys a Squirrel package but I have parameterized the batch file as well as the Jenkins job which means I am still manually typing in the version number each time I run the job. What we need is to extract the version number from the project so it can be used as a parameter in the Squirrel batch file.
For my purposes, I moved the Squirrel logic to the .csproj file in the "AfterBuild" event. Now, every time a Release build is executed, a package is built and "releasified" along with the accessible version number.
<Target Name="AfterBuild" Condition=" '$(Configuration)' == 'Release'">
<GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
<Output TaskParameter="Assemblies" ItemName="myAssemblyInfo"/>
</GetAssemblyIdentity>
<Exec Command="D:\Squirrel\nuget pack "D:\Squirrel\Nuspec Files\OurApplication.nuspec" -Version %(myAssemblyInfo.Version)" />
<Exec Command="D:\Squirrel\Squirrel.Windows-1.4.0\squirrel --releasify D:\Jenkins\default\Projects\OurApplication\Windows\OurApplication.%(myAssemblyInfo.Version).nupkg -r D:\Squirrel\Releases\OurApplication" />
</Target>
See https://github.com/Squirrel/Squirrel.Windows/blob/master/docs/using/visual-studio-packaging.md

Upgrade to MVC4 Targeting 4.0 and VS2010, but MS Build still looking for v11.0

We have upgraded our MVC3 project to MVC4 running in VS2010, still targeting the 4.0 framework (not quite ready for 4.5). This all works fine in development, but the build is failing on the build server (using Jenkins/Hudson, sadly), with the following message:
error MSB4019: The imported project "C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v11.0\WebApplications\Microsoft.WebApplication.targets" was not found. Confirm that the path in the declaration is correct, and that the file exists on disk.
I looked for an statement in the project file to match this, but there is none. The second line (below) in the csproj is explicitly targeting v10.0:
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
I suppose the first one could somehow be resolving to v11.0, but I'm not sure where that would be coming from. The solution has been opened in VS 2012 at least once, so I'm assuming it inserted something somewhere, but not sure what.
In my dev environment, I've renamed the VS 2012 path and can still load and build my project in VS 2010.
Found it - the answer is more or less here: http://blogs.msdn.com/b/webdev/archive/2012/08/22/visual-studio-project-compatability-and-visualstudioversion.aspx
Basically, if you ever open an MVC4 project in VS2012, it changes the project file to dynamically detect the VS version and default to v10.0 if that value isn't found.
I'm not 100% sure where the v11.0 was coming from on the build server. Reverting the .sln file did not seem to make a difference. However, after reverting that particular change to the .csproj file, it builds successfully.
Specifically, I commented the added lines like so:
<!--
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
-->
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!--
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
-->
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />

How can I debug an MSBuild file?

I've got a large solution that I'm using TFS (and MSBuild) to... well... build. However, it takes a long time to build everything, and I was wondering if it was possible to just debug the build XML file rather than doing the build itself.
I'm using VS2008 and TFS 2008.
Unfortunately the possibility to debug MSBuild scripts with Visual Studio has been unofficially introduced in .NET 4.0.
For earlier versions all you are left with is "debugging by tracing", that is inserting log statements at key points in your script, running the script and examining the output.
Here's how you would typically do it using the Message Task:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SomeVariable>foo</SomeVariable>
</PropertyGroup>
<Target Name="MyTarget">
<!-- Some tasks -->
<Message Text="The value of SomeVariable is: $(SomeVariable)" Importance="High" />
<!-- Some tasks -->
</Target>
</Project>
You can then invoke the script from the command line and redirect the output to a log file:
msbuild MyScript.proj /t:MyTarget > %USERPROFILE%\Desktop\MyScript.log
Related resources:
Debugging MSBuild scripts with Visual Studio (.NET 4.0)
Overview of Logging in MSBuild

Publish ClickOnce from the command line

Is there a way to have Visual Studio 2008 execute the "Publish Now" button from the command line?
I've seen posts that suggest to use msbuild /target:publish to call it. That is OK, but MSBuild doesn't increment the revision number. I'm hoping for something like:
devenv mysolution.sln /publish
To increment build numbers, I am using MSBuild Extension pack inside my .csproj file as follows:
<Target Name="BeforeBuild" Condition=" '$(Configuration)|$(Platform)' == 'Release-VersionIncrement|AnyCPU' ">
<CallTarget Targets="CleanAppBinFolder" />
<MSBuild.ExtensionPack.VisualStudio.TfsSource TaskAction="Checkout" ItemCol="#(AssemblyInfoFiles)" WorkingDirectory="C:\inetpub\wwwroot\MySolution" ContinueOnError="true" />
<!-- Microsoft's task that goes over assembly files and increments revision number. -->
<MSBuild.ExtensionPack.Framework.AssemblyInfo Condition="'$(Optimize)'=='True' " AssemblyInfoFiles="#(AssemblyInfoFiles)" AssemblyRevisionType="AutoIncrement" AssemblyFileRevisionType="AutoIncrement">
<Output TaskParameter="MaxAssemblyVersion" PropertyName="MaxAssemblyVersion" />
</MSBuild.ExtensionPack.Framework.AssemblyInfo>
<Message Text="----current version---: '$(MaxAssemblyVersion)'" />
</Target>
This way, anytime the configuration is set to Release-VersionIncrement, the version number is changed. When this is done, I can use the following MSBuild command to publish it:
msbuild c:\projects\MyProject.csproj
/t:ResolveReferences;_CopyWebApplication
/p:Configuration=Release;BuildingProject=true;WebProjectOutputDir=c:\inetpub\wwwroot\OutputProject\MyProjectOutput;OutDir=c:\inetpub\wwwroot\OutputProject\MyProjectOutput
Note that this is for an ASP.NET 3.5 web application.

Resources