diff --git a/00_Utilities/DotnetUtils/DotnetUtils/Functions.cs b/00_Utilities/DotnetUtils/DotnetUtils/Functions.cs new file mode 100644 index 00000000..7fa3115c --- /dev/null +++ b/00_Utilities/DotnetUtils/DotnetUtils/Functions.cs @@ -0,0 +1,21 @@ +using System.Xml.Linq; + +namespace DotnetUtils; + +public static class Functions { + public static string? getValue(string path, params string[] names) { + if (names.Length == 0) { throw new InvalidOperationException(); } + var parent = XDocument.Load(path).Element("Project")?.Element("PropertyGroup"); + return getValue(parent, names); + } + + public static string? getValue(XElement? parent, params string[] names) { + if (names.Length == 0) { throw new InvalidOperationException(); } + XElement? elem = null; + foreach (var name in names) { + elem = parent?.Element(name); + if (elem != null) { break; } + } + return elem?.Value; + } +} diff --git a/00_Utilities/DotnetUtils/DotnetUtils/Methods.cs b/00_Utilities/DotnetUtils/DotnetUtils/Methods.cs index 2aafa800..a8d3532b 100644 --- a/00_Utilities/DotnetUtils/DotnetUtils/Methods.cs +++ b/00_Utilities/DotnetUtils/DotnetUtils/Methods.cs @@ -44,6 +44,38 @@ public static class Methods { process.WaitForExit(); return new ProcessResult(process.ExitCode, output, error); } + + public static Task RunProcessAsync(Process process, string input = "") { + var tcs = new TaskCompletionSource(); + var (output, error) = ("", ""); + var (redirectOut, redirectErr) = ( + process.StartInfo.RedirectStandardOutput, + process.StartInfo.RedirectStandardError + ); + + process.Exited += (s, e) => tcs.SetResult(new ProcessResult(process.ExitCode, output, error)); + + if (redirectOut) { + process.OutputDataReceived += (s, ea) => output += ea.Data + "\n"; + } + if (redirectErr) { + process.ErrorDataReceived += (s, ea) => error += ea.Data + "\n"; + } + + if (!process.Start()) { + // what happens to the Exited event if process doesn't start successfully? + throw new InvalidOperationException(); + } + + if (redirectOut) { process.BeginOutputReadLine(); } + if (redirectErr) { process.BeginErrorReadLine(); } + if (!string.IsNullOrEmpty(input)) { + process.StandardInput.WriteLine(input); + process.StandardInput.Close(); + } + + return tcs.Task; + } } public sealed record ProcessResult(int ExitCode, string StdOut, string StdErr) { diff --git a/00_Utilities/DotnetUtils/DotnetUtils/Program.cs b/00_Utilities/DotnetUtils/DotnetUtils/Program.cs index e8ea235e..6121d131 100644 --- a/00_Utilities/DotnetUtils/DotnetUtils/Program.cs +++ b/00_Utilities/DotnetUtils/DotnetUtils/Program.cs @@ -1,7 +1,9 @@ -using DotnetUtils; +using System.Xml.Linq; +using DotnetUtils; using static System.Console; using static System.IO.Path; using static DotnetUtils.Methods; +using static DotnetUtils.Functions; var infos = PortInfos.Get; @@ -13,6 +15,8 @@ var actions = new (Action action, string description)[] { (missingProj, "Output missing project file"), (unexpectedProjName, "Output misnamed project files"), (multipleProjs, "Output multiple project files"), + (checkProjects, "Check .csproj/.vbproj files for target framework, nullability etc."), + (checkExecutableProject, "Check that there is at least one executable project per port"), (generateMissingSlns, "Generate solution files when missing"), (generateMissingProjs, "Generate project files when missing") @@ -220,12 +224,66 @@ void generateMissingProjs() { } void checkProjects() { - // warn if project files do not: - // target .NET 6 - // implicit using - // nullable enable - // warn if none og the projects have: - // output type exe + foreach (var (proj,item) in infos.SelectMany(item => item.Projs.Select(proj => (proj,item)))) { + var warnings = new List(); + var parent = XDocument.Load(proj).Element("Project")?.Element("PropertyGroup"); + + var ( + framework, + nullable, + implicitUsing, + rootNamespace, + langVersion + ) = ( + getValue(parent, "TargetFramework", "TargetFrameworks"), + getValue(parent, "Nullable"), + getValue(parent, "ImplicitUsings"), + getValue(parent, "RootNamespace"), + getValue(parent, "LangVersion") + ); + + if (framework != "net6.0") { + warnings.Add($"Target: {framework}"); + } + + if (item.Lang == "csharp") { + if (nullable != "enable") { + warnings.Add($"Nullable: {nullable}"); + } + if (implicitUsing != "enable") { + warnings.Add($"ImplicitUsings: {implicitUsing}"); + } + if (rootNamespace != null && rootNamespace != item.GameName) { + warnings.Add($"RootNamespace: {rootNamespace}"); + } + if (langVersion != "10") { + warnings.Add($"LangVersion: {langVersion}"); + } + } + + if (item.Lang == "vbnet") { + if (rootNamespace != item.GameName) { + warnings.Add($"RootNamespace: {rootNamespace}"); + } + if (langVersion != "16.9") { + warnings.Add($"LangVersion: {langVersion}"); + } + } + + if (warnings.Any()) { + WriteLine(proj); + WriteLine(string.Join("\n", warnings)); + WriteLine(); + } + } +} + +void checkExecutableProject() { + foreach (var item in infos) { + if (item.Projs.All(proj => getValue(proj,"OutputType") != "Exe")) { + WriteLine($"{item.LangPath}"); + } + } } void tryBuild() {