Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
78 changes: 78 additions & 0 deletions lib/scalasem/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -398,12 +398,90 @@ export function normaliseSourcePath(projectDir, p) {
/**
* Parse a Maven coordinate out of a repository layout path, for example
* `<cache>/https/repo1.maven.org/maven2/org/bouncycastle/bcprov-jdk18on/1.86/bcprov-jdk18on-1.86.jar`.
* The Ivy local repository, the Ivy cache and the Gradle cache name the group as one dotted
* directory instead:
* `~/.ivy2/local/<org>/<artifact>/<version>/jars/<artifact>.jar`,
* `~/.ivy2/cache/<org>/<artifact>/jars/<artifact>-<version>.jar` and
* `~/.gradle/caches/modules-2/files-2.1/<group>/<artifact>/<version>/<sha1>/<file>.jar`.
*
* @param {string} jarPath Jar location
* @returns {{ group: string, artifact: string, version: string }|undefined}
*/
export function jarCoordinate(jarPath) {
const parts = String(jarPath).replaceAll("\\", "/").split("/");
return (
mavenLayoutCoordinate(parts) ||
gradleCacheCoordinate(parts) ||
ivyLayoutCoordinate(parts)
);
}

const COORDINATE_SEGMENT = /^[A-Za-z0-9_.-]+$/;
// Ivy keeps each artifact type in a directory named after the type in the plural.
const IVY_TYPE_DIRS = new Set(["jars", "bundles", "orbits", "eclipse-plugins"]);

function coordinate(group, artifact, version) {
if (
!group ||
!artifact ||
!version ||
!COORDINATE_SEGMENT.test(group) ||
!COORDINATE_SEGMENT.test(artifact) ||
!/^\d/.test(version)
) {
return undefined;
}
return { group, artifact, version };
}

/** `files-2.1/<group>/<artifact>/<version>/<sha1>/<file>.jar` */
function gradleCacheCoordinate(parts) {
const at = parts.lastIndexOf("files-2.1");
if (at < 0 || parts.length !== at + 6 || !parts[at + 5].endsWith(".jar")) {
return undefined;
}
if (!/^[0-9a-f]+$/.test(parts[at + 4])) {
return undefined;
}
return coordinate(parts[at + 1], parts[at + 2], parts[at + 3]);
}

/**
* The Ivy local layout `<org>/<artifact>/<version>/jars/<artifact>.jar`, where sbt plugins
* add `scala_<v>/sbt_<v>` before the version, and the Ivy cache layout
* `<org>/<artifact>/jars/<artifact>-<version>.jar`.
*/
function ivyLayoutCoordinate(parts) {
const n = parts.length;
const file = parts[n - 1];
if (n < 4 || !file?.endsWith(".jar") || !IVY_TYPE_DIRS.has(parts[n - 2])) {
return undefined;
}
const stem = file.slice(0, -".jar".length);
const cacheArtifact = parts[n - 3];
if (stem.startsWith(`${cacheArtifact}-`)) {
const version = stem.slice(cacheArtifact.length + 1);
const cached = coordinate(parts[n - 4], cacheArtifact, version);
if (cached) {
return cached;
}
}
const version = parts[n - 3];
let at = n - 4;
while (
at >= 1 &&
(parts[at].startsWith("scala_") || parts[at].startsWith("sbt_"))
) {
at -= 1;
}
const artifact = parts[at];
if (stem !== artifact && !stem.startsWith(`${artifact}-`)) {
return undefined;
}
return coordinate(parts[at - 1], artifact, version);
}

function mavenLayoutCoordinate(parts) {
for (let i = parts.length - 3; i >= 1; i--) {
const artifact = parts[i];
const version = parts[i + 1];
Expand Down
43 changes: 43 additions & 0 deletions test-fixtures/scalasem-engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,49 @@ assert.deepStrictEqual(
version: "11.0.4"
}
);
// The Ivy local repository that `publishLocal` writes, with and without the sbt plugin
// directories, the Ivy cache of sbt 0.13 to 1.2, and the Gradle cache.
for (const [jarPath, expected] of [
[
"/home/u/.ivy2/local/io.appthreat/c2cpg_3/4.0.0/jars/c2cpg_3.jar",
{ group: "io.appthreat", artifact: "c2cpg_3", version: "4.0.0" }
],
[
"C:\\Users\\u\\.ivy2\\local\\io.appthreat\\x2cpg_3\\4.0.0-jrtfix\\jars\\x2cpg_3.jar",
{ group: "io.appthreat", artifact: "x2cpg_3", version: "4.0.0-jrtfix" }
],
[
"/home/u/.ivy2/local/com.github.sbt/sbt-native-packager/scala_2.12/sbt_1.0/1.11.1/jars/sbt-native-packager.jar",
{
group: "com.github.sbt",
artifact: "sbt-native-packager",
version: "1.11.1"
}
],
[
"/home/u/.ivy2/cache/org.scala-lang/scala-library/jars/scala-library-2.12.20.jar",
{ group: "org.scala-lang", artifact: "scala-library", version: "2.12.20" }
],
[
"/home/u/.ivy2/cache/org.typelevel/cats-core_2.13/bundles/cats-core_2.13-2.13.0.jar",
{ group: "org.typelevel", artifact: "cats-core_2.13", version: "2.13.0" }
],
[
"/home/u/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/33.6.0-jre/8a1f4bd2c0d4e9e1c1f5a3b2d6e7f8091a2b3c4d/guava-33.6.0-jre.jar",
{ group: "com.google.guava", artifact: "guava", version: "33.6.0-jre" }
]
]) {
assert.deepStrictEqual(jarCoordinate(jarPath), expected, jarPath);
}
// An unmanaged jar and a jar whose directories name no release have no coordinate.
for (const jarPath of [
"/src/atom/lib/org.eclipse.cdt.core_9.3.200.202607131546.jar",
"/home/u/.ivy2/local/io.appthreat/c2cpg_3/latest/jars/c2cpg_3.jar",
"/home/u/.ivy2/local/io.appthreat/c2cpg_3/4.0.0/jars/other.jar",
"/home/u/.gradle/caches/modules-2/files-2.1/com.google.guava/guava/33.6.0-jre/guava-33.6.0-jre.jar"
]) {
assert.strictEqual(jarCoordinate(jarPath), undefined, jarPath);
}

assert.strictEqual(
sanitizeUrl(
Expand Down
Loading