Nuget/Dotnet package manager - how can I retrieve $(<SomeVariable>) - c#

I have tried to read in Microsoft documentations as well in Nuget and Dotnet and was unable to understand the corresponding behavior.
When working on a C# project along with .csproj I have encountered the following,
Here is an example of what I want to achieve:
I am working for instance with the following .csproj file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<ProjectTypeGuids>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<Authors>Confluent Inc.;Andreas Heider</Authors>
<Description>Confluent's .NET Client for Apache Kafka</Description>
<Copyright>Copyright 2016-2020 Confluent Inc., Andreas Heider</Copyright>
<PackageProjectUrl>https://github.com/confluentinc/confluent-kafka-dotnet/</PackageProjectUrl>
<PackageLicenseUrl>https://github.com/confluentinc/confluent-kafka-dotnet/blob/master/LICENSE</PackageLicenseUrl>
<PackageIconUrl>https://raw.githubusercontent.com/confluentinc/confluent-kafka-dotnet/master/confluent_logo.png</PackageIconUrl>
<PackageReleaseNotes>https://github.com/confluentinc/confluent-kafka-dotnet/releases</PackageReleaseNotes>
<PackageTags>Kafka;Confluent;librdkafka</PackageTags>
<PackageId>Confluent.Kafka</PackageId>
<Title>Confluent.Kafka</Title>
<AssemblyName>Confluent.Kafka</AssemblyName>
<VersionPrefix>1.4.3</VersionPrefix>
<TargetFrameworks>net45;net46;netcoreapp2.1;netstandard1.3;netstandard2.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>Confluent.Kafka.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="librdkafka.redist" Version="1.4.2">
<PrivateAssets Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">None</PrivateAssets>
</PackageReference>
<PackageReference Include="System.Memory" Version="4.5.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="PeterO.Cbor" Version="3.5.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
<PackageReference Include="System.Console" Version="4.3.0" />
<PackageReference Include="System.Linq" Version="4.3.0" />
<PackageReference Include="System.Runtime.InteropServices" Version="4.3.0" />
<PackageReference Include="System.Runtime.Extensions" Version="4.3.0" />
<PackageReference Include="System.Threading" Version="4.3.0" />
</ItemGroup>
</Project>
As you can see in the last ItemGroup there is a Condition=" '$(TargetFramework)' == 'netstandard1.3',
what I am trying to figure out is from where dotnet takes this argument $(TargetFramework) from.
By the way, it seems to be variable notation $(SomeVariable) (not only TargetFramework) and it would be great for me to understand from where and how these variables are being pulled from.
Any leads?
Thanks!

As there is already a very detailed answer on some mechanics, here are a few helpful pieces of information to know that can help detail the understanding of the behavior:
the "SDK=..." part imports things at the top and bottom of the project.
It is actually syntactical sugar for <Import> statements at the top and bottom ends of your project content. This means that logic by the SDK (and its deeper imports) can both set defaults that you can use in your project (automatic Sdk.props import at the top) and process values you set in the project (automatic Sdk.targets import at the very bottom).
<PropertyGroup> elemts are processed before <ItemGroup> elements.
MSBuild evaluates static content (i.e. not inside a <Target>) in a very specific order: Property Groups and Imports then Item Definition Groups and then Item Groups.
This means you can use $(FooBar) in an <ItemGroup> at the very top even if the <PropertyGroup> defining <FooBar> i at the very bottom.
The SDK itself uses this behavior - your $(TargetFramework) (singular!, if set. more on that later) is used in some property groups in the imported SDK targets to set TargetFrameworkIdentifier, TargetFrameworkVersion and TargetFrameworkMoniker - here's the code.
When the processing of <PropertyGroup>s is complete, this means that on the pass for <ItemGroup> elements, you can use $(TargetFrameworkIdentifier).
A similar mechansim is used for some item groups that the sdk has before your project content that can be controlled by properties set in the project (DefaultItemExcludes, EnableDefaultCompileItems etc).
TargetFrameworks (plural) projects actually build multiple times.
If a project has no TargetFramework but a TargetFrameworks (plural) property, it causes multiple sub-builds for which the TargetFramework property will be set. This means that everything is evaluated once for an "outer" build and then once per target framework for "inner builds". Here's the code

