在构建期间使用NDepend生成差异报告

lys*_*cid 1 .net c# continuous-integration ndepend build

我们使用TeamCity进行持续集成,我们的源代码控制是Git,我们有一个主要的存储库,包含多个.sln文件(大约10个).

总而言之,这个存储库大约有100到200个C#项目.

在推送到主存储库后,TeamCity会触发一个构建,该构建将编译存储库中的所有项目.

我希望能够告诉哪些项目实际上受到特定提交的影响,因此只将这些项目的输出发布为当前构建的工件.

为此,我设计了一个解决方案,将NDepend集成到我们的构建过程中,并在当前和最新构建输出之间生成差异报告.已更改/添加的输出将作为构建输出发布.

我对NDepend的经验不多; 从我所看到的,它所有的真正力量都来自于它的查询语言.

我想知道如何(如果可能的话)我可以实现以下目标:

  1. 包含先前构建的输出的文件夹与构建输出的当前文件夹之间的差异.
  2. 让NDepend以可使用的格式生成报告,以便我可以确定需要复制的文件.

这种情况可能吗?这有多容易/多难?

Pat*_*eam 5

因此,简单的答案是按照本文档中的说明执行报告代码Diff方式.这个基本答案的问题在于,它预先假定两个NDepend项目总是引用两组相同的程序集.


当然,程序集的数量和名称在您的上下文中是不同的,因此我们需要动态构建两个项目(旧/新)并通过NDepend.API进行分析.

这是NDepend.API的源代码.对于It-Just-Works体验,在PowerTools源代码(in $NDependInstallDir$\NDepend.PowerTools.SourceCode\NDepend.PowerTools.sln)中,只需FoldersDiff.Main();AssemblyResolve注册调用后调用该 方法即可Program.cs.

 ...
 AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolverHelper.AssemblyResolveHandler;
 FoldersDiff.Main();
 ...
Run Code Online (Sandbox Code Playgroud)

这是利用NDepend.API的源代码.

请注意,通过两个codeBase对象和compareContext对象可以完成更多工作.您可以显示API断点更改,添加的新方法和类型,修改的类和方法,代码质量回归,而不仅仅显示3个程序集列表,可以显示有关差异的默认代码规则. ,它基于相同的NDepend.CodeModel API.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using NDepend;
using NDepend.Analysis;
using NDepend.CodeModel;
using NDepend.Path;
using NDepend.Project;


class FoldersDiff {

   private static readonly NDependServicesProvider s_NDependServicesProvider = new NDependServicesProvider();

   internal static void Main() {
      var dirOld = @"C:\MyProduct\OldAssembliesDir".ToAbsoluteDirectoryPath();
      var dirNew = @"C:\MyProduct\NewAssembliesDir".ToAbsoluteDirectoryPath();

      Console.WriteLine("Analyzing assemblies in " + dirOld.ToString());
      var codeBaseOld = GetCodeBaseFromAsmInDir(dirOld, TemporaryProjectMode.TemporaryOlder);
      Console.WriteLine("Analyzing assemblies in " + dirNew.ToString());
      var codeBaseNew = GetCodeBaseFromAsmInDir(dirNew, TemporaryProjectMode.TemporaryNewer);

      var compareContext = codeBaseNew.CreateCompareContextWithOlder(codeBaseOld);

      // So much more can be done by exploring fine-grained diff in codeBases and compareContext
      Dump("Added assemblies", codeBaseNew.Assemblies.Where(compareContext.WasAdded));
      Dump("Removed assemblies", codeBaseOld.Assemblies.Where(compareContext.WasRemoved));
      Dump("Assemblies with modified code", codeBaseNew.Assemblies.Where(compareContext.CodeWasChanged));
      Console.Read();
   }

   internal static ICodeBase GetCodeBaseFromAsmInDir(IAbsoluteDirectoryPath dir, TemporaryProjectMode temporaryProjectMode) {
      Debug.Assert(dir.Exists);
      var dotNetManager = s_NDependServicesProvider.DotNetManager;
      var assembliesPath = dir.ChildrenFilesPath.Where(dotNetManager.IsAssembly).ToArray();
      Debug.Assert(assembliesPath.Length > 0); // Make sure we found assemblies
      var projectManager = s_NDependServicesProvider.ProjectManager;
      IProject project = projectManager.CreateTemporaryProject(assembliesPath, temporaryProjectMode);

      // In PowerTool context, better call:
      // var analysisResult = ProjectAnalysisUtils.RunAnalysisShowProgressOnConsole(project);
      var analysisResult = project.RunAnalysis();
      return analysisResult.CodeBase;
   }

   internal static void Dump(string title, IEnumerable<IAssembly> assemblies) {
      Debug.Assert(!string.IsNullOrEmpty(title));
      Debug.Assert(assemblies != null);
      Console.WriteLine(title);
      foreach (var @assembly in assemblies) {
         Console.WriteLine("   " + @assembly.Name);
      }
   }
}
Run Code Online (Sandbox Code Playgroud)