Skip to content

Commit

Permalink
Support reading classpath from jar manifests (fix #1472)
Browse files Browse the repository at this point in the history
  • Loading branch information
xeno-by committed May 29, 2018
1 parent 76b54e7 commit 5fd0a63
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 5 deletions.
Expand Up @@ -3,6 +3,7 @@ package scala.meta.internal.metacp
import java.nio.file._
import java.nio.file.attribute.BasicFileAttributes
import java.util.concurrent.ConcurrentLinkedQueue
import java.util.jar._
import scala.collection.JavaConverters._
import scala.meta.cli._
import scala.meta.internal.index._
Expand Down Expand Up @@ -66,7 +67,9 @@ class Main(settings: Settings, reporter: Reporter) {
classpath.foreach { entry =>
if (entry.isDirectory) {
val out = AbsolutePath(Files.createTempDirectory("semanticdb"))
val res = convertClasspathEntry(entry, out)
val index = new Index
val res = convertClasspathEntry(entry, out, index)
index.save(out)
success.compareAndSet(true, res)
buffer.add(out)
} else if (entry.isFile) {
Expand All @@ -78,7 +81,29 @@ class Main(settings: Settings, reporter: Reporter) {
if (cacheEntry.toFile.exists) {
buffer.add(cacheEntry)
} else {
createCachedJar(cacheEntry)(out => convertClasspathEntry(entry, out))
createCachedJar(cacheEntry){ out =>
val index = new Index
def loop(entry: AbsolutePath): Boolean = {
var result = convertClasspathEntry(entry, out, index)
if (entry.isFile) {
val jar = new JarFile(entry.toFile)
val manifest = jar.getManifest
if (manifest != null) {
val classpathAttr = manifest.getMainAttributes.getValue("Class-Path")
if (classpathAttr != null) {
classpathAttr.split(" ").foreach { classpathEntry =>
val path = entry.toNIO.getParent.resolve(classpathEntry)
result &= loop(AbsolutePath(path))
}
}
}
}
result
}
val result = loop(entry)
index.save(out)
result
}
}
}
}
Expand All @@ -100,9 +125,8 @@ class Main(settings: Settings, reporter: Reporter) {
}
}

private def convertClasspathEntry(in: AbsolutePath, out: AbsolutePath): Boolean = {
private def convertClasspathEntry(in: AbsolutePath, out: AbsolutePath, index: Index): Boolean = {
var success = true
val index = new Index
val classpath = Classpath(in)
classpath.visit { base =>
new SimpleFileVisitor[Path] {
Expand Down Expand Up @@ -143,7 +167,6 @@ class Main(settings: Settings, reporter: Reporter) {
}
}
}
index.save(out)
success
}

Expand Down
Expand Up @@ -45,6 +45,16 @@ object Locator {
try fn(path, TextDocuments.parseFrom(stream))
finally stream.close()
}
val manifest = jar.getManifest
if (manifest != null) {
val classpathAttr = manifest.getMainAttributes.getValue("Class-Path")
if (classpathAttr != null) {
classpathAttr.split(" ").foreach { relativePath =>
val parentPath = path.toAbsolutePath.getParent
apply(parentPath.resolve(relativePath))(fn)
}
}
}
} else if (path.toString.endsWith(".semanticdb")) {
val stream = Files.newInputStream(path)
try fn(path, TextDocuments.parseFrom(stream))
Expand Down
Binary file added tests/jvm/src/test/resources/manifest.jar
Binary file not shown.
23 changes: 23 additions & 0 deletions tests/jvm/src/test/resources/manifest.metacp
@@ -0,0 +1,23 @@
Test.class
----------

Summary:
Schema => SemanticDB v3
Uri => Test.class
Text => empty
Language => Scala
Symbols => 5 entries

Symbols:
_empty_. => package _empty_
_empty_.Test. => final object Test.{+2 decls}
extends AnyRef
_empty_.Test.main(Array). => method main: (args: Array[String]): Unit
args => _empty_.Test.main(Array).(args)
Array => scala.Array#
String => scala.Predef.String#
Unit => scala.Unit#
_empty_.Test.main(Array).(args) => param args: Array[String]
Array => scala.Array#
String => scala.Predef.String#
_root_. => package _root_
30 changes: 30 additions & 0 deletions tests/jvm/src/test/resources/manifest.metap
@@ -0,0 +1,30 @@
Test.scala
----------

Summary:
Schema => SemanticDB v3
Uri => Test.scala
Text => non-empty
Language => Scala
Symbols => 3 entries
Occurrences => 7 entries

Symbols:
_empty_.Test. => final object Test
_empty_.Test.main(Array). => method main: (args: Array[String]): Unit
args => _empty_.Test.main(Array).(args)
Array => scala.Array#
String => scala.Predef.String#
Unit => scala.Unit#
_empty_.Test.main(Array).(args) => param args: Array[String]
Array => scala.Array#
String => scala.Predef.String#

Occurrences:
[0:7..0:11): Test <= _empty_.Test.
[1:6..1:10): main <= _empty_.Test.main(Array).
[1:11..1:15): args <= _empty_.Test.main(Array).(args)
[1:17..1:22): Array => scala.Array#
[1:23..1:29): String => scala.Predef.String#
[1:33..1:37): Unit => scala.Unit#
[2:4..2:11): println => scala.Predef.println(Any).
Binary file added tests/jvm/src/test/resources/part0.jar
Binary file not shown.
Binary file added tests/jvm/src/test/resources/part1.jar
Binary file not shown.
Expand Up @@ -61,6 +61,14 @@ class ExpectSuite extends FunSuite with DiffAssertions {
import MetacMetacpIndexDiffExpect._
assertNoDiff(loadObtained, loadExpected)
}
test("manifest.metap") {
import ManifestMetap._
assertNoDiff(loadObtained, loadExpected)
}
test("manifest.metacp") {
import ManifestMetacp._
assertNoDiff(loadObtained, loadExpected)
}
case _ =>
()
}
Expand Down Expand Up @@ -327,6 +335,22 @@ object MetacMetacpIndexDiffExpect extends ExpectHelpers {
}
}

object ManifestMetap extends ExpectHelpers {
def filename: String = "manifest.metap"
def loadObtained: String = {
val manifestJar = path.getParent.resolve("manifest.jar")
lowlevelSyntax(manifestJar)
}
}

object ManifestMetacp extends ExpectHelpers {
def filename: String = "manifest.metacp"
def loadObtained: String = {
val manifestJar = path.getParent.resolve("manifest.jar")
lowlevelSyntax(decompiledPath(manifestJar))
}
}

// To save the current behavior, run `sbt save-expect`.
object SaveExpectTest {
def main(args: Array[String]): Unit = {
Expand All @@ -339,5 +363,7 @@ object SaveExpectTest {
MetacOwnersExpect.saveExpected(MetacOwnersExpect.loadObtained)
MetacMetacpExpectDiffExpect.saveExpected(MetacMetacpExpectDiffExpect.loadObtained)
MetacMetacpIndexDiffExpect.saveExpected(MetacMetacpIndexDiffExpect.loadObtained)
ManifestMetap.saveExpected(ManifestMetap.loadObtained)
ManifestMetacp.saveExpected(ManifestMetacp.loadObtained)
}
}

0 comments on commit 5fd0a63

Please sign in to comment.