$(SomeVariable) is the syntax for resolving an MSBuild property. These are key-value pairs, which can be defined by environment variables, in arguments to MSBuild, by a <PropertyGroup> elements within an MSBuild file, and by MSBuild targets using the CreateProperty task.
TargetFramework is somewhat of a special property. If the project only builds for one target framework, then the property is set explicitly in the project file, e.g.:
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
In your case, your project can be built for multiple frameworks, as specified in the TargetFrameworks property (notice the plural), e.g.:
<PropertyGroup>
<TargetFrameworks>netcoreapp2.1;net462</TargetFrameworks>
</PropertyGroup>
When you build this project with e.g. dotnet, it actually builds the project multiple times, once for each target framework, by essentially setting the TargetFramework property via argument when calling MSBuild.
If you want to see behind the magic a bit, here are two ways to get very verbose information on what MSBuild is doing:
Use the -pp argument to have MSBuild resolve all the magic .NET Core-ish stuff, as well as all imported project files, into a single project file for you to inspect. This won't actually run the build.
Use the -bl argument to run the build, but log everything MSBuild does in a binary format. You can then view the .binlog file using this GUI.
Additional resources for MSBuild:
MSBuild documentation (written primarily from the perspective of the classic Windows-only .NET Framework)
Additions to the project file format made for "SKU-style" project files (used in .NET Core, .NET Standard, and .NET 5+).

Related

Failing SonarQube analysis on .NET Core 3.1 project

I am running sonarqube:latest in a Docker container locally on my development machine to get some stuff working.
I’m trying to analyze a C# project that runs on .NET Core 3.1. Looking at this page I assumed I could use the .NET Core Global Tool to scan it but that gives me the following error while running the end step:
WARN: Your project contains C# files which cannot be analyzed with the scanner you are using. To analyze C# or VB.NET, you must use the SonarScanner for .NET 5.x or higher, see https://redirect.sonarsource.com/doc/install-configure-scanner-msbuild.html
This also happens when I try to use .NET 5 dotnet <path to SonarScanner.MSBuild.dll> etc. It does nicely scan all the TypeScript and CSS stuff that’s in my project but in the end there are no C# results in SonarQube.
Any idea what I’m doing wrong?
Update
After doing some debugging it comes down to this:
<Target Name="ChangeAliasesOfReactiveExtensions" BeforeTargets="FindReferenceAssembliesForReferences;ResolveReferences">
<ItemGroup>
<ReferencePath Condition="'%(FileName)' == 'System.Interactive.Async'">
<Aliases>ix</Aliases>
</ReferencePath>
</ItemGroup>
</Target>
We are doing this because we're still using an early Entity Framework version.
I assume we get the error because the analyzer stumbles upon duplicate types.
Is there any way to configure SonarQube so this is not an issue?
UPDATE 2
Apparently the file causing all the problems is Directory.Build.targets:
<Project>
<Target Name="ChangeAliasesOfReactiveExtensions" BeforeTargets="FindReferenceAssembliesForReferences;ResolveReferences">
<ItemGroup>
<ReferencePath Condition="'%(FileName)' == 'System.Interactive.Async'">
<Aliases>ix</Aliases>
</ReferencePath>
</ItemGroup>
</Target>
<PropertyGroup Condition=" '$(IntermediateOutputPath)' != '' ">
<ErrorLog>$(IntermediateOutputPath)\csc_diagnostics.json</ErrorLog>
<RazorCompilationErrorLog>$(IntermediateOutputPath)\csc_razor_diagnostics.json</RazorCompilationErrorLog>
</PropertyGroup>
</Project>
Commenting out the content of this file or removing it fixes the SonarQube analysis warning and shows the C# files in SonarQube.
I can reproduce this by creating a new C# project (dotnet new console) and creating a file Directory.Build.targets with the above content.
I can reproduce the issue with the Update 2. Changing Alias doesn't have any effect on the scenario, it's only the ErrorLog that makes the difference.
As seen in .sonarqube\bin\targets\SonarQube.Integration.targets: SonarScanner sets ErrorLog to collect results of the analysis when it's empty. And it doesn't pick it up when it's explicitly set. I'm not sure exactly why is that.
To fix your scenario, update your Build.Directory.targets file to set Sonar properties as well like this:
<PropertyGroup Condition=" '$(IntermediateOutputPath)' != '' ">
<ErrorLog>$(IntermediateOutputPath)\csc_diagnostics.json</ErrorLog>
<RazorCompilationErrorLog>$(IntermediateOutputPath)\csc_razor_diagnostics.json</RazorCompilationErrorLog>
<SonarCompileErrorLog>$(ErrorLog)</SonarCompileErrorLog>
<RazorSonarCompileErrorLog>$(RazorCompilationErrorLog)</RazorSonarCompileErrorLog>
</PropertyGroup>
or remove the explicit values for ErrorLog and RazorCompilationErrorLog. Json reports will be at these paths:
Roslyn: $(TargetDir)$(TargetFileName).RoslynCA.json
Razor: $(TargetDir)$(TargetName)$(RazorTargetNameSuffix)$(TargetExt).RoslynCA.json

