From c0ea89e6735704bd6add59c0753e5b9409311e80 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 9 Jul 2023 04:07:08 +0200 Subject: [PATCH 01/45] SDK: Initial progress --- .gitignore | 1 + Cesium.Ast/Cesium.Ast.csproj | 1 + Cesium.Compiler/Cesium.Compiler.csproj | 9 + .../Cesium.Preprocessor.csproj | 10 +- Cesium.Runtime/Cesium.Runtime.csproj | 14 +- Cesium.Sdk/Cesium.Sdk.csproj | 19 ++ Cesium.Sdk/CesiumCompile.cs | 287 ++++++++++++++++++ Cesium.Sdk/Sdk/Sdk.props | 29 ++ Cesium.Sdk/Sdk/Sdk.targets | 17 ++ Cesium.sln | 6 + Directory.Build.props | 3 + nuget.config | 10 + 12 files changed, 394 insertions(+), 12 deletions(-) create mode 100644 Cesium.Sdk/Cesium.Sdk.csproj create mode 100644 Cesium.Sdk/CesiumCompile.cs create mode 100644 Cesium.Sdk/Sdk/Sdk.props create mode 100644 Cesium.Sdk/Sdk/Sdk.targets create mode 100644 nuget.config diff --git a/.gitignore b/.gitignore index a58baf71..d7e0a87f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ bin/ obj/ +artifacts/ *.dll *.exe diff --git a/Cesium.Ast/Cesium.Ast.csproj b/Cesium.Ast/Cesium.Ast.csproj index 444a7901..fdc5611d 100644 --- a/Cesium.Ast/Cesium.Ast.csproj +++ b/Cesium.Ast/Cesium.Ast.csproj @@ -3,6 +3,7 @@ net7.0 enable + true diff --git a/Cesium.Compiler/Cesium.Compiler.csproj b/Cesium.Compiler/Cesium.Compiler.csproj index 9b78d196..979c81d9 100644 --- a/Cesium.Compiler/Cesium.Compiler.csproj +++ b/Cesium.Compiler/Cesium.Compiler.csproj @@ -7,6 +7,7 @@ true true true + Major @@ -36,4 +37,12 @@ + + + true + tools + false + + + diff --git a/Cesium.Preprocessor/Cesium.Preprocessor.csproj b/Cesium.Preprocessor/Cesium.Preprocessor.csproj index 8fb1047f..fe7710f3 100644 --- a/Cesium.Preprocessor/Cesium.Preprocessor.csproj +++ b/Cesium.Preprocessor/Cesium.Preprocessor.csproj @@ -6,14 +6,14 @@ - - - - + + + + - + diff --git a/Cesium.Runtime/Cesium.Runtime.csproj b/Cesium.Runtime/Cesium.Runtime.csproj index d8fe30c4..a8da8707 100644 --- a/Cesium.Runtime/Cesium.Runtime.csproj +++ b/Cesium.Runtime/Cesium.Runtime.csproj @@ -1,12 +1,12 @@ - - netstandard2.0;net6.0 - enable - enable - true - latest - + + netstandard2.0;net6.0 + enable + enable + true + latest + diff --git a/Cesium.Sdk/Cesium.Sdk.csproj b/Cesium.Sdk/Cesium.Sdk.csproj new file mode 100644 index 00000000..e4aea2c6 --- /dev/null +++ b/Cesium.Sdk/Cesium.Sdk.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + + enable + true + + + + + + + + + + + + diff --git a/Cesium.Sdk/CesiumCompile.cs b/Cesium.Sdk/CesiumCompile.cs new file mode 100644 index 00000000..0f272959 --- /dev/null +++ b/Cesium.Sdk/CesiumCompile.cs @@ -0,0 +1,287 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +namespace Cesium.Sdk; + +/* + -o, --out Sets path for the output assembly file + --framework (Default: Net) Valid values: Net, NetFramework, NetStandard + --arch (Default: Dynamic) Valid values: Dynamic, Bit32, Bit64 + --modulekind Valid values: Dll, Console, Windows, NetModule + --nologo Suppress compiler banner message + --namespace Sets default namespace instead of "global" + --globalclass Sets default global class instead of "" + --import Provides path to assemblies which would be added as references automatically into resulting executable. + --corelib Sets path to CoreLib assembly + --runtime Sets path to Cesium C Runtime assembly + -O Set the optimization level + -W Enable warnings set + -D Define constants for preprocessor + --help Display this help screen. + --version Display version information. + value pos. 0 + */ + +public class CesiumCompile : Task +{ + [Required] public ITaskItem CompilerExe { get; set; } = null!; + [Required] public ITaskItem[] InputFiles { get; set; } = null!; + [Required] public ITaskItem OutputFile { get; set; } = null!; + + public string? Namespace { get; set; } + public string? Framework { get; set; } + public string? Architecture { get; set; } + public string? ModuleType { get; set; } + public ITaskItem? CoreLibPath { get; set; } + public ITaskItem? RuntimePath { get; set; } + public ITaskItem[] ImportItems { get; set; } = Array.Empty(); + public ITaskItem[] PreprocessorItems { get; set; } = Array.Empty(); + + [Output] public string? ResultingCommandLine { get; private set; } + + public override bool Execute() + { + if (!TryValidate(out var options) || options is null) + { + return false; + } + + var args = CollectCommandLineArguments(options); + var compilerProcess = new Process + { + StartInfo = + { + FileName = options.CompilerExe, + UseShellExecute = false, + } + }; + + foreach (var arg in args) + { + compilerProcess.StartInfo.ArgumentList.Add(arg); + } + + compilerProcess.Start(); + compilerProcess.WaitForExit(); + + return true; + } + + private static (bool, FrameworkKind?) TryParseFramework(string? framework) => framework switch + { + null => (true, null), + nameof(FrameworkKind.Net) => (true, FrameworkKind.Net), + nameof(FrameworkKind.NetFramework) => (true, FrameworkKind.NetFramework), + nameof(FrameworkKind.NetStandard) => (true, FrameworkKind.NetStandard), + _ => (false, null) + }; + + private static (bool, ArchitectureKind?) TryParseArchitectureKind(string? archKind) => archKind switch + { + null => (true, null), + nameof(ArchitectureKind.Dynamic) => (true, ArchitectureKind.Dynamic), + nameof(ArchitectureKind.Bit32) => (true, ArchitectureKind.Bit32), + nameof(ArchitectureKind.Bit64) => (true, ArchitectureKind.Bit64), + _ => (false, null) + }; + + private static (bool, ModuleKind?) TryParseModuleKind(string? moduleKind) => moduleKind switch + { + null => (true, null), + nameof(ModuleKind.Dll) => (true, ModuleKind.Dll), + nameof(ModuleKind.Console) => (true, ModuleKind.Console), + nameof(ModuleKind.Windows) => (true, ModuleKind.Windows), + nameof(ModuleKind.NetModule) => (true, ModuleKind.NetModule), + _ => (false, null) + }; + + private bool TryValidate(out ValidatedOptions? options) + { + options = null; + var success = true; + + + if (!string.IsNullOrWhiteSpace(CompilerExe.ItemSpec) && !File.Exists(CompilerExe.ItemSpec)) + { + ReportValidationError("CES1000", $"Compiler executable doesn't exist under path '{CompilerExe?.ItemSpec}'"); + success = false; + } + + var (isFrameworkValid, framework) = TryParseFramework(Framework); + if (!isFrameworkValid) + { + var validValues = Enum.GetValues().Select(kind => kind.ToString()); + ReportValidationError("CES1004", $"Framework should be in range: '{string.Join(", ", validValues)}'"); + success = false; + } + + var (isArchValid, arch) = TryParseArchitectureKind(Architecture); + if (!isArchValid) + { + var validValues = Enum.GetValues().Select(kind => kind.ToString()); + ReportValidationError("CES1005", $"Architecture should be in range: '{string.Join(", ", validValues)}'"); + success = false; + } + + var (isModuleKindValid, moduleKind) = TryParseModuleKind(ModuleType); + if (!isModuleKindValid) + { + var validValues = Enum.GetValues().Select(kind => kind.ToString()); + ReportValidationError("CES1006", $"ModuleKind should be in range: '{string.Join(", ", validValues)}'"); + success = false; + } + + var missingCompileItems = InputFiles.Where(item => !File.Exists(item.ItemSpec)).ToList(); + foreach (var item in missingCompileItems) + { + ReportValidationError("CES1001", $"Source file doesn't exist: '{item.ItemSpec}'"); + success = false; + } + + if (!string.IsNullOrWhiteSpace(CoreLibPath?.ItemSpec) && !File.Exists(CoreLibPath.ItemSpec)) + { + ReportValidationError("CES1002", $"CorLib doesn't exist under path '{CoreLibPath?.ItemSpec}'"); + success = false; + } + + if (!string.IsNullOrWhiteSpace(RuntimePath?.ItemSpec) && !File.Exists(RuntimePath.ItemSpec)) + { + ReportValidationError("CES1003", $"Cesium.Runtime doesn't exist under path '{RuntimePath.ItemSpec}'"); + success = false; + } + + var missingAssemblyImportItems = ImportItems.Where(item => !File.Exists(item.ItemSpec)).ToList(); + foreach (var item in missingAssemblyImportItems) + { + ReportValidationError("CES1001", $"Imported assembly doesn't exist: '{item.ItemSpec}'"); + success = false; + } + + if (!success) return false; + + options = new ValidatedOptions( + CompilerExe: CompilerExe?.ItemSpec ?? throw new UnreachableException(), + InputItems: InputFiles.Select(item => item.ItemSpec).ToArray(), + OutputFile: OutputFile?.ItemSpec ?? throw new UnreachableException(), + Namespace: Namespace, + Framework: framework, + Architecture: arch, + ModuleKind: moduleKind, + CoreLibPath: CoreLibPath?.ItemSpec, + RuntimePath: RuntimePath?.ItemSpec, + ImportItems: ImportItems.Select(item => item.ItemSpec).ToArray(), + PreprocessorItems: PreprocessorItems.Select(item => item.ItemSpec).ToArray() + ); + + return true; + } + + private IEnumerable CollectCommandLineArguments(ValidatedOptions options) + { + yield return options.CompilerExe; + yield return "--nologo"; + + if (options.Framework is { } framework) + { + yield return "--framework"; + yield return framework.ToString(); + } + + if (options.Architecture is { } arch) + { + yield return "--arch"; + yield return arch.ToString(); + } + + if (options.ModuleKind is { } moduleKind) + { + yield return "--modulekind"; + yield return moduleKind.ToString(); + } + + if (!string.IsNullOrWhiteSpace(options.Namespace)) + { + yield return "--namespace"; + yield return options.Namespace; + } + + foreach (var import in options.ImportItems) + { + yield return "--import"; + yield return import; + } + + if (!string.IsNullOrWhiteSpace(options.CoreLibPath)) + { + yield return "--corelib"; + yield return options.CoreLibPath; + } + + if (!string.IsNullOrWhiteSpace(options.RuntimePath)) + { + yield return "--runtime"; + yield return options.RuntimePath; + } + + foreach (var item in options.PreprocessorItems) + { + yield return "-D"; + yield return item; + } + + yield return "--out"; + yield return $"\"{options.OutputFile}\""; + + foreach (var input in options.InputItems) + { + yield return $"\"{input}\""; + } + } + + private void ReportValidationError(string code, string message) => + BuildEngine.LogErrorEvent(new BuildErrorEventArgs(nameof(CesiumCompile), code, string.Empty, -1, -1, -1, -1, message, string.Empty, nameof(CesiumCompile))); + + private void ReportValidationWarning(string code, string message) => + BuildEngine.LogWarningEvent(new BuildWarningEventArgs(nameof(CesiumCompile), code, string.Empty, -1, -1, -1, -1, message, string.Empty, nameof(CesiumCompile))); + + private enum FrameworkKind + { + Net, + NetFramework, + NetStandard + } + + private enum ArchitectureKind + { + Dynamic, + Bit32, + Bit64 + } + + private enum ModuleKind + { + Dll, + Console, + Windows, + NetModule + } + + private record ValidatedOptions( + string CompilerExe, + string[] InputItems, + string OutputFile, + string? Namespace, + FrameworkKind? Framework, + ArchitectureKind? Architecture, + ModuleKind? ModuleKind, + string? CoreLibPath, + string? RuntimePath, + string[] ImportItems, + string[] PreprocessorItems + ); +} diff --git a/Cesium.Sdk/Sdk/Sdk.props b/Cesium.Sdk/Sdk/Sdk.props new file mode 100644 index 00000000..75e230c7 --- /dev/null +++ b/Cesium.Sdk/Sdk/Sdk.props @@ -0,0 +1,29 @@ + + + + false + + + + + + false + Cesium.Compiler + 0.0.1 + + + + + + + + $(PkgCesium_Compiler) + + $(CesiumCompilerPackagePath)/tools/$(CesiumCompilerPackageName).exe + $(CesiumCompilerPath) + + + diff --git a/Cesium.Sdk/Sdk/Sdk.targets b/Cesium.Sdk/Sdk/Sdk.targets new file mode 100644 index 00000000..ae975a00 --- /dev/null +++ b/Cesium.Sdk/Sdk/Sdk.targets @@ -0,0 +1,17 @@ + + + + + + + + <_CompilerOutput>@(IntermediateAssembly->'%(FullPath)') + + + + + + diff --git a/Cesium.sln b/Cesium.sln index 47985c04..b3112c52 100644 --- a/Cesium.sln +++ b/Cesium.sln @@ -76,6 +76,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "perform-common-steps", "per EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cesium.Core", "Cesium.Core\Cesium.Core.csproj", "{C83A5A9A-5667-4574-B75C-EFF38FA0FE79}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cesium.Sdk", "Cesium.Sdk\Cesium.Sdk.csproj", "{736A7F26-F507-48C5-BF48-3726EA2399CB}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cesium.Runtime.Tests", "Cesium.Runtime.Tests\Cesium.Runtime.Tests.csproj", "{526D8E61-6143-490F-BB9D-E7CD78512E55}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cesium.Compiler.Tests", "Cesium.Compiler.Tests\Cesium.Compiler.Tests.csproj", "{A5E33E79-E710-4F2F-838F-20CD0A3142F3}" @@ -144,6 +146,10 @@ Global {3AF6A395-8B9C-4E97-9036-B7C03A62EC9D}.Debug|Any CPU.Build.0 = Debug|Any CPU {3AF6A395-8B9C-4E97-9036-B7C03A62EC9D}.Release|Any CPU.ActiveCfg = Release|Any CPU {3AF6A395-8B9C-4E97-9036-B7C03A62EC9D}.Release|Any CPU.Build.0 = Release|Any CPU + {736A7F26-F507-48C5-BF48-3726EA2399CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {736A7F26-F507-48C5-BF48-3726EA2399CB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {736A7F26-F507-48C5-BF48-3726EA2399CB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {736A7F26-F507-48C5-BF48-3726EA2399CB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Directory.Build.props b/Directory.Build.props index 028e277e..654f70da 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,4 +7,7 @@ nullable 12.0 + + + diff --git a/nuget.config b/nuget.config new file mode 100644 index 00000000..6fd055e8 --- /dev/null +++ b/nuget.config @@ -0,0 +1,10 @@ + + + + + + + + + + From 80811533bf3afa87ecdd96418d276dede3504505 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sat, 22 Jul 2023 20:28:26 +0200 Subject: [PATCH 02/45] Add dry-run option to CesiumCompile Introduced a "DryRun" boolean to the CesiumCompile class. This option, when set to true, prevents the compiler process from actually starting. This can be used for testing purposes or to verify command line arguments without performing a full compilation. --- Cesium.Sdk/CesiumCompile.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Cesium.Sdk/CesiumCompile.cs b/Cesium.Sdk/CesiumCompile.cs index 0f272959..06bd4dac 100644 --- a/Cesium.Sdk/CesiumCompile.cs +++ b/Cesium.Sdk/CesiumCompile.cs @@ -41,6 +41,7 @@ public class CesiumCompile : Task public ITaskItem? RuntimePath { get; set; } public ITaskItem[] ImportItems { get; set; } = Array.Empty(); public ITaskItem[] PreprocessorItems { get; set; } = Array.Empty(); + public bool DryRun = false; [Output] public string? ResultingCommandLine { get; private set; } @@ -66,8 +67,11 @@ public override bool Execute() compilerProcess.StartInfo.ArgumentList.Add(arg); } - compilerProcess.Start(); - compilerProcess.WaitForExit(); + if (!DryRun) + { + compilerProcess.Start(); + compilerProcess.WaitForExit(); + } return true; } From 4a6f203428e419ee270cc69bc0437fa8265bde0a Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 23 Jul 2023 02:14:30 +0200 Subject: [PATCH 03/45] Add CommandArgumentsBuilder for argument escaping --- Cesium.Sdk/CesiumCompile.cs | 19 +++++-- Cesium.Sdk/CommandArgumentsBuilder.cs | 80 +++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 Cesium.Sdk/CommandArgumentsBuilder.cs diff --git a/Cesium.Sdk/CesiumCompile.cs b/Cesium.Sdk/CesiumCompile.cs index 06bd4dac..ecba7eff 100644 --- a/Cesium.Sdk/CesiumCompile.cs +++ b/Cesium.Sdk/CesiumCompile.cs @@ -52,21 +52,23 @@ public override bool Execute() return false; } - var args = CollectCommandLineArguments(options); + var argumentsBuilder = new CommandArgumentsBuilder(); + foreach (var argument in CollectCommandLineArguments(options)) + { + argumentsBuilder.Argument(argument); + } + var compilerProcess = new Process { StartInfo = { FileName = options.CompilerExe, + Arguments = argumentsBuilder.Build(), UseShellExecute = false, } }; - foreach (var arg in args) - { - compilerProcess.StartInfo.ArgumentList.Add(arg); - } - + ResultingCommandLine = $"{compilerProcess.StartInfo.FileName} {compilerProcess.StartInfo.Arguments}"; if (!DryRun) { compilerProcess.Start(); @@ -253,6 +255,11 @@ private void ReportValidationError(string code, string message) => private void ReportValidationWarning(string code, string message) => BuildEngine.LogWarningEvent(new BuildWarningEventArgs(nameof(CesiumCompile), code, string.Empty, -1, -1, -1, -1, message, string.Empty, nameof(CesiumCompile))); + private string GetResultingCommandLine(string executable, IReadOnlyCollection arguments) + { + return $"{executable} {string.Join(" ", arguments)}"; + } + private enum FrameworkKind { Net, diff --git a/Cesium.Sdk/CommandArgumentsBuilder.cs b/Cesium.Sdk/CommandArgumentsBuilder.cs new file mode 100644 index 00000000..9de668d2 --- /dev/null +++ b/Cesium.Sdk/CommandArgumentsBuilder.cs @@ -0,0 +1,80 @@ +using System.Linq; +using System.Text; + +namespace Cesium.Sdk; + +// Implementation reference: +// https://github.com/Tyrrrz/CliWrap/blob/417aaffe171b9897799ed2a28a6467c11d69c296/CliWrap/Builders/ArgumentsBuilder.cs +// MIT License, Oleksii Holub +public class CommandArgumentsBuilder +{ + private StringBuilder _builder = new StringBuilder(); + + public CommandArgumentsBuilder Argument(string argument) + { + _builder.Append(" "); + if (NeedsEscaping(argument)) + { + argument = Escape(argument); + } + _builder.Append(argument); + + return this; + } + + public string Build() => _builder.ToString(); + + private bool NeedsEscaping(string argument) => + argument.Length > 0 && argument.All(c => !char.IsWhiteSpace(c) && c != '"'); + + private static string Escape(string argument) + { + var buffer = new StringBuilder(); + + buffer.Append('"'); + + for (var i = 0; i < argument.Length;) + { + var c = argument[i++]; + + if (c == '\\') + { + var backslashCount = 1; + while (i < argument.Length && argument[i] == '\\') + { + backslashCount++; + i++; + } + + if (i == argument.Length) + { + buffer.Append('\\', backslashCount * 2); + } + else if (argument[i] == '"') + { + buffer + .Append('\\', backslashCount * 2 + 1) + .Append('"'); + + i++; + } + else + { + buffer.Append('\\', backslashCount); + } + } + else if (c == '"') + { + buffer.Append('\\').Append('"'); + } + else + { + buffer.Append(c); + } + } + + buffer.Append('"'); + + return buffer.ToString(); + } +} From bce344168fa60f2b83f54ccc16241f1f048c4021 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 23 Jul 2023 02:26:53 +0200 Subject: [PATCH 04/45] SDK: Start writing tests --- Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj | 26 +++++++++++++++++++ Cesium.Sdk.Tests/CesiumCompileTests.cs | 9 +++++++ .../TestProjects/ValidDefinition.msbuild | 6 +++++ Cesium.Sdk.Tests/Usings.cs | 1 + Cesium.sln | 6 +++++ 5 files changed, 48 insertions(+) create mode 100644 Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj create mode 100644 Cesium.Sdk.Tests/CesiumCompileTests.cs create mode 100644 Cesium.Sdk.Tests/TestProjects/ValidDefinition.msbuild create mode 100644 Cesium.Sdk.Tests/Usings.cs diff --git a/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj b/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj new file mode 100644 index 00000000..d21167ef --- /dev/null +++ b/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj @@ -0,0 +1,26 @@ + + + + net7.0 + enable + enable + + false + true + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + diff --git a/Cesium.Sdk.Tests/CesiumCompileTests.cs b/Cesium.Sdk.Tests/CesiumCompileTests.cs new file mode 100644 index 00000000..9c69a330 --- /dev/null +++ b/Cesium.Sdk.Tests/CesiumCompileTests.cs @@ -0,0 +1,9 @@ +namespace Cesium.Sdk.Tests; + +public class CesiumCompileTests +{ + [Fact] + public void Test1() + { + } +} diff --git a/Cesium.Sdk.Tests/TestProjects/ValidDefinition.msbuild b/Cesium.Sdk.Tests/TestProjects/ValidDefinition.msbuild new file mode 100644 index 00000000..32f34ae7 --- /dev/null +++ b/Cesium.Sdk.Tests/TestProjects/ValidDefinition.msbuild @@ -0,0 +1,6 @@ +namespace Cesium.Sdk.Tests.TestProjects; + +public class ValidDefinition_msbuild +{ + +} diff --git a/Cesium.Sdk.Tests/Usings.cs b/Cesium.Sdk.Tests/Usings.cs new file mode 100644 index 00000000..c802f448 --- /dev/null +++ b/Cesium.Sdk.Tests/Usings.cs @@ -0,0 +1 @@ +global using Xunit; diff --git a/Cesium.sln b/Cesium.sln index b3112c52..5cc6f609 100644 --- a/Cesium.sln +++ b/Cesium.sln @@ -84,6 +84,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cesium.Compiler.Tests", "Ce EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cesium.TestFramework.Tests", "Cesium.TestFramework.Tests\Cesium.TestFramework.Tests.csproj", "{3AF6A395-8B9C-4E97-9036-B7C03A62EC9D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cesium.Sdk.Tests", "Cesium.Sdk.Tests\Cesium.Sdk.Tests.csproj", "{02C764E4-41C6-4E73-AD93-F3DD3E1E9630}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -150,6 +152,10 @@ Global {736A7F26-F507-48C5-BF48-3726EA2399CB}.Debug|Any CPU.Build.0 = Debug|Any CPU {736A7F26-F507-48C5-BF48-3726EA2399CB}.Release|Any CPU.ActiveCfg = Release|Any CPU {736A7F26-F507-48C5-BF48-3726EA2399CB}.Release|Any CPU.Build.0 = Release|Any CPU + {02C764E4-41C6-4E73-AD93-F3DD3E1E9630}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {02C764E4-41C6-4E73-AD93-F3DD3E1E9630}.Debug|Any CPU.Build.0 = Debug|Any CPU + {02C764E4-41C6-4E73-AD93-F3DD3E1E9630}.Release|Any CPU.ActiveCfg = Release|Any CPU + {02C764E4-41C6-4E73-AD93-F3DD3E1E9630}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE From 8a93fee019a32e4d66ef977559b756214926ac8d Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 17 Dec 2023 04:22:20 +0100 Subject: [PATCH 05/45] SDK: Propagate solution metadata from MSBuild, add test base --- Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj | 10 ++ Cesium.Sdk.Tests/CesiumCompileTests.cs | 12 +- Cesium.Sdk.Tests/SdkTestBase.cs | 170 ++++++++++++++++++ .../SimpleProject/SimpleProject.ceproj | 9 + .../TestProjects/SimpleProject/hello.c | 7 + .../TestProjects/ValidDefinition.msbuild | 6 - Cesium.Sdk/Cesium.Sdk.csproj | 4 +- Cesium.Sdk/TestTargets.targets | 10 ++ .../Cesium.Solution.Metadata.csproj | 16 ++ Cesium.Solution.Metadata/SolutionMetadata.cs | 14 ++ .../SolutionMetadataAttribute.cs | 13 ++ Cesium.sln | 6 + Directory.Build.props | 5 +- 13 files changed, 271 insertions(+), 11 deletions(-) create mode 100644 Cesium.Sdk.Tests/SdkTestBase.cs create mode 100644 Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj create mode 100644 Cesium.Sdk.Tests/TestProjects/SimpleProject/hello.c delete mode 100644 Cesium.Sdk.Tests/TestProjects/ValidDefinition.msbuild create mode 100644 Cesium.Sdk/TestTargets.targets create mode 100644 Cesium.Solution.Metadata/Cesium.Solution.Metadata.csproj create mode 100644 Cesium.Solution.Metadata/SolutionMetadata.cs create mode 100644 Cesium.Solution.Metadata/SolutionMetadataAttribute.cs diff --git a/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj b/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj index d21167ef..12e140f4 100644 --- a/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj +++ b/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj @@ -10,6 +10,9 @@ + + + @@ -22,5 +25,12 @@ + + + + + + + diff --git a/Cesium.Sdk.Tests/CesiumCompileTests.cs b/Cesium.Sdk.Tests/CesiumCompileTests.cs index 9c69a330..b7d0f2cf 100644 --- a/Cesium.Sdk.Tests/CesiumCompileTests.cs +++ b/Cesium.Sdk.Tests/CesiumCompileTests.cs @@ -1,9 +1,15 @@ +using Xunit.Abstractions; + namespace Cesium.Sdk.Tests; -public class CesiumCompileTests +public class CesiumCompileTests : SdkTestBase { - [Fact] - public void Test1() + // [Theory] + // [InlineData()] + // public void CesiumCompile_ShouldSucceed(string projectPath) + // { + // } + public CesiumCompileTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) { } } diff --git a/Cesium.Sdk.Tests/SdkTestBase.cs b/Cesium.Sdk.Tests/SdkTestBase.cs new file mode 100644 index 00000000..7ae43473 --- /dev/null +++ b/Cesium.Sdk.Tests/SdkTestBase.cs @@ -0,0 +1,170 @@ +using System.Reflection; +using Cesium.Solution.Metadata; +using Microsoft.Build.Execution; +using Microsoft.Build.Framework; +using Xunit.Abstractions; + +namespace Cesium.Sdk.Tests; + +public abstract class SdkTestBase +{ + private readonly ITestOutputHelper _testOutputHelper; + private readonly string _temporaryPath = Path.GetTempFileName(); + + public SdkTestBase(ITestOutputHelper testOutputHelper) + { + _testOutputHelper = testOutputHelper; + + File.Delete(_temporaryPath); + + _testOutputHelper.WriteLine($"Test projects folder: {_temporaryPath}"); + + var assemblyPath = Assembly.GetExecutingAssembly().Location; + var testDataPath = Path.Combine(Path.GetDirectoryName(assemblyPath)!, "TestProjects"); + CopyDirectoryRecursive(testDataPath, _temporaryPath); + + var nupkgPath = Path.GetFullPath(Path.Combine(SolutionMetadata.SourceRoot, "artifacts", "package", "debug")); + EmitNuGetConfig(Path.Combine(_temporaryPath, "NuGet.config"), nupkgPath); + EmitGlobalJson(Path.Combine(_temporaryPath, "global.json"), $"{SolutionMetadata.VersionPrefix}-dev"); + } + + protected BuildResult ExecuteTargets(string projectFile, params string[] targets) + { + var projectInstance = new ProjectInstance(projectFile); + var request = new BuildRequestData(projectInstance, targets); + var parameters = new BuildParameters + { + Loggers = new []{ new TestOutputLogger(_testOutputHelper) } + }; + var result = BuildManager.DefaultBuildManager.Build(parameters, request); + return result; + } + + private static void EmitNuGetConfig(string configFilePath, string packageSourcePath) + { + File.WriteAllText(configFilePath, $@" + + + + + + + +"); + } + + private static void EmitGlobalJson(string globalJsonPath, string packageVersion) + { + File.WriteAllText(globalJsonPath, $@"{{ + ""msbuild-sdks"": {{ + ""Cesium.Sdk"" : ""{packageVersion}"" + }} +}} +"); + } + + private static void CopyDirectoryRecursive(string source, string target) + { + Directory.CreateDirectory(target); + + foreach (var subDirPath in Directory.GetDirectories(source)) + { + var dirName = Path.GetFileName(subDirPath); + CopyDirectoryRecursive(subDirPath, Path.Combine(target, dirName)); + } + + foreach (var filePath in Directory.GetFiles(source)) + { + var fileName = Path.GetFileName(filePath); + File.Copy(filePath, Path.Combine(target, fileName)); + } + } + + private class TestOutputLogger : ILogger + { + public LoggerVerbosity Verbosity { get; set; } = LoggerVerbosity.Normal; + public string Parameters { get; set; } = string.Empty; + + private readonly ITestOutputHelper _testOutputHelper; + + public TestOutputLogger(ITestOutputHelper testOutputHelper) + { + _testOutputHelper = testOutputHelper; + } + + public void Initialize(IEventSource eventSource) + { + eventSource.AnyEventRaised += HandleEvent; + } + + public void Shutdown() + { + } + + private void HandleEvent(object sender, BuildEventArgs args) + { + var entry = args switch + { + TargetFinishedEventArgs => + new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.TargetFinished, args.Message), + TargetStartedEventArgs => + new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.TargetStarted, args.Message), + TaskFinishedEventArgs => + new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.TaskFinished, args.Message), + TaskStartedEventArgs => + new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.TaskStarted, args.Message), + BuildFinishedEventArgs => + new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.BuildFinished, args.Message), + BuildStartedEventArgs => + new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.BuildStarted, args.Message), + CustomBuildEventArgs => + new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.CustomEventRaised, args.Message), + BuildErrorEventArgs => + new BuildLogEntry(BuildLogLevel.Error, BuildLogKind.ErrorRaised, args.Message), + BuildMessageEventArgs => + new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.MessageRaised, args.Message), + ProjectFinishedEventArgs => + new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.ProjectFinished, args.Message), + ProjectStartedEventArgs => + new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.ProjectStarted, args.Message), + BuildStatusEventArgs => + new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.StatusEventRaised, args.Message), + BuildWarningEventArgs => + new BuildLogEntry(BuildLogLevel.Warning, BuildLogKind.WarningRaised, args.Message), + var other => + new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.AnyEventRaised, other.Message) + }; + + _testOutputHelper.WriteLine($"[{entry.Level.ToString()}]: {entry.Message}"); + } + } + + protected enum BuildLogLevel + { + Error, + Warning, + Info, + Verbose, + Trace, + } + + protected enum BuildLogKind + { + AnyEventRaised, + BuildFinished, + BuildStarted, + CustomEventRaised, + ErrorRaised, + MessageRaised, + ProjectFinished, + ProjectStarted, + StatusEventRaised, + TargetFinished, + TargetStarted, + TaskFinished, + TaskStarted, + WarningRaised + } + + protected record BuildLogEntry(BuildLogLevel Level, BuildLogKind Kind, string? Message); +} diff --git a/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj b/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj new file mode 100644 index 00000000..805b6d58 --- /dev/null +++ b/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/Cesium.Sdk.Tests/TestProjects/SimpleProject/hello.c b/Cesium.Sdk.Tests/TestProjects/SimpleProject/hello.c new file mode 100644 index 00000000..2889672e --- /dev/null +++ b/Cesium.Sdk.Tests/TestProjects/SimpleProject/hello.c @@ -0,0 +1,7 @@ +#include + +int main(int argc, char *argv[]) +{ + puts("Hello, world!"); + return 42; +} diff --git a/Cesium.Sdk.Tests/TestProjects/ValidDefinition.msbuild b/Cesium.Sdk.Tests/TestProjects/ValidDefinition.msbuild deleted file mode 100644 index 32f34ae7..00000000 --- a/Cesium.Sdk.Tests/TestProjects/ValidDefinition.msbuild +++ /dev/null @@ -1,6 +0,0 @@ -namespace Cesium.Sdk.Tests.TestProjects; - -public class ValidDefinition_msbuild -{ - -} diff --git a/Cesium.Sdk/Cesium.Sdk.csproj b/Cesium.Sdk/Cesium.Sdk.csproj index e4aea2c6..488e616a 100644 --- a/Cesium.Sdk/Cesium.Sdk.csproj +++ b/Cesium.Sdk/Cesium.Sdk.csproj @@ -11,9 +11,11 @@ - + + + diff --git a/Cesium.Sdk/TestTargets.targets b/Cesium.Sdk/TestTargets.targets new file mode 100644 index 00000000..fcddc5d8 --- /dev/null +++ b/Cesium.Sdk/TestTargets.targets @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Cesium.Solution.Metadata/Cesium.Solution.Metadata.csproj b/Cesium.Solution.Metadata/Cesium.Solution.Metadata.csproj new file mode 100644 index 00000000..d31b65b1 --- /dev/null +++ b/Cesium.Solution.Metadata/Cesium.Solution.Metadata.csproj @@ -0,0 +1,16 @@ + + + + net7.0 + enable + enable + + + + + <_Parameter1>$(CesiumSourceRoot) + <_Parameter2>$(VersionPrefix) + + + + diff --git a/Cesium.Solution.Metadata/SolutionMetadata.cs b/Cesium.Solution.Metadata/SolutionMetadata.cs new file mode 100644 index 00000000..050e2fd9 --- /dev/null +++ b/Cesium.Solution.Metadata/SolutionMetadata.cs @@ -0,0 +1,14 @@ +using System.Reflection; + +namespace Cesium.Solution.Metadata; + +public static class SolutionMetadata +{ + public static string SourceRoot => + Assembly.GetExecutingAssembly().GetCustomAttribute()?.SourceRoot + ?? throw new Exception($"Missing {nameof(SolutionMetadataAttribute)} metadata attribute."); + + public static string VersionPrefix => + Assembly.GetExecutingAssembly().GetCustomAttribute()?.VersionPrefix + ?? throw new Exception($"Missing {nameof(SolutionMetadataAttribute)} metadata attribute."); +} diff --git a/Cesium.Solution.Metadata/SolutionMetadataAttribute.cs b/Cesium.Solution.Metadata/SolutionMetadataAttribute.cs new file mode 100644 index 00000000..baa923e0 --- /dev/null +++ b/Cesium.Solution.Metadata/SolutionMetadataAttribute.cs @@ -0,0 +1,13 @@ +namespace Cesium.Solution.Metadata; + +public class SolutionMetadataAttribute : Attribute +{ + public string SourceRoot { get; } + public string VersionPrefix { get; } + + public SolutionMetadataAttribute(string sourceRoot, string versionPrefix) + { + SourceRoot = sourceRoot; + VersionPrefix = versionPrefix; + } +} diff --git a/Cesium.sln b/Cesium.sln index 5cc6f609..b8e235e1 100644 --- a/Cesium.sln +++ b/Cesium.sln @@ -86,6 +86,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cesium.TestFramework.Tests" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cesium.Sdk.Tests", "Cesium.Sdk.Tests\Cesium.Sdk.Tests.csproj", "{02C764E4-41C6-4E73-AD93-F3DD3E1E9630}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cesium.Solution.Metadata", "Cesium.Solution.Metadata\Cesium.Solution.Metadata.csproj", "{5B59ADD9-5C9D-482C-B238-F5EFD5E1F7CF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -156,6 +158,10 @@ Global {02C764E4-41C6-4E73-AD93-F3DD3E1E9630}.Debug|Any CPU.Build.0 = Debug|Any CPU {02C764E4-41C6-4E73-AD93-F3DD3E1E9630}.Release|Any CPU.ActiveCfg = Release|Any CPU {02C764E4-41C6-4E73-AD93-F3DD3E1E9630}.Release|Any CPU.Build.0 = Release|Any CPU + {5B59ADD9-5C9D-482C-B238-F5EFD5E1F7CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5B59ADD9-5C9D-482C-B238-F5EFD5E1F7CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B59ADD9-5C9D-482C-B238-F5EFD5E1F7CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5B59ADD9-5C9D-482C-B238-F5EFD5E1F7CF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Directory.Build.props b/Directory.Build.props index 654f70da..295e76e8 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -8,6 +8,9 @@ 12.0 - + true + + + $(MSBuildThisFileDirectory) From ea83c61b59ee629d0445c350d80d3d2a50fb1c3b Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Mon, 18 Dec 2023 05:02:07 +0100 Subject: [PATCH 06/45] SDK: WIP --- .../SimpleProject/SimpleProject.ceproj | 4 --- Cesium.Sdk/CesiumCompile.cs | 29 ++++++++++--------- Cesium.Sdk/Sdk/Sdk.props | 16 ++++++---- Cesium.Sdk/Sdk/Sdk.targets | 18 +++++++++++- 4 files changed, 43 insertions(+), 24 deletions(-) diff --git a/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj b/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj index 805b6d58..b602a5a0 100644 --- a/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj +++ b/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj @@ -2,8 +2,4 @@ - - - - diff --git a/Cesium.Sdk/CesiumCompile.cs b/Cesium.Sdk/CesiumCompile.cs index ecba7eff..e2b077a7 100644 --- a/Cesium.Sdk/CesiumCompile.cs +++ b/Cesium.Sdk/CesiumCompile.cs @@ -27,18 +27,19 @@ namespace Cesium.Sdk; value pos. 0 */ +// ReSharper disable once UnusedType.Global public class CesiumCompile : Task { - [Required] public ITaskItem CompilerExe { get; set; } = null!; + [Required] public string CompilerExe { get; set; } = null!; [Required] public ITaskItem[] InputFiles { get; set; } = null!; - [Required] public ITaskItem OutputFile { get; set; } = null!; + [Required] public string OutputFile { get; set; } = null!; public string? Namespace { get; set; } public string? Framework { get; set; } public string? Architecture { get; set; } public string? ModuleType { get; set; } - public ITaskItem? CoreLibPath { get; set; } - public ITaskItem? RuntimePath { get; set; } + public string? CoreLibPath { get; set; } + public string? RuntimePath { get; set; } public ITaskItem[] ImportItems { get; set; } = Array.Empty(); public ITaskItem[] PreprocessorItems { get; set; } = Array.Empty(); public bool DryRun = false; @@ -112,9 +113,9 @@ private bool TryValidate(out ValidatedOptions? options) var success = true; - if (!string.IsNullOrWhiteSpace(CompilerExe.ItemSpec) && !File.Exists(CompilerExe.ItemSpec)) + if (!string.IsNullOrWhiteSpace(CompilerExe) && !File.Exists(CompilerExe)) { - ReportValidationError("CES1000", $"Compiler executable doesn't exist under path '{CompilerExe?.ItemSpec}'"); + ReportValidationError("CES1000", $"Compiler executable doesn't exist under path '{CompilerExe}'"); success = false; } @@ -149,15 +150,15 @@ private bool TryValidate(out ValidatedOptions? options) success = false; } - if (!string.IsNullOrWhiteSpace(CoreLibPath?.ItemSpec) && !File.Exists(CoreLibPath.ItemSpec)) + if (!string.IsNullOrWhiteSpace(CoreLibPath) && !File.Exists(CoreLibPath)) { - ReportValidationError("CES1002", $"CorLib doesn't exist under path '{CoreLibPath?.ItemSpec}'"); + ReportValidationError("CES1002", $"CorLib doesn't exist under path '{CoreLibPath}'"); success = false; } - if (!string.IsNullOrWhiteSpace(RuntimePath?.ItemSpec) && !File.Exists(RuntimePath.ItemSpec)) + if (!string.IsNullOrWhiteSpace(RuntimePath) && !File.Exists(RuntimePath)) { - ReportValidationError("CES1003", $"Cesium.Runtime doesn't exist under path '{RuntimePath.ItemSpec}'"); + ReportValidationError("CES1003", $"Cesium.Runtime doesn't exist under path '{RuntimePath}'"); success = false; } @@ -171,15 +172,15 @@ private bool TryValidate(out ValidatedOptions? options) if (!success) return false; options = new ValidatedOptions( - CompilerExe: CompilerExe?.ItemSpec ?? throw new UnreachableException(), + CompilerExe: CompilerExe ?? throw new UnreachableException(), InputItems: InputFiles.Select(item => item.ItemSpec).ToArray(), - OutputFile: OutputFile?.ItemSpec ?? throw new UnreachableException(), + OutputFile: OutputFile ?? throw new UnreachableException(), Namespace: Namespace, Framework: framework, Architecture: arch, ModuleKind: moduleKind, - CoreLibPath: CoreLibPath?.ItemSpec, - RuntimePath: RuntimePath?.ItemSpec, + CoreLibPath: CoreLibPath, + RuntimePath: RuntimePath, ImportItems: ImportItems.Select(item => item.ItemSpec).ToArray(), PreprocessorItems: PreprocessorItems.Select(item => item.ItemSpec).ToArray() ); diff --git a/Cesium.Sdk/Sdk/Sdk.props b/Cesium.Sdk/Sdk/Sdk.props index 75e230c7..3cd7c85c 100644 --- a/Cesium.Sdk/Sdk/Sdk.props +++ b/Cesium.Sdk/Sdk/Sdk.props @@ -9,21 +9,27 @@ false Cesium.Compiler - 0.0.1 + $(VersionPrefix) $(PkgCesium_Compiler) - - $(CesiumCompilerPackagePath)/tools/$(CesiumCompilerPackageName).exe - $(CesiumCompilerPath) + + $(CesiumCompilerPackagePath)/tools/$(CesiumCompilerPackageName) + + + $(CesiumCompilerPackagePath)/tools/$(CesiumCompilerPackageName).exe + + + diff --git a/Cesium.Sdk/Sdk/Sdk.targets b/Cesium.Sdk/Sdk/Sdk.targets index ae975a00..9a2b00e3 100644 --- a/Cesium.Sdk/Sdk/Sdk.targets +++ b/Cesium.Sdk/Sdk/Sdk.targets @@ -7,9 +7,25 @@ Outputs="@(IntermediateAssembly)"> - <_CompilerOutput>@(IntermediateAssembly->'%(FullPath)') + <_CompilerOutput>$(OutDir)$(AssemblyName) + + + From fb17800402fe2b7dfa09f26eff403cfca7d07c5a Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Wed, 20 Dec 2023 22:44:38 +0100 Subject: [PATCH 07/45] Use .NET 8 across the solution & toolset --- .github/workflows/perform-common-steps/action.yml | 4 ++-- Cesium.Ast/Cesium.Ast.csproj | 2 +- Cesium.Core/Cesium.Core.csproj | 2 +- Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj | 2 +- Cesium.Solution.Metadata/Cesium.Solution.Metadata.csproj | 2 +- global.json | 7 +++++++ 6 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 global.json diff --git a/.github/workflows/perform-common-steps/action.yml b/.github/workflows/perform-common-steps/action.yml index a9ba89e9..fdf54aa2 100644 --- a/.github/workflows/perform-common-steps/action.yml +++ b/.github/workflows/perform-common-steps/action.yml @@ -4,9 +4,9 @@ runs: using: "composite" steps: - name: ⚙ Setup .NET SDK ⚙ - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v3 with: - dotnet-version: '8.0.x' + global-json-file: global.json - name: ♻ NuGet Cache ♻ uses: actions/cache@v2 diff --git a/Cesium.Ast/Cesium.Ast.csproj b/Cesium.Ast/Cesium.Ast.csproj index fdc5611d..d2fdb902 100644 --- a/Cesium.Ast/Cesium.Ast.csproj +++ b/Cesium.Ast/Cesium.Ast.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 enable true diff --git a/Cesium.Core/Cesium.Core.csproj b/Cesium.Core/Cesium.Core.csproj index aa68acee..7d989800 100644 --- a/Cesium.Core/Cesium.Core.csproj +++ b/Cesium.Core/Cesium.Core.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 enable enable diff --git a/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj b/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj index 12e140f4..fad9a01e 100644 --- a/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj +++ b/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 enable enable diff --git a/Cesium.Solution.Metadata/Cesium.Solution.Metadata.csproj b/Cesium.Solution.Metadata/Cesium.Solution.Metadata.csproj index d31b65b1..611eacd4 100644 --- a/Cesium.Solution.Metadata/Cesium.Solution.Metadata.csproj +++ b/Cesium.Solution.Metadata/Cesium.Solution.Metadata.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 enable enable diff --git a/global.json b/global.json new file mode 100644 index 00000000..c65c9eac --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "8.0.0", + "rollForward": "latestMinor", + "allowPrerelease": true + } +} \ No newline at end of file From 15c3cddb96e5a0921829ac76f58f046407cf8790 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Thu, 21 Dec 2023 04:18:42 +0100 Subject: [PATCH 08/45] SDK: WIP --- Cesium.Sdk.Tests/CesiumCompileTests.cs | 15 +- Cesium.Sdk.Tests/SdkTestBase.cs | 203 ++++++++---------- Cesium.Sdk/Cesium.Sdk.csproj | 1 + Cesium.Sdk/Sdk/Sdk.props | 2 +- .../Cesium.Solution.Metadata.csproj | 1 + Cesium.Solution.Metadata/SolutionMetadata.cs | 9 +- 6 files changed, 107 insertions(+), 124 deletions(-) diff --git a/Cesium.Sdk.Tests/CesiumCompileTests.cs b/Cesium.Sdk.Tests/CesiumCompileTests.cs index b7d0f2cf..3ef1f753 100644 --- a/Cesium.Sdk.Tests/CesiumCompileTests.cs +++ b/Cesium.Sdk.Tests/CesiumCompileTests.cs @@ -2,14 +2,15 @@ namespace Cesium.Sdk.Tests; -public class CesiumCompileTests : SdkTestBase +public class CesiumCompileTests(ITestOutputHelper testOutputHelper) : SdkTestBase(testOutputHelper) { - // [Theory] - // [InlineData()] - // public void CesiumCompile_ShouldSucceed(string projectPath) - // { - // } - public CesiumCompileTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper) + [Theory] + [InlineData("SimpleProject")] + public void CesiumCompile_ShouldSucceed(string projectName) { + var result = ExecuteTargets(projectName, "Build"); + + Assert.True(result.ExitCode == 0); + ClearOutput(); } } diff --git a/Cesium.Sdk.Tests/SdkTestBase.cs b/Cesium.Sdk.Tests/SdkTestBase.cs index 7ae43473..d28995b3 100644 --- a/Cesium.Sdk.Tests/SdkTestBase.cs +++ b/Cesium.Sdk.Tests/SdkTestBase.cs @@ -1,7 +1,6 @@ +using System.Diagnostics; using System.Reflection; using Cesium.Solution.Metadata; -using Microsoft.Build.Execution; -using Microsoft.Build.Framework; using Xunit.Abstractions; namespace Cesium.Sdk.Tests; @@ -9,7 +8,10 @@ namespace Cesium.Sdk.Tests; public abstract class SdkTestBase { private readonly ITestOutputHelper _testOutputHelper; - private readonly string _temporaryPath = Path.GetTempFileName(); + private readonly string _temporaryPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + + protected string NuGetConfigPath => Path.Combine(_temporaryPath, "NuGet.config"); + protected string GlobalJsonPath => Path.Combine(_temporaryPath, "global.json"); public SdkTestBase(ITestOutputHelper testOutputHelper) { @@ -21,46 +23,103 @@ public SdkTestBase(ITestOutputHelper testOutputHelper) var assemblyPath = Assembly.GetExecutingAssembly().Location; var testDataPath = Path.Combine(Path.GetDirectoryName(assemblyPath)!, "TestProjects"); + _testOutputHelper.WriteLine($"Copying TestProjects to {_temporaryPath}..."); CopyDirectoryRecursive(testDataPath, _temporaryPath); var nupkgPath = Path.GetFullPath(Path.Combine(SolutionMetadata.SourceRoot, "artifacts", "package", "debug")); - EmitNuGetConfig(Path.Combine(_temporaryPath, "NuGet.config"), nupkgPath); - EmitGlobalJson(Path.Combine(_temporaryPath, "global.json"), $"{SolutionMetadata.VersionPrefix}-dev"); + _testOutputHelper.WriteLine($"Local NuGet feed: {nupkgPath}."); + EmitNuGetConfig(NuGetConfigPath, nupkgPath); + EmitGlobalJson(GlobalJsonPath, $"{SolutionMetadata.VersionPrefix}"); } - protected BuildResult ExecuteTargets(string projectFile, params string[] targets) + protected BuildResult ExecuteTargets(string projectName, params string[] targets) { - var projectInstance = new ProjectInstance(projectFile); - var request = new BuildRequestData(projectInstance, targets); - var parameters = new BuildParameters + var projectFile = $"{projectName}/{projectName}.ceproj"; + var joinedTargets = string.Join(";", targets); + var testProjectFile = Path.GetFullPath(Path.Combine(_temporaryPath, projectFile)); + var testProjectFolder = Path.GetDirectoryName(testProjectFile) ?? throw new ArgumentNullException(nameof(testProjectFile)); + var startInfo = new ProcessStartInfo + { + WorkingDirectory = testProjectFolder, + FileName = "dotnet", + Arguments = $"build \"{testProjectFile}\" -t:{joinedTargets} -v:d /bl:build_result.binlog", + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + UseShellExecute = false, + }; + + using var process = new Process(); + process.StartInfo = startInfo; + + process.OutputDataReceived += (sender, e) => { - Loggers = new []{ new TestOutputLogger(_testOutputHelper) } + if (!string.IsNullOrEmpty(e.Data)) + { + _testOutputHelper.WriteLine($"[stdout]: {e.Data}"); + } + }; + + process.ErrorDataReceived += (sender, e) => + { + if (!string.IsNullOrEmpty(e.Data)) + { + _testOutputHelper.WriteLine($"[stderr]: {e.Data}"); + } }; - var result = BuildManager.DefaultBuildManager.Build(parameters, request); - return result; + + process.Start(); + + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + + process.WaitForExit(); + + var success = process.ExitCode == 0; + + _testOutputHelper.WriteLine(success + ? "Build succeeded" + : $"Build failed with exit code {process.ExitCode}"); + + var binFolder = Path.Combine(testProjectFolder, "bin"); + var objFolder = Path.Combine(testProjectFolder, "obj"); + + var binArtifacts = CollectArtifacts(binFolder); + var objArtifacts = CollectArtifacts(objFolder); + + return new BuildResult(process.ExitCode, binArtifacts, objArtifacts); + + IReadOnlyCollection CollectArtifacts(string folder) => + Directory.Exists(folder) + ? Directory.GetFiles(folder, "*.*", SearchOption.AllDirectories) + .Select(path => Path.GetRelativePath(folder, path)) + .ToList() + : Array.Empty(); } private static void EmitNuGetConfig(string configFilePath, string packageSourcePath) { - File.WriteAllText(configFilePath, $@" - - - - - - - -"); + File.WriteAllText(configFilePath, $""" + + + + + + + + + """); } private static void EmitGlobalJson(string globalJsonPath, string packageVersion) { - File.WriteAllText(globalJsonPath, $@"{{ - ""msbuild-sdks"": {{ - ""Cesium.Sdk"" : ""{packageVersion}"" - }} -}} -"); + File.WriteAllText(globalJsonPath, $$""" + { + "msbuild-sdks": { + "Cesium.Sdk" : "{{packageVersion}}" + } + } + """); } private static void CopyDirectoryRecursive(string source, string target) @@ -80,91 +139,13 @@ private static void CopyDirectoryRecursive(string source, string target) } } - private class TestOutputLogger : ILogger - { - public LoggerVerbosity Verbosity { get; set; } = LoggerVerbosity.Normal; - public string Parameters { get; set; } = string.Empty; - - private readonly ITestOutputHelper _testOutputHelper; - - public TestOutputLogger(ITestOutputHelper testOutputHelper) - { - _testOutputHelper = testOutputHelper; - } - - public void Initialize(IEventSource eventSource) - { - eventSource.AnyEventRaised += HandleEvent; - } - - public void Shutdown() - { - } - - private void HandleEvent(object sender, BuildEventArgs args) - { - var entry = args switch - { - TargetFinishedEventArgs => - new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.TargetFinished, args.Message), - TargetStartedEventArgs => - new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.TargetStarted, args.Message), - TaskFinishedEventArgs => - new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.TaskFinished, args.Message), - TaskStartedEventArgs => - new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.TaskStarted, args.Message), - BuildFinishedEventArgs => - new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.BuildFinished, args.Message), - BuildStartedEventArgs => - new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.BuildStarted, args.Message), - CustomBuildEventArgs => - new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.CustomEventRaised, args.Message), - BuildErrorEventArgs => - new BuildLogEntry(BuildLogLevel.Error, BuildLogKind.ErrorRaised, args.Message), - BuildMessageEventArgs => - new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.MessageRaised, args.Message), - ProjectFinishedEventArgs => - new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.ProjectFinished, args.Message), - ProjectStartedEventArgs => - new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.ProjectStarted, args.Message), - BuildStatusEventArgs => - new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.StatusEventRaised, args.Message), - BuildWarningEventArgs => - new BuildLogEntry(BuildLogLevel.Warning, BuildLogKind.WarningRaised, args.Message), - var other => - new BuildLogEntry(BuildLogLevel.Info, BuildLogKind.AnyEventRaised, other.Message) - }; - - _testOutputHelper.WriteLine($"[{entry.Level.ToString()}]: {entry.Message}"); - } - } + protected record BuildResult( + int ExitCode, + IReadOnlyCollection OutputArtifacts, + IReadOnlyCollection IntermediateArtifacts); - protected enum BuildLogLevel + protected void ClearOutput() { - Error, - Warning, - Info, - Verbose, - Trace, + Directory.Delete(_temporaryPath, true); } - - protected enum BuildLogKind - { - AnyEventRaised, - BuildFinished, - BuildStarted, - CustomEventRaised, - ErrorRaised, - MessageRaised, - ProjectFinished, - ProjectStarted, - StatusEventRaised, - TargetFinished, - TargetStarted, - TaskFinished, - TaskStarted, - WarningRaised - } - - protected record BuildLogEntry(BuildLogLevel Level, BuildLogKind Kind, string? Message); } diff --git a/Cesium.Sdk/Cesium.Sdk.csproj b/Cesium.Sdk/Cesium.Sdk.csproj index 488e616a..e9bd74b4 100644 --- a/Cesium.Sdk/Cesium.Sdk.csproj +++ b/Cesium.Sdk/Cesium.Sdk.csproj @@ -10,6 +10,7 @@ + diff --git a/Cesium.Sdk/Sdk/Sdk.props b/Cesium.Sdk/Sdk/Sdk.props index 3cd7c85c..f140a64a 100644 --- a/Cesium.Sdk/Sdk/Sdk.props +++ b/Cesium.Sdk/Sdk/Sdk.props @@ -29,7 +29,7 @@ - diff --git a/Cesium.Solution.Metadata/Cesium.Solution.Metadata.csproj b/Cesium.Solution.Metadata/Cesium.Solution.Metadata.csproj index 611eacd4..b5708b55 100644 --- a/Cesium.Solution.Metadata/Cesium.Solution.Metadata.csproj +++ b/Cesium.Solution.Metadata/Cesium.Solution.Metadata.csproj @@ -4,6 +4,7 @@ net8.0 enable enable + false diff --git a/Cesium.Solution.Metadata/SolutionMetadata.cs b/Cesium.Solution.Metadata/SolutionMetadata.cs index 050e2fd9..9408ea60 100644 --- a/Cesium.Solution.Metadata/SolutionMetadata.cs +++ b/Cesium.Solution.Metadata/SolutionMetadata.cs @@ -4,11 +4,10 @@ namespace Cesium.Solution.Metadata; public static class SolutionMetadata { - public static string SourceRoot => - Assembly.GetExecutingAssembly().GetCustomAttribute()?.SourceRoot - ?? throw new Exception($"Missing {nameof(SolutionMetadataAttribute)} metadata attribute."); + public static string SourceRoot => ResolvedAttribute.SourceRoot; + public static string VersionPrefix => ResolvedAttribute.VersionPrefix; - public static string VersionPrefix => - Assembly.GetExecutingAssembly().GetCustomAttribute()?.VersionPrefix + private static SolutionMetadataAttribute ResolvedAttribute => + Assembly.GetExecutingAssembly().GetCustomAttribute() ?? throw new Exception($"Missing {nameof(SolutionMetadataAttribute)} metadata attribute."); } From 6ce1bb8afd07ae1fd40e747b2331dcb270ebf971 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Fri, 22 Dec 2023 01:25:36 +0100 Subject: [PATCH 09/45] SDK: WIP --- Cesium.Compiler/Cesium.Compiler.csproj | 1 + Cesium.Sdk.Tests/SdkTestBase.cs | 2 +- .../SimpleProject/SimpleProject.ceproj | 5 +- Cesium.Sdk/Sdk/Sdk.props | 2 +- Cesium.Sdk/Sdk/Sdk.targets | 70 +++++++++++++++---- 5 files changed, 62 insertions(+), 18 deletions(-) diff --git a/Cesium.Compiler/Cesium.Compiler.csproj b/Cesium.Compiler/Cesium.Compiler.csproj index 979c81d9..5f2ffe93 100644 --- a/Cesium.Compiler/Cesium.Compiler.csproj +++ b/Cesium.Compiler/Cesium.Compiler.csproj @@ -8,6 +8,7 @@ true true Major + true diff --git a/Cesium.Sdk.Tests/SdkTestBase.cs b/Cesium.Sdk.Tests/SdkTestBase.cs index d28995b3..822acc7d 100644 --- a/Cesium.Sdk.Tests/SdkTestBase.cs +++ b/Cesium.Sdk.Tests/SdkTestBase.cs @@ -42,7 +42,7 @@ protected BuildResult ExecuteTargets(string projectName, params string[] targets { WorkingDirectory = testProjectFolder, FileName = "dotnet", - Arguments = $"build \"{testProjectFile}\" -t:{joinedTargets} -v:d /bl:build_result.binlog", + Arguments = $"build \"{testProjectFile}\" -t:{joinedTargets} -v:diag /bl:build_result.binlog", RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true, diff --git a/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj b/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj index b602a5a0..33d22c64 100644 --- a/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj +++ b/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj @@ -1,5 +1,8 @@ + + net6.0 + - + diff --git a/Cesium.Sdk/Sdk/Sdk.props b/Cesium.Sdk/Sdk/Sdk.props index f140a64a..9671dca7 100644 --- a/Cesium.Sdk/Sdk/Sdk.props +++ b/Cesium.Sdk/Sdk/Sdk.props @@ -9,7 +9,7 @@ false Cesium.Compiler - $(VersionPrefix) + 0.0.1 diff --git a/Cesium.Sdk/Sdk/Sdk.targets b/Cesium.Sdk/Sdk/Sdk.targets index 9a2b00e3..030331fa 100644 --- a/Cesium.Sdk/Sdk/Sdk.targets +++ b/Cesium.Sdk/Sdk/Sdk.targets @@ -2,32 +2,72 @@ - + <_CesiumModuleKind Condition="'$(OutputType)' == 'Exe'">Console + <_CesiumModuleKind Condition="'$(OutputType)' == 'WinExe'">Windows + <_CesiumModuleKind Condition="'$(OutputType)' == 'Dll'">Library + + + + + + + + <_CesiumFramework Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(TargetFrameworkVersion)' == 'v6.0'">net6.0 + <_CesiumFramework Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework' AND '$(TargetFrameworkVersion)' == 'v4.8'">net48 + <_CesiumFramework Condition="'$(TargetFrameworkIdentifier)' == '.NETStandard' AND '$(TargetFrameworkVersion)' == 'v2.0'">netstandard2.0 + + + + + + + + <_CesiumArchitecture Condition="'$(Platform)' == 'AnyCPU'">Dynamic + <_CesiumArchitecture Condition="'$(Platform)' == 'x64'">Bit64 + <_CesiumArchitecture Condition="'$(Platform)' == 'x86'">Bit32 + + + + + + + + <_CompilerOutput>$(OutDir)$(AssemblyName) + + + + + + Outputs="@(ResultingCommandLine)"> - - <_CompilerOutput>$(OutDir)$(AssemblyName) - + + + + + - From 5638a1f003865dcd0282e5a17e71bb9d174293d1 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sat, 23 Dec 2023 04:17:07 +0100 Subject: [PATCH 10/45] SDK: Work on compiler cross-platform packing --- Cesium.Compiler/Cesium.Compiler.csproj | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/Cesium.Compiler/Cesium.Compiler.csproj b/Cesium.Compiler/Cesium.Compiler.csproj index 5f2ffe93..7092d027 100644 --- a/Cesium.Compiler/Cesium.Compiler.csproj +++ b/Cesium.Compiler/Cesium.Compiler.csproj @@ -4,11 +4,11 @@ Exe net8.0 enable - true + true true true Major - true + win-x64;win-x86;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64 @@ -38,8 +38,24 @@ + + + <_PublishRuntimeIdentifier Include="$(RuntimeIdentifiers)" /> + + + + + + + + <_PackRuntimeIdentifier Include="$(RuntimeIdentifiers)" /> + + + + + - + true tools false From 45d1a69f88d3c60d01c4b64763a29610cdbf9a95 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 24 Dec 2023 03:42:33 +0100 Subject: [PATCH 11/45] SDK: Introduce Nuke for unified compiler packs packaging --- .config/dotnet-tools.json | 12 +++ .gitignore | 3 + .nuke/build.schema.json | 114 +++++++++++++++++++++++++ .nuke/parameters.json | 4 + Cesium.Compiler/Cesium.Compiler.csproj | 24 ------ Cesium.sln | 4 + build.cmd | 7 ++ build.ps1 | 74 ++++++++++++++++ build.sh | 67 +++++++++++++++ build/.editorconfig | 11 +++ build/Build.Sdk.cs | 99 +++++++++++++++++++++ build/Build.cs | 56 ++++++++++++ build/Configuration.cs | 16 ++++ build/Directory.Build.props | 8 ++ build/Directory.Build.targets | 8 ++ build/_build.csproj | 24 ++++++ build/_build.csproj.DotSettings | 28 ++++++ 17 files changed, 535 insertions(+), 24 deletions(-) create mode 100644 .config/dotnet-tools.json create mode 100644 .nuke/build.schema.json create mode 100644 .nuke/parameters.json create mode 100755 build.cmd create mode 100644 build.ps1 create mode 100755 build.sh create mode 100644 build/.editorconfig create mode 100644 build/Build.Sdk.cs create mode 100644 build/Build.cs create mode 100644 build/Configuration.cs create mode 100644 build/Directory.Build.props create mode 100644 build/Directory.Build.targets create mode 100644 build/_build.csproj create mode 100644 build/_build.csproj.DotSettings diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json new file mode 100644 index 00000000..b5c92636 --- /dev/null +++ b/.config/dotnet-tools.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "isRoot": true, + "tools": { + "nuke.globaltool": { + "version": "7.0.6", + "commands": [ + "nuke" + ] + } + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore index d7e0a87f..7794c572 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,12 @@ bin/ obj/ artifacts/ +/.nuke/temp *.dll *.exe *.runtimeconfig.json *.received.txt *.user + + diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json new file mode 100644 index 00000000..60a3e9fc --- /dev/null +++ b/.nuke/build.schema.json @@ -0,0 +1,114 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "$ref": "#/definitions/build", + "title": "Build Schema", + "definitions": { + "build": { + "type": "object", + "properties": { + "Configuration": { + "type": "string", + "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)", + "enum": [ + "Debug", + "Release" + ] + }, + "Continue": { + "type": "boolean", + "description": "Indicates to continue a previously failed build attempt" + }, + "Help": { + "type": "boolean", + "description": "Shows the help text for this build assembly" + }, + "Host": { + "type": "string", + "description": "Host for execution. Default is 'automatic'", + "enum": [ + "AppVeyor", + "AzurePipelines", + "Bamboo", + "Bitbucket", + "Bitrise", + "GitHubActions", + "GitLab", + "Jenkins", + "Rider", + "SpaceAutomation", + "TeamCity", + "Terminal", + "TravisCI", + "VisualStudio", + "VSCode" + ] + }, + "NoLogo": { + "type": "boolean", + "description": "Disables displaying the NUKE logo" + }, + "Partition": { + "type": "string", + "description": "Partition to use on CI" + }, + "Plan": { + "type": "boolean", + "description": "Shows the execution plan (HTML)" + }, + "Profile": { + "type": "array", + "description": "Defines the profiles to load", + "items": { + "type": "string" + } + }, + "Root": { + "type": "string", + "description": "Root directory during build execution" + }, + "Skip": { + "type": "array", + "description": "List of targets to be skipped. Empty list skips all dependencies", + "items": { + "type": "string", + "enum": [ + "Clean", + "Compile", + "PackCompilerPacks", + "PublishCompilerPacks", + "Restore" + ] + } + }, + "Solution": { + "type": "string", + "description": "Path to a solution file that is automatically loaded" + }, + "Target": { + "type": "array", + "description": "List of targets to be invoked. Default is '{default_target}'", + "items": { + "type": "string", + "enum": [ + "Clean", + "Compile", + "PackCompilerPacks", + "PublishCompilerPacks", + "Restore" + ] + } + }, + "Verbosity": { + "type": "string", + "description": "Logging verbosity during build execution. Default is 'Normal'", + "enum": [ + "Minimal", + "Normal", + "Quiet", + "Verbose" + ] + } + } + } + } +} diff --git a/.nuke/parameters.json b/.nuke/parameters.json new file mode 100644 index 00000000..45c2a69b --- /dev/null +++ b/.nuke/parameters.json @@ -0,0 +1,4 @@ +{ + "$schema": "./build.schema.json", + "Solution": "Cesium.sln" +} diff --git a/Cesium.Compiler/Cesium.Compiler.csproj b/Cesium.Compiler/Cesium.Compiler.csproj index 7092d027..67a17e10 100644 --- a/Cesium.Compiler/Cesium.Compiler.csproj +++ b/Cesium.Compiler/Cesium.Compiler.csproj @@ -38,28 +38,4 @@ - - - <_PublishRuntimeIdentifier Include="$(RuntimeIdentifiers)" /> - - - - - - - - <_PackRuntimeIdentifier Include="$(RuntimeIdentifiers)" /> - - - - - - - - true - tools - false - - - diff --git a/Cesium.sln b/Cesium.sln index b8e235e1..ae5b74e1 100644 --- a/Cesium.sln +++ b/Cesium.sln @@ -88,12 +88,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cesium.Sdk.Tests", "Cesium. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cesium.Solution.Metadata", "Cesium.Solution.Metadata\Cesium.Solution.Metadata.csproj", "{5B59ADD9-5C9D-482C-B238-F5EFD5E1F7CF}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "_build", "build\_build.csproj", "{05416E66-3615-4ABB-A130-F37EC18785A2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {05416E66-3615-4ABB-A130-F37EC18785A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {05416E66-3615-4ABB-A130-F37EC18785A2}.Release|Any CPU.ActiveCfg = Release|Any CPU {B85C397C-1F36-4A27-AB36-D03F55AE1A89}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B85C397C-1F36-4A27-AB36-D03F55AE1A89}.Debug|Any CPU.Build.0 = Debug|Any CPU {B85C397C-1F36-4A27-AB36-D03F55AE1A89}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/build.cmd b/build.cmd new file mode 100755 index 00000000..b08cc590 --- /dev/null +++ b/build.cmd @@ -0,0 +1,7 @@ +:; set -eo pipefail +:; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) +:; ${SCRIPT_DIR}/build.sh "$@" +:; exit $? + +@ECHO OFF +powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %* diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 00000000..c0c0e612 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,74 @@ +[CmdletBinding()] +Param( + [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] + [string[]]$BuildArguments +) + +Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)" + +Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 } +$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent + +########################################################################### +# CONFIGURATION +########################################################################### + +$BuildProjectFile = "$PSScriptRoot\build\_build.csproj" +$TempDirectory = "$PSScriptRoot\\.nuke\temp" + +$DotNetGlobalFile = "$PSScriptRoot\\global.json" +$DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1" +$DotNetChannel = "STS" + +$env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1 +$env:DOTNET_CLI_TELEMETRY_OPTOUT = 1 +$env:DOTNET_MULTILEVEL_LOOKUP = 0 + +########################################################################### +# EXECUTION +########################################################################### + +function ExecSafe([scriptblock] $cmd) { + & $cmd + if ($LASTEXITCODE) { exit $LASTEXITCODE } +} + +# If dotnet CLI is installed globally and it matches requested version, use for execution +if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and ` + $(dotnet --version) -and $LASTEXITCODE -eq 0) { + $env:DOTNET_EXE = (Get-Command "dotnet").Path +} +else { + # Download install script + $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1" + New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile) + + # If global.json exists, load expected version + if (Test-Path $DotNetGlobalFile) { + $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json) + if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) { + $DotNetVersion = $DotNetGlobal.sdk.version + } + } + + # Install by channel or version + $DotNetDirectory = "$TempDirectory\dotnet-win" + if (!(Test-Path variable:DotNetVersion)) { + ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath } + } else { + ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } + } + $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe" +} + +Write-Output "Microsoft (R) .NET SDK version $(& $env:DOTNET_EXE --version)" + +if (Test-Path env:NUKE_ENTERPRISE_TOKEN) { + & $env:DOTNET_EXE nuget remove source "nuke-enterprise" > $null + & $env:DOTNET_EXE nuget add source "https://f.feedz.io/nuke/enterprise/nuget" --name "nuke-enterprise" --username "PAT" --password $env:NUKE_ENTERPRISE_TOKEN > $null +} + +ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet } +ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments } diff --git a/build.sh b/build.sh new file mode 100755 index 00000000..d4a7e51e --- /dev/null +++ b/build.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash + +bash --version 2>&1 | head -n 1 + +set -eo pipefail +SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd) + +########################################################################### +# CONFIGURATION +########################################################################### + +BUILD_PROJECT_FILE="$SCRIPT_DIR/build/_build.csproj" +TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp" + +DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json" +DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh" +DOTNET_CHANNEL="STS" + +export DOTNET_CLI_TELEMETRY_OPTOUT=1 +export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 +export DOTNET_MULTILEVEL_LOOKUP=0 + +########################################################################### +# EXECUTION +########################################################################### + +function FirstJsonValue { + perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}" +} + +# If dotnet CLI is installed globally and it matches requested version, use for execution +if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then + export DOTNET_EXE="$(command -v dotnet)" +else + # Download install script + DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh" + mkdir -p "$TEMP_DIRECTORY" + curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL" + chmod +x "$DOTNET_INSTALL_FILE" + + # If global.json exists, load expected version + if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then + DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")") + if [[ "$DOTNET_VERSION" == "" ]]; then + unset DOTNET_VERSION + fi + fi + + # Install by channel or version + DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix" + if [[ -z ${DOTNET_VERSION+x} ]]; then + "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path + else + "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path + fi + export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet" +fi + +echo "Microsoft (R) .NET SDK version $("$DOTNET_EXE" --version)" + +if [[ ! -z ${NUKE_ENTERPRISE_TOKEN+x} && "NUKE_ENTERPRISE_TOKEN" != "" ]]; then + "$DOTNET_EXE" nuget remove source "nuke-enterprise" &>/dev/null || true + "$DOTNET_EXE" nuget add source "https://f.feedz.io/nuke/enterprise/nuget" --name "nuke-enterprise" --username "PAT" --password "$NUKE_ENTERPRISE_TOKEN" --store-password-in-clear-text &>/dev/null || true +fi + +"$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet +"$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@" diff --git a/build/.editorconfig b/build/.editorconfig new file mode 100644 index 00000000..31e43dcd --- /dev/null +++ b/build/.editorconfig @@ -0,0 +1,11 @@ +[*.cs] +dotnet_style_qualification_for_field = false:warning +dotnet_style_qualification_for_property = false:warning +dotnet_style_qualification_for_method = false:warning +dotnet_style_qualification_for_event = false:warning +dotnet_style_require_accessibility_modifiers = never:warning + +csharp_style_expression_bodied_methods = true:silent +csharp_style_expression_bodied_properties = true:warning +csharp_style_expression_bodied_indexers = true:warning +csharp_style_expression_bodied_accessors = true:warning diff --git a/build/Build.Sdk.cs b/build/Build.Sdk.cs new file mode 100644 index 00000000..37ff062a --- /dev/null +++ b/build/Build.Sdk.cs @@ -0,0 +1,99 @@ +using System.Collections.Generic; +using System.IO; +using NuGet.Packaging; +using NuGet.Versioning; +using Nuke.Common; +using Nuke.Common.ProjectModel; +using Nuke.Common.Tools.DotNet; +using Serilog; +using static Nuke.Common.Tools.DotNet.DotNetTasks; +using Project = Microsoft.Build.Evaluation.Project; + +public partial class Build +{ + Target PublishCompilerPacks => _ => _ + .DependsOn(Compile) + .Executes(() => + { + var compilerProject = Solution.Cesium_Compiler.GetMSBuildProject(); + var runtimeIds = compilerProject.GetProperty("RuntimeIdentifiers").EvaluatedValue.Split(";"); + Log.Information( + $"Runtime identifiers defined in {Solution.Cesium_Compiler.Name}: {string.Join(", ", runtimeIds)}"); + + foreach (var runtimeId in runtimeIds) + { + Log.Information($"Publishing for {runtimeId}..."); + DotNetPublish(o => o + .SetConfiguration(Configuration) + .SetProject(compilerProject.ProjectFileLocation.File) + .SetRuntime(runtimeId) + .SetOutput(GetCompilerRuntimePublishFolder(compilerProject, runtimeId)) + .EnableNoBuild() + .EnableNoRestore()); + } + }); + + Target PackCompilerPacks => _ => _ + .Executes(() => + { + var compilerProject = Solution.Cesium_Compiler.GetMSBuildProject(); + var runtimeIds = compilerProject.GetProperty("RuntimeIdentifiers").EvaluatedValue.Split(";"); + Log.Information( + $"Runtime identifiers defined in {Solution.Cesium_Compiler.Name}: {string.Join(", ", runtimeIds)}"); + + foreach (var runtimeId in runtimeIds) + { + Log.Information($"Packing compiler for {runtimeId}..."); + EmitCompilerPack(runtimeId, compilerProject); + } + }); + + static void EmitCompilerPack(string runtimeId, Project compilerProject) + { + var packageId = $"Cesium.Compiler.Pack.{runtimeId}"; + var packageFile = $"{packageId}.nupkg"; + var publishDirectory = GetCompilerRuntimePublishFolder(compilerProject, runtimeId); + var publishedFiles = Directory.GetFiles(publishDirectory, "*.*", SearchOption.AllDirectories); + var packageOutputPath = compilerProject.GetProperty("PackageOutputPath").EvaluatedValue; + + Log.Debug($"Source publish directory: {publishDirectory}"); + Log.Debug($"Target package ID: {packageId}"); + Log.Debug($"Target package output directory: {packageOutputPath}"); + + var builder = new PackageBuilder + { + Id = packageId, + Version = NuGetVersion.Parse(compilerProject.GetProperty("VersionPrefix").EvaluatedValue), + Description = $"Cesium compiler native executable pack for {runtimeId} platform.", + Authors = { "Cesium Team" } + }; + builder.Files.AddRange(GetPhysicalFiles(publishDirectory, publishedFiles)); + + var packageFileName = Path.Combine(packageOutputPath, packageFile); + Log.Information($"Package is ready, saving to {packageFileName}..."); + Directory.CreateDirectory(packageOutputPath); + using var outputStream = new FileStream(packageFileName, FileMode.Create); + builder.Save(outputStream); + return; + + IEnumerable GetPhysicalFiles(string publishDirectory, IEnumerable filePaths) + { + foreach (var filePath in filePaths) + { + yield return new PhysicalPackageFile + { + SourcePath = filePath, + TargetPath = $"tools/{Path.GetRelativePath(publishDirectory, filePath)}" + }; + } + } + } + + static string GetCompilerRuntimePublishFolder(Project compilerProject, string runtimeId) => + Path.Combine( + compilerProject.GetProperty("ArtifactsPath").EvaluatedValue, + compilerProject.GetProperty("ArtifactsPublishOutputName").EvaluatedValue, + GetRuntimeArtifactFolder(runtimeId)); + + static string GetRuntimeArtifactFolder(string runtimeId) => $"pack_{runtimeId}"; +} diff --git a/build/Build.cs b/build/Build.cs new file mode 100644 index 00000000..3797eab9 --- /dev/null +++ b/build/Build.cs @@ -0,0 +1,56 @@ +using System.Diagnostics; +using System.Threading; +using Nuke.Common; +using Nuke.Common.ProjectModel; +using Nuke.Common.Tools.DotNet; +using static Nuke.Common.Tools.DotNet.DotNetTasks; + +partial class Build : NukeBuild +{ + public static int Main() + { + // while (!Debugger.IsAttached) Thread.Sleep(100); + return Execute(x => x.Compile); + } + + [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] + readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release; + + [Solution(GenerateProjects = true)] + readonly Solution Solution; + + Target Clean => _ => _ + .Before(Restore) + .Executes(() => + { + DotNetClean(); + }); + + Target Restore => _ => _ + .Executes(() => + { + DotNetRestore(_ => _ + .SetProjectFile(BuildProjectFile ?? Solution.FileName)); + }); + + Target Compile => _ => _ + .DependsOn(Restore) + .Executes(() => + { + DotNetBuild(_ => _ + .SetConfiguration(Configuration) + .SetProjectFile(BuildProjectFile ?? Solution.FileName) + .EnableNoRestore()); + }); + + Target Test => _ => _ + .DependsOn(Compile) + .Executes(() => + { + DotNetTest(_ => _ + .SetConfiguration(Configuration) + .SetProjectFile(BuildProjectFile ?? Solution.FileName) + .EnableNoBuild() + .EnableNoRestore()); + }); +} diff --git a/build/Configuration.cs b/build/Configuration.cs new file mode 100644 index 00000000..9c08b1ae --- /dev/null +++ b/build/Configuration.cs @@ -0,0 +1,16 @@ +using System; +using System.ComponentModel; +using System.Linq; +using Nuke.Common.Tooling; + +[TypeConverter(typeof(TypeConverter))] +public class Configuration : Enumeration +{ + public static Configuration Debug = new Configuration { Value = nameof(Debug) }; + public static Configuration Release = new Configuration { Value = nameof(Release) }; + + public static implicit operator string(Configuration configuration) + { + return configuration.Value; + } +} diff --git a/build/Directory.Build.props b/build/Directory.Build.props new file mode 100644 index 00000000..e147d635 --- /dev/null +++ b/build/Directory.Build.props @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/build/Directory.Build.targets b/build/Directory.Build.targets new file mode 100644 index 00000000..25326095 --- /dev/null +++ b/build/Directory.Build.targets @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/build/_build.csproj b/build/_build.csproj new file mode 100644 index 00000000..9874fbb1 --- /dev/null +++ b/build/_build.csproj @@ -0,0 +1,24 @@ + + + + Exe + net8.0 + + CS0649;CS0169 + .. + .. + 1 + true + + + + + + + + + Build.cs + + + + diff --git a/build/_build.csproj.DotSettings b/build/_build.csproj.DotSettings new file mode 100644 index 00000000..eb3f4c27 --- /dev/null +++ b/build/_build.csproj.DotSettings @@ -0,0 +1,28 @@ + + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + DO_NOT_SHOW + Implicit + Implicit + ExpressionBody + 0 + NEXT_LINE + True + False + 120 + IF_OWNER_IS_SINGLE_LINE + WRAP_IF_LONG + False + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + True + True + True + True + True + True + True + True + True From c71201dabbfc58baacc1a4c6981f339750ee76bb Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sat, 16 Mar 2024 17:47:22 +0100 Subject: [PATCH 12/45] SDK: Progress with packing and testing --- .nuke/build.schema.json | 30 +++++- Cesium.Compiler/Cesium.Compiler.csproj | 2 +- Cesium.Sdk.Tests/CesiumCompileTests.cs | 2 +- Cesium.Sdk.Tests/SdkTestBase.cs | 5 +- .../SimpleProject/SimpleProject.ceproj | 2 +- Cesium.Sdk/Cesium.Sdk.csproj | 2 +- Cesium.Sdk/CesiumCompile.cs | 19 ++-- Cesium.Sdk/CommandArgumentsBuilder.cs | 2 +- Cesium.Sdk/IsExternalInit.cs | 4 + Cesium.Sdk/Sdk/Sdk.props | 2 +- Cesium.Sdk/Sdk/Sdk.targets | 36 ++++--- build/Build.Sdk.cs | 96 ++++++++++++++++--- build/Build.Tests.cs | 42 ++++++++ build/Build.cs | 32 +++---- build/ProjectExtensions.cs | 25 +++++ build/_build.csproj | 3 + build/_build.csproj.DotSettings | 5 +- 17 files changed, 242 insertions(+), 67 deletions(-) create mode 100644 Cesium.Sdk/IsExternalInit.cs create mode 100644 build/Build.Tests.cs create mode 100644 build/ProjectExtensions.cs diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index 60a3e9fc..d5dbf8e0 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -73,13 +73,26 @@ "type": "string", "enum": [ "Clean", - "Compile", + "CompileAll", + "ForceClear", "PackCompilerPacks", + "PackSdk", "PublishCompilerPacks", - "Restore" + "RestoreAll", + "TestAll", + "TestCodeGen", + "TestCompiler", + "TestIntegration", + "TestParser", + "TestRuntime", + "TestSdk" ] } }, + "SkipCaches": { + "type": "boolean", + "description": "If set to true, ignores all cached build results. Default: false" + }, "Solution": { "type": "string", "description": "Path to a solution file that is automatically loaded" @@ -91,10 +104,19 @@ "type": "string", "enum": [ "Clean", - "Compile", + "CompileAll", + "ForceClear", "PackCompilerPacks", + "PackSdk", "PublishCompilerPacks", - "Restore" + "RestoreAll", + "TestAll", + "TestCodeGen", + "TestCompiler", + "TestIntegration", + "TestParser", + "TestRuntime", + "TestSdk" ] } }, diff --git a/Cesium.Compiler/Cesium.Compiler.csproj b/Cesium.Compiler/Cesium.Compiler.csproj index 67a17e10..3f80bfde 100644 --- a/Cesium.Compiler/Cesium.Compiler.csproj +++ b/Cesium.Compiler/Cesium.Compiler.csproj @@ -4,7 +4,7 @@ Exe net8.0 enable - true + true true Major diff --git a/Cesium.Sdk.Tests/CesiumCompileTests.cs b/Cesium.Sdk.Tests/CesiumCompileTests.cs index 3ef1f753..d2d4b4cb 100644 --- a/Cesium.Sdk.Tests/CesiumCompileTests.cs +++ b/Cesium.Sdk.Tests/CesiumCompileTests.cs @@ -8,7 +8,7 @@ public class CesiumCompileTests(ITestOutputHelper testOutputHelper) : SdkTestBas [InlineData("SimpleProject")] public void CesiumCompile_ShouldSucceed(string projectName) { - var result = ExecuteTargets(projectName, "Build"); + var result = ExecuteTargets(projectName, "Restore", "Build"); Assert.True(result.ExitCode == 0); ClearOutput(); diff --git a/Cesium.Sdk.Tests/SdkTestBase.cs b/Cesium.Sdk.Tests/SdkTestBase.cs index 822acc7d..c4a2d837 100644 --- a/Cesium.Sdk.Tests/SdkTestBase.cs +++ b/Cesium.Sdk.Tests/SdkTestBase.cs @@ -7,6 +7,8 @@ namespace Cesium.Sdk.Tests; public abstract class SdkTestBase { + private const string _binLogFile = "build_result.binlog"; + private readonly ITestOutputHelper _testOutputHelper; private readonly string _temporaryPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); @@ -42,13 +44,14 @@ protected BuildResult ExecuteTargets(string projectName, params string[] targets { WorkingDirectory = testProjectFolder, FileName = "dotnet", - Arguments = $"build \"{testProjectFile}\" -t:{joinedTargets} -v:diag /bl:build_result.binlog", + Arguments = $"msbuild \"{testProjectFile}\" /t:{joinedTargets} /restore /bl:{_binLogFile}", RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true, UseShellExecute = false, }; + using var process = new Process(); process.StartInfo = startInfo; diff --git a/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj b/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj index 33d22c64..793d194e 100644 --- a/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj +++ b/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj @@ -3,6 +3,6 @@ net6.0 - + diff --git a/Cesium.Sdk/Cesium.Sdk.csproj b/Cesium.Sdk/Cesium.Sdk.csproj index e9bd74b4..4c170d49 100644 --- a/Cesium.Sdk/Cesium.Sdk.csproj +++ b/Cesium.Sdk/Cesium.Sdk.csproj @@ -1,7 +1,7 @@ - net8.0 + netstandard2.0 enable true diff --git a/Cesium.Sdk/CesiumCompile.cs b/Cesium.Sdk/CesiumCompile.cs index e2b077a7..7cf34c15 100644 --- a/Cesium.Sdk/CesiumCompile.cs +++ b/Cesium.Sdk/CesiumCompile.cs @@ -45,6 +45,7 @@ public class CesiumCompile : Task public bool DryRun = false; [Output] public string? ResultingCommandLine { get; private set; } + [Output] public TaskItem[]? OutputFiles { get; private set; } public override bool Execute() { @@ -70,6 +71,8 @@ public override bool Execute() }; ResultingCommandLine = $"{compilerProcess.StartInfo.FileName} {compilerProcess.StartInfo.Arguments}"; + OutputFiles = [new TaskItem(OutputFile)]; + if (!DryRun) { compilerProcess.Start(); @@ -122,7 +125,7 @@ private bool TryValidate(out ValidatedOptions? options) var (isFrameworkValid, framework) = TryParseFramework(Framework); if (!isFrameworkValid) { - var validValues = Enum.GetValues().Select(kind => kind.ToString()); + var validValues = Enum.GetValues(typeof(FrameworkKind)).Cast().Select(kind => kind.ToString()); ReportValidationError("CES1004", $"Framework should be in range: '{string.Join(", ", validValues)}'"); success = false; } @@ -130,7 +133,7 @@ private bool TryValidate(out ValidatedOptions? options) var (isArchValid, arch) = TryParseArchitectureKind(Architecture); if (!isArchValid) { - var validValues = Enum.GetValues().Select(kind => kind.ToString()); + var validValues = Enum.GetValues(typeof(ArchitectureKind)).Cast().Select(kind => kind.ToString()); ReportValidationError("CES1005", $"Architecture should be in range: '{string.Join(", ", validValues)}'"); success = false; } @@ -138,7 +141,7 @@ private bool TryValidate(out ValidatedOptions? options) var (isModuleKindValid, moduleKind) = TryParseModuleKind(ModuleType); if (!isModuleKindValid) { - var validValues = Enum.GetValues().Select(kind => kind.ToString()); + var validValues = Enum.GetValues(typeof(ModuleKind)).Cast().Select(kind => kind.ToString()); ReportValidationError("CES1006", $"ModuleKind should be in range: '{string.Join(", ", validValues)}'"); success = false; } @@ -172,9 +175,9 @@ private bool TryValidate(out ValidatedOptions? options) if (!success) return false; options = new ValidatedOptions( - CompilerExe: CompilerExe ?? throw new UnreachableException(), + CompilerExe: CompilerExe, InputItems: InputFiles.Select(item => item.ItemSpec).ToArray(), - OutputFile: OutputFile ?? throw new UnreachableException(), + OutputFile: OutputFile, Namespace: Namespace, Framework: framework, Architecture: arch, @@ -214,7 +217,7 @@ private IEnumerable CollectCommandLineArguments(ValidatedOptions options if (!string.IsNullOrWhiteSpace(options.Namespace)) { yield return "--namespace"; - yield return options.Namespace; + yield return options.Namespace!; } foreach (var import in options.ImportItems) @@ -226,13 +229,13 @@ private IEnumerable CollectCommandLineArguments(ValidatedOptions options if (!string.IsNullOrWhiteSpace(options.CoreLibPath)) { yield return "--corelib"; - yield return options.CoreLibPath; + yield return options.CoreLibPath!; } if (!string.IsNullOrWhiteSpace(options.RuntimePath)) { yield return "--runtime"; - yield return options.RuntimePath; + yield return options.RuntimePath!; } foreach (var item in options.PreprocessorItems) diff --git a/Cesium.Sdk/CommandArgumentsBuilder.cs b/Cesium.Sdk/CommandArgumentsBuilder.cs index 9de668d2..28c13a92 100644 --- a/Cesium.Sdk/CommandArgumentsBuilder.cs +++ b/Cesium.Sdk/CommandArgumentsBuilder.cs @@ -12,7 +12,7 @@ public class CommandArgumentsBuilder public CommandArgumentsBuilder Argument(string argument) { - _builder.Append(" "); + _builder.Append(' '); if (NeedsEscaping(argument)) { argument = Escape(argument); diff --git a/Cesium.Sdk/IsExternalInit.cs b/Cesium.Sdk/IsExternalInit.cs new file mode 100644 index 00000000..eb2da113 --- /dev/null +++ b/Cesium.Sdk/IsExternalInit.cs @@ -0,0 +1,4 @@ +namespace System.Runtime.CompilerServices +{ + internal static class IsExternalInit {} +} diff --git a/Cesium.Sdk/Sdk/Sdk.props b/Cesium.Sdk/Sdk/Sdk.props index 9671dca7..288784bd 100644 --- a/Cesium.Sdk/Sdk/Sdk.props +++ b/Cesium.Sdk/Sdk/Sdk.props @@ -8,7 +8,7 @@ false - Cesium.Compiler + Cesium.Compiler.Pack.$(NETCoreSdkRuntimeIdentifier) 0.0.1 diff --git a/Cesium.Sdk/Sdk/Sdk.targets b/Cesium.Sdk/Sdk/Sdk.targets index 030331fa..e5b64ae2 100644 --- a/Cesium.Sdk/Sdk/Sdk.targets +++ b/Cesium.Sdk/Sdk/Sdk.targets @@ -2,10 +2,13 @@ + + + <_CesiumModuleKind Condition="'$(OutputType)' == 'Exe'">Console <_CesiumModuleKind Condition="'$(OutputType)' == 'WinExe'">Windows - <_CesiumModuleKind Condition="'$(OutputType)' == 'Dll'">Library + <_CesiumModuleKind Condition="'$(OutputType)' == 'Library'">Library @@ -36,24 +39,28 @@ - <_CompilerOutput>$(OutDir)$(AssemblyName) + <_CompilerOutput>$(IntermediateOutputPath)$(AssemblyName) - + Outputs="$(_CompilerOutput)"> - - - + + + + + + + + + + + PreprocessorItems="$(DefineConstants.Split(';'))"> + + + + + diff --git a/build/Build.Sdk.cs b/build/Build.Sdk.cs index 37ff062a..f4ce604c 100644 --- a/build/Build.Sdk.cs +++ b/build/Build.Sdk.cs @@ -11,8 +11,9 @@ public partial class Build { + const string _compilerPackPackagePrefix = "Cesium.Compiler.Pack"; + Target PublishCompilerPacks => _ => _ - .DependsOn(Compile) .Executes(() => { var compilerProject = Solution.Cesium_Compiler.GetMSBuildProject(); @@ -22,48 +23,78 @@ public partial class Build foreach (var runtimeId in runtimeIds) { + Log.Information(SkipCaches+""); + if (!SkipCaches && !NeedPublishCompilerPack(compilerProject, runtimeId)) + { + Log.Information($"Skipping {runtimeId} because it was already published. Use '--skip-caches true' to re-publish."); + continue; + } + Log.Information($"Publishing for {runtimeId}..."); DotNetPublish(o => o .SetConfiguration(Configuration) .SetProject(compilerProject.ProjectFileLocation.File) .SetRuntime(runtimeId) - .SetOutput(GetCompilerRuntimePublishFolder(compilerProject, runtimeId)) - .EnableNoBuild() - .EnableNoRestore()); + .SetOutput(GetCompilerRuntimePublishFolder(compilerProject, runtimeId))); } }); Target PackCompilerPacks => _ => _ + .DependsOn(PublishCompilerPacks) .Executes(() => { var compilerProject = Solution.Cesium_Compiler.GetMSBuildProject(); - var runtimeIds = compilerProject.GetProperty("RuntimeIdentifiers").EvaluatedValue.Split(";"); + var runtimeIds = compilerProject.GetRuntimeIds(); Log.Information( $"Runtime identifiers defined in {Solution.Cesium_Compiler.Name}: {string.Join(", ", runtimeIds)}"); foreach (var runtimeId in runtimeIds) { + if (!SkipCaches && !NeedPackageCompilerPack(compilerProject, runtimeId)) + { + Log.Information($"Skipping {runtimeId} because it was already packed. Use '--skip-caches true' to re-pack."); + continue; + } + Log.Information($"Packing compiler for {runtimeId}..."); EmitCompilerPack(runtimeId, compilerProject); } }); - static void EmitCompilerPack(string runtimeId, Project compilerProject) + Target PackSdk => _ => _ + .Executes(() => + { + var sdkProject = Solution.Cesium_Sdk.GetMSBuildProject(); + if (!SkipCaches && !NeedPackageSdk(sdkProject)) + { + Log.Information($"Skipping SDK packing because it was already packed. Use '--skip-caches true' to re-publish."); + return; + } + + Log.Information($"Packing SDK..."); + DotNetPack(o => o + .SetConfiguration(Configuration) + .SetProject(Solution.Cesium_Sdk.Path)); + }); + + void EmitCompilerPack(string runtimeId, Project compilerProject) { - var packageId = $"Cesium.Compiler.Pack.{runtimeId}"; - var packageFile = $"{packageId}.nupkg"; + var version = compilerProject.GetVersion(); + var runtimePackageId = GetRuntimePackId(runtimeId); + var packageFile = GetRuntimePackFileName(version, runtimeId); var publishDirectory = GetCompilerRuntimePublishFolder(compilerProject, runtimeId); + Directory.CreateDirectory(publishDirectory); var publishedFiles = Directory.GetFiles(publishDirectory, "*.*", SearchOption.AllDirectories); - var packageOutputPath = compilerProject.GetProperty("PackageOutputPath").EvaluatedValue; + var packageOutputPath = compilerProject.GetPackageOutputPath(); Log.Debug($"Source publish directory: {publishDirectory}"); - Log.Debug($"Target package ID: {packageId}"); + Log.Debug($"Target package ID: {runtimePackageId}"); Log.Debug($"Target package output directory: {packageOutputPath}"); var builder = new PackageBuilder { - Id = packageId, - Version = NuGetVersion.Parse(compilerProject.GetProperty("VersionPrefix").EvaluatedValue), + Id = runtimePackageId, + Version = NuGetVersion.Parse(compilerProject.GetVersion()), Description = $"Cesium compiler native executable pack for {runtimeId} platform.", Authors = { "Cesium Team" } }; @@ -89,11 +120,46 @@ IEnumerable GetPhysicalFiles(string publishDirectory, IEnumerable< } } - static string GetCompilerRuntimePublishFolder(Project compilerProject, string runtimeId) => + string GetCompilerRuntimePublishFolder(Project compilerProject, string runtimeId) => Path.Combine( compilerProject.GetProperty("ArtifactsPath").EvaluatedValue, compilerProject.GetProperty("ArtifactsPublishOutputName").EvaluatedValue, - GetRuntimeArtifactFolder(runtimeId)); + Solution.Cesium_Compiler.Name, + GetRuntimeArtifactFolder(compilerProject.GetVersion(), runtimeId)); + + static string GetRuntimeArtifactFolder(string version, string runtimeId) => + $"pack_{version}_{runtimeId}"; - static string GetRuntimeArtifactFolder(string runtimeId) => $"pack_{runtimeId}"; + static string GetRuntimePackId(string runtimeId) => + $"{_compilerPackPackagePrefix}.{runtimeId}"; + + static string GetRuntimePackFileName(string version, string runtimeId) => + $"{_compilerPackPackagePrefix}.{runtimeId}.{version}.nupkg"; + + bool NeedPublishCompilerPack(Project compiler, string runtimeId) + { + var folder = GetCompilerRuntimePublishFolder(compiler, runtimeId); + + return !Directory.Exists(folder) + || Directory.GetFiles(folder, "Cesium.Compiler*").Length == 0; + } + + bool NeedPackageCompilerPack(Project compiler, string runtimeId) + { + var version = compiler.GetVersion(); + var packageDirectory = compiler.GetPackageOutputPath(); + var packageFileName = GetRuntimePackFileName(version, runtimeId); + + return !File.Exists(Path.Combine(packageDirectory, packageFileName)); + } + + bool NeedPackageSdk(Project sdk) + { + var packageId = sdk.GetProperty("PackageVersion").EvaluatedValue; + var version = sdk.GetProperty("PackageId").EvaluatedValue; + var packageDirectory = sdk.GetPackageOutputPath(); + var packageFileName = $"{packageId}.{version}"; + + return !File.Exists(Path.Combine(packageDirectory, packageFileName)); + } } diff --git a/build/Build.Tests.cs b/build/Build.Tests.cs new file mode 100644 index 00000000..0a8a5747 --- /dev/null +++ b/build/Build.Tests.cs @@ -0,0 +1,42 @@ +using Nuke.Common; +using Nuke.Common.ProjectModel; +using Nuke.Common.Tools.DotNet; +using static Nuke.Common.Tools.DotNet.DotNetTasks; + +partial class Build +{ + Target TestCodeGen => _ => _ + .Executes(() => ExecuteTests(Solution.Cesium_CodeGen_Tests)); + + Target TestCompiler => _ => _ + .Executes(() => ExecuteTests(Solution.Cesium_Compiler_Tests)); + + Target TestIntegration => _ => _ + .Executes(() => ExecuteTests(Solution.Cesium_IntegrationTests)); + + Target TestParser => _ => _ + .Executes(() => ExecuteTests(Solution.Cesium_Parser_Tests)); + + Target TestRuntime => _ => _ + .Executes(() => ExecuteTests(Solution.Cesium_Runtime_Tests)); + + Target TestSdk => _ => _ + .DependsOn(PackCompilerPacks) + .DependsOn(PackSdk) + .Executes(() => ExecuteTests(Solution.Cesium_Sdk_Tests)); + + Target TestAll => _ => _ + .DependsOn(TestCodeGen) + .DependsOn(TestCompiler) + .DependsOn(TestIntegration) + .DependsOn(TestParser) + .DependsOn(TestRuntime) + .DependsOn(TestSdk); + + void ExecuteTests(Project project) + { + DotNetTest(_ => _ + .SetConfiguration(Configuration) + .SetProjectFile(project.GetMSBuildProject().ProjectFileLocation.File)); + } +} diff --git a/build/Build.cs b/build/Build.cs index 3797eab9..ab63eeaf 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -1,5 +1,3 @@ -using System.Diagnostics; -using System.Threading; using Nuke.Common; using Nuke.Common.ProjectModel; using Nuke.Common.Tools.DotNet; @@ -10,47 +8,43 @@ partial class Build : NukeBuild public static int Main() { // while (!Debugger.IsAttached) Thread.Sleep(100); - return Execute(x => x.Compile); + return Execute(x => x.CompileAll); } [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release; + [Parameter("If set to true, ignores all cached build results. Default: false")] + readonly bool SkipCaches = false; + [Solution(GenerateProjects = true)] readonly Solution Solution; Target Clean => _ => _ - .Before(Restore) + .Before(RestoreAll) .Executes(() => { DotNetClean(); }); - Target Restore => _ => _ + Target RestoreAll => _ => _ .Executes(() => { DotNetRestore(_ => _ - .SetProjectFile(BuildProjectFile ?? Solution.FileName)); + .SetProjectFile(Solution.FileName)); }); - Target Compile => _ => _ - .DependsOn(Restore) + Target CompileAll => _ => _ + .DependsOn(RestoreAll) .Executes(() => { DotNetBuild(_ => _ .SetConfiguration(Configuration) - .SetProjectFile(BuildProjectFile ?? Solution.FileName) + .SetProjectFile(Solution.FileName) .EnableNoRestore()); }); - Target Test => _ => _ - .DependsOn(Compile) - .Executes(() => - { - DotNetTest(_ => _ - .SetConfiguration(Configuration) - .SetProjectFile(BuildProjectFile ?? Solution.FileName) - .EnableNoBuild() - .EnableNoRestore()); - }); + Target ForceClear => _ => _ + .OnlyWhenDynamic(() => SkipCaches) + .Before(RestoreAll); } diff --git a/build/ProjectExtensions.cs b/build/ProjectExtensions.cs new file mode 100644 index 00000000..88720be9 --- /dev/null +++ b/build/ProjectExtensions.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using Microsoft.Build.Evaluation; + +public static class ProjectExtensions +{ + public static IReadOnlyCollection GetRuntimeIds(this Project project) + { + return project.GetProperty("RuntimeIdentifiers").EvaluatedValue.Split(";"); + } + + public static string GetVersion(this Project project) + { + return project.GetProperty("VersionPrefix").EvaluatedValue; + } + + public static string GetPackageOutputPath(this Project project) + { + return project.GetProperty("PackageOutputPath").EvaluatedValue; + } + + public static string GetPublishDirectory(this Project project) + { + return project.GetProperty("PublishDir").EvaluatedValue; + } +} diff --git a/build/_build.csproj b/build/_build.csproj index 9874fbb1..0b05104a 100644 --- a/build/_build.csproj +++ b/build/_build.csproj @@ -19,6 +19,9 @@ Build.cs + + Build.cs + diff --git a/build/_build.csproj.DotSettings b/build/_build.csproj.DotSettings index eb3f4c27..c815d363 100644 --- a/build/_build.csproj.DotSettings +++ b/build/_build.csproj.DotSettings @@ -17,6 +17,8 @@ False <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /> + <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy> + <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy> True True True @@ -25,4 +27,5 @@ True True True - True + True + True From e956f2935b163f1f1a0670b16bbd04e2afbd1fc1 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 17 Mar 2024 03:00:13 +0100 Subject: [PATCH 13/45] SDK: Introduce option to publish & pack only specific RID --- build/Build.Sdk.cs | 27 +++++++++++++++++++++++++-- build/Build.cs | 9 ++++++++- build/ProjectExtensions.cs | 13 +++++++++---- 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/build/Build.Sdk.cs b/build/Build.Sdk.cs index f4ce604c..bba696cb 100644 --- a/build/Build.Sdk.cs +++ b/build/Build.Sdk.cs @@ -18,16 +18,28 @@ public partial class Build { var compilerProject = Solution.Cesium_Compiler.GetMSBuildProject(); var runtimeIds = compilerProject.GetProperty("RuntimeIdentifiers").EvaluatedValue.Split(";"); + + var runtimeIds = compilerProject.GetEvaluatedProperty("RuntimeIdentifiers").Split(";"); Log.Information( $"Runtime identifiers defined in {Solution.Cesium_Compiler.Name}: {string.Join(", ", runtimeIds)}"); + if (!string.IsNullOrEmpty(RuntimeId)) + { + Log.Information($"Executing only {RuntimeId} because it was specified explicitly."); + PublishCompiler(RuntimeId); + return; + } + foreach (var runtimeId in runtimeIds) + PublishCompiler(runtimeId); + + void PublishCompiler(string runtimeId) { Log.Information(SkipCaches+""); if (!SkipCaches && !NeedPublishCompilerPack(compilerProject, runtimeId)) { Log.Information($"Skipping {runtimeId} because it was already published. Use '--skip-caches true' to re-publish."); - continue; + return; } Log.Information($"Publishing for {runtimeId}..."); @@ -44,16 +56,27 @@ public partial class Build .Executes(() => { var compilerProject = Solution.Cesium_Compiler.GetMSBuildProject(); + var runtimeIds = compilerProject.GetRuntimeIds(); Log.Information( $"Runtime identifiers defined in {Solution.Cesium_Compiler.Name}: {string.Join(", ", runtimeIds)}"); + if (!string.IsNullOrEmpty(RuntimeId)) + { + Log.Information($"Executing only {RuntimeId} because it was specified explicitly."); + PackCompiler(RuntimeId); + return; + } + foreach (var runtimeId in runtimeIds) + PackCompiler(runtimeId); + + void PackCompiler(string runtimeId) { if (!SkipCaches && !NeedPackageCompilerPack(compilerProject, runtimeId)) { Log.Information($"Skipping {runtimeId} because it was already packed. Use '--skip-caches true' to re-pack."); - continue; + return; } Log.Information($"Packing compiler for {runtimeId}..."); diff --git a/build/Build.cs b/build/Build.cs index ab63eeaf..3a745e49 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -1,5 +1,6 @@ using Nuke.Common; using Nuke.Common.ProjectModel; +using Nuke.Common.Tooling; using Nuke.Common.Tools.DotNet; using static Nuke.Common.Tools.DotNet.DotNetTasks; @@ -20,17 +21,22 @@ public static int Main() [Solution(GenerateProjects = true)] readonly Solution Solution; + [Parameter("If set, only executes targets for a specified runtime identifier. Provided RID must be included in property of Cesium.Compiler project.")] + readonly string RuntimeId = string.Empty; + Target Clean => _ => _ .Before(RestoreAll) .Executes(() => { - DotNetClean(); + DotNetClean(_ => _ + .Apply(settings => !string.IsNullOrEmpty(RuntimeId) ? settings.SetRuntime(RuntimeId) : settings)); }); Target RestoreAll => _ => _ .Executes(() => { DotNetRestore(_ => _ + .Apply(settings => !string.IsNullOrEmpty(RuntimeId) ? settings.SetRuntime(RuntimeId) : settings) .SetProjectFile(Solution.FileName)); }); @@ -39,6 +45,7 @@ public static int Main() .Executes(() => { DotNetBuild(_ => _ + .Apply(settings => !string.IsNullOrEmpty(RuntimeId) ? settings.SetRuntime(RuntimeId) : settings) .SetConfiguration(Configuration) .SetProjectFile(Solution.FileName) .EnableNoRestore()); diff --git a/build/ProjectExtensions.cs b/build/ProjectExtensions.cs index 88720be9..25577c9a 100644 --- a/build/ProjectExtensions.cs +++ b/build/ProjectExtensions.cs @@ -5,21 +5,26 @@ public static class ProjectExtensions { public static IReadOnlyCollection GetRuntimeIds(this Project project) { - return project.GetProperty("RuntimeIdentifiers").EvaluatedValue.Split(";"); + return project.GetEvaluatedProperty("RuntimeIdentifiers").Split(";"); } public static string GetVersion(this Project project) { - return project.GetProperty("VersionPrefix").EvaluatedValue; + return project.GetEvaluatedProperty("VersionPrefix"); } public static string GetPackageOutputPath(this Project project) { - return project.GetProperty("PackageOutputPath").EvaluatedValue; + return project.GetEvaluatedProperty("PackageOutputPath"); } public static string GetPublishDirectory(this Project project) { - return project.GetProperty("PublishDir").EvaluatedValue; + return project.GetEvaluatedProperty("PublishDir"); + } + + public static string GetEvaluatedProperty(this Project project, string name) + { + return project.GetProperty(name).EvaluatedValue; } } From cd3c49af34083851c79be4391ed398893566c3ce Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 17 Mar 2024 03:00:46 +0100 Subject: [PATCH 14/45] SDK: Fix compilation after packages consolidation --- Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj | 14 +++++++------- Cesium.Sdk/Cesium.Sdk.csproj | 2 +- Directory.Packages.props | 4 ++++ build/Build.Sdk.cs | 1 - build/_build.csproj | 2 +- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj b/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj index fad9a01e..f91ea2ed 100644 --- a/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj +++ b/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj @@ -10,16 +10,16 @@ - - - - - - + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/Cesium.Sdk/Cesium.Sdk.csproj b/Cesium.Sdk/Cesium.Sdk.csproj index 4c170d49..dc1b44cd 100644 --- a/Cesium.Sdk/Cesium.Sdk.csproj +++ b/Cesium.Sdk/Cesium.Sdk.csproj @@ -14,7 +14,7 @@ - + diff --git a/Directory.Packages.props b/Directory.Packages.props index 2779af0a..b63f73f3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -21,5 +21,9 @@ + + + + diff --git a/build/Build.Sdk.cs b/build/Build.Sdk.cs index bba696cb..7977293a 100644 --- a/build/Build.Sdk.cs +++ b/build/Build.Sdk.cs @@ -17,7 +17,6 @@ public partial class Build .Executes(() => { var compilerProject = Solution.Cesium_Compiler.GetMSBuildProject(); - var runtimeIds = compilerProject.GetProperty("RuntimeIdentifiers").EvaluatedValue.Split(";"); var runtimeIds = compilerProject.GetEvaluatedProperty("RuntimeIdentifiers").Split(";"); Log.Information( diff --git a/build/_build.csproj b/build/_build.csproj index 0b05104a..98f857ce 100644 --- a/build/_build.csproj +++ b/build/_build.csproj @@ -12,7 +12,7 @@ - + From 0fc5d1e5ad1c36c55dd3c0365ce4994a87d33ee7 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 17 Mar 2024 03:01:24 +0100 Subject: [PATCH 15/45] SDK: Add missing build schema update --- .nuke/build.schema.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index d5dbf8e0..fe4add0c 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -66,6 +66,10 @@ "type": "string", "description": "Root directory during build execution" }, + "RuntimeId": { + "type": "string", + "description": "If set, only executes targets for a specified runtime identifier. Provided RID must be included in property of Cesium.Compiler project" + }, "Skip": { "type": "array", "description": "List of targets to be skipped. Empty list skips all dependencies", From d097f932255309b580d15e5b543a0c3ccb93a4a3 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 17 Mar 2024 03:01:48 +0100 Subject: [PATCH 16/45] SDK: Add missing Self-contained option to compiler publishing --- build/Build.Sdk.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/build/Build.Sdk.cs b/build/Build.Sdk.cs index 7977293a..d374db47 100644 --- a/build/Build.Sdk.cs +++ b/build/Build.Sdk.cs @@ -46,6 +46,7 @@ void PublishCompiler(string runtimeId) .SetConfiguration(Configuration) .SetProject(compilerProject.ProjectFileLocation.File) .SetRuntime(runtimeId) + .SetSelfContained(true) .SetOutput(GetCompilerRuntimePublishFolder(compilerProject, runtimeId))); } }); From d8025188306acc8bc87921c87b5a711a8b472c73 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 17 Mar 2024 03:02:25 +0100 Subject: [PATCH 17/45] SDK: Make enum-related compiler properties validation more self-descriptive --- Cesium.Sdk/CesiumCompile.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Cesium.Sdk/CesiumCompile.cs b/Cesium.Sdk/CesiumCompile.cs index 7cf34c15..b366c2e3 100644 --- a/Cesium.Sdk/CesiumCompile.cs +++ b/Cesium.Sdk/CesiumCompile.cs @@ -126,7 +126,7 @@ private bool TryValidate(out ValidatedOptions? options) if (!isFrameworkValid) { var validValues = Enum.GetValues(typeof(FrameworkKind)).Cast().Select(kind => kind.ToString()); - ReportValidationError("CES1004", $"Framework should be in range: '{string.Join(", ", validValues)}'"); + ReportValidationError("CES1004", $"Framework should be in range: '{string.Join(", ", validValues)}', actual: '{Framework}'"); success = false; } @@ -134,7 +134,7 @@ private bool TryValidate(out ValidatedOptions? options) if (!isArchValid) { var validValues = Enum.GetValues(typeof(ArchitectureKind)).Cast().Select(kind => kind.ToString()); - ReportValidationError("CES1005", $"Architecture should be in range: '{string.Join(", ", validValues)}'"); + ReportValidationError("CES1005", $"Architecture should be in range: '{string.Join(", ", validValues)}', actual: '{Architecture}'"); success = false; } @@ -142,7 +142,7 @@ private bool TryValidate(out ValidatedOptions? options) if (!isModuleKindValid) { var validValues = Enum.GetValues(typeof(ModuleKind)).Cast().Select(kind => kind.ToString()); - ReportValidationError("CES1006", $"ModuleKind should be in range: '{string.Join(", ", validValues)}'"); + ReportValidationError("CES1006", $"ModuleKind should be in range: '{string.Join(", ", validValues)}', actual: '{ModuleType}'"); success = false; } From 98a2cf4e415e8153de7f5c580962098b696ac45c Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 17 Mar 2024 03:03:23 +0100 Subject: [PATCH 18/45] SDK: Fix issues with proper properties propagation from MSBuild targets to Task --- Cesium.Sdk.Tests/CesiumCompileTests.cs | 2 +- Cesium.Sdk/Sdk/Sdk.props | 11 ----------- Cesium.Sdk/Sdk/Sdk.targets | 22 ++++++++++++++++------ 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/Cesium.Sdk.Tests/CesiumCompileTests.cs b/Cesium.Sdk.Tests/CesiumCompileTests.cs index d2d4b4cb..16d496dc 100644 --- a/Cesium.Sdk.Tests/CesiumCompileTests.cs +++ b/Cesium.Sdk.Tests/CesiumCompileTests.cs @@ -11,6 +11,6 @@ public void CesiumCompile_ShouldSucceed(string projectName) var result = ExecuteTargets(projectName, "Restore", "Build"); Assert.True(result.ExitCode == 0); - ClearOutput(); + // ClearOutput(); } } diff --git a/Cesium.Sdk/Sdk/Sdk.props b/Cesium.Sdk/Sdk/Sdk.props index 288784bd..4de4e806 100644 --- a/Cesium.Sdk/Sdk/Sdk.props +++ b/Cesium.Sdk/Sdk/Sdk.props @@ -15,20 +15,9 @@ - - $(PkgCesium_Compiler) - - $(CesiumCompilerPackagePath)/tools/$(CesiumCompilerPackageName) - - - $(CesiumCompilerPackagePath)/tools/$(CesiumCompilerPackageName).exe - - - diff --git a/Cesium.Sdk/Sdk/Sdk.targets b/Cesium.Sdk/Sdk/Sdk.targets index e5b64ae2..ed277b6a 100644 --- a/Cesium.Sdk/Sdk/Sdk.targets +++ b/Cesium.Sdk/Sdk/Sdk.targets @@ -2,13 +2,23 @@ + + <_NuGetPackageRootNormalized Condition="$(CesiumCompilerPackagePath) == ''">$([MSBuild]::EnsureTrailingSlash('$(NuGetPackageRoot)')) + <_CesiumCompilerPackageFolderName Condition="$(CesiumCompilerPackagePath) == ''">$(CesiumCompilerPackageName.ToLower()) + <_CesiumCompilerPackagePath Condition="$(CesiumCompilerPackagePath) == ''">$(_NuGetPackageRootNormalized) + $([System.IO.Path]::Combine($(_CesiumCompilerPackagePath), $(_CesiumCompilerPackageFolderName), $(CesiumCompilerPackageVersion))) + $(CesiumCompilerPackagePath)/tools/Cesium.Compiler + $(CesiumCompilerPackagePath)/tools/Cesium.Compiler.exe + $([System.IO.File]::Exists('$(CesiumCompilerPath)')) + + <_CesiumModuleKind Condition="'$(OutputType)' == 'Exe'">Console <_CesiumModuleKind Condition="'$(OutputType)' == 'WinExe'">Windows - <_CesiumModuleKind Condition="'$(OutputType)' == 'Library'">Library + <_CesiumModuleKind Condition="'$(OutputType)' == 'Library'">Dll @@ -17,9 +27,9 @@ - <_CesiumFramework Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(TargetFrameworkVersion)' == 'v6.0'">net6.0 - <_CesiumFramework Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework' AND '$(TargetFrameworkVersion)' == 'v4.8'">net48 - <_CesiumFramework Condition="'$(TargetFrameworkIdentifier)' == '.NETStandard' AND '$(TargetFrameworkVersion)' == 'v2.0'">netstandard2.0 + <_CesiumFramework Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND '$(TargetFrameworkVersion)' == 'v6.0'">Net + <_CesiumFramework Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework' AND '$(TargetFrameworkVersion)' == 'v4.8'">NetFramework + <_CesiumFramework Condition="'$(TargetFrameworkIdentifier)' == '.NETStandard' AND '$(TargetFrameworkVersion)' == 'v2.0'">NetStandard @@ -71,8 +81,8 @@ ModuleType="$(_CesiumModuleKind)" RuntimePath="$(RuntimePath)" PreprocessorItems="$(DefineConstants.Split(';'))"> - - + + From c17076c4e75f38b83585781281cc9acd70bd1f29 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 17 Mar 2024 18:37:25 +0100 Subject: [PATCH 19/45] Compiler: Fix AOT-related exception from CommandLineParser --- Cesium.Compiler/Main.cs | 71 +++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/Cesium.Compiler/Main.cs b/Cesium.Compiler/Main.cs index 2ccd37d2..f7098266 100644 --- a/Cesium.Compiler/Main.cs +++ b/Cesium.Compiler/Main.cs @@ -1,43 +1,52 @@ +using System.Diagnostics.CodeAnalysis; using Cesium.CodeGen; -using Cesium.Compiler; using Cesium.Core; using Mono.Cecil; -await CommandLineParser.ParseCommandLineArgs(args, new CompilerReporter(), async args => +namespace Cesium.Compiler; + +public static class Program +{ + [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Arguments))] + public static async Task Main(string[] args) { - var targetArchitectureSet = args.TargetArchitectureSet; - var targetRuntime = args.Framework switch + return await CommandLineParser.ParseCommandLineArgs(args, new CompilerReporter(), async args => { - TargetFrameworkKind.NetFramework => TargetRuntimeDescriptor.Net48, - TargetFrameworkKind.NetStandard => TargetRuntimeDescriptor.NetStandard20, - _ => TargetRuntimeDescriptor.Net60 - }; + var targetArchitectureSet = args.TargetArchitectureSet; + var targetRuntime = args.Framework switch + { + TargetFrameworkKind.NetFramework => TargetRuntimeDescriptor.Net48, + TargetFrameworkKind.NetStandard => TargetRuntimeDescriptor.NetStandard20, + _ => TargetRuntimeDescriptor.Net60 + }; - var cesiumRuntime = args.CesiumCRuntime ?? Path.Combine(AppContext.BaseDirectory, "Cesium.Runtime.dll"); - var defaultImportsAssembly = args.DefaultImportAssemblies ?? Array.Empty(); + var cesiumRuntime = args.CesiumCRuntime ?? Path.Combine(AppContext.BaseDirectory, "Cesium.Runtime.dll"); + var defaultImportsAssembly = args.DefaultImportAssemblies ?? Array.Empty(); #pragma warning disable IL3000 // Automatic discovery of corelib is fallback option, if tooling do not pass that parameter - var corelibAssembly = args.CoreLib ?? typeof(Math).Assembly.Location; // System.Runtime.dll + var corelibAssembly = args.CoreLib ?? typeof(Math).Assembly.Location; // System.Runtime.dll #pragma warning restore IL3000 - var moduleKind = args.ProducePreprocessedFile ? ModuleKind.Console : args.ModuleKind ?? Path.GetExtension(args.OutputFilePath).ToLowerInvariant() switch - { - ".exe" => ModuleKind.Console, - ".dll" => ModuleKind.Dll, - var o => throw new CompilationException($"Unknown file extension: {o}. \"modulekind\" is not specified.") - }; - var compilationOptions = new CompilationOptions( - targetRuntime, - targetArchitectureSet, - moduleKind, - corelibAssembly, - cesiumRuntime, - defaultImportsAssembly, - args.Namespace, - args.GlobalClass, - args.DefineConstant.ToList(), - args.IncludeDirectories.ToList(), - args.ProducePreprocessedFile); - return await Compilation.Compile(args.InputFilePaths, args.OutputFilePath, compilationOptions); - }); + var moduleKind = args.ProducePreprocessedFile ? ModuleKind.Console : args.ModuleKind ?? Path.GetExtension(args.OutputFilePath).ToLowerInvariant() switch + { + ".exe" => ModuleKind.Console, + ".dll" => ModuleKind.Dll, + var o => throw new CompilationException($"Unknown file extension: {o}. \"modulekind\" is not specified.") + }; + var compilationOptions = new CompilationOptions( + targetRuntime, + targetArchitectureSet, + moduleKind, + corelibAssembly, + cesiumRuntime, + defaultImportsAssembly, + args.Namespace, + args.GlobalClass, + args.DefineConstant.ToList(), + args.IncludeDirectories.ToList(), + args.ProducePreprocessedFile); + return await Compilation.Compile(args.InputFilePaths, args.OutputFilePath, compilationOptions); + }); + } +} class CompilerReporter : ICompilerReporter { From 04215a35b33375b7f09cb1538dad4b1d696ca3dc Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 17 Mar 2024 18:58:20 +0100 Subject: [PATCH 20/45] SDK: Fix compilation task --- Cesium.Sdk/CesiumCompile.cs | 55 +++++++++++++-------------- Cesium.Sdk/CommandArgumentsBuilder.cs | 12 ++++-- Cesium.Sdk/Sdk/Sdk.targets | 30 ++++++++++----- 3 files changed, 55 insertions(+), 42 deletions(-) diff --git a/Cesium.Sdk/CesiumCompile.cs b/Cesium.Sdk/CesiumCompile.cs index b366c2e3..7201cee3 100644 --- a/Cesium.Sdk/CesiumCompile.cs +++ b/Cesium.Sdk/CesiumCompile.cs @@ -54,18 +54,12 @@ public override bool Execute() return false; } - var argumentsBuilder = new CommandArgumentsBuilder(); - foreach (var argument in CollectCommandLineArguments(options)) - { - argumentsBuilder.Argument(argument); - } - var compilerProcess = new Process { StartInfo = { FileName = options.CompilerExe, - Arguments = argumentsBuilder.Build(), + Arguments = CollectCommandLineArguments(options), UseShellExecute = false, } }; @@ -191,66 +185,69 @@ private bool TryValidate(out ValidatedOptions? options) return true; } - private IEnumerable CollectCommandLineArguments(ValidatedOptions options) + private string CollectCommandLineArguments(ValidatedOptions options) { - yield return options.CompilerExe; - yield return "--nologo"; + var builder = new CommandArgumentsBuilder(); + + builder.RawArgument("--nologo"); if (options.Framework is { } framework) { - yield return "--framework"; - yield return framework.ToString(); + builder.RawArgument("--framework"); + builder.RawArgument(framework.ToString()); } if (options.Architecture is { } arch) { - yield return "--arch"; - yield return arch.ToString(); + builder.RawArgument("--arch"); + builder.RawArgument(arch.ToString()); } if (options.ModuleKind is { } moduleKind) { - yield return "--modulekind"; - yield return moduleKind.ToString(); + builder.RawArgument("--modulekind"); + builder.RawArgument(moduleKind.ToString()); } if (!string.IsNullOrWhiteSpace(options.Namespace)) { - yield return "--namespace"; - yield return options.Namespace!; + builder.RawArgument("--namespace"); + builder.RawArgument(options.Namespace!); } foreach (var import in options.ImportItems) { - yield return "--import"; - yield return import; + builder.RawArgument("--import"); + builder.Argument(import); } if (!string.IsNullOrWhiteSpace(options.CoreLibPath)) { - yield return "--corelib"; - yield return options.CoreLibPath!; + builder.RawArgument("--corelib"); + builder.Argument(options.CoreLibPath!); } if (!string.IsNullOrWhiteSpace(options.RuntimePath)) { - yield return "--runtime"; - yield return options.RuntimePath!; + builder.RawArgument("--runtime"); + builder.Argument(options.RuntimePath!); } foreach (var item in options.PreprocessorItems) { - yield return "-D"; - yield return item; + builder.RawArgument("-D"); + builder.Argument(item); } - yield return "--out"; - yield return $"\"{options.OutputFile}\""; + builder.RawArgument("--out"); + builder.Argument(options.OutputFile); foreach (var input in options.InputItems) { - yield return $"\"{input}\""; + builder.Argument(input); } + + return builder.Build(); } private void ReportValidationError(string code, string message) => diff --git a/Cesium.Sdk/CommandArgumentsBuilder.cs b/Cesium.Sdk/CommandArgumentsBuilder.cs index 28c13a92..ffcc4077 100644 --- a/Cesium.Sdk/CommandArgumentsBuilder.cs +++ b/Cesium.Sdk/CommandArgumentsBuilder.cs @@ -10,13 +10,19 @@ public class CommandArgumentsBuilder { private StringBuilder _builder = new StringBuilder(); + public CommandArgumentsBuilder RawArgument(string value) + { + _builder.Append(' '); + _builder.Append(value); + + return this; + } + public CommandArgumentsBuilder Argument(string argument) { _builder.Append(' '); if (NeedsEscaping(argument)) - { argument = Escape(argument); - } _builder.Append(argument); return this; @@ -25,7 +31,7 @@ public CommandArgumentsBuilder Argument(string argument) public string Build() => _builder.ToString(); private bool NeedsEscaping(string argument) => - argument.Length > 0 && argument.All(c => !char.IsWhiteSpace(c) && c != '"'); + argument.Length > 0 && argument.Any(c => char.IsWhiteSpace(c) || c != '"'); private static string Escape(string argument) { diff --git a/Cesium.Sdk/Sdk/Sdk.targets b/Cesium.Sdk/Sdk/Sdk.targets index ed277b6a..c1ea306e 100644 --- a/Cesium.Sdk/Sdk/Sdk.targets +++ b/Cesium.Sdk/Sdk/Sdk.targets @@ -2,19 +2,30 @@ - - <_NuGetPackageRootNormalized Condition="$(CesiumCompilerPackagePath) == ''">$([MSBuild]::EnsureTrailingSlash('$(NuGetPackageRoot)')) + <_CesiumCompilerPackageFolderName Condition="$(CesiumCompilerPackagePath) == ''">$(CesiumCompilerPackageName.ToLower()) - <_CesiumCompilerPackagePath Condition="$(CesiumCompilerPackagePath) == ''">$(_NuGetPackageRootNormalized) - $([System.IO.Path]::Combine($(_CesiumCompilerPackagePath), $(_CesiumCompilerPackageFolderName), $(CesiumCompilerPackageVersion))) - $(CesiumCompilerPackagePath)/tools/Cesium.Compiler - $(CesiumCompilerPackagePath)/tools/Cesium.Compiler.exe - $([System.IO.File]::Exists('$(CesiumCompilerPath)')) + <_CesiumCompilerPackagePath>$([System.IO.Path]::Combine($(NuGetPackageRoot), $(_CesiumCompilerPackageFolderName), $(CesiumCompilerPackageVersion))) + $(_CesiumCompilerPackagePath)/tools/Cesium.Compiler + $(_CesiumCompilerPackagePath)\tools\Cesium.Compiler.exe + + + + + + + <_CoreLibAssembly Include="@(ReferencePath)" Condition="'%(AssemblyName)'=='System.Private.CoreLib' OR '%(AssemblyName)'=='mscorlib'" /> + + + %(_CoreLibAssembly.Identity) + + + <_CesiumModuleKind Condition="'$(OutputType)' == 'Exe'">Console <_CesiumModuleKind Condition="'$(OutputType)' == 'WinExe'">Windows @@ -68,18 +79,17 @@ - From 63953e87ff09e7a785c1083fbc9fbd51d978ff6a Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 7 Apr 2024 05:48:45 +0200 Subject: [PATCH 21/45] CodeGen: Fix System.Runtime usage instead of System.Private.CoreLib --- Cesium.CodeGen/Contexts/AssemblyContext.cs | 2 +- Cesium.CodeGen/Ir/Types/InPlaceArrayType.cs | 10 +++++----- Cesium.CodeGen/Ir/Types/StructType.cs | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Cesium.CodeGen/Contexts/AssemblyContext.cs b/Cesium.CodeGen/Contexts/AssemblyContext.cs index 39b5951e..b90b57e4 100644 --- a/Cesium.CodeGen/Contexts/AssemblyContext.cs +++ b/Cesium.CodeGen/Contexts/AssemblyContext.cs @@ -299,7 +299,7 @@ private TypeReference GetStubType(int size) "", stubStructTypeName, TypeAttributes.Sealed | TypeAttributes.ExplicitLayout | TypeAttributes.NestedPrivate, - Module.ImportReference(MscorlibAssembly.GetType("System.ValueType"))) + Module.ImportReference(new TypeReference("System", "ValueType", MscorlibAssembly.MainModule, MscorlibAssembly.MainModule.TypeSystem.CoreLibrary))) { PackingSize = 1, ClassSize = size diff --git a/Cesium.CodeGen/Ir/Types/InPlaceArrayType.cs b/Cesium.CodeGen/Ir/Types/InPlaceArrayType.cs index 421eaad7..9965a11a 100644 --- a/Cesium.CodeGen/Ir/Types/InPlaceArrayType.cs +++ b/Cesium.CodeGen/Ir/Types/InPlaceArrayType.cs @@ -37,8 +37,8 @@ public FieldDefinition CreateFieldOfType(TranslationUnitContext context, TypeDef CustomAttribute GenerateCustomFieldAttribute() { - var typeType = context.Module.ImportReference(context.AssemblyContext.MscorlibAssembly.GetType("System.Type")); - var fixedBufferAttributeType = context.AssemblyContext.MscorlibAssembly.GetType("System.Runtime.CompilerServices.FixedBufferAttribute") ?? throw new AssertException( + var typeType = context.Module.ImportReference(new TypeReference("System", "Type", context.AssemblyContext.MscorlibAssembly.MainModule, context.AssemblyContext.MscorlibAssembly.MainModule.TypeSystem.CoreLibrary)); + var fixedBufferAttributeType = new TypeReference("System.Runtime.CompilerServices", "FixedBufferAttribute", context.AssemblyContext.MscorlibAssembly.MainModule, context.AssemblyContext.MscorlibAssembly.MainModule.TypeSystem.CoreLibrary) ?? throw new AssertException( "Cannot find a type System.Runtime.CompilerServices.FixedBufferAttribute."); var fixedBufferCtor = new MethodReference(".ctor", context.TypeSystem.Void, fixedBufferAttributeType); fixedBufferCtor.Parameters.Add(new ParameterDefinition(typeType)); @@ -104,12 +104,12 @@ private static TypeDefinition CreateFixedBufferType( // } ModuleDefinition module = context.Module; - var compilerGeneratedAttributeType = context.AssemblyContext.MscorlibAssembly.GetType("System.Runtime.CompilerServices.CompilerGeneratedAttribute") ?? throw new AssertException( + var compilerGeneratedAttributeType = new TypeReference("System.Runtime.CompilerServices", "CompilerGeneratedAttribute", context.AssemblyContext.MscorlibAssembly.MainModule, context.AssemblyContext.MscorlibAssembly.MainModule.TypeSystem.CoreLibrary) ?? throw new AssertException( "Cannot find a type System.Runtime.CompilerServices.CompilerGeneratedAttribute."); var compilerGeneratedCtor = new MethodReference(".ctor", context.TypeSystem.Void, compilerGeneratedAttributeType); var compilerGeneratedAttribute = new CustomAttribute(module.ImportReference(compilerGeneratedCtor)); - var unsafeValueTypeAttributeType = context.AssemblyContext.MscorlibAssembly.GetType("System.Runtime.CompilerServices.UnsafeValueTypeAttribute") ?? throw new AssertException( + var unsafeValueTypeAttributeType = new TypeReference("System.Runtime.CompilerServices", "UnsafeValueTypeAttribute", context.AssemblyContext.MscorlibAssembly.MainModule, context.AssemblyContext.MscorlibAssembly.MainModule.TypeSystem.CoreLibrary) ?? throw new AssertException( "Cannot find a type System.Runtime.CompilerServices.UnsafeValueTypeAttribute."); var unsafeValueTypeCtor = new MethodReference(".ctor", context.TypeSystem.Void, unsafeValueTypeAttributeType); var unsafeValueTypeAttribute = new CustomAttribute(module.ImportReference(unsafeValueTypeCtor)); @@ -118,7 +118,7 @@ private static TypeDefinition CreateFixedBufferType( "", $"{fieldName}", TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.SequentialLayout | TypeAttributes.NestedPublic, - module.ImportReference(context.AssemblyContext.MscorlibAssembly.GetType("System.ValueType"))) + module.ImportReference(new TypeReference("System", "ValueType", context.AssemblyContext.MscorlibAssembly.MainModule, context.AssemblyContext.MscorlibAssembly.MainModule.TypeSystem.CoreLibrary))) { PackingSize = 0, ClassSize = sizeInBytes, diff --git a/Cesium.CodeGen/Ir/Types/StructType.cs b/Cesium.CodeGen/Ir/Types/StructType.cs index 069af96a..71e9a47a 100644 --- a/Cesium.CodeGen/Ir/Types/StructType.cs +++ b/Cesium.CodeGen/Ir/Types/StructType.cs @@ -42,7 +42,7 @@ public TypeDefinition StartEmit(string name, TranslationUnitContext context) "", Identifier is null ? "" + name : Identifier, TypeAttributes.Sealed, - context.Module.ImportReference(context.AssemblyContext.MscorlibAssembly.GetType("System.ValueType"))); + context.Module.ImportReference(new TypeReference("System", "ValueType", context.AssemblyContext.MscorlibAssembly.MainModule, context.AssemblyContext.MscorlibAssembly.MainModule.TypeSystem.CoreLibrary))); switch (context.AssemblyContext.ArchitectureSet) { case TargetArchitectureSet.Dynamic: @@ -106,7 +106,7 @@ private void EmitAsAnonStructure(TranslationUnitContext context) string.Empty, CreateAnonIdentifier(Members, IsUnion), TypeAttributes.Public | TypeAttributes.Sealed, - context.Module.ImportReference(context.AssemblyContext.MscorlibAssembly.GetType("System.ValueType"))); + context.Module.ImportReference(new TypeReference("System", "ValueType", context.AssemblyContext.MscorlibAssembly.MainModule, context.AssemblyContext.MscorlibAssembly.MainModule.TypeSystem.CoreLibrary))); FinishEmit(type, type.Name, context); // emit fields From 80e98f0c306247c797ab3a7fbd58067c8cafdf78 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 7 Apr 2024 05:49:40 +0200 Subject: [PATCH 22/45] SDK: Add ability to publish non-AOT compiler pack --- .nuke/build.schema.json | 4 ++++ Directory.Packages.props | 4 ++-- build/Build.Sdk.cs | 5 ++++- build/Build.cs | 3 +++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index fe4add0c..e8ff42e1 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -62,6 +62,10 @@ "type": "string" } }, + "PublishAot": { + "type": "boolean", + "description": "If set to true, publishes compiler packs in AOT mode" + }, "Root": { "type": "string", "description": "Root directory during build execution" diff --git a/Directory.Packages.props b/Directory.Packages.props index b63f73f3..cf992554 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -21,9 +21,9 @@ - + - + \ No newline at end of file diff --git a/build/Build.Sdk.cs b/build/Build.Sdk.cs index d374db47..c0de8de3 100644 --- a/build/Build.Sdk.cs +++ b/build/Build.Sdk.cs @@ -41,12 +41,15 @@ void PublishCompiler(string runtimeId) return; } - Log.Information($"Publishing for {runtimeId}..."); + Log.Information($"Publishing for {runtimeId}, AOT {(PublishAot ? "enabled" : "disabled")}..."); DotNetPublish(o => o .SetConfiguration(Configuration) .SetProject(compilerProject.ProjectFileLocation.File) .SetRuntime(runtimeId) .SetSelfContained(true) + .SetPublishTrimmed(PublishAot) + .SetPublishSingleFile(PublishAot) + .SetProperty("PublishAot", PublishAot) .SetOutput(GetCompilerRuntimePublishFolder(compilerProject, runtimeId))); } }); diff --git a/build/Build.cs b/build/Build.cs index 3a745e49..571627a2 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -24,6 +24,9 @@ public static int Main() [Parameter("If set, only executes targets for a specified runtime identifier. Provided RID must be included in property of Cesium.Compiler project.")] readonly string RuntimeId = string.Empty; + [Parameter("If set to true, publishes compiler packs in AOT mode.")] + readonly bool PublishAot = false; + Target Clean => _ => _ .Before(RestoreAll) .Executes(() => From fe87deb23c4eb6710a235a8bb59b8664060e5b45 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 7 Apr 2024 05:50:40 +0200 Subject: [PATCH 23/45] SDK: Auto-publish Cesium.Runtime --- Cesium.Runtime/Cesium.Runtime.csproj | 1 + Cesium.Sdk/Sdk/Sdk.props | 10 +++++++++- Cesium.Sdk/Sdk/Sdk.targets | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Cesium.Runtime/Cesium.Runtime.csproj b/Cesium.Runtime/Cesium.Runtime.csproj index a8da8707..38cce558 100644 --- a/Cesium.Runtime/Cesium.Runtime.csproj +++ b/Cesium.Runtime/Cesium.Runtime.csproj @@ -6,6 +6,7 @@ enable true latest + true diff --git a/Cesium.Sdk/Sdk/Sdk.props b/Cesium.Sdk/Sdk/Sdk.props index 4de4e806..21406e78 100644 --- a/Cesium.Sdk/Sdk/Sdk.props +++ b/Cesium.Sdk/Sdk/Sdk.props @@ -8,6 +8,7 @@ false + false Cesium.Compiler.Pack.$(NETCoreSdkRuntimeIdentifier) 0.0.1 @@ -15,7 +16,14 @@ + PrivateAssets="all" + ExcludeAssets="runtime"/> + + + + - <_CoreLibAssembly Include="@(ReferencePath)" Condition="'%(AssemblyName)'=='System.Private.CoreLib' OR '%(AssemblyName)'=='mscorlib'" /> + <_CoreLibAssembly Include="@(ReferencePath)" Condition="'%(ReferencePath.AssemblyName)'=='System.Runtime' OR '%(ReferencePath.AssemblyName)'=='mscorlib'" /> %(_CoreLibAssembly.Identity) From 77202ea79cd86681be61e1acd38100909dd78f36 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 7 Apr 2024 05:52:36 +0200 Subject: [PATCH 24/45] SDK: Add missing configuration & fixes - Skip ref asm copying - Generate .runtimeconfig.json - Fix missing .dll extension - Fix OutputType in test data --- .../TestProjects/SimpleProject/SimpleProject.ceproj | 1 + Cesium.Sdk/Sdk/Sdk.props | 1 + Cesium.Sdk/Sdk/Sdk.targets | 7 ++++++- 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj b/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj index 793d194e..228362dd 100644 --- a/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj +++ b/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj @@ -1,6 +1,7 @@ net6.0 + Exe diff --git a/Cesium.Sdk/Sdk/Sdk.props b/Cesium.Sdk/Sdk/Sdk.props index 21406e78..0b681ff1 100644 --- a/Cesium.Sdk/Sdk/Sdk.props +++ b/Cesium.Sdk/Sdk/Sdk.props @@ -11,6 +11,7 @@ false Cesium.Compiler.Pack.$(NETCoreSdkRuntimeIdentifier) 0.0.1 + false diff --git a/Cesium.Sdk/Sdk/Sdk.targets b/Cesium.Sdk/Sdk/Sdk.targets index 22f16993..3008362c 100644 --- a/Cesium.Sdk/Sdk/Sdk.targets +++ b/Cesium.Sdk/Sdk/Sdk.targets @@ -2,6 +2,11 @@ + + true + false + + <_CesiumCompilerPackageFolderName Condition="$(CesiumCompilerPackagePath) == ''">$(CesiumCompilerPackageName.ToLower()) <_CesiumCompilerPackagePath>$([System.IO.Path]::Combine($(NuGetPackageRoot), $(_CesiumCompilerPackageFolderName), $(CesiumCompilerPackageVersion))) @@ -60,7 +65,7 @@ - <_CompilerOutput>$(IntermediateOutputPath)$(AssemblyName) + <_CompilerOutput>$(IntermediateOutputPath)$(AssemblyName).dll From e478946b63caad4f4398039759c03ca561482ea9 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 7 Apr 2024 22:45:24 +0200 Subject: [PATCH 25/45] SDK: Fix build for Exe and WinExe output types in netfx --- Cesium.Sdk/Sdk/Sdk.targets | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Cesium.Sdk/Sdk/Sdk.targets b/Cesium.Sdk/Sdk/Sdk.targets index 3008362c..c59669a6 100644 --- a/Cesium.Sdk/Sdk/Sdk.targets +++ b/Cesium.Sdk/Sdk/Sdk.targets @@ -65,7 +65,11 @@ - <_CompilerOutput>$(IntermediateOutputPath)$(AssemblyName).dll + <_CompilerOutputBase>$(IntermediateOutputPath)$(AssemblyName) + <_CompilerOutputExtenion>dll + + <_CompilerOutputExtenion Condition="$(_CesiumFramework) == 'NetFramework' AND $(OutputType) != 'Library'">exe + <_CompilerOutput>$(_CompilerOutputBase).$(_CompilerOutputExtenion) From f20d99077b901d793ff389aeea5bf637b9660dff Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 7 Apr 2024 22:46:44 +0200 Subject: [PATCH 26/45] SDK: Add more tests Including netfx tests --- Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj | 1 + Cesium.Sdk.Tests/CesiumCompileTests.cs | 92 ++++++++++++++++++- Cesium.Sdk.Tests/Framework/AssertEx.cs | 12 +++ .../IncludesAssertFailedException.cs | 8 ++ Cesium.Sdk.Tests/MSBuildCli.cs | 32 +++++++ Cesium.Sdk.Tests/SdkTestBase.cs | 29 ++++-- .../SimpleCoreExe.ceproj} | 0 .../{SimpleProject => SimpleCoreExe}/hello.c | 0 .../SimpleCoreLibrary.ceproj | 9 ++ .../TestProjects/SimpleCoreLibrary/library.c | 29 ++++++ .../SimpleNetStandardLibrary.ceproj | 9 ++ .../SimpleNetStandardLibrary/library.c | 29 ++++++ .../SimpleNetfxExe/SimpleNetfxExe.ceproj | 9 ++ .../TestProjects/SimpleNetfxExe/hello.c | 7 ++ .../SimpleNetfxLibrary.ceproj | 9 ++ .../TestProjects/SimpleNetfxLibrary/library.c | 29 ++++++ 16 files changed, 294 insertions(+), 10 deletions(-) create mode 100644 Cesium.Sdk.Tests/Framework/AssertEx.cs create mode 100644 Cesium.Sdk.Tests/Framework/IncludesAssertFailedException.cs create mode 100644 Cesium.Sdk.Tests/MSBuildCli.cs rename Cesium.Sdk.Tests/TestProjects/{SimpleProject/SimpleProject.ceproj => SimpleCoreExe/SimpleCoreExe.ceproj} (100%) rename Cesium.Sdk.Tests/TestProjects/{SimpleProject => SimpleCoreExe}/hello.c (100%) create mode 100644 Cesium.Sdk.Tests/TestProjects/SimpleCoreLibrary/SimpleCoreLibrary.ceproj create mode 100644 Cesium.Sdk.Tests/TestProjects/SimpleCoreLibrary/library.c create mode 100644 Cesium.Sdk.Tests/TestProjects/SimpleNetStandardLibrary/SimpleNetStandardLibrary.ceproj create mode 100644 Cesium.Sdk.Tests/TestProjects/SimpleNetStandardLibrary/library.c create mode 100644 Cesium.Sdk.Tests/TestProjects/SimpleNetfxExe/SimpleNetfxExe.ceproj create mode 100644 Cesium.Sdk.Tests/TestProjects/SimpleNetfxExe/hello.c create mode 100644 Cesium.Sdk.Tests/TestProjects/SimpleNetfxLibrary/SimpleNetfxLibrary.ceproj create mode 100644 Cesium.Sdk.Tests/TestProjects/SimpleNetfxLibrary/library.c diff --git a/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj b/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj index f91ea2ed..eb98fcf5 100644 --- a/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj +++ b/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj @@ -10,6 +10,7 @@ + diff --git a/Cesium.Sdk.Tests/CesiumCompileTests.cs b/Cesium.Sdk.Tests/CesiumCompileTests.cs index 16d496dc..4a89d6c1 100644 --- a/Cesium.Sdk.Tests/CesiumCompileTests.cs +++ b/Cesium.Sdk.Tests/CesiumCompileTests.cs @@ -1,3 +1,5 @@ +using System.Runtime.InteropServices; +using Cesium.Sdk.Tests.Framework; using Xunit.Abstractions; namespace Cesium.Sdk.Tests; @@ -5,12 +7,96 @@ namespace Cesium.Sdk.Tests; public class CesiumCompileTests(ITestOutputHelper testOutputHelper) : SdkTestBase(testOutputHelper) { [Theory] - [InlineData("SimpleProject")] - public void CesiumCompile_ShouldSucceed(string projectName) + [InlineData("SimpleCoreExe")] + public void CesiumCompile_Core_Exe_ShouldSucceed(string projectName) { + HashSet expectedObjArtifacts = + [ + $"{projectName}.dll" + ]; + + var hostExeFile = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? $"{projectName}.exe" : projectName; + HashSet expectedBinArtifacts = + [ + $"{projectName}.dll", + hostExeFile, + "Cesium.Runtime.dll", + $"{projectName}.runtimeconfig.json", + $"{projectName}.deps.json", + ]; + + var result = ExecuteTargets(projectName, "Restore", "Build"); + + Assert.True(result.ExitCode == 0); + AssertEx.Includes(expectedObjArtifacts, result.IntermediateArtifacts); + AssertEx.Includes(expectedBinArtifacts, result.OutputArtifacts); + } + + [Theory] + [InlineData("SimpleNetfxExe")] + public void CesiumCompile_NetFx_Exe_ShouldSucceed(string projectName) + { + HashSet expectedObjArtifacts = + [ + $"{projectName}.exe" + ]; + + HashSet expectedBinArtifacts = + [ + $"{projectName}.exe", + "Cesium.Runtime.dll", + $"{projectName}.runtimeconfig.json" + ]; + + var result = ExecuteTargets(projectName, "Restore", "Build"); + + Assert.True(result.ExitCode == 0); + AssertEx.Includes(expectedObjArtifacts, result.IntermediateArtifacts); + AssertEx.Includes(expectedBinArtifacts, result.OutputArtifacts); + } + + [Theory] + [InlineData("SimpleCoreLibrary")] + [InlineData("SimpleNetStandardLibrary")] + public void CesiumCompile_Core_Library_ShouldSucceed(string projectName) + { + string[] expectedObjArtifacts = + [ + $"{projectName}.dll" + ]; + + string[] expectedBinArtifacts = + [ + $"{projectName}.dll", + $"{projectName}.deps.json", + ]; + + var result = ExecuteTargets(projectName, "Restore", "Build"); + + Assert.True(result.ExitCode == 0); + AssertEx.Includes(expectedObjArtifacts, result.IntermediateArtifacts); + AssertEx.Includes(expectedBinArtifacts, result.OutputArtifacts); + } + + [Theory] + [InlineData("SimpleNetfxLibrary")] + public void CesiumCompile_NetFxLibrary_ShouldSucceed(string projectName) + { + HashSet expectedObjArtifacts = + [ + $"{projectName}.dll" + ]; + + HashSet expectedBinArtifacts = + [ + $"{projectName}.dll", + "Cesium.Runtime.dll", + ]; + var result = ExecuteTargets(projectName, "Restore", "Build"); Assert.True(result.ExitCode == 0); - // ClearOutput(); + AssertEx.Includes(expectedObjArtifacts, result.IntermediateArtifacts); + AssertEx.Includes(expectedBinArtifacts, result.OutputArtifacts); } } diff --git a/Cesium.Sdk.Tests/Framework/AssertEx.cs b/Cesium.Sdk.Tests/Framework/AssertEx.cs new file mode 100644 index 00000000..1bf223c7 --- /dev/null +++ b/Cesium.Sdk.Tests/Framework/AssertEx.cs @@ -0,0 +1,12 @@ +namespace Cesium.Sdk.Tests.Framework; + +public static class AssertEx +{ + public static void Includes(IReadOnlyCollection expected, IReadOnlyCollection all) + { + var foundItems = all.Where(expected.Contains).ToList(); + var remainingItems = expected.Except(foundItems).ToList(); + if (remainingItems.Count != 0) + throw new IncludesAssertFailedException(remainingItems); + } +} diff --git a/Cesium.Sdk.Tests/Framework/IncludesAssertFailedException.cs b/Cesium.Sdk.Tests/Framework/IncludesAssertFailedException.cs new file mode 100644 index 00000000..9306e719 --- /dev/null +++ b/Cesium.Sdk.Tests/Framework/IncludesAssertFailedException.cs @@ -0,0 +1,8 @@ +using Xunit.Sdk; + +namespace Cesium.Sdk.Tests.Framework; + +public class IncludesAssertFailedException( + IEnumerable expected, + Exception? innerException = null) + : XunitException($"Expected elements are missing: [{string.Join(", ", expected)}]", innerException); diff --git a/Cesium.Sdk.Tests/MSBuildCli.cs b/Cesium.Sdk.Tests/MSBuildCli.cs new file mode 100644 index 00000000..6de5291e --- /dev/null +++ b/Cesium.Sdk.Tests/MSBuildCli.cs @@ -0,0 +1,32 @@ +using System.Text.Json; +using Medallion.Shell; + +namespace Cesium.Sdk.Tests; + +public static class MSBuildCli +{ + public static string EvaluateProperty(string projectPath, string propertyName) + { + var command = Command.Run("dotnet", "msbuild", $"\"{projectPath}\"", $"-getProperty:{propertyName}"); + command.Wait(); + return command.Result.StandardOutput; + } + + public static IReadOnlyDictionary EvaluateProperties(string projectPath, params string[] propertyNames) + { + if (!propertyNames.Any()) + return new Dictionary(); + + var command = Command.Run("dotnet", "msbuild", $"\"{projectPath}\"", $"-getProperty:{string.Join(",", propertyNames)}"); + command.Wait(); + var resultString = command.Result.StandardOutput; + if (propertyNames.Length == 1) + return new Dictionary { { propertyNames[0], resultString } }; + + var resultJson = JsonDocument.Parse(resultString); + var propertiesJson = resultJson.RootElement.GetProperty("Properties").EnumerateObject().ToArray(); + + return propertiesJson + .ToDictionary(property => property.Name, property => property.Value.GetString() ?? string.Empty); + } +} diff --git a/Cesium.Sdk.Tests/SdkTestBase.cs b/Cesium.Sdk.Tests/SdkTestBase.cs index c4a2d837..84ec0a8a 100644 --- a/Cesium.Sdk.Tests/SdkTestBase.cs +++ b/Cesium.Sdk.Tests/SdkTestBase.cs @@ -1,11 +1,12 @@ using System.Diagnostics; using System.Reflection; +using System.Text.Json; using Cesium.Solution.Metadata; using Xunit.Abstractions; namespace Cesium.Sdk.Tests; -public abstract class SdkTestBase +public abstract class SdkTestBase : IDisposable { private const string _binLogFile = "build_result.binlog"; @@ -40,18 +41,22 @@ protected BuildResult ExecuteTargets(string projectName, params string[] targets var joinedTargets = string.Join(";", targets); var testProjectFile = Path.GetFullPath(Path.Combine(_temporaryPath, projectFile)); var testProjectFolder = Path.GetDirectoryName(testProjectFile) ?? throw new ArgumentNullException(nameof(testProjectFile)); + var binLogFile = Path.Combine(testProjectFolder, $"build_result_{projectName}_{DateTime.UtcNow:yyyy-dd-M_HH-mm-s}.binlog"); + + const string objFolderPropertyName = "IntermediateOutputPath"; + const string binFolderPropertyName = "OutDir"; + var startInfo = new ProcessStartInfo { WorkingDirectory = testProjectFolder, FileName = "dotnet", - Arguments = $"msbuild \"{testProjectFile}\" /t:{joinedTargets} /restore /bl:{_binLogFile}", + Arguments = $"msbuild \"{testProjectFile}\" /t:{joinedTargets} /restore /bl:{binLogFile}", RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true, UseShellExecute = false, }; - using var process = new Process(); process.StartInfo = startInfo; @@ -84,13 +89,18 @@ protected BuildResult ExecuteTargets(string projectName, params string[] targets ? "Build succeeded" : $"Build failed with exit code {process.ExitCode}"); - var binFolder = Path.Combine(testProjectFolder, "bin"); - var objFolder = Path.Combine(testProjectFolder, "obj"); + var properties = MSBuildCli.EvaluateProperties(testProjectFile, objFolderPropertyName, binFolderPropertyName); + _testOutputHelper.WriteLine($"Properties request result: {JsonSerializer.Serialize(properties, new JsonSerializerOptions { WriteIndented = false })}"); + + var binFolder = Path.Combine(testProjectFolder, properties[binFolderPropertyName]); + var objFolder = Path.Combine(testProjectFolder, properties[objFolderPropertyName]); var binArtifacts = CollectArtifacts(binFolder); var objArtifacts = CollectArtifacts(objFolder); - return new BuildResult(process.ExitCode, binArtifacts, objArtifacts); + var result = new BuildResult(process.ExitCode, binArtifacts, objArtifacts); + _testOutputHelper.WriteLine($"Build result: {JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true })}"); + return result; IReadOnlyCollection CollectArtifacts(string folder) => Directory.Exists(folder) @@ -147,8 +157,13 @@ protected record BuildResult( IReadOnlyCollection OutputArtifacts, IReadOnlyCollection IntermediateArtifacts); - protected void ClearOutput() + private void ClearOutput() { Directory.Delete(_temporaryPath, true); } + + public void Dispose() + { + ClearOutput(); + } } diff --git a/Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj b/Cesium.Sdk.Tests/TestProjects/SimpleCoreExe/SimpleCoreExe.ceproj similarity index 100% rename from Cesium.Sdk.Tests/TestProjects/SimpleProject/SimpleProject.ceproj rename to Cesium.Sdk.Tests/TestProjects/SimpleCoreExe/SimpleCoreExe.ceproj diff --git a/Cesium.Sdk.Tests/TestProjects/SimpleProject/hello.c b/Cesium.Sdk.Tests/TestProjects/SimpleCoreExe/hello.c similarity index 100% rename from Cesium.Sdk.Tests/TestProjects/SimpleProject/hello.c rename to Cesium.Sdk.Tests/TestProjects/SimpleCoreExe/hello.c diff --git a/Cesium.Sdk.Tests/TestProjects/SimpleCoreLibrary/SimpleCoreLibrary.ceproj b/Cesium.Sdk.Tests/TestProjects/SimpleCoreLibrary/SimpleCoreLibrary.ceproj new file mode 100644 index 00000000..69513748 --- /dev/null +++ b/Cesium.Sdk.Tests/TestProjects/SimpleCoreLibrary/SimpleCoreLibrary.ceproj @@ -0,0 +1,9 @@ + + + net6.0 + Library + + + + + diff --git a/Cesium.Sdk.Tests/TestProjects/SimpleCoreLibrary/library.c b/Cesium.Sdk.Tests/TestProjects/SimpleCoreLibrary/library.c new file mode 100644 index 00000000..c6d65a06 --- /dev/null +++ b/Cesium.Sdk.Tests/TestProjects/SimpleCoreLibrary/library.c @@ -0,0 +1,29 @@ +#include + +void greet() +{ + puts("Hello World!\n"); +} + +int add(int a, int b) +{ + return a + b; +} + +int subtract(int a, int b) +{ + return a - b; +} + +float divide(int a, int b) +{ + if (b != 0) + return (float) a / b; + else + return 0; +} + +int multiply(int a, int b) +{ + return a * b; +} diff --git a/Cesium.Sdk.Tests/TestProjects/SimpleNetStandardLibrary/SimpleNetStandardLibrary.ceproj b/Cesium.Sdk.Tests/TestProjects/SimpleNetStandardLibrary/SimpleNetStandardLibrary.ceproj new file mode 100644 index 00000000..06f876d5 --- /dev/null +++ b/Cesium.Sdk.Tests/TestProjects/SimpleNetStandardLibrary/SimpleNetStandardLibrary.ceproj @@ -0,0 +1,9 @@ + + + netstandard2.0 + Library + + + + + diff --git a/Cesium.Sdk.Tests/TestProjects/SimpleNetStandardLibrary/library.c b/Cesium.Sdk.Tests/TestProjects/SimpleNetStandardLibrary/library.c new file mode 100644 index 00000000..c6d65a06 --- /dev/null +++ b/Cesium.Sdk.Tests/TestProjects/SimpleNetStandardLibrary/library.c @@ -0,0 +1,29 @@ +#include + +void greet() +{ + puts("Hello World!\n"); +} + +int add(int a, int b) +{ + return a + b; +} + +int subtract(int a, int b) +{ + return a - b; +} + +float divide(int a, int b) +{ + if (b != 0) + return (float) a / b; + else + return 0; +} + +int multiply(int a, int b) +{ + return a * b; +} diff --git a/Cesium.Sdk.Tests/TestProjects/SimpleNetfxExe/SimpleNetfxExe.ceproj b/Cesium.Sdk.Tests/TestProjects/SimpleNetfxExe/SimpleNetfxExe.ceproj new file mode 100644 index 00000000..7a2d6ae4 --- /dev/null +++ b/Cesium.Sdk.Tests/TestProjects/SimpleNetfxExe/SimpleNetfxExe.ceproj @@ -0,0 +1,9 @@ + + + net48 + Exe + + + + + diff --git a/Cesium.Sdk.Tests/TestProjects/SimpleNetfxExe/hello.c b/Cesium.Sdk.Tests/TestProjects/SimpleNetfxExe/hello.c new file mode 100644 index 00000000..2889672e --- /dev/null +++ b/Cesium.Sdk.Tests/TestProjects/SimpleNetfxExe/hello.c @@ -0,0 +1,7 @@ +#include + +int main(int argc, char *argv[]) +{ + puts("Hello, world!"); + return 42; +} diff --git a/Cesium.Sdk.Tests/TestProjects/SimpleNetfxLibrary/SimpleNetfxLibrary.ceproj b/Cesium.Sdk.Tests/TestProjects/SimpleNetfxLibrary/SimpleNetfxLibrary.ceproj new file mode 100644 index 00000000..c41fe5bd --- /dev/null +++ b/Cesium.Sdk.Tests/TestProjects/SimpleNetfxLibrary/SimpleNetfxLibrary.ceproj @@ -0,0 +1,9 @@ + + + net48 + Library + + + + + diff --git a/Cesium.Sdk.Tests/TestProjects/SimpleNetfxLibrary/library.c b/Cesium.Sdk.Tests/TestProjects/SimpleNetfxLibrary/library.c new file mode 100644 index 00000000..c6d65a06 --- /dev/null +++ b/Cesium.Sdk.Tests/TestProjects/SimpleNetfxLibrary/library.c @@ -0,0 +1,29 @@ +#include + +void greet() +{ + puts("Hello World!\n"); +} + +int add(int a, int b) +{ + return a + b; +} + +int subtract(int a, int b) +{ + return a - b; +} + +float divide(int a, int b) +{ + if (b != 0) + return (float) a / b; + else + return 0; +} + +int multiply(int a, int b) +{ + return a * b; +} From b6ec97845a8b5f59c623c2f9b492871185ddf2a0 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Mon, 15 Apr 2024 00:28:13 +0200 Subject: [PATCH 27/45] Tests: Fix failing tests after local to global artifacts folder migration --- .../IntegrationTestContext.cs | 5 ++-- .../IntegrationTestRunner.cs | 15 ++++++------ .../Cesium.Solution.Metadata.csproj | 3 ++- Cesium.Solution.Metadata/SolutionMetadata.cs | 1 + .../SolutionMetadataAttribute.cs | 4 +++- Cesium.TestFramework/CSharpCompilationUtil.cs | 8 +++---- .../Cesium.TestFramework.csproj | 1 + Cesium.TestFramework/TestFileVerification.cs | 24 +++++++++++-------- Cesium.TestFramework/TestStructureUtil.cs | 23 ------------------ 9 files changed, 36 insertions(+), 48 deletions(-) delete mode 100644 Cesium.TestFramework/TestStructureUtil.cs diff --git a/Cesium.IntegrationTests/IntegrationTestContext.cs b/Cesium.IntegrationTests/IntegrationTestContext.cs index fc1cd10a..dc37fcf5 100644 --- a/Cesium.IntegrationTests/IntegrationTestContext.cs +++ b/Cesium.IntegrationTests/IntegrationTestContext.cs @@ -1,6 +1,7 @@ using Cesium.TestFramework; using JetBrains.Annotations; using AsyncKeyedLock; +using Cesium.Solution.Metadata; using Xunit.Abstractions; namespace Cesium.IntegrationTests; @@ -72,7 +73,7 @@ public async ValueTask DisposeAsync() private static async Task BuildRuntime(ITestOutputHelper output) { var runtimeProjectFile = Path.Combine( - TestStructureUtil.SolutionRootPath, + SolutionMetadata.SourceRoot, "Cesium.Runtime/Cesium.Runtime.csproj"); await DotNetCliHelper.BuildDotNetProject(output, BuildConfiguration, runtimeProjectFile); } @@ -80,7 +81,7 @@ private static async Task BuildRuntime(ITestOutputHelper output) private static async Task BuildCompiler(ITestOutputHelper output) { var compilerProjectFile = Path.Combine( - TestStructureUtil.SolutionRootPath, + SolutionMetadata.SourceRoot, "Cesium.Compiler/Cesium.Compiler.csproj"); await DotNetCliHelper.BuildDotNetProject(output, BuildConfiguration, compilerProjectFile); } diff --git a/Cesium.IntegrationTests/IntegrationTestRunner.cs b/Cesium.IntegrationTests/IntegrationTestRunner.cs index c012fbe3..a2ba15ca 100644 --- a/Cesium.IntegrationTests/IntegrationTestRunner.cs +++ b/Cesium.IntegrationTests/IntegrationTestRunner.cs @@ -1,3 +1,4 @@ +using Cesium.Solution.Metadata; using Cesium.TestFramework; using Xunit.Abstractions; @@ -20,7 +21,7 @@ public Task InitializeAsync() => public static IEnumerable TestCaseProvider() { - var testCaseDirectory = Path.Combine(TestStructureUtil.SolutionRootPath, "Cesium.IntegrationTests"); + var testCaseDirectory = Path.Combine(SolutionMetadata.SourceRoot, "Cesium.IntegrationTests"); var cFiles = Directory.EnumerateFileSystemEntries(testCaseDirectory, "*.c", SearchOption.AllDirectories); return cFiles .Where(file => !file.EndsWith(".ignore.c")) @@ -61,7 +62,7 @@ private async Task DoTest(string relativeSourcePath, TargetFramework targetFrame Directory.CreateDirectory(objDirPath); var sourceFilePath = Path.Combine( - TestStructureUtil.SolutionRootPath, + SolutionMetadata.SourceRoot, "Cesium.IntegrationTests", relativeSourcePath); @@ -179,7 +180,7 @@ private async Task BuildExecutableWithCesium( "run", "--no-build", "--configuration", IntegrationTestContext.BuildConfiguration, - "--project", Path.Combine(TestStructureUtil.SolutionRootPath, "Cesium.Compiler"), + "--project", Path.Combine(SolutionMetadata.SourceRoot, "Cesium.Compiler"), "--", "--nologo", sourceFilePath, @@ -192,10 +193,10 @@ private async Task BuildExecutableWithCesium( { var coreLibPath = WindowsEnvUtil.MsCorLibPath; var runtimeLibPath = Path.Combine( - TestStructureUtil.SolutionRootPath, - "Cesium.Runtime/bin", - IntegrationTestContext.BuildConfiguration, - "netstandard2.0/Cesium.Runtime.dll" + SolutionMetadata.ArtifactsRoot, + "bin/Cesium.Runtime", + $"{IntegrationTestContext.BuildConfiguration.ToLower()}_netstandard2.0", + "Cesium.Runtime.dll" ); args.AddRange(new[] { diff --git a/Cesium.Solution.Metadata/Cesium.Solution.Metadata.csproj b/Cesium.Solution.Metadata/Cesium.Solution.Metadata.csproj index b5708b55..099620c0 100644 --- a/Cesium.Solution.Metadata/Cesium.Solution.Metadata.csproj +++ b/Cesium.Solution.Metadata/Cesium.Solution.Metadata.csproj @@ -10,7 +10,8 @@ <_Parameter1>$(CesiumSourceRoot) - <_Parameter2>$(VersionPrefix) + <_Parameter2>$(ArtifactsPath) + <_Parameter3>$(VersionPrefix) diff --git a/Cesium.Solution.Metadata/SolutionMetadata.cs b/Cesium.Solution.Metadata/SolutionMetadata.cs index 9408ea60..761c29a8 100644 --- a/Cesium.Solution.Metadata/SolutionMetadata.cs +++ b/Cesium.Solution.Metadata/SolutionMetadata.cs @@ -5,6 +5,7 @@ namespace Cesium.Solution.Metadata; public static class SolutionMetadata { public static string SourceRoot => ResolvedAttribute.SourceRoot; + public static string ArtifactsRoot => ResolvedAttribute.ArtifactsRoot; public static string VersionPrefix => ResolvedAttribute.VersionPrefix; private static SolutionMetadataAttribute ResolvedAttribute => diff --git a/Cesium.Solution.Metadata/SolutionMetadataAttribute.cs b/Cesium.Solution.Metadata/SolutionMetadataAttribute.cs index baa923e0..c11ac89f 100644 --- a/Cesium.Solution.Metadata/SolutionMetadataAttribute.cs +++ b/Cesium.Solution.Metadata/SolutionMetadataAttribute.cs @@ -3,11 +3,13 @@ namespace Cesium.Solution.Metadata; public class SolutionMetadataAttribute : Attribute { public string SourceRoot { get; } + public string ArtifactsRoot { get; } public string VersionPrefix { get; } - public SolutionMetadataAttribute(string sourceRoot, string versionPrefix) + public SolutionMetadataAttribute(string sourceRoot, string artifactsRoot, string versionPrefix) { SourceRoot = sourceRoot; + ArtifactsRoot = artifactsRoot; VersionPrefix = versionPrefix; } } diff --git a/Cesium.TestFramework/CSharpCompilationUtil.cs b/Cesium.TestFramework/CSharpCompilationUtil.cs index 7a08a7f8..9c52b059 100644 --- a/Cesium.TestFramework/CSharpCompilationUtil.cs +++ b/Cesium.TestFramework/CSharpCompilationUtil.cs @@ -1,6 +1,7 @@ using System.Xml.Linq; using System.Xml.XPath; using Cesium.CodeGen; +using Cesium.Solution.Metadata; using Xunit.Abstractions; namespace Cesium.TestFramework; @@ -70,11 +71,10 @@ await ExecUtil.RunToSuccess( } public static readonly string CesiumRuntimeLibraryPath = Path.Combine( - TestStructureUtil.SolutionRootPath, - "Cesium.Runtime", + SolutionMetadata.ArtifactsRoot, "bin", - _configuration, - _cesiumRuntimeLibTargetRuntime, + "Cesium.Runtime", + $"{_configuration.ToLower()}_{_cesiumRuntimeLibTargetRuntime}", "Cesium.Runtime.dll"); private static Task CompileCSharpProject(ITestOutputHelper output, string directory, string projectName) => diff --git a/Cesium.TestFramework/Cesium.TestFramework.csproj b/Cesium.TestFramework/Cesium.TestFramework.csproj index d4c1bed7..047e53c7 100644 --- a/Cesium.TestFramework/Cesium.TestFramework.csproj +++ b/Cesium.TestFramework/Cesium.TestFramework.csproj @@ -16,6 +16,7 @@ + diff --git a/Cesium.TestFramework/TestFileVerification.cs b/Cesium.TestFramework/TestFileVerification.cs index e04dc8f8..e73bac1c 100644 --- a/Cesium.TestFramework/TestFileVerification.cs +++ b/Cesium.TestFramework/TestFileVerification.cs @@ -1,5 +1,6 @@ using System.Reflection; using System.Text; +using Cesium.Solution.Metadata; namespace Cesium.TestFramework; @@ -49,16 +50,19 @@ private static Assembly GetTestAssembly(IEnumerable types) private static string GetTestProjectSourceDirectory(Assembly assembly) { - var currentPath = Path.GetDirectoryName(assembly.Location)!; - while (!Directory.EnumerateFileSystemEntries(currentPath, "*.csproj").Any()) - { - currentPath = Path.GetDirectoryName(currentPath); - if (currentPath == null) - throw new InvalidOperationException( - $"Could not find the test project source directory for assembly \"{assembly.Location}\"."); - } - - return currentPath; + // Assuming that output assembly name (AssemblyName MSBuild property) is equal to the project name + var projectName = assembly.GetName().Name; + if (projectName is null) + throw new InvalidOperationException( + $"Name is missing for an assembly at location \"{assembly.Location}\"."); + + var fullProjectFolderPath = Path.Combine(SolutionMetadata.SourceRoot, projectName); + var fullProjectFilePath = Path.Combine(fullProjectFolderPath, $"{projectName}.csproj"); + if (!File.Exists(fullProjectFilePath)) + throw new InvalidOperationException( + $"Could not find the test project source directory for assembly \"{assembly.Location}\"."); + + return fullProjectFolderPath; } private static IReadOnlySet GetAcceptedFilePaths(string sourceDirectory) diff --git a/Cesium.TestFramework/TestStructureUtil.cs b/Cesium.TestFramework/TestStructureUtil.cs deleted file mode 100644 index 0051d8df..00000000 --- a/Cesium.TestFramework/TestStructureUtil.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Reflection; - -namespace Cesium.TestFramework; - -public static class TestStructureUtil -{ - public static readonly string SolutionRootPath = GetSolutionRoot(); - - private static string GetSolutionRoot() - { - var assemblyDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - var currentDirectory = assemblyDirectory; - while (currentDirectory != null) - { - if (File.Exists(Path.Combine(currentDirectory, "Cesium.sln"))) - return currentDirectory; - - currentDirectory = Path.GetDirectoryName(currentDirectory); - } - - throw new Exception($"Could not find the solution directory going up from directory \"{assemblyDirectory}\"."); - } -} From 32b6cf44b90a62e65803d26e7fab31b50b5e8549 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Mon, 15 Apr 2024 00:28:56 +0200 Subject: [PATCH 28/45] SDK: Include full path information for artifacts coming from build --- Cesium.Sdk.Tests/CesiumCompileTests.cs | 16 ++++++++-------- .../{MSBuildCli.cs => MSBuildCLI.cs} | 2 +- Cesium.Sdk.Tests/SdkTestBase.cs | 16 ++++++++++------ 3 files changed, 19 insertions(+), 15 deletions(-) rename Cesium.Sdk.Tests/{MSBuildCli.cs => MSBuildCLI.cs} (97%) diff --git a/Cesium.Sdk.Tests/CesiumCompileTests.cs b/Cesium.Sdk.Tests/CesiumCompileTests.cs index 4a89d6c1..e8a66b85 100644 --- a/Cesium.Sdk.Tests/CesiumCompileTests.cs +++ b/Cesium.Sdk.Tests/CesiumCompileTests.cs @@ -28,8 +28,8 @@ public void CesiumCompile_Core_Exe_ShouldSucceed(string projectName) var result = ExecuteTargets(projectName, "Restore", "Build"); Assert.True(result.ExitCode == 0); - AssertEx.Includes(expectedObjArtifacts, result.IntermediateArtifacts); - AssertEx.Includes(expectedBinArtifacts, result.OutputArtifacts); + AssertEx.Includes(expectedObjArtifacts, result.IntermediateArtifacts.Select(a => a.FileName).ToList()); + AssertEx.Includes(expectedBinArtifacts, result.OutputArtifacts.Select(a => a.FileName).ToList()); } [Theory] @@ -51,8 +51,8 @@ public void CesiumCompile_NetFx_Exe_ShouldSucceed(string projectName) var result = ExecuteTargets(projectName, "Restore", "Build"); Assert.True(result.ExitCode == 0); - AssertEx.Includes(expectedObjArtifacts, result.IntermediateArtifacts); - AssertEx.Includes(expectedBinArtifacts, result.OutputArtifacts); + AssertEx.Includes(expectedObjArtifacts, result.IntermediateArtifacts.Select(a => a.FileName).ToList()); + AssertEx.Includes(expectedBinArtifacts, result.OutputArtifacts.Select(a => a.FileName).ToList()); } [Theory] @@ -74,8 +74,8 @@ public void CesiumCompile_Core_Library_ShouldSucceed(string projectName) var result = ExecuteTargets(projectName, "Restore", "Build"); Assert.True(result.ExitCode == 0); - AssertEx.Includes(expectedObjArtifacts, result.IntermediateArtifacts); - AssertEx.Includes(expectedBinArtifacts, result.OutputArtifacts); + AssertEx.Includes(expectedObjArtifacts, result.IntermediateArtifacts.Select(a => a.FileName).ToList()); + AssertEx.Includes(expectedBinArtifacts, result.OutputArtifacts.Select(a => a.FileName).ToList()); } [Theory] @@ -96,7 +96,7 @@ public void CesiumCompile_NetFxLibrary_ShouldSucceed(string projectName) var result = ExecuteTargets(projectName, "Restore", "Build"); Assert.True(result.ExitCode == 0); - AssertEx.Includes(expectedObjArtifacts, result.IntermediateArtifacts); - AssertEx.Includes(expectedBinArtifacts, result.OutputArtifacts); + AssertEx.Includes(expectedObjArtifacts, result.IntermediateArtifacts.Select(a => a.FileName).ToList()); + AssertEx.Includes(expectedBinArtifacts, result.OutputArtifacts.Select(a => a.FileName).ToList()); } } diff --git a/Cesium.Sdk.Tests/MSBuildCli.cs b/Cesium.Sdk.Tests/MSBuildCLI.cs similarity index 97% rename from Cesium.Sdk.Tests/MSBuildCli.cs rename to Cesium.Sdk.Tests/MSBuildCLI.cs index 6de5291e..831aca90 100644 --- a/Cesium.Sdk.Tests/MSBuildCli.cs +++ b/Cesium.Sdk.Tests/MSBuildCLI.cs @@ -3,7 +3,7 @@ namespace Cesium.Sdk.Tests; -public static class MSBuildCli +public static class MSBuildCLI { public static string EvaluateProperty(string projectPath, string propertyName) { diff --git a/Cesium.Sdk.Tests/SdkTestBase.cs b/Cesium.Sdk.Tests/SdkTestBase.cs index 84ec0a8a..35ec1f5d 100644 --- a/Cesium.Sdk.Tests/SdkTestBase.cs +++ b/Cesium.Sdk.Tests/SdkTestBase.cs @@ -89,7 +89,7 @@ protected BuildResult ExecuteTargets(string projectName, params string[] targets ? "Build succeeded" : $"Build failed with exit code {process.ExitCode}"); - var properties = MSBuildCli.EvaluateProperties(testProjectFile, objFolderPropertyName, binFolderPropertyName); + var properties = MSBuildCLI.EvaluateProperties(testProjectFile, objFolderPropertyName, binFolderPropertyName); _testOutputHelper.WriteLine($"Properties request result: {JsonSerializer.Serialize(properties, new JsonSerializerOptions { WriteIndented = false })}"); var binFolder = Path.Combine(testProjectFolder, properties[binFolderPropertyName]); @@ -102,12 +102,12 @@ protected BuildResult ExecuteTargets(string projectName, params string[] targets _testOutputHelper.WriteLine($"Build result: {JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true })}"); return result; - IReadOnlyCollection CollectArtifacts(string folder) => + IReadOnlyCollection CollectArtifacts(string folder) => Directory.Exists(folder) ? Directory.GetFiles(folder, "*.*", SearchOption.AllDirectories) - .Select(path => Path.GetRelativePath(folder, path)) + .Select(path => new BuildArtifact(Path.GetRelativePath(folder, path), path)) .ToList() - : Array.Empty(); + : Array.Empty(); } private static void EmitNuGetConfig(string configFilePath, string packageSourcePath) @@ -154,8 +154,12 @@ private static void CopyDirectoryRecursive(string source, string target) protected record BuildResult( int ExitCode, - IReadOnlyCollection OutputArtifacts, - IReadOnlyCollection IntermediateArtifacts); + IReadOnlyCollection OutputArtifacts, + IReadOnlyCollection IntermediateArtifacts); + + protected record BuildArtifact( + string FileName, + string FullPath); private void ClearOutput() { From c3310f8f8b8e98951842ac11d59aa6fb20f63514 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Mon, 15 Apr 2024 02:13:09 +0200 Subject: [PATCH 29/45] CI: Include SDK tests into build workflow --- .github/workflows/perform-common-steps/action.yml | 4 ++++ .github/workflows/run-build-and-unit-tests.yml | 10 +++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/perform-common-steps/action.yml b/.github/workflows/perform-common-steps/action.yml index fdf54aa2..3f36ba61 100644 --- a/.github/workflows/perform-common-steps/action.yml +++ b/.github/workflows/perform-common-steps/action.yml @@ -14,6 +14,10 @@ runs: path: ${{ env.NUGET_PACKAGES }} key: ${{ runner.os }}.nuget.${{ hashFiles('**/*.csproj') }} + - name: 🛠️ Restore local .NET tools 🛠️ + shell: bash + run: dotnet tool restore + - name: 🔄 Restore Nuget Packages 🔄 shell: bash run: dotnet restore diff --git a/.github/workflows/run-build-and-unit-tests.yml b/.github/workflows/run-build-and-unit-tests.yml index 840fbba7..90c39bd7 100644 --- a/.github/workflows/run-build-and-unit-tests.yml +++ b/.github/workflows/run-build-and-unit-tests.yml @@ -34,6 +34,14 @@ jobs: shell: bash run: dotnet build + - name: 🚚 Publish Compiler Packs 🚚 + shell: bash + run: dotnet nuke PublishCompilerPacks + + - name: 📦 Pack Compiler Packs 📦 + shell: bash + run: dotnet nuke PackCompilerPacks + - name: ✅ Run Unit Tests ✅ shell: bash - run: dotnet test + run: dotnet nuke TestAll --configuration Debug From e7aeacd5214f01d1283b82d28e647c15cf4570af Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Mon, 15 Apr 2024 02:13:52 +0200 Subject: [PATCH 30/45] CI: Fix different issues on CI because of SDK implementation --- .../{ => Framework}/MSBuildCLI.cs | 0 Cesium.Sdk.Tests/SdkTestBase.cs | 20 +++++++++++++++---- build/Build.Sdk.cs | 1 - global.json | 6 +++--- nuget.config | 1 - 5 files changed, 19 insertions(+), 9 deletions(-) rename Cesium.Sdk.Tests/{ => Framework}/MSBuildCLI.cs (100%) diff --git a/Cesium.Sdk.Tests/MSBuildCLI.cs b/Cesium.Sdk.Tests/Framework/MSBuildCLI.cs similarity index 100% rename from Cesium.Sdk.Tests/MSBuildCLI.cs rename to Cesium.Sdk.Tests/Framework/MSBuildCLI.cs diff --git a/Cesium.Sdk.Tests/SdkTestBase.cs b/Cesium.Sdk.Tests/SdkTestBase.cs index 35ec1f5d..f3669540 100644 --- a/Cesium.Sdk.Tests/SdkTestBase.cs +++ b/Cesium.Sdk.Tests/SdkTestBase.cs @@ -1,5 +1,6 @@ using System.Diagnostics; using System.Reflection; +using System.Runtime.InteropServices; using System.Text.Json; using Cesium.Solution.Metadata; using Xunit.Abstractions; @@ -92,8 +93,8 @@ protected BuildResult ExecuteTargets(string projectName, params string[] targets var properties = MSBuildCLI.EvaluateProperties(testProjectFile, objFolderPropertyName, binFolderPropertyName); _testOutputHelper.WriteLine($"Properties request result: {JsonSerializer.Serialize(properties, new JsonSerializerOptions { WriteIndented = false })}"); - var binFolder = Path.Combine(testProjectFolder, properties[binFolderPropertyName]); - var objFolder = Path.Combine(testProjectFolder, properties[objFolderPropertyName]); + var binFolder = NormalizePath(Path.GetFullPath(properties[binFolderPropertyName], testProjectFolder)); + var objFolder = NormalizePath(Path.GetFullPath(properties[objFolderPropertyName], testProjectFolder)); var binArtifacts = CollectArtifacts(binFolder); var objArtifacts = CollectArtifacts(objFolder); @@ -102,12 +103,15 @@ protected BuildResult ExecuteTargets(string projectName, params string[] targets _testOutputHelper.WriteLine($"Build result: {JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true })}"); return result; - IReadOnlyCollection CollectArtifacts(string folder) => - Directory.Exists(folder) + IReadOnlyCollection CollectArtifacts(string folder) + { + _testOutputHelper.WriteLine($"Collecting artifacts from '{folder}' folder"); + return Directory.Exists(folder) ? Directory.GetFiles(folder, "*.*", SearchOption.AllDirectories) .Select(path => new BuildArtifact(Path.GetRelativePath(folder, path), path)) .ToList() : Array.Empty(); + } } private static void EmitNuGetConfig(string configFilePath, string packageSourcePath) @@ -152,6 +156,14 @@ private static void CopyDirectoryRecursive(string source, string target) } } + private static string NormalizePath(string path) + { + var normalizedPath = new Uri(path).LocalPath; + return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + ? normalizedPath + : normalizedPath.Replace('\\', '/'); + } + protected record BuildResult( int ExitCode, IReadOnlyCollection OutputArtifacts, diff --git a/build/Build.Sdk.cs b/build/Build.Sdk.cs index c0de8de3..cdd93615 100644 --- a/build/Build.Sdk.cs +++ b/build/Build.Sdk.cs @@ -34,7 +34,6 @@ public partial class Build void PublishCompiler(string runtimeId) { - Log.Information(SkipCaches+""); if (!SkipCaches && !NeedPublishCompilerPack(compilerProject, runtimeId)) { Log.Information($"Skipping {runtimeId} because it was already published. Use '--skip-caches true' to re-publish."); diff --git a/global.json b/global.json index c65c9eac..36394634 100644 --- a/global.json +++ b/global.json @@ -1,7 +1,7 @@ { "sdk": { "version": "8.0.0", - "rollForward": "latestMinor", - "allowPrerelease": true + "rollForward": "latestFeature", + "allowPrerelease": false } -} \ No newline at end of file +} diff --git a/nuget.config b/nuget.config index 6fd055e8..32d08817 100644 --- a/nuget.config +++ b/nuget.config @@ -4,7 +4,6 @@ - From b609346feb68f3abc35e9b461410559262c4808e Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Wed, 17 Apr 2024 20:50:12 +0200 Subject: [PATCH 31/45] SDK: Refactor compiler bundle producing, add docs - Add docs about MSBuild SDK - Add info about SDK tests - Use NUKE in tests docs - Rename compiler pack -> compiler bundle - Introduce CI-friendly single pack and publish compiler bundle targets --- .../workflows/perform-common-steps/action.yml | 2 +- .../workflows/run-build-and-unit-tests.yml | 12 +-- .nuke/build.schema.json | 14 ++- Cesium.Sdk/Sdk/Sdk.props | 2 +- Cesium.sln | 1 + README.md | 1 + build/Build.Sdk.cs | 99 ++++++++++--------- build/Build.Tests.cs | 2 +- build/Build.cs | 8 +- docs/msbuild-sdk.md | 64 ++++++++++++ docs/tests.md | 49 +++++++-- 11 files changed, 184 insertions(+), 70 deletions(-) create mode 100644 docs/msbuild-sdk.md diff --git a/.github/workflows/perform-common-steps/action.yml b/.github/workflows/perform-common-steps/action.yml index 3f36ba61..696bac90 100644 --- a/.github/workflows/perform-common-steps/action.yml +++ b/.github/workflows/perform-common-steps/action.yml @@ -20,4 +20,4 @@ runs: - name: 🔄 Restore Nuget Packages 🔄 shell: bash - run: dotnet restore + run: dotnet nuke RestoreAll diff --git a/.github/workflows/run-build-and-unit-tests.yml b/.github/workflows/run-build-and-unit-tests.yml index 90c39bd7..d4cc8566 100644 --- a/.github/workflows/run-build-and-unit-tests.yml +++ b/.github/workflows/run-build-and-unit-tests.yml @@ -32,16 +32,16 @@ jobs: - name: 🛠 Build Solution 🛠 shell: bash - run: dotnet build + run: dotnet nuke CompileAll - - name: 🚚 Publish Compiler Packs 🚚 + - name: 🚚 Publish Compiler Bundle 🚚 shell: bash - run: dotnet nuke PublishCompilerPacks + run: dotnet nuke PublishCompilerBundle - - name: 📦 Pack Compiler Packs 📦 + - name: 📦 Pack Compiler Bundle 📦 shell: bash - run: dotnet nuke PackCompilerPacks + run: dotnet nuke PackCompilerBundle - name: ✅ Run Unit Tests ✅ shell: bash - run: dotnet nuke TestAll --configuration Debug + run: dotnet nuke TestAll diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index e8ff42e1..ffb7d189 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -8,7 +8,7 @@ "properties": { "Configuration": { "type": "string", - "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)", + "description": "Configuration to build - Default is 'Debug' or 'Release'", "enum": [ "Debug", "Release" @@ -83,9 +83,11 @@ "Clean", "CompileAll", "ForceClear", - "PackCompilerPacks", + "PackAllCompilerBundles", + "PackCompilerBundle", "PackSdk", - "PublishCompilerPacks", + "PublishAllCompilerBundles", + "PublishCompilerBundle", "RestoreAll", "TestAll", "TestCodeGen", @@ -114,9 +116,11 @@ "Clean", "CompileAll", "ForceClear", - "PackCompilerPacks", + "PackAllCompilerBundles", + "PackCompilerBundle", "PackSdk", - "PublishCompilerPacks", + "PublishAllCompilerBundles", + "PublishCompilerBundle", "RestoreAll", "TestAll", "TestCodeGen", diff --git a/Cesium.Sdk/Sdk/Sdk.props b/Cesium.Sdk/Sdk/Sdk.props index 0b681ff1..e8e643e6 100644 --- a/Cesium.Sdk/Sdk/Sdk.props +++ b/Cesium.Sdk/Sdk/Sdk.props @@ -9,7 +9,7 @@ false false - Cesium.Compiler.Pack.$(NETCoreSdkRuntimeIdentifier) + Cesium.Compiler.Bundle.$(NETCoreSdkRuntimeIdentifier) 0.0.1 false diff --git a/Cesium.sln b/Cesium.sln index ae5b74e1..25e1872e 100644 --- a/Cesium.sln +++ b/Cesium.sln @@ -61,6 +61,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{986C6A13-2 docs\tests.md = docs\tests.md docs\type-system.md = docs\type-system.md docs\design-notes.md = docs\design-notes.md + docs\msbuild-sdk.md = docs\msbuild-sdk.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Cesium.Preprocessor", "Cesium.Preprocessor\Cesium.Preprocessor.csproj", "{0CDF730D-2A2A-437F-B27F-2BB04770C709}" diff --git a/README.md b/README.md index deb212e4..eda2cbc2 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ Documentation - [Contributor Guide][docs.contributing] - [Cesium Tests][docs.tests] - [Cesium Type System][docs.type-system] +- [Cesium SDK][docs.msbuild-sdk] - [Architecture Sets][docs.architecture-sets] - [CLI-Related Language Extensions][docs.language-extensions] - [Built-in Functions][docs.builtins] diff --git a/build/Build.Sdk.cs b/build/Build.Sdk.cs index cdd93615..c158a46b 100644 --- a/build/Build.Sdk.cs +++ b/build/Build.Sdk.cs @@ -11,9 +11,9 @@ public partial class Build { - const string _compilerPackPackagePrefix = "Cesium.Compiler.Pack"; + const string _compilerPackPackagePrefix = "Cesium.Compiler.Bundle"; - Target PublishCompilerPacks => _ => _ + Target PublishAllCompilerBundles => _ => _ .Executes(() => { var compilerProject = Solution.Cesium_Compiler.GetMSBuildProject(); @@ -22,39 +22,18 @@ public partial class Build Log.Information( $"Runtime identifiers defined in {Solution.Cesium_Compiler.Name}: {string.Join(", ", runtimeIds)}"); - if (!string.IsNullOrEmpty(RuntimeId)) - { - Log.Information($"Executing only {RuntimeId} because it was specified explicitly."); - PublishCompiler(RuntimeId); - return; - } - foreach (var runtimeId in runtimeIds) PublishCompiler(runtimeId); + }); - void PublishCompiler(string runtimeId) - { - if (!SkipCaches && !NeedPublishCompilerPack(compilerProject, runtimeId)) - { - Log.Information($"Skipping {runtimeId} because it was already published. Use '--skip-caches true' to re-publish."); - return; - } - - Log.Information($"Publishing for {runtimeId}, AOT {(PublishAot ? "enabled" : "disabled")}..."); - DotNetPublish(o => o - .SetConfiguration(Configuration) - .SetProject(compilerProject.ProjectFileLocation.File) - .SetRuntime(runtimeId) - .SetSelfContained(true) - .SetPublishTrimmed(PublishAot) - .SetPublishSingleFile(PublishAot) - .SetProperty("PublishAot", PublishAot) - .SetOutput(GetCompilerRuntimePublishFolder(compilerProject, runtimeId))); - } + Target PublishCompilerBundle => _ => _ + .Executes(() => + { + PublishCompiler(EffectiveRuntimeId); }); - Target PackCompilerPacks => _ => _ - .DependsOn(PublishCompilerPacks) + Target PackAllCompilerBundles => _ => _ + .DependsOn(PublishAllCompilerBundles) .Executes(() => { var compilerProject = Solution.Cesium_Compiler.GetMSBuildProject(); @@ -63,27 +42,15 @@ void PublishCompiler(string runtimeId) Log.Information( $"Runtime identifiers defined in {Solution.Cesium_Compiler.Name}: {string.Join(", ", runtimeIds)}"); - if (!string.IsNullOrEmpty(RuntimeId)) - { - Log.Information($"Executing only {RuntimeId} because it was specified explicitly."); - PackCompiler(RuntimeId); - return; - } - foreach (var runtimeId in runtimeIds) PackCompiler(runtimeId); + }); - void PackCompiler(string runtimeId) - { - if (!SkipCaches && !NeedPackageCompilerPack(compilerProject, runtimeId)) - { - Log.Information($"Skipping {runtimeId} because it was already packed. Use '--skip-caches true' to re-pack."); - return; - } - - Log.Information($"Packing compiler for {runtimeId}..."); - EmitCompilerPack(runtimeId, compilerProject); - } + Target PackCompilerBundle => _ => _ + .DependsOn(PublishCompilerBundle) + .Executes(() => + { + PackCompiler(EffectiveRuntimeId); }); Target PackSdk => _ => _ @@ -145,6 +112,42 @@ IEnumerable GetPhysicalFiles(string publishDirectory, IEnumerable< } } + void PublishCompiler(string runtimeId) + { + var compilerProject = Solution.Cesium_Compiler.GetMSBuildProject(); + + if (!SkipCaches && !NeedPublishCompilerPack(compilerProject, runtimeId)) + { + Log.Information($"Skipping {runtimeId} because it was already published. Use '--skip-caches true' to re-publish."); + return; + } + + Log.Information($"Publishing for {runtimeId}, AOT {(PublishAot ? "enabled" : "disabled")}..."); + DotNetPublish(o => o + .SetConfiguration(Configuration) + .SetProject(compilerProject.ProjectFileLocation.File) + .SetRuntime(runtimeId) + .SetSelfContained(true) + .SetPublishTrimmed(PublishAot) + .SetPublishSingleFile(PublishAot) + .SetProperty("PublishAot", PublishAot) + .SetOutput(GetCompilerRuntimePublishFolder(compilerProject, runtimeId))); + } + + void PackCompiler(string runtimeId) + { + var compilerProject = Solution.Cesium_Compiler.GetMSBuildProject(); + + if (!SkipCaches && !NeedPackageCompilerPack(compilerProject, runtimeId)) + { + Log.Information($"Skipping {runtimeId} because it was already packed. Use '--skip-caches true' to re-pack."); + return; + } + + Log.Information($"Packing compiler for {runtimeId}..."); + EmitCompilerPack(runtimeId, compilerProject); + } + string GetCompilerRuntimePublishFolder(Project compilerProject, string runtimeId) => Path.Combine( compilerProject.GetProperty("ArtifactsPath").EvaluatedValue, diff --git a/build/Build.Tests.cs b/build/Build.Tests.cs index 0a8a5747..b31bde9a 100644 --- a/build/Build.Tests.cs +++ b/build/Build.Tests.cs @@ -21,7 +21,7 @@ partial class Build .Executes(() => ExecuteTests(Solution.Cesium_Runtime_Tests)); Target TestSdk => _ => _ - .DependsOn(PackCompilerPacks) + .DependsOn(PackCompilerBundle) .DependsOn(PackSdk) .Executes(() => ExecuteTests(Solution.Cesium_Sdk_Tests)); diff --git a/build/Build.cs b/build/Build.cs index 571627a2..303ba6f8 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -12,8 +12,8 @@ public static int Main() return Execute(x => x.CompileAll); } - [Parameter("Configuration to build - Default is 'Debug' (local) or 'Release' (server)")] - readonly Configuration Configuration = IsLocalBuild ? Configuration.Debug : Configuration.Release; + [Parameter("Configuration to build - Default is 'Debug' or 'Release'")] + readonly Configuration Configuration = Configuration.Debug; [Parameter("If set to true, ignores all cached build results. Default: false")] readonly bool SkipCaches = false; @@ -24,6 +24,10 @@ public static int Main() [Parameter("If set, only executes targets for a specified runtime identifier. Provided RID must be included in property of Cesium.Compiler project.")] readonly string RuntimeId = string.Empty; + string EffectiveRuntimeId => !string.IsNullOrEmpty(RuntimeId) + ? RuntimeId + : Solution.Cesium_Compiler.GetProperty("DefaultAppHostRuntimeIdentifier") ?? string.Empty; + [Parameter("If set to true, publishes compiler packs in AOT mode.")] readonly bool PublishAot = false; diff --git a/docs/msbuild-sdk.md b/docs/msbuild-sdk.md new file mode 100644 index 00000000..f5fce3bb --- /dev/null +++ b/docs/msbuild-sdk.md @@ -0,0 +1,64 @@ +Cesium MSBuild Project SDK +-------------------------- + +Cesium provides it's own project SDK that could be used to simplify building of Cesium programs and libraries. + +Cesium MSBuild SDK inherits default behavior from a `Microsoft.NET.Sdk` SDK and tries to integrate with it the same way as C# does. + +Cesium MSBuild SDK only supports SDK-style projects. + +> Note: Some of the common MSBuild properties and items those are not stated in this document could be also used in Cesium project files. Not all of them are tested so something may not work as expected. + +### Source files +Source files are defined with `` items, very similar to other .NET languages: +```xml + + + + +``` +> Note: In the current SDK implementation, compile units should be defined explicitly, in opposite to C# `` items. + +### References + +#### Packages +Not supported yet. + +#### Projects +Not supported yet. + +#### Assemblies +Not supported yet. + +### Preprocessor directives +`` property is directly mapped to a list of preprocessor items. So, you could define such constants in .csproj: +```xml + + $(DefineConstants);FOO;BAR + +``` + +And then use it in your .c code: +```c++ +#ifdef FOO +int foo() { return 0; } +#endif + +#ifdef BAR +int bar() { return 1; } +#endif +``` + +### Output files +Output assembly and additional artifacts will be placed in `bin` folder. Depending on the target framework, output type and a platform you're compiling on, `.runtimeconfig.json` and `.deps.json` files will also be generated. + +### Properties +- `SkipCesiumCompilerInstallation`: if set to `true`, doesn't automatically install a compiler bundle package. In that case it should be explicitly provided by `CesiumCompilerPackageName` and `CesiumCompilerPackageVersion` properties. Default: `false` +- `SkipCesiumRuntimeInstallation`: if set to `true`, doesn't automatically install a `Cesium.Runtime` package. In that case it should be explicitly installed. Default: `false` +- `CesiumCompilerPackageName`: an optional platform-specific compiler bundle package name. Should be specified if `SkipCesiumCompilerInstallation` set to `true`. Default: `Cesium.Compiler.Pack.{RID}` +- `CesiumCompilerPackageName`: an optional platform-specific compiler bundle package version. Should be specified if `SkipCesiumCompilerInstallation` set to `true`. Default: Cesium SDK version +- `CesiumCompilerPath`: an optional path to compiler executable. Use this property to specify a path to the compiler not coming from a compiler package. +- `CesiumCoreLibAssemblyPath`: an optional path to .NET runtime assembly: `System.Runtime` or `mscorlib`, depending on the target framework. + +### Items +- `Compile`: a C source file to be included into compiler execution command diff --git a/docs/tests.md b/docs/tests.md index f27e486d..30781b70 100644 --- a/docs/tests.md +++ b/docs/tests.md @@ -3,16 +3,32 @@ Cesium Tests Being a compiler, Cesium requires a complicated test suite checking every feature. -There are two kinds of tests in Cesium: unit tests (directly calling various internal APIs in the compiler) and integration tests (interacting with the compiler executable and comparing the resulting programs' behavior with programs compiled by other compilers). +There are three kinds of tests in Cesium: unit tests (directly calling various internal APIs in the compiler), integration tests (interacting with the compiler executable and comparing the resulting programs' behavior with programs compiled by other compilers) and SDK tests (testing integration with MSBuild via MSBuild project SDK). -Unit Tests ----------- -Unit tests in Cesium are normal .NET tests, so they are runnable by the following shell command: -s +Running Tests +------------- +To run all tests from solution, make sure to restore locally installed tools: +```console +dotnet tool restore +``` + +Then, run `TestAll` target using NUKE: ```console -$ dotnet test +dotnet nuke TestAll ``` +You could also execute test from specific corresponding test projects: +- `dotnet nuke TestParser` +- `dotnet nuke TestCompiler` +- `dotnet nuke TestCodeGen` +- `dotnet nuke TestRuntime` +- `dotnet nuke TestIntegration` +- `dotnet nuke TestSdk` + +Unit Tests +---------- +Unit tests in Cesium are normal .NET tests. + There are two kinds of unit tests: a few of "the real" unit tests (e.g. `Cesium.Parser.Tests.LexerTests.IdentifierTests`) and a set of [characterization tests][wiki.characterization-tests]. The real unit tests verify certain facts using assertions of the Xunit testing framework, but their usage in the compiler is small. The characterization tests, on the other hand, invoke parts of the compiler on various sources, and then dump the results (e.g. a full parse tree of a code fragment, or a whole compiled assembly). The characterization tests comprise the biggest part of the compiler test suite, and it helps us to thoroughly verify the most aspects of the compiler behavior. @@ -53,3 +69,24 @@ There are two categories of integration tests: .NET interop tests and compiler v [wiki.characterization-tests]: https://en.wikipedia.org/wiki/Characterization_test +SDK Tests +--------- +#### How SDK tests work +SDK tests check correctness of integration with MSBuild. They are focused on build output, including artifacts existence asserting. + +SDK tests (and Cesium Project SDK itself) require compiler bundle to be built and packed. A compiler bundle is a special platform-specific NuGet package containing a published compiler executable with dependencies. It is not intended to be used as a runtime dependency and only used while building project. + +Compiler packing is done by 2 NUKE targets: +- `PublishCompilerBundle`: a target that make platform-specific `dotnet publish` of compiler bundle to corresponding artifacts' folder. +- `PackCompilerBundle`: a target that wraps a published compiler bundle into a NuGet package which is then used by SDK to deliver compiler to user's project + +Both targets are called automatically when `TestSdk` target is invoked. + +SDK itself should also be built to be used in test projects. This is done by dependent target `PackSdk` which produces `Cesium.Sdk` NuGet package, suitable as Project SDK. + +Having all necessary dependencies, SDK tests are invoking `dotnet build` CLI with test projects, representing different layouts and configurations. Test result is determined by MSBuild output and artifacts presence. + +#### Adding new tests +Adding new tests is quite straightforward. +1. Add a test project if needed to the `TestProjects` directory. All items from that folder will be automatically included into temporary test execution directory. +2. Write a test with the new test project in use. Look for the examples at `CesiumCompileTests.cs`. From 40cb7f6772ca50ad676365d88281e15f841660d6 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Thu, 18 Apr 2024 15:26:05 +0200 Subject: [PATCH 32/45] SDK: Fix missing reference about MSBuild SDK docs in README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index eda2cbc2..ebbb3369 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,7 @@ If you're interested in certain project areas, check the per-area issue labels: [docs.builtins]: docs/builtins.md [docs.contributing]: CONTRIBUTING.md [docs.design-notes]: docs/design-notes.md +[docs.msbuild-sdk]: docs/msbuild-sdk.md [docs.exceptions]: docs/exceptions.md [docs.language-extensions]: docs/language-extensions.md [docs.license]: LICENSE.md From 2f5c859447c27387c1d6970424434abc690c8dd1 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Thu, 18 Apr 2024 16:08:51 +0200 Subject: [PATCH 33/45] SDK: Fix line endings in NUKE build.sh script --- .gitattributes | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..73e5a20a --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +build.sh eol=lf From af3c690f1cf83fe5cab9a180bbbd690a7057b31f Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Fri, 19 Apr 2024 22:18:06 +0200 Subject: [PATCH 34/45] SDK: Move SDK helpers into TestFramework --- Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj | 2 +- Cesium.Sdk.Tests/CesiumCompileTests.cs | 34 +++++++++---------- Cesium.Sdk.Tests/Framework/MSBuildCLI.cs | 32 ----------------- Cesium.Sdk.Tests/SdkTestBase.cs | 7 ++-- .../AssertCollection.cs | 6 ++-- Cesium.TestFramework/DotNetCliHelper.cs | 24 +++++++++++++ .../IncludesAssertFailedException.cs | 2 +- 7 files changed, 50 insertions(+), 57 deletions(-) delete mode 100644 Cesium.Sdk.Tests/Framework/MSBuildCLI.cs rename Cesium.Sdk.Tests/Framework/AssertEx.cs => Cesium.TestFramework/AssertCollection.cs (76%) rename {Cesium.Sdk.Tests/Framework => Cesium.TestFramework/Exceptions}/IncludesAssertFailedException.cs (84%) diff --git a/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj b/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj index eb98fcf5..2d715572 100644 --- a/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj +++ b/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj @@ -31,7 +31,7 @@ - + diff --git a/Cesium.Sdk.Tests/CesiumCompileTests.cs b/Cesium.Sdk.Tests/CesiumCompileTests.cs index e8a66b85..11bf5c45 100644 --- a/Cesium.Sdk.Tests/CesiumCompileTests.cs +++ b/Cesium.Sdk.Tests/CesiumCompileTests.cs @@ -1,5 +1,5 @@ using System.Runtime.InteropServices; -using Cesium.Sdk.Tests.Framework; +using Cesium.TestFramework; using Xunit.Abstractions; namespace Cesium.Sdk.Tests; @@ -8,7 +8,7 @@ public class CesiumCompileTests(ITestOutputHelper testOutputHelper) : SdkTestBas { [Theory] [InlineData("SimpleCoreExe")] - public void CesiumCompile_Core_Exe_ShouldSucceed(string projectName) + public async Task CesiumCompile_Core_Exe_ShouldSucceed(string projectName) { HashSet expectedObjArtifacts = [ @@ -25,16 +25,16 @@ public void CesiumCompile_Core_Exe_ShouldSucceed(string projectName) $"{projectName}.deps.json", ]; - var result = ExecuteTargets(projectName, "Restore", "Build"); + var result = await ExecuteTargets(projectName, "Restore", "Build"); Assert.True(result.ExitCode == 0); - AssertEx.Includes(expectedObjArtifacts, result.IntermediateArtifacts.Select(a => a.FileName).ToList()); - AssertEx.Includes(expectedBinArtifacts, result.OutputArtifacts.Select(a => a.FileName).ToList()); + AssertCollection.Includes(expectedObjArtifacts, result.IntermediateArtifacts.Select(a => a.FileName).ToList()); + AssertCollection.Includes(expectedBinArtifacts, result.OutputArtifacts.Select(a => a.FileName).ToList()); } [Theory] [InlineData("SimpleNetfxExe")] - public void CesiumCompile_NetFx_Exe_ShouldSucceed(string projectName) + public async Task CesiumCompile_NetFx_Exe_ShouldSucceed(string projectName) { HashSet expectedObjArtifacts = [ @@ -48,17 +48,17 @@ public void CesiumCompile_NetFx_Exe_ShouldSucceed(string projectName) $"{projectName}.runtimeconfig.json" ]; - var result = ExecuteTargets(projectName, "Restore", "Build"); + var result = await ExecuteTargets(projectName, "Restore", "Build"); Assert.True(result.ExitCode == 0); - AssertEx.Includes(expectedObjArtifacts, result.IntermediateArtifacts.Select(a => a.FileName).ToList()); - AssertEx.Includes(expectedBinArtifacts, result.OutputArtifacts.Select(a => a.FileName).ToList()); + AssertCollection.Includes(expectedObjArtifacts, result.IntermediateArtifacts.Select(a => a.FileName).ToList()); + AssertCollection.Includes(expectedBinArtifacts, result.OutputArtifacts.Select(a => a.FileName).ToList()); } [Theory] [InlineData("SimpleCoreLibrary")] [InlineData("SimpleNetStandardLibrary")] - public void CesiumCompile_Core_Library_ShouldSucceed(string projectName) + public async Task CesiumCompile_Core_Library_ShouldSucceed(string projectName) { string[] expectedObjArtifacts = [ @@ -71,16 +71,16 @@ public void CesiumCompile_Core_Library_ShouldSucceed(string projectName) $"{projectName}.deps.json", ]; - var result = ExecuteTargets(projectName, "Restore", "Build"); + var result = await ExecuteTargets(projectName, "Restore", "Build"); Assert.True(result.ExitCode == 0); - AssertEx.Includes(expectedObjArtifacts, result.IntermediateArtifacts.Select(a => a.FileName).ToList()); - AssertEx.Includes(expectedBinArtifacts, result.OutputArtifacts.Select(a => a.FileName).ToList()); + AssertCollection.Includes(expectedObjArtifacts, result.IntermediateArtifacts.Select(a => a.FileName).ToList()); + AssertCollection.Includes(expectedBinArtifacts, result.OutputArtifacts.Select(a => a.FileName).ToList()); } [Theory] [InlineData("SimpleNetfxLibrary")] - public void CesiumCompile_NetFxLibrary_ShouldSucceed(string projectName) + public async Task CesiumCompile_NetFxLibrary_ShouldSucceed(string projectName) { HashSet expectedObjArtifacts = [ @@ -93,10 +93,10 @@ public void CesiumCompile_NetFxLibrary_ShouldSucceed(string projectName) "Cesium.Runtime.dll", ]; - var result = ExecuteTargets(projectName, "Restore", "Build"); + var result = await ExecuteTargets(projectName, "Restore", "Build"); Assert.True(result.ExitCode == 0); - AssertEx.Includes(expectedObjArtifacts, result.IntermediateArtifacts.Select(a => a.FileName).ToList()); - AssertEx.Includes(expectedBinArtifacts, result.OutputArtifacts.Select(a => a.FileName).ToList()); + AssertCollection.Includes(expectedObjArtifacts, result.IntermediateArtifacts.Select(a => a.FileName).ToList()); + AssertCollection.Includes(expectedBinArtifacts, result.OutputArtifacts.Select(a => a.FileName).ToList()); } } diff --git a/Cesium.Sdk.Tests/Framework/MSBuildCLI.cs b/Cesium.Sdk.Tests/Framework/MSBuildCLI.cs deleted file mode 100644 index 831aca90..00000000 --- a/Cesium.Sdk.Tests/Framework/MSBuildCLI.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Text.Json; -using Medallion.Shell; - -namespace Cesium.Sdk.Tests; - -public static class MSBuildCLI -{ - public static string EvaluateProperty(string projectPath, string propertyName) - { - var command = Command.Run("dotnet", "msbuild", $"\"{projectPath}\"", $"-getProperty:{propertyName}"); - command.Wait(); - return command.Result.StandardOutput; - } - - public static IReadOnlyDictionary EvaluateProperties(string projectPath, params string[] propertyNames) - { - if (!propertyNames.Any()) - return new Dictionary(); - - var command = Command.Run("dotnet", "msbuild", $"\"{projectPath}\"", $"-getProperty:{string.Join(",", propertyNames)}"); - command.Wait(); - var resultString = command.Result.StandardOutput; - if (propertyNames.Length == 1) - return new Dictionary { { propertyNames[0], resultString } }; - - var resultJson = JsonDocument.Parse(resultString); - var propertiesJson = resultJson.RootElement.GetProperty("Properties").EnumerateObject().ToArray(); - - return propertiesJson - .ToDictionary(property => property.Name, property => property.Value.GetString() ?? string.Empty); - } -} diff --git a/Cesium.Sdk.Tests/SdkTestBase.cs b/Cesium.Sdk.Tests/SdkTestBase.cs index f3669540..1cf09d62 100644 --- a/Cesium.Sdk.Tests/SdkTestBase.cs +++ b/Cesium.Sdk.Tests/SdkTestBase.cs @@ -3,14 +3,13 @@ using System.Runtime.InteropServices; using System.Text.Json; using Cesium.Solution.Metadata; +using Cesium.TestFramework; using Xunit.Abstractions; namespace Cesium.Sdk.Tests; public abstract class SdkTestBase : IDisposable { - private const string _binLogFile = "build_result.binlog"; - private readonly ITestOutputHelper _testOutputHelper; private readonly string _temporaryPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); @@ -36,7 +35,7 @@ public SdkTestBase(ITestOutputHelper testOutputHelper) EmitGlobalJson(GlobalJsonPath, $"{SolutionMetadata.VersionPrefix}"); } - protected BuildResult ExecuteTargets(string projectName, params string[] targets) + protected async Task ExecuteTargets(string projectName, params string[] targets) { var projectFile = $"{projectName}/{projectName}.ceproj"; var joinedTargets = string.Join(";", targets); @@ -90,7 +89,7 @@ protected BuildResult ExecuteTargets(string projectName, params string[] targets ? "Build succeeded" : $"Build failed with exit code {process.ExitCode}"); - var properties = MSBuildCLI.EvaluateProperties(testProjectFile, objFolderPropertyName, binFolderPropertyName); + var properties = await DotNetCliHelper.EvaluateMSBuildProperties(_testOutputHelper, testProjectFile, objFolderPropertyName, binFolderPropertyName); _testOutputHelper.WriteLine($"Properties request result: {JsonSerializer.Serialize(properties, new JsonSerializerOptions { WriteIndented = false })}"); var binFolder = NormalizePath(Path.GetFullPath(properties[binFolderPropertyName], testProjectFolder)); diff --git a/Cesium.Sdk.Tests/Framework/AssertEx.cs b/Cesium.TestFramework/AssertCollection.cs similarity index 76% rename from Cesium.Sdk.Tests/Framework/AssertEx.cs rename to Cesium.TestFramework/AssertCollection.cs index 1bf223c7..ab29b7fd 100644 --- a/Cesium.Sdk.Tests/Framework/AssertEx.cs +++ b/Cesium.TestFramework/AssertCollection.cs @@ -1,6 +1,8 @@ -namespace Cesium.Sdk.Tests.Framework; +using Cesium.TestFramework.Exceptions; -public static class AssertEx +namespace Cesium.TestFramework; + +public static class AssertCollection { public static void Includes(IReadOnlyCollection expected, IReadOnlyCollection all) { diff --git a/Cesium.TestFramework/DotNetCliHelper.cs b/Cesium.TestFramework/DotNetCliHelper.cs index d945fda6..587f9a8e 100644 --- a/Cesium.TestFramework/DotNetCliHelper.cs +++ b/Cesium.TestFramework/DotNetCliHelper.cs @@ -1,3 +1,4 @@ +using System.Text.Json; using Medallion.Shell; using Xunit.Abstractions; @@ -25,6 +26,29 @@ await RunToSuccess(output, "dotnet", Path.GetDirectoryName(projectFilePath)!, ne }); } + public static async Task EvaluateMSBuildProperty(ITestOutputHelper output, string projectPath, string propertyName) + { + var result = await ExecUtil.Run(output, "dotnet", Environment.CurrentDirectory, [ "msbuild", $"\"{projectPath}\"", $"-getProperty:{propertyName}" ]); + return result.StandardOutput; + } + + public static async Task> EvaluateMSBuildProperties(ITestOutputHelper output, string projectPath, params string[] propertyNames) + { + if (!propertyNames.Any()) + return new Dictionary(); + + var result = await ExecUtil.Run(output, "dotnet", Environment.CurrentDirectory, [ "msbuild", $"\"{projectPath}\"", $"-getProperty:{string.Join(",", propertyNames)}" ]); + var resultString = result.StandardOutput; + if (propertyNames.Length == 1) + return new Dictionary { { propertyNames[0], resultString } }; + + var resultJson = JsonDocument.Parse(resultString); + var propertiesJson = resultJson.RootElement.GetProperty("Properties").EnumerateObject().ToArray(); + + return propertiesJson + .ToDictionary(property => property.Name, property => property.Value.GetString() ?? string.Empty); + } + public static Task RunDotNetDll( ITestOutputHelper output, string workingDirectoryPath, diff --git a/Cesium.Sdk.Tests/Framework/IncludesAssertFailedException.cs b/Cesium.TestFramework/Exceptions/IncludesAssertFailedException.cs similarity index 84% rename from Cesium.Sdk.Tests/Framework/IncludesAssertFailedException.cs rename to Cesium.TestFramework/Exceptions/IncludesAssertFailedException.cs index 9306e719..48a8d4dd 100644 --- a/Cesium.Sdk.Tests/Framework/IncludesAssertFailedException.cs +++ b/Cesium.TestFramework/Exceptions/IncludesAssertFailedException.cs @@ -1,6 +1,6 @@ using Xunit.Sdk; -namespace Cesium.Sdk.Tests.Framework; +namespace Cesium.TestFramework.Exceptions; public class IncludesAssertFailedException( IEnumerable expected, From df2583e775a921fe54001d2c7993f7fe29787e04 Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Sat, 20 Apr 2024 17:37:57 +0200 Subject: [PATCH 35/45] (#80) Docs: document how to run the SDK tests --- docs/tests.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/tests.md b/docs/tests.md index 30781b70..0ce7cd13 100644 --- a/docs/tests.md +++ b/docs/tests.md @@ -80,6 +80,11 @@ Compiler packing is done by 2 NUKE targets: - `PublishCompilerBundle`: a target that make platform-specific `dotnet publish` of compiler bundle to corresponding artifacts' folder. - `PackCompilerBundle`: a target that wraps a published compiler bundle into a NuGet package which is then used by SDK to deliver compiler to user's project +If you want to run these tests separately, run the targets using a shell command: +```console +$ dotnet nuke PackCompilerBundle +``` + Both targets are called automatically when `TestSdk` target is invoked. SDK itself should also be built to be used in test projects. This is done by dependent target `PackSdk` which produces `Cesium.Sdk` NuGet package, suitable as Project SDK. From 681920c1b44d51ef4f08295ebeee3764419af716 Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Sat, 20 Apr 2024 17:40:25 +0200 Subject: [PATCH 36/45] (#80) Add documentation on the solution metadata attribute --- Cesium.Solution.Metadata/SolutionMetadataAttribute.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Cesium.Solution.Metadata/SolutionMetadataAttribute.cs b/Cesium.Solution.Metadata/SolutionMetadataAttribute.cs index c11ac89f..ecf3e7eb 100644 --- a/Cesium.Solution.Metadata/SolutionMetadataAttribute.cs +++ b/Cesium.Solution.Metadata/SolutionMetadataAttribute.cs @@ -1,5 +1,6 @@ namespace Cesium.Solution.Metadata; +/// This attribute is only used by the Cesium test infrastructure. public class SolutionMetadataAttribute : Attribute { public string SourceRoot { get; } From 5012948e0fd71b61c7c5e984eb1830b669db8219 Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Sat, 20 Apr 2024 18:48:01 +0200 Subject: [PATCH 37/45] (#80) Docs: more info on running tests --- CONTRIBUTING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cb9d2cc9..448f2dfb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -18,7 +18,8 @@ There are two kinds of tests in Cesium: unit tests and integration tests. Run the unit and integration tests using this shell command: ```console -$ dotnet test +$ dotnet restore +$ dotnet nuke TestAll ``` Publishing From 65e6b99412bf245e488a366be49e83fdd790defd Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Sat, 20 Apr 2024 18:48:33 +0200 Subject: [PATCH 38/45] (#80) SDK: self-written code for process argument passing --- Cesium.Sdk.Tests/ArgumentUtilTests.cs | 17 +++++ Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj | 1 + Cesium.Sdk/ArgumentUtil.cs | 73 ++++++++++++++++++++ Cesium.Sdk/CesiumCompile.cs | 44 ++++++------ Cesium.Sdk/CommandArgumentsBuilder.cs | 86 ------------------------ 5 files changed, 113 insertions(+), 108 deletions(-) create mode 100644 Cesium.Sdk.Tests/ArgumentUtilTests.cs create mode 100644 Cesium.Sdk/ArgumentUtil.cs delete mode 100644 Cesium.Sdk/CommandArgumentsBuilder.cs diff --git a/Cesium.Sdk.Tests/ArgumentUtilTests.cs b/Cesium.Sdk.Tests/ArgumentUtilTests.cs new file mode 100644 index 00000000..7df90d99 --- /dev/null +++ b/Cesium.Sdk.Tests/ArgumentUtilTests.cs @@ -0,0 +1,17 @@ +namespace Cesium.Sdk.Tests; + +public class ArgumentUtilTests +{ + [Fact] + public void PerformTests() + { + Assert.Equal("\"\"", ArgumentUtil.ToCommandLineString([""])); + Assert.Equal("a b c", ArgumentUtil.ToCommandLineString(["a", "b", "c"])); + Assert.Equal("\"a b\" c", ArgumentUtil.ToCommandLineString(["a b", "c"])); + Assert.Equal("a\\b c", ArgumentUtil.ToCommandLineString([@"a\b", "c"])); + Assert.Equal("\"\\\"\"", ArgumentUtil.ToCommandLineString(["\""])); + Assert.Equal("\"a \\\"b\\\"\"", ArgumentUtil.ToCommandLineString(["a \"b\""])); + Assert.Equal("\"\\\\\\\"\"", ArgumentUtil.ToCommandLineString(["\\\""])); + Assert.Equal("\"a\\ \\\\\\\"b\\\"\"", ArgumentUtil.ToCommandLineString(["a\\ \\\"b\""])); + } +} diff --git a/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj b/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj index 2d715572..bc129dc3 100644 --- a/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj +++ b/Cesium.Sdk.Tests/Cesium.Sdk.Tests.csproj @@ -31,6 +31,7 @@ + diff --git a/Cesium.Sdk/ArgumentUtil.cs b/Cesium.Sdk/ArgumentUtil.cs new file mode 100644 index 00000000..8df5dd0c --- /dev/null +++ b/Cesium.Sdk/ArgumentUtil.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Cesium.Sdk; + +public static class ArgumentUtil +{ + public static string ToCommandLineString(IEnumerable args) + { + var result = new StringBuilder(); + var first = true; + foreach (var a in args) + { + if (first) + { + first = false; + } + else + { + result.Append(' '); + } + if (a.Length == 0 || a.Any(c => char.IsWhiteSpace(c) || c == '"')) + { + result.Append(Quoted(a)); + } + else + { + result.Append(a); + } + } + return result.ToString(); + } + + private static string Quoted(string arg) + { + // The simplified rules: + // 1. Every quote should be escaped by \ + // 2. Slashes preceding the quotes should be escaped by \ + // + // Meaning any amount of slashes following a quote should be converted to twice as many slashes + slash + quote. + // The final quote (not part of the argument per se) also counts as quote for this purpose. + var result = new StringBuilder(arg.Length + 2); + result.Append('"'); + var slashes = 0; + foreach (var c in arg) + { + switch (c) + { + case '\\': + slashes++; + break; + case '"': + result.Append('\\', slashes * 2); + slashes = 0; + + result.Append("\\\""); + break; + default: + result.Append('\\', slashes); + slashes = 0; + + result.Append(c); + break; + } + } + + result.Append('\\', slashes * 2); + result.Append('"'); + + return result.ToString(); + } +} diff --git a/Cesium.Sdk/CesiumCompile.cs b/Cesium.Sdk/CesiumCompile.cs index 7201cee3..cc756379 100644 --- a/Cesium.Sdk/CesiumCompile.cs +++ b/Cesium.Sdk/CesiumCompile.cs @@ -187,67 +187,67 @@ private bool TryValidate(out ValidatedOptions? options) private string CollectCommandLineArguments(ValidatedOptions options) { - var builder = new CommandArgumentsBuilder(); + var args = new List(); - builder.RawArgument("--nologo"); + args.Add("--nologo"); if (options.Framework is { } framework) { - builder.RawArgument("--framework"); - builder.RawArgument(framework.ToString()); + args.Add("--framework"); + args.Add(framework.ToString()); } if (options.Architecture is { } arch) { - builder.RawArgument("--arch"); - builder.RawArgument(arch.ToString()); + args.Add("--arch"); + args.Add(arch.ToString()); } if (options.ModuleKind is { } moduleKind) { - builder.RawArgument("--modulekind"); - builder.RawArgument(moduleKind.ToString()); + args.Add("--modulekind"); + args.Add(moduleKind.ToString()); } if (!string.IsNullOrWhiteSpace(options.Namespace)) { - builder.RawArgument("--namespace"); - builder.RawArgument(options.Namespace!); + args.Add("--namespace"); + args.Add(options.Namespace!); } foreach (var import in options.ImportItems) { - builder.RawArgument("--import"); - builder.Argument(import); + args.Add("--import"); + args.Add(import); } if (!string.IsNullOrWhiteSpace(options.CoreLibPath)) { - builder.RawArgument("--corelib"); - builder.Argument(options.CoreLibPath!); + args.Add("--corelib"); + args.Add(options.CoreLibPath!); } if (!string.IsNullOrWhiteSpace(options.RuntimePath)) { - builder.RawArgument("--runtime"); - builder.Argument(options.RuntimePath!); + args.Add("--runtime"); + args.Add(options.RuntimePath!); } foreach (var item in options.PreprocessorItems) { - builder.RawArgument("-D"); - builder.Argument(item); + args.Add("-D"); + args.Add(item); } - builder.RawArgument("--out"); - builder.Argument(options.OutputFile); + args.Add("--out"); + args.Add(options.OutputFile); foreach (var input in options.InputItems) { - builder.Argument(input); + args.Add(input); } - return builder.Build(); + return ArgumentUtil.ToCommandLineString(args); } private void ReportValidationError(string code, string message) => diff --git a/Cesium.Sdk/CommandArgumentsBuilder.cs b/Cesium.Sdk/CommandArgumentsBuilder.cs deleted file mode 100644 index ffcc4077..00000000 --- a/Cesium.Sdk/CommandArgumentsBuilder.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System.Linq; -using System.Text; - -namespace Cesium.Sdk; - -// Implementation reference: -// https://github.com/Tyrrrz/CliWrap/blob/417aaffe171b9897799ed2a28a6467c11d69c296/CliWrap/Builders/ArgumentsBuilder.cs -// MIT License, Oleksii Holub -public class CommandArgumentsBuilder -{ - private StringBuilder _builder = new StringBuilder(); - - public CommandArgumentsBuilder RawArgument(string value) - { - _builder.Append(' '); - _builder.Append(value); - - return this; - } - - public CommandArgumentsBuilder Argument(string argument) - { - _builder.Append(' '); - if (NeedsEscaping(argument)) - argument = Escape(argument); - _builder.Append(argument); - - return this; - } - - public string Build() => _builder.ToString(); - - private bool NeedsEscaping(string argument) => - argument.Length > 0 && argument.Any(c => char.IsWhiteSpace(c) || c != '"'); - - private static string Escape(string argument) - { - var buffer = new StringBuilder(); - - buffer.Append('"'); - - for (var i = 0; i < argument.Length;) - { - var c = argument[i++]; - - if (c == '\\') - { - var backslashCount = 1; - while (i < argument.Length && argument[i] == '\\') - { - backslashCount++; - i++; - } - - if (i == argument.Length) - { - buffer.Append('\\', backslashCount * 2); - } - else if (argument[i] == '"') - { - buffer - .Append('\\', backslashCount * 2 + 1) - .Append('"'); - - i++; - } - else - { - buffer.Append('\\', backslashCount); - } - } - else if (c == '"') - { - buffer.Append('\\').Append('"'); - } - else - { - buffer.Append(c); - } - } - - buffer.Append('"'); - - return buffer.ToString(); - } -} From 21e9391df6c38a014360d2222c403807ab37747d Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Sat, 20 Apr 2024 18:49:45 +0200 Subject: [PATCH 39/45] (#80) SDK: add a TODO note --- Cesium.Sdk/Sdk/Sdk.props | 1 + 1 file changed, 1 insertion(+) diff --git a/Cesium.Sdk/Sdk/Sdk.props b/Cesium.Sdk/Sdk/Sdk.props index e8e643e6..ceac26d6 100644 --- a/Cesium.Sdk/Sdk/Sdk.props +++ b/Cesium.Sdk/Sdk/Sdk.props @@ -10,6 +10,7 @@ false false Cesium.Compiler.Bundle.$(NETCoreSdkRuntimeIdentifier) + 0.0.1 false From c020d676129a12d4c2ac1eb92f20115412fccdaa Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 21 Apr 2024 14:55:01 +0200 Subject: [PATCH 40/45] SDK: Fix minor issues in documentation --- docs/msbuild-sdk.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/msbuild-sdk.md b/docs/msbuild-sdk.md index f5fce3bb..c7258b74 100644 --- a/docs/msbuild-sdk.md +++ b/docs/msbuild-sdk.md @@ -14,10 +14,10 @@ Source files are defined with `` items, very similar to other .NET lang ```xml - + ``` -> Note: In the current SDK implementation, compile units should be defined explicitly, in opposite to C# `` items. +> Note: In the current SDK implementation, source files will not be included into compilation implicitly. They should be defined in `` items, in opposite to SDK-style C# projects, where all C# source files are implicitly added to the compilation. ### References @@ -31,7 +31,7 @@ Not supported yet. Not supported yet. ### Preprocessor directives -`` property is directly mapped to a list of preprocessor items. So, you could define such constants in .csproj: +`` property is directly mapped to a list of preprocessor items. So, you could define such constants in `.ceproj`: ```xml $(DefineConstants);FOO;BAR @@ -39,7 +39,7 @@ Not supported yet. ``` And then use it in your .c code: -```c++ +```c #ifdef FOO int foo() { return 0; } #endif From 06ccca072e5b32a8f61ff6df16b45ba39e88e737 Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 21 Apr 2024 15:03:31 +0200 Subject: [PATCH 41/45] SDK: Remove unnecessary lines --- Cesium.Compiler/Cesium.Compiler.csproj | 1 - Cesium.Sdk/Cesium.Sdk.csproj | 2 -- build/Build.cs | 1 - 3 files changed, 4 deletions(-) diff --git a/Cesium.Compiler/Cesium.Compiler.csproj b/Cesium.Compiler/Cesium.Compiler.csproj index 3f80bfde..be7246ac 100644 --- a/Cesium.Compiler/Cesium.Compiler.csproj +++ b/Cesium.Compiler/Cesium.Compiler.csproj @@ -4,7 +4,6 @@ Exe net8.0 enable - true true Major diff --git a/Cesium.Sdk/Cesium.Sdk.csproj b/Cesium.Sdk/Cesium.Sdk.csproj index dc1b44cd..4e8d3843 100644 --- a/Cesium.Sdk/Cesium.Sdk.csproj +++ b/Cesium.Sdk/Cesium.Sdk.csproj @@ -2,8 +2,6 @@ netstandard2.0 - - enable true diff --git a/build/Build.cs b/build/Build.cs index 303ba6f8..b6701f73 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -8,7 +8,6 @@ partial class Build : NukeBuild { public static int Main() { - // while (!Debugger.IsAttached) Thread.Sleep(100); return Execute(x => x.CompileAll); } From 3ae514ff0725b15f07941c9009f5c20a0f982feb Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 21 Apr 2024 15:04:12 +0200 Subject: [PATCH 42/45] SDK: Fix naming of methods those are referencing the old "pack" term instead of "bundle" --- build/Build.Sdk.cs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/build/Build.Sdk.cs b/build/Build.Sdk.cs index c158a46b..66838adf 100644 --- a/build/Build.Sdk.cs +++ b/build/Build.Sdk.cs @@ -11,7 +11,7 @@ public partial class Build { - const string _compilerPackPackagePrefix = "Cesium.Compiler.Bundle"; + const string _compilerBundlePackagePrefix = "Cesium.Compiler.Bundle"; Target PublishAllCompilerBundles => _ => _ .Executes(() => @@ -63,17 +63,17 @@ public partial class Build return; } - Log.Information($"Packing SDK..."); + Log.Information("Packing SDK..."); DotNetPack(o => o .SetConfiguration(Configuration) .SetProject(Solution.Cesium_Sdk.Path)); }); - void EmitCompilerPack(string runtimeId, Project compilerProject) + void EmitCompilerBundle(string runtimeId, Project compilerProject) { var version = compilerProject.GetVersion(); - var runtimePackageId = GetRuntimePackId(runtimeId); - var packageFile = GetRuntimePackFileName(version, runtimeId); + var runtimePackageId = GetRuntimeBundleId(runtimeId); + var packageFile = GetRuntimeBundleFileName(version, runtimeId); var publishDirectory = GetCompilerRuntimePublishFolder(compilerProject, runtimeId); Directory.CreateDirectory(publishDirectory); var publishedFiles = Directory.GetFiles(publishDirectory, "*.*", SearchOption.AllDirectories); @@ -116,7 +116,7 @@ void PublishCompiler(string runtimeId) { var compilerProject = Solution.Cesium_Compiler.GetMSBuildProject(); - if (!SkipCaches && !NeedPublishCompilerPack(compilerProject, runtimeId)) + if (!SkipCaches && !NeedPublishCompilerBundle(compilerProject, runtimeId)) { Log.Information($"Skipping {runtimeId} because it was already published. Use '--skip-caches true' to re-publish."); return; @@ -138,14 +138,14 @@ void PackCompiler(string runtimeId) { var compilerProject = Solution.Cesium_Compiler.GetMSBuildProject(); - if (!SkipCaches && !NeedPackageCompilerPack(compilerProject, runtimeId)) + if (!SkipCaches && !NeedPackageCompilerBundle(compilerProject, runtimeId)) { Log.Information($"Skipping {runtimeId} because it was already packed. Use '--skip-caches true' to re-pack."); return; } Log.Information($"Packing compiler for {runtimeId}..."); - EmitCompilerPack(runtimeId, compilerProject); + EmitCompilerBundle(runtimeId, compilerProject); } string GetCompilerRuntimePublishFolder(Project compilerProject, string runtimeId) => @@ -158,13 +158,13 @@ string GetCompilerRuntimePublishFolder(Project compilerProject, string runtimeId static string GetRuntimeArtifactFolder(string version, string runtimeId) => $"pack_{version}_{runtimeId}"; - static string GetRuntimePackId(string runtimeId) => - $"{_compilerPackPackagePrefix}.{runtimeId}"; + static string GetRuntimeBundleId(string runtimeId) => + $"{_compilerBundlePackagePrefix}.{runtimeId}"; - static string GetRuntimePackFileName(string version, string runtimeId) => - $"{_compilerPackPackagePrefix}.{runtimeId}.{version}.nupkg"; + static string GetRuntimeBundleFileName(string version, string runtimeId) => + $"{_compilerBundlePackagePrefix}.{runtimeId}.{version}.nupkg"; - bool NeedPublishCompilerPack(Project compiler, string runtimeId) + bool NeedPublishCompilerBundle(Project compiler, string runtimeId) { var folder = GetCompilerRuntimePublishFolder(compiler, runtimeId); @@ -172,11 +172,11 @@ bool NeedPublishCompilerPack(Project compiler, string runtimeId) || Directory.GetFiles(folder, "Cesium.Compiler*").Length == 0; } - bool NeedPackageCompilerPack(Project compiler, string runtimeId) + bool NeedPackageCompilerBundle(Project compiler, string runtimeId) { var version = compiler.GetVersion(); var packageDirectory = compiler.GetPackageOutputPath(); - var packageFileName = GetRuntimePackFileName(version, runtimeId); + var packageFileName = GetRuntimeBundleFileName(version, runtimeId); return !File.Exists(Path.Combine(packageDirectory, packageFileName)); } From f9745f6b1c61e2cfd0da766b84afaed772968cfc Mon Sep 17 00:00:00 2001 From: Andrii Rublov Date: Sun, 21 Apr 2024 15:04:41 +0200 Subject: [PATCH 43/45] SDK: Use ArgumentsList instead of Arguments when running MSBuild process --- Cesium.Sdk.Tests/SdkTestBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cesium.Sdk.Tests/SdkTestBase.cs b/Cesium.Sdk.Tests/SdkTestBase.cs index 1cf09d62..37ade95e 100644 --- a/Cesium.Sdk.Tests/SdkTestBase.cs +++ b/Cesium.Sdk.Tests/SdkTestBase.cs @@ -50,7 +50,7 @@ protected async Task ExecuteTargets(string projectName, params stri { WorkingDirectory = testProjectFolder, FileName = "dotnet", - Arguments = $"msbuild \"{testProjectFile}\" /t:{joinedTargets} /restore /bl:{binLogFile}", + ArgumentList = { "msbuild", testProjectFile, $"/t:{joinedTargets}", "/restore", $"/bl:{binLogFile}" }, RedirectStandardOutput = true, RedirectStandardError = true, CreateNoWindow = true, From 6463a891cb6555d5a9b2ff877f998dbdb7f5159e Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Sun, 21 Apr 2024 23:19:23 +0200 Subject: [PATCH 44/45] (#80) Docs: improve the test explanation --- docs/tests.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/tests.md b/docs/tests.md index 0ce7cd13..be231734 100644 --- a/docs/tests.md +++ b/docs/tests.md @@ -80,10 +80,11 @@ Compiler packing is done by 2 NUKE targets: - `PublishCompilerBundle`: a target that make platform-specific `dotnet publish` of compiler bundle to corresponding artifacts' folder. - `PackCompilerBundle`: a target that wraps a published compiler bundle into a NuGet package which is then used by SDK to deliver compiler to user's project -If you want to run these tests separately, run the targets using a shell command: +If you want to run these tests without Nuke (e.g. from the IDE), run the targets using a shell command: ```console $ dotnet nuke PackCompilerBundle ``` +After that, run the tests in your preferred way. Both targets are called automatically when `TestSdk` target is invoked. From 38cd4b7e26509833c65556819122319ce4b0d56e Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Sun, 21 Apr 2024 23:22:22 +0200 Subject: [PATCH 45/45] (#80, #196, #571) TODO processing --- Cesium.CodeGen.Tests/CodeGenMethodTests.cs | 2 +- Cesium.IntegrationTests/function_ptr.c | 2 +- Cesium.Sdk/Sdk/Sdk.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cesium.CodeGen.Tests/CodeGenMethodTests.cs b/Cesium.CodeGen.Tests/CodeGenMethodTests.cs index cc786ee3..842d975c 100644 --- a/Cesium.CodeGen.Tests/CodeGenMethodTests.cs +++ b/Cesium.CodeGen.Tests/CodeGenMethodTests.cs @@ -502,7 +502,7 @@ int console_read(struct struct1* s) { return s->x; }"); - // TODO [#196] + // TODO[#196] /* [Fact] public Task VarargFunctionPointerCallTest() => DoTest(@"int foo(int a, ...) { return a; } diff --git a/Cesium.IntegrationTests/function_ptr.c b/Cesium.IntegrationTests/function_ptr.c index b5fc9d11..29632475 100644 --- a/Cesium.IntegrationTests/function_ptr.c +++ b/Cesium.IntegrationTests/function_ptr.c @@ -17,7 +17,7 @@ int main(void) return x(40); - // TODO [#196] + // TODO[#196] // foov_t y = &foov; // return (x(40) + y(40, 123, 456, "foo")) / 2; } diff --git a/Cesium.Sdk/Sdk/Sdk.props b/Cesium.Sdk/Sdk/Sdk.props index ceac26d6..6cb6f3d8 100644 --- a/Cesium.Sdk/Sdk/Sdk.props +++ b/Cesium.Sdk/Sdk/Sdk.props @@ -10,7 +10,7 @@ false false Cesium.Compiler.Bundle.$(NETCoreSdkRuntimeIdentifier) - + 0.0.1 false