Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

using Classpath shrinker to warn on indirect targets #1

Closed
wants to merge 24 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
697f23a
Restructure scala provider, add JavaProvider
Apr 3, 2017
49fae27
Using JavaProvider, requires Bazel 0.5.2
Jun 20, 2017
031ddee
using classpath shrinker plugin to warn about indirect dependency usage
natansil Jun 28, 2017
a827601
fix escaping bugs
natansil Jun 29, 2017
f0a96ff
attempt to put dependency-analyzer inside rules_scala
natansil Jul 2, 2017
22cb9d2
merge workspaces
natansil Jul 3, 2017
3ec634d
renamed MoreScala* targets to ResourcesStripScala* targets (#232)
ittaiz Jun 20, 2017
9aa0499
introduced scalac_jvm_flags
ittaiz Jun 25, 2017
3e31081
introduced javac_jvm_flags
ittaiz Jun 25, 2017
78d6ed6
removed duplication of failing tests
ittaiz Jun 25, 2017
0f8bb37
changed docs to include scalac_jvm_flags and javac_jvm_flags and to m…
ittaiz Jun 27, 2017
6fccf57
Restructure scala provider, add JavaProvider (#227)
sdtwigg Jun 28, 2017
7330451
update rules_scala version to latest in readme (#237)
ittaiz Jun 28, 2017
3e2f34d
update README.md
johnynek Jun 28, 2017
6459879
don't put signatures in deploy jar (#229)
oscar-stripe Jun 29, 2017
0dc305a
Add a provider for binary targets (#238)
johnynek Jun 29, 2017
b700d96
misc: add line for test and build formatting
matt-stripe Jun 29, 2017
d51e465
Use workspace name in tut rule
johnynek Jul 1, 2017
091e841
Restructure scala provider, add JavaProvider
Apr 3, 2017
d725ad6
using classpath shrinker plugin to warn about indirect dependency usage
natansil Jun 28, 2017
303af80
fix escaping bugs
natansil Jun 29, 2017
6d1de17
merge workspaces
natansil Jul 3, 2017
a46aefe
fix bugs
natansil Jul 3, 2017
93db8c3
remove hardcoded jar paths
natansil Jul 4, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ os:

env:
- V=HEAD
- V=0.4.5
- V=0.5.2

before_install:
- |
Expand Down
62 changes: 55 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ and `scala_test`.
## Getting started

In order to use `scala_library`, `scala_macro_library`, and `scala_binary`,
you must have bazel 0.3.1 or later and add the following to your WORKSPACE file:
you must have bazel 0.5.2 or later and add the following to your WORKSPACE file:

```python

rules_scala_version="d916599d38de29085e5ca9eae167716c4f150a02" # update this as needed
rules_scala_version="031e73c02e0d8bfcd06c6e4086cdfc7f3a3061a8" # update this as needed

http_archive(
name = "io_bazel_rules_scala",
Expand Down Expand Up @@ -58,8 +58,8 @@ to your command line, or to enable by default for building/testing add it to you
## scala\_library / scala\_macro_library

```python
scala_library(name, srcs, deps, runtime_deps, exports, data, main_class, resources, resource_strip_prefix, scalacopts, jvm_flags)
scala_macro_library(name, srcs, deps, runtime_deps, exports, data, main_class, resources, resource_strip_prefix, scalacopts, jvm_flags)
scala_library(name, srcs, deps, runtime_deps, exports, data, main_class, resources, resource_strip_prefix, scalacopts, jvm_flags, scalac_jvm_flags, javac_jvm_flags)
scala_macro_library(name, srcs, deps, runtime_deps, exports, data, main_class, resources, resource_strip_prefix, scalacopts, jvm_flags, scalac_jvm_flags, javac_jvm_flags)
```

`scala_library` generates a `.jar` file from `.scala` source files. This rule
Expand Down Expand Up @@ -174,6 +174,15 @@ In order to make a java rule use this jar file, use the `java_import` rule.
</tr>
<tr>
<td><code>jvm_flags</code></td>
<td>
<p><code>List of strings; optional; deprecated</code></p>
<p>
Deprecated, superseded by scalac_jvm_flags and javac_jvm_flags. Is not used and is kept as backwards compatibility for the near future. Effectively jvm_flags is now an executable target attribute only.
</p>
</td>
</tr>
<tr>
<td><code>scalac_jvm_flags</code></td>
<td>
<p><code>List of strings; optional</code></p>
<p>
Expand All @@ -185,14 +194,27 @@ In order to make a java rule use this jar file, use the `java_import` rule.
</p>
</td>
</tr>
<tr>
<td><code>javac_jvm_flags</code></td>
<td>
<p><code>List of strings; optional</code></p>
<p>
List of JVM flags to be passed to javac after the
<code>javacopts</code>. Subject to
<a href="http://bazel.io/docs/be/make-variables.html">Make variable
substitution</a> and
<a href="http://bazel.io/docs/be/common-definitions.html#borne-shell-tokenization">Bourne shell tokenization.</a>
</p>
</td>
</tr>
</tbody>
</table>

<a name="scala_binary"></a>
## scala_binary

```python
scala_binary(name, srcs, deps, runtime_deps, data, main_class, resources, resource_strip_prefix, scalacopts, jvm_flags)
scala_binary(name, srcs, deps, runtime_deps, data, main_class, resources, resource_strip_prefix, scalacopts, jvm_flags, scalac_jvm_flags, javac_jvm_flags)
```

`scala_binary` generates a Scala executable. It may depend on `scala_library`, `scala_macro_library`
Expand Down Expand Up @@ -292,6 +314,18 @@ A `scala_binary` requires a `main_class` attribute.
</tr>
<tr>
<td><code>jvm_flags</code></td>
<td>
<p><code>List of strings; optional</code></p>
<p>
List of JVM flags to be passed to the executing JVM. Subject to
<a href="http://bazel.io/docs/be/make-variables.html">Make variable
substitution</a> and
<a href="http://bazel.io/docs/be/common-definitions.html#borne-shell-tokenization">Bourne shell tokenization.</a>
</p>
</td>
</tr>
<tr>
<td><code>scalac_jvm_flags</code></td>
<td>
<p><code>List of strings; optional</code></p>
<p>
Expand All @@ -303,14 +337,27 @@ A `scala_binary` requires a `main_class` attribute.
</p>
</td>
</tr>
<tr>
<td><code>javac_jvm_flags</code></td>
<td>
<p><code>List of strings; optional</code></p>
<p>
List of JVM flags to be passed to javac after the
<code>javacopts</code>. Subject to
<a href="http://bazel.io/docs/be/make-variables.html">Make variable
substitution</a> and
<a href="http://bazel.io/docs/be/common-definitions.html#borne-shell-tokenization">Bourne shell tokenization.</a>
</p>
</td>
</tr>
</tbody>
</table>

<a name="scala_test"></a>
## scala_test

```python
scala_test(name, srcs, suites, deps, data, main_class, resources, resource_strip_prefix, scalacopts, jvm_flags)
scala_test(name, srcs, suites, deps, data, main_class, resources, resource_strip_prefix, scalacopts, jvm_flags, scalac_jvm_flags, javac_jvm_flags)
```

`scala_test` generates a Scala executable which runs unit test suites written
Expand All @@ -326,7 +373,7 @@ populated and tests are not run.
<a name="scala_repl"></a>
## scala_repl
```python
scala_repl(name, deps, scalacopts, jvm_flags)
scala_repl(name, deps, scalacopts, jvm_flags, scalac_jvm_flags, javac_jvm_flags)
```
A scala repl allows you to add library dependendencies (not currently `scala_binary` targets)
to generate a script to run which starts a REPL.
Expand Down Expand Up @@ -419,6 +466,7 @@ thrift_library(name, srcs, deps, absolute_prefix, absolute_prefixes)
</tr>
</tbody>
</table>

## Building from source
Test & Build:
```
Expand Down
26 changes: 26 additions & 0 deletions WORKSPACE
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
workspace(name = "io_bazel_rules_scala")


####### previous rules scala for building of scala compiler plugin
rules_scala_version="3070353d06bb726141e6cea241e5d6e355a4d14f" # update this as needed

http_archive(
name = "io_bazel_rules_scala_for_plugin_bootstrapping",
url = "https://github.com/wix/rules_scala/archive/%s.zip"%rules_scala_version,
type = "zip",
strip_prefix= "rules_scala-%s" % rules_scala_version
)

load("//scala:scala.bzl", "scala_repositories", "scala_mvn_artifact")
scala_repositories()

Expand Down Expand Up @@ -28,4 +39,19 @@ maven_jar(
artifact = scala_mvn_artifact("org.psywerx.hairyfotr:linter:0.1.13"),
sha1 = "e5b3e2753d0817b622c32aedcb888bcf39e275b4")

# test of a scala plugin
maven_jar(
name = "io_get_coursier_coursier_cache",
artifact = "io.get-coursier:coursier-cache_2.11:jar:1.0.0-M15",
sha1 = "cbf7a208ccd4c3ad44efa3886e237ecbf96a6fd9")

maven_jar(
name = "io_get_coursier_coursier",
artifact = "io.get-coursier:coursier_2.11:jar:1.0.0-M15",
sha1 = "9c6281274f9964a786cba4a5df62740c07b07046")

maven_jar(
name = "org_scalaz_scalaz_concurrent_2_11",
artifact = "org.scalaz:scalaz-concurrent_2.11:jar:7.2.7",
sha1 = "abaea3aa04f11301f63099d96cf47f91ec229ed4"
)
3 changes: 1 addition & 2 deletions jmh/jmh.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,7 @@ def _scala_construct_runtime_classpath(deps):
java_targets = [d.java for d in deps if hasattr(d, "java")]
files = []
for scala in scala_targets:
files += list(scala.transitive_runtime_deps)
files += list(scala.transitive_runtime_exports)
files += list(scala.transitive_runtime_jars)
for java in java_targets:
files += list(java.transitive_runtime_deps)
return files
Expand Down
20 changes: 20 additions & 0 deletions plugin/src/main/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
load("@io_bazel_rules_scala_for_plugin_bootstrapping//scala:scala.bzl", "scala_library")

scala_library(
name = "dependency_analyzer",
srcs = ["scala/io/github/retronym/dependencyanalyzer/DependencyAnalyzer.scala",
"scala_2_11/io/github/retronym/dependencyanalyzer/Compat.scala",
],
resources = ["resources/scalac-plugin.xml"],
visibility = ["//visibility:public"],
deps = [
"//external:io_bazel_rules_scala/dependency/scala/scala_compiler"
],
# enable_dependency_analyzer = False
)

scala_library(
name = "resources",
resources = ["resources/toolbox.classpath"],
visibility = ["//visibility:public"],
)
4 changes: 4 additions & 0 deletions plugin/src/main/resources/scalac-plugin.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<plugin>
<name>dependency-analyzer</name>
<classname>plugin.src.main.scala.io.github.retronym.dependencyanalyzer.DependencyAnalyzer</classname>
</plugin>
1 change: 1 addition & 0 deletions plugin/src/main/resources/toolbox.classpath
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/Users/natans/Work/rules_scala/bazel-bin/plugin/src/test/dependency_analyzer_test.runfiles/io_bazel_rules_scala/external/scala/lib/scala-library.jar
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package plugin.src.main.scala.io.github.retronym.dependencyanalyzer

import plugin.src.main.scala_2_11.io.github.retronym.dependencyanalyzer.Compat

import scala.reflect.io.AbstractFile
import scala.tools.nsc.plugins.{Plugin, PluginComponent}
import scala.tools.nsc.{Global, Phase}

class DependencyAnalyzer(val global: Global) extends Plugin with Compat {

val name = "dependency-analyzer"
val description =
"Warns about classpath entries that are not directly needed."
val components = List[PluginComponent](Component)

var indirect: Map[String, String] = Map.empty
var direct: Set[String] = Set.empty

override def processOptions(options: List[String], error: (String) => Unit): Unit = {
var indirectJars: Seq[String] = Seq.empty
var indirectTargets: Seq[String] = Seq.empty

for (option <- options) {
option.split(":").toList match {
case "direct-jars" :: data => direct = data.toSet
case "indirect-jars" :: data => indirectJars = data;
case "indirect-targets" :: data => indirectTargets = data.map(_.replace(";", ":"))
case unknown :: _ => error(s"unknown param $unknown")
case Nil =>
}
}
indirect = indirectJars.zip(indirectTargets).toMap
}


private object Component extends PluginComponent {
val global: DependencyAnalyzer.this.global.type =
DependencyAnalyzer.this.global

import global._

override val runsAfter = List("jvm")

val phaseName = DependencyAnalyzer.this.name

override def newPhase(prev: Phase): StdPhase = new StdPhase(prev) {
override def run(): Unit = {

super.run()

val usedJars = findUsedJars

warnOnIndirectTargetsFoundIn(usedJars)
}

private def warnOnIndirectTargetsFoundIn(usedJars: Set[AbstractFile]) = {
for (usedJar <- usedJars;
usedJarPath = usedJar.path;
target <- indirect.get(usedJarPath)
if !direct.contains(usedJarPath)) {
reporter.error(NoPosition, s"Target '$target' is used but isn't explicitly declared, please add it to the deps")
}
}

override def apply(unit: CompilationUnit): Unit = ()
}

}

import global._

private def findUsedJars: Set[AbstractFile] = {
val jars = collection.mutable.Set[AbstractFile]()

def walkTopLevels(root: Symbol): Unit = {
def safeInfo(sym: Symbol): Type =
if (sym.hasRawInfo && sym.rawInfo.isComplete) sym.info else NoType

def packageClassOrSelf(sym: Symbol): Symbol =
if (sym.hasPackageFlag && !sym.isModuleClass) sym.moduleClass else sym

for (x <- safeInfo(packageClassOrSelf(root)).decls) {
if (x == root) ()
else if (x.hasPackageFlag) walkTopLevels(x)
else if (x.owner != root) { // exclude package class members
if (x.hasRawInfo && x.rawInfo.isComplete) {
val assocFile = x.associatedFile
if (assocFile.path.endsWith(".class") && assocFile.underlyingSource.isDefined)
assocFile.underlyingSource.foreach(jars += _)
}
}
}
}

exitingTyper {
walkTopLevels(RootClass)
}
jars.toSet
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package plugin.src.main.scala_2_11.io.github.retronym.dependencyanalyzer

import scala.tools.nsc.Settings
import scala.tools.nsc.classpath.FlatClassPathFactory

/**
* Provides compatibility stubs for 2.11 and 2.12 Scala compilers.
*/
trait Compat {
def getClassPathFrom(settings: Settings) = new FlatClassPathFactory(settings)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package plugin.src.main.scala_2_12.io.github.retronym.dependencyanalyzer

import scala.tools.nsc.Settings
import scala.tools.nsc.classpath.ClassPathFactory

/**
* Provides compatibility stubs for 2.11 and 2.12 Scala compilers.
*/
trait Compat {
def getClassPathFrom(settings: Settings) = new ClassPathFactory(settings)
}
22 changes: 22 additions & 0 deletions plugin/src/test/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
load("@io_bazel_rules_scala_for_plugin_bootstrapping//scala:scala.bzl", "scala_junit_test")

scala_junit_test(
name = "dependency_analyzer_test",
srcs = ["scala/io/github/retronym/dependencyanalyzer/DependencyAnalyzerSpec.scala",
"scala/io/github/retronym/dependencyanalyzer/TestUtil.scala"],
suffixes = ["Test"],
size = "small",
deps = ["//plugin/src/main:dependency_analyzer",
"//plugin/src/main:resources",
"@io_get_coursier_coursier_cache//jar",
"@io_get_coursier_coursier//jar",
"@org_scalaz_scalaz_concurrent_2_11//jar",
"//external:io_bazel_rules_scala/dependency/scalaz/scalaz_effect",
"//external:io_bazel_rules_scala/dependency/scalaz/scalaz_core",
"//external:io_bazel_rules_scala/dependency/scala/scala_xml",
"//external:io_bazel_rules_scala/dependency/scala/scala_compiler",
"//external:io_bazel_rules_scala/dependency/scala/scala_library",
],
jvm_flags = ["-Dplugin.jar.location=$(location //plugin/src/main:dependency_analyzer)",
"-Dscala.library.location=$(location //external:io_bazel_rules_scala/dependency/scala/scala_library)"],
)
Loading