dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
18.96k stars 4.02k forks source link

Build failure when referencing a source generator project #47275

Closed devsko closed 4 years ago

devsko commented 4 years ago

Version Used: 5.0.100-preview.8.20417.9

Steps to Reproduce:

  1. Add a new project to the source generator sample
  2. Add <ProjectReference Include="..\SourceGeneratorSamples\SourceGeneratorSamples.csproj" />

Expected Behavior: Build of the added project succeeds and the source generator is available for testing.

Actual Behavior:

..\Microsoft.NET.Sdk.targets(195,5): error MSB4018: Unerwarteter Fehler bei der GenerateDepsFile-Aufgabe. [C:\Users\stefa\source\repos\roslyn-sdk\samples\CSharp\SourceGenerators\TestProject1\TestProject1.csproj]
..\Microsoft.NET.Sdk.targets(195,5): error MSB4018: System.ArgumentException: An item with the same key has already been added. Key: C:\Users\stefa\source\repos\roslyn-sdk\samples\CSharp\SourceGenerators\SourceGeneratorSamples\SourceGeneratorSamples.csproj [C:\Users\stefa\source\repos\roslyn-sdk\samples\CSharp\SourceGenerators\TestProject1\TestProject1.csproj]
..\Microsoft.NET.Sdk.targets(195,5): error MSB4018:    at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior) [C:\Users\stefa\source\repos\roslyn-sdk\samples\CSharp\SourceGenerators\TestProject1\TestProject1.csproj]
..\Microsoft.NET.Sdk.targets(195,5): error MSB4018:    at Microsoft.NET.Build.Tasks.SingleProjectInfo.CreateProjectReferenceInfos(IEnumerable`1 referencePaths, IEnumerable`1 referenceSatellitePaths, Func`2 isRuntimeAssembly) [C:\Users\stefa\source\repos\roslyn-sdk\samples\CSharp\SourceGenerators\TestProject1\TestProject1.csproj]
..\Microsoft.NET.Sdk.targets(195,5): error MSB4018:    at Microsoft.NET.Build.Tasks.GenerateDepsFile.WriteDepsFile(String depsFilePath) [C:\Users\stefa\source\repos\roslyn-sdk\samples\CSharp\SourceGenerators\TestProject1\TestProject1.csproj]
..\Microsoft.NET.Sdk.targets(195,5): error MSB4018:    at Microsoft.NET.Build.Tasks.GenerateDepsFile.ExecuteCore() [C:\Users\stefa\source\repos\roslyn-sdk\samples\CSharp\SourceGenerators\TestProject1\TestProject1.csproj]
..\Microsoft.NET.Sdk.targets(195,5): error MSB4018:    at Microsoft.NET.Build.Tasks.TaskBase.Execute() [C:\Users\stefa\source\repos\roslyn-sdk\samples\CSharp\SourceGenerators\TestProject1\TestProject1.csproj]
..\Microsoft.NET.Sdk.targets(195,5): error MSB4018:    at Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute() [C:\Users\stefa\source\repos\roslyn-sdk\samples\CSharp\SourceGenerators\TestProject1\TestProject1.csproj]
..\Microsoft.NET.Sdk.targets(195,5): error MSB4018:    at Microsoft.Build.BackEnd.TaskBuilder.ExecuteInstantiatedTask(ITaskExecutionHost taskExecutionHost, TaskLoggingContext taskLoggingContext, TaskHost taskHost, ItemBucket bucket, TaskExecutionMode howToExecuteTask) [C:\Users\stefa\source\repos\roslyn-sdk\samples\CSharp\SourceGenerators\TestProject1\TestProject1.csproj]

After removing the part below from SourceGeneratorSamples.csproj building the new project succeeds but of course now building GeneratedDemo fails because the referenced assemblies are not found by the generator.

  <PropertyGroup>
    <GetTargetPathDependsOn>$(GetTargetPathDependsOn);GetDependencyTargetPaths</GetTargetPathDependsOn>
  </PropertyGroup>

  <Target Name="GetDependencyTargetPaths">
    <ItemGroup>
      <TargetPathWithTargetPlatformMoniker Include="$(PKGCsvTextFieldParser)\lib\netstandard2.0\CsvTextFieldParser.dll" />
      <TargetPathWithTargetPlatformMoniker Include="$(PKGHandlebars_Net)\lib\netstandard2.0\Handlebars.dll" />
      <TargetPathWithTargetPlatformMoniker Include="$(PKGNewtonsoft_Json)\lib\netstandard2.0\Newtonsoft.Json.dll" />
    </ItemGroup>
  </Target>

/cc: @sharwell

sharwell commented 4 years ago

The most likely cause of this error is an incorrectly formed <ProjectReference> element. A reference to a source generator in the same solution needs to have this form:

<ProjectReference
    Include="[relative path]"
    SetTargetFramework="TargetFramework=netstandard2.0"
    OutputItemType="Analyzer"
    ReferenceOutputAssembly="false" />

The SetTargetFramework attribute can be omitted if the project using the source generator is netstandard2.0 or newer, but it's often easiest to just include it.

devsko commented 4 years ago

Thanks for looking at this. The kind of reference you mentioned works fine for actually generating code, but I'm trying to reference the project (not just the source generator) from a unit test. I thought a "normal" ProjectReerence should work. (Absolutely not blocking for me)

sharwell commented 4 years ago

Is the problem fixed if you add the attribute PrivateAssets="all" to the <ProjectReference> form I showed above?

devsko commented 4 years ago

The build succeeds but the assembly is not referenced thus I cannot test the classes in the generator project.

devsko commented 4 years ago

PrivateAssests="all" has no effect on this behavior.

sharwell commented 4 years ago

The build succeeds but the assembly is not referenced thus I cannot test the classes in the generator project.

Ah, we don't currently have a mechanism for unit testing the source generator code itself. I filed https://github.com/dotnet/roslyn-sdk/issues/602 for this.

devsko commented 4 years ago

I understand that referencing a project with source generator in any other way than just for generating code is currently not possible - at least when it self references other packages. Right? I can live with that but think it should be mentioned somewhere ;)

devsko commented 4 years ago

@sharwell Adding IncludeRuntimeDependency="false" fixes the issue. The GetDependencyTargetPaths target in SourceGeneratorSamples.csproj should look like this. Everything works as expected and the generator project can be referenced by other projects like any other project.

  <Target Name="GetDependencyTargetPaths">
    <ItemGroup>
      <TargetPathWithTargetPlatformMoniker Include="$(PKGCsvTextFieldParser)\lib\netstandard2.0\CsvTextFieldParser.dll" 
                                           IncludeRuntimeDependency="false" />
      <TargetPathWithTargetPlatformMoniker Include="$(PKGHandlebars_Net)\lib\netstandard2.0\Handlebars.dll" 
                                           IncludeRuntimeDependency="false" />
      <TargetPathWithTargetPlatformMoniker Include="$(PKGNewtonsoft_Json)\lib\netstandard2.0\Newtonsoft.Json.dll" 
                                           IncludeRuntimeDependency="false" />
    </ItemGroup>
  </Target>