File: BackEnd\Components\BuildComponentFactoryCollection.cs
Web Access
Project: ..\..\..\src\Build\Microsoft.Build.csproj (Microsoft.Build)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using Microsoft.Build.BackEnd.Components.Caching;
using Microsoft.Build.BackEnd.SdkResolution;
using Microsoft.Build.Experimental.BuildCheck.Infrastructure;
using Microsoft.Build.FileAccesses;
using Microsoft.Build.Shared;
 
#nullable disable
 
namespace Microsoft.Build.BackEnd
{
    /// <summary>
    /// Helper class for maintaining the component collection
    /// </summary>
    internal class BuildComponentFactoryCollection
    {
        /// <summary>
        /// The build component factories.
        /// </summary>
        private readonly Dictionary<BuildComponentType, BuildComponentEntry> _componentEntriesByType;
 
        /// <summary>
        /// The host used to initialize components.
        /// </summary>
        private readonly IBuildComponentHost _host;
 
        /// <summary>
        /// Constructor.
        /// </summary>
        public BuildComponentFactoryCollection(IBuildComponentHost host)
        {
            _host = host;
            _componentEntriesByType = new Dictionary<BuildComponentType, BuildComponentEntry>();
        }
 
        /// <summary>
        /// The creation pattern to use for this component.
        /// </summary>
        public enum CreationPattern
        {
            /// <summary>
            /// The component should be created as a singleton.
            /// </summary>
            Singleton,
 
            /// <summary>
            /// A new instance of the component should be created with every request.
            /// </summary>
            CreateAlways
        }
 
        /// <summary>
        /// Registers the default factories.
        /// </summary>
        public void RegisterDefaultFactories()
        {
            _componentEntriesByType[BuildComponentType.Scheduler] = new BuildComponentEntry(BuildComponentType.Scheduler, Scheduler.CreateComponent, CreationPattern.Singleton);
            _componentEntriesByType[BuildComponentType.ConfigCache] = new BuildComponentEntry(BuildComponentType.ConfigCache, ConfigCache.CreateComponent, CreationPattern.Singleton);
            _componentEntriesByType[BuildComponentType.ResultsCache] = new BuildComponentEntry(BuildComponentType.ResultsCache, ResultsCache.CreateComponent, CreationPattern.Singleton);
            _componentEntriesByType[BuildComponentType.NodeManager] = new BuildComponentEntry(BuildComponentType.NodeManager, NodeManager.CreateComponent, CreationPattern.Singleton);
            _componentEntriesByType[BuildComponentType.TaskHostNodeManager] = new BuildComponentEntry(BuildComponentType.TaskHostNodeManager, TaskHostNodeManager.CreateComponent, CreationPattern.Singleton);
 
            _componentEntriesByType[BuildComponentType.NodeLauncher] = new BuildComponentEntry(BuildComponentType.NodeLauncher, NodeLauncher.CreateComponent, CreationPattern.Singleton);
            _componentEntriesByType[BuildComponentType.InProcNodeProvider] = new BuildComponentEntry(BuildComponentType.InProcNodeProvider, NodeProviderInProc.CreateComponent, CreationPattern.Singleton);
            _componentEntriesByType[BuildComponentType.OutOfProcNodeProvider] = new BuildComponentEntry(BuildComponentType.OutOfProcNodeProvider, NodeProviderOutOfProc.CreateComponent, CreationPattern.Singleton);
            _componentEntriesByType[BuildComponentType.OutOfProcTaskHostNodeProvider] = new BuildComponentEntry(BuildComponentType.OutOfProcTaskHostNodeProvider, NodeProviderOutOfProcTaskHost.CreateComponent, CreationPattern.Singleton);
 
            // PropertyCache,
            // RemoteNodeProvider,
            // NodePacketFactory,
            _componentEntriesByType[BuildComponentType.RequestEngine] = new BuildComponentEntry(BuildComponentType.RequestEngine, BuildRequestEngine.CreateComponent, CreationPattern.Singleton);
 
            // FileMonitor,
            // NodeEndpoint,
            _componentEntriesByType[BuildComponentType.LoggingService] = new BuildComponentEntry(BuildComponentType.LoggingService, null);
            _componentEntriesByType[BuildComponentType.RequestBuilder] = new BuildComponentEntry(BuildComponentType.RequestBuilder, RequestBuilder.CreateComponent, CreationPattern.CreateAlways);
            // This conditionally registers real or no-op implementation based on BuildParameters
            _componentEntriesByType[BuildComponentType.BuildCheckManagerProvider] = new BuildComponentEntry(BuildComponentType.BuildCheckManagerProvider, BuildCheckManagerProvider.CreateComponent, CreationPattern.Singleton);
            _componentEntriesByType[BuildComponentType.TargetBuilder] = new BuildComponentEntry(BuildComponentType.TargetBuilder, TargetBuilder.CreateComponent, CreationPattern.CreateAlways);
            _componentEntriesByType[BuildComponentType.TaskBuilder] = new BuildComponentEntry(BuildComponentType.TaskBuilder, TaskBuilder.CreateComponent, CreationPattern.CreateAlways);
            _componentEntriesByType[BuildComponentType.RegisteredTaskObjectCache] = new BuildComponentEntry(BuildComponentType.RegisteredTaskObjectCache, RegisteredTaskObjectCache.CreateComponent, CreationPattern.Singleton);
 
            // SDK resolution
            _componentEntriesByType[BuildComponentType.SdkResolverService] = new BuildComponentEntry(BuildComponentType.SdkResolverService, MainNodeSdkResolverService.CreateComponent, CreationPattern.Singleton);
 
#if FEATURE_REPORTFILEACCESSES
            _componentEntriesByType[BuildComponentType.FileAccessManager] = new BuildComponentEntry(BuildComponentType.FileAccessManager, FileAccessManager.CreateComponent, CreationPattern.Singleton);
#endif
        }
 
