Example #1
0
        /// <summary>
        ///     Constructs a graph starting from the given graph entry points, evaluating with the provided project collection.
        /// </summary>
        /// <param name="entryPoints">The entry points to use in constructing the graph</param>
        /// <param name="projectCollection">
        ///     The collection with which all projects in the graph should be associated. May not be
        ///     null.
        /// </param>
        /// <param name="projectInstanceFactory">
        ///     A delegate used for constructing a <see cref="ProjectInstance" />, called for each
        ///     project created during graph creation. This value can be null, which uses
        ///     a default implementation that calls the ProjectInstance constructor. See the remarks
        ///     on <see cref="ProjectInstanceFactoryFunc" /> for other scenarios.
        /// </param>
        /// <param name="degreeOfParallelism">
        ///     Number of threads to participate in building the project graph.
        /// </param>
        /// <param name="cancellationToken">
        ///     The <see cref="T:System.Threading.CancellationToken" /> token to observe.
        /// </param>
        /// <exception cref="InvalidProjectFileException">
        ///     If the evaluation of any project in the graph fails
        /// </exception>
        /// <exception cref="InvalidOperationException">
        ///     If a null reference is returned from <paramref name="projectInstanceFactory" />
        /// </exception>
        /// <exception cref="CircularDependencyException">
        ///     If the evaluation is successful but the project graph contains a circular
        ///     dependency
        /// </exception>
        public ProjectGraph(
            IEnumerable <ProjectGraphEntryPoint> entryPoints,
            ProjectCollection projectCollection,
            ProjectInstanceFactoryFunc projectInstanceFactory,
            int degreeOfParallelism,
            CancellationToken cancellationToken)
        {
            ErrorUtilities.VerifyThrowArgumentNull(projectCollection, nameof(projectCollection));

            var measurementInfo = BeginMeasurement();

            projectInstanceFactory ??= DefaultProjectInstanceFactory;

            var graphBuilder = new GraphBuilder(
                entryPoints,
                projectCollection,
                projectInstanceFactory,
                ProjectInterpretation.Instance,
                degreeOfParallelism,
                cancellationToken);

            graphBuilder.BuildGraph();

            EntryPointNodes = graphBuilder.EntryPointNodes;
            GraphRoots      = graphBuilder.RootNodes;
            ProjectNodes    = graphBuilder.ProjectNodes;
            Edges           = graphBuilder.Edges;

            _projectNodesTopologicallySorted = new Lazy <IReadOnlyCollection <ProjectGraphNode> >(() => TopologicalSort(GraphRoots, ProjectNodes));

            ConstructionMetrics = EndMeasurement();

            (Stopwatch Timer, string ETWArgs) BeginMeasurement()
            {
                string etwArgs = null;

                if (MSBuildEventSource.Log.IsEnabled())
                {
                    etwArgs = string.Join(";", entryPoints.Select(
                                              e =>
                    {
                        var globalPropertyString = e.GlobalProperties == null
                                ? string.Empty
                                : string.Join(", ", e.GlobalProperties.Select(kvp => $"{kvp.Key} = {kvp.Value}"));

                        return($"{e.ProjectFile}({globalPropertyString})");
                    }));

                    MSBuildEventSource.Log.ProjectGraphConstructionStart(etwArgs);
                }

                return(Stopwatch.StartNew(), etwArgs);
            }

            GraphConstructionMetrics EndMeasurement()
            {
                if (MSBuildEventSource.Log.IsEnabled())
                {
                    MSBuildEventSource.Log.ProjectGraphConstructionStop(measurementInfo.ETWArgs);
                }

                measurementInfo.Timer.Stop();

                return(new GraphConstructionMetrics(
                           measurementInfo.Timer.Elapsed,
                           ProjectNodes.Count,
                           Edges.Count));
            }
        }
Example #2
0
        /// <summary>
        /// To avoid calling nuget at graph construction time, the graph is initially constructed with outer build nodes referencing inner build nodes.
        /// However, at build time, for non root outer builds, the inner builds are NOT referenced by the outer build, but by the nodes referencing the
        /// outer build. Change the graph to mimic this behaviour.
        /// Examples
        /// OuterAsRoot -> Inner stays the same
        /// Node -> Outer -> Inner goes to: Node -> Outer; Node->Inner; Outer -> empty
        /// </summary>
        public void PostProcess(Dictionary <ConfigurationMetadata, ParsedProject> allNodes, GraphBuilder graphBuilder)
        {
            foreach (var node in allNodes)
            {
                var outerBuild = node.Value.GraphNode;

                if (GetProjectType(outerBuild.ProjectInstance) == ProjectType.OuterBuild && outerBuild.ReferencingProjects.Count != 0)
                {
                    foreach (var innerBuild in outerBuild.ProjectReferences)
                    {
                        foreach (var outerBuildReferencingProject in outerBuild.ReferencingProjects)
                        {
                            // Which edge should be used to connect the outerBuildReferencingProject to the inner builds?
                            // Decided to use the outerBuildBuildReferencingProject -> outerBuild edge in order to preserve any extra metadata
                            // information that may be present on the edge, like the "Targets" metadata which specifies what
                            // targets to call on the references.
                            var newInnerBuildEdge = graphBuilder.Edges[(outerBuildReferencingProject, outerBuild)];