Search code examples
.net-coremsbuildcsprojpreprocessor-directive

Define a C# preprocessor symbol based on NuGet PackageReference


I'd like to make define a symbol NEWTONSOFT if (and only if) the Newtonsoft.Json NuGet package is added as a PackageReference to my .NET Core app. How can I do that?

EDIT: To clarify, I'd like add the symbol, if the reference is present. And if I delete the reference, the symbol should no longer be defined - but I should not manually add/remove the symbol definition. Something like this:

<Choose>
    <When Condition=" '$(PackageReference).Identity'=='Newtonsoft.Json' ">
        <PropertyGroup>
            <DefineConstants>HDN</DefineConstants>
        </PropertyGroup>
    </When>
</Choose>

Except this does not work.


Solution

  • A way to do this sort of automatically is including a target that contributes build logic into your csproj file like this:

    <Target Name="AddPackageSpecificConstants" BeforeTargets="BeforeCompile">
      <PropertyGroup>
        <DefineConstants Condition="@(Reference->AnyHaveMetadataValue('NuGetPackageId','Newtonsoft.Json'))">$(DefineConstants);NEWTONSOFT_JSON</DefineConstants>
        <DefineConstants Condition="@(Reference->AnyHaveMetadataValue('NuGetPackageId','YamlDotNet '))">$(DefineConstants);YAML_DOT_NET</DefineConstants>
      </PropertyGroup>
    </Target>
    

    By hooking into the build process this can detect if your code has any compile-time references (meaning the API surface of the pacakges is available in your C# code) to specific NuGet packages, even if they are only transitively referenced (e.g. you reference a library that references Newtonsoft.Json so your could can use it).

    By doing definitions like <X>$(X);</X> the additional constants are added to the property so this leavs intact anything the SDK gives you by default based on the target framework or your other project contents.