New built-in pmd plugin fails looking for /etc/pmd-nicerhtml.xsl file

Attempt to use milestone-9 to replace my custom PMD task, but immediately hit a snag.

I’m running pmd 4.2.5 and with my custom task it can definitely run with just asm-3.1 and jaxen-1.1.1 to generate raw text reports.

With just the bare-necessity pmd jar and supporting jars available for the newly embedded pmd plugin, it fails like so:

Caused by: : Can’t file XSLT sheet :/etc/pmd-nicerhtml.xsl at net.sourceforge.pmd.ant.Formatter.start(Formatter.java:123) at net.sourceforge.pmd.ant.PMDTask.doTask(PMDTask.java:213) at net.sourceforge.pmd.ant.PMDTask.execute(PMDTask.java:334) …

I don’t see any options to adjust what type of report is being generated, but I’m viewing the results primarily via Hudson/Jenkins and only want the text form.

-Spencer

Hello, the pmd plugin currently supports two types of report: “xml” and “html”. To enable or disable these reports you can do:

pmdMain {
        reports {
            xml.enabled true
            html.enabled false
        }
    }

did you do any configuration on your pmd tasks? Can you provide a sample snippet to reproduce the error above?

regards, René

Thanks! After adjusting pmdMain and pmdTest with that, it works as desired. I recalled seeing something like that in some earlier post (or mailing list comment) but couldn’t find it in the documentation examples.

-Spencer

