@@ -6,24 +6,26 @@ import java.nio.file.Files
6
6
import java .nio .file .Path
7
7
import java .nio .file .Paths
8
8
9
+ import scala .annotation .tailrec
9
10
import scala .jdk .CollectionConverters ._
10
11
11
12
import com .sourcegraph .scip_semanticdb .MavenPackage
12
13
13
14
/**
14
- * Represents a single jar file on the classpath of a project, used to emit SCIP
15
- * "packageInformation" nodes.
15
+ * Represents a single classpath entry on the classpath of a project, used to
16
+ * emit SCIP "packageInformation" nodes. A classpath entry can either be a jar
17
+ * file or a directory path.
16
18
*/
17
19
case class ClasspathEntry (
18
- jar : Path ,
20
+ entry : Path ,
19
21
sources : Option [Path ],
20
22
groupId : String ,
21
23
artifactId : String ,
22
24
version : String
23
25
) {
24
26
def toPackageHubId : String = s " maven: $groupId: $artifactId: $version"
25
27
def toPackageInformation : MavenPackage =
26
- new MavenPackage (jar , groupId, artifactId, version)
28
+ new MavenPackage (entry , groupId, artifactId, version)
27
29
}
28
30
29
31
object ClasspathEntry {
@@ -39,13 +41,16 @@ object ClasspathEntry {
39
41
* @param targetroot
40
42
* @return
41
43
*/
42
- def fromTargetroot (targetroot : Path ): List [ClasspathEntry ] = {
44
+ def fromTargetroot (
45
+ targetroot : Path ,
46
+ sourceroot : Path
47
+ ): List [ClasspathEntry ] = {
43
48
val javacopts = targetroot.resolve(" javacopts.txt" )
44
49
val dependencies = targetroot.resolve(" dependencies.txt" )
45
50
if (Files .isRegularFile(dependencies)) {
46
51
fromDependencies(dependencies)
47
52
} else if (Files .isRegularFile(javacopts)) {
48
- fromJavacopts(javacopts)
53
+ fromJavacopts(javacopts, sourceroot )
49
54
} else {
50
55
Nil
51
56
}
@@ -55,17 +60,18 @@ object ClasspathEntry {
55
60
* Parses ClasspathEntry from a "dependencies.txt" file in the targetroot.
56
61
*
57
62
* Every line of the file is a tab separated value with the following columns:
58
- * groupId, artifactId, version, path to the jar file.
63
+ * groupId, artifactId, version, path to the jar file OR classes directory
64
+ * path.
59
65
*/
60
66
private def fromDependencies (dependencies : Path ): List [ClasspathEntry ] = {
61
67
Files
62
68
.readAllLines(dependencies, StandardCharsets .UTF_8 )
63
69
.asScala
64
70
.iterator
65
71
.map(_.split(" \t " ))
66
- .collect { case Array (groupId, artifactId, version, jar ) =>
72
+ .collect { case Array (groupId, artifactId, version, entry ) =>
67
73
ClasspathEntry (
68
- jar = Paths .get(jar ),
74
+ entry = Paths .get(entry ),
69
75
sources = None ,
70
76
groupId = groupId,
71
77
artifactId = artifactId,
@@ -81,36 +87,70 @@ object ClasspathEntry {
81
87
* Every line of the file represents a Java compiler options, such as
82
88
* "-classpath" or "-encoding".
83
89
*/
84
- private def fromJavacopts (javacopts : Path ): List [ClasspathEntry ] = {
90
+ private def fromJavacopts (
91
+ javacopts : Path ,
92
+ sourceroot : Path
93
+ ): List [ClasspathEntry ] = {
85
94
Files
86
95
.readAllLines(javacopts, StandardCharsets .UTF_8 )
87
96
.asScala
88
97
.iterator
89
98
.map(_.stripPrefix(" \" " ).stripSuffix(" \" " ))
90
99
.sliding(2 )
91
- .collect { case Seq (" -cp" | " -classpath" , classpath) =>
92
- classpath.split(File .pathSeparator).iterator
100
+ .collect {
101
+ case Seq (" -d" , classesDirectory) =>
102
+ fromClassesDirectory(Paths .get(classesDirectory), sourceroot).toList
103
+ case Seq (" -cp" | " -classpath" , classpath) =>
104
+ classpath
105
+ .split(File .pathSeparator)
106
+ .iterator
107
+ .map(Paths .get(_))
108
+ .flatMap(ClasspathEntry .fromClasspathJarFile)
109
+ .toList
93
110
}
94
111
.flatten
95
- .map(Paths .get(_))
96
- .toSet
97
- .iterator
98
- .flatMap(ClasspathEntry .fromPom)
99
112
.toList
100
113
}
101
114
115
+ private def fromClassesDirectory (
116
+ classesDirectory : Path ,
117
+ sourceroot : Path
118
+ ): Option [ClasspathEntry ] = {
119
+ @ tailrec
120
+ def loop (dir : Path ): Option [ClasspathEntry ] = {
121
+ if (dir == null || ! dir.startsWith(sourceroot))
122
+ None
123
+ else
124
+ fromPomXml(dir.resolve(" pom.xml" ), classesDirectory, None ) match {
125
+ case None =>
126
+ loop(dir.getParent())
127
+ case Some (value) =>
128
+ Some (value)
129
+ }
130
+ }
131
+ loop(classesDirectory.getParent())
132
+ }
133
+
102
134
/**
103
135
* Tries to parse a ClasspathEntry from the POM file that lies next to the
104
136
* given jar file.
105
137
*/
106
- private def fromPom (jar : Path ): Option [ClasspathEntry ] = {
138
+ private def fromClasspathJarFile (jar : Path ): Option [ClasspathEntry ] = {
107
139
val pom = jar
108
140
.resolveSibling(jar.getFileName.toString.stripSuffix(" .jar" ) + " .pom" )
109
141
val sources = Option (
110
142
jar.resolveSibling(
111
143
jar.getFileName.toString.stripSuffix(" .jar" ) + " .sources"
112
144
)
113
145
).filter(Files .isRegularFile(_))
146
+ fromPomXml(pom, jar, sources)
147
+ }
148
+
149
+ private def fromPomXml (
150
+ pom : Path ,
151
+ classpathEntry : Path ,
152
+ sources : Option [Path ]
153
+ ): Option [ClasspathEntry ] = {
114
154
if (Files .isRegularFile(pom)) {
115
155
val xml = scala.xml.XML .loadFile(pom.toFile)
116
156
def xmlValue (key : String ): String = {
@@ -123,7 +163,9 @@ object ClasspathEntry {
123
163
val groupId = xmlValue(" groupId" )
124
164
val artifactId = xmlValue(" artifactId" )
125
165
val version = xmlValue(" version" )
126
- Some (ClasspathEntry (jar, sources, groupId, artifactId, version))
166
+ Some (
167
+ ClasspathEntry (classpathEntry, sources, groupId, artifactId, version)
168
+ )
127
169
} else {
128
170
None
129
171
}
0 commit comments