Skip to content

Commit e9603f9

Browse files
committed
Explicit handling of paths in using directives
1 parent 1b76535 commit e9603f9

File tree

3 files changed

+96
-2
lines changed

3 files changed

+96
-2
lines changed

modules/build/src/test/scala/scala/build/tests/DirectiveTests.scala

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import com.eed3si9n.expecty.Expecty.expect
44

55
import java.io.IOException
66
import scala.build.{BuildThreads, Directories, LocalRepo, Position, Positioned}
7-
import scala.build.options.{BuildOptions, InternalOptions, MaybeScalaVersion}
7+
import scala.build.options.{BuildOptions, InternalOptions, MaybeScalaVersion, ScalacOpt}
88
import scala.build.tests.util.BloopServer
99
import build.Ops.EitherThrowOps
1010
import scala.build.Position
@@ -107,4 +107,60 @@ class DirectiveTests extends munit.FunSuite {
107107
}
108108
}
109109

110+
test("handling special syntax for path") {
111+
val filePath = os.rel / "src" / "simple.scala"
112+
val testInputs = TestInputs(
113+
os.rel / filePath ->
114+
"""//> using options "-coverage-out:${.}""""
115+
)
116+
testInputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {
117+
(root, _, maybeBuild) =>
118+
val build = maybeBuild.orThrow
119+
val scalacOptions: Option[Positioned[ScalacOpt]] =
120+
build.options.scalaOptions.scalacOptions.toSeq.headOption
121+
assert(scalacOptions.nonEmpty)
122+
123+
val scalacOpt = scalacOptions.get.value.value
124+
val expectedCoveragePath = (root / filePath / os.up).toString
125+
expect(scalacOpt == s"-coverage-out:$expectedCoveragePath")
126+
}
127+
}
128+
129+
test("handling special syntax for path with more dollars before") {
130+
val filePath = os.rel / "src" / "simple.scala"
131+
val testInputs = TestInputs(
132+
os.rel / filePath ->
133+
"""//> using options "-coverage-out:$$${.}""""
134+
)
135+
testInputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {
136+
(root, _, maybeBuild) =>
137+
val build = maybeBuild.orThrow
138+
val scalacOptions: Option[Positioned[ScalacOpt]] =
139+
build.options.scalaOptions.scalacOptions.toSeq.headOption
140+
assert(scalacOptions.nonEmpty)
141+
142+
val scalacOpt = scalacOptions.get.value.value
143+
val expectedCoveragePath = (root / filePath / os.up).toString
144+
expect(scalacOpt == s"-coverage-out:$$$expectedCoveragePath")
145+
}
146+
}
147+
148+
test("skip handling special syntax for path when double dollar") {
149+
val filePath = os.rel / "src" / "simple.scala"
150+
val testInputs = TestInputs(
151+
os.rel / filePath ->
152+
"""//> using options "-coverage-out:$${.}""""
153+
)
154+
testInputs.withBuild(baseOptions, buildThreads, bloopConfigOpt) {
155+
(_, _, maybeBuild) =>
156+
val build = maybeBuild.orThrow
157+
val scalacOptions: Option[Positioned[ScalacOpt]] =
158+
build.options.scalaOptions.scalacOptions.toSeq.headOption
159+
assert(scalacOptions.nonEmpty)
160+
161+
val scalacOpt = scalacOptions.get.value.value
162+
expect(scalacOpt == """-coverage-out:${.}""")
163+
}
164+
}
165+
110166
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package scala.build.directives
2+
3+
import scala.util.matching.Regex
4+
5+
object DirectiveSpecialSyntax {
6+
7+
/** Replaces the `${.}` pattern in the directive value with the parent directory of the file
8+
* containing the directive. Skips replacement if the pattern is preceded by two dollar signs
9+
* ($$). https://github.com/VirtusLab/scala-cli/issues/1098
10+
*
11+
* @param directiveValue
12+
* the value of the directive, e.g., "-coverage-out:${.}" for example for the directive "//>
13+
* using options "-coverage-out:${.}""
14+
* @param path
15+
* the file path from which the directive is read; replacement occurs only if the directive is
16+
* from a local file
17+
* @return
18+
* the directive value with the `${.}` pattern replaced by the parent directory, if applicable
19+
*/
20+
def handlingSpecialPathSyntax(directiveValue: String, path: Either[String, os.Path]): String = {
21+
val pattern = """(((?:\$)+)(\{\.\}))""".r
22+
path match {
23+
case Right(p) =>
24+
pattern.replaceAllIn(
25+
directiveValue,
26+
(m: Regex.Match) => {
27+
val dollarSigns = m.group(2)
28+
val dollars = "\\$" * (dollarSigns.length / 2)
29+
if (dollarSigns.length % 2 == 0)
30+
s"$dollars${m.group(3)}"
31+
else
32+
s"$dollars${p / os.up}"
33+
}
34+
)
35+
case _ => directiveValue
36+
}
37+
}
38+
}

modules/directives/src/main/scala/scala/build/directives/DirectiveValueParser.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ object DirectiveValueParser {
146146
s"Expected a string, got '${value.getRelatedASTNode.toString}'",
147147
Seq(pos)
148148
)
149-
}
149+
}.map(DirectiveSpecialSyntax.handlingSpecialPathSyntax(_, path))
150150

151151
final case class MaybeNumericalString(value: String)
152152

0 commit comments

Comments
 (0)