public void ReevaluationShouldNotReuseInitialContext(EvaluationContext.SharingPolicy policy)
        {
            try
            {
                EvaluationContext.TestOnlyHookOnCreate = c => SetResolverForContext(c, _resolver);

                var collection = _env.CreateProjectCollection().Collection;

                var context = EvaluationContext.Create(policy);

                var project = Project.FromXmlReader(
                    XmlReader.Create(new StringReader("<Project Sdk=\"foo\"></Project>")),
                    new ProjectOptions
                {
                    ProjectCollection = collection,
                    EvaluationContext = context,
                    LoadSettings      = ProjectLoadSettings.IgnoreMissingImports
                });

                _resolver.ResolvedCalls["foo"].ShouldBe(1);

                project.AddItem("a", "b");

                project.ReevaluateIfNecessary();

                _resolver.ResolvedCalls["foo"].ShouldBe(2);
            }
            finally
            {
                EvaluationContext.TestOnlyHookOnCreate = null;
            }
        }
        public void PassedInFileSystemShouldBeReusedInSharedContext()
        {
            var projectFiles = new[]
            {
                _env.CreateFile("1.proj", @"<Project> <PropertyGroup Condition=`Exists('1.file')`></PropertyGroup> </Project>".Cleanup()).Path,
                _env.CreateFile("2.proj", @"<Project> <PropertyGroup Condition=`Exists('2.file')`></PropertyGroup> </Project>".Cleanup()).Path
            };

            var projectCollection = _env.CreateProjectCollection().Collection;
            var fileSystem        = new Helpers.LoggingFileSystem();
            var evaluationContext = EvaluationContext.Create(EvaluationContext.SharingPolicy.Shared, fileSystem);

            foreach (var projectFile in projectFiles)
            {
                Project.FromFile(
                    projectFile,
                    new ProjectOptions
                {
                    ProjectCollection = projectCollection,
                    EvaluationContext = evaluationContext
                }
                    );
            }

            fileSystem.ExistenceChecks.OrderBy(kvp => kvp.Key)
            .ShouldBe(
                new Dictionary <string, int>
            {
                { Path.Combine(_env.DefaultTestDirectory.Path, "1.file"), 1 },
                { Path.Combine(_env.DefaultTestDirectory.Path, "2.file"), 1 }
            }.OrderBy(kvp => kvp.Key));

            fileSystem.FileOrDirectoryExistsCalls.ShouldBe(2);
        }
        public void SharedContextShouldGetReusedWhereasIsolatedContextShouldNot(EvaluationContext.SharingPolicy policy)
        {
            var previousContext = EvaluationContext.Create(policy);

            for (var i = 0; i < 10; i++)
            {
                var currentContext = previousContext.ContextForNewProject();

                if (i == 0)
                {
                    currentContext.ShouldBeSameAs(previousContext, "first usage context was not the same as the initial context");
                }
                else
                {
                    switch (policy)
                    {
                    case EvaluationContext.SharingPolicy.Shared:
                        currentContext.ShouldBeSameAs(previousContext, $"Shared policy: usage {i} was not the same as usage {i - 1}");
                        break;

                    case EvaluationContext.SharingPolicy.Isolated:
                        currentContext.ShouldNotBeSameAs(previousContext, $"Isolated policy: usage {i} was the same as usage {i - 1}");
                        break;

                    default:
                        throw new ArgumentOutOfRangeException(nameof(policy), policy, null);
                    }
                }

                previousContext = currentContext;
            }
        }
示例#4
0
        public void ReevaluationShouldRespectContextLifetime(EvaluationContext.SharingPolicy policy)
        {
            var collection = _env.CreateProjectCollection().Collection;

            var context1 = EvaluationContext.Create(policy);

            var project = Project.FromXmlReader(
                XmlReader.Create(new StringReader("<Project></Project>")),
                new ProjectOptions
            {
                ProjectCollection = collection,
                EvaluationContext = context1,
                LoadSettings      = ProjectLoadSettings.IgnoreMissingImports
            });

            project.AddItem("a", "b");

            project.ReevaluateIfNecessary();

            var context2 = GetEvaluationContext(project);

            switch (policy)
            {
            case EvaluationContext.SharingPolicy.Shared:
                context1.ShouldBeSameAs(context2);
                break;

            case EvaluationContext.SharingPolicy.Isolated:
                context1.ShouldNotBeSameAs(context2);
                break;
            }
        }
