Sharpmake.Generators/FastBuild/Bff.cs (2,123 lines of code) (raw):
// Copyright (c) Ubisoft. All Rights Reserved.
// Licensed under the Apache 2.0 License. See LICENSE.md in the project root for license information.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Sharpmake.Generators.VisualStudio;
namespace Sharpmake.Generators.FastBuild
{
public partial class Bff : IProjectGenerator
{
private class BffGenerationContext : IBffGenerationContext
{
private Resolver _envVarResolver;
public Builder Builder { get; }
public Project Project { get; }
public Project.Configuration Configuration { get; set; }
public IReadOnlyList<Project.Configuration> ProjectConfigurations { get; }
public string ProjectDirectory { get; }
public Options.ExplicitOptions Options { get; set; } = new Options.ExplicitOptions();
public IDictionary<string, string> CommandLineOptions { get; set; } = new ProjectOptionsGenerator.VcxprojCmdLineOptions();
public DevEnv DevelopmentEnvironment => Configuration.Compiler;
public string ProjectDirectoryCapitalized { get; }
public string ProjectSourceCapitalized { get; }
public bool PlainOutput { get { return false; } }
public Resolver EnvironmentVariableResolver
{
get
{
Debug.Assert(_envVarResolver != null);
return _envVarResolver;
}
set
{
_envVarResolver = value;
}
}
public IReadOnlyDictionary<Platform, IPlatformBff> PresentPlatforms { get; }
public BffGenerationContext(Builder builder, Project project, string projectDir, IEnumerable<Project.Configuration> projectConfigurations)
{
Builder = builder;
Project = project;
ProjectDirectory = projectDir;
ProjectDirectoryCapitalized = Util.GetCapitalizedPath(projectDir);
ProjectSourceCapitalized = Util.GetCapitalizedPath(project.SourceRootPath);
ProjectConfigurations = projectConfigurations as IReadOnlyList<Project.Configuration>;
PresentPlatforms = ProjectConfigurations.Select(conf => conf.Platform).Distinct().ToDictionary(p => p, PlatformRegistry.Get<IPlatformBff>);
}
public void SelectOption(params Options.OptionAction[] options)
{
Sharpmake.Options.SelectOption(Configuration, options);
}
public void SelectOptionWithFallback(Action fallbackAction, params Options.OptionAction[] options)
{
Sharpmake.Options.SelectOptionWithFallback(Configuration, fallbackAction, options);
}
}
// NOTE: The dot slash prefix is a workaround because FastBuild sometimes resolve the current bff dir as an empty string,
// which if followed by a directory separator could create an invalid path...
// Once the bug is fixed upstream, nuke it!
public static readonly string CurrentBffPathKey = "." + Path.DirectorySeparatorChar + "$_CURRENT_BFF_DIR_$";
public static IUnityResolver UnityResolver = new HashUnityResolver();
private static ConcurrentDictionary<Project.Configuration, string> s_configurationArguments = new ConcurrentDictionary<Project.Configuration, string>(); // fastbuild arguments for a specific configuration
internal static void SetCommandLineArguments(Project.Configuration conf, string arguments)
{
s_configurationArguments.TryAdd(conf, arguments);
}
public static string GetCommandLineArguments(Project.Configuration conf)
{
string value;
s_configurationArguments.TryGetValue(conf, out value);
return value;
}
internal static string GetBffFileName(string path, string bffFileName)
{
return Path.Combine(path, bffFileName + FastBuildSettings.FastBuildConfigFileExtension);
}
public static string GetShortProjectName(Project project, Project.Configuration conf)
{
string platformString = Util.GetSimplePlatformString(conf.Platform);
if (conf.Platform != Platform.win64) // this is to reduce changes compared to old format
{
// Append the toolchain-specific platform name if it differs from the simple name,
// in order to prevent clashes between different build targets on the same platform
string vsPlatformString = Util.GetToolchainPlatformString(conf.Platform, project, conf.Target, isForSolution: false);
if (!vsPlatformString.Equals(platformString, StringComparison.OrdinalIgnoreCase))
platformString += "_" + vsPlatformString;
}
string dirtyConfigName = string.Join("_", project.Name, conf.Name, platformString);
return string.Join("_", dirtyConfigName.Split(new[] { ' ', ':', '.' }, StringSplitOptions.RemoveEmptyEntries));
}
public static string GetPlatformSpecificDefine(Platform platform)
{
string define = PlatformRegistry.Get<IPlatformBff>(platform).BffPlatformDefine;
if (define == null)
throw new NotImplementedException($"Please add {Util.GetSimplePlatformString(platform)} specific define for bff sections, ideally the same as ExplicitDefine, to get Intellisense.");
return define;
}
public static void InitializeBuilder(Builder builder)
{
}
private static ConcurrentDictionary<DevEnv, string> s_LatestTargetPlatformVersions = new ConcurrentDictionary<DevEnv, string>();
/// <summary>
/// Find the latest usable kit root
/// </summary>
/// <param name="devEnv"></param>
/// <returns></returns>
private static string GetLatestTargetPlatformVersion(DevEnv devEnv)
{
string value;
if (!s_LatestTargetPlatformVersions.TryGetValue(devEnv, out value))
{
value = FileGeneratorUtilities.RemoveLineTag;
KitsRootEnum kitsRootVersion = KitsRootPaths.GetUseKitsRootForDevEnv(devEnv);
if (kitsRootVersion != KitsRootEnum.KitsRoot81)
{
string kitRoot = KitsRootPaths.GetRoot(kitsRootVersion);
Options.Vc.General.WindowsTargetPlatformVersion[] platformVersionsEnumValues = (Options.Vc.General.WindowsTargetPlatformVersion[])Enum.GetValues(Options.Vc.General.WindowsTargetPlatformVersion.Latest.GetType());
foreach (var version in platformVersionsEnumValues.Reverse())
{
string binPath = Path.Combine(kitRoot, "bin", version.ToVersionString());
if (Directory.Exists(binPath))
{
// Stop once we found something
value = version.ToVersionString();
break;
}
}
}
s_LatestTargetPlatformVersions.TryAdd(devEnv, value);
}
return value;
}
// ===================================================================================
// BFF Generation
// ===================================================================================
[Flags]
private enum Languages
{
None = 0,
Asm = 1,
C = 2,
CPP = 4,
ObjC = 8,
ObjCPP = 16,
Swift = 32,
Nasm = 64
}
[Flags]
private enum LanguageFeatures
{
None = 0,
ConsumeWinRTExtensions = 1,
CLR = 2,
NonCLR = 4 // TODO: remove this
}
private struct SubConfig
{
public SubConfig()
{ }
public bool IsUsePrecomp = true;
public Languages Languages = Languages.None;
public LanguageFeatures LanguageFeatures = LanguageFeatures.None;
public Options.Vc.Compiler.Exceptions Exceptions = Options.Vc.Compiler.Exceptions.Disable;
}
public void Generate(
Builder builder,
Project project,
List<Project.Configuration> configurations,
string projectFile,
List<string> generatedFiles,
List<string> skipFiles
)
{
if (!FastBuildSettings.FastBuildSupportEnabled)
return;
//To make sure that all the projects are fastbuild
configurations = configurations.Where(x => x.IsFastBuild && !x.DoNotGenerateFastBuild).OrderBy(x => x.Platform).ToList();
if (!configurations.Any())
return;
Project.Configuration firstConf = configurations.First();
string projectName = firstConf.ProjectName;
string projectPath = new FileInfo(projectFile).Directory.FullName;
var context = new BffGenerationContext(builder, project, projectPath, configurations);
string projectBffFile = Bff.GetBffFileName(projectPath, firstConf.BffFileName); // TODO: bff file name could be different per conf, hence we would generate more than one file
List<Vcxproj.ProjectFile> filesInNonDefaultSection;
Dictionary<Project.Configuration, Dictionary<SubConfig, List<Vcxproj.ProjectFile>>> confSourceFiles;
using (builder.CreateProfilingScope("BffGenerator.Generate:GetGeneratedFiles"))
{
confSourceFiles = GetGeneratedFiles(context, configurations, out filesInNonDefaultSection);
}
// Generate all configuration options onces...
var options = new Dictionary<Project.Configuration, Options.ExplicitOptions>();
var cmdLineOptions = new Dictionary<Project.Configuration, ProjectOptionsGenerator.VcxprojCmdLineOptions>();
var dependenciesInfoPerConf = new Dictionary<Project.Configuration, DependenciesInfo>();
ProjectOptionsGenerator projectOptionsGen;
using (builder.CreateProfilingScope("BffGenerator.Generate:ProjectOptionsGenerator()"))
{
projectOptionsGen = new ProjectOptionsGenerator();
}
using (builder.CreateProfilingScope("BffGenerator.Generate:confs1"))
{
foreach (Project.Configuration conf in configurations)
{
context.Options = new Options.ExplicitOptions();
context.CommandLineOptions = new ProjectOptionsGenerator.VcxprojCmdLineOptions();
context.Configuration = conf;
GenerateBffOptions(projectOptionsGen, context, dependenciesInfoPerConf);
options.Add(conf, context.Options);
cmdLineOptions.Add(conf, (ProjectOptionsGenerator.VcxprojCmdLineOptions)context.CommandLineOptions);
// Validation of unsupported cases
if (conf.EventPreLink.Count > 0)
throw new Error("Sharpmake-FastBuild : Pre-Link Events not yet supported.");
if (context.Options["IgnoreImportLibrary"] == "true" && conf.ExportDllSymbols)
throw new Error("Sharpmake-FastBuild : IgnoreImportLibrary not yet supported, set ExportDllSymbols to false for similar behavior.");
if (conf.Output != Project.Configuration.OutputType.None && conf.FastBuildBlobbed)
{
ConfigureUnities(context, confSourceFiles);
}
}
}
ResolveUnities(project, projectPath);
// Start writing Bff
Resolver resolver = new Resolver();
var bffGenerator = new FileGenerator(resolver);
var bffGeneratorProject = new FileGenerator(resolver);
var bffWholeFileGenerator = new FileGenerator(resolver);
using (bffWholeFileGenerator.Declare("fastBuildProjectName", projectName))
{
bffWholeFileGenerator.Write(Template.ConfigurationFile.HeaderFile);
}
int configIndex = 0;
var allFileCustomBuild = new Dictionary<string, Project.Configuration.CustomFileBuildStepData>();
Dictionary<string, bool> confBffHasMasters = new Dictionary<string, bool>();
var configurationsToBuild = confSourceFiles.Keys.OrderBy(x => x.Platform).ToList();
foreach (Project.Configuration conf in configurationsToBuild)
{
if (!conf.Platform.IsSupportedFastBuildPlatform())
continue;
var platformBff = PlatformRegistry.Get<IPlatformBff>(conf.Platform);
var clangPlatformBff = PlatformRegistry.Query<IClangPlatformBff>(conf.Platform);
var applePlatformBff = PlatformRegistry.Query<IApplePlatformBff>(conf.Platform);
var microsoftPlatformBff = PlatformRegistry.Query<IMicrosoftPlatformBff>(conf.Platform);
var dotNetConf = Util.IsDotNet(conf);
if (conf.FastBuildMasterBffList.Any())
confBffHasMasters[conf.BffFullFileName] = true;
else
confBffHasMasters.TryAdd(conf.BffFullFileName, false);
// TODO: really not ideal, refactor and move the properties we need from it someplace else
var vcxprojPlatform = PlatformRegistry.Query<IPlatformVcxproj>(conf.Platform);
using (resolver.NewScopedParameter("conf", conf))
{
if (conf.IsBlobbed && conf.FastBuildBlobbed)
{
throw new Error("Sharpmake-FastBuild: Configuration " + conf + " is configured for blobbing by fastbuild and sharpmake. This is illegal.");
}
var confSubConfigs = confSourceFiles[conf];
ProjectOptionsGenerator.VcxprojCmdLineOptions confCmdLineOptions = cmdLineOptions[conf];
// We will need as many "sub"-libraries as subConfigs to generate the final library
int subConfigIndex = 0;
Strings subConfigObjectList = new Strings();
bool isUnity = false;
if (configIndex == 0 || configurationsToBuild[configIndex - 1].Platform != conf.Platform)
{
using (bffGenerator.Declare("fastBuildDefine", GetPlatformSpecificDefine(conf.Platform)))
bffGenerator.Write(Template.ConfigurationFile.PlatformBeginSection);
}
List<string> resourceFilesSections = new List<string>();
List<string> embeddedResourceFilesSections = new List<string>();
List<string> additionalLibs = new List<string>();
Options.ExplicitOptions confOptions = options[conf];
bool confUseLibraryDependencyInputs = Options.GetObject<Options.Vc.Linker.UseLibraryDependencyInputs>(conf) == Options.Vc.Linker.UseLibraryDependencyInputs.Enable;
string outputFile = confOptions["OutputFile"];
bool isOutputTypeExe = conf.Output == Project.Configuration.OutputType.Exe;
bool isOutputTypeAppleApp = conf.Output == Project.Configuration.OutputType.AppleApp;
bool isOutputTypeDll = conf.Output == Project.Configuration.OutputType.Dll;
bool isOutputTypeLib = conf.Output == Project.Configuration.OutputType.Lib;
bool isOutputTypeExeOrDllOrAppleApp = isOutputTypeExe || isOutputTypeDll || isOutputTypeAppleApp;
var dependenciesInfo = dependenciesInfoPerConf[conf];
OrderableStrings additionalDependencies = dependenciesInfo.AdditionalDependencies;
foreach (var subConfig in confSubConfigs.Keys)
{
var scopedOptions = new List<Options.ScopedOption>();
bool isDefaultSubConfig = s_DefaultSubConfig.Equals(subConfig);
bool isUsePrecomp = subConfig.IsUsePrecomp && conf.PrecompSource != null;
bool isCompileAsCFile = subConfig.Languages.HasFlag(Languages.C);
bool isCompileAsCPPFile = subConfig.Languages.HasFlag(Languages.CPP);
bool isCompileAsObjCFile = subConfig.Languages.HasFlag(Languages.ObjC);
bool isCompileAsObjCPPFile = subConfig.Languages.HasFlag(Languages.ObjCPP);
bool isCompileAsSwiftFile = subConfig.Languages.HasFlag(Languages.Swift);
bool isASMFileSection = subConfig.Languages.HasFlag(Languages.Asm);
bool isNASMFileSection = subConfig.Languages.HasFlag(Languages.Nasm);
bool isCompileAsCLRFile = subConfig.LanguageFeatures.HasFlag(LanguageFeatures.CLR);
bool isCompileAsNonCLRFile = subConfig.LanguageFeatures.HasFlag(LanguageFeatures.NonCLR);
bool isConsumeWinRTExtensions = subConfig.LanguageFeatures.HasFlag(LanguageFeatures.ConsumeWinRTExtensions) || (Options.GetObject<Options.Vc.Compiler.CompileAsWinRT>(conf) == Options.Vc.Compiler.CompileAsWinRT.Enable);
Options.Vc.Compiler.Exceptions exceptionsSetting = subConfig.Exceptions;
bool isFirstSubConfig = subConfigIndex == 0;
bool isLastSubConfig = subConfigIndex == confSubConfigs.Keys.Count - 1;
if (isConsumeWinRTExtensions)
{
if (isCompileAsCFile)
throw new Error("A C file cannot be marked to consume WinRT.");
isCompileAsCFile = false;
}
// For now, this will do.
if (conf.FastBuildBlobbed && isDefaultSubConfig && !isUnity)
{
isUnity = true;
}
else
{
isUnity = false;
}
var useClr = dotNetConf && !isCompileAsNonCLRFile || isCompileAsCLRFile;
var fastBuildSubConfigClrSupport = useClr ? "/clr" : FileGeneratorUtilities.RemoveLineTag;
Trace.Assert(!isCompileAsCLRFile || !isCompileAsNonCLRFile, "Sharpmake-FastBuild : a file cannot be simultaneously compiled with and without the CLR");
Strings fastBuildCompilerInputPatternList = isCompileAsCFile ? new Strings { ".c" } : project.SourceFilesCPPExtensions;
Strings fastBuildCompilerInputPatternTransformedList = new Strings(fastBuildCompilerInputPatternList.Select((s) => { return "*" + s; }));
string fastBuildCompilerInputPattern = UtilityMethods.FBuildCollectionFormat(fastBuildCompilerInputPatternTransformedList, 32);
string fastBuildPrecompiledSourceFile = FileGeneratorUtilities.RemoveLineTag;
string fastBuildCompileAsC = FileGeneratorUtilities.RemoveLineTag;
string fastBuildUnityName = isUnity ? GetUnityName(conf) : null;
switch (exceptionsSetting)
{
case Options.Vc.Compiler.Exceptions.Enable:
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "ExceptionHandling", "/EHsc"));
break;
case Options.Vc.Compiler.Exceptions.EnableWithExternC:
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "ExceptionHandling", "/EHs"));
break;
case Options.Vc.Compiler.Exceptions.EnableWithSEH:
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "ExceptionHandling", "/EHa"));
break;
}
bool isNoBlobImplicitConfig = false;
if (conf.FastBuildNoBlobStrategy == Project.Configuration.InputFileStrategy.Exclude &&
conf.IsBlobbed == false &&
conf.FastBuildBlobbed == false
)
{
if (isCompileAsCPPFile == false && isCompileAsCFile == false && !isConsumeWinRTExtensions)
{
isNoBlobImplicitConfig = true;
}
}
string fastBuildOutputFile = CurrentBffPathKeyCombine(Util.PathGetRelative(projectPath, outputFile, true));
fastBuildOutputFile = platformBff.GetOutputFilename(conf.Output, fastBuildOutputFile);
bool useObjectLists = confUseLibraryDependencyInputs;
string fastBuildOutputFileShortName = GetShortProjectName(project, conf);
var fastBuildProjectDependencies = new Strings();
var fastBuildBuildOnlyDependencies = new Strings();
var fastBuildProjectExeUtilityDependencyList = new Strings();
var fastBuildTargetSubTargets = new Strings();
bool mustGenerateLibrary = confSubConfigs.Count > 1 && !useObjectLists && isLastSubConfig && isOutputTypeLib;
if (!useObjectLists && confSubConfigs.Count > 1 && !isLastSubConfig)
{
useObjectLists = true;
}
if (isOutputTypeExeOrDllOrAppleApp)
{
var orderedProjectDeps = UtilityMethods.GetOrderedFlattenedProjectDependencies(conf, false);
foreach (var depProjConfig in orderedProjectDeps)
{
if (depProjConfig.Project == project)
throw new Error("Sharpmake-FastBuild : Project dependencies refers to itself.");
if (!conf.ResolvedDependencies.Contains(depProjConfig))
throw new Error("Sharpmake-FastBuild : dependency was not resolved.");
bool isExport = depProjConfig.Project.SharpmakeProjectType == Project.ProjectTypeAttribute.Export;
if (isExport)
continue;
if (depProjConfig.Output != Project.Configuration.OutputType.Exe &&
depProjConfig.Output != Project.Configuration.OutputType.AppleApp &&
depProjConfig.Output != Project.Configuration.OutputType.Utility)
{
string shortProjectName = GetShortProjectName(depProjConfig.Project, depProjConfig);
if (!dependenciesInfo.IgnoredLibraryNames.Contains(depProjConfig.TargetFileFullNameWithExtension))
fastBuildProjectDependencies.Add(shortProjectName + "_LibraryDependency");
if (depProjConfig.EventPostBuildExecute.Count != 0)
{
fastBuildTargetSubTargets.Add(shortProjectName);
}
}
else if (!depProjConfig.IsExcludedFromBuild)
{
fastBuildProjectExeUtilityDependencyList.Add(GetShortProjectName(depProjConfig.Project, depProjConfig));
}
}
orderedProjectDeps = UtilityMethods.GetOrderedFlattenedBuildOnlyDependencies(conf);
foreach (var depProjConfig in orderedProjectDeps)
{
if (depProjConfig.Project == project)
throw new Error("Sharpmake-FastBuild : Project dependencies refers to itself.");
bool isExport = depProjConfig.Project.SharpmakeProjectType == Project.ProjectTypeAttribute.Export;
if (isExport)
continue;
if (depProjConfig.Output != Project.Configuration.OutputType.Exe &&
depProjConfig.Output != Project.Configuration.OutputType.AppleApp &&
depProjConfig.Output != Project.Configuration.OutputType.Utility)
{
fastBuildBuildOnlyDependencies.Add(GetShortProjectName(depProjConfig.Project, depProjConfig));
}
else
{
fastBuildProjectExeUtilityDependencyList.Add(GetShortProjectName(depProjConfig.Project, depProjConfig));
}
}
}
string librarianAdditionalInputs = FileGeneratorUtilities.RemoveLineTag;
string outputType;
switch (conf.Output)
{
case Project.Configuration.OutputType.Lib:
outputType = "Library";
break;
case Project.Configuration.OutputType.Exe:
case Project.Configuration.OutputType.AppleApp:
outputType = "Executable";
break;
case Project.Configuration.OutputType.Dll:
outputType = "DLL";
break;
default:
outputType = "Unknown";
break;
}
if (confSubConfigs.Keys.Count > 1)
{
if (!isLastSubConfig)
{
fastBuildOutputFileShortName += "_" + subConfigIndex.ToString();
fastBuildOutputFile = Path.ChangeExtension(fastBuildOutputFile, null); // removes the extension
fastBuildOutputFile += "_" + subConfigIndex.ToString();
fastBuildOutputFile += vcxprojPlatform.StaticLibraryFileFullExtension;
subConfigObjectList.Add(fastBuildOutputFileShortName);
additionalLibs.Add(fastBuildOutputFileShortName + "_objects");
}
else
{
StringBuilder result = new StringBuilder();
foreach (string subConfigObject in subConfigObjectList)
{
if (!useObjectLists && conf.Output != Project.Configuration.OutputType.Dll && conf.Output != Project.Configuration.OutputType.Exe && conf.Output != Project.Configuration.OutputType.AppleApp)
fastBuildProjectDependencies.Add(subConfigObject + "_" + outputType);
else
fastBuildProjectDependencies.Add(subConfigObject + "_objects");
}
}
}
string fastBuildPCHForceInclude = FileGeneratorUtilities.RemoveLineTag;
string fastBuildCompilerPCHOptions = isUsePrecomp ? Template.ConfigurationFile.UsePrecomp : FileGeneratorUtilities.RemoveLineTag;
string fastBuildCompilerPCHOptionsClang = isUsePrecomp ? Template.ConfigurationFile.UsePrecompClang : FileGeneratorUtilities.RemoveLineTag;
string fastBuildCompilerDeoptimizeOptionClang = isCompileAsSwiftFile ? "-Onone" : "-O0";
string fastBuildLinkerOutputFile = fastBuildOutputFile;
string fastBuildStampExecutable = FileGeneratorUtilities.RemoveLineTag;
string fastBuildStampArguments = FileGeneratorUtilities.RemoveLineTag;
var postBuildEvents = new Dictionary<string, Project.Configuration.BuildStepBase>();
Strings preBuildTargets = new Strings();
var fastBuildTargetLibraryDependencies = new Strings();
{
if (isLastSubConfig) // post-build steps on the last subconfig
{
if (isOutputTypeExe || isOutputTypeAppleApp || conf.ExecuteTargetCopy)
{
if (conf.CopyDependenciesBuildStep != null)
throw new NotImplementedException("CopyDependenciesBuildStep are not supported with FastBuild");
var copies = ProjectOptionsGenerator.ConvertPostBuildCopiesToRelative(conf, projectPath);
foreach (var copy in copies)
{
var sourceFile = copy.Key;
var destinationFolder = copy.Value;
// 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(projectPath, destinationFolder, conf.Project.RootPath, true, conf.Project.RootPath);
string fastBuildCopyAlias = UtilityMethods.GetFastBuildCopyAlias(Path.GetFileName(sourceFile), destinationRelativeToGlobal);
fastBuildBuildOnlyDependencies.Add(fastBuildCopyAlias);
}
}
}
// When we have a Library/Dll/Executable section, put the prebuild dependencies there (which is the last subconfig).
// Otherwise put it on the first object list
var preBuildTargetsOnLastSubconfig = isOutputTypeExeOrDllOrAppleApp || (isOutputTypeLib && !confUseLibraryDependencyInputs);
if ((preBuildTargetsOnLastSubconfig && isLastSubConfig) || (!preBuildTargetsOnLastSubconfig && isFirstSubConfig))
{
// the pre-steps are written in the master bff, we only need to refer their aliases
preBuildTargets.AddRange(conf.EventPreBuildExecute.Select(e => e.Key));
preBuildTargets.AddRange(conf.ResolvedEventPreBuildExe.Select(e => ProjectOptionsGenerator.MakeBuildStepName(conf, e, Vcxproj.BuildStep.PreBuild, project.RootPath, projectPath)));
preBuildTargets.AddRange(conf.EventCustomPrebuildExecute.Select(e => e.Key));
preBuildTargets.AddRange(conf.ResolvedEventCustomPreBuildExe.Select(e => ProjectOptionsGenerator.MakeBuildStepName(conf, e, Vcxproj.BuildStep.PreBuildCustomAction, project.RootPath, projectPath)));
}
fastBuildTargetSubTargets.AddRange(fastBuildProjectExeUtilityDependencyList);
if (conf.Output == Project.Configuration.OutputType.Lib && useObjectLists)
{
string objectList = fastBuildOutputFileShortName + "_objects";
fastBuildTargetLibraryDependencies.Add(objectList);
fastBuildTargetSubTargets.Add(objectList);
}
else if (conf.Output == Project.Configuration.OutputType.None && project.IsFastBuildAll)
{
// filter to only get the configurations of projects that were explicitly added, not the dependencies
var minResolvedConf = conf.ResolvedPrivateDependencies.Where(x => conf.UnResolvedPrivateDependencies.ContainsKey(x.Project.GetType()));
foreach (var dep in minResolvedConf)
{
if (dep.Project.SharpmakeProjectType != Project.ProjectTypeAttribute.Export &&
dep.Output != Project.Configuration.OutputType.None &&
dep.Output != Project.Configuration.OutputType.Utility)
{
fastBuildTargetSubTargets.Add(GetShortProjectName(dep.Project, dep));
}
}
}
else
{
string targetId = fastBuildOutputFileShortName + "_" + outputType;
fastBuildTargetLibraryDependencies.Add(targetId);
fastBuildTargetSubTargets.Add(targetId);
}
if (isLastSubConfig) // post-build steps on the last subconfig
{
foreach (var eventPair in conf.EventPostBuildExecute)
{
fastBuildTargetSubTargets.Add(eventPair.Key);
postBuildEvents.Add(eventPair.Key, eventPair.Value);
}
var extraPlatformEvents = new List<Project.Configuration.BuildStepBase>();
if (!FastBuildSettings.FastBuildSupportLinkerStampList && isOutputTypeExeOrDllOrAppleApp)
extraPlatformEvents.AddRange(platformBff.GetExtraStampEvents(conf, fastBuildOutputFile).Select(step => { step.Resolve(resolver); return step; }));
extraPlatformEvents.AddRange(platformBff.GetExtraPostBuildEvents(conf, fastBuildOutputFile).Select(step => { step.Resolve(resolver); return step; }));
foreach (var buildEvent in extraPlatformEvents.Concat(conf.ResolvedEventPostBuildExe))
{
string eventKey = ProjectOptionsGenerator.MakeBuildStepName(conf, buildEvent, Vcxproj.BuildStep.PostBuild, project.RootPath, projectPath);
fastBuildTargetSubTargets.Add(eventKey);
postBuildEvents.Add(eventKey, buildEvent);
}
foreach (var eventPair in conf.EventCustomPostBuildExecute)
{
fastBuildTargetSubTargets.Add(eventPair.Key);
postBuildEvents.Add(eventPair.Key, eventPair.Value);
}
foreach (var buildEvent in conf.ResolvedEventCustomPostBuildExe)
{
string eventKey = ProjectOptionsGenerator.MakeBuildStepName(conf, buildEvent, Vcxproj.BuildStep.PostBuildCustomAction, project.RootPath, projectPath);
fastBuildTargetSubTargets.Add(eventKey);
postBuildEvents.Add(eventKey, buildEvent);
}
if (conf.PostBuildStepTest != null)
{
string eventKey = ProjectOptionsGenerator.MakeBuildStepName(conf, conf.PostBuildStepTest, Vcxproj.BuildStep.PostBuildCustomAction, project.RootPath, projectPath);
fastBuildTargetSubTargets.Add(eventKey);
postBuildEvents.Add(eventKey, conf.PostBuildStepTest);
}
}
if (conf.Output != Project.Configuration.OutputType.Dll && conf.Output != Project.Configuration.OutputType.Exe && conf.Output != Project.Configuration.OutputType.AppleApp)
{
foreach (var subConfigObject in subConfigObjectList)
{
string subTarget;
if (useObjectLists)
subTarget = subConfigObject + "_objects";
else
subTarget = subConfigObject + "_" + outputType;
if (!fastBuildTargetSubTargets.Contains(subTarget))
fastBuildTargetSubTargets.Add(subTarget);
if (!fastBuildTargetLibraryDependencies.Contains(subTarget))
fastBuildTargetLibraryDependencies.Add(subTarget);
}
}
}
if (additionalDependencies != null && additionalDependencies.Any())
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "AdditionalDependencies", string.Join($"'{Environment.NewLine} + ' ", additionalDependencies)));
else
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "AdditionalDependencies", FileGeneratorUtilities.RemoveLineTag));
string fastBuildConsumeWinRTExtension = isConsumeWinRTExtensions ? "/ZW" : FileGeneratorUtilities.RemoveLineTag;
string fastBuildUsingPlatformConfig = FileGeneratorUtilities.RemoveLineTag;
string fastBuildSourceFileType;
string clangFileLanguage = string.Empty;
if (isCompileAsCFile)
{
fastBuildUsingPlatformConfig = platformBff.CConfigName(conf);
// Do not take Cpp Language conformance into account while compiling in C
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "CppLanguageStd", FileGeneratorUtilities.RemoveLineTag));
scopedOptions.Add(new Options.ScopedOption(confOptions, "ClangCppLanguageStandard", FileGeneratorUtilities.RemoveLineTag));
// and remove the stdlib specification as well
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "StdLib", FileGeneratorUtilities.RemoveLineTag));
// MSVC
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "LanguageStandard", FileGeneratorUtilities.RemoveLineTag));
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "ClangEnableObjC_ARC", FileGeneratorUtilities.RemoveLineTag));
if (clangPlatformBff != null)
clangFileLanguage = "-x c "; // Compiler option to indicate that its a C file
fastBuildSourceFileType = "/TC";
}
else if (isCompileAsObjCFile)
{
// Do not take Cpp Language conformance into account while compiling in objc
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "CppLanguageStd", FileGeneratorUtilities.RemoveLineTag));
scopedOptions.Add(new Options.ScopedOption(confOptions, "ClangCppLanguageStandard", FileGeneratorUtilities.RemoveLineTag));
// and remove the stdlib specification as well
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "StdLib", FileGeneratorUtilities.RemoveLineTag));
// MSVC
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "LanguageStandard", FileGeneratorUtilities.RemoveLineTag));
if (clangPlatformBff != null)
clangFileLanguage = "-x objective-c ";
fastBuildUsingPlatformConfig = platformBff.CConfigName(conf);
fastBuildSourceFileType = "";
}
else if (isCompileAsObjCPPFile)
{
// Do not take C Language conformance into account while compiling in objcpp
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "CLanguageStd", FileGeneratorUtilities.RemoveLineTag));
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "ClangCLanguageStandard", FileGeneratorUtilities.RemoveLineTag));
// MSVC
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "LanguageStandard_C", FileGeneratorUtilities.RemoveLineTag));
if (clangPlatformBff != null)
clangFileLanguage = "-x objective-c++ ";
fastBuildUsingPlatformConfig = platformBff.CppConfigName(conf);
fastBuildSourceFileType = "";
}
else if (isCompileAsSwiftFile)
{
clangFileLanguage = "";
fastBuildUsingPlatformConfig = applePlatformBff.SwiftConfigName(conf);
fastBuildSourceFileType = "";
}
else
{
// Do not take C Language conformance into account while compiling in Cpp
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "CLanguageStd", FileGeneratorUtilities.RemoveLineTag));
scopedOptions.Add(new Options.ScopedOption(confOptions, "ClangCLanguageStandard", FileGeneratorUtilities.RemoveLineTag));
// MSVC
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "LanguageStandard_C", FileGeneratorUtilities.RemoveLineTag));
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "ClangEnableObjC_ARC", FileGeneratorUtilities.RemoveLineTag));
// if files are specifically c++, we need to add the language flag to make sure the compiler sees them as c++
if (isCompileAsCPPFile && clangPlatformBff != null)
clangFileLanguage = "-x c++ ";
fastBuildSourceFileType = "/TP";
fastBuildUsingPlatformConfig = platformBff.CppConfigName(conf);
}
// TODOANT: Add nasm/masm change
if (isASMFileSection)
{
fastBuildUsingPlatformConfig += Template.ConfigurationFile.MasmConfigNameSuffix;
}
if (isNASMFileSection)
{
fastBuildUsingPlatformConfig += Template.ConfigurationFile.NasmConfigNameSuffix;
}
string fastBuildCompilerExtraOptions = Template.ConfigurationFile.CPPCompilerExtraOptions;
if (isASMFileSection)
{
fastBuildCompilerExtraOptions = Template.ConfigurationFile.MasmCompilerExtraOptions;
}
if (isNASMFileSection)
{
fastBuildCompilerExtraOptions = Template.ConfigurationFile.NasmCompilerExtraOptions;
}
string fastBuildCompilerOptionsDeoptimize = FileGeneratorUtilities.RemoveLineTag;
if (!isASMFileSection && conf.FastBuildDeoptimization != Project.Configuration.DeoptimizationWritableFiles.NoDeoptimization)
fastBuildCompilerOptionsDeoptimize = Template.ConfigurationFile.CPPCompilerOptionsDeoptimize;
string compilerOptions = Template.ConfigurationFile.CompilerOptionsCPP;
if (isASMFileSection)
{
compilerOptions = Template.ConfigurationFile.CompilerOptionsMasm;
}
if (isNASMFileSection)
{
compilerOptions = Template.ConfigurationFile.CompilerOptionsNasm;
}
compilerOptions += Template.ConfigurationFile.CompilerOptionsCommon;
string compilerOptionsClang = Template.ConfigurationFile.CompilerOptionsClang;
if (isNASMFileSection)
{
compilerOptionsClang = Template.ConfigurationFile.CompilerOptionsNasm;
}
compilerOptionsClang += Template.ConfigurationFile.CompilerOptionsCommon;
string fastBuildDeoptimizationWritableFiles = null;
string fastBuildDeoptimizationWritableFilesWithToken = null;
Project.Configuration.DeoptimizationWritableFiles deoptimizeSetting = conf.FastBuildDeoptimization;
if (isASMFileSection)
deoptimizeSetting = Project.Configuration.DeoptimizationWritableFiles.NoDeoptimization;
switch (deoptimizeSetting)
{
case Project.Configuration.DeoptimizationWritableFiles.DeoptimizeWritableFiles:
fastBuildDeoptimizationWritableFiles = "true";
fastBuildDeoptimizationWritableFilesWithToken = FileGeneratorUtilities.RemoveLineTag;
break;
case Project.Configuration.DeoptimizationWritableFiles.DeoptimizeWritableFilesWithToken:
fastBuildDeoptimizationWritableFiles = FileGeneratorUtilities.RemoveLineTag;
fastBuildDeoptimizationWritableFilesWithToken = "true";
break;
default:
fastBuildDeoptimizationWritableFiles = FileGeneratorUtilities.RemoveLineTag;
fastBuildDeoptimizationWritableFilesWithToken = FileGeneratorUtilities.RemoveLineTag;
break;
}
string fastBuildCompilerForceUsing = FileGeneratorUtilities.RemoveLineTag;
string fastBuildAdditionalCompilerOptionsFromCode = FileGeneratorUtilities.RemoveLineTag;
if (conf.ReferencesByPath.Count > 0) // only ref by path supported
{
fastBuildAdditionalCompilerOptionsFromCode = "";
foreach (var refByPath in conf.ReferencesByPath)
{
string refByPathCopy = refByPath;
if (ShouldMakePathRelative(refByPath, context.Project))
refByPathCopy = CurrentBffPathKeyCombine(Util.PathGetRelative(context.ProjectDirectory, refByPath));
fastBuildAdditionalCompilerOptionsFromCode += "/FU\"" + refByPathCopy + "\" ";
}
}
string llvmClangCompilerOptions = null;
if (!isConsumeWinRTExtensions)
{
var platformToolset = Options.GetObject<Options.Vc.General.PlatformToolset>(conf);
if (platformToolset.IsLLVMToolchain() && Options.GetObject<Options.Vc.LLVM.UseClangCl>(context.Configuration) == Options.Vc.LLVM.UseClangCl.Enable)
{
switch (platformToolset)
{
case Options.Vc.General.PlatformToolset.LLVM:
case Options.Vc.General.PlatformToolset.ClangCL:
llvmClangCompilerOptions = "-m64"; // -m$(PlatformArchitecture)
fastBuildPCHForceInclude = @"/FI""[cmdLineOptions.PrecompiledHeaderThrough]""";
// <!-- Set the value of _MSC_VER and _MSC_FULL_VER to claim for compatibility -->
Project.Configuration.FastBuildClangMscVersionDetectionType detectionType = conf.FastBuildClangMscVersionDetectionInfo;
string overridenMscVer = Options.GetString<Options.Clang.Compiler.MscVersion>(conf);
Options.Vc.General.PlatformToolset overridenPlatformToolset = Options.Vc.General.PlatformToolset.Default;
Options.WithArgOption<Options.Vc.General.PlatformToolset>.Get<Options.Clang.Compiler.LLVMVcPlatformToolset>(conf, ref overridenPlatformToolset);
CompilerVersionForClangCl detectedVersion = DetectCompilerVersionForClangCl(
detectionType, overridenMscVer, overridenPlatformToolset, context.DevelopmentEnvironment, conf.Target.GetPlatform());
switch (detectedVersion.versionType)
{
case CompilerVersionForClangClType.MscVersion :
llvmClangCompilerOptions += string.Format(" -fmsc-version={0}", detectedVersion.mscVersion);
break;
case CompilerVersionForClangClType.MsCompatibilityVersion :
llvmClangCompilerOptions += string.Format(" -fms-compatibility-version={0}.{1}.{2}", detectedVersion.msCompatibilityVersion.Major, detectedVersion.msCompatibilityVersion.Minor, detectedVersion.msCompatibilityVersion.Build);
break;
}
break;
}
}
}
if (!string.IsNullOrEmpty(llvmClangCompilerOptions))
{
if (fastBuildAdditionalCompilerOptionsFromCode == FileGeneratorUtilities.RemoveLineTag)
fastBuildAdditionalCompilerOptionsFromCode = llvmClangCompilerOptions;
else
fastBuildAdditionalCompilerOptionsFromCode += " " + llvmClangCompilerOptions;
}
// c1xx: warning C4199: two-phase name lookup is not supported for C++/CLI, C++/CX, or OpenMP; use /Zc:twoPhase-
if (isConsumeWinRTExtensions && context.DevelopmentEnvironment >= DevEnv.vs2017)
{
if (conf.AdditionalCompilerOptions.Contains("/permissive-") && !conf.AdditionalCompilerOptions.Contains("/Zc:twoPhase-"))
{
if (fastBuildAdditionalCompilerOptionsFromCode == FileGeneratorUtilities.RemoveLineTag)
fastBuildAdditionalCompilerOptionsFromCode = "/Zc:twoPhase-";
else
fastBuildAdditionalCompilerOptionsFromCode += " /Zc:twoPhase-";
}
}
if (conf.ReferencesByName.Count > 0)
{
throw new Exception("Use ReferencesByPath instead of ReferencesByName for FastBuild support; ");
}
if (conf.ForceUsingDependencies.Any() || conf.DependenciesForceUsingFiles.Any() || conf.ForceUsingFiles.Any())
{
StringBuilder builderForceUsingFiles = new StringBuilder();
foreach (var fuConfig in conf.ForceUsingDependencies)
{
builderForceUsingFiles.AppendFormat(@" /FU""{0}.dll""", fuConfig.TargetFileFullName);
}
foreach (var f in conf.ForceUsingFiles.Union(conf.DependenciesForceUsingFiles))
{
string file = f;
if (ShouldMakePathRelative(f, context.Project))
file = CurrentBffPathKeyCombine(Util.PathGetRelative(context.ProjectDirectory, f));
builderForceUsingFiles.AppendFormat(@" /FU""{0}""", file);
}
fastBuildCompilerForceUsing = builderForceUsingFiles.ToString();
}
if (isOutputTypeExeOrDllOrAppleApp)
{
var extraPlatformEvents = new List<Project.Configuration.BuildStepExecutable>();
if (FastBuildSettings.FastBuildSupportLinkerStampList)
extraPlatformEvents.AddRange(platformBff.GetExtraStampEvents(conf, fastBuildOutputFile).Select(step => { step.Resolve(resolver); return step; }));
if (conf.PostBuildStampExe != null || conf.PostBuildStampExes.Any() || extraPlatformEvents.Any())
{
var fastbuildStampExecutableList = new List<string>();
var fastBuildStampArgumentsList = new List<string>();
foreach (var stampExe in extraPlatformEvents.Concat(conf.PostBuildStampExes.Prepend(conf.PostBuildStampExe)).Where(x => x != null))
{
fastbuildStampExecutableList.Add(CurrentBffPathKeyCombine(Util.PathGetRelative(projectPath, stampExe.ExecutableFile, true)));
fastBuildStampArgumentsList.Add(string.Format("{0} {1} {2}",
stampExe.ExecutableInputFileArgumentOption,
stampExe.ExecutableOutputFileArgumentOption,
stampExe.ExecutableOtherArguments));
}
fastBuildStampExecutable = UtilityMethods.FBuildFormatList(fastbuildStampExecutableList, 30);
fastBuildStampArguments = UtilityMethods.FBuildFormatList(fastBuildStampArgumentsList, 30);
}
}
bool linkObjects = false;
if (isOutputTypeExeOrDllOrAppleApp)
{
linkObjects = confUseLibraryDependencyInputs;
}
Strings fullInputPaths = new Strings();
string fastBuildInputPath = FileGeneratorUtilities.RemoveLineTag;
string fastBuildInputExcludedFiles = FileGeneratorUtilities.RemoveLineTag;
{
Strings excludedSourceFiles = new Strings();
if (isNoBlobImplicitConfig && isDefaultSubConfig)
{
fullInputPaths.Add(context.ProjectSourceCapitalized);
fullInputPaths.AddRange(project.AdditionalSourceRootPaths.Select(Util.GetCapitalizedPath));
excludedSourceFiles.AddRange(filesInNonDefaultSection.Select(f => f.FileName));
}
if (isDefaultSubConfig && conf.FastBuildBlobbingStrategy == Project.Configuration.InputFileStrategy.Exclude && conf.FastBuildBlobbed)
{
// Adding the folders excluded from unity to the folders to build without unity(building each file individually)
fullInputPaths.AddRange(project.SourcePathsBlobExclude.Select(Util.GetCapitalizedPath));
}
if (project.SourceFilesFiltersRegex.Count == 0)
{
var relativePaths = new Strings(fullInputPaths.Select(p => CurrentBffPathKeyCombine(Util.PathGetRelative(context.ProjectDirectory, p, true))));
fastBuildInputPath = UtilityMethods.FBuildCollectionFormat(relativePaths, 32);
}
else
{
fullInputPaths.Clear();
}
excludedSourceFiles.AddRange(conf.ResolvedSourceFilesBuildExclude);
excludedSourceFiles.AddRange(conf.PrecompSourceExclude);
// Converting the excluded filenames to relative path to the input path so that this
// can work properly with subst usage when running with fastbuild caching active.
//
// Also exclusion checks in fastbuild assume that the exclusion filenames are
// relative to the .UnityInputPath and checks that paths are ending with the specified
// path which means that any filename starting with a .. will never be excluded by fastbuild.
//
// Note: Ideally fastbuild should expect relative paths to the bff file path instead of the .UnityInputPath but
// well I guess we are stuck with this.
var excludedSourceFilesRelative = new Strings();
foreach (string file in excludedSourceFiles.SortedValues)
{
string fileExtension = Path.GetExtension(file);
if (project.SourceFilesCompileExtensions.Contains(fileExtension))
{
if (IsFileInInputPathList(fullInputPaths, file))
excludedSourceFilesRelative.Add(CurrentBffPathKeyCombine(Util.PathGetRelative(context.ProjectDirectoryCapitalized, file)));
}
}
if (excludedSourceFilesRelative.Count > 0)
{
Strings includedExtensions = isCompileAsCFile ? new Strings { ".c" } : project.SourceFilesCPPExtensions;
fastBuildInputExcludedFiles = UtilityMethods.FBuildCollectionFormat(excludedSourceFilesRelative, 34, includedExtensions);
}
}
bool projectHasResourceFiles = false;
bool projectHasEmbeddedResources = false;
string fastBuildSourceFiles = FileGeneratorUtilities.RemoveLineTag;
string fastBuildResourceFiles = FileGeneratorUtilities.RemoveLineTag;
string fastBuildEmbeddedResources = FileGeneratorUtilities.RemoveLineTag;
string fastBuildEmbeddedOutputPrefix = conf.EmbeddedResourceOutputPrefix;
{
List<string> fastbuildSourceFilesList = new List<string>();
List<string> fastbuildResourceFilesList = new List<string>();
List<string> fastbuildEmbeddedResourceFilesList = new List<string>();
var sourceFiles = confSubConfigs[subConfig];
foreach (var sourceFile in sourceFiles)
{
string sourceFileName = CurrentBffPathKeyCombine(sourceFile.FileNameProjectRelative);
if (isUsePrecomp && conf.PrecompSource != null && sourceFile.FileName.EndsWith(conf.PrecompSource, StringComparison.OrdinalIgnoreCase))
{
fastBuildPrecompiledSourceFile = sourceFileName;
}
else if (string.Compare(sourceFile.FileExtension, ".rc", StringComparison.OrdinalIgnoreCase) == 0)
{
if (microsoftPlatformBff != null && microsoftPlatformBff.SupportsResourceFiles)
{
fastbuildResourceFilesList.Add(sourceFileName);
projectHasResourceFiles = true;
}
}
else if (string.Compare(sourceFile.FileExtension, ".resx", StringComparison.OrdinalIgnoreCase) == 0)
{
if (microsoftPlatformBff != null && microsoftPlatformBff.SupportsResourceFiles)
{
fastbuildEmbeddedResourceFilesList.Add(sourceFileName);
projectHasEmbeddedResources = true;
}
}
else
{
bool isSourceFileExtension = project.SourceFilesCompileExtensions.Contains(sourceFile.FileExtension);
bool isBlobbed = project.SourceFilesBlobExtensions.Contains(sourceFile.FileExtension);
if ((isSourceFileExtension && !isBlobbed) ||
conf.ResolvedSourceFilesBlobExclude.Contains(sourceFile.FileName) ||
isNoBlobImplicitConfig ||
!isUnity)
{
if (!IsFileInInputPathList(fullInputPaths, sourceFile.FileName))
fastbuildSourceFilesList.Add(sourceFileName);
}
}
}
fastBuildSourceFiles = UtilityMethods.FBuildFormatList(fastbuildSourceFilesList, 32);
fastBuildResourceFiles = UtilityMethods.FBuildFormatList(fastbuildResourceFilesList, 30);
fastBuildEmbeddedResources = UtilityMethods.FBuildFormatList(fastbuildEmbeddedResourceFilesList, 30);
}
var fileCustomBuildKeys = new Strings();
UtilityMethods.WriteConfigCustomBuildStepsAsGenericExecutable(context.ProjectDirectoryCapitalized, bffGenerator, context.Project, conf,
key =>
{
if (!allFileCustomBuild.TryGetValue(key.Description, out var alreadyRegistered))
{
allFileCustomBuild.Add(key.Description, key);
bffGenerator.Write(Template.ConfigurationFile.GenericExecutableSection);
}
else if (key.Executable != alreadyRegistered.Executable ||
key.KeyInput != alreadyRegistered.KeyInput ||
key.Output != alreadyRegistered.Output ||
key.ExecutableArguments != alreadyRegistered.ExecutableArguments)
{
throw new Exception(string.Format("Command key '{0}' duplicates another command. Command is:\n{1}", key, bffGenerator.Resolver.Resolve(Template.ConfigurationFile.GenericExecutableSection)));
}
fileCustomBuildKeys.Add(key.Description);
return false;
});
Strings fastBuildPreBuildDependencies = new Strings();
var orderedForceUsingDeps = UtilityMethods.GetOrderedFlattenedProjectDependencies(conf, false, true);
fastBuildPreBuildDependencies.AddRange(orderedForceUsingDeps.Select(dep => GetShortProjectName(dep.Project, dep)));
// fastBuildBuildOnlyDependencies only gets added to exe/dll sections.
// Add the prebuild steps to fastBuildPreBuildDependencies if we are building a lib
if (isOutputTypeExeOrDllOrAppleApp)
{
fastBuildBuildOnlyDependencies.AddRange(preBuildTargets);
fastBuildBuildOnlyDependencies.AddRange(fileCustomBuildKeys);
}
else if (isOutputTypeLib)
{
fastBuildPreBuildDependencies.AddRange(preBuildTargets);
if (isLastSubConfig)
fastBuildPreBuildDependencies.AddRange(fileCustomBuildKeys);
}
if (projectHasResourceFiles)
resourceFilesSections.Add(fastBuildOutputFileShortName + "_resources");
if (projectHasEmbeddedResources)
{
embeddedResourceFilesSections.Add(fastBuildOutputFileShortName + "_embedded");
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "EmbedResources", "/ASSEMBLYRESOURCE:\"%3\""));
}
else
{
scopedOptions.Add(new Options.ScopedOption(confCmdLineOptions, "EmbedResources", FileGeneratorUtilities.RemoveLineTag));
}
if (mustGenerateLibrary)
{
librarianAdditionalInputs = UtilityMethods.FBuildFormatList(additionalLibs, 33);
}
// It is useless to have an input pattern defined if there is no input path
if (fastBuildInputPath == FileGeneratorUtilities.RemoveLineTag)
fastBuildCompilerInputPattern = FileGeneratorUtilities.RemoveLineTag;
fastBuildProjectDependencies.AddRange(resourceFilesSections);
fastBuildProjectDependencies.Add("[fastBuildOutputFileShortName]_objects");
string fastBuildObjectListEmbeddedResources = FormatListPartForTag(embeddedResourceFilesSections, 32, true);
string fastBuildInputFilesRootPath = FileGeneratorUtilities.RemoveLineTag;
if (conf.FastBuildInputFilesRootPath != null)
{
fastBuildInputFilesRootPath = CurrentBffPathKeyCombine(Util.PathGetRelative(context.ProjectDirectory, conf.FastBuildInputFilesRootPath));
}
using (bffGenerator.Declare("conf", conf))
using (bffGenerator.Declare("project", project))
using (bffGenerator.Declare("target", conf.Target))
{
switch (conf.Output)
{
case Project.Configuration.OutputType.Lib:
case Project.Configuration.OutputType.Exe:
case Project.Configuration.OutputType.AppleApp:
case Project.Configuration.OutputType.Dll:
using (bffGenerator.Declare("$(ProjectName)", projectName))
using (bffGenerator.Declare("options", confOptions))
using (bffGenerator.Declare("cmdLineOptions", confCmdLineOptions))
using (bffGenerator.Declare("fastBuildUsingPlatformConfig", "Using( " + fastBuildUsingPlatformConfig + " )"))
using (bffGenerator.Declare("fastBuildProjectName", projectName))
using (bffGenerator.Declare("fastBuildClrSupport", fastBuildSubConfigClrSupport))
using (bffGenerator.Declare("fastBuildOutputFileShortName", fastBuildOutputFileShortName))
using (bffGenerator.Declare("fastBuildOutputFile", fastBuildOutputFile))
using (bffGenerator.Declare("fastBuildLinkerOutputFile", fastBuildLinkerOutputFile))
using (bffGenerator.Declare("fastBuildLinkerLinkObjects", linkObjects ? "true" : "false"))
using (bffGenerator.Declare("fastBuildInputPath", fastBuildInputPath))
using (bffGenerator.Declare("fastBuildCompilerInputPattern", fastBuildCompilerInputPattern))
using (bffGenerator.Declare("fastBuildInputExcludedFiles", fastBuildInputExcludedFiles))
using (bffGenerator.Declare("fastBuildSourceFiles", fastBuildSourceFiles))
using (bffGenerator.Declare("fastBuildResourceFiles", fastBuildResourceFiles))
using (bffGenerator.Declare("fastBuildEmbeddedResources", fastBuildEmbeddedResources))
using (bffGenerator.Declare("fastBuildPrecompiledSourceFile", fastBuildPrecompiledSourceFile))
using (bffGenerator.Declare("fastBuildProjectDependencies", UtilityMethods.FBuildFormatList(fastBuildProjectDependencies.Values, 30)))
using (bffGenerator.Declare("fastBuildBuildOnlyDependencies", UtilityMethods.FBuildFormatList(fastBuildBuildOnlyDependencies.Values, 30)))
using (bffGenerator.Declare("fastBuildPreBuildTargets", UtilityMethods.FBuildFormatList(fastBuildPreBuildDependencies.Values, 28)))
using (bffGenerator.Declare("fastBuildObjectListEmbeddedResources", fastBuildObjectListEmbeddedResources))
using (bffGenerator.Declare("fastBuildCompilerPCHOptions", fastBuildCompilerPCHOptions))
using (bffGenerator.Declare("fastBuildCompilerPCHOptionsClang", fastBuildCompilerPCHOptionsClang))
using (bffGenerator.Declare("fastBuildCompilerDeoptimizeOptionClang", fastBuildCompilerDeoptimizeOptionClang))
using (bffGenerator.Declare("fastBuildPCHForceInclude", isUsePrecomp ? fastBuildPCHForceInclude : FileGeneratorUtilities.RemoveLineTag))
using (bffGenerator.Declare("fastBuildConsumeWinRTExtension", fastBuildConsumeWinRTExtension))
using (bffGenerator.Declare("fastBuildOutputType", outputType))
using (bffGenerator.Declare("fastBuildLibrarianAdditionalInputs", librarianAdditionalInputs))
using (bffGenerator.Declare("fastBuildCompileAsC", fastBuildCompileAsC))
using (bffGenerator.Declare("fastBuildUnityName", fastBuildUnityName ?? FileGeneratorUtilities.RemoveLineTag))
using (bffGenerator.Declare("fastBuildInputFilesRootPath", fastBuildInputFilesRootPath))
using (bffGenerator.Declare("fastBuildClangFileLanguage", clangFileLanguage))
using (bffGenerator.Declare("fastBuildDeoptimizationWritableFiles", fastBuildDeoptimizationWritableFiles))
using (bffGenerator.Declare("fastBuildDeoptimizationWritableFilesWithToken", fastBuildDeoptimizationWritableFilesWithToken))
using (bffGenerator.Declare("fastBuildCompilerForceUsing", fastBuildCompilerForceUsing))
using (bffGenerator.Declare("fastBuildSourceFileType", fastBuildSourceFileType))
using (bffGenerator.Declare("fastBuildAdditionalCompilerOptionsFromCode", fastBuildAdditionalCompilerOptionsFromCode))
using (bffGenerator.Declare("fastBuildStampExecutable", fastBuildStampExecutable))
using (bffGenerator.Declare("fastBuildStampArguments", fastBuildStampArguments))
using (bffGenerator.Declare("fastBuildEmbeddedOutputPrefix", fastBuildEmbeddedOutputPrefix))
using (bffGenerator.Declare("fastbuildConcurrencyGroupName", conf.FastBuildLinkConcurrencyGroup ?? FileGeneratorUtilities.RemoveLineTag))
{
if (projectHasResourceFiles)
{
bffGenerator.Write(Template.ConfigurationFile.ResourcesBeginSection);
bffGenerator.Write(Template.ConfigurationFile.ResourceCompilerExtraOptions);
bffGenerator.Write(Template.ConfigurationFile.ResourceCompilerOptions);
bffGenerator.Write(Template.ConfigurationFile.EndSection);
}
if (projectHasEmbeddedResources)
{
// Only declare the compiler here to avoid potential exceptions caused by GetFragment in targets without a .Net framework
using (bffGenerator.Declare("fastBuildEmbeddedResourceCompiler", KitsRootPaths.GetNETFXToolsDir(conf.Target.GetFragment<DotNetFramework>()) + "resgen.exe"))
{
bffGenerator.Write(Template.ConfigurationFile.EmbeddedResourcesBeginSection);
bffGenerator.Write(Template.ConfigurationFile.EmbeddedResourceCompilerOptions);
bffGenerator.Write(Template.ConfigurationFile.EndSection);
}
}
// Exe, DLL and AppleApp will always add an extra objectlist
if (isOutputTypeExeOrDllOrAppleApp && isLastSubConfig // only last subconfig will generate objectlist
)
{
bffGenerator.Write(Template.ConfigurationFile.ObjectListBeginSection);
if (conf.Platform.IsMicrosoft())
{
bffGenerator.Write(fastBuildCompilerExtraOptions);
bffGenerator.Write(Template.ConfigurationFile.CPPCompilerOptimizationOptions);
if (isUsePrecomp)
bffGenerator.Write(Template.ConfigurationFile.PCHOptions);
bffGenerator.Write(compilerOptions);
if (conf.FastBuildDeoptimization != Project.Configuration.DeoptimizationWritableFiles.NoDeoptimization)
{
if (isUsePrecomp)
bffGenerator.Write(Template.ConfigurationFile.PCHOptionsDeoptimize);
bffGenerator.Write(fastBuildCompilerOptionsDeoptimize);
bffGenerator.Write(Template.ConfigurationFile.DeOptimizeOption);
}
}
else
{
if (isCompileAsSwiftFile)
applePlatformBff?.SetupSwiftOptions(bffGenerator);
else if (!isNASMFileSection)
clangPlatformBff?.SetupClangOptions(bffGenerator); // TODO: This checks twice if the platform supports Clang -- fix?
else
bffGenerator.Write(fastBuildCompilerExtraOptions);
if (conf.Platform.IsUsingClang())
{
if (isUsePrecomp)
bffGenerator.Write(Template.ConfigurationFile.PCHOptionsClang);
bffGenerator.Write(compilerOptionsClang);
if (conf.FastBuildDeoptimization != Project.Configuration.DeoptimizationWritableFiles.NoDeoptimization)
{
if (isUsePrecomp)
bffGenerator.Write(Template.ConfigurationFile.PCHOptionsDeoptimize);
bffGenerator.Write(Template.ConfigurationFile.ClangCompilerOptionsDeoptimize);
bffGenerator.Write(Template.ConfigurationFile.DeOptimizeOption);
}
}
}
if (fastBuildPreBuildDependencies.Any())
bffGenerator.Write(Template.ConfigurationFile.PreBuildDependencies);
bffGenerator.Write(Template.ConfigurationFile.EndSection);
}
if (isOutputTypeDll && !isLastSubConfig)
{
bffGenerator.Write(Template.ConfigurationFile.ObjectListBeginSection);
if (conf.Platform.IsMicrosoft())
{
bffGenerator.Write(fastBuildCompilerExtraOptions);
bffGenerator.Write(Template.ConfigurationFile.CPPCompilerOptimizationOptions);
if (isUsePrecomp)
bffGenerator.Write(Template.ConfigurationFile.PCHOptions);
bffGenerator.Write(compilerOptions);
if (conf.FastBuildDeoptimization != Project.Configuration.DeoptimizationWritableFiles.NoDeoptimization)
{
if (isUsePrecomp)
bffGenerator.Write(Template.ConfigurationFile.PCHOptionsDeoptimize);
bffGenerator.Write(fastBuildCompilerOptionsDeoptimize);
bffGenerator.Write(Template.ConfigurationFile.DeOptimizeOption);
}
}
else
{
if (isCompileAsSwiftFile)
applePlatformBff?.SetupSwiftOptions(bffGenerator);
else if (!isNASMFileSection)
clangPlatformBff?.SetupClangOptions(bffGenerator); // TODO: This checks twice if the platform supports Clang -- fix?
else
bffGenerator.Write(fastBuildCompilerExtraOptions);
if (conf.Platform.IsUsingClang())
{
if (isUsePrecomp)
bffGenerator.Write(Template.ConfigurationFile.PCHOptionsClang);
bffGenerator.Write(compilerOptionsClang);
if (conf.FastBuildDeoptimization != Project.Configuration.DeoptimizationWritableFiles.NoDeoptimization)
{
if (isUsePrecomp)
bffGenerator.Write(Template.ConfigurationFile.PCHOptionsDeoptimize);
bffGenerator.Write(Template.ConfigurationFile.ClangCompilerOptionsDeoptimize);
bffGenerator.Write(Template.ConfigurationFile.DeOptimizeOption);
}
}
// TODO: Add BFF generation for Win64 on Windows/Mac/Linux?
}
if (fastBuildPreBuildDependencies.Any())
bffGenerator.Write(Template.ConfigurationFile.PreBuildDependencies);
bffGenerator.Write(Template.ConfigurationFile.EndSection);
}
else
{
bool outputLib = false;
string beginSectionType = null;
switch (conf.Output)
{
case Project.Configuration.OutputType.AppleApp:
case Project.Configuration.OutputType.Exe:
{
if (isLastSubConfig)
{
beginSectionType = Template.ConfigurationFile.ExeDllBeginSection;
}
else
{
// in the case the lib has the flag force to be an objectlist, change the template
if (useObjectLists)
beginSectionType = Template.ConfigurationFile.ObjectListBeginSection;
else
beginSectionType = Template.ConfigurationFile.LibBeginSection;
outputLib = true;
}
}
break;
case Project.Configuration.OutputType.Dll:
{
beginSectionType = Template.ConfigurationFile.ExeDllBeginSection;
}
break;
case Project.Configuration.OutputType.Lib:
{
// in the case the lib has the flag force to be an objectlist, change the template
if (useObjectLists)
beginSectionType = Template.ConfigurationFile.ObjectListBeginSection;
else
beginSectionType = Template.ConfigurationFile.LibBeginSection;
outputLib = true;
}
break;
}
bffGenerator.Write(beginSectionType);
if (outputLib)
{
if (conf.Platform.IsMicrosoft())
{
bffGenerator.Write(fastBuildCompilerExtraOptions);
bffGenerator.Write(Template.ConfigurationFile.CPPCompilerOptimizationOptions);
if (isUsePrecomp)
bffGenerator.Write(Template.ConfigurationFile.PCHOptions);
bffGenerator.Write(compilerOptions);
if (!useObjectLists)
{
bffGenerator.Write(Template.ConfigurationFile.LibrarianAdditionalInputs);
bffGenerator.Write(Template.ConfigurationFile.LibrarianOptions);
}
if (conf.FastBuildDeoptimization != Project.Configuration.DeoptimizationWritableFiles.NoDeoptimization)
{
if (isUsePrecomp)
bffGenerator.Write(Template.ConfigurationFile.PCHOptionsDeoptimize);
bffGenerator.Write(fastBuildCompilerOptionsDeoptimize);
bffGenerator.Write(Template.ConfigurationFile.DeOptimizeOption);
}
}
else
{
if (isCompileAsSwiftFile)
applePlatformBff?.SetupSwiftOptions(bffGenerator);
else if (!isNASMFileSection)
clangPlatformBff?.SetupClangOptions(bffGenerator); // TODO: This checks twice if the platform supports Clang -- fix?
else
bffGenerator.Write(fastBuildCompilerExtraOptions);;
if (conf.Platform.IsUsingClang())
{
if (isUsePrecomp)
bffGenerator.Write(Template.ConfigurationFile.PCHOptionsClang);
bffGenerator.Write(compilerOptionsClang);
if (conf.FastBuildDeoptimization != Project.Configuration.DeoptimizationWritableFiles.NoDeoptimization)
{
if (isUsePrecomp)
bffGenerator.Write(Template.ConfigurationFile.PCHOptionsDeoptimize);
bffGenerator.Write(Template.ConfigurationFile.ClangCompilerOptionsDeoptimize);
bffGenerator.Write(Template.ConfigurationFile.DeOptimizeOption);
}
if (!useObjectLists)
{
bffGenerator.Write(Template.ConfigurationFile.LibrarianAdditionalInputs);
bffGenerator.Write(Template.ConfigurationFile.LibrarianOptionsClang);
}
}
}
if (fastBuildPreBuildDependencies.Any())
bffGenerator.Write(Template.ConfigurationFile.PreBuildDependencies);
}
else
{
platformBff.SetupExtraLinkerSettings(bffGenerator, conf, fastBuildOutputFile);
}
bffGenerator.Write(Template.ConfigurationFile.EndSection);
// Resolve node name of the prebuild dependency for PostBuildEvents.
string resolvedSectionNodeIdentifier;
if (beginSectionType == Template.ConfigurationFile.ObjectListBeginSection)
{
resolvedSectionNodeIdentifier = resolver.Resolve("[fastBuildOutputFileShortName]_objects");
}
else
{
resolvedSectionNodeIdentifier = resolver.Resolve("[fastBuildOutputFileShortName]_[fastBuildOutputType]");
}
// Convert build steps to Bff resolvable objects
var resolvableBuildSteps = UtilityMethods.GetBffNodesFromBuildSteps(postBuildEvents, new Strings(resolvedSectionNodeIdentifier));
// Resolve objects using the current project path
var resolvedBuildSteps = resolvableBuildSteps.Select(b => b.Resolve(project.RootPath, projectPath, resolver));
foreach (var buildStep in resolvedBuildSteps)
{
bffGenerator.Write(buildStep);
}
// Write Target Alias
if (isLastSubConfig)
{
string genLibName = "'" + fastBuildOutputFileShortName + "_" + outputType + "'";
using (bffGenerator.Declare("fastBuildTargetSubTargets", mustGenerateLibrary ? genLibName : UtilityMethods.FBuildFormatList(fastBuildTargetSubTargets.Values, 15)))
using (bffGenerator.Declare("fastBuildOutputFileShortName", fastBuildOutputFileShortName))
using (bffGenerator.Declare("fastBuildTargetLibraryDependencies", mustGenerateLibrary ? genLibName : UtilityMethods.FBuildFormatList(fastBuildTargetLibraryDependencies.Values, 15)))
{
bffGenerator.Write(Template.ConfigurationFile.TargetSection);
bffGenerator.Write(Template.ConfigurationFile.TargetForLibraryDependencySection);
}
}
}
}
break;
case Project.Configuration.OutputType.None:
{
// Write Target Alias
using (bffGenerator.Declare("fastBuildOutputFileShortName", fastBuildOutputFileShortName))
using (bffGenerator.Declare("fastBuildTargetSubTargets", UtilityMethods.FBuildFormatList(fastBuildTargetSubTargets.Values, 15)))
using (bffGenerator.Declare("fastBuildTargetLibraryDependencies", UtilityMethods.FBuildFormatList(fastBuildTargetLibraryDependencies.Values, 15)))
{
bffGenerator.Write(Template.ConfigurationFile.TargetSection);
if (!project.IsFastBuildAll)
bffGenerator.Write(Template.ConfigurationFile.TargetForLibraryDependencySection);
}
}
break;
}
}
scopedOptions.ForEach(scopedOption => scopedOption.Dispose());
string outputDirectory = Path.GetDirectoryName(fastBuildOutputFile);
bffGenerator.ResolveEnvironmentVariables(conf.Platform,
new VariableAssignment("ProjectName", projectName),
new VariableAssignment("outputDirectory", outputDirectory));
subConfigIndex++;
}
if (configIndex == (configurationsToBuild.Count - 1) || configurationsToBuild[configIndex + 1].Platform != conf.Platform)
{
using (bffGenerator.Declare("fastBuildDefine", GetPlatformSpecificDefine(conf.Platform)))
bffGenerator.Write(Template.ConfigurationFile.PlatformEndSection);
}
}
bffGenerator.WriteTo(bffGeneratorProject);
bffGenerator.Clear();
++configIndex;
}
foreach (string masterlessBff in confBffHasMasters.Where(x => !x.Value).Select(x => x.Key))
Builder.Instance.LogWarningLine("Bff {0} doesn't appear in any master bff, it won't be buildable.", masterlessBff + FastBuildSettings.FastBuildConfigFileExtension);
// Write all unity sections together at the beginning of the .bff just after the header.
if (_unities.Any())
{
foreach (var unityFile in _unities.Keys.OrderBy(u => u.UnityName))
{
using (bffWholeFileGenerator.Declare("unityFile", unityFile))
bffWholeFileGenerator.Write(Template.ConfigurationFile.UnitySection);
// Record the unities in the autocleanupdb to allow auto removal when they become stale.
// Note that can't record them as 'generated', since they are created by FastBuild and not by us.
int nbUnities = 1;
if (unityFile.UnityNumFiles != FileGeneratorUtilities.RemoveLineTag)
{
if (!int.TryParse(unityFile.UnityNumFiles, out nbUnities))
throw new Error("'{0}' cannot be converted to int!", unityFile.UnityNumFiles);
}
string outputPattern = unityFile.UnityOutputPattern == FileGeneratorUtilities.RemoveLineTag ? Sharpmake.Generators.FastBuild.Bff.Unity.DefaultUnityOutputPatternExtension : unityFile.UnityOutputPattern;
int wildcardIndex = outputPattern.IndexOf('*');
if (wildcardIndex == -1)
throw new Error("UnityOutputPattern must include a '*', but none was found in '{0}'!", unityFile.UnityNumFiles);
string firstStringChunk = outputPattern.Substring(0, wildcardIndex);
string lastStringChunk = outputPattern.Substring(wildcardIndex + 1);
for (int i = 1; i <= nbUnities; ++i)
{
string fullPath = Path.Combine(unityFile.UnityFullOutputPath, $"{firstStringChunk}{i}{lastStringChunk}");
Util.RecordInAutoCleanupDatabase(fullPath);
}
}
}
// Now combine all the streams.
bffGeneratorProject.WriteTo(bffWholeFileGenerator);
// remove all line that contain RemoveLineTag
bffWholeFileGenerator.RemoveTaggedLines();
// Write bff file
FileInfo bffFileInfo = new FileInfo(projectBffFile);
if (builder.Context.WriteGeneratedFile(project.GetType(), bffFileInfo, bffWholeFileGenerator))
{
Project.IncrementFastBuildGeneratedFileCount();
generatedFiles.Add(bffFileInfo.FullName);
}
else
{
Project.IncrementFastBuildUpToDateFileCount();
skipFiles.Add(bffFileInfo.FullName);
}
}
internal static string CmdLineConvertIncludePathsFunc(IGenerationContext context, Resolver resolver, string include, string prefix)
{
// if the include is below the global root, we compute the relative path,
// otherwise it's probably a system include for which we keep the full path
string resolvedInclude = resolver.Resolve(include);
if (ShouldMakePathRelative(resolvedInclude, context.Project))
resolvedInclude = CurrentBffPathKeyCombine(Util.PathGetRelative(context.ProjectDirectory, resolvedInclude, true));
return $@"{prefix}{Util.DoubleQuotes}{resolvedInclude}{Util.DoubleQuotes}";
}
private class DependenciesInfo
{
public OrderableStrings AdditionalDependencies;
public Strings IgnoredLibraryNames;
}
private static void GenerateBffOptions(
ProjectOptionsGenerator projectOptionsGen,
BffGenerationContext context,
Dictionary<Project.Configuration, DependenciesInfo> dependenciesInfoPerConf
)
{
// resolve targetPlatformVersion as it may be used in includes
string targetPlatformVersionString = "";
if (context.Configuration.Compiler.IsVisualStudio())
{
targetPlatformVersionString = GetLatestTargetPlatformVersion(context.Configuration.Compiler);
}
var platformBff = context.PresentPlatforms[context.Configuration.Platform];
var resolverParams = new[] {
new VariableAssignment("project", context.Project),
new VariableAssignment("target", context.Configuration.Target),
new VariableAssignment("conf", context.Configuration),
new VariableAssignment("latesttargetplatformversion", targetPlatformVersionString)
};
var platformDescriptor = PlatformRegistry.Get<IPlatformDescriptor>(context.Configuration.Platform);
context.EnvironmentVariableResolver = platformDescriptor.GetPlatformEnvironmentResolver(resolverParams);
projectOptionsGen.GenerateOptions(context);
platformBff.SelectPreprocessorDefinitionsBff(context);
platformBff.SelectAdditionalCompilerOptionsBff(context);
FillIncludeDirectoriesOptions(context);
FillLinkerOptions(context);
var dependenciesInfo = FillLibrariesOptions(context);
dependenciesInfoPerConf.Add(context.Configuration, dependenciesInfo);
FillNasmOptions(context);
}
internal enum CompilerVersionForClangClType
{
/// <summary>
/// Version is for the -fmsc-version compilation flag
/// </summary>
MscVersion,
/// <summary>
/// Version is for the -fms-compatibility-version compilation flag
/// </summary>
MsCompatibilityVersion,
/// <summary>
/// Version is not set
/// </summary>
None
}
internal class CompilerVersionForClangCl
{
public CompilerVersionForClangClType versionType { get; }
public string mscVersion { get; }
public System.Version msCompatibilityVersion { get; }
public CompilerVersionForClangCl(System.Version version)
{
versionType = CompilerVersionForClangClType.MsCompatibilityVersion;
msCompatibilityVersion = version;
}
public CompilerVersionForClangCl(string version)
{
versionType = CompilerVersionForClangClType.MscVersion;
mscVersion = version;
}
public CompilerVersionForClangCl()
{
versionType = CompilerVersionForClangClType.None;
}
public override string ToString()
{
switch (versionType)
{
case CompilerVersionForClangClType.MsCompatibilityVersion:
return string.Format("MsCompatibilityVersion : {0}.{1}.{2}", msCompatibilityVersion.Major, msCompatibilityVersion.Minor, msCompatibilityVersion.Build);
case CompilerVersionForClangClType.MscVersion:
return string.Format("MscVersion : {0}", mscVersion);
case CompilerVersionForClangClType.None:
default:
return string.Format("No version");
}
}
#region IEquatable
public override bool Equals(object obj)
{
CompilerVersionForClangCl other = obj as CompilerVersionForClangCl;
if (other != null)
{
return Equals(other);
}
else
{
return false;
}
}
public override int GetHashCode()
{
switch (versionType)
{
case CompilerVersionForClangClType.MsCompatibilityVersion:
return msCompatibilityVersion.GetHashCode();
case CompilerVersionForClangClType.MscVersion:
return Int32.TryParse(mscVersion, out int numValue) ? numValue : -1;
case CompilerVersionForClangClType.None:
return 0;
default:
return -2;
}
}
public bool Equals(CompilerVersionForClangCl other)
{
if (other.versionType != versionType)
{
return false;
}
switch (versionType)
{
case CompilerVersionForClangClType.MsCompatibilityVersion:
return other.msCompatibilityVersion == msCompatibilityVersion;
case CompilerVersionForClangClType.MscVersion:
return other.mscVersion == mscVersion;
case CompilerVersionForClangClType.None:
default:
return true;
}
}
#endregion
}
internal static CompilerVersionForClangCl DetectCompilerVersionForClangCl(
Project.Configuration.FastBuildClangMscVersionDetectionType detectionType, string overridenMscVer,
Options.Vc.General.PlatformToolset overridenPlatformToolset, DevEnv devenv, Platform platform)
{
switch (detectionType)
{
case Project.Configuration.FastBuildClangMscVersionDetectionType.MajorVersion :
{
if (!string.IsNullOrEmpty(overridenMscVer))
{
return new CompilerVersionForClangCl(overridenMscVer);
}
string mscVer = DetectMscVerForClang(devenv, overridenPlatformToolset);
return new CompilerVersionForClangCl(mscVer);
}
case Project.Configuration.FastBuildClangMscVersionDetectionType.FullVersion:
{
if (!string.IsNullOrEmpty(overridenMscVer))
{
throw new Error("Options.Clang.Compiler.MscVersion and FastBuildClangMscVersionDetection.FullVersion are both set but are mutually exclusive.");
}
System.Version mscFullVer = new System.Version();
try
{
mscFullVer = devenv.GetVisualStudioVCToolsCompilerVersion(platform);
}
catch
{
// mscFullVer couldn't be retrieved, fallback to MajorVersion behavior
string mscVer = DetectMscVerForClang(devenv, overridenPlatformToolset);
return new CompilerVersionForClangCl(mscVer);
}
return new CompilerVersionForClangCl(mscFullVer);
}
case Project.Configuration.FastBuildClangMscVersionDetectionType.Disabled:
{
if (!string.IsNullOrEmpty(overridenMscVer))
{
// Detection is disabled but the Clang option version is set, set the version
return new CompilerVersionForClangCl(overridenMscVer);
}
return new CompilerVersionForClangCl();
}
default:
{
return new CompilerVersionForClangCl();
}
}
}
internal static string DetectMscVerForClang(DevEnv devenv, Options.Vc.General.PlatformToolset overridenPlatformToolset)
{
if (overridenPlatformToolset != Options.Vc.General.PlatformToolset.Default
&& !overridenPlatformToolset.IsDefaultToolsetForDevEnv(devenv))
{
switch (overridenPlatformToolset)
{
case Options.Vc.General.PlatformToolset.v141:
case Options.Vc.General.PlatformToolset.v141_xp:
return "1910";
case Options.Vc.General.PlatformToolset.v142:
return "1920";
case Options.Vc.General.PlatformToolset.v143:
return "1930";
default:
throw new Error("LLVMVcPlatformToolset! Platform toolset override '{0}' not supported", overridenPlatformToolset);
}
}
else
{
switch (devenv)
{
case DevEnv.vs2017:
return "1910";
case DevEnv.vs2019:
return "1920";
case DevEnv.vs2022:
return "1930";
default:
throw new Error("Clang-cl used with unsupported DevEnv: " + devenv.ToString());
}
}
}
private static void FillIncludeDirectoriesOptions(BffGenerationContext context)
{
// TODO: really not ideal, refactor and move the properties we need from it someplace else
var platformVcxproj = PlatformRegistry.Query<IPlatformVcxproj>(context.Configuration.Platform);
var includePaths = new OrderableStrings(platformVcxproj.GetIncludePaths(context));
var resourceIncludePaths = new OrderableStrings(platformVcxproj.GetResourceIncludePaths(context));
var assemblyIncludePaths = new OrderableStrings(platformVcxproj.GetAssemblyIncludePaths(context));
context.CommandLineOptions["AdditionalIncludeDirectories"] = FileGeneratorUtilities.RemoveLineTag;
context.CommandLineOptions["SwiftAdditionalIncludeDirectories"] = FileGeneratorUtilities.RemoveLineTag;
context.CommandLineOptions["AdditionalResourceIncludeDirectories"] = FileGeneratorUtilities.RemoveLineTag;
context.CommandLineOptions["AdditionalUsingDirectories"] = FileGeneratorUtilities.RemoveLineTag;
context.CommandLineOptions["AdditionalAssemblyIncludeDirectories"] = FileGeneratorUtilities.RemoveLineTag;
var platformDescriptor = PlatformRegistry.Get<IPlatformDescriptor>(context.Configuration.Platform);
if (context.EnvironmentVariableResolver != null)
{
string defaultCmdLineIncludePrefix = platformDescriptor.IsUsingClang ? "-I " : "/I";
// Fill include dirs
var platformIncludePaths = platformVcxproj.GetPlatformIncludePathsWithPrefix(context);
var dirs = new List<string>();
dirs.AddRange(includePaths.Select(p => CmdLineConvertIncludePathsFunc(context, context.EnvironmentVariableResolver, p, defaultCmdLineIncludePrefix)));
dirs.AddRange(platformIncludePaths.Select(p => CmdLineConvertIncludePathsFunc(context, context.EnvironmentVariableResolver, p.Path, p.CmdLinePrefix)));
if (dirs.Any())
context.CommandLineOptions["AdditionalIncludeDirectories"] = string.Join($"'{Environment.NewLine} + ' ", dirs);
var applePlatformBff = PlatformRegistry.Query<IApplePlatformBff>(context.Configuration.Platform);
if (applePlatformBff != null && applePlatformBff.IsSwiftSupported())
{
string swifttCmdLineIncludePrefix = "-Xcc " + defaultCmdLineIncludePrefix.Replace(" ", " -Xcc ");
var swiftDirs = new List<string>();
swiftDirs.AddRange(includePaths.Select(p => CmdLineConvertIncludePathsFunc(context, context.EnvironmentVariableResolver, p, swifttCmdLineIncludePrefix)));
swiftDirs.AddRange(platformIncludePaths.Select(p => CmdLineConvertIncludePathsFunc(context, context.EnvironmentVariableResolver, p.Path, "-Xcc " + p.CmdLinePrefix.Replace(" ", " -Xcc "))));
if (swiftDirs.Any())
context.CommandLineOptions["SwiftAdditionalIncludeDirectories"] = string.Join($"'{Environment.NewLine} + ' ", swiftDirs);
}
// Fill resource include dirs
var resourceDirs = new List<string>();
resourceDirs.AddRange(resourceIncludePaths.Select(p => CmdLineConvertIncludePathsFunc(context, context.EnvironmentVariableResolver, p, defaultCmdLineIncludePrefix)));
// with LLVM as toolchain, we are still using the default resource compiler, so we need the default include prefix
// TODO: this is not great, ideally we would need the prefix to be per "compiler", and a platform can have many
var platformIncludePathsDefaultPrefix = platformIncludePaths.Select(p => CmdLineConvertIncludePathsFunc(context, context.EnvironmentVariableResolver, p.Path, defaultCmdLineIncludePrefix));
resourceDirs.AddRange(platformIncludePathsDefaultPrefix);
if (resourceDirs.Any())
context.CommandLineOptions["AdditionalResourceIncludeDirectories"] = string.Join($"'{Environment.NewLine} + ' ", resourceDirs);
// Fill Assembly include dirs
var assemblyDirs = new List<string>();
assemblyDirs.AddRange(assemblyIncludePaths.Select(p => CmdLineConvertIncludePathsFunc(context, context.EnvironmentVariableResolver, p, defaultCmdLineIncludePrefix)));
if (assemblyDirs.Any())
context.CommandLineOptions["AdditionalAssemblyIncludeDirectories"] = string.Join($"'{Environment.NewLine} + ' ", assemblyDirs);
// Fill using dirs
Strings additionalUsingDirectories = Options.GetStrings<Options.Vc.Compiler.AdditionalUsingDirectories>(context.Configuration);
additionalUsingDirectories.AddRange(context.Configuration.AdditionalUsingDirectories);
additionalUsingDirectories.AddRange(platformVcxproj.GetCxUsingPath(context));
if (additionalUsingDirectories.Any())
{
var cmdAdditionalUsingDirectories = additionalUsingDirectories.Select(p => CmdLineConvertIncludePathsFunc(context, context.EnvironmentVariableResolver, p, "/AI"));
context.CommandLineOptions["AdditionalUsingDirectories"] = string.Join($"'{Environment.NewLine} + ' ", cmdAdditionalUsingDirectories);
}
}
}
private static void FillLinkerOptions(BffGenerationContext context)
{
FillEmbeddedNatvisOptions(context);
}
private static Strings CollectNatvisFiles(BffGenerationContext context)
{
Project.Configuration projectConfig = context.Configuration;
var natvisFiles = new Strings(projectConfig.Project.NatvisFiles);
if (projectConfig.Output == Project.Configuration.OutputType.Dll || projectConfig.Output == Project.Configuration.OutputType.Exe || projectConfig.Output == Project.Configuration.OutputType.AppleApp)
{
var visitedProjects = new HashSet<Project>();
foreach (Project.Configuration resolvedDepConfig in projectConfig.ResolvedDependencies)
{
if (resolvedDepConfig.Output != Project.Configuration.OutputType.Dll && resolvedDepConfig.Output != Project.Configuration.OutputType.Exe && resolvedDepConfig.Output != Project.Configuration.OutputType.AppleApp)
{
if (!visitedProjects.Contains(resolvedDepConfig.Project))
{
visitedProjects.Add(resolvedDepConfig.Project);
foreach (string natvisFile in resolvedDepConfig.Project.NatvisFiles)
{
natvisFiles.Add(natvisFile);
}
}
}
}
}
return natvisFiles;
}
private static void FillEmbeddedNatvisOptions(BffGenerationContext context)
{
Strings natvisFiles = CollectNatvisFiles(context);
if (natvisFiles.Count > 0)
{
var cmdNatvisFiles = natvisFiles.SortedValues.Select(n => Bff.CmdLineConvertIncludePathsFunc(context, context.EnvironmentVariableResolver, n, "/NATVIS:"));
string linkerNatvis = string.Join($"'{Environment.NewLine} + ' ", cmdNatvisFiles);
context.CommandLineOptions["LinkerNatvisFiles"] = linkerNatvis;
}
else
{
context.CommandLineOptions["LinkerNatvisFiles"] = FileGeneratorUtilities.RemoveLineTag;
}
}
private static DependenciesInfo FillLibrariesOptions(BffGenerationContext context)
{
var dependenciesInfo = new DependenciesInfo();
// TODO: really not ideal, refactor and move the properties we need from it someplace else
var platformVcxproj = PlatformRegistry.Query<IPlatformVcxproj>(context.Configuration.Platform);
var libFiles = new OrderableStrings(context.Configuration.LibraryFiles);
libFiles.AddRange(context.Configuration.DependenciesOtherLibraryFiles);
libFiles.AddRange(platformVcxproj.GetLibraryFiles(context));
if (context.Configuration.Platform.IsMicrosoft())
{
Strings delayedDLLs = Options.GetStrings<Options.Vc.Linker.DelayLoadDLLs>(context.Configuration);
if (delayedDLLs.Any())
libFiles.Add("Delayimp.lib");
}
libFiles.Sort();
Strings ignoreSpecificLibraryNames = Options.GetStrings<Options.Vc.Linker.IgnoreSpecificLibraryNames>(context.Configuration);
ignoreSpecificLibraryNames.ToLower();
ignoreSpecificLibraryNames.InsertSuffix(platformVcxproj.StaticLibraryFileFullExtension, true, new[] { platformVcxproj.SharedLibraryFileFullExtension });
dependenciesInfo.IgnoredLibraryNames = ignoreSpecificLibraryNames;
context.CommandLineOptions["AdditionalDependencies"] = FileGeneratorUtilities.RemoveLineTag;
context.CommandLineOptions["AdditionalLibraryDirectories"] = FileGeneratorUtilities.RemoveLineTag;
if (!(context.Configuration.Output == Project.Configuration.OutputType.None || context.Configuration.Output == Project.Configuration.OutputType.Lib && !context.Configuration.ExportAdditionalLibrariesEvenForStaticLib))
{
//AdditionalLibraryDirectories
// AdditionalLibraryDirectories="dir1;dir2" /LIBPATH:"dir1" /LIBPATH:"dir2"
SelectAdditionalLibraryDirectoriesOption(context);
//AdditionalDependencies
// AdditionalDependencies="lib1;lib2" "lib1;lib2"
dependenciesInfo.AdditionalDependencies = SelectAdditionalDependenciesOption(context, libFiles, ignoreSpecificLibraryNames);
}
else
{
dependenciesInfo.AdditionalDependencies = new OrderableStrings();
}
////IgnoreSpecificLibraryNames
//// IgnoreDefaultLibraryNames=[lib] /NODEFAULTLIB:[lib]
if (ignoreSpecificLibraryNames.Any())
{
var result = new StringBuilder();
foreach (string ignoreLib in ignoreSpecificLibraryNames.SortedValues)
result.Append(@"/NODEFAULTLIB:""" + ignoreLib + @""" ");
result.Remove(result.Length - 1, 1);
context.CommandLineOptions["IgnoreDefaultLibraryNames"] = result.ToString();
}
else
{
context.CommandLineOptions["IgnoreDefaultLibraryNames"] = FileGeneratorUtilities.RemoveLineTag;
}
return dependenciesInfo;
}
private static void FillNasmOptions(BffGenerationContext context)
{
// Compiler path for nasm
context.CommandLineOptions["PathExe"] = context.Project.NasmExePath;
// Pre included files for NASM syntax
var preIncludedFiles = new List<string>();
preIncludedFiles.AddRange(context.Project.NasmPreIncludedFiles.Select(p => "-P\"" + p + "\""));
string preIncludedFilesJoined = string.Join(' ', preIncludedFiles);
context.CommandLineOptions["PreIncludedFiles"] = preIncludedFilesJoined;
// Fill Assembly include dirs in nasm syntax
var nasmAssemblyDirs = new List<string>();
var platformVcxproj = PlatformRegistry.Query<IPlatformVcxproj>(context.Configuration.Platform);
var assemblyIncludePaths = new OrderableStrings(platformVcxproj.GetAssemblyIncludePaths(context));
nasmAssemblyDirs.AddRange(assemblyIncludePaths.Select(p => CmdLineConvertIncludePathsFunc(context, context.EnvironmentVariableResolver, p, "-I ")));
if (nasmAssemblyDirs.Any())
{
context.CommandLineOptions["AdditionalAssemblyNasmIncludeDirectories"] = string.Join($"'{Environment.NewLine} + ' ", nasmAssemblyDirs);
}
else
{
context.CommandLineOptions["AdditionalAssemblyNasmIncludeDirectories"] = FileGeneratorUtilities.RemoveLineTag;
}
// Defines in NASM syntax
var defines = new Strings();
defines.AddRange(context.Options.ExplicitDefines);
defines.AddRange(context.Configuration.Defines);
if (defines.Count > 0)
{
var fastBuildNasmDefines = new List<string>();
foreach (string define in defines.SortedValues)
{
if (!string.IsNullOrWhiteSpace(define))
fastBuildNasmDefines.Add(string.Format(@"{0}{1}{2}{1}", "-D", Util.DoubleQuotes, define.Replace(Util.DoubleQuotes, Util.EscapedDoubleQuotes)));
}
context.CommandLineOptions["NasmPreprocessorDefinitions"] = string.Join($"'{Environment.NewLine} + ' ", fastBuildNasmDefines);
}
else
{
context.CommandLineOptions["NasmPreprocessorDefinitions"] = FileGeneratorUtilities.RemoveLineTag;
}
}
private static void SelectAdditionalLibraryDirectoriesOption(BffGenerationContext context)
{
// TODO: really not ideal, refactor and move the properties we need from it someplace else
var platformVcxproj = PlatformRegistry.Query<IPlatformVcxproj>(context.Configuration.Platform);
context.CommandLineOptions["AdditionalLibraryDirectories"] = FileGeneratorUtilities.RemoveLineTag;
var libDirs = new OrderableStrings(context.Configuration.LibraryPaths);
libDirs.AddRange(context.Configuration.DependenciesOtherLibraryPaths);
libDirs.AddRange(platformVcxproj.GetLibraryPaths(context));
libDirs.Sort();
if (context.EnvironmentVariableResolver != null)
{
var configTasks = PlatformRegistry.Get<Project.Configuration.IConfigurationTasks>(context.Configuration.Platform);
libDirs.AddRange(configTasks.GetPlatformLibraryPaths(context.Configuration));
if (libDirs.Count > 0)
{
string linkOption;
if (!PlatformRegistry.Get<IPlatformDescriptor>(context.Configuration.Platform).IsUsingClang)
linkOption = @"/LIBPATH:";
else
linkOption = @"-L ";
var cmdAdditionalLibDirectories = libDirs.Select(p => Bff.CmdLineConvertIncludePathsFunc(context, context.EnvironmentVariableResolver, p, linkOption));
context.CommandLineOptions["AdditionalLibraryDirectories"] = string.Join($"'{Environment.NewLine} + ' ", cmdAdditionalLibDirectories);
}
}
}
private static OrderableStrings SelectAdditionalDependenciesOption(
BffGenerationContext context,
OrderableStrings libraryFiles,
Strings ignoreSpecificLibraryNames
)
{
var configurationTasks = PlatformRegistry.Get<Project.Configuration.IConfigurationTasks>(context.Configuration.Platform);
// TODO: really not ideal, refactor and move the properties we need from it someplace else
var platformVcxproj = PlatformRegistry.Query<IPlatformVcxproj>(context.Configuration.Platform);
platformVcxproj.SetupPlatformLibraryOptions(out var platformLibraryExtension, out var platformOutputLibraryExtension, out var platformPrefix, out var libPrefix);
var additionalDependencies = new OrderableStrings();
for (int i = 0; i < libraryFiles.Count; ++i)
{
string libraryFile = libraryFiles[i];
// convert all root paths to be relative to the project folder
if (Path.IsPathRooted(libraryFile))
{
// if the path is below the global root, we compute the relative path, otherwise we keep the full path
if (ShouldMakePathRelative(libraryFile, context.Project))
additionalDependencies.Add(CurrentBffPathKeyCombine(Util.PathGetRelative(context.ProjectDirectory, libraryFile, true)), libraryFiles.GetOrderNumber(i));
else
additionalDependencies.Add(libraryFile, libraryFiles.GetOrderNumber(i));
}
else
{
// If not a path, we've got two kinds of way of listing a library:
// - With a filename without extension we must add the potential prefix and potential extension.
// Ex: On clang we add -l (supposedly because the exact file is named lib<library>.a)
// - With a filename with a static or shared lib extension (eg. .a/.lib/.so), we shouldn't touch it as it's already set by the script.
string extension = Path.GetExtension(libraryFile).ToLower();
string filenameOnly = Path.GetFileNameWithoutExtension(libraryFile);
string finalFilename = null;
if (string.IsNullOrEmpty(extension))
{
finalFilename = libPrefix + filenameOnly + platformOutputLibraryExtension;
}
else if (extension != platformVcxproj.StaticLibraryFileFullExtension && extension != platformVcxproj.SharedLibraryFileFullExtension)
{
// Handle case such SomeLib.Platform
finalFilename = libPrefix + libraryFile + platformOutputLibraryExtension;
}
else
{
finalFilename = libraryFile;
}
string libDependencyFile = platformPrefix + finalFilename;
// LCTODO: this might be broken, clarify the rules for which this is supposed to work
if (!ignoreSpecificLibraryNames.Contains(finalFilename))
additionalDependencies.Add(libDependencyFile);
else
ignoreSpecificLibraryNames.Remove(finalFilename);
}
}
var finalDependencies = new OrderableStrings();
if (context.EnvironmentVariableResolver != null)
{
var platformAdditionalDependencies = platformVcxproj.GetPlatformLibraryFiles(context);
// Joins the list of dependencies with a ; and then re-split them after a resolve.
// We have to do it that way because a token can be resolved into a
// semicolon -separated list of dependencies.
var resolvedAdditionalDependencies = new Strings(context.EnvironmentVariableResolver.Resolve(
string.Join(";", additionalDependencies.Concat(platformAdditionalDependencies))
).Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries));
if (resolvedAdditionalDependencies.Any())
{
foreach (string additionalDependency in resolvedAdditionalDependencies)
finalDependencies.Add(@"""" + additionalDependency + @"""");
}
}
return finalDependencies;
}
/// <summary>
/// Method that allows to determine for a specified dependency if it's a library or an object list. if a dep is within
/// the list, the second condition check if objects is present which means that the current dependency is considered to be
/// a force objectlist.
/// </summary>
/// <param name="dependencies">all the dependencies of a specific project configuration</param>
/// <param name="dep">additional dependency clear of additional suffix</param>
/// <returns>return boolean value of presence of a dep within the containing dependencies list</returns>
private bool IsObjectList(IEnumerable<string> dependencies, string dep)
{
return dependencies.Any(dependency => dependency.Contains(dep) && dependency.Contains("objects"));
}
private Dictionary<Unity, List<Project.Configuration>> _unities = new Dictionary<Unity, List<Project.Configuration>>();
private string GetUnityName(Project.Configuration conf)
{
if (_unities.Count > 0)
{
var match = _unities.First(x => x.Value.Contains(conf));
return match.Key.UnityName;
}
return null;
}
private void ConfigureUnities(IGenerationContext context, Dictionary<Project.Configuration, Dictionary<SubConfig, List<Vcxproj.ProjectFile>>> confSourceFiles)
{
var conf = context.Configuration;
// Only add unity build to non blobbed projects -> which they will be blobbed by FBuild
if (!conf.FastBuildBlobbed)
return;
if (!confSourceFiles.ContainsKey(conf)) // no source files, so no unity section
return;
var confSubConfigs = confSourceFiles[conf];
var unitySubConfig = s_DefaultSubConfig;
var sourceFiles = confSubConfigs[unitySubConfig];
var project = context.Project;
const int spaceLength = 42;
string fastBuildUnityInputFiles = FileGeneratorUtilities.RemoveLineTag;
string fastBuildUnityInputExcludedfiles = FileGeneratorUtilities.RemoveLineTag;
string fastBuildUnityPaths = FileGeneratorUtilities.RemoveLineTag;
string fastBuildUnityInputPattern = FileGeneratorUtilities.RemoveLineTag;
string fastBuildUnityInputExcludePath = FileGeneratorUtilities.RemoveLineTag;
string fastBuildUnityCount = FileGeneratorUtilities.RemoveLineTag;
int unityCount = conf.FastBuildUnityCount > 0 ? conf.FastBuildUnityCount : conf.GeneratableBlobCount;
if (unityCount > 0)
fastBuildUnityCount = unityCount.ToString(CultureInfo.InvariantCulture);
var fastbuildUnityInputExcludePathList = new Strings(project.SourcePathsBlobExclude.Select(Util.GetCapitalizedPath));
string fastBuildUnityInputIsolateListFile = FileGeneratorUtilities.RemoveLineTag;
bool srcDirsAreEmpty = true;
var items = new List<string>();
// Fastbuild will process as unity all files contained in source Root folder and all additional roots.
var unityInputPaths = new Strings(context.ProjectSourceCapitalized);
unityInputPaths.AddRange(project.AdditionalSourceRootPaths.Select(Util.GetCapitalizedPath));
foreach (var file in sourceFiles)
{
bool isBlobbed = project.SourceFilesBlobExtensions.Contains(file.FileExtension);
if (isBlobbed &&
(conf.PrecompSource == null || !file.FileName.EndsWith(conf.PrecompSource, StringComparison.OrdinalIgnoreCase)) &&
!conf.ResolvedSourceFilesBlobExclude.Contains(file.FileName))
{
if (conf.FastBuildBlobbingStrategy == Project.Configuration.InputFileStrategy.Include || !IsFileInInputPathList(unityInputPaths, file.FileName))
{
string sourceFileRelative = CurrentBffPathKeyCombine(Util.PathGetRelative(context.ProjectDirectoryCapitalized, file.FileName));
items.Add(sourceFileRelative);
}
srcDirsAreEmpty = false;
}
}
// Conditional statement depending on the blobbing strategy
if (items.Count == 0 && srcDirsAreEmpty)
{
fastBuildUnityInputFiles = FileGeneratorUtilities.RemoveLineTag;
}
else if (conf.FastBuildBlobbingStrategy == Project.Configuration.InputFileStrategy.Include)
{
fastBuildUnityInputFiles = UtilityMethods.FBuildFormatList(items, spaceLength);
}
else
{
fastBuildUnityInputFiles = UtilityMethods.FBuildFormatList(items, spaceLength);
// check if there's some static blobs lying around to exclude
if (IsFileInInputPathList(unityInputPaths, conf.BlobPath))
fastbuildUnityInputExcludePathList.Add(conf.BlobPath);
// Remove any excluded paths(exclusion has priority)
unityInputPaths.RemoveRange(fastbuildUnityInputExcludePathList);
var unityInputRelativePaths = new Strings(unityInputPaths.Select(
p =>
{
if (ShouldMakePathRelative(p, context.Project))
return CurrentBffPathKeyCombine(Util.PathGetRelative(context.ProjectDirectoryCapitalized, p, true));
return p;
}
));
fastBuildUnityPaths = UtilityMethods.FBuildCollectionFormat(unityInputRelativePaths, spaceLength);
var excludedSourceFiles = new Strings(conf.ResolvedSourceFilesBlobExclude);
excludedSourceFiles.AddRange(conf.ResolvedSourceFilesBuildExclude);
excludedSourceFiles.AddRange(conf.PrecompSourceExclude);
var excludedSourceFilesRelative = new Strings();
// Converting the excluded filenames to relative path to the input path so that this
// can work properly with subst usage when running with fastbuild caching active.
//
// Also exclusion checks in fastbuild assume that the exclusion filenames are
// relative to the .UnityInputPath and checks that paths are ending with the specified
// path which means that any filename starting with a .. will never be excluded by fastbuild.
//
// Note: Ideally fastbuild should expect relative paths to the bff file path instead of the .UnityInputPath but
// well I guess we are stuck with this.
foreach (string file in excludedSourceFiles.SortedValues)
{
if (IsFileInInputPathList(unityInputPaths, file))
excludedSourceFilesRelative.Add(CurrentBffPathKeyCombine(Util.PathGetRelative(context.ProjectDirectoryCapitalized, file, true)));
}
if (excludedSourceFilesRelative.Count > 0)
fastBuildUnityInputExcludedfiles = UtilityMethods.FBuildCollectionFormat(excludedSourceFilesRelative, spaceLength, project.SourceFilesBlobExtensions);
}
if (fastBuildUnityInputFiles == FileGeneratorUtilities.RemoveLineTag &&
fastBuildUnityPaths == FileGeneratorUtilities.RemoveLineTag)
{
// completely drop the subconfig in case it was only a unity subConfig, without any files
if (sourceFiles.Count == 0)
confSubConfigs.Remove(unitySubConfig);
// no input path nor files => no unity
return;
}
if (fastbuildUnityInputExcludePathList.Any())
{
var unityInputExcludePathRelative = new Strings(fastbuildUnityInputExcludePathList.Select(p => CurrentBffPathKeyCombine(Util.PathGetRelative(context.ProjectDirectoryCapitalized, p, true))));
fastBuildUnityInputExcludePath = UtilityMethods.FBuildCollectionFormat(unityInputExcludePathRelative, spaceLength);
}
// only write UnityInputPattern if it's not FastBuild's default value of .cpp
if (project.SourceFilesBlobExtensions.Count != 1 || !project.SourceFilesBlobExtensions.Contains(Unity.DefaultUnityInputPatternExtension))
{
var inputPatterns = new Strings(project.SourceFilesBlobExtensions);
inputPatterns.InsertPrefix("*");
fastBuildUnityInputPattern = UtilityMethods.FBuildCollectionFormat(inputPatterns, spaceLength);
}
if (!string.IsNullOrEmpty(conf.FastBuildUnityInputIsolateListFile))
fastBuildUnityInputIsolateListFile = CurrentBffPathKeyCombine(Util.PathGetRelative(context.ProjectDirectoryCapitalized, conf.FastBuildUnityInputIsolateListFile, true));
Unity unityFile = new Unity
{
// Note that the UnityName and UnityOutputPattern are intentionally left empty: they will be set in the Resolve
UnityOutputPath = CurrentBffPathKeyCombine(Util.PathGetRelative(context.ProjectDirectoryCapitalized, conf.FastBuildUnityPath, true)),
UnityFullOutputPath = Path.Combine(context.ProjectDirectoryCapitalized, conf.FastBuildUnityPath),
UnityInputIsolateWritableFiles = conf.FastBuildUnityInputIsolateWritableFiles.ToString().ToLower(),
UnityInputIsolateWritableFilesLimit = conf.FastBuildUnityInputIsolateWritableFiles ? conf.FastBuildUnityInputIsolateWritableFilesLimit.ToString() : FileGeneratorUtilities.RemoveLineTag,
UnityInputIsolateListFile = fastBuildUnityInputIsolateListFile,
UnityPCH = conf.PrecompHeader ?? FileGeneratorUtilities.RemoveLineTag,
UnityInputExcludePath = fastBuildUnityInputExcludePath,
UnityNumFiles = fastBuildUnityCount,
UnityInputPath = fastBuildUnityPaths,
UnityInputFiles = fastBuildUnityInputFiles,
UnityInputExcludedFiles = fastBuildUnityInputExcludedfiles,
UnityInputPattern = fastBuildUnityInputPattern,
UseRelativePaths = conf.FastBuildUnityUseRelativePaths ? "true" : FileGeneratorUtilities.RemoveLineTag,
UnitySectionBucket = conf.FastBuildUnitySectionBucket,
};
// _unities being a dictionary, a new entry will be created only
// if the combination of options forming that unity was never seen before
var confListForUnity = _unities.GetValueOrAdd(unityFile, new List<Project.Configuration>());
// add the current conf in the list that this unity serves
confListForUnity.Add(conf);
}
private void ResolveUnities(Project project, string projectPath)
{
if (_unities.Count == 0)
return;
UnityResolver.ResolveUnities(project, projectPath, ref _unities);
}
// For now, this will do.
private static SubConfig s_DefaultSubConfig = new SubConfig();
private static string FormatListPartForTag(List<string> items, int spaceLength, bool addSeparatorAfterList)
{
if (items.Count == 0)
return FileGeneratorUtilities.RemoveLineTag;
StringBuilder strBuilder = new StringBuilder(1024 * 16);
string indent = new string(' ', spaceLength);
// Write all selected items.
string separator = "," + Environment.NewLine + indent;
strBuilder.Append(string.Join(separator, items.Select(i => $"'{i}'")));
if (addSeparatorAfterList)
strBuilder.Append(",");
return strBuilder.ToString();
}
private static void Write(string value, TextWriter writer, Resolver resolver)
{
string resolvedValue = resolver.Resolve(value);
StringReader reader = new StringReader(resolvedValue);
string str = reader.ReadToEnd();
writer.Write(str);
writer.Flush();
}
private static Dictionary<Project.Configuration, Dictionary<SubConfig, List<Vcxproj.ProjectFile>>>
GetGeneratedFiles(
IGenerationContext context,
List<Project.Configuration> configurations,
out List<Vcxproj.ProjectFile> filesInNonDefaultSections
)
{
var confSubConfigs = new Dictionary<Project.Configuration, Dictionary<SubConfig, List<Vcxproj.ProjectFile>>>();
filesInNonDefaultSections = new List<Vcxproj.ProjectFile>();
// Add source files
var allFiles = new List<Vcxproj.ProjectFile>();
Strings projectFiles = context.Project.GetSourceFilesForConfigurations(configurations);
foreach (string file in projectFiles)
{
var projectFile = new Vcxproj.ProjectFile(context, file);
allFiles.Add(projectFile);
}
allFiles.Sort((l, r) => string.Compare(l.FileNameProjectRelative, r.FileNameProjectRelative, StringComparison.OrdinalIgnoreCase));
var sourceFiles = new List<Vcxproj.ProjectFile>();
foreach (var projectFile in allFiles)
{
if (context.Project.SourceFilesCompileExtensions.Contains(projectFile.FileExtension) ||
(string.Compare(projectFile.FileExtension, ".rc", StringComparison.OrdinalIgnoreCase) == 0) ||
(string.Compare(projectFile.FileExtension, ".resx", StringComparison.OrdinalIgnoreCase) == 0))
sourceFiles.Add(projectFile);
}
foreach (var file in sourceFiles)
{
foreach (Project.Configuration conf in configurations)
{
bool isExcludeFromBuild = conf.ResolvedSourceFilesBuildExclude.Contains(file.FileName);
if (!isExcludeFromBuild)
{
bool isDontUsePrecomp = conf.PrecompSourceExclude.Contains(file.FileName) ||
conf.PrecompSourceExcludeFolders.Any(folder => file.FileName.StartsWith(folder, StringComparison.OrdinalIgnoreCase)) ||
conf.PrecompSourceExcludeExtension.Contains(file.FileExtension);
bool isCompileAsCFile = conf.ResolvedSourceFilesWithCompileAsCOption.Contains(file.FileName);
bool isCompileAsCPPFile = conf.ResolvedSourceFilesWithCompileAsCPPOption.Contains(file.FileName);
bool isCompileAsObjCFile = conf.ResolvedSourceFilesWithCompileAsObjCOption.Contains(file.FileName);
bool isCompileAsObjCPPFile = conf.ResolvedSourceFilesWithCompileAsObjCPPOption.Contains(file.FileName);
bool isCompileAsCLRFile = conf.ResolvedSourceFilesWithCompileAsCLROption.Contains(file.FileName);
bool isCompileAsNonCLRFile = conf.ResolvedSourceFilesWithCompileAsNonCLROption.Contains(file.FileName);
bool isConsumeWinRTExtensions = (conf.ConsumeWinRTExtensions.Contains(file.FileName) ||
conf.ResolvedSourceFilesWithCompileAsWinRTOption.Contains(file.FileName)) &&
!(conf.ExcludeWinRTExtensions.Contains(file.FileName) ||
conf.ResolvedSourceFilesWithExcludeAsWinRTOption.Contains(file.FileName));
// TODOANT: Also trigger on .nasm files
bool isASMFile = string.Compare(file.FileExtension, ".asm", StringComparison.OrdinalIgnoreCase) == 0;
bool isSwiftFile = string.Compare(file.FileExtension, ".swift", StringComparison.OrdinalIgnoreCase) == 0 &&
(PlatformRegistry.Query<IApplePlatformBff>(conf.Platform)?.IsSwiftSupported() ?? false);
bool isNASMFile = string.Compare(file.FileExtension, ".nasm", StringComparison.OrdinalIgnoreCase) == 0;
Options.Vc.Compiler.Exceptions exceptionSetting = conf.GetExceptionSettingForFile(file.FileName);
if (isCompileAsCLRFile || isConsumeWinRTExtensions)
isDontUsePrecomp = true;
if (!isCompileAsCPPFile && string.Compare(file.FileExtension, ".c", StringComparison.OrdinalIgnoreCase) == 0)
{
isDontUsePrecomp = true;
isCompileAsCFile = true;
}
else if (isCompileAsObjCFile || isCompileAsObjCPPFile || isSwiftFile)
{
isDontUsePrecomp = true;
}
Languages languageKind = Languages.None;
if (isCompileAsCFile)
languageKind |= Languages.C;
if (isCompileAsCPPFile)
languageKind |= Languages.CPP;
if (isCompileAsObjCFile)
languageKind |= Languages.ObjC;
if (isCompileAsObjCPPFile)
languageKind |= Languages.ObjCPP;
if (isSwiftFile)
languageKind |= Languages.Swift;
if (isASMFile)
languageKind |= Languages.Asm;
if (isNASMFile)
languageKind |= Languages.Nasm;
LanguageFeatures languageFeatures = LanguageFeatures.None;
if (isCompileAsCLRFile)
languageFeatures |= LanguageFeatures.CLR;
if (isCompileAsNonCLRFile)
languageFeatures |= LanguageFeatures.NonCLR;
if (isConsumeWinRTExtensions)
languageFeatures |= LanguageFeatures.ConsumeWinRTExtensions;
var subConfig = new SubConfig()
{
IsUsePrecomp = !isDontUsePrecomp,
Languages = languageKind,
LanguageFeatures = languageFeatures,
Exceptions = exceptionSetting
};
Dictionary<SubConfig, List<Vcxproj.ProjectFile>> subConfigs = null;
if (!confSubConfigs.TryGetValue(conf, out subConfigs))
{
subConfigs = new Dictionary<SubConfig, List<Vcxproj.ProjectFile>>();
confSubConfigs.Add(conf, subConfigs);
}
List<Vcxproj.ProjectFile> subConfigFiles = null;
if (!subConfigs.TryGetValue(subConfig, out subConfigFiles))
{
subConfigFiles = new List<Vcxproj.ProjectFile>();
subConfigs.Add(subConfig, subConfigFiles);
}
subConfigFiles.Add(file);
if (!subConfig.Equals(s_DefaultSubConfig))
{
filesInNonDefaultSections.Add(file);
}
}
}
}
// Check if we need to add a compatible config for unity build - For now this is limited to C++ files compiled with no special options....
foreach (Project.Configuration conf in configurations)
{
if (conf.FastBuildBlobbed && (sourceFiles.Count > 0 || conf.Project.IsFastBuildAll))
{
// For now, this will do.
var subConfig = s_DefaultSubConfig;
Dictionary<SubConfig, List<Vcxproj.ProjectFile>> subConfigs = null;
if (!confSubConfigs.TryGetValue(conf, out subConfigs))
{
subConfigs = new Dictionary<SubConfig, List<Vcxproj.ProjectFile>>();
confSubConfigs.Add(conf, subConfigs);
}
List<Vcxproj.ProjectFile> subConfigFiles = null;
if (!subConfigs.TryGetValue(subConfig, out subConfigFiles))
{
subConfigFiles = new List<Vcxproj.ProjectFile>();
subConfigs.Add(subConfig, subConfigFiles);
}
}
}
return confSubConfigs;
}
private static bool ShouldMakePathRelative(string path, Project project)
{
string rootPath = FastBuildSettings.WorkspaceRoot ?? project.RootPath;
return path.StartsWith(rootPath, StringComparison.OrdinalIgnoreCase);
}
private bool IsFileInInputPathList(Strings inputPaths, string path)
{
// Convert each of file paths to each of the input paths and try to
// find the first one not starting from ..(ie the file is in the tested input path)
foreach (string inputAbsPath in inputPaths)
{
string sourceFileRelativeTmp = Util.PathGetRelative(inputAbsPath, path, true);
if (!sourceFileRelativeTmp.StartsWith("..", StringComparison.Ordinal))
return true;
}
return false;
}
}
}