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;
}