private static bool GenerateMasterBffFile()

in Sharpmake.Generators/FastBuild/MasterBff.cs [263:515]


        private static bool GenerateMasterBffFile(Builder builder, ConfigurationsPerBff configurationsPerBff)
        {
            configurationsPerBff.Sort();

            string masterBffFilePath = Util.GetCapitalizedPath(configurationsPerBff.BffFilePathWithExtension);
            string masterBffDirectory = Path.GetDirectoryName(masterBffFilePath);
            string masterBffFileName = Path.GetFileName(masterBffFilePath);

            // Global configuration file is in the same directory as the master bff but filename suffix added to its filename.
            string globalConfigFullPath = GetGlobalBffConfigFileName(masterBffFilePath);
            string globalConfigFileName = Path.GetFileName(globalConfigFullPath);

            var solutionProjects = configurationsPerBff.ResolvedProjects;
            if (solutionProjects.Count == 0 && configurationsPerBff.ProjectsWereFiltered)
            {
                // We are running in filter mode for submit assistant and all projects were filtered out. 
                // We need to skip generation and delete any existing master bff file.
                Util.TryDeleteFile(masterBffFilePath);
                return false;
            }

            // Start writing Bff
            var fileGenerator = new FileGenerator();

            var masterBffInfo = new MasterBffInfo();

            var bffPreBuildSection = new Dictionary<string, string>();
            var bffCustomPreBuildSection = new Dictionary<string, string>();
            var bffCopyNodes = new Dictionary<string, (string sourceFullPath, string src, string dest)>();
            var masterBffCopySections = new List<string>();
            var masterBffCustomSections = new UniqueList<string>(); // section that is not ordered

            bool mustGenerateFastbuild = false;

            var platformBffCache = new Dictionary<Platform, IPlatformBff>();

            var verificationPostBuildCopies = new Dictionary<string, string>();
            var outputsByBffAndNode = new Dictionary<string, (string sourceBff, string sourceNodeIdentifier)>(FileSystemStringComparer.Default);
            foreach (Solution.Configuration solutionConfiguration in configurationsPerBff)
            {
                foreach (var solutionProject in solutionProjects)
                {
                    var project = solutionProject.Project;

                    // Export projects do not have any bff
                    if (project.SharpmakeProjectType == Project.ProjectTypeAttribute.Export)
                        continue;

                    // When the project has a source file filter, only keep it if the file list is not empty
                    if (project.SourceFilesFilters != null && (project.SourceFilesFiltersCount == 0 || project.SkipProjectWhenFiltersActive))
                        continue;

                    Solution.Configuration.IncludedProjectInfo includedProject = solutionConfiguration.GetProject(project.GetType());
                    bool perfectMatch = includedProject != null && solutionProject.Configurations.Contains(includedProject.Configuration);
                    if (!perfectMatch)
                        continue;

                    var conf = includedProject.Configuration;
                    if (!conf.IsFastBuildEnabledProjectConfig())
                        continue;

                    mustGenerateFastbuild = true;

                    var otherConfigurationsInSameBff = project.Configurations.Where(c => conf.BffFullFileName == c.BffFullFileName);
                    foreach (var c in otherConfigurationsInSameBff)
                    {
                        IPlatformBff platformBff = platformBffCache.GetValueOrAdd(c.Platform, PlatformRegistry.Query<IPlatformBff>(c.Platform));
                        platformBff.AddCompilerSettings(masterBffInfo.CompilerSettings, c);
                    }

                    string fastBuildTargetIdentifier = Bff.GetShortProjectName(project, conf);

                    if (FastBuildSettings.WriteAllConfigsSection && includedProject.ToBuild == Solution.Configuration.IncludedProjectInfo.Build.Yes)
                        masterBffInfo.AllConfigsSections.Add(fastBuildTargetIdentifier);

                    bool isOutputTypeExe = conf.Output == Project.Configuration.OutputType.Exe;
                    bool isOutputTypeDll = conf.Output == Project.Configuration.OutputType.Dll;
                    bool isOutputTypeLib = conf.Output == Project.Configuration.OutputType.Lib;
                    bool isOutputTypeExeOrDll = isOutputTypeExe || isOutputTypeDll;

                    using (fileGenerator.Declare("conf", conf))
                    using (fileGenerator.Declare("target", conf.Target))
                    using (fileGenerator.Declare("project", conf.Project))
                    {
                        var preBuildEvents = new Dictionary<string, Project.Configuration.BuildStepBase>();
                        if (isOutputTypeExeOrDll || conf.ExecuteTargetCopy)
                        {
                            RegisterBuiltOutputsForConf(conf, fastBuildTargetIdentifier, outputsByBffAndNode);

                            var copies = ProjectOptionsGenerator.ConvertPostBuildCopiesToRelative(conf, masterBffDirectory);
                            foreach (var copy in copies)
                            {
                                var sourceFile = copy.Key;
                                var sourceFileName = Path.GetFileName(sourceFile);
                                var destinationFolder = copy.Value;
                                var destinationFile = Path.Combine(destinationFolder, sourceFileName);

                                // use the global root for alias computation, as the project has not idea in which master bff it has been included
                                var destinationRelativeToGlobal = Util.GetConvertedRelativePath(masterBffDirectory, destinationFolder, conf.Project.RootPath, true, conf.Project.RootPath);

                                string fastBuildCopyAlias = UtilityMethods.GetFastBuildCopyAlias(sourceFileName, destinationRelativeToGlobal);
                                string currentSourceFullPath = Util.PathGetAbsolute(masterBffDirectory, sourceFile);

                                if (FastBuildSettings.FastBuildValidateCopyFiles)
                                {
                                    string key = sourceFileName + destinationRelativeToGlobal;
                                    if (verificationPostBuildCopies.TryGetValue(key, out var previous))
                                    {
                                        if (FileSystemStringComparer.StaticCompare(previous, currentSourceFullPath) != 0)
                                            builder.LogErrorLine("A post-build copy to the destination '{0}' already exist but from different sources: '{1}' and '{2}'!", Util.PathGetAbsolute(masterBffDirectory, destinationFolder), previous, currentSourceFullPath);
                                    }
                                    else
                                    {
                                        verificationPostBuildCopies.Add(key, currentSourceFullPath);
                                    }
                                }

                                if (!bffCopyNodes.ContainsKey(fastBuildCopyAlias))
                                    bffCopyNodes.Add(fastBuildCopyAlias, (currentSourceFullPath, Bff.CurrentBffPathKeyCombine(sourceFile), Bff.CurrentBffPathKeyCombine(destinationFile)));
                            }
                        }

                        foreach (var eventPair in conf.EventPreBuildExecute)
                        {
                            preBuildEvents.Add(eventPair.Key, eventPair.Value);
                        }

                        string projectPath = new FileInfo(solutionProject.ProjectFile).Directory.FullName;

                        foreach (var buildEvent in conf.ResolvedEventPreBuildExe)
                        {
                            string eventKey = ProjectOptionsGenerator.MakeBuildStepName(conf, buildEvent, Vcxproj.BuildStep.PreBuild, project.RootPath, projectPath);
                            preBuildEvents.Add(eventKey, buildEvent);
                        }

                        WriteEvents(fileGenerator.Resolver, preBuildEvents, bffPreBuildSection, conf.Project.RootPath, masterBffDirectory);

                        var customPreBuildEvents = new Dictionary<string, Project.Configuration.BuildStepBase>();
                        foreach (var eventPair in conf.EventCustomPrebuildExecute)
                            customPreBuildEvents.Add(eventPair.Key, eventPair.Value);

                        foreach (var buildEvent in conf.ResolvedEventCustomPreBuildExe)
                        {
                            string eventKey = ProjectOptionsGenerator.MakeBuildStepName(conf, buildEvent, Vcxproj.BuildStep.PreBuildCustomAction, project.RootPath, projectPath);
                            customPreBuildEvents.Add(eventKey, buildEvent);
                        }

                        WriteEvents(fileGenerator.Resolver, customPreBuildEvents, bffCustomPreBuildSection, conf.Project.RootPath, masterBffDirectory);

                        if (includedProject.ToBuild == Solution.Configuration.IncludedProjectInfo.Build.Yes)
                            MergeBffIncludeTreeRecursive(conf, ref masterBffInfo.BffIncludeToDependencyIncludes);
                    }
                }
            }

            if (!mustGenerateFastbuild)
                throw new Error("Sharpmake-FastBuild : Trying to generate a MasterBff with none of its projects having a FastBuild configuration, or having a platform supporting it, or all of them having conf.DoNotGenerateFastBuild = true");

            var afterBffCopies = new Dictionary<string, List<string>>();
            foreach (var copyNode in bffCopyNodes)
            {
                bool foundTargetInBff = outputsByBffAndNode.TryGetValue(copyNode.Value.sourceFullPath, out var bffAndNode);
                using (fileGenerator.Declare("fastBuildCopyAlias", copyNode.Key))
                using (fileGenerator.Declare("fastBuildCopySource", copyNode.Value.src))
                using (fileGenerator.Declare("fastBuildCopyDest", copyNode.Value.dest))
                using (fileGenerator.Declare("fastBuildCopyDependencies", foundTargetInBff ? $"'{bffAndNode.sourceNodeIdentifier}'" : FileGeneratorUtilities.RemoveLineTag))
                {
                    string nodeContent = fileGenerator.Resolver.Resolve(Bff.Template.ConfigurationFile.CopyFileSection);

                    if (!foundTargetInBff)
                    {
                        masterBffCopySections.Add(nodeContent);
                    }
                    else
                    {
                        if (!afterBffCopies.ContainsKey(bffAndNode.sourceBff))
                            afterBffCopies.Add(bffAndNode.sourceBff, new List<string> { nodeContent });
                        else
                            afterBffCopies[bffAndNode.sourceBff].Add(nodeContent);
                    }
                }
            }

            masterBffCopySections.AddRange(bffPreBuildSection.Values);

            masterBffCustomSections.AddRange(bffCustomPreBuildSection.Values);

            GenerateMasterBffGlobalSettingsFile(builder, globalConfigFullPath, masterBffInfo);

            using (fileGenerator.Declare("fastBuildProjectName", masterBffFileName))
            using (fileGenerator.Declare("fastBuildGlobalConfigurationInclude", $"#include \"{globalConfigFileName}\""))
            {
                fileGenerator.Write(Bff.Template.ConfigurationFile.HeaderFile);
                foreach (Platform platform in platformBffCache.Keys) // kind of cheating to use that cache instead of the masterBffInfo.CompilerSettings, but it works :)
                {
                    using (fileGenerator.Declare("fastBuildDefine", Bff.GetPlatformSpecificDefine(platform)))
                        fileGenerator.Write(Bff.Template.ConfigurationFile.Define);
                }
                fileGenerator.Write(Bff.Template.ConfigurationFile.GlobalConfigurationInclude);
            }

            WriteMasterCopySection(fileGenerator, masterBffCopySections);
            WriteMasterCustomSection(fileGenerator, masterBffCustomSections);

            var result = new StringBuilder();
            foreach (var projectBffFullPath in GetMasterIncludeList(masterBffInfo.BffIncludeToDependencyIncludes))
            {
                string projectFullPath = Path.GetDirectoryName(projectBffFullPath);
                var projectPathRelativeFromMasterBff = Util.PathGetRelative(masterBffDirectory, projectFullPath, true);

                string bffKeyRelative = Path.Combine(projectPathRelativeFromMasterBff, Path.GetFileName(projectBffFullPath));

                result.AppendLine($"#include \"{bffKeyRelative}\"");

                if (afterBffCopies.TryGetValue(projectBffFullPath, out List<string> copyNodes))
                {
                    foreach (var copyNode in copyNodes)
                        result.Append(copyNode);
                    afterBffCopies.Remove(projectBffFullPath); // not necessary but just to verify that we wrote all we wanted
                }
            }

            if (afterBffCopies.Count > 0)
                throw new Error("The target source of some postbuild copies was not included in the master bff!");

            string fastBuildMasterBffDependencies = result.Length == 0 ? FileGeneratorUtilities.RemoveLineTag : result.ToString();

            using (fileGenerator.Declare("fastBuildProjectName", masterBffFileName))
            using (fileGenerator.Declare("fastBuildOrderedBffDependencies", fastBuildMasterBffDependencies))
            {
                fileGenerator.Write(Bff.Template.ConfigurationFile.Includes);
            }

            if (masterBffInfo.AllConfigsSections.Count != 0)
            {
                using (fileGenerator.Declare("fastBuildConfigs", UtilityMethods.FBuildFormatList(masterBffInfo.AllConfigsSections.SortedValues, 4)))
                {
                    fileGenerator.Write(Bff.Template.ConfigurationFile.AllConfigsSection);
                }
            }

            // remove all line that contain RemoveLineTag
            fileGenerator.RemoveTaggedLines();

            // Write master .bff file
            FileInfo bffFileInfo = new FileInfo(masterBffFilePath);
            bool updated = builder.Context.WriteGeneratedFile(null, bffFileInfo, fileGenerator);

            foreach (var confsPerSolution in configurationsPerBff)
                confsPerSolution.Solution.PostGenerationCallback?.Invoke(masterBffDirectory, Path.GetFileNameWithoutExtension(masterBffFileName), FastBuildSettings.FastBuildConfigFileExtension);

            return updated;
        }