Search code examples
c#msbuildnugetsourcegeneratorscsharp-source-generator

Source generator configurable settings file installed with nuget package


I've created a source generator that reads in some information from a config file, allowing the consumer to change settings that alter the output of the generator. That file is added to the consuming project by adding the following to the .csproj file:

<ItemGroup>
      <AdditionalFiles Include="generator.config" />
</ItemGroup>

I'd like for coworkers to be able to install my nuget package and have the generator.config file as well as the above ItemGroup automatically added to their projects.

I thought this would be rather simple, but I've spent days trying to achieve this with no luck so far and I'm ready to pull my hair out. I've tried:

  • Including generator.config in the content and contentFiles directories of the package. This did get the file into the consuming project, but it wasn't editable, which made it useless a config file.
  • Various permutations of .props and .targets files in my nuget package. I managed to get generator.config copied to the output directory after building, but not to the project directory
  • I found some posts talking about about running a powershell script to copy files on install but that seemed like that's a deprecated way to go about this

I think I may be experiencing some additional difficulty due to my consuming project targeting .NET 6, while the source generator has to target netstandard2.0. I only have a passing familiarity with Nuget/MSBuild from looking at .csproj files so I suspect I may be missing something fairly obvious, but any help/suggestions would be appreciated.


Solution

  • I managed to get this working. For anyone who may be having a similar issue:

    I mostly worked off this answer from Mr Qian. I first tried implementing almost exactly as in that post:

    I placed my generator.config file in the project root added the following to the generator project file:

    <ItemGroup>
        <None Include="generator.config" Pack="true" PackagePath="lib"></None>
        <None Include="build\myGenerator.props" Pack="true" PackagePath="build"></None>
    </ItemGroup>
    

    Then I created a "build" directory off my project root, and created myGenerator.props like this as suggested in the linked answer:

    <Project>
        <ItemGroup>
            <AdditionalFiles Include="generator.config" />
        </ItemGroup>
        <Target Name="CopyGeneratorConfig" BeforeTargets="Build" Condition="!Exists('$(MSBuildProjectDirectory)\generator.config')">
            <ItemGroup>
                <MyEnvConfig Include="$(MSBuildThisFileDirectory)..\lib\generator.config"/>
            </ItemGroup>
            <Copy
               SourceFiles="@(MyEnvConfig)"
               DestinationFolder="$(MSBuildProjectDirectory)"
             />
        </Target>
    </Project>
    

    But this didn't work for me at all. Nothing seemed to be happening.

    I made some progress by separating out the <Target> into its own myGenerator.targets file. After doing so, I could see my <AdditionalFiles> under Imports in the consuming project immediately after installing the nuget package. But the file copy still didn't work. It didn't seem that my Copy target was running at all.

    After much hand wringing, teeth gnashing, hair pulling, and furious googling, I found a solution: change the value of BeforeTargets on the copy target from "Build" to "BeforeBuild". Once I made that change, it worked exactly as I wanted. (Well, almost exactly... it still requires a build, and I ideally wanted the copy to happen upon installation of the nuget package.)

    So my working set-up uses the change to the project file mentioned above, the following .props file (note the name before .props and .targets must match your project name):

    <Project>
        <ItemGroup>
            <AdditionalFiles Include="generator.config" />
        </ItemGroup>
    </Project>
    

    And the following .targets file:

    <Project>
        <Target Name="CopyGeneratorConfig" BeforeTargets="BeforeBuild" Condition="!Exists('$(MSBuildProjectDirectory)\generator.config')">
            <ItemGroup>
                <MyEnvConfig Include="$(MSBuildThisFileDirectory)..\lib\generator.config"/>
            </ItemGroup>
            <Copy
               SourceFiles="@(MyEnvConfig)"
               DestinationFolder="$(MSBuildProjectDirectory)"
             />
        </Target>
    </Project>