diff --git a/tools/dotnet-linker/Steps/ConfigurationAwareStep.cs b/tools/dotnet-linker/Steps/ConfigurationAwareStep.cs index 286ec759e9..b3cc03fa5f 100644 --- a/tools/dotnet-linker/Steps/ConfigurationAwareStep.cs +++ b/tools/dotnet-linker/Steps/ConfigurationAwareStep.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using Mono.Cecil; using Mono.Linker.Steps; @@ -66,18 +67,71 @@ namespace Xamarin.Linker { // failure overrides, with defaults + bool CollectProductExceptions (Exception e, out List productExceptions) + { + if (e is ProductException pe) { + productExceptions = new List (); + productExceptions.Add (pe); + return true; + } + + if (e is AggregateException ae && ae.InnerExceptions.All (v => v is ProductException)) { + productExceptions = new List (ae.InnerExceptions.Cast ()); + return true; + } + + productExceptions = null; + return false; + } + protected virtual Exception Fail (AssemblyDefinition assembly, Exception e) { + // Detect if we're reporting one or more ProductExceptions (and no other exceptions), and in that case + // report the product exceptions as top-level exceptions + the step-specific exception at the end, + // instead of the step-specific exception with all the other exceptions as an inner exception. + // This makes the errors show up nicer in the output. + if (CollectProductExceptions (e, out var productExceptions)) { + // don't add inner exception + var ex = ErrorHelper.CreateError (ErrorCode, Errors.MX_ConfigurationAwareStepWithAssembly, Name, assembly?.FullName, e.Message); + // instead return an aggregate exception with the original exception and all the ProductExceptions we're reporting. + productExceptions.Add (ex); + return new AggregateException (productExceptions); + } + return ErrorHelper.CreateError (ErrorCode, e, Errors.MX_ConfigurationAwareStepWithAssembly, Name, assembly?.FullName, e.Message); } protected virtual Exception Fail (Exception e) { + // Detect if we're reporting one or more ProductExceptions (and no other exceptions), and in that case + // report the product exceptions as top-level exceptions + the step-specific exception at the end, + // instead of the step-specific exception with all the other exceptions as an inner exception. + // This makes the errors show up nicer in the output. + if (CollectProductExceptions (e, out var productExceptions)) { + // don't add inner exception + var ex = ErrorHelper.CreateError (ErrorCode | 1, Errors.MX_ConfigurationAwareStep, Name, e.Message); + // instead return an aggregate exception with the original exception and all the ProductExceptions we're reporting. + productExceptions.Add (ex); + return new AggregateException (productExceptions); + } + return ErrorHelper.CreateError (ErrorCode | 1, e, Errors.MX_ConfigurationAwareStep, Name, e.Message); } protected virtual Exception FailEnd (Exception e) { + // Detect if we're reporting one or more ProductExceptions (and no other exceptions), and in that case + // report the product exceptions as top-level exceptions + the step-specific exception at the end, + // instead of the step-specific exception with all the other exceptions as an inner exception. + // This makes the errors show up nicer in the output. + if (CollectProductExceptions (e, out var productExceptions)) { + // don't add inner exception + var ex = ErrorHelper.CreateError (ErrorCode | 2, Errors.MX_ConfigurationAwareStep, Name, e.Message); + // instead return an aggregate exception with the original exception and all the ProductExceptions we're reporting. + productExceptions.Add (ex); + return new AggregateException (productExceptions); + } + return ErrorHelper.CreateError (ErrorCode | 2, e, Errors.MX_ConfigurationAwareStep, Name, e.Message); }