How to use the latest C# version in Xamarin Forms projects

I have a Xamarin Forms application. I'd like to use the latest C# version in platform specific projects (i.e. Xamarin.iOS and Xamarin.Android). It looks like I can add <LangVersion>latest</LangVersion> to the .csproj files. However, I'm not sure where to add it exactly. I see a lot of PropertyGroup tags in the project files (usually one for each simulator and release type). Do I need to add it to every PropertyGroup? I need the latest language version to be available when debugging and in production.
Generally, if you use the latest version of Target framework, the version of C# will be the latest. Then you will not need to set the version of C# manually.
Right click the solution of Xamarin Forms, then click Properties, then can set the Target framework as follow.
You will refer to this official document to know its explains.
And also can set C# version manually in .csproj as follow:
<PropertyGroup>
<LangVersion>latest</LangVersion>
</PropertyGroup>
Here is the C# language version reference.
James Montamagno posted a tutorial on his Blog here: https://montemagno.com/use-csharp-8-everywehre/
You just have to change the <LangVersion /> value of your .csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Xamarin.Forms" Version="4.3.0.947036" />
<PackageReference Include="Xamarin.Essentials" Version="1.3.1" />
</ItemGroup>
</Project>
if you want to change the language of your platform specific code just add:
<PropertyGroup>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
to your ProjectName.iOS.csproj or ProjectName.Android.csproj.

WPF .Net Core 3.1 WPF Conversion Not going Well (Fun Quesiton!!)

I am converting a WPF App from Framework 4.7.2 to .NetCore 3.1
Entire project all source under discussion is here:
https://github.com/BicycleMark/LatestSweeper
I am using the side by side / same folder technique so that both Framework and new Core can use same project and subfolders.
Sweeper.csproj
SweperCore.csproj
exist in same folder
the Sweeper.csproj compiles and runs just fine as before.
The SweeperCore.csproj comes back with:
Severity Code Description Project File Line Suppression State
Error NETSDK1005 Assets file 'D:\Src\Github\Sweeper\Sweeper\obj\project.assets.json' doesn't have a target for
'.NETCoreApp,Version=v3.1'. Ensure that restore has run and that you have included 'netcoreapp3.1' in the TargetFrameworks for your project. SweeperCore C:\Program Files\dotnet\sdk\3.1.300\Sdks\Microsoft.NET.Sdk\targets\Microsoft.PackageDependencyResolution.targets 234
I have examined that it is not about the packages as csproj references no pkg files.
Here is the .NetCore SweperCore.csproj contents:
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWPF>true</UseWPF>
<OutputPath>bin_core\Debug\</OutputPath>
<IntermediateOutputPath>out_core\Debug\</IntermediateOutputPath>
</PropertyGroup>
</Project>
Here is the image from Framework app:
Problem:
The net472 and the netcoreapp3.1 projects are both using the ./obj/ folder for intermediate build files. Obviously this is a problem, if both projects are loaded in Visual Studio.
Solution
Replace the IntermediateOutputPath or BaseIntermediateOutputPath (both should work) for one project. This will make side by side usage in Visual Studio possible. I've cloned your repo and managed to build both apps sucessfully without unloading one of them.
Due to the fact that the BaseIntermediateOutputPath must be evaluated before any Microsoft.Common properties are being used, overriding it needs more work, than I've expected:
Modify the SweeperCore.csproj as followed:
Remove the Sdk Attribute in the root Project Tag:
<Project>
Add the following PropertyGroup and Import as the first childs of <Project>
<PropertyGroup>
<BaseIntermediateOutputPath>obj_core</BaseIntermediateOutputPath>
</PropertyGroup>
<Import Sdk="Microsoft.NET.Sdk.WindowsDesktop" Project="Sdk.props" />
Add this PropertyGroup to remove the net472 out/ folder from the core app:
<ItemGroup>
<Compile Remove="obj\**" />
<EmbeddedResource Remove="obj\**" />
<None Remove="obj\**" />
<Page Remove="obj\**" />
</ItemGroup>
Add this Import as the last child before </Project>.
<Import Sdk="Microsoft.NET.Sdk.WindowsDesktop" Project="Sdk.targets" />
Original Source: https://github.com/microsoft/msbuild/issues/1603
Here I have encoded the full answer in a tip and trick on CodeProject
https://www.codeproject.com/Tips/5272662/Simple-Side-By-Side-Migration-Project-Template-for