Hello Spencer, the details of the pmd plugin are described in the user guide at (http://gradle.org/docs/current/userguide/userguide_single.html#pmd_plugin) or in the the DSL Documentation of the PMD task (http://gradle.org/docs/current/dsl/org.gradle.api.plugins.quality.Pmd.html)

regards, René

I’m seeing this problem also. I’d like to see only XML output, no HTML. But I have a bunch of subprojects with various source sets; how can I set those options for all source sets of all projects?

Hello Chris, you can configure all tasks of a specific type with the withType method of TaskContainer. Try this in your root build.gradle file:

subprojects{
   tasks.withType(Pmd).each{
       reports {
           xml.enabled true
           html.enabled false
       }
   }
}

regards, René

Could reports be moved to the Pmd Extension (and Findbugs Extension, etc)? Then a report could be configured outside the task:

subprojects {
    pmd {
        reports {
            xml.enabled true
            html.enabled false
         }
    }
}

Or even better would be if we could configure reports for all code-quality tasks:

subprojects {
    reports {
        xml.enabled true
        html.enabled false
    }
}

The reports currently belong to tasks. This allows you to customize the report output different for each task. (e.g. generate different reports for test and operational source sets. Configuring all Pmd tasks is very simple (see my snippet above) I can’t see a real benefit from separating report configuration from the task.

regards, René

The ‘.each’ should be omitted to avoid eager evaluation.

I understand that reports currently belong to tasks, I just don’t think some report settings are inherently in the scope of a task and expanding their scope would be more consistent and helpful. For an example of where it’s helpful, see FindBugsExtension (http://gradle.org/docs/current/dsl/org.gradle.api.plugins.quality.FindBugsExtension.html) where reports Dir is located. That’s a great place to configure all reports (findbugsMain and findbugsTest). It only makes sense that the format of the reports could go there into a central place, how often would people want one format for Main and one for Test?

I accept that pulling reports out into its own Extension would be a big effort, but it would be appreciated.

The new reporting mechanism was designed so that this can be done, there just wasn’t time to go the last mile in the iteration. What’s missing is adding a reports container to the extension, and wiring each report in the extension to the corresponding one for each task.

Given that there is a reasonable workaround, it’s not high priority to fix.

Regarding the original issue: in my opinion this is not solved at all.

i’m seeing that as well, with the default configuration of the PMD plugin. Somewhen in the rework of the PMD plugin, the ant task configuration was changed from a “html” formatter to a “betterhtml” formatter. In PMD, this results in using an XSLTRenderer, which needs a xsltFile property.

Default for that property is “/etc/pmd-nicerhtml.xsl”, which is tried to open as a file, and failing that, as a class path resource (which does not exist in the PMD jar file).

Unfortunately the PMD plugin provides no way to access that ant task from the build file (it is created on the fly in the PMD task’s run method).

So the situation is: * PMD plugin implements HTML reports with a renderer that needs configuration * it fails to do the configuration * it provides no way for the user to do that configuration * it enables HTML reports by default.

This means: PMD plugin is broken by default, and can only be repaired by disabling a feature in every build. IMO this is a bug that should be fixed, ideally in 1.0.

  • most minimal fix: disable HTML reposrts * bit further reaching fix: revert HTML report to use the regular HTML renderer instead of the XSLTRenderer * full fix: provide a way to configure the stylesheet instead * even more luxury: additionally configure a default sytylesheet, e.g. by packaging one of the PMD project’s provided XSLT files

The PMD plugin works just fine for me with default configuration. HTML report is created successfully. Our integration tests, which are run on several different JVMs and OSes, are saying the same. Unsure what the problem is on your side, but the plugin isn’t broken by default.

I still see this (Gradle 1.2):

Caused by: : Can't file XSLT sheet :/etc/pmd-nicerhtml.xsl
 at net.sourceforge.pmd.ant.Formatter.start(Formatter.java:123)
 at net.sourceforge.pmd.ant.PMDTask.doTask(PMDTask.java:213)
 at net.sourceforge.pmd.ant.PMDTask.execute(PMDTask.java:334)
 at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
 at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
 at org.gradle.api.internal.project.ant.BasicAntBuilder.nodeCompleted(BasicAntBuilder.java:71)
 at org.gradle.api.internal.project.AntBuilderDelegate.nodeCompleted(DefaultIsolatedAntBuilder.groovy:172)
 at org.gradle.api.plugins.quality.Pmd$_run_closure1.doCall(Pmd.groovy:81)
 at org.gradle.util.ConfigureUtil.configure(ConfigureUtil.java:141)
 at org.gradle.util.ConfigureUtil.configure(ConfigureUtil.java:90)
 at org.gradle.util.ConfigureUtil$configure.call(Unknown Source)
 at org.gradle.api.internal.project.DefaultIsolatedAntBuilder.execute(DefaultIsolatedAntBuilder.groovy:112)
 at org.gradle.api.internal.project.IsolatedAntBuilder$execute.call(Unknown Source)
 at org.gradle.api.plugins.quality.Pmd.run(Pmd.groovy:79)

I can also clearly see this in code (current master of gradle, branch 4.3 of PMD): ### gradle PmdPlugin.groovy Just to show that default is PMD 4.3

@Override
    protected CodeQualityExtension createExtension() {
        extension = project.extensions.create("pmd", PmdExtension, project)
        extension.with {
            toolVersion = "4.3"

gradle Pmd.groovy “betterhtml” is set as HTML report renderer

if (reports.html.enabled) {
                    assert reports.html.destination.parentFile.exists()
                    formatter(type: 'betterhtml', toFile: reports.html.destination)
                }

PMD 4.3 src/net/sourceforge/pmd/ant/Formatter.java

renderersByCode.put("betterhtml", new RendererBuilder() {
            public Renderer build(Object[] arg) { return new XSLTRenderer(); }
        });

PMD 4.3 net/sourceforge/pmd/renderers/XSLTRenderer.java

public class XSLTRenderer extends XMLRenderer {
 private Transformer transformer;
 private String xsltFilename = "/etc/pmd-nicerhtml.xsl";
...
 public void start() throws IOException {
  // We keep the inital writer to put the final html output
  this.outputWriter = getWriter();
  // We use a new one to store the XML...
  Writer w = new StringWriter();
  setWriter(w);
  // If don't find the xsl no need to bother doing the all report,
  // so we check this here...
  InputStream xslt = null;
  File file = new File(this.xsltFilename);
  if ( file.exists() && file.canRead() ) {
   xslt = new FileInputStream(file);
  }
  else {
   xslt = this.getClass().getResourceAsStream(xsltFilename);
  }
  if ( xslt == null ) {
   throw new FileNotFoundException("Can't file XSLT sheet :" + xsltFilename);
  }
...

Are you using a newer PMD version?(Though in 5.0 the format string “betterhtml” seems not to be supported anymore)

Yes, the default is 4.3, and yes, it works fine for me. I recently tried to upgrade to 5.0, but unfortunately, 5.0 breaks backwards compatibility in several ways, and I haven’t been able to make it work yet. I’ve contacted the PMD folks and am waiting for feedback.

OK, found the reason, PMD is to blame.

I guess you use pmd as maven artifact. The pmd:pmd:4.3 jar in Maven central contains a classpath resource etc/pmd-nicerhtml.xsl

The jar file in the zip binary bundle from sourceforge does not contain this file.

Different jars for the same software in different release channels … arggh!

1 Like

I am trying to upgrade to pmd 5.1.1 from 4.2.5 and I am using the gradle pmd. After adjusting my custom rules.xml, they way I reference such file in the gradle script and adjust custom java rules based on the changes pmd has reorganized, the only issue I have left is the html report with 5.1.1 is a very simple html report, very different from the report I got with 4.2.5. I think the reason is the pmd project moved the location of pmd-nicerhtml.xsl from the previous location (from the jar internals):

4.2.5:

etc/pmd-nicerhtml.xsl

5.1.1: pmd-nicerhtml.xsl

So my guess is the gradle plugin does not find the xsl file and uses the default format, not the nice format.

If somehow I could override such file location I think all would work as before. I haven’t find a way for doing it. The workaround would be to extract such file and run the transofrmation manually based on the xml report, but it would not be an out-of-the box experience that such gradle pmd plugin accomplished with previous pmd versions. THe main reason to upgrade to newer pmd is to support new java 8 syntax.

Thanks