public void DiffBaseibraryV2VsV1() { AssemblyDiffer differ = new AssemblyDiffer(TestConstants.BaseLibV1Assembly, TestConstants.BaseLibV2Assembly); AssemblyDiffCollection diff = differ.GenerateTypeDiff(myQueries); Assert.AreEqual(4, diff.AddedRemovedTypes.AddedCount, "Added types"); Assert.AreEqual(4, diff.AddedRemovedTypes.RemovedCount, "Removed types"); Assert.AreEqual(3, diff.ChangedTypes.Count, "Changed Types"); }
internal void CreateUsageQueriesFromAssemblyDiff(AssemblyDiffCollection diff) { List <TypeDefinition> removedTypes = diff.AddedRemovedTypes.RemovedList; if (removedTypes.Count > 0) { // removed types new WhoUsesType(myAggregator, removedTypes); } // changed types foreach (TypeDiff changedType in diff.ChangedTypes) { if (changedType.TypeV1.IsInterface) { AddIntefaceChange(changedType); } else if (changedType.TypeV1.IsEnum) { List <FieldDefinition> removedEnumConstants = changedType.Fields.RemovedList; if (removedEnumConstants.Count > 0) { new WhoUsesType(myAggregator, changedType.TypeV1); } } else { List <EventDefinition> removedEvents = changedType.Events.RemovedList; if (removedEvents.Count > 0) { new WhoUsesEvents(myAggregator, removedEvents); } List <MethodDefinition> removedMethods = changedType.Methods.RemovedList; if (removedMethods.Count > 0) { new WhoUsesMethod(myAggregator, removedMethods); } CheckFieldChanges(changedType); if (changedType.Interfaces.RemovedCount > 0) { new WhoUsesType(myAggregator, changedType.TypeV1); } if (changedType.HasChangedBaseType) { new WhoUsesType(myAggregator, changedType.TypeV1); } } } }
internal void Print(AssemblyDiffCollection diff) { PrintAddedRemovedTypes(diff.AddedRemovedTypes); if (diff.ChangedTypes.Count > 0) { foreach (var typeChange in diff.ChangedTypes) { PrintTypeChanges(typeChange); } } }
public ApiBreakLevel GetBreakLevel(AssemblyDiffCollection diffResult) { var defaultApiBreakRuleStrategy = new DefaultApiBreakRuleStrategy(); var apiBreakLevel = defaultApiBreakRuleStrategy.GetBreakLevel(diffResult); if (apiBreakLevel == ApiBreakLevel.Major) { return(ApiBreakLevel.Minor); } return(apiBreakLevel); }
public bool Detect(AssemblyDiffCollection assemblyDiffCollection) { if (assemblyDiffCollection.AddedRemovedTypes.RemovedCount > 0) { return(true); } if (assemblyDiffCollection.ChangedTypes.Count > 0) { foreach (var typeChange in assemblyDiffCollection.ChangedTypes) { if (typeChange.HasChangedBaseType) { return(true); } if (typeChange.Interfaces.Count > 0) { if (typeChange.Interfaces.Removed.Any()) { return(true); } } if (typeChange.Events.Removed.Any()) { return(true); } if (typeChange.Fields.Removed.Any()) { return(true); } if (typeChange.Methods.Removed.Any()) { return(true); } } } return(false); }
public override void Execute() { using (Tracer t = new Tracer(myType, "Execute")) { base.Execute(); if (!IsValid) { Help(); return; } Out.WriteLine("Compare from {0} against {1}", myParsedArgs.Queries1.GetSearchDirs(), myParsedArgs.Queries2.GetSearchDirs()); int removedTypes = 0; int changedTypes = 0; List <string> removedFiles = myParsedArgs.Queries1.GetNotExistingFilesInOtherQuery(myParsedArgs.Queries2); if (removedFiles.Count > 0) { Out.WriteLine("Removed {0} files", removedFiles.Count); foreach (string str in removedFiles) { Console.WriteLine("\t{0}", Path.GetFileName(str)); } } HashSet <string> query1 = new HashSet <string>(myParsedArgs.Queries1.GetFiles(), new FileNameComparer()); HashSet <string> query2 = new HashSet <string>(myParsedArgs.Queries2.GetFiles(), new FileNameComparer()); // Get files which are present in one set and the other query1.IntersectWith(query2); DiffPrinter printer = new DiffPrinter(Out); foreach (string fileName1 in query1) { if (fileName1.EndsWith(".XmlSerializers.dll", StringComparison.OrdinalIgnoreCase)) { t.Info("Ignore xml serializer dll {0}", fileName1); continue; } string fileName2 = myParsedArgs.Queries2.GetMatchingFileByName(fileName1); var assemblyV1 = AssemblyLoader.LoadCecilAssembly(fileName1); var assemblyV2 = AssemblyLoader.LoadCecilAssembly(fileName2); if (assemblyV1 != null && assemblyV2 != null) { AssemblyDiffer differ = new AssemblyDiffer(assemblyV1, assemblyV2); AssemblyDiffCollection diff = differ.GenerateTypeDiff(QueryAggregator.PublicApiQueries); removedTypes += diff.AddedRemovedTypes.RemovedCount; changedTypes += diff.ChangedTypes.Count; if (diff.AddedRemovedTypes.Count > 0 || diff.ChangedTypes.Count > 0) { Out.WriteLine("{0} has {1} changes", Path.GetFileName(fileName1), diff.AddedRemovedTypes.Count + diff.ChangedTypes.Sum(type => type.Events.Count + type.Fields.Count + type.Interfaces.Count + type.Methods.Count)); printer.Print(diff); } } } Out.WriteLine("From {0} assemblies were {1} types removed and {2} changed.", myParsedArgs.Queries1.GetFiles().Count(), removedTypes, changedTypes); } }
/// <summary> /// Marked as internal for unit test purpose. In charge of fetching all the version of an assembly and analyze the public /// API to know if there were breaking change or not /// </summary> internal Tuple <SemanticVersion, string> ResolveConflictingDependancy(IEnumerable <DllReference> conflictingAssembliesVersions, DllReference parentAssembly) { var diffs = new List <AssemblyDiffCollection>(); DiffPrinter diffPrinter = new DiffPrinter(); //Skip SemanticVersion until we reach the official SemanticVersion supported var asms = conflictingAssembliesVersions.Reverse(); asms = asms.Reverse(); //We start to next one as we already set PreviousAPI... var previousAPIAssembly = asms.FirstOrDefault(); asms = asms.Skip(1); DllReference selectedVersion = previousAPIAssembly; foreach (var apiAssembly in asms) { diffs = new List <AssemblyDiffCollection>(); AssemblyDiffCollection diff = null; try { if (previousAPIAssembly.Path != null && apiAssembly.Path != null) { diff = new AssemblyDiffer(previousAPIAssembly.Path, apiAssembly.Path).GenerateTypeDiff( QueryAggregator.AllExternallyVisibleApis); } //The previous assembly doesn't have any path we can compare with let's forget it else if (previousAPIAssembly.Path == null && apiAssembly.Path != null) { previousAPIAssembly = apiAssembly; continue; } //Current assembly doesn't have any path we can use to compare, ignore it else { continue; } //Nothing Was added between those 2 versions...Strange but let's go ahead if (diff.AddedRemovedTypes.Count <= 0 && diff.ChangedTypes.Count <= 0) { previousAPIAssembly = apiAssembly; continue; } diffs.Add(diff); var aggregator = new UsageQueryAggregator(); var breakingChangeSearcher = AppDomain.CurrentDomain.CreateInstanceAndUnwrap( "ApiChange.Api, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", "ApiChange.Api.Introspection.Diff.BreakingChangeSearcher", true, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public, null, new object[] { diffs, aggregator }, null, null); AssemblyDefinition assembly = AssemblyLoader.LoadCecilAssembly(parentAssembly.Path, false); if (assembly != null) { aggregator.Analyze(assembly); } //None of New Types, methods, fields etc. are used in assembly //So we stop here and return the previous one if (aggregator.AssemblyMatches.Count != 0 || aggregator.TypeMatches.Count != 0 || aggregator.MethodMatches.Count != 0 || aggregator.FieldMatches.Count != 0) { selectedVersion = apiAssembly; } } catch (Exception exc) { Trace.TraceError(exc.Message); Trace.TraceError(exc.StackTrace); Trace.TraceError(String.Format("Unable to diff assembly from {0} to {1}", previousAPIAssembly, apiAssembly)); //We don't set the prevous assembly because the current raised an exception, let's ignore it totally continue; } previousAPIAssembly = apiAssembly; } return(new Tuple <SemanticVersion, string>(selectedVersion.Id.Item1, selectedVersion.Id.Item2)); }
public override void Execute() { base.Execute(); if (!IsValid) { Help(); return; } List <AssemblyDiffCollection> assemblyDiffs = new List <AssemblyDiffCollection>(); DiffPrinter printer = new DiffPrinter(Out); foreach (string newFile in myParsedArgs.NewFiles.GetFiles()) { string oldFile = myParsedArgs.OldFiles.GetMatchingFileByName(newFile); if (oldFile == null) { oldFile = myParsedArgs.OldFiles2.GetMatchingFileByName(newFile); } if (oldFile == null) { Out.WriteLine("Warning: {0} seems to be a new file. Cannot diff.", Path.GetFileName(newFile)); continue; } try { AssemblyDiffer differ = new AssemblyDiffer(oldFile, newFile); AssemblyDiffCollection diff = differ.GenerateTypeDiff(QueryAggregator.AllExternallyVisibleApis); if (diff.AddedRemovedTypes.Count > 0 || diff.ChangedTypes.Count > 0) { Out.WriteLine("Changes of {0}", Path.GetFileName(newFile)); printer.Print(diff); assemblyDiffs.Add(diff); } } catch (ArgumentException) { // ignore C++ and other targets in diff } } UsageQueryAggregator usage = new UsageQueryAggregator(); BreakingChangeSearcher breaking = new BreakingChangeSearcher(assemblyDiffs, usage); foreach (string fileV2 in myParsedArgs.SearchInQuery.GetFiles()) { AssemblyDefinition aV2 = AssemblyLoader.LoadCecilAssembly(fileV2); if (aV2 != null) { usage.Analyze(aV2); } } Out.WriteLine("Detected {0} assemblies which need a recompilation", usage.AssemblyMatches.Count); foreach (var needsRecompilation in usage.AssemblyMatches) { Out.WriteLine("{0}", Path.GetFileName(needsRecompilation)); } }
public AssemblyDiffCollection Execute(List <FileQuery> oldFiles, List <FileQuery> newFiles) { using (var t = new Tracer(MyType, "Execute")) { //var removedTypes = 0; //var changedTypes = 0; var removedFiles = oldFiles.GetNotExistingFilesInOtherQuery(newFiles); if (removedFiles.Count > 0) { foreach (var str in removedFiles) { Console.WriteLine("\t{0}", Path.GetFileName(str)); } } var oldFilesQuery = new HashSet <string>(oldFiles.GetFiles(), new FileNameComparer()); var newFilesQuery = new HashSet <string>(newFiles.GetFiles(), new FileNameComparer()); // Get files which are present in one set and the other oldFilesQuery.IntersectWith(newFilesQuery); //DiffPrinter printer = new DiffPrinter(Out); var result = new AssemblyDiffCollection(); foreach (var fileName1 in oldFilesQuery) { if (fileName1.EndsWith(".XmlSerializers.dll", StringComparison.OrdinalIgnoreCase)) { t.Info("Ignore xml serializer dll {0}", fileName1); continue; } var fileName2 = newFiles.GetMatchingFileByName(fileName1); var assemblyV1 = AssemblyLoader.LoadCecilAssembly(fileName1); var assemblyV2 = AssemblyLoader.LoadCecilAssembly(fileName2); if (assemblyV1 != null && assemblyV2 != null) { var differ = new AssemblyDiffer(assemblyV1, assemblyV2); var differences = differ.GenerateTypeDiff(QueryAggregator.PublicApiQueries); result.AddedRemovedTypes.AddRange(differences.AddedRemovedTypes); result.ChangedTypes.AddRange(differences.ChangedTypes); //removedTypes += differences.AddedRemovedTypes.RemovedCount; //changedTypes += differences.ChangedTypes.Count; /*if (diff.AddedRemovedTypes.Count > 0 || diff.ChangedTypes.Count > 0) * { * // Out.WriteLine("{0} has {1} changes", Path.GetFileName(fileName1), diff.AddedRemovedTypes.Count + diff.ChangedTypes.Sum(type => type.Events.Count + type.Fields.Count + type.Interfaces.Count + type.Methods.Count)); * * // printer.Print(diff); * // Out.WriteLine("From {0} assemblies were {1} types removed and {2} changed.", myParsedArgs.Queries1.GetFiles().Count(), removedTypes, changedTypes); * * }*/ } } return(result); } }