Skip to content

Commit c5e0091

Browse files
authored
Extend behaviour for Maven classifiers and types (#53)
* Handle Maven classifier corner cases * Add test cases for Maven classifier/tag behaviour Add test case with fixed version of plugin to demonstrate bug. Add test case with new version of plugin and expected success.
1 parent 4805850 commit c5e0091

File tree

30 files changed

+888
-26
lines changed

30 files changed

+888
-26
lines changed

src/main/scala/com/typesafe/sbt/pom/MavenHelper.scala

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -190,37 +190,64 @@ object MavenHelper {
190190

191191
def convertDep(dep: PomDependency): sbt.ModuleID = {
192192
// TODO - Handle mapping all the maven oddities into sbt's DSL.
193-
val scopeString: Option[String] =
194-
for {
195-
scope <- Option(dep.getScope)
196-
} yield scope
197-
198-
def fixScope(m: ModuleID): ModuleID =
199-
scopeString match {
200-
case Some(scope) => m % scope
201-
case None => m
202-
}
203-
193+
// See https://maven.apache.org/ref/3.6.3/maven-core/artifact-handlers.html for
194+
// matrix on Maven side of attributes
204195
def addExclusions(mod: ModuleID): ModuleID = {
205196
val exclusions = dep.getExclusions.asScala
206197
exclusions.foldLeft(mod) { (mod, exclude) =>
207198
mod.exclude(exclude.getGroupId, exclude.getArtifactId)
208199
}
209200
}
201+
210202
def addClassifier(mod: ModuleID): ModuleID = {
211203
Option(dep.getClassifier) match {
212204
case Some(_classifier) => mod classifier _classifier
213205
case None => mod
214206
}
215207
}
216-
addExclusions(addClassifier(fixScope(dep.getGroupId % dep.getArtifactId % dep.getVersion)))
208+
209+
def addConfiguration(mod: ModuleID): ModuleID = {
210+
val mvnScope = Option(dep.getScope)
211+
val mvnType = Option(dep.getType)
212+
val mvnClassifier = Option(dep.getClassifier)
213+
214+
(mvnScope, mvnType) match {
215+
// <type>test-jar</type>
216+
// <scope>SCOPE</scope>
217+
case (Some(mScope), Some(mType)) if ("test-jar".equalsIgnoreCase(mType)) =>
218+
mod
219+
.withConfigurations(Some(s"${mScope}->test"))
220+
.intransitive
221+
// <classifier>tests</classifier>
222+
// <scope>SCOPE</scope>
223+
case (Some(mScope), _) if (mvnClassifier.getOrElse("").equalsIgnoreCase("tests")) =>
224+
mod
225+
.withConfigurations(Some(s"${mScope}->test"))
226+
// <scope>SCOPE</scope>
227+
case (Some(mScope), _) =>
228+
mod % mScope
229+
case _ => mod
230+
}
231+
}
232+
233+
// order does not matter
234+
def conversion = addExclusions _ andThen addClassifier andThen addConfiguration
235+
236+
conversion(ModuleID(dep.getGroupId, dep.getArtifactId, dep.getVersion))
217237
}
218-
238+
219239
def getDependencies(pom: PomModel): Seq[ModuleID] = {
220240
for {
221241
dep <- pom.getDependencies.asScala
222242
} yield convertDep(dep)
223243
}
244+
245+
/** Filters module dependencies within same group id, which results in list
246+
* of modules this one depends on. */
247+
def getModuleDependencies(pom: PomModel): Seq[ModuleID] = {
248+
val allDependencies = getDependencies(pom)
249+
allDependencies.filter { dep => dep.organization == pom.getGroupId }
250+
}
224251

225252
def getPomResolvers(pom: PomModel): Seq[Resolver] = {
226253
for {

src/main/scala/com/typesafe/sbt/pom/MavenProjectHelper.scala

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ object MavenProjectHelper {
4848
// Next flatten the list of all projects.
4949
val projects = allProjectsInTree(tree)
5050
// Create a mapping of all dependencies between projects.
51-
val depMap = makeDependencyMap(projects)
51+
val depMap: Map[ProjectTree, Seq[ProjectTree]] = makeDependencyMap(projects)
5252
// Helper to look up dependencies in the presence of absences.
5353
def getDepsFor(project: ProjectTree): Seq[ProjectTree] =
5454
depMap.getOrElse(project, Nil)
@@ -62,11 +62,11 @@ object MavenProjectHelper {
6262
val deps = getDepsFor(project)
6363
aggregates ++ deps
6464
}
65-
def makeProjects(toMake: Seq[ProjectTree], made: Map[ProjectTree, Project] = Map.empty): Seq[Project] =
65+
def makeProjects(toMake: Seq[ProjectTree], made: Map[ProjectTree, (Project, ModuleID)] = Map.empty): Seq[Project] =
6666
toMake match {
6767
case current :: rest =>
6868
// Make a project, and add it to the stack
69-
val depProjects: Seq[Project] =
69+
val depProjects: Seq[(Project, ModuleID)] =
7070
for {
7171
dep <- getDepsFor(current)
7272
depProject <- made.get(dep)
@@ -77,16 +77,32 @@ object MavenProjectHelper {
7777
for {
7878
child <- children
7979
depProject <- made.get(child)
80-
} yield depProject
80+
} yield depProject._1
8181
case _ => Nil
8282
}
83+
// Prepare data:
84+
// list of projects and their moduleIds with correct configurations,
85+
// according to current node in POM we are processing now
86+
val modulesMap: Map[String, Seq[ModuleID]] =
87+
MavenHelper
88+
.getModuleDependencies(current.model)
89+
.groupBy { m => makeId(m.organization, m.name, m.revision) }
90+
val projectsMap: Map[String, Seq[(Project, ModuleID)]] = depProjects.groupBy { case (p, m) => makeId(m.organization, m.name, m.revision) }
91+
val projectsWithModules: Seq[(Project, ModuleID)] = modulesMap.keySet.map { id =>
92+
val mwc: Seq[ModuleID] = modulesMap.get(id).getOrElse(Seq.empty)
93+
val projects: Seq[Project] = projectsMap.get(id).getOrElse(Seq.empty).map(_._1)
94+
projects.zip(mwc)
95+
}
96+
.flatten
97+
.toSeq
98+
8399
// TODO - Configure debugging output....
84-
val currentProject = (
85-
Project(makeProjectName(current.model,overrideRootProjectName),current.dir)
100+
val currentProject: Project = (
101+
Project(makeProjectName(current.model,overrideRootProjectName), current.dir)
86102
// First pull in settings from pom
87103
settings(useMavenPom:_*)
88-
// Now update depends on relationships
89-
dependsOn(depProjects.map(x =>x: ClasspathDep[ProjectReference]):_*)
104+
// Now update depends on relationships with actual configurations
105+
dependsOn( projectsWithModules.map { case (p, m) => new ClasspathDependency(p, m.configurations) }: _* )
90106
// Now fix aggregate relationships
91107
aggregate(aggregates.map(x => x:ProjectReference):_*)
92108
// Now remove any inter-project dependencies we pulled in from the maven pom.
@@ -98,13 +114,13 @@ object MavenProjectHelper {
98114
Keys.libraryDependencies.value.filterNot { dep =>
99115
val id = makeId(dep.organization, dep.name, dep.revision)
100116
depIds contains id
101-
}
102-
}
117+
}
118+
}
103119
)
104-
105120
)
106-
makeProjects(rest, made + (current -> currentProject))
107-
case Nil => made.values.toSeq
121+
val currentModule = ModuleID(current.model.getGroupId, current.model.getArtifactId, current.model.getVersion)
122+
makeProjects(rest, made + (current -> (currentProject, currentModule)))
123+
case Nil => made.values.map(_._1).toSeq
108124
}
109125
makeProjects(sorted)
110126
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
2+
<modelVersion>4.0.0</modelVersion>
3+
<groupId>net.gemelen.example</groupId>
4+
<artifactId>module1_2.12</artifactId>
5+
<version>1.0</version>
6+
<packaging>jar</packaging>
7+
8+
<parent>
9+
<groupId>net.gemelen.example</groupId>
10+
<artifactId>example-parent_2.12</artifactId>
11+
<version>1.0</version>
12+
<relativePath>../../pom.xml</relativePath>
13+
</parent>
14+
15+
<properties>
16+
<sbt.project.name>module1</sbt.project.name>
17+
</properties>
18+
19+
<dependencies>
20+
<dependency>
21+
<groupId>org.scala-lang</groupId>
22+
<artifactId>scala-library</artifactId>
23+
<version>${scala.version}</version>
24+
</dependency>
25+
</dependencies>
26+
27+
<build>
28+
<outputDirectory>target/scala-${scala.binary.version}/classes</outputDirectory>
29+
<testOutputDirectory>target/scala-${scala.binary.version}/test-classes</testOutputDirectory>
30+
</build>
31+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package net.gemelen.example.annotation;
2+
3+
import java.lang.annotation.*;
4+
5+
@Retention(RetentionPolicy.RUNTIME)
6+
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER,
7+
ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE, ElementType.PACKAGE})
8+
public @interface ExampleAnnotation1 {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package net.gemelen.example.annotation
2+
3+
import scala.annotation.StaticAnnotation
4+
import scala.annotation.meta._
5+
6+
@param @field @getter @setter @beanGetter @beanSetter
7+
class ExampleAnnotation2(version: String) extends StaticAnnotation
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package net.gemelen.example.annotation;
2+
3+
import org.scalatest.TagAnnotation;
4+
5+
import java.lang.annotation.ElementType;
6+
import java.lang.annotation.Retention;
7+
import java.lang.annotation.RetentionPolicy;
8+
import java.lang.annotation.Target;
9+
10+
@TagAnnotation
11+
@Retention(RetentionPolicy.RUNTIME)
12+
@Target({ElementType.METHOD, ElementType.TYPE})
13+
public @interface MarkerTest { }
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
2+
<modelVersion>4.0.0</modelVersion>
3+
<groupId>net.gemelen.example</groupId>
4+
<artifactId>core_2.12</artifactId>
5+
<version>1.0</version>
6+
<packaging>jar</packaging>
7+
8+
<parent>
9+
<groupId>net.gemelen.example</groupId>
10+
<artifactId>example-parent_2.12</artifactId>
11+
<version>1.0</version>
12+
<relativePath>../pom.xml</relativePath>
13+
</parent>
14+
15+
<properties>
16+
<sbt.project.name>core</sbt.project.name>
17+
</properties>
18+
19+
<dependencies>
20+
<dependency>
21+
<groupId>net.gemelen.example</groupId>
22+
<artifactId>other_${scala.binary.version}</artifactId>
23+
<version>${project.version}</version>
24+
</dependency>
25+
<dependency>
26+
<groupId>net.gemelen.example</groupId>
27+
<artifactId>other_${scala.binary.version}</artifactId>
28+
<version>${project.version}</version>
29+
<classifier>tests</classifier>
30+
<scope>test</scope>
31+
</dependency>
32+
33+
<dependency>
34+
<groupId>net.gemelen.example</groupId>
35+
<artifactId>module1_${scala.binary.version}</artifactId>
36+
<version>${project.version}</version>
37+
</dependency>
38+
<dependency>
39+
<groupId>net.gemelen.example</groupId>
40+
<artifactId>module1_${scala.binary.version}</artifactId>
41+
<version>${project.version}</version>
42+
<classifier>tests</classifier>
43+
<type>test-jar</type>
44+
<scope>test</scope>
45+
</dependency>
46+
47+
<dependency>
48+
<groupId>org.scala-lang</groupId>
49+
<artifactId>scala-library</artifactId>
50+
<version>${scala.version}</version>
51+
</dependency>
52+
</dependencies>
53+
54+
<build>
55+
<outputDirectory>target/scala-${scala.binary.version}/classes</outputDirectory>
56+
<testOutputDirectory>target/scala-${scala.binary.version}/test-classes</testOutputDirectory>
57+
</build>
58+
</project>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package net.gemelen.example.core;
2+
3+
import net.gemelen.example.annotation.ExampleAnnotation1;
4+
5+
public class Core {
6+
7+
@ExampleAnnotation1
8+
public void method() {}
9+
}
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package net.gemelen.example.core;
2+
3+
import net.gemelen.example.annotation.MarkerTest;
4+
5+
public class JavaCoreTest {
6+
7+
@MarkerTest
8+
public void method() {}
9+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package net.gemelen.example.core
2+
3+
import net.gemelen.example.annotation.ExampleAnnotation1
4+
import net.gemelen.example.annotation.ExampleAnnotation2
5+
6+
class CoreTest {
7+
8+
@ExampleAnnotation1
9+
@ExampleAnnotation2(version = "something")
10+
def f(): Unit = {}
11+
}

0 commit comments

Comments
 (0)