From 3c0ce95fcfd96138e86baf113af96185dd953c6a Mon Sep 17 00:00:00 2001 From: Thomas Altenburger Date: Wed, 11 Jun 2025 23:56:26 +0200 Subject: [PATCH 1/5] Search Visual Studio if not on PATH --- build/Build.csproj | 3 +- build/BuildWindowsTask.cs | 129 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 127 insertions(+), 5 deletions(-) diff --git a/build/Build.csproj b/build/Build.csproj index 63b5fde..40de881 100644 --- a/build/Build.csproj +++ b/build/Build.csproj @@ -26,7 +26,8 @@ - + + diff --git a/build/BuildWindowsTask.cs b/build/BuildWindowsTask.cs index 9d59c0c..bb101ad 100644 --- a/build/BuildWindowsTask.cs +++ b/build/BuildWindowsTask.cs @@ -1,4 +1,7 @@ +using Microsoft.VisualStudio.Setup.Configuration; +using System.Runtime.InteropServices; + namespace BuildScripts; [TaskName("Build Windows")] @@ -10,12 +13,21 @@ public sealed class BuildWindowsTask : FrostingTask public override void Run(BuildContext context) { + string cmake = "cmake"; + string msbuild = "msbuild"; + + // If processes are not on PATH, we want to retrieve the Visual Studio installation + if (!IsOnPATH(cmake)) + cmake = System.IO.Path.Combine(GetVisualStudioPath(), "Common7\\IDE\\CommonExtensions\\Microsoft\\CMake\\CMake\\bin\\cmake.exe"); + if (!IsOnPATH(msbuild)) + msbuild = System.IO.Path.Combine(GetVisualStudioPath(), "MSBuild\\Current\\Bin\\MSBuild.exe"); + // Build var buildDir = "sdl/build_x64"; context.CreateDirectory(buildDir); context.CreateDirectory($"{context.ArtifactsDir}/win-x64"); - context.StartProcess("cmake", new ProcessSettings { WorkingDirectory = buildDir, Arguments = "-A x64 -D CMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded ../" }); - context.StartProcess("msbuild", new ProcessSettings { WorkingDirectory = buildDir, Arguments = "SDL2.sln /p:Configuration=Release" }); + context.StartProcess(cmake, new ProcessSettings { WorkingDirectory = buildDir, Arguments = "-A x64 -D CMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded ../" }); + context.StartProcess(msbuild, new ProcessSettings { WorkingDirectory = buildDir, Arguments = "SDL2.sln /p:Configuration=Release" }); // Copy artifact context.CreateDirectory(context.ArtifactsDir); @@ -24,11 +36,120 @@ public override void Run(BuildContext context) buildDir = "sdl/build_arm64"; context.CreateDirectory(buildDir); context.CreateDirectory($"{context.ArtifactsDir}/win-arm64"); - context.StartProcess("cmake", new ProcessSettings { WorkingDirectory = buildDir, Arguments = "-A ARM64 -D CMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded ../" }); - context.StartProcess("msbuild", new ProcessSettings { WorkingDirectory = buildDir, Arguments = "SDL2.sln /p:Configuration=Release" }); + context.StartProcess(cmake, new ProcessSettings { WorkingDirectory = buildDir, Arguments = "-A ARM64 -D CMAKE_MSVC_RUNTIME_LIBRARY=MultiThreaded ../" }); + context.StartProcess(msbuild, new ProcessSettings { WorkingDirectory = buildDir, Arguments = "SDL2.sln /p:Configuration=Release" }); // Copy artifact context.CreateDirectory(context.ArtifactsDir); context.CopyFile("sdl/build_arm64/Release/SDL2.dll", $"{context.ArtifactsDir}/win-arm64/SDL2.dll"); } + + private bool IsOnPATH(string process) + { + if (string.IsNullOrEmpty(process)) + return false; + + if (!process.EndsWith(".exe")) + process += ".exe"; + + // check if process exist on PATH env + + var split = Environment.GetEnvironmentVariable("PATH")?.Split(';'); + if (split != null) + { + foreach (var path in split) + { + string processPath = System.IO.Path.Combine(path, process); + if (File.Exists(processPath)) + return true; + } + } + + return false; + } + + private string GetVisualStudioPath() + { + // This code to retrieve where Visual Studio is installed is adapted from + // https://github.com/microsoft/MSBuildLocator + // Surprisingly, this package has code to retrieve a VS installation but + // this feature is disabled from the public nuget... so adapting it from source + + // This will only detect Visual Studio 2015 and above + + string path = string.Empty; + Version version = new Version(); + + try + { + // This code is not obvious. See the sample (link above) for reference. + var query = (ISetupConfiguration2)GetQuery(); + var e = query.EnumAllInstances(); + + int fetched; + var instances = new ISetupInstance[1]; + do + { + // Call e.Next to query for the next instance (single item or nothing returned). + e.Next(1, instances, out fetched); + if (fetched <= 0) continue; + + var instance = (ISetupInstance2)instances[0]; + InstanceState state = instance.GetState(); + + // If the install was complete + if (state == InstanceState.Complete || + (state.HasFlag(InstanceState.Registered) && state.HasFlag(InstanceState.NoRebootRequired))) + { + if (!Version.TryParse(instance.GetInstallationVersion(), out var current)) + continue; + + // We want the highest version installed + if (current <= version) + continue; + + version = current; + path = instance.GetInstallationPath(); + } + } + while (fetched > 0); + } + catch (COMException) + { + } + catch (DllNotFoundException) + { + // This is OK, VS "15" or greater likely not installed. + } + + return path; + } + + private static ISetupConfiguration GetQuery() + { + const int REGDB_E_CLASSNOTREG = unchecked((int)0x80040154); + + try + { + // Try to CoCreate the class object. + return new SetupConfiguration(); + } + + catch (COMException ex) when (ex.ErrorCode == REGDB_E_CLASSNOTREG) + { + // Try to get the class object using app-local call. + ISetupConfiguration query; + var result = GetSetupConfiguration(out query, IntPtr.Zero); + + if (result < 0) + throw new COMException($"Failed to get {nameof(query)}", result); + + return query; + } + } + + [DllImport("Microsoft.VisualStudio.Setup.Configuration.Native.dll", ExactSpelling = true, PreserveSig = true)] + private static extern int GetSetupConfiguration( + [MarshalAs(UnmanagedType.Interface)][Out] out ISetupConfiguration configuration, + IntPtr reserved); } From 8b52612877e82b5ea73a397f35e4d6917feee09b Mon Sep 17 00:00:00 2001 From: Thomas Altenburger Date: Mon, 14 Jul 2025 18:30:15 +0200 Subject: [PATCH 2/5] Use vswhere to locate Visual Studio installations --- build/Build.csproj | 3 +- build/BuildWindowsTask.cs | 100 ++++++++------------------------------ 2 files changed, 21 insertions(+), 82 deletions(-) diff --git a/build/Build.csproj b/build/Build.csproj index 40de881..63b5fde 100644 --- a/build/Build.csproj +++ b/build/Build.csproj @@ -26,8 +26,7 @@ - - + diff --git a/build/BuildWindowsTask.cs b/build/BuildWindowsTask.cs index bb101ad..d10fa48 100644 --- a/build/BuildWindowsTask.cs +++ b/build/BuildWindowsTask.cs @@ -1,7 +1,4 @@ -using Microsoft.VisualStudio.Setup.Configuration; -using System.Runtime.InteropServices; - namespace BuildScripts; [TaskName("Build Windows")] @@ -52,7 +49,7 @@ private bool IsOnPATH(string process) if (!process.EndsWith(".exe")) process += ".exe"; - // check if process exist on PATH env + // Check if process exist on PATH env var split = Environment.GetEnvironmentVariable("PATH")?.Split(';'); if (split != null) @@ -70,86 +67,29 @@ private bool IsOnPATH(string process) private string GetVisualStudioPath() { - // This code to retrieve where Visual Studio is installed is adapted from - // https://github.com/microsoft/MSBuildLocator - // Surprisingly, this package has code to retrieve a VS installation but - // this feature is disabled from the public nuget... so adapting it from source - - // This will only detect Visual Studio 2015 and above - - string path = string.Empty; - Version version = new Version(); - - try + // We can use vswhere.exe which ships with VS2017 and later. + // Microsoft guarantees that it is always installed in %ProgramFiles(x86)%\Microsoft Visual Studio\Installer + + System.Diagnostics.Process process = new System.Diagnostics.Process(); + process.StartInfo.FileName = System.IO.Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%"), "Microsoft Visual Studio\\Installer\\vswhere.exe"); + process.StartInfo.Arguments = "-latest"; + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.CreateNoWindow = true; + process.Start(); + + string result = process.StandardOutput.ReadToEnd(); + + if (!string.IsNullOrEmpty(result)) { - // This code is not obvious. See the sample (link above) for reference. - var query = (ISetupConfiguration2)GetQuery(); - var e = query.EnumAllInstances(); - - int fetched; - var instances = new ISetupInstance[1]; - do + var lines = result.Split("\r\n"); + foreach (var line in lines) { - // Call e.Next to query for the next instance (single item or nothing returned). - e.Next(1, instances, out fetched); - if (fetched <= 0) continue; - - var instance = (ISetupInstance2)instances[0]; - InstanceState state = instance.GetState(); - - // If the install was complete - if (state == InstanceState.Complete || - (state.HasFlag(InstanceState.Registered) && state.HasFlag(InstanceState.NoRebootRequired))) - { - if (!Version.TryParse(instance.GetInstallationVersion(), out var current)) - continue; - - // We want the highest version installed - if (current <= version) - continue; - - version = current; - path = instance.GetInstallationPath(); - } + if (line.StartsWith("installationPath: ")) + return line.Substring(18); } - while (fetched > 0); - } - catch (COMException) - { - } - catch (DllNotFoundException) - { - // This is OK, VS "15" or greater likely not installed. } - return path; + return string.Empty; } - - private static ISetupConfiguration GetQuery() - { - const int REGDB_E_CLASSNOTREG = unchecked((int)0x80040154); - - try - { - // Try to CoCreate the class object. - return new SetupConfiguration(); - } - - catch (COMException ex) when (ex.ErrorCode == REGDB_E_CLASSNOTREG) - { - // Try to get the class object using app-local call. - ISetupConfiguration query; - var result = GetSetupConfiguration(out query, IntPtr.Zero); - - if (result < 0) - throw new COMException($"Failed to get {nameof(query)}", result); - - return query; - } - } - - [DllImport("Microsoft.VisualStudio.Setup.Configuration.Native.dll", ExactSpelling = true, PreserveSig = true)] - private static extern int GetSetupConfiguration( - [MarshalAs(UnmanagedType.Interface)][Out] out ISetupConfiguration configuration, - IntPtr reserved); } From a81656861c71e3b34e5a32b688b9e3dd735617bb Mon Sep 17 00:00:00 2001 From: Thomas Altenburger Date: Mon, 28 Jul 2025 13:03:22 +0200 Subject: [PATCH 3/5] Switch to integrated vswhere --- build/BuildWindowsTask.cs | 35 +++++------------------------------ 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/build/BuildWindowsTask.cs b/build/BuildWindowsTask.cs index d10fa48..f70e576 100644 --- a/build/BuildWindowsTask.cs +++ b/build/BuildWindowsTask.cs @@ -1,3 +1,4 @@ +using Cake.Common.Tools.VSWhere.Latest; namespace BuildScripts; @@ -10,14 +11,16 @@ public sealed class BuildWindowsTask : FrostingTask public override void Run(BuildContext context) { + var vswhere = new VSWhereLatest(context.FileSystem, context.Environment, context.ProcessRunner, context.Tools); + string cmake = "cmake"; string msbuild = "msbuild"; // If processes are not on PATH, we want to retrieve the Visual Studio installation if (!IsOnPATH(cmake)) - cmake = System.IO.Path.Combine(GetVisualStudioPath(), "Common7\\IDE\\CommonExtensions\\Microsoft\\CMake\\CMake\\bin\\cmake.exe"); + cmake = vswhere.Latest(new VSWhereLatestSettings()).FullPath + @"\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin\cmake.exe"; if (!IsOnPATH(msbuild)) - msbuild = System.IO.Path.Combine(GetVisualStudioPath(), "MSBuild\\Current\\Bin\\MSBuild.exe"); + msbuild = vswhere.Latest(new VSWhereLatestSettings()).FullPath + @"\MSBuild\Current\Bin\MSBuild.exe"; // Build var buildDir = "sdl/build_x64"; @@ -64,32 +67,4 @@ private bool IsOnPATH(string process) return false; } - - private string GetVisualStudioPath() - { - // We can use vswhere.exe which ships with VS2017 and later. - // Microsoft guarantees that it is always installed in %ProgramFiles(x86)%\Microsoft Visual Studio\Installer - - System.Diagnostics.Process process = new System.Diagnostics.Process(); - process.StartInfo.FileName = System.IO.Path.Combine(Environment.ExpandEnvironmentVariables("%ProgramFiles(x86)%"), "Microsoft Visual Studio\\Installer\\vswhere.exe"); - process.StartInfo.Arguments = "-latest"; - process.StartInfo.UseShellExecute = false; - process.StartInfo.RedirectStandardOutput = true; - process.StartInfo.CreateNoWindow = true; - process.Start(); - - string result = process.StandardOutput.ReadToEnd(); - - if (!string.IsNullOrEmpty(result)) - { - var lines = result.Split("\r\n"); - foreach (var line in lines) - { - if (line.StartsWith("installationPath: ")) - return line.Substring(18); - } - } - - return string.Empty; - } } From 3f03643d770871d6b5cfcd140737e02a93fec3a8 Mon Sep 17 00:00:00 2001 From: Thomas Altenburger Date: Mon, 11 Aug 2025 09:56:27 +0200 Subject: [PATCH 4/5] Upgrade SDL to custom commit between 2.32.8 and 2.32.9 to fix ARM64 --- sdl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdl b/sdl index 98d1f3a..7f371c7 160000 --- a/sdl +++ b/sdl @@ -1 +1 @@ -Subproject commit 98d1f3a45aae568ccd6ed5fec179330f47d4d356 +Subproject commit 7f371c78082be168c9bc52eb4f52d16e81d56501 From 36e5fb2b64ef77d7c20562d683fe86277b618162 Mon Sep 17 00:00:00 2001 From: Thomas Altenburger Date: Tue, 2 Sep 2025 10:27:44 +0200 Subject: [PATCH 5/5] Update to SDL 2.32.10 --- sdl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdl b/sdl index 7f371c7..5d24957 160000 --- a/sdl +++ b/sdl @@ -1 +1 @@ -Subproject commit 7f371c78082be168c9bc52eb4f52d16e81d56501 +Subproject commit 5d249570393f7a37e037abf22cd6012a4cc56a71