Skip to content

Commit b181fa4

Browse files
authored
Various PMD refinements (#5435)
Pull request: #5435
1 parent ee8e0d6 commit b181fa4

File tree

4 files changed

+59
-42
lines changed

4 files changed

+59
-42
lines changed

example/javalib/linting/6-pmd/build.mill

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,53 +3,71 @@
33
// PMD is a source code analyzer for Java that finds common programming flaws like unused variables,
44
// empty catch blocks, unnecessary object creation, and more.
55
//
6-
// To use this plugin in a Java module:
6+
// To customize this plugin in a Java module:
77
//
88
// 1. Extend JavaModule with PmdModule.
9-
// 2. [Optional] Define a PMD ruleset file(s) with `pmdRulesets` (default is `pmd-rules.xml` in root).
10-
// 3. [Optional] Define a version with `pmdVersion`.
11-
// 3. Run the `pmd` command.
9+
// 2. Define a PMD ruleset file(s) with `pmdRulesets` (default is `pmd-rules.xml` in root).
10+
// 3. Define the PMD version with `pmdVersion`.
1211

1312
package build
1413

1514
import mill._, javalib._
1615
import mill.javalib.pmd.PmdModule
1716

18-
object `package` extends JavaModule with PmdModule {
17+
object `package` extends JavaModule, PmdModule {
1918
def pmdVersion = "7.15.0"
2019
}
2120

22-
// Here's an example of simple PMD ruleset file, which is used to setup analysis rules.
23-
//
24-
// Be careful, rule formats can differ for different PMD versions.
21+
// Here's an example of a simple PMD ruleset file and a single Java source file.
22+
2523
/** See Also: pmd-ruleset.xml */
24+
/** See Also: src/foo/EmptyCatchBlock.java */
25+
26+
// CAUTION: Be careful, rule formats can differ between major PMD versions.
27+
28+
// You can either run the `pmd` command on selected modules
29+
// or use the external module `mill.javalib.pmd/`.
2630

2731
/** Usage
28-
> ./mill pmd -s true -v false # default format is text
29-
...src/EmptyCatchBlock.java:7: EmptyCatchBlock: Avoid empty catch blocks...
32+
33+
> ./mill pmd -s true -v false # <1>
34+
Running PMD ...
35+
Writing PMD output to .../out/pmd.dest/pmd-output.text ...
36+
...src/foo/EmptyCatchBlock.java:7: EmptyCatchBlock: Avoid empty catch blocks...
3037
PMD found 1 violation(s)
31-
*/
3238

39+
> ./mill mill.javalib.pmd/ # <2>
40+
error: Running PMD ...
41+
error: Writing PMD output to .../out/mill/javalib/pmd/PmdModule/pmd.dest/pmd-output.text ...
42+
error: 1 tasks failed
43+
error: mill.javalib.pmd.PmdModule.pmd PMD found 1 violation(s)
44+
45+
*/
46+
// <1> `-s` shows the results on the console, `-v` does not fail the build
47+
// <2> Use the external `mill.javalib.pmd` module with default configuration
48+
//
3349
// The `pmd` command accepts several options to customize its behavior. Mill supports:
3450
//
35-
// * --stdout / -s : Enable output to stdout. False by default. (PMD will write output to a file regardless of this option).
51+
// * `--stdout` / `-s` : Enable output to stdout. False by default. (PMD will write output to a file regardless of this option).
3652
//
37-
// * --format / -f : Output format of the report (`text`, `xml`, `html`). Defaults to `text`.
53+
// * `--format` / `-f` : Output format of the report (`text`, `xml`, `html`). Defaults to `text`.
3854
//
39-
// * --fail-on-violation / -v : Fail if violations are found (true by default).
55+
// * `--fail-on-violation` / `-v` : Fail if violations are found (true by default).
4056
//
4157
// For a full list of PMD options, see the PMD documentation:
4258
// https://pmd.github.io/pmd/pmd_userdocs_cli_reference.html.
4359
//
4460
// Here's an example of producing HTML report:
61+
4562
/** Usage
46-
> ./mill pmd -f "html" -s true -v false
63+
> ./mill pmd -f "html" -s true -v false
64+
Writing PMD output to .../out/pmd.dest/pmd-output.html ...
4765
...<html><head><title>PMD</title></head><body>...
4866
...<center><h3>PMD report</h3></center><center><h3>Problems found</h3></center><table align="center" cellspacing="0" cellpadding="3"><tr>...
4967
...<th>#</th><th>File</th><th>Line</th><th>Problem</th></tr>...
5068
...<tr bgcolor="lightgrey">...
5169
...<td align="center">1</td>...
52-
...<td width="*%">...src/EmptyCatchBlock.java</td>...
70+
...<td width="*%">...src/foo/EmptyCatchBlock.java</td>...
5371
...<td align="center" width="5%">7</td>...
5472
...<td width="*"><a href="https://docs.pmd-code.org/pmd-doc-7.15.0/pmd_rules_java_errorprone.html#emptycatchblock">Avoid empty catch blocks</a></td>...
5573
...</tr>...

example/javalib/linting/6-pmd/src/EmptyCatchBlock.java renamed to example/javalib/linting/6-pmd/src/foo/EmptyCatchBlock.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package src;
1+
package foo;
22

33
public class EmptyCatchBlock {
44
public void bar() {

libs/jvmlib/src/mill/javalib/pmd/PmdModule.scala

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,35 @@ import mill.api.{Discover, ExternalModule, TaskCtx}
55
import mill.jvmlib.api.Versions
66
import mill.scalalib.scalafmt.ScalafmtModule.sources
77
import mill.scalalib.{CoursierModule, Dep, DepSyntax, OfflineSupportModule}
8-
import mill.util.Jvm
8+
import mill.util.{Jvm, Version}
99

1010
/**
1111
* Checks Java source files with PMD static code analyzer [[https://pmd.github.io/]].
1212
*/
1313
trait PmdModule extends CoursierModule, OfflineSupportModule {
1414

1515
/**
16-
* Runs PMD and returns the number of violations found (exit code).
16+
* Runs PMD.
1717
*
1818
* @note [[sources]] are processed when no [[PmdArgs.sources]] are specified.
1919
*/
2020
def pmd(@mainargs.arg pmdArgs: PmdArgs): Command[(exitCode: Int, outputPath: PathRef)] =
2121
Task.Command {
22-
val (outputPath, exitCode) = pmd0(pmdArgs.format, pmdArgs.sources)()
22+
val res = pmd0(pmdArgs.format, pmdArgs.sources)()
2323
pmdHandleExitCode(
2424
pmdArgs.stdout,
2525
pmdArgs.failOnViolation,
26-
exitCode,
27-
outputPath,
26+
res.exitCode,
27+
res.outputPath,
2828
pmdArgs.format
2929
)
30-
(exitCode, outputPath)
30+
(res.exitCode, res.outputPath)
3131
}
3232

33-
protected def pmd0(format: String, leftover: mainargs.Leftover[String]) =
33+
protected def pmd0(
34+
format: String,
35+
leftover: mainargs.Leftover[String]
36+
): Task[(outputPath: PathRef, exitCode: Int)] =
3437
Task.Anon {
3538
val output = Task.dest / s"pmd-output.$format"
3639
os.makeDir.all(output / os.up)
@@ -54,9 +57,9 @@ trait PmdModule extends CoursierModule, OfflineSupportModule {
5457
else "net.sourceforge.pmd.cli.PmdCli"
5558
val jvmArgs = pmdLanguage().map(lang => s"-Duser.language=$lang").toSeq
5659

57-
Task.log.info("Running PMD...")
60+
Task.log.info("Running PMD ...")
5861
Task.log.debug(s"with $args")
59-
Task.log.info(s"Writing PMD output to: $output...")
62+
Task.log.info(s"Writing PMD output to $output ...")
6063

6164
val exitCode = Jvm.callProcess(
6265
mainCls,
@@ -69,7 +72,7 @@ trait PmdModule extends CoursierModule, OfflineSupportModule {
6972
jvmArgs = jvmArgs
7073
).exitCode
7174

72-
(PathRef(output), exitCode)
75+
(outputPath = PathRef(output), exitCode = exitCode)
7376
}
7477

7578
private def pmdHandleExitCode(
@@ -87,7 +90,7 @@ trait PmdModule extends CoursierModule, OfflineSupportModule {
8790
reportViolations(failOnViolation, countViolations(output, format, stdout))
8891
case 5 =>
8992
reportViolations(failOnViolation, countViolations(output, format, stdout))
90-
throw new RuntimeException("At least one recoverable PMD error has occurred.")
93+
Task.fail("At least one recoverable PMD error has occurred.")
9194
case x => Task.log.error(s"Unsupported PMD exit code: $x")
9295
exitCode
9396
}
@@ -127,10 +130,9 @@ trait PmdModule extends CoursierModule, OfflineSupportModule {
127130
failOnViolation: Boolean,
128131
violationCount: Option[Int]
129132
)(implicit ctx: TaskCtx): Unit = {
130-
if (failOnViolation)
131-
throw new RuntimeException(s"PMD found ${violationCount.getOrElse("some")} violation(s)")
132-
else
133-
Task.log.error(s"PMD found ${violationCount.getOrElse("some")} violation(s)")
133+
val msg = s"PMD found ${violationCount.getOrElse("some")} violation(s)"
134+
if (failOnViolation) Task.fail(msg)
135+
else Task.log.error(msg)
134136
}
135137

136138
/**
@@ -155,22 +157,19 @@ trait PmdModule extends CoursierModule, OfflineSupportModule {
155157
}
156158

157159
/** Helper to check if the version is <= 6. False by default. */
158-
private def isPmd6OrOlder(version: String): Boolean = {
159-
version
160-
.split(":").lastOption
161-
.flatMap(_.takeWhile(_ != '.').toIntOption)
162-
.exists(_ < 7)
163-
}
160+
private def isPmd6OrOlder(version: String): Boolean =
161+
!Version.isAtLeast(version, "7")(using Version.IgnoreQualifierOrdering)
164162

165163
/** PMD version. */
166164
def pmdVersion: T[String] = Task { Versions.pmdVersion }
167165
}
168166

169167
/**
170168
* External module for PMD integration.
171-
* Allows usage via `import mill.javalib.pmd.PmdModule` in build.sc.
169+
*
170+
* Allows usage via `import mill.javalib.pmd/` in build.mill.
172171
*/
173-
object PmdModule extends ExternalModule, PmdModule {
172+
object PmdModule extends ExternalModule, PmdModule, TaskModule {
174173
lazy val millDiscover: Discover = Discover[this.type]
175-
def defaultCommandName() = "pmd"
174+
override def defaultTask() = "pmd"
176175
}

libs/jvmlib/test/src/mill/javalib/pmd/PmdModuleTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ object PmdModuleTest extends TestSuite {
1212
val resources: os.Path = os.Path(sys.env("MILL_TEST_RESOURCE_DIR")) / "pmd"
1313
val modulePath: os.Path = resources / "example"
1414

15-
def runTest(module: TestRootModule with PmdModule with JavaModule): Unit = {
15+
def runTest(module: TestRootModule & PmdModule & JavaModule): Unit = {
1616
UnitTester(module, modulePath).scoped { eval =>
1717
val format = "text"
1818
eval(module.pmd(PmdArgs(

0 commit comments

Comments
 (0)