diff --git a/AutoDI.Build/AssemblyResolver.cs b/AutoDI.Build/AssemblyResolver.cs index 3b4928f..72bd7d3 100644 --- a/AutoDI.Build/AssemblyResolver.cs +++ b/AutoDI.Build/AssemblyResolver.cs @@ -29,7 +29,7 @@ public AssemblyResolver(IEnumerable assembliesToInclude, ILogger logger) _assemblyCache[assembly.Name.Name] = assembly; } } - + logger.Info("Done loading referenced assemblies"); } @@ -43,7 +43,7 @@ public override AssemblyDefinition Resolve(AssemblyNameReference name, ReaderPar return assemblyDefinition; } assemblyDefinition = base.Resolve(name, readParameters); - + if (assemblyDefinition != null) { _logger.Debug($"Resolved assembly {name.FullName} from '{assemblyDefinition.MainModule.FileName}'", DebugLogLevel.Verbose); @@ -98,9 +98,10 @@ private AssemblyDefinition OnResolveFailure(object sender, AssemblyNameReference assembly = Assembly.LoadWithPartialName(reference.Name); #pragma warning restore 618 } - catch (FileNotFoundException) + catch (FileNotFoundException ex) { - _logger.Warning($"Failed to resolve '{reference.Name}'"); + var additionalInformation = StackTracer.GetStackTrace(ex); + _logger.Warning($"Failed to resolve '{reference.Name}'", additionalInformation); assembly = null; } diff --git a/AutoDI.Build/ILogger.cs b/AutoDI.Build/ILogger.cs index d50491e..36b791f 100644 --- a/AutoDI.Build/ILogger.cs +++ b/AutoDI.Build/ILogger.cs @@ -7,7 +7,8 @@ public interface ILogger void Debug(string message, DebugLogLevel debugLevel); void Info(string message); - void Warning(string message); - void Error(string message); + void Warning(string message, AdditionalInformation additionalInformation = null); + //This could probably just replace the AdditionalInformation class with SequencePoint + void Error(string message, AdditionalInformation additionalInformation); } } \ No newline at end of file diff --git a/AutoDI.Build/MultipleConstructorException.cs b/AutoDI.Build/MultipleConstructorException.cs index 97f261b..05f851f 100644 --- a/AutoDI.Build/MultipleConstructorException.cs +++ b/AutoDI.Build/MultipleConstructorException.cs @@ -1,9 +1,14 @@ -namespace AutoDI.Build +using Mono.Cecil; + +namespace AutoDI.Build { internal class MultipleConstructorException : AutoDIBuildException { - public MultipleConstructorException(string message) : base(message) + public MethodDefinition DuplicateConstructor { get; } + + public MultipleConstructorException(string message, MethodDefinition duplicateContructor) : base(message) { + DuplicateConstructor = duplicateContructor; } } } diff --git a/AutoDI.Build/ProcessAssemblyTask.Container.cs b/AutoDI.Build/ProcessAssemblyTask.Container.cs index 8e3e30d..3ced4a8 100644 --- a/AutoDI.Build/ProcessAssemblyTask.Container.cs +++ b/AutoDI.Build/ProcessAssemblyTask.Container.cs @@ -12,17 +12,17 @@ partial class ProcessAssemblyTask { //TODO: out parameters... yuck private TypeDefinition GenerateAutoDIClass(Mapping mapping, Settings settings, ICodeGenerator codeGenerator, - out MethodDefinition initMethod) + out MethodDefinition initMethod) { var containerType = new TypeDefinition(Constants.Namespace, Constants.TypeName, - TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Sealed - | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit) + TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Sealed + | TypeAttributes.AnsiClass | TypeAttributes.BeforeFieldInit) { BaseType = Import.System.Object }; FieldDefinition globalServiceProvider = - ModuleDefinition.CreateStaticReadonlyField(Constants.GlobalServiceProviderName, false, Import.System.IServiceProvider); + ModuleDefinition.CreateStaticReadonlyField(Constants.GlobalServiceProviderName, false, Import.System.IServiceProvider); containerType.Fields.Add(globalServiceProvider); MethodDefinition configureMethod = GenerateAddServicesMethod(mapping, settings, containerType, codeGenerator); @@ -40,8 +40,8 @@ private TypeDefinition GenerateAutoDIClass(Mapping mapping, Settings settings, I private MethodDefinition GenerateAddServicesMethod(Mapping mapping, Settings settings, TypeDefinition containerType, ICodeGenerator codeGenerator) { var method = new MethodDefinition("AddServices", - MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, - Import.System.Void); + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, + Import.System.Void); var serviceCollection = new ParameterDefinition("collection", ParameterAttributes.None, Import.DependencyInjection.IServiceCollection); method.Parameters.Add(serviceCollection); @@ -85,13 +85,13 @@ private MethodDefinition GenerateAddServicesMethod(Mapping mapping, Settings set Logger.Debug($"Processing map for {registration.TargetType.FullName}", DebugLogLevel.Verbose); if (!factoryMethods.TryGetValue(registration.TargetType.FullName, - out MethodDefinition factoryMethod)) + out MethodDefinition factoryMethod)) { factoryMethod = GenerateFactoryMethod(registration.TargetType, factoryIndex, codeGenerator); if (factoryMethod == null) { Logger.Debug($"No acceptable constructor for '{registration.TargetType.FullName}', skipping map", - DebugLogLevel.Verbose); + DebugLogLevel.Verbose); continue; } factoryMethods[registration.TargetType.FullName] = factoryMethod; @@ -105,8 +105,8 @@ private MethodDefinition GenerateAddServicesMethod(Mapping mapping, Settings set TypeReference importedKey = ModuleDefinition.ImportReference(registration.Key); Logger.Debug( - $"Mapping {importedKey.FullName} => {registration.TargetType.FullName} ({registration.Lifetime})", - DebugLogLevel.Default); + $"Mapping {importedKey.FullName} => {registration.TargetType.FullName} ({registration.Lifetime})", + DebugLogLevel.Default); processor.Emit(OpCodes.Ldtoken, importedKey); processor.Emit(OpCodes.Call, Import.System.Type.GetTypeFromHandle); @@ -116,9 +116,9 @@ private MethodDefinition GenerateAddServicesMethod(Mapping mapping, Settings set processor.Emit(OpCodes.Ldnull); processor.Emit(OpCodes.Ldftn, factoryMethod); processor.Emit(OpCodes.Newobj, - ModuleDefinition.ImportReference( - funcCtor.MakeGenericDeclaringType(Import.System.IServiceProvider, - ModuleDefinition.ImportReference(registration.TargetType)))); + ModuleDefinition.ImportReference( + funcCtor.MakeGenericDeclaringType(Import.System.IServiceProvider, + ModuleDefinition.ImportReference(registration.TargetType)))); processor.Emit(OpCodes.Ldc_I4, (int)registration.Lifetime); @@ -147,14 +147,14 @@ private MethodDefinition GenerateAddServicesMethod(Mapping mapping, Settings set processor.Append(handlerEnd); var exceptionHandler = - new ExceptionHandler(ExceptionHandlerType.Catch) - { - CatchType = Import.System.Exception, - TryStart = tryStart, - TryEnd = handlerStart, - HandlerStart = handlerStart, - HandlerEnd = afterCatch - }; + new ExceptionHandler(ExceptionHandlerType.Catch) + { + CatchType = Import.System.Exception, + TryStart = tryStart, + TryEnd = handlerStart, + HandlerStart = handlerStart, + HandlerEnd = afterCatch + }; method.Body.ExceptionHandlers.Add(exceptionHandler); @@ -176,11 +176,23 @@ private MethodDefinition GenerateAddServicesMethod(Mapping mapping, Settings set } catch (MultipleConstructorException e) { - Logger.Error($"Failed to create map for {registration}\r\n{e}"); + var additionalInformation = StackTracer.GetStackTrace(e); + if (e.DuplicateConstructor?.DebugInformation?.HasSequencePoints == true) + { + SequencePoint sequencePoint = e.DuplicateConstructor.DebugInformation.SequencePoints.First(); + additionalInformation = new AdditionalInformation + { + File = sequencePoint.Document.Url, + Column = sequencePoint.StartColumn, + Line = sequencePoint.StartLine + }; + } + Logger.Error($"Failed to create map for {registration}\r\n{e}", additionalInformation); } catch (Exception e) { - Logger.Warning($"Failed to create map for {registration}\r\n{e}"); + var additionalInformation = StackTracer.GetStackTrace(e); + Logger.Warning($"Failed to create map for {registration}\r\n{e}", additionalInformation); } } } @@ -229,8 +241,8 @@ private MethodDefinition GenerateFactoryMethod(TypeDefinition targetType, int in if (targetTypeCtor == null) return null; var factory = new MethodDefinition($"<{targetType.Name}>_generated_{index}", - MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, - ModuleDefinition.ImportReference(targetType)); + MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, + ModuleDefinition.ImportReference(targetType)); factory.Parameters.Add(new ParameterDefinition("serviceProvider", ParameterAttributes.None, Import.System.IServiceProvider)); ILProcessor factoryProcessor = factory.Body.GetILProcessor(); @@ -261,8 +273,8 @@ private MethodDefinition GenerateFactoryMethod(TypeDefinition targetType, int in private MethodDefinition GenerateInitMethod(MethodDefinition configureMethod, FieldDefinition globalServiceProvider) { var initMethod = new MethodDefinition(Constants.InitMethodName, - MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, - Import.System.Void); + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, + Import.System.Void); var configureAction = new ParameterDefinition("configure", ParameterAttributes.None, Import.System.Action.Type.MakeGenericInstanceType(Import.AutoDI.IApplicationBuilder.Type)); initMethod.Parameters.Add(configureAction); @@ -326,8 +338,8 @@ private MethodDefinition GenerateInitMethod(MethodDefinition configureMethod, Fi private MethodDefinition GenerateDisposeMethod(FieldDefinition globalServiceProvider) { var disposeMethod = new MethodDefinition("Dispose", - MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, - Import.System.Void); + MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, + Import.System.Void); VariableDefinition disposable = new VariableDefinition(Import.System.IDisposable.Type); disposeMethod.Body.Variables.Add(disposable); diff --git a/AutoDI.Build/ProcessAssemblyTask.Settings.cs b/AutoDI.Build/ProcessAssemblyTask.Settings.cs index 1ea93b1..91eff6e 100644 --- a/AutoDI.Build/ProcessAssemblyTask.Settings.cs +++ b/AutoDI.Build/ProcessAssemblyTask.Settings.cs @@ -13,7 +13,8 @@ private Settings LoadSettings() } catch (SettingsParseException e) { - Logger.Error($"Failed to parse AutoDI settings{Environment.NewLine}{e.Message}"); + var additionalInformation = StackTracer.GetStackTrace(e); + Logger.Error($"Failed to parse AutoDI settings{Environment.NewLine}{e.Message}",additionalInformation); return null; } diff --git a/AutoDI.Build/ProcessAssemblyTask.cs b/AutoDI.Build/ProcessAssemblyTask.cs index 2277bb0..7a9996b 100644 --- a/AutoDI.Build/ProcessAssemblyTask.cs +++ b/AutoDI.Build/ProcessAssemblyTask.cs @@ -94,7 +94,8 @@ protected override bool WeaveAssembly() var sb = new StringBuilder(); for (Exception e = ex; e != null; e = e.InnerException) sb.AppendLine(e.ToString()); - Logger.Error(sb.ToString()); + var additionalInformation = StackTracer.GetStackTrace(ex); + Logger.Error(sb.ToString(),additionalInformation); return false; } } diff --git a/AutoDI.Build/StackTracer.cs b/AutoDI.Build/StackTracer.cs new file mode 100644 index 0000000..14d4831 --- /dev/null +++ b/AutoDI.Build/StackTracer.cs @@ -0,0 +1,34 @@ +using System; +using System.Diagnostics; + +namespace AutoDI.Build +{ + public static class StackTracer + { + public static AdditionalInformation GetStackTrace(Exception exception) + { + //var trace = new StackTrace(exception, true); + //var reflectedType = trace.GetFrame(0).GetMethod().ReflectedType; + var additionalInformation = new AdditionalInformation(); + //if (reflectedType != null) + //{ + // additionalInformation = new AdditionalInformation() + // { + // Column = trace.GetFrame(0).GetFileColumnNumber(), + // Line = trace.GetFrame(0).GetFileLineNumber(), + // //MethodName = reflectedType.FullName, + // File = trace.GetFrame(0).GetFileName() + // }; + // + //} + return additionalInformation; + } + } + + public class AdditionalInformation + { + public string File { get; set; } + public int Line { get; set; } + public int Column { get; set; } + } +} diff --git a/AutoDI.Build/TaskLogger.cs b/AutoDI.Build/TaskLogger.cs index b094455..41f32a6 100644 --- a/AutoDI.Build/TaskLogger.cs +++ b/AutoDI.Build/TaskLogger.cs @@ -19,10 +19,11 @@ public TaskLogger(Task task) _task = task ?? throw new ArgumentNullException(nameof(task)); } - public void Error(string message) + public void Error(string message, AdditionalInformation additionalInformation) { ErrorLogged = true; - _task.BuildEngine.LogErrorEvent(new BuildErrorEventArgs("", "", null, 0, 0, 0, 0, $"{MessageSender} {message}", "", MessageSender)); + _task.BuildEngine.LogErrorEvent(new BuildErrorEventArgs("", "", additionalInformation.File, additionalInformation.Line, additionalInformation.Column, + additionalInformation.Line, additionalInformation.Column, $"{MessageSender} {message}", "", MessageSender)); } public void Debug(string message, DebugLogLevel debugLevel) @@ -38,9 +39,20 @@ public void Info(string message) _task.BuildEngine.LogMessageEvent(new BuildMessageEventArgs($"{MessageSender} {message}", "", MessageSender, MessageImportance.Normal)); } - public void Warning(string message) + public void Warning(string message, AdditionalInformation additionalInformation) { - _task.BuildEngine.LogWarningEvent(new BuildWarningEventArgs("", "", null, 0, 0, 0, 0, $"{MessageSender} {message}", "", MessageSender)); + if (additionalInformation == null) + { + _task.BuildEngine.LogWarningEvent(new BuildWarningEventArgs("", "", null, 0, 0, 0, 0, + $"{MessageSender} {message}", "", + MessageSender)); + } + else + { + _task.BuildEngine.LogWarningEvent(new BuildWarningEventArgs("", "", additionalInformation.File, + additionalInformation.Line, additionalInformation.Column, 0, 0, $"{MessageSender} {message}", "", + MessageSender)); + } } } } \ No newline at end of file diff --git a/AutoDI.Build/TypeDefinitionMixins.cs b/AutoDI.Build/TypeDefinitionMixins.cs index cadc136..3fd8c14 100644 --- a/AutoDI.Build/TypeDefinitionMixins.cs +++ b/AutoDI.Build/TypeDefinitionMixins.cs @@ -45,7 +45,7 @@ public static MethodDefinition GetMappingConstructor(this TypeDefinition targetT { if (annotatedConstructors.Length > 1) { - throw new MultipleConstructorException($"More then one constructor on '{targetType.Name}' annotated with DiConstructorAttribute"); + throw new MultipleConstructorException($"More then one constructor on '{targetType.Name}' annotated with DiConstructorAttribute", annotatedConstructors[1]); } targetTypeCtor = annotatedConstructors[0]; }