Skip to content

MarkupCompilePass2 shouldn't zero GeneratedInternalTypeHelper.g.cs #2281

@KirillOsenkov

Description

@KirillOsenkov

This is for .NET Framework but it looks like it might be a problem for .NET Core as well.

Sometimes (and under circumstances unclear to me) MarkupCompilePass2 decides that the internal type helper is not necessary and it zeroes out the file:

//
// If no any xaml file with local-types wants to reference an internal type from
// current assembly and friend assembly, and InternalTypeHelperFile is set in the
// cache file, now it is the time to remove the content of InternalTypeHelper File.
//
// We still keep the empty file to make other parts of the build system happy.
//
if (!String.IsNullOrEmpty(_internalTypeHelperFile) && !compilerWrapper.HasInternals)
{
if (TaskFileService.Exists(_internalTypeHelperFile))
{
// Make empty content for this file.
MemoryStream memStream = new MemoryStream();
using (StreamWriter writer = new StreamWriter(memStream, new UTF8Encoding(false)))
{
writer.WriteLine(String.Empty);
writer.Flush();
TaskFileService.WriteFile(memStream.ToArray(), _internalTypeHelperFile);
}
Log.LogMessageFromResources(MessageImportance.Low, SRID.InternalTypeHelperNotRequired, _internalTypeHelperFile);
}
}

From what I can see this is a race condition with MarkupCompilePass1 and/or design-time builds which write into the file.

The problem is that the presence or absence of the GeneratedInternalTypeHelper class in the assembly changes its public API. In scenarios where reference assemblies are used for better incremental builds (by setting ProduceReferenceAssembly property to true) this public API change results in a different reference assembly, thus invalidating the entire dependency tree and causing all dependent projects to rebuild unnecessarily.

I'm not entirely certain whether this generated type is really required and when it is actually used, and why is it generated in the first place (nothing in the file looks like it couldn't be in a framework type). But if we are generating it, we should always be generating it deterministically, such that the public API surface doesn't flip flop between having this type and not having it.

See related:
dotnet/msbuild#4217

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugProduct bug (most likely)

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions