// Copyright (c) Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using System.IO; using Flax.Build.Graph; using Flax.Build.NativeCpp; namespace Flax.Build { partial class Configuration { /// /// Specifies the minimum Windows version to use (eg. 10). /// [CommandLine("winMinVer", "", "Specifies the minimum Windows version to use (eg. 10).")] public static string WindowsMinVer = "10"; /// /// Specifies the minimum CPU architecture type to support (on x86/x64). /// [CommandLine("winCpuArch", "", "Specifies the minimum CPU architecture type to support (on x86/x64).")] public static CpuArchitecture WindowsCpuArch = CpuArchitecture.SSE4_2; // 99.78% support on PC according to Steam Hardware & Software Survey: September 2025 (https://store.steampowered.com/hwsurvey/) } } namespace Flax.Build.Platforms { /// /// The Microsoft Windows toolchain implementation. /// /// /// public sealed class WindowsToolchain : WindowsToolchainBase { private Version _minVersion; /// /// Initializes a new instance of the class. /// /// The platform. /// The target architecture. public WindowsToolchain(WindowsPlatform platform, TargetArchitecture architecture) : base(platform, architecture, WindowsPlatformToolset.Latest, WindowsPlatformSDK.Latest) { // Select minimum Windows version if (!Utilities.ParseVersion(Configuration.WindowsMinVer, out _minVersion)) _minVersion = new Version(7, 0); } /// public override void SetupEnvironment(BuildOptions options) { base.SetupEnvironment(options); options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_WINDOWS"); int winVer; if (_minVersion.Major >= 10) winVer = 0x0A00; // Windows 10 else if (_minVersion.Major == 8 && _minVersion.Minor >= 1) winVer = 0x0603; // Windows 8.1 else if (_minVersion.Major == 8) winVer = 0x0602; // Windows 8 else winVer = 0x0601; // Windows 7 options.CompileEnv.PreprocessorDefinitions.Add($"WINVER=0x{winVer:X4}"); options.LinkEnv.InputLibraries.Add("dwmapi.lib"); options.LinkEnv.InputLibraries.Add("kernel32.lib"); options.LinkEnv.InputLibraries.Add("user32.lib"); options.LinkEnv.InputLibraries.Add("comdlg32.lib"); options.LinkEnv.InputLibraries.Add("advapi32.lib"); options.LinkEnv.InputLibraries.Add("shell32.lib"); options.LinkEnv.InputLibraries.Add("ole32.lib"); options.LinkEnv.InputLibraries.Add("oleaut32.lib"); options.LinkEnv.InputLibraries.Add("delayimp.lib"); options.CompileEnv.CpuArchitecture = Configuration.WindowsCpuArch; if (options.Architecture == TargetArchitecture.x64) { if (_minVersion.Major <= 7 && options.CompileEnv.CpuArchitecture == CpuArchitecture.AVX2) { // Old Windows had lower support ratio for latest CPU features options.CompileEnv.CpuArchitecture = CpuArchitecture.AVX; } if (_minVersion.Major >= 11 && options.CompileEnv.CpuArchitecture == CpuArchitecture.AVX) { // Windows 11 has hard requirement on SSE4.2 options.CompileEnv.CpuArchitecture = CpuArchitecture.SSE4_2; } } else if (options.Architecture == TargetArchitecture.ARM64) { options.CompileEnv.PreprocessorDefinitions.Add("USE_SOFT_INTRINSICS"); options.LinkEnv.InputLibraries.Add("softintrin.lib"); if (options.CompileEnv.CpuArchitecture != CpuArchitecture.None) options.CompileEnv.CpuArchitecture = CpuArchitecture.NEON; } } /// protected override void SetupCompileCppFilesArgs(TaskGraph graph, BuildOptions options, List args) { base.SetupCompileCppFilesArgs(graph, options, args); switch (options.CompileEnv.CpuArchitecture) { case CpuArchitecture.AVX: args.Add("/arch:AVX"); break; case CpuArchitecture.AVX2: args.Add("/arch:AVX2"); break; case CpuArchitecture.AVX512: args.Add("/arch:AVX512"); break; case CpuArchitecture.SSE2: args.Add("/arch:SSE2"); break; case CpuArchitecture.SSE4_2: args.Add("/arch:SSE4.2"); break; } } /// public override void LinkFiles(TaskGraph graph, BuildOptions options, string outputFilePath) { // Compile and include resource file if need to if (options.Target.Win32ResourceFile != null && !options.Target.IsPreBuilt && (options.LinkEnv.Output == LinkerOutput.Executable || options.LinkEnv.Output == LinkerOutput.SharedLibrary)) { var task = graph.Add(); var args = new List(); var sourceFile = options.Target.Win32ResourceFile; // Suppress Startup Banner args.Add("/nologo"); // Language args.Add("/l 0x0409"); // Add preprocessor definitions foreach (var definition in options.CompileEnv.PreprocessorDefinitions) args.Add(string.Format("/D \"{0}\"", definition)); args.Add(string.Format("/D \"ORIGINAL_FILENAME=\\\"{0}\\\"\"", Path.GetFileName(outputFilePath))); args.Add(string.Format("/D \"PRODUCT_NAME=\\\"{0}\\\"\"", options.Target.ProjectName + " " + options.Target.ConfigurationName)); args.Add(string.Format("/D \"PRODUCT_NAME_INTERNAL=\\\"{0}\\\"\"", options.Target.Name)); // Add include paths foreach (var includePath in options.CompileEnv.IncludePaths) AddIncludePath(args, includePath); // Add the resource file to the produced item list var outputFile = Path.Combine(options.IntermediateFolder, Path.GetFileName(outputFilePath) + ".res"); args.Add(string.Format("/Fo\"{0}\"", outputFile)); options.LinkEnv.InputFiles.Add(outputFile); // Request included files to exist task.PrerequisiteFiles.AddRange(IncludesCache.FindAllIncludedFiles(sourceFile)); // Add the source file args.Add(string.Format("\"{0}\"", sourceFile)); task.ProducedFiles.Add(outputFile); task.WorkingDirectory = options.WorkingDirectory; task.CommandPath = _resourceCompilerPath; task.CommandArguments = string.Join(" ", args); task.PrerequisiteFiles.Add(sourceFile); task.Cost = 1; } base.LinkFiles(graph, options, outputFilePath); } } }