Skip to content

Commit fd3410a

Browse files
Add internal and CLI options for javac plugins and options
1 parent 4202c84 commit fd3410a

File tree

8 files changed

+150
-22
lines changed

8 files changed

+150
-22
lines changed

modules/build/src/main/scala/scala/build/Artifacts.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ final case class Artifacts(
2424
compilerDependencies: Seq[AnyDependency],
2525
compilerArtifacts: Seq[(String, Path)],
2626
compilerPlugins: Seq[(AnyDependency, String, Path)],
27+
javacPluginDependencies: Seq[(AnyDependency, String, Path)],
28+
extraJavacPlugins: Seq[Path],
2729
dependencies: Seq[AnyDependency],
2830
scalaNativeCli: Seq[Path],
2931
detailedArtifacts: Seq[(CsDependency, csCore.Publication, csUtil.Artifact, Path)],
@@ -63,6 +65,8 @@ object Artifacts {
6365
def apply(
6466
params: ScalaParameters,
6567
compilerPlugins: Seq[Positioned[AnyDependency]],
68+
javacPluginDependencies: Seq[Positioned[AnyDependency]],
69+
extraJavacPlugins: Seq[Path],
6670
dependencies: Seq[Positioned[AnyDependency]],
6771
extraClassPath: Seq[Path],
6872
extraCompileOnlyJars: Seq[Path],
@@ -198,10 +202,23 @@ object Artifacts {
198202
.map(_.flatten)
199203
}
200204

205+
val javacPlugins0 = value {
206+
javacPluginDependencies
207+
.map { posDep =>
208+
artifacts(posDep.map(Seq(_)), allExtraRepositories, params, logger)
209+
.map(_.map { case (url, path) => (posDep.value, url, path) })
210+
}
211+
.sequence
212+
.left.map(CompositeBuildException(_))
213+
.map(_.flatten)
214+
}
215+
201216
Artifacts(
202217
compilerDependencies,
203218
compilerArtifacts,
204219
compilerPlugins0,
220+
javacPlugins0,
221+
extraJavacPlugins,
205222
updatedDependencies.map(_.value),
206223
scalaNativeCli,
207224
fetchRes.fullDetailedArtifacts.collect { case (d, p, a, Some(f)) => (d, p, a, f.toPath) },

modules/build/src/main/scala/scala/build/Build.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,8 @@ object Build {
601601
compilerClassPath = artifacts.compilerClassPath
602602
)
603603

604+
val javacOptions = javacReleaseV ++ options.javaOptions.javacOptions
605+
604606
// `test` scope should contains class path to main scope
605607
val mainClassesPath =
606608
if (scope == Scope.Test)
@@ -609,6 +611,11 @@ object Build {
609611

610612
value(validate(logger, options))
611613

614+
val fullClassPath = artifacts.compileClassPath ++
615+
mainClassesPath ++
616+
artifacts.javacPluginDependencies.map(_._3) ++
617+
artifacts.extraJavacPlugins
618+
612619
val project = Project(
613620
directory = inputs.workspace / ".scala",
614621
workspace = inputs.workspace,
@@ -622,13 +629,13 @@ object Build {
622629
Some(options.scalaNativeOptions.bloopConfig())
623630
else None,
624631
projectName = inputs.scopeProjectName(scope),
625-
classPath = artifacts.compileClassPath ++ mainClassesPath,
632+
classPath = fullClassPath,
626633
resolution = Some(Project.resolution(artifacts.detailedArtifacts)),
627634
sources = allSources,
628635
resourceDirs = sources.resourceDirs,
629636
scope = scope,
630637
javaHomeOpt = Option(options.javaHomeLocation().value),
631-
javacOptions = javacReleaseV
638+
javacOptions = javacOptions
632639
)
633640
project
634641
}

modules/build/src/main/scala/scala/build/options/BuildOptions.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ final case class BuildOptions(
114114
scalaOptions.compilerPlugins
115115
}
116116

117+
def javacPluginDependencies: Either[BuildException, Seq[Positioned[AnyDependency]]] = either {
118+
javaOptions.javacPluginDependencies
119+
}
120+
117121
def allExtraJars: Seq[Path] =
118122
classPathOptions.extraClassPath.map(_.toNIO)
119123
def allExtraCompileOnlyJars: Seq[Path] =
@@ -337,6 +341,8 @@ final case class BuildOptions(
337341
val maybeArtifacts = Artifacts(
338342
params = value(scalaParams),
339343
compilerPlugins = value(compilerPlugins),
344+
javacPluginDependencies = value(javacPluginDependencies),
345+
extraJavacPlugins = javaOptions.javacPlugins.map(_.value.toNIO),
340346
dependencies = value(dependencies),
341347
extraClassPath = allExtraJars,
342348
scalaNativeCliVersion =

modules/build/src/main/scala/scala/build/options/JavaOptions.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package scala.build.options
22

3+
import dependency.AnyDependency
4+
35
import scala.build.Positioned
46

57
final case class JavaOptions(
@@ -9,7 +11,10 @@ final case class JavaOptions(
911
jvmIndexOs: Option[String] = None,
1012
jvmIndexArch: Option[String] = None,
1113
javaOpts: Seq[Positioned[String]] = Nil,
12-
bloopJvmVersion: Option[Positioned[Int]] = None
14+
bloopJvmVersion: Option[Positioned[Int]] = None,
15+
javacPluginDependencies: Seq[Positioned[AnyDependency]] = Nil,
16+
javacPlugins: Seq[Positioned[os.Path]] = Nil,
17+
javacOptions: Seq[String] = Nil
1318
)
1419

1520
object JavaOptions {

modules/cli/src/main/scala/scala/cli/commands/SharedJvmOptions.scala

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ package scala.cli.commands
33
import caseapp._
44
import upickle.default.{ReadWriter, macroRW}
55

6+
import java.io.File
7+
68
import scala.build.options.JavaOptions
79
import scala.build.{Os, Position, Positioned}
10+
import scala.util.Properties
811

912
// format: off
1013
final case class SharedJvmOptions(
@@ -33,18 +36,44 @@ final case class SharedJvmOptions(
3336
@HelpMessage("CPU architecture to use when looking up in the JVM index")
3437
@ValueDescription("amd64|arm64|arm|…")
3538
@Hidden
36-
jvmIndexArch: Option[String] = None
39+
jvmIndexArch: Option[String] = None,
40+
41+
@Group("Java")
42+
@HelpMessage("Javac plugin dependencies or files")
43+
@Hidden
44+
javacPlugin: List[String] = Nil,
45+
46+
@Group("Java")
47+
@HelpMessage("Javac options")
48+
@Name("javacOpt")
49+
@Hidden
50+
javacOption: List[String] = Nil
3751
) {
3852
// format: on
3953

54+
private lazy val (javacFilePlugins, javacPluginDeps) =
55+
javacPlugin
56+
.filter(_.trim.nonEmpty)
57+
.partition { input =>
58+
input.contains(File.separator) ||
59+
(Properties.isWin && input.contains("/")) ||
60+
input.count(_ == ':') < 2
61+
}
62+
4063
def javaOptions = JavaOptions(
4164
javaHomeOpt = javaHome.filter(_.nonEmpty).map(v =>
4265
Positioned(Seq(Position.CommandLine("--java-home")), os.Path(v, Os.pwd))
4366
),
4467
jvmIdOpt = jvm.filter(_.nonEmpty),
4568
jvmIndexOpt = jvmIndex.filter(_.nonEmpty),
4669
jvmIndexOs = jvmIndexOs.map(_.trim).filter(_.nonEmpty),
47-
jvmIndexArch = jvmIndexArch.map(_.trim).filter(_.nonEmpty)
70+
jvmIndexArch = jvmIndexArch.map(_.trim).filter(_.nonEmpty),
71+
javacPluginDependencies = SharedOptions.parseDependencies(
72+
javacPluginDeps.map(Positioned.none(_)),
73+
ignoreErrors = false
74+
),
75+
javacPlugins = javacFilePlugins.map(s => Positioned.none(os.Path(s, Os.pwd))),
76+
javacOptions = javacOption
4877
)
4978

5079
}

modules/cli/src/main/scala/scala/cli/commands/SharedOptions.scala

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -107,21 +107,6 @@ final case class SharedOptions(
107107

108108
def logger = logging.logger
109109

110-
private def parseDependencies(
111-
deps: List[Positioned[String]],
112-
ignoreErrors: Boolean
113-
): Seq[Positioned[AnyDependency]] =
114-
deps.map(_.map(_.trim)).filter(_.value.nonEmpty)
115-
.flatMap { posDepStr =>
116-
val depStr = posDepStr.value
117-
DependencyParser.parse(depStr) match {
118-
case Left(err) =>
119-
if (ignoreErrors) Nil
120-
else sys.error(s"Error parsing dependency '$depStr': $err")
121-
case Right(dep) => Seq(posDepStr.map(_ => dep))
122-
}
123-
}
124-
125110
def buildOptions(
126111
enableJmh: Boolean,
127112
jmhVersion: Option[String],
@@ -139,7 +124,10 @@ final case class SharedOptions(
139124
generateSemanticDbs = semanticDb,
140125
scalacOptions = scalac.scalacOption.filter(_.nonEmpty),
141126
compilerPlugins =
142-
parseDependencies(dependencies.compilerPlugin.map(Positioned.none(_)), ignoreErrors),
127+
SharedOptions.parseDependencies(
128+
dependencies.compilerPlugin.map(Positioned.none(_)),
129+
ignoreErrors
130+
),
143131
platform = platformOpt.map(o => Positioned(List(Position.CommandLine()), o))
144132
),
145133
scriptOptions = ScriptOptions(
@@ -169,7 +157,10 @@ final case class SharedOptions(
169157
.map(os.Path(_, os.pwd)),
170158
extraRepositories = dependencies.repository.map(_.trim).filter(_.nonEmpty),
171159
extraDependencies =
172-
parseDependencies(dependencies.dependency.map(Positioned.none(_)), ignoreErrors)
160+
SharedOptions.parseDependencies(
161+
dependencies.dependency.map(Positioned.none(_)),
162+
ignoreErrors
163+
)
173164
),
174165
internal = InternalOptions(
175166
cache = Some(coursierCache),
@@ -258,6 +249,21 @@ object SharedOptions {
258249
implicit lazy val help: Help[SharedOptions] = Help.derive
259250
implicit lazy val jsonCodec: ReadWriter[SharedOptions] = macroRW
260251

252+
def parseDependencies(
253+
deps: List[Positioned[String]],
254+
ignoreErrors: Boolean
255+
): Seq[Positioned[AnyDependency]] =
256+
deps.map(_.map(_.trim)).filter(_.value.nonEmpty)
257+
.flatMap { posDepStr =>
258+
val depStr = posDepStr.value
259+
DependencyParser.parse(depStr) match {
260+
case Left(err) =>
261+
if (ignoreErrors) Nil
262+
else sys.error(s"Error parsing dependency '$depStr': $err")
263+
case Right(dep) => Seq(posDepStr.map(_ => dep))
264+
}
265+
}
266+
261267
def readStdin(in: InputStream = System.in, logger: Logger): Option[Array[Byte]] =
262268
if (in == null) {
263269
logger.debug("No stdin available")

modules/integration/src/test/scala/scala/cli/integration/CompileTestDefinitions.scala

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,4 +273,52 @@ abstract class CompileTestDefinitions(val scalaVersionOpt: Option[String])
273273

274274
}
275275
}
276+
277+
test("Manual javac SemanticDB") {
278+
val inputs = TestInputs(
279+
Seq(
280+
os.rel / "foo" / "Test.java" ->
281+
"""package foo;
282+
|
283+
|public class Test {
284+
| public static void main(String[] args) {
285+
| System.err.println("Hello");
286+
| }
287+
|}
288+
|""".stripMargin
289+
)
290+
)
291+
inputs.fromRoot { root =>
292+
val compilerPackages = Seq(
293+
"com.sun.tools.javac.api",
294+
"com.sun.tools.javac.code",
295+
"com.sun.tools.javac.model",
296+
"com.sun.tools.javac.tree",
297+
"com.sun.tools.javac.util"
298+
)
299+
val exports = compilerPackages
300+
.flatMap { pkg =>
301+
Seq("-J--add-exports", s"-Jjdk.compiler/$pkg=ALL-UNNAMED")
302+
}
303+
.flatMap(opt => List("--javac-opt", opt))
304+
val javaSemDbOptions = Seq(
305+
"--javac-plugin",
306+
"com.sourcegraph:semanticdb-javac:0.7.4",
307+
"--javac-opt",
308+
s"-Xplugin:semanticdb -sourceroot:$root -targetroot:javac-classes-directory"
309+
) ++ exports
310+
os.proc(TestUtil.cli, "compile", extraOptions, javaSemDbOptions, ".")
311+
.call(cwd = root)
312+
313+
val files = os.walk(root / ".scala")
314+
val semDbFiles = files
315+
.filter(_.last.endsWith(".semanticdb"))
316+
.filter(!_.segments.exists(_ == "bloop-internal-classes"))
317+
expect(semDbFiles.length == 1)
318+
val semDbFile = semDbFiles.head
319+
expect(
320+
semDbFile.endsWith(os.rel / "META-INF" / "semanticdb" / "foo" / "Test.java.semanticdb")
321+
)
322+
}
323+
}
276324
}

website/docs/reference/cli-options.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,16 @@ Operating system to use when looking up in the JVM index
524524

525525
CPU architecture to use when looking up in the JVM index
526526

527+
#### `--javac-plugin`
528+
529+
Javac plugin dependencies or files
530+
531+
#### `--javac-option`
532+
533+
Aliases: `--javac-opt`
534+
535+
Javac options
536+
527537
## Logging options
528538

529539
Available in commands:

0 commit comments

Comments
 (0)