Directory.Build.Props and Msbuild /nr parameter - msbuild

I have a quite huge solution and I need to add the /nr:false parameter to each build.I've found googling that starting from msbuild 3.5 it's possible to write a Directory.Build.Props and all the msbuild will realay to this one.
I've tried with this XMLbut doesn't seem to work
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MSBUILDDISABLENODEREUSE>1</MSBUILDDISABLENODEREUSE>
</PropertyGroup>
</Project>
What am I doing wrong?

Node reuse is a flag on msbuild itself, not in the projects. While environment variables are also treated as global properties in projects, some - like MSBUILDDISABLENODEREUSE - are interpreted by msbuild itself, before even processing projects. So setting a property with the same name inside a project file will have no effect.
In MSBuild 15.6 (in preview at the time of writing), it will support a similar concept with files named Directory.Build.rsp which can contain additional command line switches, so you can create a file with that name and /nr:false as content.

Related

Is it possible to extract the value of element in csproj file with dotnet CLI?

csproj file contains project properties which I'd like to use in CI/CD pipeline.
Is it possible to extract those values with dotnet cli (or some other standard tool) without parsing xml via some standalone script?
For example having the project:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.0.0</Version>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
</<Project>
I need to resolve the value of <Version>.
It's also wroth noting that project may contain conditional elements, and ideally it would be nice to resolve properties in the context of predefined variables.
MSBuild is very extensible. You could use a target to write the version number to a file:
<Target Name="WriteVersion" AfterTargets="Build">
<WriteLinesToFile Lines="$(Version)"
File="$(IntermediateOutputPath)version.txt" />
</Target>
That would write a version.txt file to a folder such as obj/Debug/net5.0 (depending on the Configuration and TargetFramework).
Also be sure to check out https://msbuildlog.com/ for how to investigate / debug builds.

How to ship the stylecop.json and custom.ruleset files with a NuGet package in VS2017

At the moment we are switching from VS2015 to VS2017. One of our upgrade steps is to switch from stylecop to the new Stylecop.Analyzer package. The new Stylecop is using 2 files. The stylecop.json and the Stylecop.ruleset.
The target: I want to provide the stylecop files as a custom nuget package. But I dont know how to create the needed .csproj entries.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
...
<CodeAnalysisRuleSet>packages\My.StyleCop.1.0.0-pre15\RuleSet\My.StyleCop.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
...
<ItemGroup>
<AdditionalFiles Include="packages\My.StyleCop.1.0.0-pre15\Config\stylecop.json">
<Link>stylecop.json</Link>
</AdditionalFiles>
</ItemGroup>
</Project>
In the past, there was the possibility to use a install.ps1 script to do this stuff. But with NuGet 3. (or 4.) the install scripts are obsolete and will be ignored.
I already tried to use My.StyleCop.targets:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<AdditionalFiles Include="packages\My.StyleCop.1.0.0-pre17\Config\stylecop.json">
<Link>stylecop.json</Link>
</AdditionalFiles>
</ItemGroup>
</Project>
But here I have some issues, too. Since NuGet 3. (or 4.) there is no solution wide package folder and I dont know any variable or placeholder I can use here to get a absolute or relative path to my package.
You can add .props or .targets files to the build folder in your packages and they will be imported to the projects.
On the .props file, you can use the MSBuildThisFileDirectory MSBuild variable that represents the folder where that file is located.
Thanks to Paulo.
How I did it:
This is the structure of my NuGet package.
The solution is quiet easy. You need to create to files. A .props and a .targets file named like the NuGet package and place them in the build folder of your package.
In these MSBuild files you can use the $(MSBuildThisFileDirectory) variable to get the path of your NuGet package.
MSBuildThisFileDirectory = C:\Users\UserName\.nuget\packages\sig.stylecop\1.0.0-pre23\build\
My SIG.StyleCop.props file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)\..\RuleSet\SIG.combiLink.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
</Project>
My SIG.StyleCop.targets file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<AdditionalFiles Include="$(MSBuildThisFileDirectory)\..\Config\stylecop.json">
<Link>stylecop.json</Link>
</AdditionalFiles>
</ItemGroup>
</Project>
Cause of the structure of my package i need to navigate (..) into the Config and into the RuleSet folder.
The variable $(MSBuildThisFileDirectory) already includes the backslash at the end. It is important to omit the backslash when you reference the ruleset and the stylecop.json file:
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)..\RuleSet\SIG.combiLink.ruleset</CodeAnalysisRuleSet>
<AdditionalFiles Include="$(MSBuildThisFileDirectory)..\Config\stylecop.json">
With the double backslash I experienced two strange problems in Visual Studio 2017:
Unit tests rebuild the code each time I start them, even without any code change
The IDE shows many StyleCop errors in the Error List window and shows red marks in the scroll bar even for rules that are explicitly disabled in the rule set.

