Wpf: The generated content of GeneratedInternalTypeHelper.g.cs is not stable

Created on 2 Mar 2020  路  5Comments  路  Source: dotnet/wpf

Problem description:

Since switching WPF projects in Roslyn repo to Microsoft.NET.Sdk.WindowsDesktop the design time build is failing intermittently.

image

This is because we use Public API analyzer that checks public API list against a baseline. The public types defined in GeneratedInternalTypeHelper.g.cs are not listed in the baseline, but appear sometimes in the build.

I observed that during command line build GeneratedInternalTypeHelper.g.cs is created with a declaration of class GeneratedInternalTypeHelper and a few moments later it is replaced with an empty file. It seems likely that this sometimes does not happen during design time build.

Actual behavior:

Binary log for the design time build here:
\\mlangfs1\public\jorobich\GenerateInternalTypeHelper

Expected behavior:

The public types are either always generated or never generated.
In addition, I'd expect source file content to not be rewritten during build. An alternative approach to do this is to always generate the code but enclose it in #if. Then you can define a conditional compilation symbol and pass it to the compiler depending on whether the type should be active or not.

Minimal repro:

This is an intermittent issue happening during work in Roslyn.sln.

issue-type-bug

Most helpful comment

Summary of my current understanding:

Markup Compilation Pass 1

Since an intellisense build does not clean generated files, you can have a situation where the initial intellisense build generates some types, but a subsequent incremental build does not since MCPass2 emptied out the file contents.

All 5 comments

Summary of my current understanding:

Markup Compilation Pass 1

Since an intellisense build does not clean generated files, you can have a situation where the initial intellisense build generates some types, but a subsequent incremental build does not since MCPass2 emptied out the file contents.

This could be related to the temp project building the same name binary as the real project. We generate the temporary project and compile it in-between MCPass1 and MCPass2. The output assembly is named the same as the normal project's assembly. This temporary assembly will always have a version of GeneratedInternalTypeHelper with actual types inside of it as the decision to empty that file is not made until MCPass2 is over. Subsequently, the actual build of the real project can potentially be done with an emptied version of the file.

We might be able to fix this in the following fashion:

  • Change the output path of the temp project
  • Ensure PBT ingests this new assembly path as needed
  • Ensure that clean targets properly pick up all of the separately created files

If the temporary project was completely separate from the real one (in output as well) we would always generate one assembly with a filled GeneratedInternalTypeHelper and then the real assembly would be consistently generated with or without GeneratedInternalTypeHelper based on MCPass2's result.

The build as is, should be deterministic, but depends on specific knowledge gained during the markup compilation passes. This is fairly opaque to external users. Changing this will give two distinct assemblies that will generally have unvarying inputs regardless of how the build is done.

As is, Roslyn has a workaround for their analyzers, and further issues are theoretical. We can look at this more carefully in the .NET 5 timeframe, putting this on the backlog for now.

@rladuca Thanks for the analysis.

This is a pretty annoying bug; I went to attempt to workaround it by adding it the to the "public API" but then you get the reverse error when its not generated. I've see this popup in 4 separate code bases:

1) Roslyn (http://github.com/dotnet/roslyn)
2) Project System (http://github.com/dotnet/project-system)
3) Editor (internal code base)
4) Common Project System (internal code base).

Was this page helpful?
0 / 5 - 0 ratings