示例#5
0
        public void ContextPinsGlobExpansionCache(EvaluationContext.SharingPolicy policy, string[][] expectedGlobExpansions)
        {
            var projectDirectory = _env.DefaultTestDirectory.FolderPath;

            _env.SetCurrentDirectory(projectDirectory);

            var context = EvaluationContext.Create(policy);

            var evaluationCount = 0;

            File.WriteAllText(Path.Combine(projectDirectory, $"{evaluationCount}.cs"), "");

            EvaluateProjects(
                _globProjects,
                context,
                project =>
            {
                var expectedGlobExpansion = expectedGlobExpansions[evaluationCount];
                evaluationCount++;

                File.WriteAllText(Path.Combine(projectDirectory, $"{evaluationCount}.cs"), "");

                ObjectModelHelpers.AssertItems(expectedGlobExpansion, project.GetItems("i"));
            }
                );
        }
示例#6
0
        public void ContextDisambiguatesAFullyQualifiedGlobPointingInAnotherRelativeGlobsCone(EvaluationContext.SharingPolicy policy, string[][] expectedGlobExpansions)
        {
            var project1Directory     = _env.DefaultTestDirectory.CreateDirectory("Project1");
            var project1GlobDirectory = project1Directory.CreateDirectory("Glob").CreateDirectory("1").Path;

            var project2Directory = _env.DefaultTestDirectory.CreateDirectory("Project2");

            var context = EvaluationContext.Create(policy);

            var evaluationCount = 0;

            File.WriteAllText(Path.Combine(project1GlobDirectory, $"{evaluationCount}.cs"), "");

            EvaluateProjects(
                new []
            {
                // first project uses a relative path
                new ProjectSpecification(
                    Path.Combine(project1Directory.Path, "1"),
                    $@"<Project>
                            <ItemGroup>
                                <i Include=`{Path.Combine("Glob", "**", "*.cs")}` />
                            </ItemGroup>
                        </Project>"),
                // second project reaches out into first project's cone via a fully qualified path
                new ProjectSpecification(
                    Path.Combine(project2Directory.Path, "2"),
                    $@"<Project>
                            <ItemGroup>
                                <i Include=`{Path.Combine(project1Directory.Path, "Glob", "**", "*.cs")}` />
                            </ItemGroup>
                        </Project>")
            },
                context,
                project =>
            {
                var projectName = Path.GetFileNameWithoutExtension(project.FullPath);

                // globs have the fixed directory part prepended, so add it to the expected results
                var expectedGlobExpansion = expectedGlobExpansions[evaluationCount]
                                            .Select(i => Path.Combine("Glob", "1", i))
                                            .ToArray();

                // project 2 has fully qualified directory parts, so make the results for 2 fully qualified
                if (projectName.Equals("2"))
                {
                    expectedGlobExpansion = expectedGlobExpansion
                                            .Select(i => Path.Combine(project1Directory.Path, i))
                                            .ToArray();
                }

                var actualGlobExpansion = project.GetItems("i");
                ObjectModelHelpers.AssertItems(expectedGlobExpansion, actualGlobExpansion);

                evaluationCount++;

                File.WriteAllText(Path.Combine(project1GlobDirectory, $"{evaluationCount}.cs"), "");
            }
                );
        }
        public void ProjectInstanceShouldRespectSharingPolicy(EvaluationContext.SharingPolicy policy)
        {
            try
            {
                var seenContexts = new HashSet <EvaluationContext>();

                EvaluationContext.TestOnlyHookOnCreate = c => seenContexts.Add(c);

                var collection = _env.CreateProjectCollection().Collection;

                var context = EvaluationContext.Create(policy);

                const int numIterations = 10;
                for (int i = 0; i < numIterations; i++)
                {
                    ProjectInstance.FromProjectRootElement(
                        ProjectRootElement.Create(),
                        new ProjectOptions
                    {
                        ProjectCollection = collection,
                        EvaluationContext = context,
                        LoadSettings      = ProjectLoadSettings.IgnoreMissingImports
                    });
                }

                int expectedNumContexts = policy == EvaluationContext.SharingPolicy.Shared ? 1 : numIterations;

                seenContexts.Count.ShouldBe(expectedNumContexts);
                seenContexts.ShouldAllBe(c => c.Policy == policy);
            }
            finally
            {
                EvaluationContext.TestOnlyHookOnCreate = null;
            }
        }
        public void IsolatedContextShouldNotSupportBeingPassedAFileSystem()
        {
            _env.DoNotLaunchDebugger();

            var fileSystem = new Helpers.LoggingFileSystem();

            Should.Throw <ArgumentException>(() => EvaluationContext.Create(EvaluationContext.SharingPolicy.Isolated, fileSystem));
        }
        public void ContextDisambiguatesDistinctRelativeGlobsPointingOutsideOfSameProjectCone(EvaluationContext.SharingPolicy policy, string[][] expectedGlobExpansions)
        {
            var globDirectory = _env.DefaultTestDirectory.CreateDirectory("glob");

            var projectRoot = _env.DefaultTestDirectory.CreateDirectory("proj");

            var project1Directory = projectRoot.CreateDirectory("Project1");

            var project2SubDir = projectRoot.CreateDirectory("subdirectory");

            var project2Directory = project2SubDir.CreateDirectory("Project2");

            var context = EvaluationContext.Create(policy);

            var evaluationCount = 0;

            File.WriteAllText(Path.Combine(globDirectory.Path, $"{evaluationCount}.cs"), "");

            EvaluateProjects(
                new []
            {
                new ProjectSpecification(
                    Path.Combine(project1Directory.Path, "1"),
                    @"<Project>
                            <ItemGroup>
                                <i Include=`../../glob/*.cs` />
                            </ItemGroup>
                        </Project>"),
                new ProjectSpecification(
                    Path.Combine(project2Directory.Path, "2"),
                    @"<Project>
                            <ItemGroup>
                                <i Include=`../../../glob/*.cs` />
                            </ItemGroup>
                        </Project>")
            },
                context,
                project =>
            {
                var projectName            = Path.GetFileNameWithoutExtension(project.FullPath);
                var globFixedDirectoryPart = projectName.EndsWith("1")
                        ? Path.Combine("..", "..", "glob")
                        : Path.Combine("..", "..", "..", "glob");

                // globs have the fixed directory part prepended, so add it to the expected results
                var expectedGlobExpansion = expectedGlobExpansions[evaluationCount]
                                            .Select(i => Path.Combine(globFixedDirectoryPart, i))
                                            .ToArray();

                var actualGlobExpansion = project.GetItems("i");
                ObjectModelHelpers.AssertItems(expectedGlobExpansion, actualGlobExpansion);

                evaluationCount++;

                File.WriteAllText(Path.Combine(globDirectory.Path, $"{evaluationCount}.cs"), "");
            }
                );
        }
        private void ContextCachesCommonOutOfProjectCone(bool itemSpecPathIsRelative, EvaluationContext.SharingPolicy policy, string[][] expectedGlobExpansions)
        {
            var testDirectory = _env.DefaultTestDirectory;
            var globDirectory = testDirectory.CreateDirectory("GlobDirectory");

            var itemSpecDirectoryPart = itemSpecPathIsRelative
                ? Path.Combine("..", "GlobDirectory")
                : globDirectory.Path;

            Directory.CreateDirectory(globDirectory.Path);

            // Globs with a directory part will produce items prepended with that directory part
            foreach (var globExpansion in expectedGlobExpansions)
            {
                for (var i = 0; i < globExpansion.Length; i++)
                {
                    globExpansion[i] = Path.Combine(itemSpecDirectoryPart, globExpansion[i]);
                }
            }

            var projectSpecs = new[]
            {
                $@"<Project>
                <ItemGroup>
                    <i Include=`{Path.Combine("{0}", "**", "*.cs")}`/>
                </ItemGroup>
            </Project>",
                $@"<Project>
                <ItemGroup>
                    <i Include=`{Path.Combine("{0}", "**", "*.cs")}`/>
                </ItemGroup>
            </Project>"
            }
            .Select(p => string.Format(p, itemSpecDirectoryPart))
            .Select((p, i) => new ProjectSpecification(Path.Combine(testDirectory.Path, $"ProjectDirectory{i}", $"Project{i}.proj"), p));

            var context = EvaluationContext.Create(policy);

            var evaluationCount = 0;

            File.WriteAllText(Path.Combine(globDirectory.Path, $"{evaluationCount}.cs"), "");

            EvaluateProjects(
                projectSpecs,
                context,
                project =>
            {
                var expectedGlobExpansion = expectedGlobExpansions[evaluationCount];
                evaluationCount++;

                File.WriteAllText(Path.Combine(globDirectory.Path, $"{evaluationCount}.cs"), "");

                ObjectModelHelpers.AssertItems(expectedGlobExpansion, project.GetItems("i"));
            }
                );
        }
        public void ContextDisambiguatesSameRelativeGlobsPointingOutsideDifferentProjectCones(EvaluationContext.SharingPolicy policy, string[][] expectedGlobExpansions)
        {
            var project1Root          = _env.DefaultTestDirectory.CreateDirectory("Project1");
            var project1Directory     = project1Root.CreateDirectory("1").Path;
            var project1GlobDirectory = project1Root.CreateDirectory("Glob").CreateDirectory("1").Path;

            var project2Root          = _env.DefaultTestDirectory.CreateDirectory("Project2");
            var project2Directory     = project2Root.CreateDirectory("2").Path;
            var project2GlobDirectory = project2Root.CreateDirectory("Glob").CreateDirectory("2").Path;

            var context = EvaluationContext.Create(policy);

            var evaluationCount = 0;

            File.WriteAllText(Path.Combine(project1GlobDirectory, $"1.{evaluationCount}.cs"), "");
            File.WriteAllText(Path.Combine(project2GlobDirectory, $"2.{evaluationCount}.cs"), "");

            EvaluateProjects(
                new []
            {
                new ProjectSpecification(
                    Path.Combine(project1Directory, "1"),
                    $@"<Project>
                            <ItemGroup>
                                <i Include=`{Path.Combine("..", "Glob", "**", "*.cs")}`/>
                            </ItemGroup>
                        </Project>"),
                new ProjectSpecification(
                    Path.Combine(project2Directory, "2"),
                    $@"<Project>
                            <ItemGroup>
                                <i Include=`{Path.Combine("..", "Glob", "**", "*.cs")}`/>
                            </ItemGroup>
                        </Project>")
            },
                context,
                project =>
            {
                var projectName = Path.GetFileNameWithoutExtension(project.FullPath);

                // globs have the fixed directory part prepended, so add it to the expected results
                var expectedGlobExpansion = expectedGlobExpansions[evaluationCount]
                                            .Select(i => Path.Combine("..", "Glob", projectName, $"{projectName}.{i}"))
                                            .ToArray();

                var actualGlobExpansion = project.GetItems("i");
                ObjectModelHelpers.AssertItems(expectedGlobExpansion, actualGlobExpansion);

                evaluationCount++;

                File.WriteAllText(Path.Combine(project1GlobDirectory, $"1.{evaluationCount}.cs"), "");
                File.WriteAllText(Path.Combine(project2GlobDirectory, $"2.{evaluationCount}.cs"), "");
            }
                );
        }
        public void ContextCachesExistenceChecksInGetDirectoryNameOfFileAbove(EvaluationContext.SharingPolicy policy)
        {
            var context = EvaluationContext.Create(policy);

            var subdirectory     = _env.DefaultTestDirectory.CreateDirectory("subDirectory");
            var subdirectoryFile = subdirectory.CreateFile("a");

            _env.DefaultTestDirectory.CreateFile("a");

            int evaluationCount = 0;

            EvaluateProjects(
                new []
            {
                $@"<Project>
                      <PropertyGroup>
                        <SearchedPath>$([MSBuild]::GetDirectoryNameOfFileAbove('{subdirectory.Path}', 'a'))</SearchedPath>
                      </PropertyGroup>
                    </Project>"
            },
                context,
                project =>
            {
                evaluationCount++;

                var searchedPath = project.GetProperty("SearchedPath");

                switch (policy)
                {
                case EvaluationContext.SharingPolicy.Shared:
                    searchedPath.EvaluatedValue.ShouldBe(subdirectory.Path);
                    break;

                case EvaluationContext.SharingPolicy.Isolated:
                    searchedPath.EvaluatedValue.ShouldBe(
                        evaluationCount == 1
                                    ? subdirectory.Path
                                    : _env.DefaultTestDirectory.Path);
                    break;

                default:
                    throw new ArgumentOutOfRangeException(nameof(policy), policy, null);
                }

                if (evaluationCount == 1)
                {
                    // this will cause the upper file to get picked up in the Isolated policy
                    subdirectoryFile.Delete();
                }
            });

            evaluationCount.ShouldBe(2);
        }
        public void ContextCachesExistenceChecksInConditions(EvaluationContext.SharingPolicy policy)
        {
            var projectDirectory = _env.DefaultTestDirectory.FolderPath;

            _env.SetCurrentDirectory(projectDirectory);

            var context = EvaluationContext.Create(policy);

            var theFile = Path.Combine(projectDirectory, "0.cs");

            File.WriteAllText(theFile, "");

            var evaluationCount = 0;

            EvaluateProjects(
                _projectsWithConditions,
                context,
                project =>
            {
                evaluationCount++;

                if (File.Exists(theFile))
                {
                    File.Delete(theFile);
                }

                if (evaluationCount == 1)
                {
                    project.GetPropertyValue("p").ShouldBe("val");
                }
                else
                {
                    switch (policy)
                    {
                    case EvaluationContext.SharingPolicy.Shared:
                        project.GetPropertyValue("p").ShouldBe("val");
                        break;

                    case EvaluationContext.SharingPolicy.Isolated:
                        project.GetPropertyValue("p").ShouldBeEmpty();
                        break;

                    default:
                        throw new ArgumentOutOfRangeException(nameof(policy), policy, null);
                    }
                }
            }
                );
        }
示例#14
0
        public static ProjectOptions CreateProjectOptionsWithResolver(SdkResolver resolver)
        {
            var context    = EvaluationContext.Create(EvaluationContext.SharingPolicy.Isolated);
            var sdkService = (SdkResolverService)context.SdkResolverService;

            sdkService.InitializeForTests(null, new List <SdkResolver>()
            {
                resolver
            });

            return(new ProjectOptions
            {
                EvaluationContext = context
            });
        }
        public void ContextDisambiguatesSameRelativeGlobsPointingInsideDifferentProjectCones(EvaluationContext.SharingPolicy policy, string[][] expectedGlobExpansions)
        {
            var projectDirectory1 = _env.DefaultTestDirectory.CreateDirectory("1").Path;
            var projectDirectory2 = _env.DefaultTestDirectory.CreateDirectory("2").Path;

            var context = EvaluationContext.Create(policy);

            var evaluationCount = 0;

            File.WriteAllText(Path.Combine(projectDirectory1, $"1.{evaluationCount}.cs"), "");
            File.WriteAllText(Path.Combine(projectDirectory2, $"2.{evaluationCount}.cs"), "");

            EvaluateProjects(
                new []
            {
                new ProjectSpecification(
                    Path.Combine(projectDirectory1, "1"),
                    $@"<Project>
                            <ItemGroup>
                                <i Include=`{Path.Combine("**", "*.cs")}` />
                            </ItemGroup>
                        </Project>"),
                new ProjectSpecification(
                    Path.Combine(projectDirectory2, "2"),
                    $@"<Project>
                            <ItemGroup>
                                <i Include=`{Path.Combine("**", "*.cs")}` />
                            </ItemGroup>
                        </Project>"),
            },
                context,
                project =>
            {
                var projectName = Path.GetFileNameWithoutExtension(project.FullPath);

                var expectedGlobExpansion = expectedGlobExpansions[evaluationCount]
                                            .Select(i => $"{projectName}.{i}")
                                            .ToArray();

                ObjectModelHelpers.AssertItems(expectedGlobExpansion, project.GetItems("i"));

                evaluationCount++;

                File.WriteAllText(Path.Combine(projectDirectory1, $"1.{evaluationCount}.cs"), "");
                File.WriteAllText(Path.Combine(projectDirectory2, $"2.{evaluationCount}.cs"), "");
            }
                );
        }
        public static ProjectOptions CreateProjectOptionsWithResolverFileMapping(Dictionary <string, string> mapping)
        {
            var resolver = new FileBasedMockSdkResolver(mapping);

            var context    = EvaluationContext.Create(EvaluationContext.SharingPolicy.Isolated);
            var sdkService = (SdkResolverService)context.SdkResolverService;

            sdkService.InitializeForTests(null, new List <SdkResolver>()
            {
                resolver
            });

            return(new ProjectOptions
            {
                EvaluationContext = context
            });
        }
        // projects should cache glob expansions when the glob is shared between projects and points outside of project cone
        public void ContextCachesCommonOutOfProjectConeGlob(bool itemSpecPathIsRelative, EvaluationContext.SharingPolicy policy, string[][] expectedGlobExpansions)
        {
            var testDirectory = _env.DefaultTestDirectory.FolderPath;
            var globDirectory = Path.Combine(testDirectory, "GlobDirectory");

            var itemSpecDirectoryPart = itemSpecPathIsRelative
                ? Path.Combine("..", "GlobDirectory")
                : globDirectory;

            itemSpecDirectoryPart = itemSpecDirectoryPart.WithTrailingSlash();

            Directory.CreateDirectory(globDirectory);

            // Globs with a directory part will produce items prepended with that directory part
            foreach (var globExpansion in expectedGlobExpansions)
            {
                for (var i = 0; i < globExpansion.Length; i++)
                {
                    globExpansion[i] = Path.Combine(itemSpecDirectoryPart, globExpansion[i]);
                }
            }

            var projectSpecs = _projectsWithOutOfConeGlobs
                               .Select(p => string.Format(p, itemSpecDirectoryPart))
                               .Select((p, i) => new ProjectSpecification(Path.Combine(testDirectory, $"ProjectDirectory{i}", $"Project{i}.proj"), p));

            var context = EvaluationContext.Create(policy);

            var evaluationCount = 0;

            File.WriteAllText(Path.Combine(globDirectory, $"{evaluationCount}.cs"), "");

            EvaluateProjects(
                projectSpecs,
                context,
                project =>
            {
                var expectedGlobExpansion = expectedGlobExpansions[evaluationCount];
                evaluationCount++;

                File.WriteAllText(Path.Combine(globDirectory, $"{evaluationCount}.cs"), "");

                ObjectModelHelpers.AssertItems(expectedGlobExpansion, project.GetItems("i"));
            }
                );
        }
示例#18
0
        public void ContextSdkResolverIsUsed(EvaluationContext.SharingPolicy policy, int sdkLookupsForFoo, int sdkLookupsForBar)
        {
            try
            {
                EvaluationContext.TestOnlyAlterStateOnCreate = c => SetResolverForContext(c, _resolver);

                var context = EvaluationContext.Create(policy);
                EvaluateProjects(context);

                _resolver.ResolvedCalls["foo"].ShouldBe(sdkLookupsForFoo);
                _resolver.ResolvedCalls["bar"].ShouldBe(sdkLookupsForBar);
                _resolver.ResolvedCalls.Count.ShouldBe(2);
            }
            finally
            {
                EvaluationContext.TestOnlyAlterStateOnCreate = null;
            }
        }
        public void ContextPinsSdkResolverCache(EvaluationContext.SharingPolicy policy, int sdkLookupsForFoo, int sdkLookupsForBar)
        {
            try
            {
                EvaluationContext.TestOnlyHookOnCreate = c => SetResolverForContext(c, _resolver);

                var context = EvaluationContext.Create(policy);
                EvaluateProjects(_sdkResolutionProjects, context, null);

                _resolver.ResolvedCalls.Count.ShouldBe(2);
                _resolver.ResolvedCalls["foo"].ShouldBe(sdkLookupsForFoo);
                _resolver.ResolvedCalls["bar"].ShouldBe(sdkLookupsForBar);
            }
            finally
            {
                EvaluationContext.TestOnlyHookOnCreate = null;
            }
        }
        public void ContextCachesImportGlobExpansions(EvaluationContext.SharingPolicy policy, string[][] expectedGlobExpansions)
        {
            var projectDirectory = _env.DefaultTestDirectory.Path;

            var context = EvaluationContext.Create(policy);

            var evaluationCount = 0;

            File.WriteAllText(Path.Combine(projectDirectory, $"{evaluationCount}.props"), $"<Project><ItemGroup><i Include=`{evaluationCount}.cs`/></ItemGroup></Project>".Cleanup());

            EvaluateProjects(
                _projectsWithGlobImports,
                context,
                project =>
            {
                var expectedGlobExpansion = expectedGlobExpansions[evaluationCount];
                evaluationCount++;

                File.WriteAllText(Path.Combine(projectDirectory, $"{evaluationCount}.props"), $"<Project><ItemGroup><i Include=`{evaluationCount}.cs`/></ItemGroup></Project>".Cleanup());

                ObjectModelHelpers.AssertItems(expectedGlobExpansion, project.GetItems("i"));
            }
                );
        }
        /// <summary>
        /// Recursively loads and evaluates MSBuild projects.
        /// </summary>
        /// <param name="entryProjects">An <see cref="IEnumerable{ProjectGraphEntryPoint}" /> containing the entry projects to load.</param>
        /// <returns>An <see cref="ICollection{ProjectWithInnerNodes}" /> object containing projects and their inner nodes if they are targeting multiple frameworks.</returns>
        private ICollection <ProjectWithInnerNodes> LoadProjects(IEnumerable <ProjectGraphEntryPoint> entryProjects)
        {
            var loggers = new List <Microsoft.Build.Framework.ILogger>
            {
                LoggingQueue
            };

            // Get user specified parameters for a binary logger
            string binlogParameters = Environment.GetEnvironmentVariable("RESTORE_TASK_BINLOG_PARAMETERS");

            // Attach the binary logger if Debug or binlog parameters were specified
            if (Debug || !string.IsNullOrWhiteSpace(binlogParameters))
            {
                loggers.Add(new BinaryLogger
                {
                    // Default the binlog parameters if only the debug option was specified
                    Parameters = binlogParameters ?? "LogFile=nuget.binlog"
                });
            }

            var projects = new ConcurrentDictionary <string, ProjectWithInnerNodes>(StringComparer.OrdinalIgnoreCase);

            var projectCollection = new ProjectCollection(
                globalProperties: null,
                // Attach a logger for evaluation only if the Debug option is set
                loggers: loggers,
                remoteLoggers: null,
                toolsetDefinitionLocations: ToolsetDefinitionLocations.Default,
                // Having more than 1 node spins up multiple msbuild.exe instances to run builds in parallel
                // However, these targets complete so quickly that the added overhead makes it take longer
                maxNodeCount: 1,
                onlyLogCriticalEvents: false,
                // Loading projects as readonly makes parsing a little faster since comments and whitespace can be ignored
                loadProjectsReadOnly: true);

            var failedBuildSubmissions = new ConcurrentBag <BuildSubmission>();

            try
            {
                var sw = Stopwatch.StartNew();

                var evaluationContext = EvaluationContext.Create(EvaluationContext.SharingPolicy.Shared);

                ProjectGraph projectGraph;

                int buildCount = 0;

                var buildParameters = new BuildParameters(projectCollection)
                {
                    // Use the same loggers as the project collection
                    Loggers       = projectCollection.Loggers,
                    LogTaskInputs = Debug
                };

                // BeginBuild starts a queue which accepts build requests and applies the build parameters to all of them
                BuildManager.DefaultBuildManager.BeginBuild(buildParameters);

                try
                {
                    // Create a ProjectGraph object and pass a factory method which creates a ProjectInstance
                    projectGraph = new ProjectGraph(entryProjects, projectCollection, (path, properties, collection) =>
                    {
                        var projectOptions = new ProjectOptions
                        {
                            EvaluationContext = evaluationContext,
                            GlobalProperties  = properties,
                            // Ignore bad imports to maximize the chances of being able to load the project and restore
                            LoadSettings      = ProjectLoadSettings.IgnoreEmptyImports | ProjectLoadSettings.IgnoreInvalidImports | ProjectLoadSettings.IgnoreMissingImports | ProjectLoadSettings.DoNotEvaluateElementsWithFalseCondition,
                            ProjectCollection = collection
                        };

                        // Create a Project object which does the evaluation
                        var project = Project.FromFile(path, projectOptions);

                        // Create a ProjectInstance object which is what this factory needs to return
                        var projectInstance = project.CreateProjectInstance(ProjectInstanceSettings.None, evaluationContext);

                        if (!projectInstance.Targets.ContainsKey("_IsProjectRestoreSupported") || properties.TryGetValue("TargetFramework", out var targetFramework) && string.IsNullOrWhiteSpace(targetFramework))
                        {
                            // In rare cases, users can set an empty TargetFramework value in a project-to-project reference.  Static Graph will respect that
                            // but NuGet does not need to do anything with that instance of the project since the actual project is still loaded correctly
                            // with its actual TargetFramework.
                            return(projectInstance);
                        }

                        // If the project supports restore, queue up a build of the 3 targets needed for restore
                        BuildManager.DefaultBuildManager
                        .PendBuildRequest(
                            new BuildRequestData(
                                projectInstance,
                                TargetsToBuild,
                                hostServices: null,
                                // Suppresses an error that a target does not exist because it may or may not contain the targets that we're running
                                BuildRequestDataFlags.SkipNonexistentTargets))
                        .ExecuteAsync(
                            callback: buildSubmission =>
                        {
                            // If the build failed, add its result to the list to be processed later
                            if (buildSubmission.BuildResult.OverallResult == BuildResultCode.Failure)
                            {
                                failedBuildSubmissions.Add(buildSubmission);
                            }
                        },
                            context: null);

                        Interlocked.Increment(ref buildCount);

                        // Add the project instance to the list, if its an inner node for a multi-targeting project it will be added to the inner collection
                        projects.AddOrUpdate(
                            path,
                            key => new ProjectWithInnerNodes(targetFramework, new MSBuildProjectInstance(projectInstance)),
                            (_, item) => item.Add(targetFramework, new MSBuildProjectInstance(projectInstance)));

                        return(projectInstance);
                    });
                }
                finally
                {
                    // EndBuild blocks until all builds are complete
                    BuildManager.DefaultBuildManager.EndBuild();
                }

                sw.Stop();

                MSBuildLogger.LogInformation(string.Format(CultureInfo.CurrentCulture, Strings.ProjectEvaluationSummary, projectGraph.ProjectNodes.Count, sw.ElapsedMilliseconds, buildCount, failedBuildSubmissions.Count));

                if (failedBuildSubmissions.Any())
                {
                    // Return null if any builds failed, they will have logged errors
                    return(null);
                }
            }
            catch (Exception e)
            {
                LoggingQueue.TaskLoggingHelper.LogErrorFromException(e, showStackTrace: true);

                return(null);
            }
            finally
            {
                projectCollection.Dispose();
            }

            // Just return the projects not the whole dictionary as it was just used to group the projects together
            return(projects.Values);
        }