|
| 1 | +// https://github.com/com-lihaoyi/acyclic[Acyclic] is a Scala compiler plugin that |
| 2 | +// detects circular dependencies between files within a module. This can be very |
| 3 | +// useful for ensuring code quality at a high-level: |
| 4 | +// |
| 5 | +// * While most linters can be concern themselves at a micro-level with formatting |
| 6 | +// and whitespace, acyclic is concerned at a macro-level with how your codebase |
| 7 | +// is structured into different files |
| 8 | +// |
| 9 | +// * While most linters satisfied by tweaking whitespace, circular dependencies may |
| 10 | +// need significant refactorings like dependency-injection or |
| 11 | +// interface-implementation-separation to resolve. |
| 12 | +// |
| 13 | +// As a Scala compiler plugin, Acyclic can be enabled on any `ScalaModule` by |
| 14 | +// adding its `compileIvyDeps,` `scalacPluginIvyDeps`, and `scalacOptions` as |
| 15 | +// shown below: |
| 16 | + |
| 17 | +package build |
| 18 | +import mill._, scalalib._ |
| 19 | + |
| 20 | +object `package` extends RootModule with ScalaModule { |
| 21 | + def scalaVersion = "2.13.11" |
| 22 | + def compileIvyDeps = Agg(ivy"com.lihaoyi:::acyclic:0.3.15") |
| 23 | + def scalacPluginIvyDeps = Agg(ivy"com.lihaoyi:::acyclic:0.3.15") |
| 24 | + def scalacOptions = Seq("-P:acyclic:force") |
| 25 | +} |
| 26 | + |
| 27 | +/** See Also: src/Foo.scala */ |
| 28 | + |
| 29 | +/** See Also: src/Bar.scala */ |
| 30 | + |
| 31 | +// Here we have a single `ScalaModule` with two files: `Foo.scala` and `Bar.scala`. |
| 32 | +// `Bar` and `Foo` both depend on each other, which usually indicates an issue: |
| 33 | + |
| 34 | +/** Usage |
| 35 | + |
| 36 | +> ./mill compile |
| 37 | +error: Unwanted cyclic dependency |
| 38 | +...src/Bar.scala... |
| 39 | + val value = Foo + " world" |
| 40 | + ^ |
| 41 | +symbol: object Foo |
| 42 | +...src/Foo.scala... |
| 43 | + println("hello " + Bar) |
| 44 | + ^ |
| 45 | +symbol: object Bar |
| 46 | + |
| 47 | +*/ |
| 48 | + |
| 49 | +// Usually the code should be refactored such that references between files |
| 50 | +// is only one way. For this example, we remove the reference to `Foo` in `Bar.scala`, |
| 51 | +// which allows the code to compile: |
| 52 | + |
| 53 | + |
| 54 | +/** Usage |
| 55 | + |
| 56 | +> sed -i.bak 's/Foo/Bar/g' src/Bar.scala |
| 57 | + |
| 58 | +> ./mill compile |
| 59 | +done compiling |
| 60 | + |
| 61 | +*/ |
0 commit comments