Sharpmake.Platforms/Sharpmake.CommonPlatforms/BasePlatform.cs (450 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.Generic;
using System.Linq;
using Sharpmake.Generators;
using Sharpmake.Generators.FastBuild;
using Sharpmake.Generators.VisualStudio;
namespace Sharpmake
{
public abstract partial class BasePlatform : IPlatformDescriptor, IPlatformBff, IPlatformVcxproj
{
#region IPlatformDescriptor
public abstract string SimplePlatformString { get; }
public abstract string GetToolchainPlatformString(ITarget target);
public abstract bool IsMicrosoftPlatform { get; }
public abstract bool IsPcPlatform { get; }
public abstract bool IsUsingClang { get; }
public abstract bool IsLinkerInvokedViaCompiler { get; set; }
public abstract bool HasDotNetSupport { get; }
public abstract bool HasSharedLibrarySupport { get; }
public virtual bool HasPrecompiledHeaderSupport => true;
public virtual EnvironmentVariableResolver GetPlatformEnvironmentResolver(params VariableAssignment[] parameters)
{
//
// TODO: EnvironmentVariableResolver is not an actual environment variable resolver,
// and doesn't care about environment variables, just those passed to it in the
// argument. This causes it to attempt to resolve environment variable that were
// not passed to it, and throw an exception for it.
//
//return new EnvironmentVariableResolver(assignments);
return null;
}
#endregion
#region IPlatformBff implementation
protected const string RemoveLineTag = FileGeneratorUtilities.RemoveLineTag;
public virtual string BffPlatformDefine => null;
public virtual string CConfigName(Configuration conf)
{
return string.Empty;
}
public virtual string CppConfigName(Configuration conf)
{
return string.Empty;
}
public virtual void SelectPreprocessorDefinitionsBff(IBffGenerationContext context)
{
var platformDescriptor = PlatformRegistry.Get<IPlatformDescriptor>(context.Configuration.Platform);
string platformDefineSwitch = platformDescriptor.IsUsingClang ? "-D" : "/D";
var defines = new Strings();
defines.AddRange(context.Options.ExplicitDefines);
defines.AddRange(context.Configuration.Defines);
if (defines.Count > 0)
{
var fastBuildDefines = new List<string>();
foreach (string define in defines.SortedValues)
{
if (!string.IsNullOrWhiteSpace(define))
fastBuildDefines.Add(string.Format(@"{0}{1}{2}{1}", platformDefineSwitch, Util.DoubleQuotes, define.Replace(Util.DoubleQuotes, Util.EscapedDoubleQuotes)));
}
context.CommandLineOptions["PreprocessorDefinitions"] = string.Join($"'{Environment.NewLine} + ' ", fastBuildDefines);
}
else
{
context.CommandLineOptions["PreprocessorDefinitions"] = FileGeneratorUtilities.RemoveLineTag;
}
Strings resourceDefines = Options.GetStrings<Options.Vc.ResourceCompiler.PreprocessorDefinitions>(context.Configuration);
if (resourceDefines.Any())
{
var fastBuildDefines = new List<string>();
foreach (string resourceDefine in resourceDefines.SortedValues)
{
if (!string.IsNullOrWhiteSpace(resourceDefine))
fastBuildDefines.Add(string.Format(@"""{0}{1}""", platformDefineSwitch, resourceDefine.Replace(Util.DoubleQuotes, Util.EscapedDoubleQuotes)));
}
context.CommandLineOptions["ResourcePreprocessorDefinitions"] = string.Join($"'{Environment.NewLine} + ' ", fastBuildDefines);
}
else
{
context.CommandLineOptions["ResourcePreprocessorDefinitions"] = FileGeneratorUtilities.RemoveLineTag;
}
}
public virtual void SelectAdditionalCompilerOptionsBff(IBffGenerationContext context)
{
}
public virtual void SetupExtraLinkerSettings(IFileGenerator fileGenerator, Project.Configuration configuration, string fastBuildOutputFile)
{
SetupExtraLinkerSettings(fileGenerator, configuration.Output);
}
private void SetupExtraLinkerSettings(IFileGenerator fileGenerator, Project.Configuration.OutputType outputType)
{
using (fileGenerator.Resolver.NewScopedParameter("dllOption", outputType == Project.Configuration.OutputType.Dll ? " /DLL" : ""))
{
fileGenerator.Write(Bff.Template.ConfigurationFile.LinkerOptions);
}
}
public virtual IEnumerable<Project.Configuration.BuildStepBase> GetExtraPostBuildEvents(Project.Configuration configuration, string fastBuildOutputFile)
{
return Enumerable.Empty<Project.Configuration.BuildStepBase>();
}
public virtual IEnumerable<Project.Configuration.BuildStepExecutable> GetExtraStampEvents(Project.Configuration configuration, string fastBuildOutputFile)
{
return Enumerable.Empty<Project.Configuration.BuildStepExecutable>();
}
public virtual string GetOutputFilename(Project.Configuration.OutputType outputType, string fastBuildOutputFile) => fastBuildOutputFile;
public virtual void AddCompilerSettings(IDictionary<string, CompilerSettings> masterCompilerSettings, Project.Configuration conf)
{
}
#endregion
#region IPlatformVcxproj implementation
public abstract string ExecutableFileFullExtension { get; }
public virtual string PackageFileFullExtension => ExecutableFileFullExtension;
public abstract string SharedLibraryFileFullExtension { get; }
public abstract string ProgramDatabaseFileFullExtension { get; }
public virtual string StaticLibraryFileFullExtension => ".lib";
public virtual string StaticOutputLibraryFileFullExtension => StaticLibraryFileFullExtension;
public virtual bool ExcludesPrecompiledHeadersFromBuild => false;
public virtual bool HasUserAccountControlSupport => false;
public virtual bool HasEditAndContinueDebuggingSupport => false;
public virtual void SetupDeleteExtensionsOnCleanOptions(IGenerationContext context)
{
}
public virtual IEnumerable<string> GetImplicitlyDefinedSymbols(IGenerationContext context)
{
yield break;
}
public virtual IEnumerable<string> GetLibraryPaths(IGenerationContext context)
{
yield break;
}
public virtual IEnumerable<string> GetLibraryFiles(IGenerationContext context)
{
yield break;
}
public virtual IEnumerable<string> GetPlatformLibraryFiles(IGenerationContext context)
{
yield break;
}
public IEnumerable<string> GetIncludePaths(IGenerationContext context)
{
return GetIncludePathsImpl(context);
}
public IEnumerable<string> GetPlatformIncludePaths(IGenerationContext context)
{
return GetPlatformIncludePathsWithPrefixImpl(context).Select(x => x.Path);
}
public IEnumerable<IncludeWithPrefix> GetPlatformIncludePathsWithPrefix(IGenerationContext context)
{
return GetPlatformIncludePathsWithPrefixImpl(context);
}
public IEnumerable<string> GetResourceIncludePaths(IGenerationContext context)
{
return GetResourceIncludePathsImpl(context);
}
public IEnumerable<string> GetAssemblyIncludePaths(IGenerationContext context)
{
return GetAssemblyIncludePathsImpl(context);
}
public virtual IEnumerable<string> GetCxUsingPath(IGenerationContext context)
{
yield break;
}
public virtual IEnumerable<VariableAssignment> GetEnvironmentVariables(IGenerationContext context)
{
yield break;
}
public virtual void SetupSdkOptions(IGenerationContext context)
{
}
public virtual void SetupPlatformToolsetOptions(IGenerationContext context)
{
}
public virtual void SetupPlatformTargetOptions(IGenerationContext context)
{
context.Options["TargetMachine"] = FileGeneratorUtilities.RemoveLineTag;
context.CommandLineOptions["TargetMachine"] = FileGeneratorUtilities.RemoveLineTag;
}
public virtual void SelectCompilerOptions(IGenerationContext context)
{
}
protected void FixupPrecompiledHeaderOptions(IGenerationContext context)
{
var options = context.Options;
var cmdLineOptions = context.CommandLineOptions;
var conf = context.Configuration;
if (options["UsePrecompiledHeader"] == "NotUsing")
{
options["UsePrecompiledHeader"] = FileGeneratorUtilities.RemoveLineTag;
}
else
{
Strings pathsToConsider = new Strings(context.ProjectSourceCapitalized);
pathsToConsider.AddRange(context.Project.AdditionalSourceRootPaths);
pathsToConsider.AddRange(GetIncludePaths(context));
string pchFileSourceRelative = context.Options["PrecompiledHeaderThrough"];
string pchFileVcxprojRelative = null;
bool foundPchInclude = false;
foreach (var includePath in pathsToConsider)
{
var pchFile = Util.PathGetAbsolute(includePath, pchFileSourceRelative);
if (conf.Project.ResolvedSourceFiles.Contains(pchFile))
{
pchFileVcxprojRelative = Util.PathGetRelative(context.ProjectDirectory, pchFile, true);
foundPchInclude = true;
break;
}
}
if (!foundPchInclude)
{
foreach (var includePath in pathsToConsider)
{
var pchFile = Util.PathGetAbsolute(includePath, pchFileSourceRelative);
if (Util.FileExists(pchFile))
{
pchFileVcxprojRelative = Util.PathGetRelative(context.ProjectDirectory, pchFile, true);
foundPchInclude = true;
break;
}
}
}
if (!foundPchInclude)
throw new Error($"Sharpmake couldn't locate the PCH '{pchFileSourceRelative}' in {conf}");
context.Options["PrecompiledHeaderThrough"] = pchFileVcxprojRelative;
}
}
public virtual void SelectPrecompiledHeaderOptions(IGenerationContext context)
{
}
public virtual void SelectLinkerOptions(IGenerationContext context)
{
}
public virtual void SelectPlatformAdditionalDependenciesOptions(IGenerationContext context)
{
}
public virtual void SelectApplicationFormatOptions(IGenerationContext context)
{
}
public virtual void SelectBuildType(IGenerationContext context)
{
}
public virtual void SelectPreprocessorDefinitionsVcxproj(IVcxprojGenerationContext context)
{
// concat defines, don't add options.Defines since they are automatically added by VS
var defines = new Strings();
defines.AddRange(context.Options.ExplicitDefines);
defines.AddRange(context.Configuration.Defines);
context.Options["PreprocessorDefinitions"] = defines.JoinStrings(";");
}
public virtual bool HasPrecomp(IGenerationContext context)
{
Project.Configuration conf = context.Configuration;
return !string.IsNullOrEmpty(conf.PrecompSource) && !string.IsNullOrEmpty(conf.PrecompHeader);
}
public virtual void GenerateSdkVcxproj(IVcxprojGenerationContext context, IFileGenerator generator)
{
}
public virtual void GenerateMakefileConfigurationVcxproj(IVcxprojGenerationContext context, IFileGenerator generator)
{
}
public virtual void GenerateProjectCompileVcxproj(IVcxprojGenerationContext context, IFileGenerator generator)
{
generator.Write(_projectConfigurationsCompileTemplate);
}
public virtual void GenerateProjectLinkVcxproj(IVcxprojGenerationContext context, IFileGenerator generator)
{
var simpleOutput = Project.Configuration.SimpleOutputType(context.Configuration.Output);
switch (simpleOutput)
{
case Project.Configuration.OutputType.Lib:
generator.Write(GetProjectStaticLinkVcxprojTemplate());
break;
case Project.Configuration.OutputType.Dll:
generator.Write(GetProjectLinkSharedVcxprojTemplate());
break;
case Project.Configuration.OutputType.Exe:
generator.Write(GetProjectLinkExecutableVcxprojTemplate());
break;
}
}
public virtual void GenerateProjectMasmVcxproj(IVcxprojGenerationContext context, IFileGenerator generator)
{
}
public virtual void GenerateProjectNasmVcxproj(IVcxprojGenerationContext context, IFileGenerator generator)
{
// Fill Assembly include dirs
var preIncludedFiles = new List<string>();
preIncludedFiles.AddRange(context.Project.NasmPreIncludedFiles.AsEnumerable<string>());
string preIncludedFilesJoined = string.Join(';', preIncludedFiles);
using (generator.Declare("ExePath", context.Project.NasmExePath))
using (generator.Declare("PreIncludedFiles", preIncludedFilesJoined))
{
generator.Write(_projectConfigurationsNasmTemplate);
}
}
public virtual void GenerateUserConfigurationFile(Project.Configuration conf, IFileGenerator generator)
{
generator.Write(_userFileConfigurationGeneralTemplate);
}
public virtual void GenerateRunFromPcDeployment(IVcxprojGenerationContext context, IFileGenerator generator)
{
}
protected virtual void WriteWindowsKitsOverrides(IVcxprojGenerationContext context, IFileGenerator fileGenerator)
{
KitsRootEnum? kitsRootWritten = null;
for (DevEnv devEnv = context.DevelopmentEnvironmentsRange.MinDevEnv; devEnv <= context.DevelopmentEnvironmentsRange.MaxDevEnv; devEnv = (DevEnv)((int)devEnv << 1))
{
// there's no need to write the properties with older versions of vs, as we override
// completely the VC++ directories entries in the vcxproj
if (devEnv < DevEnv.vs2015)
continue;
KitsRootEnum kitsRootVersion = KitsRootPaths.GetUseKitsRootForDevEnv(devEnv);
if (kitsRootWritten == null)
kitsRootWritten = kitsRootVersion;
else if (kitsRootWritten != kitsRootVersion)
throw new Error($"Different values of kitsRoot in the same vcxproj {context.ProjectFileName}");
else
continue;
string windowsSdkDirKey = FileGeneratorUtilities.RemoveLineTag;
string windowsSdkDirValue = FileGeneratorUtilities.RemoveLineTag;
string UniversalCRTSdkDir_10 = FileGeneratorUtilities.RemoveLineTag;
string UCRTContentRoot = FileGeneratorUtilities.RemoveLineTag;
string targetPlatformVersionString = FileGeneratorUtilities.RemoveLineTag;
if (kitsRootVersion != KitsRootEnum.KitsRoot81) // 8.1 is the default value for vs2015 and vs2017, so only specify a different platformVersion if we need to
targetPlatformVersionString = KitsRootPaths.GetWindowsTargetPlatformVersionForDevEnv(devEnv).ToVersionString();
if (devEnv.OverridenWindowsPath())
{
windowsSdkDirValue = Util.EnsureTrailingSeparator(KitsRootPaths.GetRoot(kitsRootVersion));
switch (kitsRootVersion)
{
case KitsRootEnum.KitsRoot:
windowsSdkDirKey = "WindowsSdkDir_80";
break;
case KitsRootEnum.KitsRoot81:
windowsSdkDirKey = "WindowsSdkDir_81";
break;
case KitsRootEnum.KitsRoot10:
{
windowsSdkDirKey = "WindowsSdkDir_10";
UniversalCRTSdkDir_10 = windowsSdkDirValue;
// this variable is found in Windows Kits\10\DesignTime\CommonConfiguration\Neutral\uCRT.props
// it is always read from the registry unless overridden, so we need to explicitly set it
UCRTContentRoot = windowsSdkDirValue;
}
break;
default:
throw new NotImplementedException($"Unsupported kitsRoot '{kitsRootVersion}'");
}
}
using (fileGenerator.Declare("windowsSdkDirKey", windowsSdkDirKey))
using (fileGenerator.Declare("windowsSdkDirValue", windowsSdkDirValue))
using (fileGenerator.Declare("UniversalCRTSdkDir_10", UniversalCRTSdkDir_10))
using (fileGenerator.Declare("UCRTContentRoot", UCRTContentRoot))
using (fileGenerator.Declare("targetPlatformVersion", targetPlatformVersionString))
{
fileGenerator.Write(_windowsSDKOverridesBegin);
// vs2015 specific, we need to set the UniversalCRTSdkDir to $(UniversalCRTSdkDir_10) because it is not done in the .props
if (devEnv == DevEnv.vs2015 && !string.Equals(UniversalCRTSdkDir_10, FileGeneratorUtilities.RemoveLineTag, StringComparison.Ordinal))
{
using (fileGenerator.Declare("custompropertyname", "UniversalCRTSdkDir"))
using (fileGenerator.Declare("custompropertyvalue", "$(UniversalCRTSdkDir_10)"))
{
fileGenerator.Write(fileGenerator.Resolver.Resolve(Vcxproj.Template.Project.CustomProperty));
}
}
fileGenerator.Write(_windowsSDKOverridesEnd);
}
}
}
public virtual void GenerateProjectPlatformSdkDirectoryDescription(IVcxprojGenerationContext context, IFileGenerator generator)
{
bool hasNonFastBuildConfig = context.ProjectConfigurations.Any(c => !c.IsFastBuild);
if (hasNonFastBuildConfig)
WriteWindowsKitsOverrides(context, generator);
}
public virtual void GeneratePostDefaultPropsImport(IVcxprojGenerationContext context, IFileGenerator generator)
{
}
public virtual void GenerateProjectConfigurationGeneral(IVcxprojGenerationContext context, IFileGenerator generator)
{
generator.Write(_projectConfigurationsGeneral);
}
public virtual void GenerateProjectConfigurationGeneral2(IVcxprojGenerationContext context, IFileGenerator generator)
{
generator.Write(_projectConfigurationsGeneral2);
}
public virtual void GenerateProjectConfigurationFastBuildMakeFile(IVcxprojGenerationContext context, IFileGenerator generator)
{
generator.Write(_projectConfigurationsFastBuildMakefile);
}
public virtual void GenerateProjectConfigurationCustomMakeFile(IVcxprojGenerationContext context, IFileGenerator generator)
{
generator.Write(_projectConfigurationsCustomMakefile);
}
public virtual void GenerateProjectPlatformImportSheet(IVcxprojGenerationContext context, IFileGenerator generator)
{
}
public virtual void GeneratePlatformResourceFileList(IVcxprojGenerationContext context, IFileGenerator generator, Strings alreadyWrittenPriFiles, IList<Vcxproj.ProjectFile> resourceFiles, IList<Vcxproj.ProjectFile> imageResourceFiles)
{
}
public virtual void GeneratePlatformReferences(IVcxprojGenerationContext context, IFileGenerator generator)
{
}
public virtual void GeneratePlatformSpecificProjectDescription(IVcxprojGenerationContext context, IFileGenerator generator)
{
}
public virtual IEnumerable<Tuple<string, List<Vcxproj.ProjectFile>>> GetPlatformFileLists(IVcxprojGenerationContext context)
{
yield break;
}
public virtual void SetupPlatformLibraryOptions(out string platformLibExtension, out string platformOutputLibExtension, out string platformPrefixExtension, out string platformLibPrefix)
{
platformLibExtension = ".lib";
platformOutputLibExtension = ".lib";
platformPrefixExtension = string.Empty;
platformLibPrefix = string.Empty;
}
protected virtual string GetProjectLinkExecutableVcxprojTemplate()
{
return GetProjectLinkSharedVcxprojTemplate();
}
protected virtual string GetProjectLinkSharedVcxprojTemplate()
{
return _projectConfigurationsLinkTemplate;
}
protected virtual string GetProjectStaticLinkVcxprojTemplate()
{
return _projectConfigurationsStaticLinkTemplate;
}
protected IEnumerable<string> EnumerateSemiColonSeparatedString(string str)
{
string[] dirs = str.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var dir in dirs)
yield return dir;
}
protected virtual IEnumerable<string> GetIncludePathsImpl(IGenerationContext context)
{
var includePaths = new OrderableStrings();
includePaths.AddRange(context.Configuration.IncludePrivatePaths);
includePaths.AddRange(context.Configuration.IncludePaths);
includePaths.AddRange(context.Configuration.DependenciesIncludePaths);
includePaths.AddRange(context.Configuration.IncludeSystemPaths);
includePaths.AddRange(context.Configuration.DependenciesIncludeSystemPaths);
includePaths.Sort();
return includePaths;
}
protected virtual IEnumerable<IncludeWithPrefix> GetPlatformIncludePathsWithPrefixImpl(IGenerationContext context)
{
yield break;
}
protected virtual IEnumerable<string> GetResourceIncludePathsImpl(IGenerationContext context)
{
var resourceIncludePaths = new OrderableStrings();
resourceIncludePaths.AddRange(context.Configuration.ResourceIncludePrivatePaths);
resourceIncludePaths.AddRange(context.Configuration.ResourceIncludePaths);
resourceIncludePaths.AddRange(context.Configuration.DependenciesResourceIncludePaths);
return resourceIncludePaths;
}
protected virtual IEnumerable<string> GetAssemblyIncludePathsImpl(IGenerationContext context)
{
yield break;
}
#endregion
}
}