1. 程式人生 > >【持續整合】如何用sonar-pmd外掛整合pmd-xml的規則

【持續整合】如何用sonar-pmd外掛整合pmd-xml的規則

需求 sonar-pmd外掛只有添加了pmd的java規則,現在需要新增pmd的xml規則,更準確是新增自定義的xml規則.

步驟: 為了更好整合和示範,選擇前人已整合p3c的sonar-pmd外掛. url: https://github.com/mrprince/sonar-p3c-pmd git clone 到本地 整合分為兩個環節: 1.規則配置 2.原始碼修改

規則配置 該外掛首先依賴PmdRulesDefinition對倉庫repository進行定義,從

extractRulesData(repository, "/org/sonar/plugins/pmd/rules.xml", "/org/sonar/l10n/pmd/rules/pmd");

方法內部,可以得知其是讀取外部配置來初始化pmd的rules. 一共有四處需要配置: /org/sonar/plugins/pmd/rules.xml /org/sonar/l10n/pmd/rules/pmd /com/sonar/sqale/pmd-model.xml /org/sonar/l10n/pmd.properties 對於配置,沒有什麼多說的,原則就是模仿! l10n下的html是需要和rule的key一致.作用是sonar的rule頁面展示. pmd.propertiesrule.pmd-xml.MistypedCDATASection.name中,rule.pmd-xml代表repository名字,需要一致.

原始碼修改

PmdRulesDefinition類的define(Context context)裡可以看到extractRulesData是讀取配置資訊,如果想分別管理不同型別的規則,例如pmd原生和p3c規則,就可以分別配置,另外讀取. NewRepository類是和sonar的規則語言繫結的,所以另外增加一個新的repository物件,新增Xml.Key pom.xml 新增依賴:

<dependency>
          <groupId>org.sonarsource.sonar-xml-plugin</groupId>
          <artifactId>sonar-xml-plugin</artifactId>
          <version>1.3</version>
          <scope>provided</scope>
</dependency>
NewRepository xmlRepository = context
              .createRepository(PmdConstants.XML_REPOSITORY_KEY, Xml.KEY)
              .setName(PmdConstants.XML_REPOSITORY_NAME);

模仿之前的repository設定,將一些常量寫在PmdConstants類裡,後續還會經常用到這個string值.

入口:PmdSensor 真正的入口是PmdSensor,重寫父類Sensoranalyse方法,定位到這個方法,繼續開始修改. 因為添加了一個新的倉庫,而且從原始碼得知,一開始程式碼只是支援java的規則,全部都是寫死的,現在需要新增xml. 修改該類的shouldExecuteOnProjecthasFilesToCheck,這裡顯然也是和sonar介面對接的方法,用於判斷是否執行的.

@Override
  public boolean shouldExecuteOnProject(Project project) {
    return (hasFilesToCheck(Type.MAIN, PmdConstants.REPOSITORY_KEY))
      || (hasFilesToCheck(Type.TEST, PmdConstants.TEST_REPOSITORY_KEY))
            || (hasFilesToCheck(Type.MAIN, PmdConstants.XML_REPOSITORY_KEY)) ;
  }

  private boolean hasFilesToCheck(Type type, String repositoryKey) {
    FilePredicates predicates = fs.predicates();
    Iterable<File> files = fs.files(predicates.or(
            predicates.and(predicates.hasLanguage(Xml.KEY),predicates.hasType(type)),
            predicates.and(predicates.hasLanguage(Java.KEY),
      predicates.hasType(type))));
    return !Iterables.isEmpty(files) && !profile.getActiveRulesByRepository(repositoryKey).isEmpty();
  }

新增了xml的判斷,Type.MAIN是代表掃描的source是在src/main下,和src/test對應.FilePredicates很像之前接觸的FileFilter類,使用方式也很像,為了有xml或java時都返回true,就在外面寫一個predicates.or,這種類的設計感覺除了寫著麻煩,實際上很好理解. 接下來從analyse方法一步步進去,遇到有硬編碼和判斷語言型別的地方就著手修改.

PmdExecutor 執行pmd的地方.

private Report executePmd(URLClassLoader classLoader) {
    ...
    PmdTemplate pmdFactory = createPmdTemplate(classLoader);
    executeRules(pmdFactory, context, javaFiles(Type.MAIN), PmdConstants.REPOSITORY_KEY);
     ...
    return report;
  }

可以看到,是通過PmdTemplate來執行rules,模仿它新增一句: executeRules(pmdFactory, context, xmlFiles(Type.MAIN), PmdConstants.XML_REPOSITORY_KEY); 並且提供自己的xmlFiles方法,基於語言過濾. 為了一探究竟,順便看看PmdTemplate類 從程式碼看是通過create方法初始化PMDConfigurationSourceCodeProcessor兩個物件.SourceCodeProcessor是傳入InputStreamrulesets來分析每個檔案的PMD核心分析類. 通過languageVersions來控制可使用何種語言的rule. 原先是隻添加了java的LanguageModule,現在需要增加xml部分.對pmd原始碼進行分析,即可知道new XmlLanguageModule().getVersion("")可獲得xml的languageVersion.

PmdViolationRecorder 輸出 最後輸出的地方仍舊需要修改.

private Rule findRuleFor(RuleViolation violation) {
    String ruleKey = violation.getRule().getName();
    Rule xmlRule = ruleFinder.findByKey(PmdConstants.XML_REPOSITORY_KEY, ruleKey);
      if (xmlRule != null) {
          return xmlRule;
      }
      Rule rule = ruleFinder.findByKey(PmdConstants.REPOSITORY_KEY, ruleKey);
      if (rule != null) {
          return rule;
      }
      return ruleFinder.findByKey(PmdConstants.TEST_REPOSITORY_KEY, ruleKey);
  }

這裡需要新增 xmlRule,否則是找一個普通javaRule,沒有則返回java的testRule

原始碼改的差不多了.但test裡的類還需要修改,如果直接執行,會有部分類報錯,因為它test裡也是硬編碼了java的.當然也可以選擇-Dmaven.test.skip=true.

本文對應github地址: https://github.com/phinehasz/sonar-pmd-xml-plugin 最快捷的辦法,下載我的程式碼,在其基礎上新增配置.

git clone https://github.com/phinehasz/sonar-pmd-xml-plugin
mvn package
放到sonar{version}\extensions\plugins 
restart sonar即可