How to ignore the property value given on the command line from within the respective csproj file?

Our TFS build controllers build all the projects from the same solution into the same shared bin directory, because the TFS build workflow passes the OutDir parameter to the msbuild command responsible for building the solution.
I have a project where I want to suppress this behavior and let it build into the standard relative bin\Debug or bin\Release directory.
But I cannot find how to do it. Indeed, observe the following trivial msbuild script:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<OutDir>$(MSBuildThisFileDirectory)bin\$(Configuration)</OutDir>
</PropertyGroup>
<Target Name="Build">
<Message Text="$(OutDir)" Importance="High"/>
</Target>
</Project>
Now, I am running it:
PS C:\> msbuild .\1.csproj /p:OutDir=XoXo /nologo
Build started 11/13/2015 9:50:57 PM.
Project "C:\1.csproj" on node 1 (default targets).
Build:
XoXo
Done Building Project "C:\1.csproj" (default targets).
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.03
PS C:\>
Notice it displays XoXo, ignoring my attempt to override it from within.
So, is it possible?
This is a bit of a classic RTFM situation but interesting nonetheless. See the documentation for MSBuild Properties in particular the part on Global Properties and how to make properties not being overridden by the former:
MSBuild lets you set properties on the command line by using the /property (or /p) switch. These global property values override property values that are set in the project file. This includes environment properties, but does not include reserved properties, which cannot be changed.
Global properties can also be set or modified for child projects in a multi-project build by using the Properties attribute of the MSBuild task
If you specify a property by using the TreatAsLocalProperty attribute
in a project tag, that global property value doesn't override the
property value that's set in the project file.
It also links to the Project element documentation which basically repeats the same info and says multiple properties in the attribute should be seperated by semi-colons.
In short, code applied to your case:
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
TreatAsLocalProperty="OutDir">
Note that this will completely disable altering OutDir from outside the project though. An alternate solution which is more configurable could be to have a small stub project which you make TFS build instead of the main project. In that project you can then decide on whether to pass OutDir to the actual project or override it, e.g. by fetching the value by importing a file which might or might not be defined, or based on an environment variable or so. This gives the basic idea:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Try import if this file exists, it should supply the value for CustomOutDir-->
<Import Project="$(MSBuildThisFileDirectory)customoutdir.props" Condition="Exists('$(MSBuildThisFileDirectory)customoutdir.props')"/>
<PropertyGroup>
<!-- Default value for CustomOutDir if not set elsewhere -->
<CustomOutDir Condition="'$(CustomOutDir)' == ''">$(MSBuildThisFileDirectory)bin\$(Configuration)</CustomOutDir>
<!-- ApplyCustomOutDir specifies whether or not to apply CustomOutDir -->
<ActualOutDir Condition="'$(ApplyCustomOutDir)' == 'True'">$(CustomOutDir)</ActualOutDir>
<ActualOutDir Condition="'$(ApplyCustomOutDir)' != 'True'">$(OutDir)</ActualOutDir>
</PropertyGroup>
<Target Name="Build">
<MSBuild Projects="$(MasterProject)" Properties="OutDir=$(ActualOutDir)"/>
</Target>
</Project>
And should be invoked by passing the neede properties like
msbuild stub.targets /p:MasterProject=/path/to/main.vcxproj;ApplyCustomOutDir=True
(I have never used TFS so the way to get the properties passed might be different)

