简体   繁体   中英

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

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:

  1. 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).

  1. <PropertyGroup> elemts are processed before <ItemGroup> elements.

MSBuild evaluates static content (ie 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).

  1. 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, eg:

<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), eg:

<PropertyGroup>
  <TargetFrameworks>netcoreapp2.1;net462</TargetFrameworks>
</PropertyGroup>

When you build this project with eg 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:

  1. 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.

  2. 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:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM