| File: ObjectModelRemoting\LinkedObjectFactory.cs | Web Access |
| Project: src\msbuild\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 System.Threading; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; using Microsoft.Build.Execution; using Microsoft.Build.Framework; #nullable disable namespace Microsoft.Build.ObjectModelRemoting { /// <summary> /// implemented by MSBuild objects that support remote linking; /// </summary> internal interface ILinkableObject { /// <summary> /// Gets the current link, if any. For local objects returns null; /// </summary> object Link { get; } } /// <summary> /// Provide facility to ExternalProjectsProvider implementation /// to create local OM objects based on the remote link. /// These object are fully useful for associated Collection. /// </summary> public class LinkedObjectsFactory { private LinkedObjectsFactory(ProjectCollection collection) { Collection = collection; } /// <summary> /// Acquire a <see cref="LinkedObjectsFactory"/> instance for a given ProjectCollection. /// Allows creating a local MSBuild OM objects representing externally hosted Projects. /// </summary> public static LinkedObjectsFactory Get(ProjectCollection collection) { return new LinkedObjectsFactory(collection); } /// <summary> /// Get the underlying "link" proxy for a given MSBuild object model object (null if it is not linked). /// can be used by ExternalProjectsProvider to prevent double linking when implementing remote calls. /// </summary> public static object GetLink(object obj) { var linkable = obj as ILinkableObject; return linkable?.Link; } /// <summary> /// Check if an msbuild object is local (aka not from External Project) /// </summary> public static bool IsLocal(object obj) { return GetLink(obj) == null; } /// <summary> /// Local collection. /// </summary> public ProjectCollection Collection { get; } /// <summary> /// Gets only locally load projects, excluding external /// </summary> public static IReadOnlyCollection<Project> GetLocalProjects(ProjectCollection collection, string projectFile = null) { return (IReadOnlyCollection<Project>)collection.GetLoadedProjects(false, projectFile); } #region Evaluation public ProjectItem Create(ProjectItemLink link, Project project = null, ProjectItemElement xml = null) { project ??= link.Project; xml ??= link.Xml; return new LinkedProjectItem(xml, project, link); } public ProjectItemDefinition Create(ProjectItemDefinitionLink link, Project project = null) { project ??= link.Project; return new LinkedProjectItemDefinition(link, project, link.ItemType); } public Project Create(ProjectLink link) { // note we do not use wrapper LikedProjects class in this case. // Project element storage is in fact increased to support linked (with few bytes) // but since the Projects objects number are relatively low, this is not a big concern // as with other items that can be typically 1000s of times the number of projects. // That is done for simplicity, but if needed we can use the same approach here as well. return new Project(Collection, link); } public ProjectMetadata Create(ProjectMetadataLink link, object parent = null) { parent ??= link.Parent; return new LinkedProjectMetadata(parent, link); } public ProjectProperty Create(ProjectPropertyLink link, Project project = null) { project ??= link.Project; return new LinkedProjectProperty(project, link); } public ResolvedImport Create(ProjectImportElement importingElement, ProjectRootElement importedProject, int versionEvaluated, SdkResult sdkResult, bool isImported) { return new ResolvedImport(importingElement, importedProject, versionEvaluated, sdkResult, isImported); } #endregion #region Construction public ProjectRootElement Create(ProjectRootElementLink link) { return new ProjectRootElement(link); } public ProjectChooseElement Create(ProjectChooseElementLink link) { return new ProjectChooseElement(link); } public ProjectExtensionsElement Create(ProjectExtensionsElementLink link) { return new ProjectExtensionsElement(link); } public ProjectImportElement Create(ProjectImportElementLink link) { return new ProjectImportElement(link); } public ProjectImportGroupElement Create(ProjectImportGroupElementLink link) { return new ProjectImportGroupElement(link); } public ProjectItemDefinitionElement Create(ProjectItemDefinitionElementLink link) { return new ProjectItemDefinitionElement(link); } public ProjectItemDefinitionGroupElement Create(ProjectItemDefinitionGroupElementLink link) { return new ProjectItemDefinitionGroupElement(link); } public ProjectItemElement Create(ProjectItemElementLink link) { return new ProjectItemElement(link); } public ProjectItemGroupElement Create(ProjectItemGroupElementLink link) { return new ProjectItemGroupElement(link); } public ProjectMetadataElement Create(ProjectMetadataElementLink link) { return new ProjectMetadataElement(link); } public ProjectOnErrorElement Create(ProjectOnErrorElementLink link) { return new ProjectOnErrorElement(link); } public ProjectOtherwiseElement Create(ProjectOtherwiseElementLink link) { return new ProjectOtherwiseElement(link); } public ProjectOutputElement Create(ProjectOutputElementLink link) { return new ProjectOutputElement(link); } public ProjectPropertyElement Create(ProjectPropertyElementLink link) { return new ProjectPropertyElement(link); } public ProjectPropertyGroupElement Create(ProjectPropertyGroupElementLink link) { return new ProjectPropertyGroupElement(link); } public ProjectSdkElement Create(ProjectSdkElementLink link) { return new ProjectSdkElement(link); } public ProjectTargetElement Create(ProjectTargetElementLink link) { return new ProjectTargetElement(link); } public ProjectTaskElement Create(ProjectTaskElementLink link) { return new ProjectTaskElement(link); } public ProjectUsingTaskBodyElement Create(ProjectUsingTaskBodyElementLink link) { return new ProjectUsingTaskBodyElement(link); } public ProjectUsingTaskElement Create(ProjectUsingTaskElementLink link) { return new ProjectUsingTaskElement(link); } public ProjectUsingTaskParameterElement Create(ProjectUsingTaskParameterElementLink link) { return new ProjectUsingTaskParameterElement(link); } public ProjectWhenElement Create(ProjectWhenElementLink link) { return new ProjectWhenElement(link); } public UsingTaskParameterGroupElement Create(UsingTaskParameterGroupElementLink link) { return new UsingTaskParameterGroupElement(link); } #endregion #region Linked classes helpers // Using the pattern with overloaded classes that provide "Link" object so we ensure we do not increase the // memory storage of original items (with the Link field) while it is small, some of the MSBuild items can be created // in millions so it does adds up otherwise. private class LinkedProjectItem : ProjectItem, ILinkableObject, IImmutableInstanceProvider<ProjectItemInstance> { private ProjectItemInstance _immutableInstance; internal LinkedProjectItem(ProjectItemElement xml, Project project, ProjectItemLink link) : base(xml, project) { Link = link; } public ProjectItemInstance ImmutableInstance => _immutableInstance; public ProjectItemInstance GetOrSetImmutableInstance(ProjectItemInstance instance) { Interlocked.CompareExchange(ref _immutableInstance, instance, null); return _immutableInstance; } internal override ProjectItemLink Link { get; } object ILinkableObject.Link => Link; } private class LinkedProjectItemDefinition : ProjectItemDefinition, ILinkableObject, IImmutableInstanceProvider<ProjectItemDefinitionInstance> { private ProjectItemDefinitionInstance _immutableInstance; internal LinkedProjectItemDefinition(ProjectItemDefinitionLink link, Project project, string itemType) : base(project, itemType) { Link = link; } public ProjectItemDefinitionInstance ImmutableInstance => _immutableInstance; public ProjectItemDefinitionInstance GetOrSetImmutableInstance(ProjectItemDefinitionInstance instance) { Interlocked.CompareExchange(ref _immutableInstance, instance, null); return _immutableInstance; } internal override ProjectItemDefinitionLink Link { get; } object ILinkableObject.Link => Link; } private class LinkedProjectMetadata : ProjectMetadata, ILinkableObject, IImmutableInstanceProvider<ProjectMetadataInstance> { private ProjectMetadataInstance _immutableInstance; internal LinkedProjectMetadata(object parent, ProjectMetadataLink link) : base(parent, link.Xml) { Link = link; } public ProjectMetadataInstance ImmutableInstance => _immutableInstance; public ProjectMetadataInstance GetOrSetImmutableInstance(ProjectMetadataInstance instance) { Interlocked.CompareExchange(ref _immutableInstance, instance, null); return _immutableInstance; } internal override ProjectMetadataLink Link { get; } object ILinkableObject.Link => Link; } private class LinkedProjectProperty : ProjectProperty, ILinkableObject, IImmutableInstanceProvider<ProjectPropertyInstance> { private ProjectPropertyInstance _immutableInstance; internal ProjectPropertyLink Link { get; } object ILinkableObject.Link => Link; /// <summary> /// Creates a regular evaluated property, with backing XML. /// Called by Project.SetProperty. /// Property MAY NOT have reserved name and MAY NOT overwrite a global property. /// Predecessor is any immediately previous property that was overridden by this one during evaluation and may be null. /// </summary> internal LinkedProjectProperty(Project project, ProjectPropertyLink link) : base(project) { Link = link; } public ProjectPropertyInstance ImmutableInstance => _immutableInstance; public ProjectPropertyInstance GetOrSetImmutableInstance(ProjectPropertyInstance instance) { Interlocked.CompareExchange(ref _immutableInstance, instance, null); return _immutableInstance; } public override string Name => Link.Name; public override string UnevaluatedValue { get => Link.UnevaluatedValue; set => Link.UnevaluatedValue = value; } public override bool IsEnvironmentProperty => Link.IsEnvironmentProperty; public override bool IsGlobalProperty => Link.IsGlobalProperty; public override bool IsReservedProperty => Link.IsReservedProperty; public override ProjectPropertyElement Xml => Link.Xml; public override ProjectProperty Predecessor => Link.Predecessor; public override bool IsImported => Link.IsImported; internal override string EvaluatedValueEscapedInternal => Link.EvaluatedIncludeEscaped; } #endregion } }