当今的软件开发须要应用许多不同的工具和技术来确保代码品质和稳定性。PMD是一个风行的动态代码剖析工具,能够帮忙开发者在编译代码之前发现潜在的问题。在本文中,咱们将探讨如何在Gradle中应用PMD,并介绍一些最佳实际。
什么是PMD?
PMD是一个用于Java代码的动态代码剖析工具。它能够帮忙开发者找出潜在的问题,如代码反复、未应用的变量、谬误的异样解决等。PMD反对多种规定,能够依据具体我的项目的须要进行配置。其工作原理参考How PMD Works。
PMD反对通过命令行界面(CLI, Command Line Interface for batch scripting)和其余多种集成形式,比方Maven、Gradle、Java API等等。
PMD在Gradle中配置和应用
Gradle中自带了PMD插件,插件的默认版本能够通过源码DEFAULT_PMD_VERSION晓得。应用和配置能够参考The PMD Plugin,页面左上角能够抉择Gradle版本,确保查看的版本和你应用的Gradle版本统一,因为很多PMD的配置属性或者性能不肯定在每个版本都有。
通过页面左上角选了其余版本后跳转的地址是Gradle文档的首页,而不是PMD插件的文档页。咱们能够通过批改The PMD Plugin链接中的8.0.2
为其余版本号即可跳转到对应Gradle版本蕴含的PMD插件文档的页面。比方:
以后最新版:https://docs.gradle.org/current/userguide/pmd_plugin.html
7.3.3版本:https://docs.gradle.org/7.3.3/userguide/pmd_plugin.html
在我的项目build.gradle文件中减少以下内容利用插件和扩大PMD,参考Usage和Configuration,更多的配置属性能够参考PmdExtension。
plugins { id 'pmd'}pmd { // 是否将 PMD 后果输入到终端 consoleOutput = true // 要应用的PMD版本 toolVersion = "6.21.0" // 规定优先级阈值,低于这个优先级则会被疏忽 rulesMinimumPriority = 5 // 应用的规定集配置文件门路 ruleSets = ["category/java/errorprone.xml", "category/java/bestpractices.xml"]}
插件会生成两个次要的PMD TaskpmdMain和pmdTest别离对main和test两个我的项目源文件目录应用PMD进行代码查看。
找到IDEA Gradle窗口 > Tasks > other
,双击生成的Task;或者在我的项目根目录运行./gradlew pmdMain
都能够运行PMD。查看后果将输入到终端中(前提是配置了consoleOutput = true
),违反了PMD规定的类会给出残缺的跳转门路以及规定提示信息。
最初还会给出一个报告的地址,内容蕴含了输入到终端的信息。Problem列出了规定的提醒,点击能够跳转到PMD规定形容文档对应的地位。
Gradle PMD Plugin扩大属性
在这里咱们将PMD插件的扩大属性作用进行阐明,参考PmdExtension,这个文档具体阐明了各个属性的作用、默认值和配置示例。如果文档形容的不是很分明也能够参考PMD CLI options的对应形容。
consoleOutput
是否将后果输入到终端(System.out
)允许值为true|false
。
ignoreFailures
如果呈现了正告,是否容许持续构建,允许值为true|false
。
配置为否(false),在执行build的时候(build工作中默认蕴含了pmdMain和pmdTest),如果发现了代码有违反规定,将会中断构建过程;配置为是(true),将不会中断构建,只是输入报告信息。
maxFailures
进行构建前容许的最大失败次数。
incrementalAnalysis
是否开启增量剖析,允许值为true|false
。在pmd docs Incremental Analysis中详细描述了增量剖析的相干信息。简略来说,开启了增量剖析,PMD会缓存剖析数据和后果,后续剖析仅查看那些新的/已更改的文件,以此显著缩小剖析的工夫,在Gradle中,这个性能应用PMD6.0.0
及以上版本才有。
然而有一些状况会导致增量剖析的缓存生效:应用PMD的版本产生了变动;应用的规定集已更改;被剖析的代码的类门路已更改;被剖析代码依赖的库的类门路已更改。具体参考When is the cache invalidated?
在以上前提下,即便切换分支缓存也是无效的,甚至还反对在不同的机器重复使用缓存文件。参考Can I reuse a cache created on branch A for analyzing my project on branch B? 和Can I reuse a cache file across different machines?
reportsDir
报告生成的门路。
ruleSetFiles
要应用的自定义规定集文件门路,能够在files()
中填多个门路。
ruleSetFiles = files("config/pmd/myRuleSet.xml")
ruleSetConfig
跟ruleSetFiles
的作用一样,不过只能填一个文件门路。
ruleSetConfig = resources.text.fromFile("config/pmd/myRuleSet.xml")
ruleSets
指定应用的规定集,默认值为["category/java/errorprone.xml"]。
倡议如果配置了ruleSetFiles
或者ruleSetConfig
,就将ruleSets
配置为空(ruleSets = []
),免得相互烦扰,官网文档Custom ruleset给出的例子也是如此。
ruleSets = ["category/java/errorprone.xml", "category/java/bestpractices.xml"]
rulesMinimumPriority
每个规定都有个优先级,是从 1 到 5 的整数,其中 1 是最高优先级,参考Message and priority overriding,每个规定的优先级参考[Java Rules。rulesMinimumPriority
的作用是配置报告的最低优先级,低于这个优先级的规定将被疏忽。比方配置rulesMinimumPriority = 4
,优先级为 5 的规定将被疏忽。
sourceSets
作为 check
和 build
工作的一部分进行剖析的源代码汇合,配置形式参考SourceSet。
targetJdk
PMD应用的JDK版本。有些规定可能会要求JDK的最低或者最高版本,具体要求参考Java Rules。
threads
PMD 运行时应用的线程数。
toolVersion
要应用的PMD的版本。
为我的项目自定义适合的规定集
规定分类和查找
PMD能检测的语音有很多种(前面内容以Java为例),针对不同的语音,PMD内置了很多检测规定,并归为了以下几个类别:
- 最佳实际(Best Practices):这些规定执行广泛承受的最佳实际。
- 代码格调(Code Style):这些规定强制执行特定的编码格调。
- 设计(Design):帮忙您发现设计问题的规定。
- 文档(Documentation):这些规定与代码文档无关。
- 容易出错(Error Prone):用于检测损坏、极度凌乱或容易呈现运行时谬误的结构的规定。
- 多线程(Multithreading):这些是在解决多个执行线程时标记问题的规定。
- 性能(Performance):标记次优代码的规定。
- 安全性(Security):标记潜在安全漏洞的规定。
在Java Rules列出了所有相干的规定,点击蓝色字符能够跳转到规定的详细描述页面。
下图是规定AbstractClassWithoutAbstractMethod文档形容的信息,其余规定的形容可能还会蕴含JDK版本的要求,其余可配置属性等等。
须要留神有的规定可能被标记为Deprecated
代表被弃用了。
配置规定集
咱们能够编辑XML格局的规定集文件,指定咱们我的项目要执行的规定,参考Making rulesets。上面是没有蕴含任何规定的规定集文件的模版。
<?xml version="1.0"?><ruleset name="Custom Rules" xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> <description> My custom rules </description> <!-- Your rules will come here --></ruleset>
从上文咱们能够晓得PMD内置的每个规定都会提供援用实例,咱们援用单个规定的时候,只须要将示例的XML代码复制到规定集文件中即可。
<rule ref="category/java/errorprone.xml/EmptyCatchBlock" />
从ref
中填写的门路category/java/bestpractices.xml/AbstractClassWithoutAbstractMethod
咱们能够显著看到它是依照内置规定集文件门路/规定名称的格局
组织的,一个内置规定集文件对应了一个分类。
咱们能够援用内置规定集文件实现批量引入分类下的所有规定,每个分类对应的XML文件名能够参考GitHub pmd-java resources。再通过exclude
指定规定的名称来排除某些规定。
<rule ref="category/java/codestyle.xml"> <exclude name="WhileLoopsMustUseBraces"/> <exclude name="IfElseStmtsMustUseBraces"/></rule>
咱们能够应用exclude-pattern
排除某些文件,使其不被PMD查看,也能够应用include-pattern
蕴含的形式。如果两种形式都蕴含雷同的文件,最终这个文件会被PMD查看。
<?xml version="1.0"?><ruleset name="myruleset" xmlns="http://pmd.sourceforge.net/ruleset/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd"> <description>My ruleset</description> <exclude-pattern>.*/some/package/.*</exclude-pattern> <exclude-pattern>.*/some/other/package/FunkyClassNamePrefix.*</exclude-pattern> <include-pattern>.*/some/package/ButNotThisClass.*</include-pattern> <!-- Rules here ... --></ruleset>
规定集文件编辑好后,应用ruleSetFiles
或者ruleSetConfig
配置门路。比方上面配置的意思是指向了我的项目根目录下的/code-analysis/pmd/rulesets/custom-rule.xml
。
ruleSetFiles = files("${project.rootDir}/code-analysis/pmd/rulesets/custom-rule.xml"
配置规定
规定援用的同时,咱们能够笼罩其原有的一些配置,比方提醒音讯message
和优先级priority
。
<rule ref="category/java/errorprone.xml/EmptyCatchBlock" message="Empty catch blocks should be avoided" > <priority>5</priority></rule>
某些规定可能有特定的属性,咱们也能够将其笼罩。这些特定的属性Java Rules中都有提供,比方上面这个例子参考NPathComplexity。
<rule ref="category/java/design.xml/NPathComplexity"> <properties> <property name="reportLevel" value="150"/> </properties></rule>
有些属性能够提供多个值,这种状况下能够通过分隔符来提供,比方竖线(|
)或逗号(,
)。
<property name="legalCollectionTypes" value="java.util.ArrayList|java.util.Vector|java.util.HashMap"/>
克制正告
有时候PMD可能会产生误报,这种时候咱们能够通过克制正告让PMD跳过对这些代码的查看。
从Java 1.5开始能够应用注解@SuppressWarnings
来标记类或者办法。
@SuppressWarnings('PMD')
克制所有PMD的正告。@SuppressWarnings("PMD.UnusedLocalVariable")
克制规定UnusedLocalVariable
的正告。@SuppressWarnings({"PMD.UnusedLocalVariable", "PMD.UnusedPrivateMethod"})
抑规定UnusedLocalVariable
和UnusedPrivateMethod
的正告。@SuppressWarnings("unused")
JDK外面的unused
PMD也恪守,克制所有跟未应用相干的正告。比方:UnusedLocalVariable
和UnusedPrivateMethod
。
在正告提醒的代码行的开端加上正文// NOPMD
也能够克制这一行引起的正告,参考NOPMD。
在规定集文件中也能够配置要克制正告的文件,匹配的形式能够是正则表达式或者XPath,具体能够理解The property violationSuppressRegex和The property violationSuppressXPath。
第三方规定集
除了PMD内置的规定集,咱们还能够引入第三方规定集。在3rd party rulesets中列出了一些,还有阿里Java开发标准p3c也基于PMD开发一套规定集,从它的pom.xml能够理解到是基于PMD6.15.0
版本。
参考Dependency management引入规定集依赖,在规定集配置中引入提供的规定即可。
dependencies { pmd "com.alibaba.p3c:p3c-pmd:2.1.1"}
须要留神的是,第三方的规定集很可能没有依照PMD内置规定集那样分类,它们提供的规定配置文件目录也可能不一样,比方p3c的规定配置文件都在/resources/rulesets/目录下并单独定义了一套分类。
其余技巧
PMD的最新官网文档地址是:https://docs.pmd-code.org/latest/pmd_userdocs_tools.html。链接中的latest
对应了版本号,指向的是以后最新版本,如果想查看其余版本的文档,批改为对应的版号即可。比方6.39.0
版本的链接为:https://docs.pmd-code.org/pmd-doc-6.39.0/index.html。不过可能只有比拟新的一些版本能力看到对应的文档。
官网提供了一个PMD的最佳实际能够理解下。
PMD还有跟特定语言相干的文档,比方Java support,外面有反对的JDK版本等信息。
如果应用过程中遇到了问题,能够参考Getting Help从这些网站外面寻找帮忙github discussions、github issues、stackoverflow tagged pmd。
PMD官网文档还提供了Copy/Paste Detector (CPD)关信息,CPD能够用于检测反复代码。还提及了Duplicate Code教咱们遇到反复代码如何打消,以及一个对于设计模式的网站Design Patterns。
对于PMD这个名字,并没有非凡的含意,作者纯正只是感觉这几个字母放一起作为名称挺好的,来自What does 'PMD' mean?
参考
GitHub - pmd
PMD - docs
Gradle - The PMD Plugin