Ejemplo n.º 1
0
        private ProjectGraphNode CreateNewNode(
            ConfigurationMetadata configurationMetadata,
            ProjectCollection projectCollection,
            ProjectInstanceFactoryFunc projectInstanceFactory)
        {
            // TODO: ProjectInstance just converts the dictionary back to a PropertyDictionary, so find a way to directly provide it.
            var globalProperties = configurationMetadata.GlobalProperties.ToDictionary();

            var projectInstance = projectInstanceFactory(
                configurationMetadata.ProjectFullPath,
                globalProperties,
                projectCollection);

            if (projectInstance == null)
            {
                throw new InvalidOperationException(ResourceUtilities.GetResourceString("NullReferenceFromProjectInstanceFactory"));
            }

            var graphNode = new ProjectGraphNode(
                projectInstance,
                globalProperties);

            _allParsedProjects[configurationMetadata] = graphNode;
            return(graphNode);
        }
Ejemplo n.º 2
0
        internal void AddProjectReference(ProjectGraphNode reference, ProjectItemInstance projectReferenceItem, GraphBuilder.GraphEdges edges)
        {
            _projectReferences.Add(reference);
            reference._referencingProjects.Add(this);

            // First edge wins, in accordance with vanilla msbuild behaviour when multiple msbuild tasks call into the same logical project
            edges[(this, reference)] = projectReferenceItem;
Ejemplo n.º 3
0
        private ParsedProject ParseProject(ConfigurationMetadata configurationMetadata)
        {
            // TODO: ProjectInstance just converts the dictionary back to a PropertyDictionary, so find a way to directly provide it.
            var globalProperties = configurationMetadata.GlobalProperties.ToDictionary();
            ProjectGraphNode graphNode;
            ProjectInstance  projectInstance;
            var negotiatePlatform = PlatformNegotiationEnabled && !configurationMetadata.IsSetPlatformHardCoded;

            projectInstance = _projectInstanceFactory(
                configurationMetadata.ProjectFullPath,
                negotiatePlatform ? null : globalProperties,                 // Platform negotiation requires an evaluation with no global properties first
                _projectCollection);

            if (ConversionUtilities.ValidBooleanTrue(projectInstance.GetPropertyValue(EnableDynamicPlatformResolutionMetadataName)))
            {
                PlatformNegotiationEnabled = true;
            }

            if (projectInstance == null)
            {
                throw new InvalidOperationException(ResourceUtilities.GetResourceString("NullReferenceFromProjectInstanceFactory"));
            }

            if (negotiatePlatform)
            {
                var selectedPlatform = PlatformNegotiation.GetNearestPlatform(projectInstance.GetPropertyValue(PlatformMetadataName), projectInstance.GetPropertyValue(PlatformsMetadataName), projectInstance.GetPropertyValue(PlatformLookupTableMetadataName), configurationMetadata.PreviousPlatformLookupTable, projectInstance.FullPath, configurationMetadata.PreviousPlatform);

                if (selectedPlatform.Equals(String.Empty))
                {
                    globalProperties.Remove(PlatformMetadataName);
                }
                else
                {
                    globalProperties[PlatformMetadataName] = selectedPlatform;
                }
                projectInstance = _projectInstanceFactory(
                    configurationMetadata.ProjectFullPath,
                    globalProperties,
                    _projectCollection);
            }

            graphNode = new ProjectGraphNode(projectInstance);

            var referenceInfos = ParseReferences(graphNode);

            return(new ParsedProject(configurationMetadata, graphNode, referenceInfos));
        }
Ejemplo n.º 4
0
        private static ImmutableList <string> DetermineTargetsToPropagate(ProjectGraphNode node, ImmutableList <string> entryTargets)
        {
            var targetsToPropagate = ImmutableList <string> .Empty;
            ICollection <ProjectItemInstance> projectReferenceTargets = node.ProjectInstance.GetItems(ProjectReferenceTargetsItemType);

            foreach (var entryTarget in entryTargets)
            {
                foreach (var projectReferenceTarget in projectReferenceTargets)
                {
                    if (projectReferenceTarget.EvaluatedInclude.Equals(entryTarget, StringComparison.OrdinalIgnoreCase))
                    {
                        string targetsMetadataValue = projectReferenceTarget.GetMetadataValue(ProjectReferenceTargetsMetadataName);
                        targetsToPropagate = targetsToPropagate.AddRange(ExpressionShredder.SplitSemiColonSeparatedList(targetsMetadataValue));
                    }
                }
            }

            return(targetsToPropagate);
        }
Ejemplo n.º 5
0
        private ParsedProject ParseProject(ConfigurationMetadata configurationMetadata)
        {
            // TODO: ProjectInstance just converts the dictionary back to a PropertyDictionary, so find a way to directly provide it.
            var globalProperties = configurationMetadata.GlobalProperties.ToDictionary();

            var projectInstance = _projectInstanceFactory(
                configurationMetadata.ProjectFullPath,
                globalProperties,
                _projectCollection);

            if (projectInstance == null)
            {
                throw new InvalidOperationException(ResourceUtilities.GetResourceString("NullReferenceFromProjectInstanceFactory"));
            }

            var graphNode = new ProjectGraphNode(projectInstance);

            var referenceInfos = ParseReferences(graphNode);

            return(new ParsedProject(configurationMetadata, graphNode, referenceInfos));
        }
Ejemplo n.º 6
0
        private List <ProjectInterpretation.ReferenceInfo> ParseReferences(ProjectGraphNode parsedProject)
        {
            var referenceInfos = new List <ProjectInterpretation.ReferenceInfo>();

            foreach (var referenceInfo in _projectInterpretation.GetReferences(parsedProject.ProjectInstance))
            {
                if (FileUtilities.IsSolutionFilename(referenceInfo.ReferenceConfiguration.ProjectFullPath))
                {
                    throw new InvalidOperationException(ResourceUtilities.FormatResourceStringIgnoreCodeAndKeyword(
                                                            "StaticGraphDoesNotSupportSlnReferences",
                                                            referenceInfo.ReferenceConfiguration.ProjectFullPath,
                                                            referenceInfo.ReferenceConfiguration.ProjectFullPath
                                                            ));
                }

                SubmitProjectForParsing(referenceInfo.ReferenceConfiguration);

                referenceInfos.Add(referenceInfo);
            }

            return(referenceInfos);
        }
Ejemplo n.º 7
0
 public ProjectItemInstance this[(ProjectGraphNode node, ProjectGraphNode reference)key]
Ejemplo n.º 8
0
 public BuildResult this[ProjectGraphNode node] => ResultsByNode[node];
Ejemplo n.º 9
0
 public ProjectGraphBuildRequest(ProjectGraphNode node, ImmutableList <string> targets)
 {
     Node             = node ?? throw new ArgumentNullException(nameof(node));
     RequestedTargets = targets ?? throw new ArgumentNullException(nameof(targets));
 }
Ejemplo n.º 10
0
        /// <remarks>
        /// Traverse an evaluated graph
        /// Maintain the state of each node (InProcess and Processed) to detect cycles
        /// returns false if loading the graph is not successful
        /// </remarks>
        private (bool success, List <string> projectsInCycle) DetectCycles(ProjectGraphNode node,
                                                                           Dictionary <ProjectGraphNode, NodeState> nodeState,
                                                                           ProjectCollection projectCollection,
                                                                           PropertyDictionary <ProjectPropertyInstance> globalProperties)
        {
            nodeState[node] = NodeState.InProcess;
            IEnumerable <ProjectItemInstance> projectReferenceItems = node.ProjectInstance.GetItems(MSBuildConstants.ProjectReferenceItemName);

            foreach (var projectReferenceToParse in projectReferenceItems)
            {
                string projectReferenceFullPath = projectReferenceToParse.GetMetadataValue(FullPathMetadataName);
                PropertyDictionary <ProjectPropertyInstance> projectReferenceGlobalProperties = GetProjectReferenceGlobalProperties(projectReferenceToParse, globalProperties);
                var projectReferenceConfigurationMetadata = new ConfigurationMetadata(projectReferenceFullPath, projectReferenceGlobalProperties);
                ProjectGraphNode projectReference         = _allParsedProjects[projectReferenceConfigurationMetadata];
                if (nodeState.TryGetValue(projectReference, out NodeState projectReferenceNodeState))
                {
                    // Because this is a depth-first search, we should only encounter new nodes or nodes whose subgraph has been completely processed.
                    // If we encounter a node that is currently being processed(InProcess state), it must be one of the ancestors in a circular dependency.
                    if (projectReferenceNodeState == NodeState.InProcess)
                    {
                        if (node.Equals(projectReference))
                        {
                            // the project being evaluated has a reference to itself
                            var selfReferencingProjectString = FormatCircularDependencyError(new List <string> {
                                node.ProjectInstance.FullPath, node.ProjectInstance.FullPath
                            });
                            throw new CircularDependencyException(string.Format(
                                                                      ResourceUtilities.GetResourceString("CircularDependencyInProjectGraph"),
                                                                      selfReferencingProjectString));
                        }
                        else
                        {
                            // the project being evaluated has a circular dependency involving multiple projects
                            // add this project to the list of projects involved in cycle
                            var projectsInCycle = new List <string> {
                                projectReferenceConfigurationMetadata.ProjectFullPath
                            };
                            return(false, projectsInCycle);
                        }
                    }
                }
                else
                {
                    // recursively process newly discovered references
                    var loadReference = DetectCycles(projectReference, nodeState, projectCollection,
                                                     projectReferenceGlobalProperties);
                    if (!loadReference.success)
                    {
                        if (loadReference.projectsInCycle[0].Equals(node.ProjectInstance.FullPath))
                        {
                            // we have reached the nth project in the cycle, form error message and throw
                            loadReference.projectsInCycle.Add(projectReferenceConfigurationMetadata.ProjectFullPath);
                            loadReference.projectsInCycle.Add(node.ProjectInstance.FullPath);
                            var errorMessage = FormatCircularDependencyError(loadReference.projectsInCycle);
                            throw new CircularDependencyException(string.Format(
                                                                      ResourceUtilities.GetResourceString("CircularDependencyInProjectGraph"),
                                                                      errorMessage));
                        }
                        else
                        {
                            // this is one of the projects in the circular dependency
                            // update the list of projects in cycle and return the list to the caller
                            loadReference.projectsInCycle.Add(projectReferenceConfigurationMetadata.ProjectFullPath);
                            return(false, loadReference.projectsInCycle);
                        }
                    }
                }
                ProjectGraphNode parsedProjectReference = _allParsedProjects[projectReferenceConfigurationMetadata];
                node.AddProjectReference(parsedProjectReference);
                parsedProjectReference.AddReferencingProject(node);
            }
            nodeState[node] = NodeState.Processed;
            return(true, null);
        }
Ejemplo n.º 11
0
        /// <summary>
        /// Load a graph with root node at entryProjectFile
        /// Maintain a queue of projects to be processed and evaluate projects in parallel
        /// Returns false if loading the graph is not successful
        /// </summary>
        private bool LoadGraph(
            ConcurrentQueue <ConfigurationMetadata> projectsToEvaluate,
            ProjectCollection projectCollection,
            ConcurrentDictionary <ConfigurationMetadata, object> tasksInProgress,
            ProjectInstanceFactoryFunc projectInstanceFactory,
            out List <Exception> exceptions)
        {
            var exceptionsInTasks    = new ConcurrentBag <Exception>();
            var evaluationWaitHandle = new AutoResetEvent(false);

            while (projectsToEvaluate.Count != 0 || tasksInProgress.Count != 0)
            {
                ConfigurationMetadata projectToEvaluate;
                if (projectsToEvaluate.Count != 0)
                {
                    projectToEvaluate = projectsToEvaluate.Dequeue();
                    var task = new Task(() =>
                    {
                        ProjectGraphNode parsedProject = CreateNewNode(projectToEvaluate, projectCollection, projectInstanceFactory);
                        IEnumerable <ProjectItemInstance> projectReferenceItems = parsedProject.ProjectInstance.GetItems(MSBuildConstants.ProjectReferenceItemName);
                        foreach (var projectReferenceToParse in projectReferenceItems)
                        {
                            if (!string.IsNullOrEmpty(projectReferenceToParse.GetMetadataValue(ToolsVersionMetadataName)))
                            {
                                throw new InvalidOperationException(string.Format(
                                                                        CultureInfo.InvariantCulture,
                                                                        ResourceUtilities.GetResourceString(
                                                                            "ProjectGraphDoesNotSupportProjectReferenceWithToolset"),
                                                                        projectReferenceToParse.EvaluatedInclude,
                                                                        parsedProject.ProjectInstance.FullPath));
                            }

                            string projectReferenceFullPath = projectReferenceToParse.GetMetadataValue(FullPathMetadataName);
                            PropertyDictionary <ProjectPropertyInstance> projectReferenceGlobalProperties = GetProjectReferenceGlobalProperties(projectReferenceToParse, projectToEvaluate.GlobalProperties);
                            var projectReferenceConfigurationMetadata = new ConfigurationMetadata(projectReferenceFullPath, projectReferenceGlobalProperties);
                            if (!tasksInProgress.ContainsKey(projectReferenceConfigurationMetadata))
                            {
                                if (!_allParsedProjects.ContainsKey(projectReferenceConfigurationMetadata))
                                {
                                    projectsToEvaluate.Enqueue(projectReferenceConfigurationMetadata);
                                    evaluationWaitHandle.Set();
                                }
                            }
                        }
                    });

                    if (tasksInProgress.TryAdd(projectToEvaluate, null))
                    {
                        // once the task completes, remove it from tasksInProgress using a chained task
                        // signal the wait handle to process new projects that have been discovered by this task or exit if all projects have been evaluated
                        task.ContinueWith(_ =>
                        {
                            if (task.IsFaulted)
                            {
                                exceptionsInTasks.Add(task.Exception.InnerException);
                            }
                            tasksInProgress.TryRemove(projectToEvaluate, out var _);
                            evaluationWaitHandle.Set();
                        });
                        task.Start();
                    }
                }
                else
                {
                    // if projectsToEvaluate is empty but there are tasks in progress, there is nothing to do till a task completes and discovers new projects
                    // wait till a task completes and sends a signal
                    evaluationWaitHandle.WaitOne();
                }
            }

            if (exceptionsInTasks.Count != 0)
            {
                exceptions = exceptionsInTasks.ToList();
                return(false);
            }

            exceptions = null;
            return(true);
        }
Ejemplo n.º 12
0
        /// <summary>
        /// Gets the target list to be executed for every project in the graph, given a particular target list for the entry project.
        /// </summary>
        /// <remarks>
        /// This method uses the ProjectReferenceTargets items to determine the targets to run per node. The results can then be used
        /// to start building each project individually, assuming a given project is built after its references.
        /// </remarks>
        /// <param name="entryProjectTargets">The target list for the entry project. May be null or empty, in which case the entry projects' default targets will be used.</param>
        /// <returns>A dictionary containing the target list for each node.</returns>
        public IReadOnlyDictionary <ProjectGraphNode, ImmutableList <string> > GetTargetLists(ICollection <string> entryProjectTargets)
        {
            // Seed the dictionary with empty lists for every node. In this particular case though an empty list means "build nothing" rather than "default targets".
            Dictionary <ProjectGraphNode, ImmutableList <string> > targetLists = ProjectNodes.ToDictionary(node => node, node => ImmutableList <string> .Empty);

            var encounteredEdges = new HashSet <ProjectGraphBuildRequest>();
            var edgesToVisit     = new Queue <ProjectGraphBuildRequest>();

            // Initial state of the graph traversal.
            foreach (var entryPointNode in EntryPointNodes)
            {
                ImmutableList <string> entryTargets = entryProjectTargets == null || entryProjectTargets.Count == 0
                    ? ImmutableList.CreateRange(entryPointNode.ProjectInstance.DefaultTargets)
                    : ImmutableList.CreateRange(entryProjectTargets);
                var entryEdge = new ProjectGraphBuildRequest(entryPointNode, entryTargets);
                encounteredEdges.Add(entryEdge);
                edgesToVisit.Enqueue(entryEdge);
            }

            // Traverse the entire graph, visiting each edge once.
            while (edgesToVisit.Count > 0)
            {
                ProjectGraphBuildRequest buildRequest     = edgesToVisit.Dequeue();
                ProjectGraphNode         node             = buildRequest.Node;
                ImmutableList <string>   requestedTargets = buildRequest.RequestedTargets;

                targetLists[node] = targetLists[node].AddRange(requestedTargets);

                // No need to continue if this node has no project references.
                if (node.ProjectReferences.Count == 0)
                {
                    continue;
                }

                // Based on the entry points of this project, determine which targets to propagate down to project references.
                ImmutableList <string> targetsToPropagate = DetermineTargetsToPropagate(node, requestedTargets);

                // Queue the project references for visitation, if the edge hasn't already been traversed.
                foreach (var projectReference in node.ProjectReferences)
                {
                    var projectReferenceEdge = new ProjectGraphBuildRequest(
                        projectReference,
                        ExpandDefaultTargets(projectReference.ProjectInstance, targetsToPropagate));
                    if (encounteredEdges.Add(projectReferenceEdge))
                    {
                        edgesToVisit.Enqueue(projectReferenceEdge);
                    }
                }
            }

            // Dedupe target lists
            List <KeyValuePair <ProjectGraphNode, ImmutableList <string> > > entriesToUpdate = new List <KeyValuePair <ProjectGraphNode, ImmutableList <string> > >();

            foreach (KeyValuePair <ProjectGraphNode, ImmutableList <string> > pair in targetLists)
            {
                ImmutableList <string> targetList = pair.Value;

                SortedSet <string> seenTargets = new SortedSet <string>(StringComparer.OrdinalIgnoreCase);
                int i = 0;
                while (i < targetList.Count)
                {
                    if (seenTargets.Add(targetList[i]))
                    {
                        i++;
                    }
                    else
                    {
                        targetList = targetList.RemoveAt(i);
                    }
                }

                // Only update if it changed
                if (targetList != pair.Value)
                {
                    entriesToUpdate.Add(new KeyValuePair <ProjectGraphNode, ImmutableList <string> >(pair.Key, targetList));
                }
            }

            // Update in a separate pass to avoid modifying a collection while iterating it.
            foreach (KeyValuePair <ProjectGraphNode, ImmutableList <string> > pair in entriesToUpdate)
            {
                targetLists[pair.Key] = pair.Value;
            }

            return(targetLists);
        }
Ejemplo n.º 13
0
 internal void AddReferencingProject(ProjectGraphNode projectGraphNode) => _referencingProjects.Add(projectGraphNode);
Ejemplo n.º 14
0
 internal void AddProjectReference(ProjectGraphNode projectGraphNode) => _projectReferences.Add(projectGraphNode);