private string Generate()

in Sharpmake.Generators/VisualStudio/Sln.cs [249:680]


        private string Generate(
            Solution solution,
            IReadOnlyList<Solution.Configuration> solutionConfigurations,
            string solutionPath,
            string solutionFile,
            bool addMasterBff,
            out bool updated
        )
        {
            // reset current solution state
            _rootSolutionFolders.Clear();
            _solutionFolders.Clear();

            FileInfo solutionFileInfo = new FileInfo(Util.GetCapitalizedPath(solutionPath + Path.DirectorySeparatorChar + solutionFile + SolutionExtension));

            string solutionGuid = Util.BuildGuid(solutionFileInfo.FullName, solution.SharpmakeCsPath);

            DevEnv devEnv = solutionConfigurations[0].Target.GetFragment<DevEnv>();
            List<Solution.ResolvedProject> solutionProjects = ResolveSolutionProjects(solution, solutionConfigurations);

            if (solutionProjects.Count == 0)
            {
                updated = solutionFileInfo.Exists;
                if (updated)
                    Util.TryDeleteFile(solutionFileInfo.FullName);
                return solutionFileInfo.FullName;
            }

            List<Solution.ResolvedProject> resolvedPathReferences = ResolveReferencesByPath(solutionProjects, solutionConfigurations[0].ProjectReferencesByPath);

            var guidlist = solutionProjects.Select(p => p.UserData["Guid"]);
            resolvedPathReferences = resolvedPathReferences.Where(r => !guidlist.Contains(r.UserData["Guid"])).ToList();

            var fileGenerator = new FileGenerator();

            // write solution header
            switch (devEnv)
            {
                case DevEnv.vs2015:
                    fileGenerator.Write(Template.Solution.HeaderBeginVs2015);
                    break;
                case DevEnv.vs2017:
                    fileGenerator.Write(Template.Solution.HeaderBeginVs2017);
                    break;
                case DevEnv.vs2019:
                    fileGenerator.Write(Template.Solution.HeaderBeginVs2019);
                    break;
                case DevEnv.vs2022:
                    fileGenerator.Write(Template.Solution.HeaderBeginVs2022);
                    break;
                default:
                    throw new Error($"Unsupported DevEnv {devEnv} for solution {solution.Name}");
            }

            SolutionFolder masterBffFolder = null;
            if (addMasterBff)
            {
                masterBffFolder = GetSolutionFolder(solution.FastBuildMasterBffSolutionFolder);
                if (masterBffFolder == null)
                    throw new Error("FastBuildMasterBffSolutionFolder needs to be set in solution " + solutionFile);
            }

            // Write all needed folders before the projects to make sure the proper startup project is selected.

            // Ensure folders are always in the same order to avoid random shuffles
            _solutionFolders.Sort((a, b) =>
            {
                int nameComparison = string.Compare(a.Name, b.Name, StringComparison.InvariantCultureIgnoreCase);
                if (nameComparison != 0)
                    return nameComparison;

                return a.Guid.CompareTo(b.Guid);
            });

            foreach (SolutionFolder folder in _solutionFolders)
            {
                using (fileGenerator.Declare("folderName", folder.Name))
                using (fileGenerator.Declare("folderGuid", folder.Guid.ToString().ToUpper()))
                {
                    fileGenerator.Write(Template.Solution.ProjectFolder);
                    if (masterBffFolder == folder)
                    {
                        var bffFilesPaths = new SortedSet<string>(new FileSystemStringComparer());

                        foreach (var conf in solutionConfigurations)
                        {
                            string masterBffFilePath = conf.MasterBffFilePath + FastBuildSettings.FastBuildConfigFileExtension;
                            bffFilesPaths.Add(Util.PathGetRelative(solutionPath, masterBffFilePath));
                            bffFilesPaths.Add(Util.PathGetRelative(solutionPath, FastBuild.MasterBff.GetGlobalBffConfigFileName(masterBffFilePath)));
                        }

                        // This always needs to be created so make sure it's there.
                        bffFilesPaths.Add(solutionFile + FastBuildSettings.FastBuildConfigFileExtension);

                        fileGenerator.Write(Template.Solution.SolutionItemBegin);
                        {
                            foreach (var path in bffFilesPaths)
                            {
                                using (fileGenerator.Declare("solutionItemPath", path))
                                    fileGenerator.Write(Template.Solution.SolutionItem);
                            }
                        }
                        fileGenerator.Write(Template.Solution.ProjectSectionEnd);
                    }
                    fileGenerator.Write(Template.Solution.ProjectEnd);
                }
            }

            Solution.ResolvedProject fastBuildAllProjectForSolutionDependency = null;
            if (solution.FastBuildAllSlnDependencyFromExe)
            {
                var fastBuildAllProjects = solutionProjects.Where(p => p.Project.IsFastBuildAll).ToArray();
                if (fastBuildAllProjects.Length > 1)
                    throw new Error("More than one FastBuildAll project");
                if (fastBuildAllProjects.Length == 1)
                    fastBuildAllProjectForSolutionDependency = fastBuildAllProjects[0];
            }

            using (fileGenerator.Declare("solution", solution))
            using (fileGenerator.Declare("solutionGuid", solutionGuid))
            {
                foreach (Solution.ResolvedProject resolvedProject in solutionProjects.Concat(resolvedPathReferences).Distinct(new Solution.ResolvedProjectGuidComparer()))
                {
                    FileInfo projectFileInfo = new FileInfo(resolvedProject.ProjectFile);
                    using (fileGenerator.Declare("project", resolvedProject.Project))
                    using (fileGenerator.Declare("projectName", resolvedProject.ProjectName))
                    using (fileGenerator.Declare("projectFile", Util.PathGetRelative(solutionFileInfo.Directory.FullName, projectFileInfo.FullName)))
                    using (fileGenerator.Declare("projectGuid", resolvedProject.UserData["Guid"]))
                    using (fileGenerator.Declare("projectTypeGuid", resolvedProject.UserData["TypeGuid"]))
                    {
                        fileGenerator.Write(Template.Solution.ProjectBegin);
                        Strings buildDepsGuids = new Strings(resolvedProject.Configurations.SelectMany(
                            c => c.GenericBuildDependencies
                                .Where(dep => !dep.IsFastBuild)
                                .Select(p => p.ProjectGuid ?? ReadGuidFromProjectFile(p.ProjectFullFileNameWithExtension)
                            )
                        ));

                        if (fastBuildAllProjectForSolutionDependency != null)
                        {
                            bool writeDependencyToFastBuildAll = (resolvedProject.Configurations.Any(conf => conf.IsFastBuild && conf.Output == Project.Configuration.OutputType.Exe)) ||
                                                                 solution.ProjectsDependingOnFastBuildAllForThisSolution.Contains(resolvedProject.Project);

                            if (writeDependencyToFastBuildAll)
                                buildDepsGuids.Add(fastBuildAllProjectForSolutionDependency.UserData["Guid"] as string);
                        }

                        if (buildDepsGuids.Any())
                        {
                            fileGenerator.Write(Template.Solution.ProjectDependencyBegin);
                            foreach (string guid in buildDepsGuids.SortedValues)
                            {
                                using (fileGenerator.Declare("projectDependencyGuid", guid))
                                    fileGenerator.Write(Template.Solution.ProjectDependency);
                            }
                            fileGenerator.Write(Template.Solution.ProjectSectionEnd);
                        }
                        fileGenerator.Write(Template.Solution.ProjectEnd);
                    }
                }
            }

            // Write extra solution items
            // TODO: What happens if we define an existing folder?
            foreach (var items in solution.ExtraItems)
            {
                var folder = GetSolutionFolder(items.Key);

                using (fileGenerator.Declare("folderName", folder.Name))
                using (fileGenerator.Declare("folderGuid", folder.Guid))
                using (fileGenerator.Declare("solution", solution))
                {
                    fileGenerator.Write(Template.Solution.ProjectFolder);
                    {
                        fileGenerator.Write(Template.Solution.SolutionItemBegin);
                        foreach (string file in items.Value)
                        {
                            using (fileGenerator.Declare("solutionItemPath", Util.PathGetRelative(solutionPath, file)))
                                fileGenerator.Write(Template.Solution.SolutionItem);
                        }
                        fileGenerator.Write(Template.Solution.ProjectSectionEnd);
                    }
                    fileGenerator.Write(Template.Solution.ProjectEnd);
                }
            }

            fileGenerator.Write(Template.Solution.GlobalBegin);

            // write solution configurations
            string visualStudioExe = GetVisualStudioIdePath(devEnv) + Util.WindowsSeparator + "devenv.com";

            var configurationSectionNames = new List<string>();

            bool containsMultiDotNetFramework = solutionConfigurations.All(sc => sc.Target.HaveFragment<DotNetFramework>()) &&
                                                solutionConfigurations.Select(sc => sc.Target.GetFragment<DotNetFramework>()).Distinct().Count() > 1;

            var multiDotNetFrameworkConfigurationNames = new HashSet<string>();

            fileGenerator.Write(Template.Solution.GlobalSectionSolutionConfigurationBegin);
            foreach (Solution.Configuration solutionConfiguration in solutionConfigurations)
            {
                string configurationName;
                string category;
                if (solution.MergePlatformConfiguration)
                {
                    configurationName = solutionConfiguration.PlatformName + "-" + solutionConfiguration.Name;
                    category = "All Platforms";
                }
                else
                {
                    configurationName = solutionConfiguration.Name;
                    category = solutionConfiguration.PlatformName;
                }

                if (containsMultiDotNetFramework)
                {
                    if (multiDotNetFrameworkConfigurationNames.Contains(configurationName))
                        continue;

                    multiDotNetFrameworkConfigurationNames.Add(configurationName);
                }

                using (fileGenerator.Declare("configurationName", configurationName))
                using (fileGenerator.Declare("category", category))
                {
                    configurationSectionNames.Add(fileGenerator.Resolver.Resolve(Template.Solution.GlobalSectionSolutionConfiguration));
                }

                // set the compile command line 
                if (File.Exists(visualStudioExe))
                {
                    solutionConfiguration.CompileCommandLine = string.Format(@"""{0}"" ""{1}"" /build ""{2}|{3}""",
                        visualStudioExe, solutionFileInfo.FullName, configurationName, category);
                }
            }

            configurationSectionNames.Sort();

            VerifySectionNamesDuplicates(solutionFileInfo.FullName, solutionConfigurations, configurationSectionNames);

            foreach (string configurationSectionName in configurationSectionNames)
                fileGenerator.Write(configurationSectionName);

            fileGenerator.Write(Template.Solution.GlobalSectionSolutionConfigurationEnd);

            // write all project target and match then to a solution target
            fileGenerator.Write(Template.Solution.GlobalSectionProjectConfigurationBegin);

            var solutionConfigurationFastBuildBuilt = new Dictionary<Solution.Configuration, List<string>>();
            foreach (Solution.ResolvedProject solutionProject in solutionProjects)
            {
                if (containsMultiDotNetFramework)
                    multiDotNetFrameworkConfigurationNames.Clear();

                foreach (Solution.Configuration solutionConfiguration in solutionConfigurations)
                {
                    ITarget solutionTarget = solutionConfiguration.Target;

                    ITarget projectTarget = null;

                    Solution.Configuration.IncludedProjectInfo includedProject = solutionConfiguration.GetProject(solutionProject.Project.GetType());

                    bool perfectMatch = includedProject != null && solutionProject.Configurations.Contains(includedProject.Configuration);
                    if (perfectMatch)
                    {
                        projectTarget = includedProject.Target;
                    }
                    else
                    {
                        // try to find the target in the project that is the closest match from the solution one
                        int maxEqualFragments = 0;
                        int[] solutionTargetValues = solutionTarget.GetFragmentsValue();

                        Platform previousPlatform = Platform._reserved1;

                        foreach (var conf in solutionProject.Configurations)
                        {
                            Platform currentTargetPlatform = conf.Target.GetPlatform();

                            int[] candidateTargetValues = conf.Target.GetFragmentsValue();
                            if (solutionTargetValues.Length != candidateTargetValues.Length)
                                continue;

                            int equalFragments = 0;
                            for (int i = 0; i < solutionTargetValues.Length; ++i)
                            {
                                if ((solutionTargetValues[i] & candidateTargetValues[i]) != 0)
                                    equalFragments++;
                            }

                            if ((equalFragments == maxEqualFragments && currentTargetPlatform < previousPlatform) || equalFragments > maxEqualFragments)
                            {
                                projectTarget = conf.Target;
                                maxEqualFragments = equalFragments;
                                previousPlatform = currentTargetPlatform;
                            }
                        }

                        // last resort: if we didn't find a good enough match, fallback to TargetDefault
                        if (projectTarget == null)
                            projectTarget = solutionProject.TargetDefault;
                    }

                    Project.Configuration projectConf = solutionProject.Project.GetConfiguration(projectTarget);

                    if (includedProject != null && includedProject.Configuration.IsFastBuild)
                        solutionConfigurationFastBuildBuilt.GetValueOrAdd(solutionConfiguration, new List<string>());

                    Platform projectPlatform = projectTarget.GetPlatform();

                    string configurationName;
                    string category;
                    if (solution.MergePlatformConfiguration)
                    {
                        configurationName = solutionConfiguration.PlatformName + "-" + solutionConfiguration.Name;
                        category = "All Platforms";
                    }
                    else
                    {
                        configurationName = solutionConfiguration.Name;
                        category = solutionConfiguration.PlatformName;
                    }

                    if (containsMultiDotNetFramework && includedProject?.Project is CSharpProject)
                    {
                        if (multiDotNetFrameworkConfigurationNames.Contains(configurationName))
                            continue;

                        multiDotNetFrameworkConfigurationNames.Add(configurationName);
                    }

                    using (fileGenerator.Declare("solutionConf", solutionConfiguration))
                    using (fileGenerator.Declare("projectGuid", solutionProject.UserData["Guid"]))
                    using (fileGenerator.Declare("projectConf", projectConf))
                    using (fileGenerator.Declare("projectPlatform", Util.GetToolchainPlatformString(projectPlatform, solutionProject.Project, solutionConfiguration.Target, true)))
                    using (fileGenerator.Declare("category", category))
                    using (fileGenerator.Declare("configurationName", configurationName))
                    {
                        bool build = false;
                        bool forceDeploy = false;
                        if (solution is PythonSolution)
                        {
                            // nothing is built in python solutions
                        }
                        else if (perfectMatch)
                        {
                            build = includedProject.ToBuild == Solution.Configuration.IncludedProjectInfo.Build.Yes;
                            forceDeploy = includedProject.Project.DeployProjectType == Project.DeployType.AlwaysDeploy || includedProject.Configuration.DeployProjectType == Project.DeployType.AlwaysDeploy;

                            // for fastbuild, only build the projects that cannot be built through dependency chain
                            if (!projectConf.IsFastBuild)
                                build |= includedProject.ToBuild == Solution.Configuration.IncludedProjectInfo.Build.YesThroughDependency;
                            else
                            {
                                if (build)
                                    solutionConfigurationFastBuildBuilt[solutionConfiguration].Add(projectConf.Project.Name + " " + projectConf.Name);
                            }
                        }

                        fileGenerator.Write(Template.Solution.GlobalSectionProjectConfigurationActive);
                        bool buildDeploy = false;
                        if (build)
                        {
                            buildDeploy = includedProject.Project.DeployProjectType == Project.DeployType.OnlyIfBuild || includedProject.Configuration.DeployProjectType == Project.DeployType.OnlyIfBuild;
                            fileGenerator.Write(Template.Solution.GlobalSectionProjectConfigurationBuild);
                        }

                        if (forceDeploy || buildDeploy)
                        {
                            fileGenerator.Write(Template.Solution.GlobalSectionProjectConfigurationDeploy);
                        }
                    }
                }
            }

            foreach (var fb in solutionConfigurationFastBuildBuilt)
            {
                var solutionConfiguration = fb.Key;
                if (fb.Value.Count == 0)
                    Builder.Instance.LogErrorLine($"{solutionFile} - {solutionConfiguration.Name}|{solutionConfiguration.PlatformName} - has no FastBuild projects to build.");
                else if (solution.GenerateFastBuildAllProject && fb.Value.Count > 1)
                    Builder.Instance.LogErrorLine($"{solutionFile} - {solutionConfiguration.Name}|{solutionConfiguration.PlatformName} - has more than one FastBuild project to build ({string.Join(";", fb.Value)}).");
            }

            fileGenerator.Write(Template.Solution.GlobalSectionProjectConfigurationEnd);

            fileGenerator.Write(Template.Solution.SolutionProperties);

            // Write nested folders

            if (_solutionFolders.Count != 0)
            {
                fileGenerator.Write(Template.Solution.NestedProjectBegin);

                foreach (SolutionFolder folder in _solutionFolders)
                {
                    if (folder.Parent != null)
                    {
                        using (fileGenerator.Declare("nestedChildGuid", folder.Guid.ToString().ToUpper()))
                        using (fileGenerator.Declare("nestedParentGuid", folder.Parent.Guid.ToString().ToUpper()))
                        {
                            fileGenerator.Write(Template.Solution.NestedProjectItem);
                        }
                    }
                }

                foreach (Solution.ResolvedProject resolvedProject in solutionProjects.Concat(resolvedPathReferences))
                {
                    SolutionFolder folder = resolvedProject.UserData["Folder"] as SolutionFolder;

                    if (folder != null)
                    {
                        using (fileGenerator.Declare("nestedChildGuid", resolvedProject.UserData["Guid"].ToString().ToUpper()))
                        using (fileGenerator.Declare("nestedParentGuid", folder.Guid.ToString().ToUpper()))
                        {
                            fileGenerator.Write(Template.Solution.NestedProjectItem);
                        }
                    }
                }

                fileGenerator.Write(Template.Solution.NestedProjectEnd);
            }

            fileGenerator.Write(Template.Solution.GlobalEnd);

            // Write the solution file
            updated = _builder.Context.WriteGeneratedFile(solution.GetType(), solutionFileInfo, fileGenerator);

            solution.PostGenerationCallback?.Invoke(solutionPath, solutionFile, SolutionExtension);

            return solutionFileInfo.FullName;
        }