【持續整合】如何用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.properties
的rule.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
,重寫父類Sensor
的analyse
方法,定位到這個方法,繼續開始修改.
因為添加了一個新的倉庫,而且從原始碼得知,一開始程式碼只是支援java的規則,全部都是寫死的,現在需要新增xml. 修改該類的shouldExecuteOnProject
和hasFilesToCheck
,這裡顯然也是和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
方法初始化PMDConfiguration
和SourceCodeProcessor
兩個物件.SourceCodeProcessor
是傳入InputStream
和rulesets
來分析每個檔案的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即可