Skip to content

Commit 1347657

Browse files
Override using directives scalac, java, and dependency options with their cli counterparts (#612)
* Add initial String options and dependency shadowing mechanism * Move implicits to seperate object * Make scalac options Positioned and add prefixed option handling * Shadow using directives java options with cli options * Add tests for shadowing * Apply scalafix * scalafix * Redesign shadowing for a more generic approach * Revert changes to map ConfigMonoid * scalafix * fix compiler plugin option handling * Simplify JavaOpt / ScalacOpt They now only wrap a single option * Better hashing for ShadowingSeq * NIT / clean-up / diff minimization Co-authored-by: Alexandre Archambault <[email protected]>
1 parent 63c33c9 commit 1347657

30 files changed

+399
-70
lines changed

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

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ object Build {
554554
}
555555
else if (options.javaOptions.bloopJvmVersion.exists(_.value == 8))
556556
None
557-
else if (options.scalaOptions.scalacOptions.contains("-release"))
557+
else if (options.scalaOptions.scalacOptions.toSeq.exists(_.value.value == "-release"))
558558
None
559559
else
560560
Some(javaHome.value.version)
@@ -635,11 +635,13 @@ object Build {
635635
val scalacReleaseV = releaseFlagVersion.map(v => List("-release", v)).getOrElse(Nil)
636636
val javacReleaseV = releaseFlagVersion.map(v => List("--release", v)).getOrElse(Nil)
637637

638-
val scalacOptions = options.scalaOptions.scalacOptions ++
639-
pluginScalacOptions ++
640-
semanticDbScalacOptions ++
641-
sourceRootScalacOptions ++
642-
scalaJsScalacOptions ++ scalacReleaseV
638+
val scalacOptions =
639+
options.scalaOptions.scalacOptions.toSeq.map(_.value.value) ++
640+
pluginScalacOptions ++
641+
semanticDbScalacOptions ++
642+
sourceRootScalacOptions ++
643+
scalaJsScalacOptions ++
644+
scalacReleaseV
643645

644646
val scalaCompiler = ScalaCompiler(
645647
scalaVersion = params.scalaVersion,

modules/build/src/main/scala/scala/build/Positioned.scala

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

3-
import scala.build.options.{ConfigMonoid, HashedType}
3+
import scala.build.options.{ConfigMonoid, HashedType, ShadowingSeq}
44

55
final case class Positioned[+T](
66
positions: Seq[Position],
@@ -50,4 +50,12 @@ object Positioned {
5050

5151
implicit def ordering[T](implicit underlying: Ordering[T]): Ordering[Positioned[T]] =
5252
Ordering.by(_.value)
53+
54+
implicit def keyOf[T](implicit
55+
underlying: ShadowingSeq.KeyOf[T]
56+
): ShadowingSeq.KeyOf[Positioned[T]] =
57+
ShadowingSeq.KeyOf(
58+
p => underlying.get(p.value),
59+
seq => underlying.groups(seq.map(_.value))
60+
)
5361
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ final case class BuildOptions(
8585
value(maybeJsDependencies).map(Positioned.none(_)) ++
8686
value(maybeNativeDependencies).map(Positioned.none(_)) ++
8787
value(scalaLibraryDependencies).map(Positioned.none(_)) ++
88-
classPathOptions.extraDependencies
88+
classPathOptions.extraDependencies.toSeq
8989
}
9090

9191
private def semanticDbPlugins: Either[BuildException, Seq[AnyDependency]] = either {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ final case class ClassPathOptions(
1010
extraCompileOnlyJars: Seq[os.Path] = Nil,
1111
extraSourceJars: Seq[os.Path] = Nil,
1212
fetchSources: Option[Boolean] = None,
13-
extraDependencies: Seq[Positioned[AnyDependency]] = Nil,
13+
extraDependencies: ShadowingSeq[Positioned[AnyDependency]] = ShadowingSeq.empty,
1414
resourcesDir: Seq[os.Path] = Nil,
1515
resourcesVirtualDir: Seq[os.SubPath] = Nil
1616
)
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package scala.build.options
2+
3+
final case class JavaOpt(value: String) {
4+
def key: Option[String] =
5+
JavaOpt.optionPrefixes.find(value.startsWith)
6+
.orElse {
7+
if (value.startsWith("-")) Some(value.takeWhile(_ != ':'))
8+
else if (value.startsWith("@")) Some("@")
9+
else None
10+
}
11+
}
12+
13+
object JavaOpt {
14+
/* Hardcoded prefixes for java options */
15+
private val optionPrefixes = Set("-Xmn", "-Xms", "-Xmx", "-Xss")
16+
17+
implicit val hashedType: HashedType[JavaOpt] = {
18+
opt => opt.value
19+
}
20+
implicit val keyOf: ShadowingSeq.KeyOf[JavaOpt] =
21+
ShadowingSeq.KeyOf(
22+
_.key,
23+
seq => ScalacOpt.groupCliOptions(seq.map(_.value))
24+
)
25+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ final case class JavaOptions(
1010
jvmIndexOpt: Option[String] = None,
1111
jvmIndexOs: Option[String] = None,
1212
jvmIndexArch: Option[String] = None,
13-
javaOpts: Seq[Positioned[String]] = Nil,
13+
javaOpts: ShadowingSeq[Positioned[JavaOpt]] = ShadowingSeq.empty,
1414
bloopJvmVersion: Option[Positioned[Int]] = None,
1515
javacPluginDependencies: Seq[Positioned[AnyDependency]] = Nil,
1616
javacPlugins: Seq[Positioned[os.Path]] = Nil,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ final case class ScalaOptions(
99
scalaBinaryVersion: Option[String] = None,
1010
addScalaLibrary: Option[Boolean] = None,
1111
generateSemanticDbs: Option[Boolean] = None,
12-
scalacOptions: Seq[String] = Nil,
12+
scalacOptions: ShadowingSeq[Positioned[ScalacOpt]] = ShadowingSeq.empty,
1313
extraScalaVersions: Set[String] = Set.empty,
1414
compilerPlugins: Seq[Positioned[AnyDependency]] = Nil,
1515
platform: Option[Positioned[Platform]] = None,
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package scala.build.options
2+
3+
final case class ScalacOpt(value: String) {
4+
def key: Option[String] =
5+
if (value.startsWith("-"))
6+
Some(value.takeWhile(_ != ':'))
7+
.filterNot(ScalacOpt.repeatingKeys.contains)
8+
else if (value.startsWith("@"))
9+
Some("@")
10+
else
11+
None
12+
}
13+
14+
object ScalacOpt {
15+
private val repeatingKeys = Set(
16+
"-Xplugin:",
17+
"-P" // plugin options
18+
)
19+
20+
implicit val hashedType: HashedType[ScalacOpt] = {
21+
opt => opt.value
22+
}
23+
implicit val keyOf: ShadowingSeq.KeyOf[ScalacOpt] =
24+
ShadowingSeq.KeyOf(
25+
_.key,
26+
seq => groupCliOptions(seq.map(_.value))
27+
)
28+
29+
// Groups options (starting with `-` or `@`) with option arguments that follow
30+
def groupCliOptions(opts: Seq[String]): Seq[Int] =
31+
opts
32+
.zipWithIndex
33+
.collect {
34+
case (opt, idx) if opt.startsWith("-") || opt.startsWith("@") =>
35+
idx
36+
}
37+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package scala.build.options
2+
3+
import dependency.AnyDependency
4+
import shapeless.Lazy
5+
6+
import scala.collection.mutable
7+
8+
/** Seq ensuring some of its values are unique according to some key */
9+
final case class ShadowingSeq[T] private (values: Seq[Seq[T]]) {
10+
lazy val toSeq: Seq[T] = values.flatten
11+
def ++(other: Seq[T])(implicit key: ShadowingSeq.KeyOf[T]): ShadowingSeq[T] =
12+
addGroups(ShadowingSeq.groups(other, key.groups(other)))
13+
private def addGroups(other: Seq[Seq[T]])(implicit key: ShadowingSeq.KeyOf[T]): ShadowingSeq[T] =
14+
if (other.isEmpty) this
15+
else {
16+
val l = new mutable.ListBuffer[Seq[T]]
17+
val seen = new mutable.HashSet[String]
18+
19+
for (group <- values.iterator ++ other.iterator) {
20+
assert(group.nonEmpty)
21+
val keyOpt = key.get(group.head)
22+
if (!keyOpt.exists(seen.contains)) {
23+
l += group
24+
for (key <- keyOpt)
25+
seen += key
26+
}
27+
}
28+
29+
ShadowingSeq(l.toList)
30+
}
31+
}
32+
33+
object ShadowingSeq {
34+
35+
final case class KeyOf[T](
36+
get: T => Option[String],
37+
/** The indices at which sub-groups of values start */
38+
groups: Seq[T] => Seq[Int]
39+
)
40+
object KeyOf {
41+
implicit val keyOfAnyDependency: KeyOf[AnyDependency] =
42+
KeyOf(dep => Some(dep.module.render), _.indices)
43+
}
44+
45+
implicit def monoid[T](implicit key: KeyOf[T]): ConfigMonoid[ShadowingSeq[T]] =
46+
ConfigMonoid.instance(ShadowingSeq.empty[T]) { (a, b) =>
47+
a.addGroups(b.values)
48+
}
49+
implicit def hashedField[T](implicit
50+
hasher: Lazy[HashedType[T]]
51+
): HashedField[ShadowingSeq[T]] = {
52+
(name, seq, update) =>
53+
for (t <- seq.toSeq)
54+
update(s"$name+=${hasher.value.hashedValue(t)}")
55+
}
56+
57+
def empty[T]: ShadowingSeq[T] = ShadowingSeq(Nil)
58+
59+
def from[T](values: Seq[T])(implicit key: KeyOf[T]): ShadowingSeq[T] =
60+
empty[T] ++ values
61+
62+
private def groups[T](values: Seq[T], indices: Seq[Int]): Seq[Seq[T]] = {
63+
val safeIndices = Seq(0) ++ indices ++ Seq(values.length)
64+
safeIndices
65+
.sliding(2)
66+
.map {
67+
case Seq(start, end) =>
68+
values.slice(start, end)
69+
}
70+
.filter(_.nonEmpty)
71+
.toVector
72+
}
73+
}

modules/build/src/main/scala/scala/build/options/validation/BuildOptionsRule.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ class ValidationException(
1717

1818
object JvmOptionsForNonJvmBuild extends BuildOptionsRule {
1919
def validate(options: BuildOptions): List[Diagnostic] = {
20-
val jvmOptions = options.javaOptions.javaOpts.find(p => p.value.nonEmpty)
20+
val jvmOptions = options.javaOptions.javaOpts.toSeq
2121
if (jvmOptions.nonEmpty && options.platform.value != scala.build.options.Platform.JVM)
2222
List(Diagnostic(
2323
"Conflicting options. Jvm Options are valid only for jvm platform.",
2424
Severity.Warning,
25-
options.platform.positions ++ jvmOptions.get.positions
25+
options.platform.positions ++ jvmOptions.flatMap(_.positions)
2626
))
2727
else Nil
2828
}

0 commit comments

Comments
 (0)