VS Code Project - can't find Antlr3.runtime file (.NET Core)

So we have a project I'll call PrimaryProj for discussion purposes. We also have another project with the code for NCalc, the .NET mathematical expression evaluation system.
PrimaryProj uses a Project Reference to NCalc, such as:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<Platforms>x86;AnyCPU</Platforms>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<!--blah blah blah -->
</PropertyGroup>
<ItemGroup>
<!-- <PackageReference Include="PrimaryProj.NCalc" Version="0.0.1" /> -->
<ProjectReference Include="../../primary-proj/src/NCalc.csproj" />
</ItemGroup>
</Project>
As you can see, we previous used a PackageReference to a NuGet package, but are now trying to use a ProjectReference to our NCalc code in another project.
Now, the NCalc's csproj uses package references still, thusly:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<Platforms>x86;AnyCPU</Platforms>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<!-- redacted product info here -->
<Description>
NCalc is a mathematical expressions evaluator in .NET. NCalc can parse any expression
and evaluate the result, including static or dynamic parameters and custom functions.
This library is derived from the original NCalc, released under the MIT License,
Copyright (c) 2011 Sebastien Ros.
</Description>
<Copyright>U.S. Army</Copyright>
<!-- blah blah blah --->
</PropertyGroup>
<!-- <ItemGroup>
<PackageReference Include="Antlr3.CodeGenerator" Version="3.5.2-beta2" />
<PackageReference Include="Antlr3.Runtime" Version="3.5.2-beta2" />
</ItemGroup> -->
<ItemGroup>
<PackageReference Include="Antlr3" Version="3.5.2-rc1" />
<PackageReference Include="Antlr3.Runtime" Version="3.5.2-rc1" />
</ItemGroup>
</Project>
It originally used / tried to use the 3.5.2-beta2, but neither this, nor the rc1 work. It did work when we had PrimaryProj's csproj file using PackageReferences instead of ProjectReferences.
Now we get an error:
Could not load file or assembly 'Antlr3.Runtime, Version=3.5.0.2, Culture=neutral, PublicKeyToken=eb42632606e9261f' or one of its dependencies. The system cannot find the file specified.
Do PackageReferences and ProjectReferences have compatibility problems? (e.g., if one project uses ProjectReferences, the projects it references has to also?)
This has been a massive head-scratcher. Also, the only place we've ever seen this version number 3.5.0.2 in the error above is in some ANTLRStringStream reference from inside of NCalc - we are able to use omnisharp-metadata, and this is where we see this version indicated.
This is all very confusing. Any help, especially if you happen to deal with these types of .NET headaches, would be appreciated.

Target .NET 4.x and .NET Standard 2.0

Last year I added .NET Standard 2.0 support to the Network library. I did achieve this by creating a second (.NET Standard) project, and basically copy + paste the sourcecode. With some adjustments it was ready to go.
But since I add features on demand, it is really bothersome to change the same thing in both projects. It would be great to just create one code-base and simply change the compile target.
Pre-Compile statements aren't an option, because the .NET 4.x version does additionally include some NuGet packages, which aren't available for .NET Standard.
The solution I can currently think of is, to create a shared library, including all the cross-project classes. Or is there a much smoother solution?
Solved the Problem with the suggested solution. The .csproj Looks like following
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net46;netstandard2.0</TargetFrameworks>
</PropertyGroup>
<PropertyGroup>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
</PropertyGroup>
<ItemGroup>
<Compile Remove="packages\**" />
<EmbeddedResource Remove="packages\**" />
<None Remove="packages\**" />
</ItemGroup>
<ItemGroup>
<!-- PackageReferences for all TargetFrameworks -->
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net46' ">
<!-- PackageReferences for net46 TargetFramework -->
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0' ">
<!-- PackageReferences for standard2.0 TargetFramework -->
</ItemGroup>
</Project>
The only issue currently: I can't use the NuGet Package-Manager. I have to add every entry manually into the correct ItemGroup.
EDIT: The manual edit is only required if the packages are not supported by both TargetFrameworks. Simply Change in the Settings -> NuGet-Paket-Manager -> Default Format -> PackageReference
You can add source files from an existing project to another project as a link.
project a
somefile.cs
project b
Right click on project b, add existing item...navigate to somefile.cs in project a, and then add
You can edit the file from either project...so be careful.

Categories

Resources