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); } }
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)); } }
/// <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)); }
/// <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); }