error MSB4064: The "OverwriteReadOnlyFiles" parameter is not supported by the "Copy" task

I am using Msbuild 4.0. When i was using Msbuild 3.5 OverwriteReadonlyfiles worked without any issue.
But today when i was trying to use the copy task i am getting this issue.
error MSB4064:
The "OverwriteReadOnlyFiles" parameter is not supported
by the "Copy" t ask. Verify the parameter exists on the task, and it
is a settable public instance property.
This is my target which has copy task
<Target Name="CopyBOM">
<Copy SourceFiles="#(BOM)" DestinationFolder="%(BOM.Destination)" OverwriteReadOnlyFiles="true">
<Output TaskParameter="CopiedFiles" ItemName="CopyBOMFiles" />
</Copy>
<Message Text="Copied to BOM: #(CopyBOMFiles)"/>
</Target>
Following is the itemgroup i am using in my properties file
<BOM Include="..\..\..\Release\CoreDeployment.msi">
<Destination>..\..\..\Core\BOM\Comp1</Destination>
</BOM>
I am having multiple properties file, I have verified that in all the place Toolsversion=4.0 is placed.
Has any one faced this? Any way is there to solve this?
I'm using OverwriteReadOnlyFiles="true" without issues. Try to add ToolsVersion="4.0" into your Project tag:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
You are most likely dealing with a ToolsVersion issue, even though you say you've checked all your files and their imports. Omitting the ToolsVersion from a file will cause it to drop to a lower version, so if all you did was search for "ToolsVersion" you may have missed a file where it wasn't declared on the <Project> node at all.
Run a command line build with diagnostic level logging, with the following additional parameters:
> msbuild My.proj /fl /flp:v=diag;logfile=My.proj.diagnostic.log
After the build fails, open the log file, seach for "MSB4064" then backtrack up the file looking for anything specifying the 2.0 framework.

Specifying assembly version of all projects within a web deployment wdproj script

I have a .wdproj Web Deployment Project created with VS2010 that contains references to other class libraries, like this:
<Project ToolsVersion="4.0" DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ProjectReference Include="..\Path\Proj1.csproj">
<Project>{GUID-HERE}</Project>
<Name>Proj1</Name>
</ProjectReference>
<ProjectReference Include="..\Path\Proj2.csproj">
<Project>{GUID-HERE}</Project>
<Name>Proj2</Name>
</ProjectReference>
There are lots of these. I want to be able to run msbuild /t:Rebuild /p:Configuration=Release and have all the assemblies of all the included projects compiled at a specified version. Nothing fancy just static like 2.5.6.0 and specified once in the wdproj file. I dont want to open 30 files manually.
I have looked at MSBuild Community Task and MSBuildExtension Pack and can not get anything to work. The build runs ok without errors.
Anyone have an example of how this can be done?
This is an attempt with MSBuild Extensions (adapted from the sample included) that doesn't work:
<Import Project="$(MSBuildExtensionsPath)\ExtensionPack\4.0\MSBuild.ExtensionPack.VersionNumber.targets"/>
<Target Name="Build">
<MSBuild.ExtensionPack.Framework.AssemblyInfo
ComVisible="true"
AssemblyInfoFiles="VersionInfo.cs"
AssemblyFileMajorVersion="2"
AssemblyFileMinorVersion="5"
AssemblyFileBuildNumber="6"
AssemblyFileRevision="0"
/>
</Target>
MSBuild is definately looking at the MSBuild.ExtensionPack.Framework.AssemblyInfo element because if the attribute names are incorrect the build will fail. This builds ok but none of the versions on the referenced assemblies are changed. The version numbers on the ASP.NET page assemblies from the website are all 0.0.0.0.
Are you maybe missing to specify the CodeLanguage and OutputFile attributes?
I think the AssemblyInfo task is intended to generate (replace) a source file prior to compiling.