Skip to content

Commit ee6e700

Browse files
committed
Merge pull request etsy#22 from JosephEarl/feature/auto-import
SBT plugin best practices
2 parents 1d91493 + 57ddd53 commit ee6e700

File tree

35 files changed

+178
-133
lines changed

35 files changed

+178
-133
lines changed

README.md

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,6 @@ addSbtPlugin("com.etsy" % "sbt-checkstyle-plugin" % "2.1.0")
1919

2020
sbt-checkstyle-plugin is an AutoPlugin, so there is no need to modify the `build.sbt` file to enable it.
2121

22-
If you want to modify any of the default settings, you should add the following import to `build.sbt`, however:
23-
24-
```scala
25-
import com.etsy.sbt.checkstyle._
26-
```
27-
2822
## Usage
2923

3024
You can run Checkstyle over your Java source files with the
@@ -33,58 +27,58 @@ the `test:checkstyle` task.
3327

3428
The Checkstyle configuration file is `./checkstyle-config.xml` by
3529
default. This can be changed by setting the value of
36-
`Checkstyle.configLocation`. By default `test:checkstyle` uses the same
30+
`checkstyleConfigLocation`. By default `test:checkstyle` uses the same
3731
configuration file, but this can be changed by setting the value of
38-
`Checkstyle.configLocation in Test`.
32+
`checkstyleConfigLocation in Test`.
3933

4034
The Checkstyle report is output to `target/checkstyle-report.xml` by
4135
default. This can be changed by setting the value of
42-
`Checkstyle.outputFile`. `test:checkstyle` outputs to
36+
`checkstyleOutputFile`. `test:checkstyle` outputs to
4337
`target/checkstyle-test-report.xml`, but this can be changed by
44-
setting the value of `Checkstyle.outputFile in Test`.
38+
setting the value of `checkstyleOutputFile in Test`.
4539

46-
To change the checkstyle configuration file set `Checkstyle.configLocation` in `build.sbt`:
40+
To change the checkstyle configuration file set `checkstyleConfigLocation` in `build.sbt`:
4741
```scala
48-
Checkstyle.configLocation := CheckstyleConfig.File("checkstyle-config.xml")
42+
checkstyleConfigLocation := CheckstyleConfigLocation.File("checkstyle-config.xml")
4943
```
5044

5145
You can also load remote configuration files by specifying a URL:
5246
```scala
53-
Checkstyle.configLocation :=
54-
CheckstyleConfig.URL("https://raw.githubusercontent.com/checkstyle/checkstyle/master/config/checkstyle_checks.xml")
47+
checkstyleConfigLocation :=
48+
CheckstyleConfigLocation.URL("https://raw.githubusercontent.com/checkstyle/checkstyle/master/config/checkstyle_checks.xml")
5549
```
5650

5751
Or load configuration files from the classpath by specifying a resource name:
5852
```scala
59-
Checkstyle.configLocation := CheckstyleConfig.File("com/etsy/checkstyle-config.xml")
53+
checkstyleConfigLocation := CheckstyleConfigLocation.Classpath("com/etsy/checkstyle-config.xml")
6054
```
6155

6256
To run Checkstyle automatically after compilation:
6357
```scala
64-
(Checkstyle.checkstyle in Compile) <<= (Checkstyle.checkstyle in Compile) triggeredBy (compile in Compile)
58+
(checkstyle in Compile) <<= (checkstyle in Compile) triggeredBy (compile in Compile)
6559
```
6660

6761
To run Checkstyle automatically after test compilation:
6862
```scala
69-
(Checkstyle.checkstyle in Test) <<= (Checkstyle.checkstyle in Test) triggeredBy (compile in Test)
63+
(checkstyle in Test) <<= (checkstyle in Test) triggeredBy (compile in Test)
7064
```
7165

7266
### XSLT transformations
7367

74-
The `xsltTransformations` setting allows applying XSLT transformations to the XML report generated by Checkstyle. For instance, this could be used to generate a more readable HTML report. This setting takes values of `Option[Set[XSLTSettings]]`, so multiple transformations can be applied.
68+
The `checkstyleXsltTransformations` setting allows applying XSLT transformations to the XML report generated by Checkstyle. For instance, this could be used to generate a more readable HTML report. This setting takes values of `Option[Set[XSLTSettings]]`, so multiple transformations can be applied.
7569

