PackageReferences - is there a way how to force inclusion order? - msbuild

I have a project that looks like
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<PackageReference Include="TypescriptCommonProperties.MSBuild" />
<PackageReference Include="Microsoft.TypeScript.MSBuild" />
</ItemGroup>
Each package contains .targets file - TypescriptCommonProperties.MSBuild punch of common variables, Microsoft.TypeScript.MSBuild is the msbuild typescript official tooling. I tried to experiment with the order - but each time I end up with Microsoft.TypeScript.MSBuild properties being evaluated before the TypescriptCommonProperties.MSBuild properties.
Inlining the properties into the csproj works, but I'd like to have a common "settings" file - is there a way how to force it ?

Related

How to stop the localized Microsoft.CodeAnalysis.*.resources.dll files from getting published by ASP.NET Core?

When I publish an ASP.NET Core 3.0 project, I get a few localized folders where the 4 assemblies shown are in each of these folders. I am not sure why these folders and files get included. None of my packages reference a CodeAnalysis package.
I added <PreserveCompilationContext>false</PreserveCompilationContext> in the csproj file but it didn't help. Is there a way to exclude them?
Add this:
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
to the .csproj file:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
</PropertyGroup>
As suggested, you can use none to exclude all of them:
<SatelliteResourceLanguages>none</SatelliteResourceLanguages>
and taking consideration languages do you want like english and spanish:
<SatelliteResourceLanguages>en;es</SatelliteResourceLanguages>
Works with VS2019 and other versions
UPDATE 2021/2022:
Still working with Visual Studio 2022 and .NET 6
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
You get a lot of language folders containing CodeAnalysis.dll files in your published output if you have a project reference to Microsoft.VisualStudio.Web.CodeGeneration.Design, which is needed for scaffolding controllers. If that is true for your project, change the package reference in your .csproj file to include ExcludeAssets="all"
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.0.0" ExcludeAssets="All" />
For example, old *.csproj file
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<UserSecretsId>aspnet-foo-4E53EF45-B3BE-4943-81BE-2449DC5AA2BC</UserSecretsId>
<BlazorLinkOnBuild>false</BlazorLinkOnBuild>
</PropertyGroup>
<ItemGroup>
<!-- ... -->
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design"
Version="3.0.0" />
</ItemGroup>
<ItemGroup>
<!-- ... -->
</ItemGroup>
</Project>
New file *.csproj should be
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<UserSecretsId>aspnet-foo-4E53EF45-B3BE-4943-81BE-2449DC5AA2BC</UserSecretsId>
<BlazorLinkOnBuild>false</BlazorLinkOnBuild>
</PropertyGroup>
<ItemGroup>
<!-- ... -->
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design"
Version="3.0.0"
ExcludeAssets="All" />
</ItemGroup>
<ItemGroup>
<!-- ... -->
</ItemGroup>
</Project>
In my case, the source of these localized folders was from the package Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation. It has a dependency on Microsoft.CodeAnalysis.Razor. You can read more about the purpose of the package here: https://learn.microsoft.com/en-us/aspnet/core/mvc/views/view-compilation?view=aspnetcore-3.1
You cannot just exclude an asset when trying to take advantage of the package. My work-around was to conditionally include the package reference whenever the project is in debug mode.
<ItemGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<PackageReference Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="3.1.1" />
</ItemGroup>
I then used an #if pre-processor directive to conditionally run the code that enables razor runtime compilation.
#if DEBUG
services.AddRazorPages().AddRazorRuntimeCompilation();
#else
services.AddRazorPages();
#endif
Please note: You may need to delete your bin folder to see the folders removed after a build. Also, make sure you are building under the correct solution configuration.
I was able to find a Github issue describing this exact scenario, but unfortunately it was never resolved. https://github.com/dotnet/extensions/issues/2247

msbuild PackageReference.PrivateAssets = All does not seem to work