        /// <summary>
        /// Shuts down all factories registered to this component factory collection.
        /// </summary>
        public void ShutdownComponents()
        {
            foreach (KeyValuePair<BuildComponentType, BuildComponentEntry> componentEntry in _componentEntriesByType)
            {
                if (componentEntry.Value.Pattern == CreationPattern.Singleton)
                {
                    componentEntry.Value.ShutdownSingletonInstance();
                }
            }
        }
 
        /// <summary>
        /// Shuts down a specific singleton component.
        /// </summary>
        public void ShutdownComponent(BuildComponentType componentType)
        {
            BuildComponentEntry existingEntry = _componentEntriesByType[componentType];
            existingEntry.ShutdownSingletonInstance();
        }
 
        /// <summary>
        /// Registers a factory to replace one of the defaults.  Creation pattern is inherited from the original.
        /// </summary>
        /// <param name="componentType">The type which is created by this factory.</param>
        /// <param name="factory">The factory to be registered.</param>
        public void ReplaceFactory(BuildComponentType componentType, BuildComponentFactoryDelegate factory)
        {
            BuildComponentEntry existingEntry = _componentEntriesByType[componentType];
            _componentEntriesByType[componentType] = new BuildComponentEntry(componentType, factory, existingEntry.Pattern);
        }
 
        /// <summary>
        /// Registers a factory to replace one of the defaults.  Creation pattern is inherited from the original.
        /// </summary>
        /// <param name="componentType">The type which is created by this factory.</param>
        /// <param name="instance">The instance to be registered.</param>
        public void ReplaceFactory(BuildComponentType componentType, IBuildComponent instance)
        {
            ErrorUtilities.VerifyThrow(_componentEntriesByType[componentType].Pattern == CreationPattern.Singleton, "Previously existing factory for type {0} was not a singleton factory.", componentType);
            _componentEntriesByType[componentType] = new BuildComponentEntry(componentType, instance);
        }
 
        /// <summary>
        /// Adds a factory.
        /// </summary>
        /// <param name="componentType">The type which is created by this factory.</param>
        /// <param name="factory">Delegate which is responsible for creating the Component.</param>
        /// <param name="creationPattern">Creation pattern.</param>
        public void AddFactory(BuildComponentType componentType, BuildComponentFactoryDelegate factory, CreationPattern creationPattern)
        {
            _componentEntriesByType[componentType] = new BuildComponentEntry(componentType, factory, creationPattern);
        }
 
        /// <summary>
        /// Gets an instance of the specified component type from the host.
        /// </summary>
        /// <param name="type">The component type to be retrieved</param>
        /// <returns>The component</returns>
        public IBuildComponent GetComponent(BuildComponentType type)
        {
            if (!_componentEntriesByType.TryGetValue(type, out BuildComponentEntry componentEntry))
            {
                ErrorUtilities.ThrowInternalError("No factory registered for component type {0}", type);
            }
 
            return componentEntry.GetInstance(_host);
        }
 
        internal TComponent GetComponent<TComponent>(BuildComponentType type) where TComponent : IBuildComponent
        {
            return (TComponent)GetComponent(type);
        }
 
        /// <summary>
        /// A helper class wrapping build components.
        /// </summary>
        private class BuildComponentEntry
        {
            /// <summary>
            /// The factory used to construct instances of the component.
            /// </summary>
            private readonly BuildComponentFactoryDelegate _factory;
 
            /// <summary>
            /// The singleton instance for components which adhere to the singleton pattern.
            /// </summary>
            private IBuildComponent _singleton;
 
            /// <summary>
            /// Constructor.
            /// </summary>
            public BuildComponentEntry(BuildComponentType type, BuildComponentFactoryDelegate factory, CreationPattern pattern)
            {
                ComponentType = type;
                _factory = factory;
                Pattern = pattern;
            }
 
            /// <summary>
            /// Constructor for existing singleton.
            /// </summary>
            public BuildComponentEntry(BuildComponentType type, IBuildComponent singleton)
            {
                ComponentType = type;
                _singleton = singleton;
                Pattern = CreationPattern.Singleton;
            }
 
            /// <summary>
            /// Retrieves the component type.
            /// </summary>
            private BuildComponentType ComponentType { get; }
 
            /// <summary>
            /// Retrieves the creation pattern.
            /// </summary>
            public CreationPattern Pattern { get; }
 
            /// <summary>
            /// Gets an instance of the component.
            /// </summary>
            public IBuildComponent GetInstance(IBuildComponentHost host)
            {
                if (Pattern == CreationPattern.Singleton)
                {
                    if (_singleton == null)
                    {
                        _singleton = _factory(ComponentType);
                        _singleton.InitializeComponent(host);
                    }
 
                    return _singleton;
                }
 
                IBuildComponent component = _factory(ComponentType);
                component.InitializeComponent(host);
                return component;
            }
 
            /// <summary>
            /// Shuts down the single instance for this component type.
            /// </summary>
            public void ShutdownSingletonInstance()
            {
                ErrorUtilities.VerifyThrow(Pattern == CreationPattern.Singleton, "Cannot shutdown non-singleton.");
                if (_singleton != null)
                {
                    _singleton.ShutdownComponent();
                    _singleton = null;
                }
            }
        }
    }
}