Skip to content

Commit 7c08b92

Browse files
yadavan88Gedochao
andauthored
Add support for exporting to a Maven project (#3003)
* Option to export pom file for a Java Project * Fixed format and linting issues * Added todo comment * resolved review comments * revert accident edit * Regenarated reference docs * trying maven * Added support for scala with maven * Linting issues fixed * resolved the scala version correctly and append to dependency * fixed scope issue * fixed the dependency issue * Fixed format * Addressing review comments * Addressing review comments * Added tests for other scala version, fixed formatting and linting * Added tests for other scala version, fixed formatting and linting * Added language separation for maven profiles * separate command in test for java and scala projects * Fixed issues with pom generation * reformatting * fixed dependency resolution issue * Separated scala specific tests into a separate trait to support Maven export * Separated scala specific tests into a separate trait to support Maven export * Passed the exec command as a build param in maven * Passed the exec command as a build param in maven * Adjusted maven export tests * fixed formatting * Added option to define resource directory in maven pom * fixed linting issues * Fixed scala version during export to maven * Fix tests in progress * Fix tests * moved zio test spec to scala-specific build tool test * Fixed maven tests * scalafmt ran * disabled running scala export tests in maven java export * Fixed zio export test for mill - temp fix * Fixed some review comments and also fixed issue with zio test * Used constants for maven default versions * Used constants for maven default versions * resolved conflicts * Fixed issue with test scope in maven export * Fixed maven test * Update build.sc Co-authored-by: Piotr Chabelski <ged.subfan@gmail.com> * removed old todo * Fixed the issue with maven export when same libraries are coming from both test and main scope * added maven-exec plugin in java mode * addressed review comments * Added maven groupId etc in command line --------- Co-authored-by: Piotr Chabelski <ged.subfan@gmail.com>
1 parent dddef7f commit 7c08b92

24 files changed

+889
-123
lines changed

build.sc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,13 @@ trait Core extends ScalaCliCrossSbtModule
491491
| def giter8Organization = "${Deps.giter8.dep.module.organization.value}"
492492
| def giter8Name = "${Deps.giter8.dep.module.name.value}"
493493
| def giter8Version = "${Deps.giter8.dep.version}"
494+
|
495+
| def mavenVersion = "${Deps.Versions.mavenVersion}"
496+
| def mavenScalaCompilerPluginVersion = "${Deps.Versions.mavenScalaCompilerPluginVersion}"
497+
| def mavenExecPluginVersion = "${Deps.Versions.mavenExecPluginVersion}"
498+
| def mavenAppArtifactId = "${Deps.Versions.mavenAppArtifactId}"
499+
| def mavenAppGroupId = "${Deps.Versions.mavenAppGroupId}"
500+
| def mavenAppVersion = "${Deps.Versions.mavenAppVersion}"
494501
|}
495502
|""".stripMargin
496503
if (!os.isFile(dest) || os.read(dest) != code)

modules/cli/src/main/scala/scala/cli/commands/export0/Export.scala

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,28 @@ object Export extends ScalaCommand[ExportOptions] {
7979
logger: Logger
8080
): SbtProjectDescriptor =
8181
SbtProjectDescriptor(sbtVersion, extraSettings, logger)
82+
83+
def mavenProjectDescriptor(
84+
mavenPluginVersion: String,
85+
mavenScalaPluginVersion: String,
86+
mavenExecPluginVersion: String,
87+
extraSettings: Seq[String],
88+
mavenGroupId: String,
89+
mavenArtifactId: String,
90+
mavenVersion: String,
91+
logger: Logger
92+
): MavenProjectDescriptor =
93+
MavenProjectDescriptor(
94+
mavenPluginVersion,
95+
mavenScalaPluginVersion,
96+
mavenExecPluginVersion,
97+
extraSettings,
98+
mavenGroupId,
99+
mavenArtifactId,
100+
mavenVersion,
101+
logger
102+
)
103+
82104
def millProjectDescriptor(
83105
cache: FileCache[Task],
84106
projectName: Option[String],
@@ -129,17 +151,22 @@ object Export extends ScalaCommand[ExportOptions] {
129151
sys.exit(1)
130152
}
131153

132-
val shouldExportToMill = options.mill.getOrElse(false)
133-
val shouldExportToSbt = options.sbt.getOrElse(false)
134-
if (shouldExportToMill && shouldExportToSbt) {
154+
val shouldExportToMill = options.mill.getOrElse(false)
155+
val shouldExportToSbt = options.sbt.getOrElse(false)
156+
val shouldExportToMaven = options.maven.getOrElse(false)
157+
158+
val exportOptions = List(shouldExportToMill, shouldExportToSbt, shouldExportToMaven)
159+
160+
if (exportOptions.count(identity) > 1) {
135161
logger.error(
136162
s"Error: Cannot export to both mill and sbt. Please pick one build tool to export."
137163
)
138164
sys.exit(1)
139165
}
140166

141167
if (!shouldExportToJson) {
142-
val buildToolName = if (shouldExportToMill) "mill" else "sbt"
168+
val buildToolName =
169+
if (shouldExportToMill) "mill" else if (shouldExportToMaven) "maven" else "sbt"
143170
logger.message(s"Exporting to a $buildToolName project...")
144171
}
145172
else if (!shouldExportJsonToStdout)
@@ -211,14 +238,36 @@ object Export extends ScalaCommand[ExportOptions] {
211238
project.print(System.out)
212239
}
213240
else {
214-
val sbtVersion = options.sbtVersion.getOrElse("1.10.1")
241+
val sbtVersion = options.sbtVersion.getOrElse("1.10.1")
242+
val defaultMavenCompilerVersion = options.mvnVersion.getOrElse(Constants.mavenVersion)
243+
val defaultScalaMavenCompilerVersion =
244+
options.mvnScalaVersion.getOrElse(Constants.mavenScalaCompilerPluginVersion)
245+
val defaultMavenExecPluginVersion =
246+
options.mvnExecPluginVersion.getOrElse(Constants.mavenExecPluginVersion)
247+
val defaultMavenArtifactId =
248+
options.mvnAppArtifactId.getOrElse(Constants.mavenAppArtifactId)
249+
val defaultMavenGroupId =
250+
options.mvnAppGroupId.getOrElse(Constants.mavenAppGroupId)
251+
val defaultMavenVersion =
252+
options.mvnAppVersion.getOrElse(Constants.mavenAppVersion)
215253

216254
def sbtProjectDescriptor0 =
217255
sbtProjectDescriptor(options.sbtSetting.map(_.trim).filter(_.nonEmpty), sbtVersion, logger)
218256

219257
val projectDescriptor =
220258
if (shouldExportToMill)
221259
millProjectDescriptor(options.shared.coursierCache, options.project, logger)
260+
else if (shouldExportToMaven)
261+
mavenProjectDescriptor(
262+
defaultMavenCompilerVersion,
263+
defaultScalaMavenCompilerVersion,
264+
defaultMavenExecPluginVersion,
265+
Nil,
266+
defaultMavenGroupId,
267+
defaultMavenArtifactId,
268+
defaultMavenVersion,
269+
logger
270+
)
222271
else if (shouldExportToJson)
223272
jsonProjectDescriptor(options.project, inputs.workspace, logger)
224273
else // shouldExportToSbt isn't checked, as it's treated as default

modules/cli/src/main/scala/scala/cli/commands/export0/ExportOptions.scala

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ final case class ExportOptions(
2727
@HelpMessage("Sets the export format to SBT")
2828
sbt: Option[Boolean] = None,
2929
@Group(HelpGroup.BuildToolExport.toString)
30+
@Tag(tags.experimental)
31+
@Tag(tags.inShortHelp)
32+
@HelpMessage("Sets the export format to Maven")
33+
@Name("mvn")
34+
maven: Option[Boolean] = None,
35+
@Group(HelpGroup.BuildToolExport.toString)
3036
@Tag(tags.restricted)
3137
@Tag(tags.inShortHelp)
3238
@HelpMessage("Sets the export format to Mill")
@@ -50,6 +56,30 @@ final case class ExportOptions(
5056
@Tag(tags.restricted)
5157
@HelpMessage("Version of SBT to be used for the export")
5258
sbtVersion: Option[String] = None,
59+
@Group(HelpGroup.BuildToolExport.toString)
60+
@Tag(tags.experimental)
61+
@HelpMessage("Version of Maven Compiler Plugin to be used for the export")
62+
mvnVersion: Option[String] = None,
63+
@Group(HelpGroup.BuildToolExport.toString)
64+
@Tag(tags.experimental)
65+
@HelpMessage("Version of Maven Scala Plugin to be used for the export")
66+
mvnScalaVersion: Option[String] = None,
67+
@Group(HelpGroup.BuildToolExport.toString)
68+
@Tag(tags.experimental)
69+
@HelpMessage("Version of Maven Exec Plugin to be used for the export")
70+
mvnExecPluginVersion: Option[String] = None,
71+
@Group(HelpGroup.BuildToolExport.toString)
72+
@Tag(tags.experimental)
73+
@HelpMessage("ArtifactId to be used for the maven export")
74+
mvnAppArtifactId: Option[String] = None,
75+
@Group(HelpGroup.BuildToolExport.toString)
76+
@Tag(tags.experimental)
77+
@HelpMessage("GroupId to be used for the maven export")
78+
mvnAppGroupId: Option[String] = None,
79+
@Group(HelpGroup.BuildToolExport.toString)
80+
@Tag(tags.experimental)
81+
@HelpMessage("Version to be used for the maven export")
82+
mvnAppVersion: Option[String] = None,
5383
@Name("o")
5484
@Group(HelpGroup.BuildToolExport.toString)
5585
@Tag(tags.restricted)
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
package scala.cli.exportCmd
2+
3+
import os.RelPath
4+
5+
import java.nio.charset.StandardCharsets
6+
7+
import scala.build.options.{ConfigMonoid, Scope}
8+
import scala.xml.{Elem, NodeSeq, PrettyPrinter, XML}
9+
10+
final case class MavenProject(
11+
groupId: Option[String] = None,
12+
artifactId: Option[String] = None,
13+
version: Option[String] = None,
14+
plugins: Seq[MavenPlugin] = Nil,
15+
imports: Seq[String] = Nil,
16+
settings: Seq[Seq[String]] = Nil,
17+
dependencies: Seq[MavenLibraryDependency] = Nil,
18+
mainSources: Seq[(os.SubPath, String, Array[Byte])] = Nil,
19+
testSources: Seq[(os.SubPath, String, Array[Byte])] = Nil,
20+
resourceDirectories: Seq[String] = Nil
21+
) extends Project {
22+
23+
def +(other: MavenProject): MavenProject =
24+
MavenProject.monoid.orElse(this, other)
25+
26+
def writeTo(dir: os.Path): Unit = {
27+
28+
val nl = System.lineSeparator()
29+
val charset = StandardCharsets.UTF_8
30+
31+
val buildMavenContent = MavenModel(
32+
"4.0.0",
33+
groupId.getOrElse("groupId"),
34+
artifactId.getOrElse("artifactId"),
35+
version.getOrElse("0.1-SNAPSHOT"),
36+
dependencies,
37+
plugins,
38+
resourceDirectories
39+
)
40+
41+
val prettyPrinter = new PrettyPrinter(width = 80, step = 2)
42+
val formattedXml = prettyPrinter.format(buildMavenContent.toXml)
43+
44+
os.write(
45+
dir / "pom.xml",
46+
formattedXml.getBytes(charset)
47+
)
48+
49+
for ((path, language, content) <- mainSources) {
50+
val path0 = dir / "src" / "main" / language / path
51+
os.write(path0, content, createFolders = true)
52+
}
53+
for ((path, language, content) <- testSources) {
54+
val path0 = dir / "src" / "test" / language / path
55+
os.write(path0, content, createFolders = true)
56+
}
57+
58+
}
59+
}
60+
61+
object MavenProject {
62+
implicit val monoid: ConfigMonoid[MavenProject] = ConfigMonoid.derive
63+
}
64+
65+
final case class MavenModel(
66+
model: String,
67+
groupId: String,
68+
artifactId: String,
69+
version: String,
70+
dependencies: Seq[MavenLibraryDependency],
71+
plugins: Seq[MavenPlugin],
72+
resourceDirectories: Seq[String]
73+
) {
74+
75+
private def resourceNodes: NodeSeq =
76+
if (resourceDirectories.isEmpty)
77+
NodeSeq.Empty
78+
else {
79+
val resourceNodes = resourceDirectories.map { path =>
80+
<resource>
81+
<directory>
82+
{path}
83+
</directory>
84+
</resource>
85+
}
86+
<resources>
87+
{resourceNodes}
88+
</resources>
89+
}
90+
91+
def toXml: Elem =
92+
<project>
93+
<modelVersion>{model}</modelVersion>
94+
<groupId>{groupId}</groupId>
95+
<artifactId>{artifactId}</artifactId>
96+
<version>{version}</version>
97+
98+
<dependencies>
99+
{dependencies.map(_.toXml)}
100+
</dependencies>
101+
<build>
102+
{resourceNodes}
103+
<plugins>
104+
{plugins.map(_.toXml)}
105+
</plugins>
106+
</build>
107+
</project>
108+
}
109+
110+
final case class MavenLibraryDependency(
111+
groupId: String,
112+
artifactId: String,
113+
version: String,
114+
scope: MavenScopes
115+
) {
116+
117+
private val scopeParam =
118+
if scope == MavenScopes.Main then scala.xml.Null else <scope>{scope.name}</scope>
119+
120+
def toXml: Elem =
121+
<dependency>
122+
<groupId>{groupId}</groupId>
123+
<artifactId>{artifactId}</artifactId>
124+
<version>{version}</version>
125+
{scopeParam}
126+
</dependency>
127+
}
128+
129+
final case class MavenPlugin(
130+
groupId: String,
131+
artifactId: String,
132+
version: String,
133+
jdk: String,
134+
additionalNode: Elem
135+
) {
136+
137+
def toXml: Elem =
138+
<plugin>
139+
<groupId>{groupId}</groupId>
140+
<artifactId>{artifactId}</artifactId>
141+
<version>{version}</version>
142+
{additionalNode}
143+
</plugin>
144+
}

0 commit comments

Comments
 (0)