I have a test project which reference NUnit3TestAdapter. I do not this reference to be copied over to the projects that depend on this one.
I thought setting PrivateAssets = All would do it, but apparently I misunderstand how it works, because it does not have the desired effect.
Here is the code:
Rollup\Rollup.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\UITests\UITests.csproj"/>
</ItemGroup>
</Project>
UITests\UITests.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NUnit3TestAdapter" Version="3.11.2">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>
Directory.Build.rsp
.\Rollup.sln /restore /v:m
After I run msbuild all is built, but I can see NUnit3TestAdapter is in the bin folder for Rollup.
What am I missing?
(https://github.com/Microsoft/msbuild/issues/3996)
PrivateAssets works as expected but the NUnit test adapter NuGet package adds an MSBuild target to the build that adds a few dll files as content items to the project, which then flow transitively through the build - this has the same effect as if you added a text file and set its "Copy to Output Directory" property.
The NUnit3TestAdapter.props contains definitions like:
<Content Include="$(MSBuildThisFileDirectory)NUnit3.TestAdapter.dll">
<Link>NUnit3.TestAdapter.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>False</Visible>
</Content>
You should see these files if you click the "Show All Files" in the Visual Studio solution explorer.
Note that test projects aren't really supposed to be packaged or referenced. They should be leaf projects. The test project templates even contain an <IsPackable>false</…> definition and XUnit's core package also adds it as an imported MSBuild file. The test frameworks expect you to use their abstraction libraries and not runtime assemblies / test adapter packages for projects that share tests or test logic.

How to include a local DLL reference in to a nuget package when calling msbuild pack?

We have several projects that need to include a few static DLL. Therefore the project files include code like this:
<ItemGroup>
<Reference Include="..\_Solutions\dependencies\abc123.dll" />
<Reference Include="..\_Solutions\dependencies\def456.dll" />
<Reference Include="System.Web" />
</ItemGroup>
Expected:
We expected that the two dlls; abc123.dll and def456.dll would befound in the nupkg file.
Actual:
However, the nupkg doesn't include the abc123.dll nor the def456.dll files.
One can always include custom content in the nuget-package. Like this:
<ItemGroup>
<Content Include="$(OutputPath)\ReferencedLib.dll">
<Pack>true</Pack>
<PackagePath>lib\$(TargetFramework)</PackagePath>
</Content>
</ItemGroup>
If you target multiple frameworks:
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard1.6</TargetFrameworks>
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);IncludeReferencedProjectInPackage</TargetsForTfmSpecificContentInPackage>
</PropertyGroup>
<Target Name="IncludeReferencedProjectInPackage" Condition="'$(IncludeBuildOutput)' != 'false'">
<ItemGroup>
<TfmSpecificPackageFile Include="$(OutputPath)\ReferencedLib.dll" PackagePath="lib/$(TargetFramework)" />
</ItemGroup>
</Target>
How to include a local DLL reference in to a nuget package when calling msbuild pack?
According the issue on the GitHub, this is currently not directly supported by NuGet.
The workaround I suggest is using the .nuspec file:
NuGet allows you to disable the automatic generation of the resulting
.nuspec file and automatic collection of files by setting the
property in your project, along with a
property that allows you to pass replacement tokens for parsing the
.nuspec file.
See Martin`s answer for details.

How to include an item in BuiltProjectOutputGroup

I have a custom project system, that uses the standard net sdk targets.
During the build, I produce an extra zip file. I'd like this extra file to be included in an output group, so that when I query my projects output groups (from vs) it shows up.
My project file looks like this:
<Project Sdk="Microsoft.NET.Sdk">
... stuff
<ItemGroup>
<PackageReference Include="DnnVsProjectSystem.BuildTools" Version="0.0.5">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
<Import Project="$(CustomProjectExtensionsPath)DnnVsProjectSystem.targets"/>
</Project>
Notice, I am using the "sdk" attribute, which is a fairly new feature of msbuild.
The PackageReference that you see, is a nuget package that imports a .props and a .targets which augment the build with some custom build tasks. These are the ones that produce the zip file.
I have drilled into the net sdk targets and found this:
<Target Name="AllProjectOutputGroups" DependsOnTargets="
BuiltProjectOutputGroup;
DebugSymbolsProjectOutputGroup;
DocumentationProjectOutputGroup;
SatelliteDllsProjectOutputGroup;
SourceFilesProjectOutputGroup;
ContentFilesProjectOutputGroup;
SGenFilesOutputGroup" />
<!--
This is the key output for the BuiltProjectOutputGroup and is meant to be read directly from the IDE.
Reading an item is faster than invoking a target.
-->
<ItemGroup Condition=" '$(OutputType)' != 'winmdobj' ">
<BuiltProjectOutputGroupKeyOutput Include="#(IntermediateAssembly->'%(FullPath)')">
<IsKeyOutput>true</IsKeyOutput>
<FinalOutputPath>$(TargetPath)</FinalOutputPath>
<TargetPath>$(TargetFileName)</TargetPath>
<COM2REG Condition="'$(RegisterForComInterop)'=='true' and '$(OutputType)'=='library'">true</COM2REG>
</BuiltProjectOutputGroupKeyOutput>
</ItemGroup>
<ItemGroup Condition=" '$(OutputType)' == 'winmdobj' ">
<WinMDExpOutputWindowsMetadataFileItem Include="$(_IntermediateWindowsMetadataPath)" Condition="'$(_IntermediateWindowsMetadataPath)' != ''" />
<BuiltProjectOutputGroupKeyOutput Include="#(WinMDExpOutputWindowsMetadataFileItem->'%(FullPath)')">
<IsKeyOutput>true</IsKeyOutput>
<FinalOutputPath>$(TargetPath)</FinalOutputPath>
<TargetPath>$(TargetFileName)</TargetPath>
</BuiltProjectOutputGroupKeyOutput>
</ItemGroup>
This appears to be the target that is called by VS, when it wants information about output groups.
The problem is, i am not sure how I can get my item included in one of those output groups, as If i just add the item to the item group, in my own targets - my targets are irrelevent at this point, as they are not included in this dependency chain.
I also can't override any of the targets, because, as i'm using the sdk attribute, it looks like the sdk targets will always be imported last, overwriting anything that I declare.
Any guidance much appreciated.
If your only concern is to hook into the target or its dependency chain, I suggest using msbuild's BeforeTargets functionality:
<Target Name="IncludeMyCustomOutputGroup" BeforeTargets="AllProjectOutputGroups" DependsOnTargets="ResolveMyCustomPropertiesAndItems">
<ItemGroup>
<!-- Assuming #(MyCustomOutput) items are generated by your ResolveMyCustomPropertiesAndItems target, or just add anything else -->
<BuiltProjectOutputGroupKeyOutput Include="#(MyCustomOutput->'%(FullPath)')">
<IsKeyOutput>true</IsKeyOutput>
<FinalOutputPath>$(TargetPath)</FinalOutputPath>
<TargetPath>$(TargetFileName)</TargetPath>
</BuiltProjectOutputGroupKeyOutput>
</ItemGroup>
</Target>

Apply an MSBuild effect across multiple projects in a solution

I've got the AssemblyInfo feature of the MSBuild Extension Pack working, so that my assemblies description and file version have the details I want in the code below ...
But I want to apply this effect across every project in a 50+ project solution!
So how can I work on all the projects ... without going through each project adding the code?
<PropertyGroup>
<CoreCompileDependsOn>
$(CoreCompileDependsOn);
AssemblyDefaults;
</CoreCompileDependsOn>
</PropertyGroup>
<PropertyGroup>
<ExtensionTasksPath>
$(MSBuildProjectDirectory)\..\packages\MSBuild.Extension.Pack.1.4.0\tools\net40\
</ExtensionTasksPath>
</PropertyGroup>
<Import Project="$(ExtensionTasksPath)\MSBuild.ExtensionPack.tasks" />
<Target Name="AssemblyDefaults">
<ItemGroup>
<AssemblyInfoFiles Include=".\Properties\AssemblyInfo.cs" />
</ItemGroup>
<AssemblyInfo
AssemblyInfoFiles="#(AssemblyInfoFiles)"
AssemblyProduct="Compiled on: $([System.Environment]::MachineName)"
AssemblyDescription="Compiled at: $([System.DateTime]::Now)"
AssemblyFileBuildNumberType="DateString"
AssemblyFileBuildNumberFormat="MMdd"
AssemblyFileRevisionType="DateString"
AssemblyFileRevisionFormat="HHmm" />
</Target>
You could create a common assembly file for all the projects and edit the csproj files to refer to the common assembly info file like:
<Compile Include="..\CommonAssemblyInfo.cs">
<Link>Properties\CommonAssemblyInfo.cs</Link>
</Compile>
More information can be found at Shared AssemblyInfo for uniform versioning across the solution