Skip to content

Commit 5fc8251

Browse files
authored
Explicit handling of paths in using directives (#2040)
1 parent 9e28ddf commit 5fc8251

File tree

4 files changed

+111
-2
lines changed

4 files changed

+111
-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

website/docs/guides/using-directives.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,21 @@ We plan to add ways to Scala CLI to migrate these settings into a centralized lo
103103

104104
We are aware that `using` directives may be a controversial topic, so we’ve created a [dedicated space for discussing `using` directives](https://github.com/VirtusLab/scala-cli/discussions/categories/using-directives-and-cmd-configuration-options).
105105

106+
### Explicit handling of paths in using directives
107+
108+
The `${.}` pattern in directive values will be replaced by the parent directory of the file containing the
109+
directive. This makes it possible for example to generate coverage output files relative to the source file location.
110+
111+
```scala
112+
//> using options "-coverage-out:${.}"
113+
```
114+
115+
However, if you want to include the `${.}` pattern in the directive value without it being replaced, you can precede it
116+
with two dollar signs (`$$`), like this:
117+
118+
```scala
119+
//> using options "-coverage-out:$${.}"
120+
```
106121

107122
## How to comment out using directives?
108123

0 commit comments

Comments
 (0)