Sharpmake.Platforms/Sharpmake.CommonPlatforms/Windows/Win64Platform.cs (408 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.IO; using System.Linq; using Sharpmake.Generators; using Sharpmake.Generators.FastBuild; using Sharpmake.Generators.VisualStudio; namespace Sharpmake { public static partial class Windows { [PlatformImplementation(Platform.win64, typeof(IPlatformDescriptor), typeof(Project.Configuration.IConfigurationTasks), typeof(IFastBuildCompilerSettings), typeof(IWindowsFastBuildCompilerSettings), typeof(IPlatformBff), typeof(IMicrosoftPlatformBff), typeof(IPlatformVcxproj))] public sealed class Win64Platform : BaseWindowsPlatform { #region IPlatformDescriptor implementation public override string SimplePlatformString => "Win64"; public override string GetToolchainPlatformString(ITarget target) => "x64"; public override EnvironmentVariableResolver GetPlatformEnvironmentResolver(params VariableAssignment[] assignments) { return new Win64EnvironmentVariableResolver(assignments); } #endregion #region IMicrosoftPlatformBff implementation public override string BffPlatformDefine => "WIN64"; public override bool SupportsResourceFiles => true; public override string CConfigName(Configuration conf) { var platformToolset = Options.GetObject<Options.Vc.General.PlatformToolset>(conf); string platformToolsetString = string.Empty; if (platformToolset != Options.Vc.General.PlatformToolset.Default && !platformToolset.IsDefaultToolsetForDevEnv(conf.Target.GetFragment<DevEnv>())) platformToolsetString = $"_{platformToolset}"; string lldString = string.Empty; var useLldLink = Options.GetObject<Options.Vc.LLVM.UseLldLink>(conf); if (useLldLink == Options.Vc.LLVM.UseLldLink.Enable || (useLldLink == Options.Vc.LLVM.UseLldLink.Default && platformToolset.IsLLVMToolchain())) { lldString = "_LLD"; } if (platformToolset.IsLLVMToolchain()) { Options.Vc.General.PlatformToolset overridenPlatformToolset = Options.Vc.General.PlatformToolset.Default; if (Options.WithArgOption<Options.Vc.General.PlatformToolset>.Get<Options.Clang.Compiler.LLVMVcPlatformToolset>(conf, ref overridenPlatformToolset)) platformToolsetString += $"_{overridenPlatformToolset}"; } return $".win64{platformToolsetString}{lldString}Config"; } public override string CppConfigName(Configuration conf) { return CConfigName(conf); } public override void AddCompilerSettings( IDictionary<string, CompilerSettings> masterCompilerSettings, Project.Configuration conf ) { var projectRootPath = conf.Project.RootPath; var devEnv = conf.Target.GetFragment<DevEnv>(); var platform = conf.Target.GetPlatform(); string compilerName = "Compiler-" + Util.GetToolchainPlatformString(platform, conf.Target); var platformToolset = Options.GetObject<Options.Vc.General.PlatformToolset>(conf); if (platformToolset.IsLLVMToolchain()) { var useClangCl = Options.GetObject<Options.Vc.LLVM.UseClangCl>(conf); // Use default platformToolset to get MS compiler instead of Clang, when ClangCl is disabled if (useClangCl == Options.Vc.LLVM.UseClangCl.Disable) { Options.Vc.General.PlatformToolset overridenPlatformToolset = Options.Vc.General.PlatformToolset.Default; if (Options.WithArgOption<Options.Vc.General.PlatformToolset>.Get<Options.Clang.Compiler.LLVMVcPlatformToolset>(conf, ref overridenPlatformToolset)) platformToolset = overridenPlatformToolset; else platformToolset = Options.Vc.General.PlatformToolset.Default; } } if (platformToolset != Options.Vc.General.PlatformToolset.Default && !platformToolset.IsDefaultToolsetForDevEnv(devEnv)) compilerName += "-" + platformToolset; else compilerName += "-" + devEnv; CompilerSettings compilerSettings = GetMasterCompilerSettings(masterCompilerSettings, compilerName, devEnv, projectRootPath, platformToolset, false); compilerSettings.PlatformFlags |= Platform.win64; SetConfiguration(conf, compilerSettings.Configurations, CppConfigName(conf), projectRootPath, devEnv, false); } public CompilerSettings GetMasterCompilerSettings( IDictionary<string, CompilerSettings> masterCompilerSettings, string compilerName, DevEnv devEnv, string projectRootPath, Options.Vc.General.PlatformToolset platformToolset, bool useCCompiler ) { CompilerSettings compilerSettings; if (masterCompilerSettings.ContainsKey(compilerName)) { compilerSettings = masterCompilerSettings[compilerName]; } else { DevEnv? compilerDevEnv = null; string platformToolSetPath = null; string pathToCompiler = null; string compilerExeName = null; var compilerFamily = Sharpmake.CompilerFamily.Auto; var fastBuildSettings = PlatformRegistry.Get<IFastBuildCompilerSettings>(Platform.win64); switch (platformToolset) { case Options.Vc.General.PlatformToolset.Default: compilerDevEnv = devEnv; break; case Options.Vc.General.PlatformToolset.LLVM: case Options.Vc.General.PlatformToolset.ClangCL: platformToolSetPath = platformToolset == Options.Vc.General.PlatformToolset.ClangCL ? ClangForWindows.Settings.LLVMInstallDirVsEmbedded(devEnv) : ClangForWindows.Settings.LLVMInstallDir; pathToCompiler = Path.Combine(platformToolSetPath, "bin"); compilerExeName = "clang-cl.exe"; var compilerFamilyKey = new FastBuildWindowsCompilerFamilyKey(devEnv, platformToolset); if (!fastBuildSettings.CompilerFamily.TryGetValue(compilerFamilyKey, out compilerFamily)) compilerFamily = Sharpmake.CompilerFamily.ClangCl; break; default: compilerDevEnv = platformToolset.GetDefaultDevEnvForToolset(); break; } if (compilerDevEnv.HasValue) { platformToolSetPath = Path.Combine(compilerDevEnv.Value.GetVisualStudioDir(), "VC"); pathToCompiler = compilerDevEnv.Value.GetVisualStudioBinPath(Platform.win64); compilerExeName = "cl.exe"; var compilerFamilyKey = new FastBuildWindowsCompilerFamilyKey(devEnv, platformToolset); if (!fastBuildSettings.CompilerFamily.TryGetValue(compilerFamilyKey, out compilerFamily)) compilerFamily = Sharpmake.CompilerFamily.MSVC; } Strings extraFiles = new Strings(); { Strings userExtraFiles; if (fastBuildSettings.ExtraFiles.TryGetValue(devEnv, out userExtraFiles)) extraFiles.AddRange(userExtraFiles); } if (compilerDevEnv.HasValue) { extraFiles.Add( @"$ExecutableRootPath$\c1.dll", @"$ExecutableRootPath$\c1xx.dll", @"$ExecutableRootPath$\c2.dll", @"$ExecutableRootPath$\mspdbcore.dll", @"$ExecutableRootPath$\mspdbsrv.exe", @"$ExecutableRootPath$\1033\clui.dll" ); if (compilerDevEnv.Value.IsVisualStudio()) { string systemDllPath = FastBuildSettings.SystemDllRoot; if (systemDllPath == null) { var windowsTargetPlatformVersion = KitsRootPaths.GetWindowsTargetPlatformVersionForDevEnv(compilerDevEnv.Value); string redistDirectory; if (windowsTargetPlatformVersion <= Options.Vc.General.WindowsTargetPlatformVersion.v10_0_17134_0) redistDirectory = @"Redist\ucrt\DLLs\x64\"; else redistDirectory = $@"Redist\{windowsTargetPlatformVersion.ToVersionString()}\ucrt\DLLs\x64\"; systemDllPath = Path.Combine(KitsRootPaths.GetRoot(KitsRootEnum.KitsRoot10), redistDirectory); } if (!Path.IsPathRooted(systemDllPath)) systemDllPath = Util.SimplifyPath(Path.Combine(projectRootPath, systemDllPath)); extraFiles.Add( @"$ExecutableRootPath$\msobj140.dll", @"$ExecutableRootPath$\mspft140.dll", @"$ExecutableRootPath$\mspdb140.dll" ); if (compilerDevEnv.Value == DevEnv.vs2015) { extraFiles.Add( @"$ExecutableRootPath$\vcvars64.bat", Path.Combine(platformToolSetPath, @"redist\x64\Microsoft.VC140.CRT\concrt140.dll"), Path.Combine(platformToolSetPath, @"redist\x64\Microsoft.VC140.CRT\msvcp140.dll"), Path.Combine(platformToolSetPath, @"redist\x64\Microsoft.VC140.CRT\vccorlib140.dll"), Path.Combine(platformToolSetPath, @"redist\x64\Microsoft.VC140.CRT\vcruntime140.dll"), Path.Combine(systemDllPath, "ucrtbase.dll") ); } else { extraFiles.Add( @"$ExecutableRootPath$\mspdbcore.dll", @"$ExecutableRootPath$\msvcdis140.dll", @"$ExecutableRootPath$\msvcp140.dll", @"$ExecutableRootPath$\pgodb140.dll", @"$ExecutableRootPath$\vcruntime140.dll", Path.Combine(platformToolSetPath, @"Auxiliary\Build\vcvars64.bat") ); } if (compilerDevEnv.Value >= DevEnv.vs2019) { Version toolsVersion = compilerDevEnv.Value.GetVisualStudioVCToolsVersion(); if (toolsVersion >= new Version("14.22.27905")) // 16.3.2 extraFiles.Add(@"$ExecutableRootPath$\tbbmalloc.dll"); if (toolsVersion >= new Version("14.25.28610")) // 16.5 extraFiles.Add(@"$ExecutableRootPath$\vcruntime140_1.dll"); if (toolsVersion >= new Version("14.28.29333")) // 16.8 extraFiles.Add(@"$ExecutableRootPath$\msvcp140_atomic_wait.dll"); } try { foreach (string p in Util.DirectoryGetFiles(systemDllPath, "api-ms-win-*.dll")) extraFiles.Add(p); } catch { } } else { throw new NotImplementedException("This devEnv (" + compilerDevEnv.Value + ") is not supported!"); } } string executable = Path.Combine("$ExecutableRootPath$", compilerExeName); compilerSettings = new CompilerSettings(compilerName, compilerFamily, Platform.win64, extraFiles, executable, pathToCompiler, devEnv, new Dictionary<string, CompilerSettings.Configuration>()); masterCompilerSettings.Add(compilerName, compilerSettings); } return compilerSettings; } private void SetConfiguration( Project.Configuration conf, IDictionary<string, CompilerSettings.Configuration> configurations, string configName, string projectRootPath, DevEnv devEnv, bool useCCompiler) { if (configurations.ContainsKey(configName)) return; string linkerPathOverride = null; string linkerExeOverride = null; string librarianExeOverride = null; GetLinkerExecutableInfo(conf, out linkerPathOverride, out linkerExeOverride, out librarianExeOverride); var fastBuildCompilerSettings = PlatformRegistry.Get<IWindowsFastBuildCompilerSettings>(Platform.win64); string binPath; if (!fastBuildCompilerSettings.BinPath.TryGetValue(devEnv, out binPath)) binPath = devEnv.GetVisualStudioBinPath(Platform.win64); string linkerPath; if (!string.IsNullOrEmpty(linkerPathOverride)) linkerPath = linkerPathOverride; else if (!fastBuildCompilerSettings.LinkerPath.TryGetValue(devEnv, out linkerPath)) linkerPath = binPath; string linkerExe; if (!string.IsNullOrEmpty(linkerExeOverride)) linkerExe = linkerExeOverride; else if (!fastBuildCompilerSettings.LinkerExe.TryGetValue(devEnv, out linkerExe)) linkerExe = "link.exe"; string librarianExe; if (!string.IsNullOrEmpty(librarianExeOverride)) librarianExe = librarianExeOverride; else if (!fastBuildCompilerSettings.LibrarianExe.TryGetValue(devEnv, out librarianExe)) librarianExe = "lib.exe"; string resCompiler; if (!fastBuildCompilerSettings.ResCompiler.TryGetValue(devEnv, out resCompiler)) resCompiler = devEnv.GetWindowsResourceCompiler(Platform.win64); string capitalizedBinPath = Util.GetCapitalizedPath(Util.PathGetAbsolute(projectRootPath, binPath)); configurations.Add( configName, new CompilerSettings.Configuration( Platform.win64, binPath: capitalizedBinPath, linkerPath: Util.GetCapitalizedPath(Util.PathGetAbsolute(projectRootPath, linkerPath)), resourceCompiler: Util.GetCapitalizedPath(Util.PathGetAbsolute(projectRootPath, resCompiler)), librarian: Path.Combine(@"$LinkerPath$", librarianExe), linker: Path.Combine(@"$LinkerPath$", linkerExe) ) ); string masmConfigurationName = configName + "Masm"; var masmConfiguration = new CompilerSettings.Configuration( Platform.win64, compiler: "ML" + masmConfigurationName, usingOtherConfiguration: configName ); masmConfiguration.Masm = Path.Combine(capitalizedBinPath, "ml64.exe"); configurations.Add( masmConfigurationName, masmConfiguration ); string nasmConfigurationName = configName + "Nasm"; var nasmConfiguration = new CompilerSettings.Configuration( Platform.win64, compiler: "Nasm" + nasmConfigurationName, usingOtherConfiguration: configName ); nasmConfiguration.Nasm = conf.Project.NasmExePath; configurations.Add( nasmConfigurationName, nasmConfiguration ); } #endregion #region IPlatformVcxproj implementation public override bool HasEditAndContinueDebuggingSupport => true; public override IEnumerable<string> GetImplicitlyDefinedSymbols(IGenerationContext context) { var defines = new List<string>(); defines.AddRange(base.GetImplicitlyDefinedSymbols(context)); defines.Add("WIN64"); return defines; } public override void SetupPlatformTargetOptions(IGenerationContext context) { context.Options["TargetMachine"] = "MachineX64"; context.CommandLineOptions["TargetMachine"] = "/MACHINE:X64"; context.CommandLineOptions["NasmCompilerFormat"] = "-fwin64"; } public override void SelectPlatformAdditionalDependenciesOptions(IGenerationContext context) { base.SelectPlatformAdditionalDependenciesOptions(context); context.Options["AdditionalDependencies"] += ";%(AdditionalDependencies)"; } protected override 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.Sort(); return includePaths; } protected override IEnumerable<IncludeWithPrefix> GetPlatformIncludePathsWithPrefixImpl(IGenerationContext context) { var includes = new List<IncludeWithPrefix>(); string includePrefix = "/I"; var platformToolset = Options.GetObject<Options.Vc.General.PlatformToolset>(context.Configuration); DevEnv devEnv = platformToolset.GetDefaultDevEnvForToolset() ?? context.DevelopmentEnvironment; if (platformToolset.IsLLVMToolchain() && Options.GetObject<Options.Vc.LLVM.UseClangCl>(context.Configuration) == Options.Vc.LLVM.UseClangCl.Enable) { // when using clang-cl, mark MSVC includes, so they are properly recognized includePrefix = "/clang:-isystem"; Options.Vc.General.PlatformToolset overridenPlatformToolset = Options.Vc.General.PlatformToolset.Default; if (Options.WithArgOption<Options.Vc.General.PlatformToolset>.Get<Options.Clang.Compiler.LLVMVcPlatformToolset>(context.Configuration, ref overridenPlatformToolset)) { platformToolset = overridenPlatformToolset; devEnv = platformToolset.GetDefaultDevEnvForToolset() ?? context.DevelopmentEnvironment; } string clangIncludePath = platformToolset == Options.Vc.General.PlatformToolset.ClangCL ? ClangForWindows.GetWindowsClangIncludePath(devEnv) : ClangForWindows.GetWindowsClangIncludePath(); includes.Add(new IncludeWithPrefix(includePrefix, clangIncludePath)); } else { // this option is mandatory when using /external:I with msvc, so if the user has selected it // we consider that the vcxproj supports ExternalIncludePath if (Options.HasOption<Options.Vc.General.ExternalWarningLevel>(context.Configuration)) includePrefix = "/external:I"; } IEnumerable<string> msvcIncludePaths = EnumerateSemiColonSeparatedString(devEnv.GetWindowsIncludePath()); includes.AddRange(msvcIncludePaths.Select(path => new IncludeWithPrefix(includePrefix, path))); // Additional system includes OrderableStrings SystemIncludes = new OrderableStrings(context.Configuration.DependenciesIncludeSystemPaths); SystemIncludes.AddRange(context.Configuration.IncludeSystemPaths); if (SystemIncludes.Count > 0) { SystemIncludes.Sort(); includes.AddRange(SystemIncludes.Select(path => new IncludeWithPrefix(includePrefix, path))); } return includes; } public override void GeneratePlatformSpecificProjectDescription(IVcxprojGenerationContext context, IFileGenerator generator) { string propertyGroups = string.Empty; // MSBuild override when mixing devenvs in the same vcxproj is not supported, // but before throwing an exception check if we have some override for (DevEnv devEnv = context.DevelopmentEnvironmentsRange.MinDevEnv; devEnv <= context.DevelopmentEnvironmentsRange.MaxDevEnv; devEnv = (DevEnv)((int)devEnv << 1)) { switch (devEnv) { case DevEnv.vs2015: case DevEnv.vs2017: { string platformFolder = MSBuildGlobalSettings.GetCppPlatformFolder(devEnv, Platform.win64); if (!string.IsNullOrEmpty(platformFolder)) { using (generator.Declare("platformFolder", Util.EnsureTrailingSeparator(platformFolder))) // _PlatformFolder require the path to end with a "\" propertyGroups += generator.Resolver.Resolve(Vcxproj.Template.Project.PlatformFolderOverride); } } break; case DevEnv.vs2019: case DevEnv.vs2022: { // Note1: _PlatformFolder override is deprecated starting with vs2019, so we write AdditionalVCTargetsPath instead // Note2: MSBuildGlobalSettings.SetCppPlatformFolder for vs2019 and above is no more the valid way to handle it. if (!string.IsNullOrEmpty(MSBuildGlobalSettings.GetCppPlatformFolder(devEnv, Platform.win64))) throw new Error($"SetCppPlatformFolder is not supported by {devEnv}: use of MSBuildGlobalSettings.SetCppPlatformFolder should be replaced by use of MSBuildGlobalSettings.SetAdditionalVCTargetsPath."); // vs2019 and up use AdditionalVCTargetsPath string additionalVCTargetsPath = MSBuildGlobalSettings.GetAdditionalVCTargetsPath(devEnv, Platform.win64); if (!string.IsNullOrEmpty(additionalVCTargetsPath)) { using (generator.Declare("additionalVCTargetsPath", Util.EnsureTrailingSeparator(additionalVCTargetsPath))) // the path shall end with a "\" propertyGroups += generator.Resolver.Resolve(Vcxproj.Template.Project.AdditionalVCTargetsPath); } } break; } } string llvmOverrideSection = ClangForWindows.GetLLVMOverridesSection(context, generator.Resolver); if (!string.IsNullOrEmpty(llvmOverrideSection)) propertyGroups += llvmOverrideSection; if (!string.IsNullOrEmpty(propertyGroups)) { if (context.DevelopmentEnvironmentsRange.MinDevEnv != context.DevelopmentEnvironmentsRange.MaxDevEnv) throw new Error("Different vs versions not supported in the same vcxproj"); using (generator.Declare("platformName", GetToolchainPlatformString(null))) { generator.Write(Vcxproj.Template.Project.ProjectDescriptionStartPlatformConditional); generator.WriteVerbatim(propertyGroups); generator.Write(Vcxproj.Template.Project.PropertyGroupEnd); } } } #endregion } } }