diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index a9bcc2bd4b..ba616f071a 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -1,48 +1,78 @@ { "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Build Schema", - "$ref": "#/definitions/build", "definitions": { - "build": { - "type": "object", + "Host": { + "type": "string", + "enum": [ + "AppVeyor", + "AzurePipelines", + "Bamboo", + "Bitbucket", + "Bitrise", + "GitHubActions", + "GitLab", + "Jenkins", + "Rider", + "SpaceAutomation", + "TeamCity", + "Terminal", + "TravisCI", + "VisualStudio", + "VSCode" + ] + }, + "ExecutableTarget": { + "type": "string", + "enum": [ + "BuildToNuGetCache", + "CiAzureLinux", + "CiAzureOSX", + "CiAzureWindows", + "Clean", + "Compile", + "CompileHtmlPreviewer", + "CompileNative", + "CreateIntermediateNugetPackages", + "CreateNugetPackages", + "DownloadApiBaselinePackages", + "GenerateCppHeaders", + "OutputApiDiff", + "OutputVersion", + "Package", + "RunCoreLibsTests", + "RunHtmlPreviewerTests", + "RunLeakTests", + "RunRenderTests", + "RunTests", + "RunToolsTests", + "ValidateApiDiff", + "VerifyXamlCompilation", + "ZipFiles" + ] + }, + "Verbosity": { + "type": "string", + "description": "", + "enum": [ + "Verbose", + "Normal", + "Minimal", + "Quiet" + ] + }, + "NukeBuild": { "properties": { - "configuration": { - "type": "string" - }, "Continue": { "type": "boolean", "description": "Indicates to continue a previously failed build attempt" }, - "force-api-baseline": { - "type": "string" - }, - "force-nuget-version": { - "type": "string" - }, "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" - ] + "$ref": "#/definitions/Host" }, "NoLogo": { "type": "boolean", @@ -71,91 +101,54 @@ "type": "array", "description": "List of targets to be skipped. Empty list skips all dependencies", "items": { - "type": "string", - "enum": [ - "BuildToNuGetCache", - "CiAzureLinux", - "CiAzureOSX", - "CiAzureWindows", - "Clean", - "Compile", - "CompileHtmlPreviewer", - "CompileNative", - "CreateIntermediateNugetPackages", - "CreateNugetPackages", - "DownloadApiBaselinePackages", - "GenerateCppHeaders", - "OutputApiDiff", - "OutputVersion", - "Package", - "RunCoreLibsTests", - "RunHtmlPreviewerTests", - "RunLeakTests", - "RunRenderTests", - "RunTests", - "RunToolsTests", - "ValidateApiDiff", - "VerifyXamlCompilation", - "ZipFiles" - ] + "$ref": "#/definitions/ExecutableTarget" } }, - "skip-previewer": { - "type": "boolean" - }, - "skip-tests": { - "type": "boolean" - }, "Target": { "type": "array", "description": "List of targets to be invoked. Default is '{default_target}'", "items": { - "type": "string", - "enum": [ - "BuildToNuGetCache", - "CiAzureLinux", - "CiAzureOSX", - "CiAzureWindows", - "Clean", - "Compile", - "CompileHtmlPreviewer", - "CompileNative", - "CreateIntermediateNugetPackages", - "CreateNugetPackages", - "DownloadApiBaselinePackages", - "GenerateCppHeaders", - "OutputApiDiff", - "OutputVersion", - "Package", - "RunCoreLibsTests", - "RunHtmlPreviewerTests", - "RunLeakTests", - "RunRenderTests", - "RunTests", - "RunToolsTests", - "ValidateApiDiff", - "VerifyXamlCompilation", - "ZipFiles" - ] + "$ref": "#/definitions/ExecutableTarget" } }, - "update-api-suppression": { - "type": "boolean" - }, "Verbosity": { - "type": "string", "description": "Logging verbosity during build execution. Default is 'Normal'", - "enum": [ - "Minimal", - "Normal", - "Quiet", - "Verbose" + "$ref": "#/definitions/Verbosity" + } + } + } + }, + "allOf": [ + { + "properties": { + "configuration": { + "type": "string" + }, + "force-api-baseline": { + "type": "string" + }, + "force-nuget-version": { + "type": "string" + }, + "skip-previewer": { + "type": "boolean" + }, + "skip-tests": { + "type": "boolean" + }, + "update-api-suppression": { + "type": [ + "boolean", + "null" ] }, "version-output-dir": { "type": "string" } } + }, + { + "$ref": "#/definitions/NukeBuild" } - } -} \ No newline at end of file + ] +} diff --git a/nukebuild/ApiDiffHelper.cs b/nukebuild/ApiDiffHelper.cs index 06f3e23b7b..1d1f1b1b32 100644 --- a/nukebuild/ApiDiffHelper.cs +++ b/nukebuild/ApiDiffHelper.cs @@ -7,6 +7,7 @@ using System.IO; using System.IO.Compression; using System.Linq; using System.Security.Cryptography; +using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using NuGet.Common; @@ -26,9 +27,13 @@ public static class ApiDiffHelper const string MainPackageName = "Avalonia"; const string FolderLib = "lib"; + private static readonly Regex s_suppressionPathRegex = + new("<(Left|Right)>(.*?)", RegexOptions.Compiled); + public static void ValidatePackage( Tool apiCompatTool, PackageDiffInfo packageDiff, + AbsolutePath rootAssembliesFolderPath, AbsolutePath suppressionFilesFolderPath, bool updateSuppressionFile) { @@ -36,28 +41,68 @@ public static class ApiDiffHelper Directory.CreateDirectory(suppressionFilesFolderPath); - var suppressionArgs = ""; - - var suppressionFile = suppressionFilesFolderPath / (packageDiff.PackageId + ".nupkg.xml"); - if (suppressionFile.FileExists()) - suppressionArgs += $""" --suppression-file="{suppressionFile}" --permit-unnecessary-suppressions """; - - if (updateSuppressionFile) - suppressionArgs += $""" --suppression-output-file="{suppressionFile}" --generate-suppression-file --preserve-unnecessary-suppressions """; - + var suppressionFilePath = suppressionFilesFolderPath / (packageDiff.PackageId + ".nupkg.xml"); + var replaceDirectorySeparators = Path.DirectorySeparatorChar == '\\'; var allErrors = new List(); foreach (var framework in packageDiff.Frameworks) { - var args = $""" -l="{framework.BaselineFolderPath}" -r="{framework.CurrentFolderPath}" {suppressionArgs}"""; + var relativeBaselinePath = rootAssembliesFolderPath.GetRelativePathTo(framework.BaselineFolderPath); + var relativeCurrentPath = rootAssembliesFolderPath.GetRelativePathTo(framework.CurrentFolderPath); + var args = ""; + + if (suppressionFilePath.FileExists()) + { + args += $""" --suppression-file="{suppressionFilePath}" --permit-unnecessary-suppressions """; + + if (replaceDirectorySeparators) + ReplaceDirectorySeparators(suppressionFilePath, '/', '\\'); + } + + if (updateSuppressionFile) + args += $""" --suppression-output-file="{suppressionFilePath}" --generate-suppression-file --preserve-unnecessary-suppressions """; + + args += $""" -l="{relativeBaselinePath}" -r="{relativeCurrentPath}" """; + + var localErrors = GetErrors(apiCompatTool($"{args:nq}", rootAssembliesFolderPath, exitHandler: _ => { })); + + if (replaceDirectorySeparators) + ReplaceDirectorySeparators(suppressionFilePath, '\\', '/'); - var localErrors = GetErrors(apiCompatTool(args)); allErrors.AddRange(localErrors); } ThrowOnErrors(allErrors, packageDiff.PackageId, "ValidateApiDiff"); } + /// + /// The ApiCompat tool treats paths with '/' and '\' separators as different files. + /// Before running the tool, adjust the existing separators (using a dirty regex) to match the current platform. + /// After running the tool, change all separators back to '/'. + /// + static void ReplaceDirectorySeparators(AbsolutePath suppressionFilePath, char oldSeparator, char newSeparator) + { + if (!File.Exists(suppressionFilePath)) + return; + + var lines = File.ReadAllLines(suppressionFilePath); + + for (var i = 0; i < lines.Length; i++) + { + var original = lines[i]; + + var replacement = s_suppressionPathRegex.Replace(original, match => + { + var path = match.Groups[2].Value.Replace(oldSeparator, newSeparator); + return $"<{match.Groups[1].Value}>{path}"; + }); + + lines[i] = replacement; + } + + File.WriteAllLines(suppressionFilePath, lines); + } + public static void GenerateMarkdownDiff( Tool apiDiffTool, PackageDiffInfo packageDiff, @@ -87,7 +132,7 @@ public static class ApiDiffHelper var frameworkOutputFolderPath = packageOutputFolderPath / framework.Framework.GetShortFolderName(); var args = $""" -b="{framework.BaselineFolderPath}" -bfn="{baselineDisplay}" -a="{framework.CurrentFolderPath}" -afn="{currentDisplay}" -o="{frameworkOutputFolderPath}" -eattrs="{excludedAttributesFilePath}" """; - var localErrors = GetErrors(apiDiffTool(args)); + var localErrors = GetErrors(apiDiffTool($"{args:nq}")); if (localErrors.Length > 0) { diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index 0c962fa129..4f577444d1 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -11,12 +11,8 @@ using Nuke.Common.Tooling; using Nuke.Common.Tools.DotNet; using Nuke.Common.Tools.Npm; using static Nuke.Common.EnvironmentInfo; -using static Nuke.Common.IO.FileSystemTasks; using static Nuke.Common.IO.PathConstruction; -using static Nuke.Common.Tools.MSBuild.MSBuildTasks; using static Nuke.Common.Tools.DotNet.DotNetTasks; -using static Nuke.Common.Tools.Xunit.XunitTasks; -using static Nuke.Common.Tools.VSWhere.VSWhereTasks; using static Serilog.Log; using MicroCom.CodeGenerator; using NuGet.Configuration; @@ -40,13 +36,13 @@ partial class Build : NukeBuild ApiDiffHelper.GlobalDiffInfo? GlobalDiff { get; set; } #nullable restore - [PackageExecutable("Microsoft.DotNet.ApiCompat.Tool", "Microsoft.DotNet.ApiCompat.Tool.dll", Framework = "net8.0")] + [NuGetPackage("Microsoft.DotNet.ApiCompat.Tool", "Microsoft.DotNet.ApiCompat.Tool.dll", Framework = "net8.0")] Tool ApiCompatTool; - [PackageExecutable("Microsoft.DotNet.ApiDiff.Tool", "Microsoft.DotNet.ApiDiff.Tool.dll", Framework = "net8.0")] + [NuGetPackage("Microsoft.DotNet.ApiDiff.Tool", "Microsoft.DotNet.ApiDiff.Tool.dll", Framework = "net8.0")] Tool ApiDiffTool; - [PackageExecutable("dotnet-ilrepack", "ILRepackTool.dll", Framework = "net8.0")] + [NuGetPackage("dotnet-ilrepack", "ILRepackTool.dll", Framework = "net8.0")] Tool IlRepackTool; protected override void OnBuildInitialized() @@ -96,7 +92,7 @@ partial class Build : NukeBuild c.AddProperty("JavaSdkDirectory", GetVariable("JAVA_HOME_11_X64")); c.AddProperty("PackageVersion", Parameters.Version) .SetConfiguration(Parameters.Configuration) - .SetVerbosity(DotNetVerbosity.Minimal); + .SetVerbosity(DotNetVerbosity.minimal); if (Parameters.IsPackingToLocalCache) c .AddProperty("ForcePackAvaloniaNative", "True") @@ -116,12 +112,23 @@ partial class Build : NukeBuild Target Clean => _ => _.Executes(() => { - Parameters.BuildDirs.ForEach(DeleteDirectory); - EnsureCleanDirectory(Parameters.ArtifactsDir); - EnsureCleanDirectory(Parameters.NugetIntermediateRoot); - EnsureCleanDirectory(Parameters.NugetRoot); - EnsureCleanDirectory(Parameters.ZipRoot); - EnsureCleanDirectory(Parameters.TestResultsRoot); + foreach (var buildDir in Parameters.BuildDirs) + { + Information("Deleting {Directory}", buildDir); + buildDir.DeleteDirectory(); + } + + CleanDirectory(Parameters.ArtifactsDir); + CleanDirectory(Parameters.NugetIntermediateRoot); + CleanDirectory(Parameters.NugetRoot); + CleanDirectory(Parameters.ZipRoot); + CleanDirectory(Parameters.TestResultsRoot); + + void CleanDirectory(AbsolutePath path) + { + Information("Cleaning {Path}", path); + path.CreateOrCleanDirectory(); + } }); Target CompileHtmlPreviewer => _ => _ @@ -133,7 +140,7 @@ partial class Build : NukeBuild NpmTasks.NpmInstall(c => c .SetProcessWorkingDirectory(webappDir) - .SetProcessArgumentConfigurator(a => a.Add("--silent"))); + .SetProcessAdditionalArguments("--silent")); NpmTasks.NpmRun(c => c .SetProcessWorkingDirectory(webappDir) .SetCommand("dist")); @@ -226,7 +233,7 @@ partial class Build : NukeBuild .SetFramework(tfm) .EnableNoBuild() .EnableNoRestore() - .When(Parameters.PublishTestResults, _ => _ + .When(_ => Parameters.PublishTestResults, _ => _ .SetLoggers("trx") .SetResultsDirectory(Parameters.TestResultsRoot))); } @@ -241,7 +248,7 @@ partial class Build : NukeBuild NpmTasks.NpmInstall(c => c .SetProcessWorkingDirectory(webappTestDir) - .SetProcessArgumentConfigurator(a => a.Add("--silent"))); + .SetProcessAdditionalArguments("--silent")); NpmTasks.NpmRun(c => c .SetProcessWorkingDirectory(webappTestDir) .SetCommand("test")); @@ -318,7 +325,7 @@ partial class Build : NukeBuild Parameters.Version + ".nupkg", IlRepackTool); var config = Numerge.MergeConfiguration.LoadFile(RootDirectory / "nukebuild" / "numerge.config"); - EnsureCleanDirectory(Parameters.NugetRoot); + Parameters.NugetRoot.CreateOrCleanDirectory(); if(!Numerge.NugetPackageMerger.Merge(Parameters.NugetIntermediateRoot, Parameters.NugetRoot, config, new NumergeNukeLogger())) throw new Exception("Package merge failed"); @@ -350,6 +357,7 @@ partial class Build : NukeBuild packageDiff => ApiDiffHelper.ValidatePackage( ApiCompatTool, packageDiff, + Parameters.ArtifactsDir / "api-diff" / "assemblies", Parameters.ApiValidationSuppressionFiles, Parameters.UpdateApiValidationSuppression)); }); @@ -451,7 +459,7 @@ partial class Build : NukeBuild var artifactsDirectory = buildTestsDirectory / "artifacts"; var nugetCacheDirectory = artifactsDirectory / "nuget-cache"; - DeleteDirectory(artifactsDirectory); + artifactsDirectory.DeleteDirectory(); BuildTestsAndVerify("Debug"); BuildTestsAndVerify("Release"); @@ -465,7 +473,7 @@ partial class Build : NukeBuild .SetProperty("NuGetPackageRoot", nugetCacheDirectory) .SetPackageDirectory(nugetCacheDirectory) .SetProjectFile(buildTestsDirectory / "BuildTests.sln") - .SetProcessArgumentConfigurator(arguments => arguments.Add("--nodeReuse:false"))); + .SetProcessAdditionalArguments("--nodeReuse:false")); // Standard compilation - should have compiled XAML VerifyBuildTestAssembly("bin", "BuildTests"); @@ -494,7 +502,7 @@ partial class Build : NukeBuild .SetPackageDirectory(nugetCacheDirectory) .SetNoBuild(noBuild) .SetProject(buildTestsDirectory / projectName / (projectName + ".csproj")) - .SetProcessArgumentConfigurator(arguments => arguments.Add("--nodeReuse:false"))); + .SetProcessAdditionalArguments("--nodeReuse:false")); void VerifyBuildTestAssembly(string folder, string projectName) => XamlCompilationVerifier.VerifyAssemblyCompiledXaml( diff --git a/nukebuild/BuildParameters.cs b/nukebuild/BuildParameters.cs index ff28764569..bfcfa46522 100644 --- a/nukebuild/BuildParameters.cs +++ b/nukebuild/BuildParameters.cs @@ -9,7 +9,6 @@ using System.Xml.Linq; using Nuke.Common; using Nuke.Common.CI.AzurePipelines; using Nuke.Common.IO; -using static Nuke.Common.IO.PathConstruction; public partial class Build { @@ -68,7 +67,7 @@ public partial class Build public AbsolutePath ZipRoot { get; } public AbsolutePath TestResultsRoot { get; } public string DirSuffix { get; } - public List BuildDirs { get; } + public List BuildDirs { get; } public string FileZipSuffix { get; } public AbsolutePath ZipCoreArtifacts { get; } public AbsolutePath ZipNuGetArtifacts { get; } @@ -147,10 +146,10 @@ public partial class Build NugetIntermediateRoot = RootDirectory / "build-intermediate" / "nuget"; ZipRoot = ArtifactsDir / "zip"; TestResultsRoot = ArtifactsDir / "test-results"; - BuildDirs = GlobDirectories(RootDirectory, "**/bin") - .Concat(GlobDirectories(RootDirectory, "**/obj")) - .Where(dir => !dir.Contains("nukebuild")) - .Concat(GlobDirectories(RootDirectory, "**/node_modules")) + BuildDirs = RootDirectory.GlobDirectories("**/bin") + .Concat(RootDirectory.GlobDirectories("**/obj")) + .Where(dir => !((string)dir).Contains("nukebuild")) + .Concat(RootDirectory.GlobDirectories("**/node_modules")) .ToList(); DirSuffix = Configuration; FileZipSuffix = Version + ".zip"; diff --git a/nukebuild/BuildTasksPatcher.cs b/nukebuild/BuildTasksPatcher.cs index 6bb71f4320..874d663490 100644 --- a/nukebuild/BuildTasksPatcher.cs +++ b/nukebuild/BuildTasksPatcher.cs @@ -85,7 +85,7 @@ public class BuildTasksPatcher var cecilMdbAsm = GetAssemblyPath(typeof(Mono.Cecil.Mdb.MdbReaderProvider)); ilRepackTool.Invoke( - $"/internalize /out:\"{output}\" \"{temp}\" \"{cecilAsm}\" \"{cecilRocksAsm}\" \"{cecilPdbAsm}\" \"{cecilMdbAsm}\"", + $"/internalize /out:\"{output:nq}\" \"{temp:nq}\" \"{cecilAsm:nq}\" \"{cecilRocksAsm:nq}\" \"{cecilPdbAsm:nq}\" \"{cecilMdbAsm:nq}\"", tempDir); // 'hurr-durr assembly with the same name is already loaded' prevention diff --git a/nukebuild/_build.csproj b/nukebuild/_build.csproj index 7c52be978a..2ea83ead71 100644 --- a/nukebuild/_build.csproj +++ b/nukebuild/_build.csproj @@ -15,18 +15,12 @@ - - + - + - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - -