Skip to content

GeneratedInternalTypeHelper.g.cs Causes Rebuild Of Solution When Opening Visual Studio But Not in MSBuild #4217

Open
@aolszowka

Description

@aolszowka

Hi,

I am currently investigating why we get full builds on some of our larger solution files (500+ CSPROJ).

We are currently using Visual Studio 2017 15.9.6

Goals

Here is what we are trying to do:

  1. We want to be able to invoke MSBuild from the Command Line to build our solutions (this way we can script this to happen in the morning before a developer comes in).
  2. With NO changes we expect to be able to open the Solution File in Visual Studio and hit Build and have nothing build (since everything should be up to date).

In this way our Developers can be productive right when they get in instead of waiting on a build.

Unfortunately as it stands today Visual Studio feels like it needs to rebuild a significant amount of the code base.

Background

We have already encountered and corrected the following issues:

  1. The State Files attempt to write to paths that were too long. We would constantly get MSB3101: Could not write state file errors. We had these as relative paths but unbeknown to us apparently MSBuild does not "expand" this path prior to passing it off; this actually resulted in paths that were even LONGER than we expected. We worked around that by setting our IntermediateOutputPath to be explicitly expanded by MSBuild prior to passing it off. Like so:
<IntermediateOutputPath>$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)\..\..\..\..\..\..\bin\obj\$(ProjectGuid)'))</IntermediateOutputPath>

This seems to have resolved that issue and we no longer encounter the warnings and incremental builds seem to work. I have not seen any issues reported for this; but I did find this StackOverflow post which led me in the right direction: https://stackoverflow.com/questions/139964/msbuild-directory-structure-limit-workarounds/9635709#9635709 Its unclear if this is a known issue, an issue that will go away once we get Long Path Support, or an issue that should be reported as a new bug.

  1. We then encountered Issue GenerateCompiledExpressionsTempFile does not clean up the files it creates #1648 wherein when Visual Studio would launch it would then create the 3 Temporary generated files in the \bin\obj\ folder even when we were not utilizing Workflow; this of course triggered a rebuild. We have worked around that by inserting into every single project a no-op like so:
  <Target Name="GenerateCompiledExpressionsTempFile">
    <!--This is a no-op to overwrite the existing target that ships with MSBuild. When we upgrade to Visual Studio 2019 (MSBuild 16.0) we can remove this. See https://github.com/Microsoft/msbuild/issues/1648-->
  </Target>
  1. We identified several locations in our code base where we had <CopyToOutputDirectory>Always</CopyToOutputDirectory> this seemed to trigger builds; we have converted all of these to <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> which again seems to have reduced the copying. This seems to have been documented in several places on the internet; one such place is https://blogs.msdn.microsoft.com/kirillosenkov/2014/08/04/how-to-investigate-rebuilding-in-visual-studio-when-nothing-has-changed/.

Current Roadblock (This Report)

With these changes we have been able to drive down our solution rebuilds from 900 projects down to 140 Projects; all of which are Xaml Projects or have an N-Order Dependency on a shared library which contains Xaml code.

Using the diagnostic verbosity level we can see the following from within Visual Studio:

Project 'CU.TestClient.Core' is not up to date. Input file 's:\timssvn\6x\trunk\bin\obj\{5b44a2e5-81f9-4d9d-9857-006b76d62f8c}\generatedinternaltypehelper.g.cs' is modified after output file ''.

When we build via the Command Line in MSBuild in MSBuild we see that the file is indeed created; however the file is empty. When we run this build in diagnostic verbosity we see the following:

InternalTypeHelper class is not required for this project, make file 'S:\TimsSVN\6x\Trunk\bin\obj\{5B44A2E5-81F9-4D9D-9857-006B76D62F8C}\GeneratedInternalTypeHelper.g.cs' empty.

This traces back to this section of logic in MarkupCompilePass2.cs: https://referencesource.microsoft.com/#PresentationBuildTasks/BuildTasks/Microsoft/Build/Tasks/Windows/MarkupCompilePass2.cs,684

//
// 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);
    }
}

Therefore the file is indeed empty. HOWEVER when the solution is loaded up in Visual Studio (on launch; not even hitting Build or in any way interacting with the IDE beyond opening) we see:

  • generatedinternaltypehelper.g.cs get deleted
  • generatedinternaltypehelper.g.i.cs get created with content
  • generatedinternaltypehelper.g.cs get recreated with the same content as the i.cs

The Code within these files is:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace XamlGeneratedNamespace {
    
    
    /// <summary>
    /// GeneratedInternalTypeHelper
    /// </summary>
    [System.Diagnostics.DebuggerNonUserCodeAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "4.0.0.0")]
    [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)]
    public sealed class GeneratedInternalTypeHelper : System.Windows.Markup.InternalTypeHelper {
        
        /// <summary>
        /// CreateInstance
        /// </summary>
        protected override object CreateInstance(System.Type type, System.Globalization.CultureInfo culture) {
            return System.Activator.CreateInstance(type, ((System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic) 
                            | (System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.CreateInstance)), null, null, culture);
        }
        
        /// <summary>
        /// GetPropertyValue
        /// </summary>
        protected override object GetPropertyValue(System.Reflection.PropertyInfo propertyInfo, object target, System.Globalization.CultureInfo culture) {
            return propertyInfo.GetValue(target, System.Reflection.BindingFlags.Default, null, null, culture);
        }
        
        /// <summary>
        /// SetPropertyValue
        /// </summary>
        protected override void SetPropertyValue(System.Reflection.PropertyInfo propertyInfo, object target, object value, System.Globalization.CultureInfo culture) {
            propertyInfo.SetValue(target, value, System.Reflection.BindingFlags.Default, null, null, culture);
        }
        
        /// <summary>
        /// CreateDelegate
        /// </summary>
        protected override System.Delegate CreateDelegate(System.Type delegateType, object target, string handler) {
            return ((System.Delegate)(target.GetType().InvokeMember("_CreateDelegate", (System.Reflection.BindingFlags.InvokeMethod 
                            | (System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)), null, target, new object[] {
                        delegateType,
                        handler}, null)));
        }
        
        /// <summary>
        /// AddEventHandler
        /// </summary>
        protected override void AddEventHandler(System.Reflection.EventInfo eventInfo, object target, System.Delegate handler) {
            eventInfo.AddEventHandler(target, handler);
        }
    }
}

Reading the source to MarkupCompilePass2 it appears that the g.i.cs file is generated for use by Intellisense and is not put into play with the build (as is expected) but for some reason Visual Studio felt that it needed to regenerate the g.cs file.

This is where I am stuck; I am not smart enough to understand the scenarios in which the MarkupCompilePass2 will require itself to build the g.cs file. I have tried to narrow down if it is a project or solution file causing the issue (to try and get a smaller repo) but I have been unsuccessful.

When I have attempted to reduce it down to just this project I am unable to get the same behavior; with smaller solutions I will often get this behavior:

  • generatedinternaltypehelper.g.cs is left alone (still blank)
  • generatedinternaltypehelper.g.i.cs is created with the content listed above

In this cases the incremental build obviously works because g.cs was not touched.

Can anyone provide pointers as to where I can go to continue to debug this? I am currently stuck and unsure how to debug further.

Questions

  • What in the Visual Studio Startup Process is muxing the generatedinternaltypehelper files?
  • How can I debug that above process?

I am dedicated to identifying this issue and am pretty resourceful if pointed in the right direction.

Thank you

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area: TasksIssues impacting the tasks shipped in Microsoft.Build.Tasks.Core.dll.Performance-Scenario-BuildThis issue affects build performance.triaged

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions