---
title: AntでPMDを実行する
author: aterai
pubdate: 2012-01-25T00:21:32+09:00
description: ソースコードの静的解析を行うPMDをAntから実行するためのターゲットや、ruleset.xmlのサンプルなど。
---
#contents
* Summary [#summary]
`Ant`から`PMD`を実行します。
* サンプルターゲット [#target]
- `build.xml`に記述するターゲットサンプル
#code{{
<target name="pmd">
<taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask" />
<mkdir dir="${build.reports}" />
<pmd rulesetfiles="ruleset.xml" encoding="${compile.encoding}"
cacheLocation="${build.reports}/pmd/pmd.cache">
<sourceLanguage name="java" version="${compile.source}"/>
<classpath refid="project.class.path" />
<formatter type="xml" toFile="${build.reports}/pmd.xml" />
<formatter type="text" toConsole="true" />
<fileset dir="${src.dir}" excludes="**/module-info.java" includes="**/*.java" />
</pmd>
</target>
}}
- `PMD 6.0.0`向けの`ruleset.xml`サンプル
#code{{
<?xml version="1.0"?>
<ruleset name="Custom ruleset"
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 http://pmd.sourceforge.net/ruleset_2_0_0.xsd">
<description>This ruleset checks my code for bad stuff</description>
<rule ref="category/java/bestpractices.xml">
<exclude name="AvoidPrintStackTrace" />
<exclude name="SystemPrintln" />
</rule>
<rule ref="category/java/codestyle.xml">
<exclude name="AtLeastOneConstructor" />
<exclude name="OnlyOneReturn" />
<exclude name="ShortVariable" />
<exclude name="LongVariable" />
<exclude name="LocalVariableCouldBeFinal" />
<exclude name="MethodArgumentCouldBeFinal" />
</rule>
<rule ref="category/java/design.xml">
<exclude name="DataClass" />
<exclude name="LawOfDemeter" />
<exclude name="LoosePackageCoupling" />
<exclude name="NcssCount" />
</rule>
<!-- rule ref="category/java/documentation.xml" / -->
<rule ref="category/java/errorprone.xml">
<exclude name="BeanMembersShouldSerialize" />
<exclude name="DataflowAnomalyAnalysis" />
<exclude name="DoNotCallSystemExit" />
<exclude name="NullAssignment" />
<exclude name="UseProperClassLoader" />
</rule>
<rule ref="category/java/multithreading.xml">
<exclude name="DoNotUseThreads" />
</rule>
<rule ref="category/java/performance.xml">
<exclude name="AvoidInstantiatingObjectsInLoops" />
</rule>
</ruleset>
}}
* Description [#explanation]
* Description [#description]
+ [https://pmd.github.io/ PMD]からダウンロードした`pmd-bin-x.x.zip`の`lib`以下にある`pmd-x.x.jar`などの`jar`ファイルを`%ANT_HOME%\lib`にコピー、もしくは環境変数`%PMD_HOME%`を設定した場所に`lib`を展開
+ 上記のような`pmd`ターゲットを`build.xml`に追加し、`ant pmd`で`pmd.xml`を生成、`jenkins`の`PMD`プラグインなどで読み込む
+ チェックするルールをカスタマイズする場合は、`ruleset.xml`を記述してこれを`rulesetfiles`属性で参照する
*** toFile or toConsole [#w61eb2ba]
- `FindBugs`のように、`xml`ファイルとコンソールの両方に結果を表示する場合は、`formatter`要素の`toFile`と`toConsole`属性を併用する
-- `ant -v pmd`は、出力される情報が多すぎる
- [https://pmd.github.io/pmd-5.3.3/usage/ant-task.html PMD - Ant Task] にあるように、`<formatter type="html" toFile="pmd_report.html" toConsole="true"/>`や、`<formatter type="xml" toFile="${build.reports}/pmd.xml" toConsole="true" />`と`toFile`と`toConsole`を指定しても、ファイルにのみ結果が書き出されて、コンソールには何も出力されない
- `<formatter type="text" toConsole="true" />`と、`toFile`属性なしで`toConsole="true"`とする必要がある?
-- `<formatter type="text" toConsole="false" />`は、`toFile or toConsole needs to be specified in Formatter`となって、`BUILD FAILED`
- ファイルとコンソールの両方に出力する場合は、`formatter`を複数指定しなくてはならない?
* 更新履歴 [#changelog]
** PMD 6.5.0 [#d87ca01e]
- %%`*`を使用したインポート文と同名のメソッドが存在すると、[https://pmd.github.io/pmd-6.5.0/pmd_rules_java_codestyle.html#unnecessaryfullyqualifiedname UnnecessaryFullyQualifiedName]ルールで誤検出が発生する%%
-- [https://github.com/pmd/pmd/pull/1220 java Fix unnecessaryFQCN false positive with import-on-demand #1220]、`6.6.0`で修正済
- 例:
-- `import java.util.stream.*;`で、`Arrays.stream(...)`が誤って警告
-- `import java.time.format.*;`で、`LocalDate#format(...)`が誤って警告
-- `import javax.swing.undo.*;`で、`UndoManager#undo()`が誤って警告
> pmd.bat -dir C:\tmp -R category/java/codestyle.xml/UnnecessaryFullyQualifiedName
C:\tmp\Test.java:9: Unnecessary use of fully qualified name 'Arrays.stream' due to existing import 'java.util.stream.*'
#code{{
import java.awt.Component;
import java.awt.Container;
import java.util.Arrays;
import java.util.stream.*;
// import java.util.stream.Stream; // *を使用しない場合は誤検出は発生しない
public final class Test {
public static Stream<Component> stream(Container parent) {
return Arrays.stream(parent.getComponents())
.filter(Container.class::isInstance)
.map(c -> stream(Container.class.cast(c)))
.reduce(Stream.of(parent), Stream::concat);
}
}
}}
** PMD 6.0.0 [#f08801d0]
- `rulesets/java/basic.xml`などが非推奨になって、`category/java/bestpractices.xml`などが使用可能になった
- キャッシュディレクトリの指定がない場合、警告されるようになった
-- [https://pmd.github.io/pmd/pmd_userdocs_getting_started.html#incremental-analysis Getting Started | PMD Source Code Analyzer]
- %%以下のような`JDK 1.8.0`で有効な総称型の型引数の省略が誤判定されて、`Error while parsing ...`とエラーになる%%
-- `6.1.0`で修正済み [https://github.com/pmd/pmd/issues/888 java ParseException occurs with valid '<>' in Java 1.8 mode · Issue #888 · pmd/pmd]
** PMD 5.1.3 [#se3b451a]
- %%上記の誤検出は`pmd-bin-5.1.3-SNAPSHOT.zip`ですべて修正済み。ただし新規で今まで問題なかった以下のようなコードの`makeButton(Action action, String title)`が`UnusedPrivateMethod`となってしまう%% 修正済み
C:\temp>C:\pmd-bin-5.1.3-SNAPSHOT\bin\pmd.bat -dir . -rulesets java-unusedcode
C:\temp\MenuBarFactoryTest.java:44: Avoid unused private methods such as 'makeButton(Action,String)'.
#code{{
import java.awt.*;
import java.util.Arrays;
import java.util.List;
import javax.swing.*;
import javax.swing.text.DefaultEditorKit;
public final class MenuBarFactoryTest {
public static JMenuBar makeMenuBar() {
JMenuBar mb = new JMenuBar();
mb.add(makeFileMenu());
return mb;
}
private static JMenu makeFileMenu() {
JMenu menu = new JMenu("File");
menu.add(makeEditMenuItem(makeEditButtonBar(Arrays.asList(
makeButton(new DefaultEditorKit.CutAction(), "Cut"),
makeButton(new DefaultEditorKit.CopyAction(), "Copy"),
makeButton(new DefaultEditorKit.PasteAction(), "Paste")))));
return menu;
}
private static JMenuItem makeEditMenuItem(final JComponent edit) {
JMenuItem item = new JMenuItem("Edit") {
@Override public Dimension getPreferredSize() {
Dimension d = super.getPreferredSize();
d.width += edit.getPreferredSize().width;
d.height = Math.max(edit.getPreferredSize().height, d.height);
return d;
}
};
item.setEnabled(false);
item.setLayout(new GridBagLayout());
item.add(edit);
return item;
}
private static JComponent makeEditButtonBar(List<AbstractButton> list) {
JPanel p = new JPanel(new GridLayout(1, list.size(), 0, 0));
for (AbstractButton b : list) {
p.add(b);
}
p.setBorder(BorderFactory.createEmptyBorder(4, 10, 4, 10));
p.setOpaque(false);
return p;
}
private static AbstractButton makeButton(Action action, String title) {
JButton b = new JButton(action);
b.setText(title);
return b;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.setJMenuBar(makeMenuBar());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
}}
** PMD 5.1.2 [#xfc6bb0b]
- %%`PMD 5.1.2`で以下のようなコードが、`Avoid unused private methods such as 'makePanel(String,JComponent)'.`と警告されてしまう?%% 修正済み
-- [https://sourceforge.net/p/pmd/bugs/1228/ PMD / Issues / #1228 UnusedPrivateMethod returns false positives]
-- `C:\pmd-bin-5.1.2\bin\pmd.bat -dir /root/directory/for/sources -rulesets java-unusedcode`
#code{{
import java.awt.*;
import javax.swing.*;
public class Foo {
public JComponent makeUI() {
Box box = Box.createVerticalBox();
JTextField field = new JTextField();
box.add(makePanel("aaa", field));
return box;
}
private static JPanel makePanel(String title, JComponent c) {
JPanel p = new JPanel(new BorderLayout());
p.setBorder(BorderFactory.createTitledBorder(title));
p.add(c);
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new Foo().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
}}
** PMD 5.0.0 [#wf0618ac]
- `RuleSet short names now require a language prefix, 'basic' is now 'java-basic', and 'rulesets/basic.xml' is now 'rulesets/java/basic.xml'`
-- 上記のサンプルで、`rulesetfiles`属性の`basic`などを、`java-basic`に変更
- `Removed -targetjdk use -version {name} {version} instead`
-- 上記のサンプルから、`targetjdk="${compile.source}"`を削除
* 警告 [#rules]
** GodClass [#GodClass]
- [https://pmd.github.io/pmd/pmd_rules_java_design.html#godclass GodClass - Design | PMD Source Code Analyzer]
#code{{
/**
* Very high threshold for WMC (Weighted Method Count). See: Lanza.
* Object-Oriented Metrics in Practice. Page 16.
*/
private static final int WMC_VERY_HIGH = 47;
/**
* Few means between 2 and 5. See: Lanza. Object-Oriented Metrics in
* Practice. Page 18.
*/
private static final int FEW_THRESHOLD = 5;
/**
* One third is a low value. See: Lanza. Object-Oriented Metrics in
* Practice. Page 17.
*/
private static final double ONE_THIRD_THRESHOLD = 1.0 / 3.0; //0.33333
// ...
if (wmcCounter >= WMC_VERY_HIGH && atfdCounter > FEW_THRESHOLD && tcc < ONE_THIRD_THRESHOLD) {
// ...
}}
** SimplifiedTernary [#SimplifiedTernary]
- [https://pmd.github.io/pmd/pmd_rules_java_design.html#simplifiedternary SimplifiedTernary - Design | PMD Source Code Analyzer]
#code{{
public class SimplifiedTernaryTest {
public static void main(String... args) {
//boolean condition = true;
boolean b1 = condition ? true : something(); // can be as simple as condition || something();
boolean b2 = condition ? false : something(); // can be as simple as !condition && something();
boolean b3 = condition ? something() : true; // can be as simple as !condition || something();
boolean b4 = condition ? something() : false; // can be as simple as condition && something();
}
private static boolean something() {
return true;
}
}
}}
- 例:
#code{{
model2 = new BasicDirectoryModel(getFileChooser()) {
@Override public boolean renameFile(File oldFile, File newFile) {
//return oldFile.canWrite() ? super.renameFile(oldFile, newFile) : false;
return oldFile.canWrite() && super.renameFile(oldFile, newFile);
}
};
}}
- `PMD 5.4.0`
-- %%`PMD 5.4.0`では、以下のような三項演算子も修正可と誤認識される%% `5.4.2`で修正済
--- `repaintRect = prevRollover ? r.union(table.getCellRect(prevRow, prevCol, false)) : r;`
-- [http://sourceforge.net/p/pmd/bugs/1424/ PMD Issues #1424 False negative with ternary operator]
* Jenkins [#jenkins]
- [[Jenkins]]に移動
* Reference [#reference]
- [https://pmd.github.io/ PMD]
- [http://d.hatena.ne.jp/guess880/20121014/1350200644 PMD5.0.0のルールを和訳しながら読む - 1 - Design Ruleset - 開発メモ]
* Comment [#comment]
#comment
#comment