public static ProjectLoader Create(ILogger logger, string sourceRootFolder)
		{
			ProjectLoader projectLoader;
			if (!projectLoaderCache.TryGetValue(sourceRootFolder, out projectLoader))
			{
				projectLoader = new ProjectLoader(logger);
				projectLoaderCache.Add(sourceRootFolder, projectLoader);
			}
			return projectLoader;
		}
 public List<string> GetTargetProjectFiles(ProjectLoader projectLoader, string[] targetProjects)
 {
     var targetProjectFiles = new List<string>();
     foreach (var assemblyName in targetProjects)
     {
         var projectFile = projectLoader.GetProjectByAssemblyName(assemblyName);
         if (projectFile == null)
             throw new Exception($"Project by assembly name {assemblyName} not found");
         targetProjectFiles.Add(projectFile.ProjectFileLocation);
     }
     return targetProjectFiles;
 }
        public GeneratedSolution CreateSolution(ProjectSetup projectSetup, ProjectLoader projectLoader, IEnumerable<string> projectLocations, string solutionFileLocation, string[] thirdPartyFolders, HashSet<string> usedThirdParties, string customAppend)
        {
            var solutionProjectList = ReferenceWalker.WalkReferencesRecursively(projectSetup, projectLoader, projectLocations, thirdPartyFolders, usedThirdParties);

            var solutionFile = new StringBuilder();
            solutionFile.AppendLine();
            solutionFile.AppendLine("Microsoft Visual Studio Solution File, Format Version 12.00");
            solutionFile.AppendLine("# Visual Studio 14");
            solutionFile.AppendLine("VisualStudioVersion = 14.0.24720.0");
            solutionFile.AppendLine("MinimumVisualStudioVersion = 10.0.40219.1");

            if (!string.IsNullOrWhiteSpace(customAppend))
            {
                solutionFile.AppendLine(customAppend);
            }

            var testProjects = new List<Guid>();
            var startProjects = new List<Guid>();
            var otherProjects = new List<Guid>();

            foreach (var projectId in solutionProjectList)
            {
                var project = projectLoader.GetProjectById(projectId);

                var isTestProject = project.AssemblyName.ToLowerInvariant().Contains("test");
                var isStartProject = project.IsLauncher;

                if (isTestProject)
                    testProjects.Add(projectId);
                else if (isStartProject)
                    startProjects.Add(projectId);
                else
                    otherProjects.Add(projectId);

                // FullProjectPath can be changed to relative, but then root folder is needed
                var projectFileLocation = MakeRelativePath(solutionFileLocation, project.ProjectFileLocation);

                solutionFile.AppendFormat("Project(\"{0}\") = \"{1}\", \"{2}\", \"{3}\"",
                                          "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}",
                                          Path.GetFileNameWithoutExtension(projectFileLocation),
                                          projectFileLocation,
                                          project.ProjectId.ToString("B").ToUpperInvariant());
                solutionFile.AppendLine();

                #region DEPENDENCIES

                var dependencies = ReferenceWalker.GetProjectDependencies(projectSetup, projectLoader, project, thirdPartyFolders, usedThirdParties);

                if (dependencies != null && dependencies.Count > 0)
                {
                    var projectSectionAdded = false;
                    foreach (var dependentItemId in dependencies)
                    {
                        // do not add dependency to itself
                        if (dependentItemId == project.ProjectId)
                            continue;

                        if (!projectSectionAdded)
                        {
                            solutionFile.AppendLine("\tProjectSection(ProjectDependencies) = postProject");
                            projectSectionAdded = true;
                        }

                        solutionFile.AppendFormat("\t\t{0} = {0}", dependentItemId.ToString("B").ToUpperInvariant());
                        solutionFile.AppendLine();
                    }

                    if (projectSectionAdded)
                        solutionFile.AppendLine("\tEndProjectSection");
                }

                #endregion

                solutionFile.AppendLine("EndProject");
            }

            var otherProjectId = Guid.NewGuid();
            var testProjectId = Guid.NewGuid();

            if (startProjects.Count > 0)
            {
                solutionFile.AppendLine($"Project(\"{{2150E333-8FDC-42A3-9474-1A3956D46DE8}}\") = \"Projects\", \"Projects\", \"{otherProjectId.ToString("B").ToUpperInvariant()}\"");
                solutionFile.AppendLine("EndProject");
            }

            if (testProjects.Count > 0)
            {
                solutionFile.AppendLine($"Project(\"{{2150E333-8FDC-42A3-9474-1A3956D46DE8}}\") = \"Tests\", \"Tests\", \"{testProjectId.ToString("B").ToUpperInvariant()}\"");
                solutionFile.AppendLine("EndProject");
            }

            solutionFile.AppendLine("Global");

            #region GLOBAL SECTION

            solutionFile.AppendLine("\tGlobalSection(SolutionConfigurationPlatforms) = preSolution");
            solutionFile.AppendLine("\t\tDebug|Any CPU = Debug|Any CPU");
            solutionFile.AppendLine("\t\tDebug|Mixed Platforms = Debug|Mixed Platforms");
            solutionFile.AppendLine("\t\tDebug|X64 = Debug|X64");
            solutionFile.AppendLine("\t\tDebug|x86 = Debug|x86");
            solutionFile.AppendLine("\t\tRelease|Any CPU = Release|Any CPU");
            solutionFile.AppendLine("\t\tRelease|Mixed Platforms = Release|Mixed Platforms");
            solutionFile.AppendLine("\t\tRelease|X64 = Release|X64");
            solutionFile.AppendLine("\t\tRelease|x86 = Release|x86");
            solutionFile.AppendLine("\tEndGlobalSection");

            #endregion

            #region GLOBAL SECTION

            solutionFile.AppendLine("\tGlobalSection(ProjectConfigurationPlatforms) = postSolution");
            foreach (Guid projectId in solutionProjectList)
            {
                string idString = projectId.ToString("B").ToUpperInvariant();
                solutionFile.AppendFormat("\t\t{0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU", idString).AppendLine();
                solutionFile.AppendFormat("\t\t{0}.Debug|Any CPU.Build.0 = Debug|Any CPU", idString).AppendLine();
                solutionFile.AppendFormat("\t\t{0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU", idString).AppendLine();
                solutionFile.AppendFormat("\t\t{0}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU", idString).AppendLine();
                solutionFile.AppendFormat("\t\t{0}.Debug|X64.ActiveCfg = Debug|Any CPU", idString).AppendLine();
                solutionFile.AppendFormat("\t\t{0}.Debug|X64.Build.0 = Debug|Any CPU", idString).AppendLine();
                solutionFile.AppendFormat("\t\t{0}.Debug|x86.ActiveCfg = Debug|Any CPU", idString).AppendLine();
                solutionFile.AppendFormat("\t\t{0}.Debug|x86.Build.0 = Debug|Any CPU", idString).AppendLine();

                solutionFile.AppendFormat("\t\t{0}.Release|Any CPU.ActiveCfg = Release|Any CPU", idString).AppendLine();
                solutionFile.AppendFormat("\t\t{0}.Release|Any CPU.Build.0 = Release|Any CPU", idString).AppendLine();
                solutionFile.AppendFormat("\t\t{0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU", idString).AppendLine();
                solutionFile.AppendFormat("\t\t{0}.Release|Mixed Platforms.Build.0 = Release|Any CPU", idString).AppendLine();
                solutionFile.AppendFormat("\t\t{0}.Release|X64.ActiveCfg = Release|Any CPU", idString).AppendLine();
                solutionFile.AppendFormat("\t\t{0}.Release|X64.Build.0 = Release|Any CPU", idString).AppendLine();
                solutionFile.AppendFormat("\t\t{0}.Release|x86.ActiveCfg = Release|Any CPU", idString).AppendLine();
                solutionFile.AppendFormat("\t\t{0}.Release|x86.Build.0 = Release|Any CPU", idString).AppendLine();
            }
            solutionFile.AppendLine("\tEndGlobalSection");

            solutionFile.AppendLine("\tGlobalSection(SolutionProperties) = preSolution");
            solutionFile.AppendFormat("\t\tHideSolutionNode = FALSE").AppendLine();
            solutionFile.AppendLine("\tEndGlobalSection");

            if (testProjects.Count > 0 || startProjects.Count > 0)  // put to folder only we have one of these..
            {
                solutionFile.AppendLine("\tGlobalSection(NestedProjects) = preSolution");

                foreach (var projectId in otherProjects)
                {
                    solutionFile.AppendFormat("\t\t{0} = {1}", projectId.ToString("B").ToUpperInvariant(), otherProjectId.ToString("B").ToUpperInvariant());
                    solutionFile.AppendLine();
                }

                foreach (var projectId in testProjects)
                {
                    solutionFile.AppendFormat("\t\t{0} = {1}", projectId.ToString("B").ToUpperInvariant(), testProjectId.ToString("B").ToUpperInvariant());
                    solutionFile.AppendLine();
                }

                solutionFile.AppendLine("\tEndGlobalSection");
            }

            #endregion

            solutionFile.AppendLine("EndGlobal");

            Logger.Trace("Created solution file with {0} projects", solutionProjectList.Count);

            var generatedSolution = new GeneratedSolution
            {
                Content = solutionFile.ToString(),
                IncludedProjects = solutionProjectList.Count
            };
            return generatedSolution;
        }
        public List<Guid> WalkReferencesRecursively(ProjectSetup projectSetup, ProjectLoader projectLoader, IEnumerable<string> projectLocations, string[] thirdPartyFolders, HashSet<string> usedThirdParties)
        {
            List<Guid> completeReferences = new List<Guid>();
            foreach (string projectLocation in projectLocations)
            {
                VSProject project = projectLoader.LoadProject(projectSetup, projectLocation);

                List<Guid> dependentReferences = WalkReferencesRecursively(projectSetup, projectLoader, project, thirdPartyFolders, usedThirdParties);
                dependentReferences.Add(project.ProjectId);

                foreach (Guid dependentReference in dependentReferences)
                {
                    if (!completeReferences.Contains(dependentReference))
                        completeReferences.Add(dependentReference);
                }
            }
            return completeReferences;
        }
        private void ResolveProject(ProjectSetup projectSetup, ProjectLoader projectLoader, VSProject project, VSProjectReference reference, List<VSProject> referencedProjects)
        {
            VSProject vsProject;
            if (reference.IsProjectReference)
            {
                // resolve by ID
                vsProject = projectLoader.GetProjectById(reference.ProjectReferenceId);
            }
            else if (reference.IsSilverlightReference)
            {
                vsProject = projectLoader.GetProjectById(reference.ProjectReferenceId);
            }
            else
            {
                // resolve by output path
                vsProject = projectLoader.GetProjectByOutput(reference.ResolvedHintPath);
            }

            if (vsProject != null)
            {
                referencedProjects.Add(vsProject);
            }
            else
            {
                // fall back scenario, check assembly name in current project output path
                string outputDirectory = Path.GetDirectoryName(project.ResolvedOutput) ?? ".";
                string currentFolderAssembly = Path.Combine(outputDirectory, reference.ResolvedInclude) + ".dll";
                vsProject = projectLoader.GetProjectByOutput(currentFolderAssembly);

                if (vsProject== null)
                {
                    currentFolderAssembly = Path.Combine(outputDirectory, reference.ResolvedInclude) + ".exe";
                    vsProject = projectLoader.GetProjectByOutput(currentFolderAssembly);
                }

                if (vsProject == null)
                {
                    if (projectSetup.WhenReferenceNotResolved == ProjectSetupBehavior.Fail)
                    {
                        throw new SolutionGeneratorException(
                            "Reference {0} was not resolved. {3}Project {1}. {3}Expected location = {2}{3}",
                            reference.ResolvedInclude, project.ProjectFileLocation, reference.ResolvedHintPath, Environment.NewLine);
                    }
                    if (projectSetup.WhenReferenceNotResolved == ProjectSetupBehavior.Warn)
                    {
                        Logger.Warn(
                            "Reference {0} was not resolved. {3}Project {1}. {3}Expected location = {2}{3}",
                            reference.ResolvedInclude, project.ProjectFileLocation, reference.ResolvedHintPath, Environment.NewLine);
                    }
                }
                else
                {
                    if (projectSetup.WhenReferenceResolvedInDifferentLocation == ProjectSetupBehavior.Fail)
                    {
                        throw new SolutionGeneratorException(
                            "Reference {0} was not resolved. {4}Project {1}. {4}Expected location = {2}{4}However it was found in project output folder {3}.{4}",
                            reference.ResolvedInclude, project.ProjectFileLocation, reference.ResolvedHintPath, outputDirectory, Environment.NewLine);
                    }
                    if (projectSetup.WhenReferenceResolvedInDifferentLocation == ProjectSetupBehavior.Warn)
                    {
                        Logger.Warn(
                            "Reference {0} was not resolved. {4}Project {1}. {4}Expected location = {2}{4}However it was found in project output folder {3}.{4}",
                            reference.ResolvedInclude, project.ProjectFileLocation, reference.ResolvedHintPath, outputDirectory, Environment.NewLine);
                    }
                }
            }
        }
        private List<Guid> GetCurrentProjectReferences(ProjectSetup projectSetup, ProjectLoader projectLoader, VSProject project, HashSet<string> thirdPartyFileMap, HashSet<string> usedThirdParties, List<Guid> checkedProjects)
        {
            ReferenceCacheItem cacheItem;
            if (!ProjectReferenceCache.TryGetValue(project.ProjectId, out cacheItem))
            {
                if (checkedProjects.Contains(project.ProjectId))
                {
                    StringBuilder builder = new StringBuilder();

                    for (int i = checkedProjects.Count - 1; i >= 0; i--)
                    {
                        Guid checkedProject = checkedProjects[i];
                        builder.Insert(0, projectLoader.GetProjectById(checkedProject).ProjectFileLocation + Environment.NewLine);
                        if (checkedProject == project.ProjectId)
                            break;
                    }

                    builder.AppendLine(project.ProjectFileLocation);

                    throw new SolutionGeneratorException("Circular reference detected! {1}Projects: {1}{0}", builder, Environment.NewLine);
                }
                checkedProjects.Add(project.ProjectId);

                cacheItem = new ReferenceCacheItem
                {
                    References = new List<Guid>(),
                    UsedThirdParties = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase)
                };

                if (project.References != null)
                {
                    List<VSProject> referencedProjects = new List<VSProject>();
                    foreach (VSProjectReference reference in project.References)
                    {
                        if (thirdPartyFileMap.Contains(reference.ResolvedHintPath))
                        {
                            cacheItem.UsedThirdParties.Add(reference.ResolvedHintPath);
                            continue;
                        }

                        if (SystemReferenceFileMap.Contains(reference.ResolvedInclude))
                        {
                            continue;
                        }

                        if (reference.ResolvedInclude.StartsWith("Microsoft.", StringComparison.InvariantCultureIgnoreCase))
                        {
                            continue;
                        }

                        ResolveProject(projectSetup, projectLoader, project, reference, referencedProjects);
                    }

                    foreach (VSProject referencedProject in referencedProjects)
                    {
                        if (!cacheItem.References.Contains(referencedProject.ProjectId))
                            cacheItem.References.Add(referencedProject.ProjectId);
                    }
                }

                //cacheItem.UsedThirdParties.UnionWith(usedThirdParties);
                ProjectReferenceCache.Add(project.ProjectId, cacheItem);
            }

            usedThirdParties.UnionWith(cacheItem.UsedThirdParties);
            return cacheItem.References;
        }
        public List<Guid> GetProjectDependencies(ProjectSetup projectSetup, ProjectLoader projectLoader, VSProject project, string[] thirdPartyFolders, HashSet<string> usedThirdParties)
        {
            HashSet<string> thirdPartyFileMap;
            if (thirdPartyFolders != null && thirdPartyFolders.Length > 0)
            {
                string key = string.Join(":", thirdPartyFolders);

                if (!ThirdPartyFileCache.TryGetValue(key, out thirdPartyFileMap))
                {
                    thirdPartyFileMap = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);

                    List<string> thirdPartyFiles = FileSearcher.Scan("*.dll", thirdPartyFolders);
                    thirdPartyFileMap.UnionWith(thirdPartyFiles);
                    ThirdPartyFileCache.Add(key, thirdPartyFileMap);
                }
            }
            else
            {
                thirdPartyFileMap = new HashSet<string>();
            }

            List<Guid> checkedProjects = new List<Guid>();

            var completeReferences = GetCurrentProjectReferences(projectSetup, projectLoader, project, thirdPartyFileMap, usedThirdParties, checkedProjects);
            return completeReferences;
        }