76-
You can set `xsltTransformations` like so in `build.sbt`:
70+
You can set `checkstyleXsltTransformations` like so in `build.sbt`:
7771
```scala
78-
Checkstyle.xsltTransformations := {
79-
Some(Set(XSLTSettings(baseDirectory(_ / "checkstyle-noframes.xml").value, target(_ / "checkstyle-report.html").value)))
72+
checkstyleXsltTransformations := {
73+
Some(Set(CheckstyleXSLTSettings(baseDirectory(_ / "checkstyle-noframes.xml").value, target(_ / "checkstyle-report.html").value)))
8074
}
8175
```
8276

8377
### Failing the build
8478

8579
You can control what severity of issues should break the build by setting the `checkstyleSeverityLevel` in your `build.sbt` as follows:
8680
```scala
87-
Checkstyle.severityLevel := Some(CheckstyleSeverityLevel.Error)
81+
checkstyleSeverityLevel := Some(CheckstyleSeverityLevel.Error)
8882
```
8983

9084
Possible values are defined by the `CheckstyleSeverityLevel` enumeration. The default is `None`.
@@ -97,11 +91,9 @@ lazy val root = (project in file(".")).configs(IntegrationTest)
9791

9892
Defaults.itSettings
9993

100-
Checkstyle.checkstyleSettings ++ Seq(
101-
Checkstyle.configLocation := Checkstyle.CheckstyleConfig.File("my-checkstyle-config.xml"),
102-
Checkstyle.checkstyle in IntegrationTest <<= Checkstyle.checkstyleTask(IntegrationTest),
103-
Checkstyle.outputFile in IntegrationTest <<= target(_ / "checkstyle-integration-test-report.xml")
104-
)
94+
checkstyleConfigLocation := CheckstyleConfigLocation.File("my-checkstyle-config.xml"),
95+
checkstyle in IntegrationTest <<= checkstyleTask(IntegrationTest),
96+
checkstyleOutputFile in IntegrationTest <<= target(_ / "checkstyle-integration-test-report.xml")
10597
```
10698

10799
You can then run the tasks `it:checkstyle` and `it:checkstyle-check`.
@@ -115,3 +107,25 @@ Provided the new Checkstyle version is compatible, you can override the version
115107
```scala
116108
dependencyOverrides += "com.puppycrawl.tools" % "checkstyle" % "6.13"
117109
```
110+
111+
## Settings
112+
113+
### `checkstyleOutputFile`
114+
* *Description:* The location of the generated checkstyle report.
115+
* *Accepts:* any legal file path
116+
* *Default:* `Some(target.value / "checkstyle-report.xml")`
117+
118+
### `checkstyleConfigLocation`
119+
* *Description:* The location of the checkstyle configuration file.
120+
* *Accepts:* `CheckstyleConfigLocation.{File, URL, Classpath}`
121+
* *Default:* `CheckstyleConfigLocation.File("checkstyle-config.xml")`
122+
123+
### `checkstyleXsltTransformations`
124+
* *Description:* A set of XSLT transformations to be applied to the checkstyle output (optional).
125+
* *Accepts:* `Some(Set[CheckstyleXSLTSettings])`
126+
* *Default:* `None`
127+
128+
### `checkstyleSeverityLevel`
129+
* *Description:* Decide how much effort to put into analysis.
130+
* *Accepts:* `Some(CheckstyleSeverityLevel.{Ignore, Info, Warning, Error})`
131+
* *Default:* `None`

src/main/scala/com/etsy/sbt/checkstyle/Checkstyle.scala

Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,44 @@ package com.etsy.sbt.checkstyle
22

33
import javax.xml.transform.stream.StreamSource
44

5-
import com.etsy.sbt.checkstyle.CheckstyleSeverityLevel.CheckstyleSeverityLevel
5+
import com.etsy.sbt.checkstyle.CheckstyleSeverityLevel._
66
import com.puppycrawl.tools.checkstyle.Main.{main => CheckstyleMain}
77
import net.sf.saxon.s9api.Processor
8-
import sbt.Def.Initialize
98
import sbt.Keys._
109
import sbt._
1110

1211
/**
13-
* An SBT plugin to run checkstyle over Java code
12+
* A Scala wrapper around the Checkstyle Java API
1413
*
1514
* @author Andrew Johnson <ajohnson@etsy.com>
16-
* @author Alejandro Rivera <alejandro.rivera.lopez@gmail.com>
1715
* @author Joseph Earl <joe@josephearl.co.uk>
1816
*/
19-
object Checkstyle extends AutoPlugin {
20-
override def trigger: PluginTrigger = allRequirements
21-
22-
val checkstyle = TaskKey[Unit]("checkstyle", "Runs checkstyle")
23-
val outputFile = SettingKey[File]("checkstyle-target", "The location of the generated checkstyle report")
24-
val configLocation = SettingKey[CheckstyleConfig]("checkstyle-config-location", "The location of the checkstyle configuration file")
25-
val xsltTransformations = SettingKey[Option[Set[XSLTSettings]]]("xslt-transformations", "An optional set of XSLT transformations to be applied to the checkstyle output")
26-
val severityLevel = SettingKey[Option[CheckstyleSeverityLevel]]("checkstyle-severity-level", "Sets the severity levels which should fail the build")
27-
17+
object Checkstyle {
2818
/**
2919
* Runs checkstyle
3020
*
31-
* @param conf The configuration (Compile or Test) in which context to execute the checkstyle command
21+
* @param javaSource The Java source path.
22+
* @param outputFile The Checkstyle report output path.
23+
* @param configLocation The Checkstyle config location.
24+
* @param xsltTransformations XSLT transformations to apply.
25+
* @param severityLevel The severity level used to fail the build.
3226
*/
33-
def checkstyleTask(conf: Configuration): Initialize[Task[Unit]] = Def.task {
34-
val outputLocation = (outputFile in conf).value.getAbsolutePath
35-
val targetFolder = (outputFile in conf).value.getParentFile
27+
def checkstyle(javaSource: File, resources: Seq[File], outputFile: File, configLocation: CheckstyleConfigLocation,
28+
xsltTransformations: Option[Set[CheckstyleXSLTSettings]], severityLevel: Option[CheckstyleSeverityLevel], streams: TaskStreams): Unit = {
29+
val outputLocation = outputFile.absolutePath
30+
val targetFolder = outputFile.getParentFile
3631
val configFile = targetFolder + "/checkstyle-config.xml"
3732

3833
targetFolder.mkdirs()
3934

40-
val resolvedCheckstyleConfig = (configLocation in conf).value
41-
42-
val config = scala.xml.XML.loadString(resolvedCheckstyleConfig.read((resources in Compile).value))
35+
val config = scala.xml.XML.loadString(configLocation.read(resources))
4336
scala.xml.XML.save(configFile, config, "UTF-8", xmlDecl = true,
4437
scala.xml.dtd.DocType("module", scala.xml.dtd.PublicID("-//Puppy Crawl//DTD Check Configuration 1.3//EN",
4538
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd"), Nil))
4639

4740
val checkstyleArgs = Array(
4841
"-c", configFile, // checkstyle configuration file
49-
(javaSource in conf).value.getAbsolutePath, // location of Java source file
42+
javaSource.absolutePath, // location of Java source file
5043
"-f", "xml", // output format
5144
"-o", outputLocation // output file
5245
)
@@ -58,14 +51,14 @@ object Checkstyle extends AutoPlugin {
5851
CheckstyleMain(checkstyleArgs: _*)
5952
}
6053

61-
xsltTransformations.value match {
54+
xsltTransformations match {
6255
case None => // Nothing to do
6356
case Some(xslt) => applyXSLT(file(outputLocation), xslt)
6457
}
6558

66-
if (file(outputLocation).exists && severityLevel.value.isDefined) {
67-
val log = streams.value.log
68-
val issuesFound = processIssues(log, outputLocation, severityLevel.value.get)
59+
if (file(outputLocation).exists && severityLevel.isDefined) {
60+
val log = streams.log
61+
val issuesFound = processIssues(log, outputLocation, severityLevel.get)
6962

7063
if (issuesFound > 0) {
7164
log.error(issuesFound + " issue(s) found in Checkstyle report: " + outputLocation + "")
@@ -109,11 +102,11 @@ object Checkstyle extends AutoPlugin {
109102
* @param input The XML file produced by checkstyle
110103
* @param transformations The XSLT transformations to be applied
111104
*/
112-
private def applyXSLT(input: File, transformations: Set[XSLTSettings]): Unit = {
105+
private def applyXSLT(input: File, transformations: Set[CheckstyleXSLTSettings]): Unit = {
113106
val processor = new Processor(false)
114107
val source = processor.newDocumentBuilder().build(input)
115108

116-
transformations foreach { transform: XSLTSettings =>
109+
transformations foreach { transform: CheckstyleXSLTSettings =>
117110
val output = processor.newSerializer(transform.output)
118111
val compiler = processor.newXsltCompiler()
119112
val executor = compiler.compile(new StreamSource(transform.xslt))
@@ -146,15 +139,4 @@ object Checkstyle extends AutoPlugin {
146139
System.setSecurityManager(original)
147140
}
148141
}
149-
150-
override def projectSettings: Seq[Def.Setting[_]] = Seq(
151-
outputFile <<= target(_ / "checkstyle-report.xml"),
152-
outputFile in Test <<= target(_ / "checkstyle-test-report.xml"),
153-
configLocation := CheckstyleConfig.File("checkstyle-config.xml"),
154-
configLocation in Test <<= configLocation,
155-
checkstyle in Compile <<= checkstyleTask(Compile),
156-
checkstyle in Test <<= checkstyleTask(Test),
157-
xsltTransformations := None,
158-
severityLevel := None
159-
)
160142
}

src/main/scala/com/etsy/sbt/checkstyle/CheckstyleConfig.scala renamed to src/main/scala/com/etsy/sbt/checkstyle/CheckstyleConfigLocation.scala

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,25 @@ import sbt.File
44

55
import scala.io.Source
66

7-
sealed abstract class CheckstyleConfig(val location: String) {
7+
/**
8+
* Represents a Checkstyle XML configuration located locally, on the class path or remotely at a URL
9+
*
10+
* @author Joseph Earl
11+
*/
12+
sealed abstract class CheckstyleConfigLocation(val location: String) {
813
def read(resources: Seq[File]): String
914
}
1015

11-
object CheckstyleConfig {
12-
case class URL(url: String) extends CheckstyleConfig(url) {
16+
object CheckstyleConfigLocation {
17+
case class URL(url: String) extends CheckstyleConfigLocation(url) {
1318
override def read(resources: Seq[sbt.File]): String = Source.fromURL(url).mkString
1419
}
1520

16-
case class File(path: String) extends CheckstyleConfig(path) {
21+
case class File(path: String) extends CheckstyleConfigLocation(path) {
1722
override def read(resources: Seq[sbt.File]): String = Source.fromFile(path).mkString
1823
}
1924

20-
case class Classpath(name: String) extends CheckstyleConfig(name) {
25+
case class Classpath(name: String) extends CheckstyleConfigLocation(name) {
2126
override def read(resources: Seq[sbt.File]): String = {
2227
val classpath = resources.map((f) => f.toURI.toURL)
2328
val loader = new java.net.URLClassLoader(classpath.toArray, getClass.getClassLoader)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.etsy.sbt.checkstyle
2+
3+
import com.etsy.sbt.checkstyle.CheckstyleSeverityLevel.CheckstyleSeverityLevel
4+
import sbt.Def.Initialize
5+
import sbt.Keys._
6+
import sbt._
7+
8+
/**
9+
* An SBT plugin to run checkstyle over Java code
10+
*
11+
* @author Andrew Johnson <ajohnson@etsy.com>
12+
* @author Alejandro Rivera <alejandro.rivera.lopez@gmail.com>
13+
* @author Joseph Earl <joe@josephearl.co.uk>
14+
*/
15+
object CheckstylePlugin extends AutoPlugin {
16+
override def trigger: PluginTrigger = allRequirements
17+
18+
object autoImport {
19+
val checkstyle = TaskKey[Unit]("checkstyle", "Runs checkstyle")
20+
val checkstyleOutputFile = SettingKey[File]("checkstyle-target", "The location of the generated checkstyle report")
21+
val checkstyleConfigLocation = SettingKey[CheckstyleConfigLocation]("checkstyle-config-location", "The location of the checkstyle configuration file")
22+
val checkstyleXsltTransformations = SettingKey[Option[Set[CheckstyleXSLTSettings]]]("xslt-transformations", "An optional set of XSLT transformations to be applied to the checkstyle output")
23+
val checkstyleSeverityLevel = SettingKey[Option[CheckstyleSeverityLevel]]("checkstyle-severity-level", "Sets the severity levels which should fail the build")
24+
25+
val CheckstyleConfigLocation = com.etsy.sbt.checkstyle.CheckstyleConfigLocation
26+
val CheckstyleSeverityLevel = com.etsy.sbt.checkstyle.CheckstyleSeverityLevel
27+
val CheckstyleXSLTSettings = com.etsy.sbt.checkstyle.CheckstyleXSLTSettings
28+
29+
/**
30+
* Runs checkstyle
31+
*
32+
* @param conf The configuration (Compile or Test) in which context to execute the checkstyle command
33+
*/
34+
def checkstyleTask(conf: Configuration): Initialize[Task[Unit]] = Def.task {
35+
Checkstyle.checkstyle((javaSource in conf).value, (resources in Compile).value, (checkstyleOutputFile in conf).value, (checkstyleConfigLocation in conf).value,
36+
(checkstyleXsltTransformations in conf).value, (checkstyleSeverityLevel in conf).value, streams.value)
37+
}
38+
}
39+
40+
// scalastyle:off import.grouping
41+
import autoImport._
42+
// scalastyle:on import.grouping
43+
44+
private lazy val commonSettings: Seq[Def.Setting[_]] = Seq(
45+
checkstyleXsltTransformations := None,
46+
checkstyleSeverityLevel := None
47+
)
48+
49+
override lazy val projectSettings: Seq[Def.Setting[_]] = Seq(
50+
checkstyleOutputFile <<= target(_ / "checkstyle-report.xml"),
51+
checkstyleOutputFile in Test <<= target(_ / "checkstyle-test-report.xml"),
52+
checkstyleConfigLocation := com.etsy.sbt.checkstyle.CheckstyleConfigLocation.File("checkstyle-config.xml"),
53+
checkstyleConfigLocation in Test <<= checkstyleConfigLocation,
54+
checkstyle <<= checkstyleTask(Compile),
55+
checkstyle in Test <<= checkstyleTask(Test)
56+
) ++ commonSettings
57+
}

src/main/scala/com/etsy/sbt/checkstyle/CheckstyleSeverityLevel.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
package com.etsy.sbt.checkstyle
22

3+
/**
4+
* Enumeration of the different Checkstyle severity levels
5+
*
6+
* @author Andrew Johnson
7+
*/
38
object CheckstyleSeverityLevel extends Enumeration {
49
type CheckstyleSeverityLevel = Value
510
val Ignore = Value("ignore")

src/main/scala/com/etsy/sbt/checkstyle/XSLTSettings.scala renamed to src/main/scala/com/etsy/sbt/checkstyle/CheckstyleXSLTSettings.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ import java.io.File
77
*
88
* @author Andrew Johnson
99
*/
10-
case class XSLTSettings(xslt: File, output: File)
10+
case class CheckstyleXSLTSettings(xslt: File, output: File)

src/sbt-test/checkstyle/checkstyle-auto/build.sbt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ name := "checkstyle-test"
44

55
organization := "com.etsy"
66

7-
import com.etsy.sbt.checkstyle._
7+
checkstyleConfigLocation := CheckstyleConfigLocation.File("checkstyle-config.xml")
88

9-
Checkstyle.configLocation := CheckstyleConfig.File("checkstyle-config.xml")
10-
11-
(Checkstyle.checkstyle in Compile) <<= (Checkstyle.checkstyle in Compile) triggeredBy (compile in Compile)
9+
(checkstyle in Compile) <<= (checkstyle in Compile) triggeredBy (compile in Compile)

src/sbt-test/checkstyle/checkstyle-check-empty/build.sbt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,4 @@ name := "checkstyle-check-empty"
44

55
organization := "com.etsy"
66

7-
import com.etsy.sbt.checkstyle._
8-
9-
Checkstyle.severityLevel := Some(CheckstyleSeverityLevel.Error)
7+
checkstyleSeverityLevel := Some(CheckstyleSeverityLevel.Error)

src/sbt-test/checkstyle/checkstyle-check/build.sbt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,5 @@ name := "checkstyle-check"
44

55
organization := "com.etsy"
66

7-
import com.etsy.sbt.checkstyle._
8-
9-
Checkstyle.configLocation := CheckstyleConfig.File("my-checkstyle-config.xml")
10-
Checkstyle.severityLevel := Some(CheckstyleSeverityLevel.Error)
7+
checkstyleConfigLocation := CheckstyleConfigLocation.File("my-checkstyle-config.xml")
8+
checkstyleSeverityLevel := Some(CheckstyleSeverityLevel.Error)

src/sbt-test/checkstyle/checkstyle-classpath-config-dependency/build.sbt

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)