Skip to content

Commit f608ece

Browse files
Merge pull request #220 from alexarchambault/develop
Tweak doc and fix relative paths handling in using directives
2 parents 279ac20 + 7aec1d6 commit f608ece

37 files changed

+284
-149
lines changed

modules/build/src/main/scala/scala/build/CrossSources.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ object CrossSources {
8888

8989
val scopedRequirements = preprocessedSources.flatMap(_.scopedRequirements)
9090
val scopedRequirementsByRoot = scopedRequirements.groupBy(_.path.root)
91-
def baseReqs(path: PreprocessedSource.ScopePath): BuildRequirements =
91+
def baseReqs(path: ScopePath): BuildRequirements =
9292
scopedRequirementsByRoot
9393
.getOrElse(path.root, Nil)
9494
.flatMap(_.valueFor(path).toSeq)

modules/build/src/main/scala/scala/build/Inputs.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import java.util.zip.{ZipEntry, ZipInputStream}
88

99
import scala.annotation.tailrec
1010
import scala.build.options.Scope
11-
import scala.build.preprocessing.PreprocessedSource
11+
import scala.build.preprocessing.ScopePath
1212
import scala.util.matching.Regex
1313

1414
final case class Inputs(
@@ -146,8 +146,8 @@ object Inputs {
146146
os.sub / source.drop(idx + 1)
147147
}
148148

149-
def scopePath: PreprocessedSource.ScopePath =
150-
PreprocessedSource.ScopePath(source, subPath)
149+
def scopePath: ScopePath =
150+
ScopePath(Left(source), subPath)
151151
}
152152

153153
sealed trait SingleFile extends OnDisk with SingleElement
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package scala.build.errors
2+
3+
import scala.build.Position
4+
5+
final class ForbiddenPathReferenceError(
6+
val virtualRoot: String,
7+
val positionOpt: Option[Position]
8+
) extends BuildException(
9+
s"Can't reference paths from sources from $virtualRoot",
10+
positionOpt.toSeq
11+
)

modules/build/src/main/scala/scala/build/preprocessing/DirectivesProcessor.scala

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package scala.build.preprocessing
33
import com.virtuslab.using_directives.custom.model.{Path, Value}
44

55
import scala.build.Ops._
6+
import scala.build.Position
67
import scala.build.errors.{BuildException, CompositeBuildException}
78
import scala.build.options.{BuildOptions, ScalaOptions}
89
import scala.build.preprocessing.directives.DirectiveHandler
@@ -45,7 +46,8 @@ object DirectivesProcessor {
4546

4647
def process(
4748
directives: Map[Path, Seq[Value[_]]],
48-
handlers: Seq[DirectiveHandler]
49+
handlers: Seq[DirectiveHandler],
50+
cwd: ScopePath
4951
): Either[BuildException, BuildOptions] = {
5052

5153
val values = directives.map {
@@ -63,7 +65,9 @@ object DirectivesProcessor {
6365
.iterator
6466
.flatMap {
6567
case (k, v) =>
66-
handlersMap.get(k).iterator.map(_(v))
68+
// FIXME using_directives needs to give us positions here
69+
val positionOpt = Option.empty[Position]
70+
handlersMap.get(k).iterator.map(_(v, cwd, positionOpt))
6771
}
6872
.toVector
6973
.sequence

modules/build/src/main/scala/scala/build/preprocessing/PreprocessedSource.scala

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ sealed abstract class PreprocessedSource extends Product with Serializable {
77
def requirements: Option[BuildRequirements]
88
def mainClassOpt: Option[String]
99

10-
def scopedRequirements: Seq[PreprocessedSource.Scoped[BuildRequirements]]
11-
def scopePath: PreprocessedSource.ScopePath
10+
def scopedRequirements: Seq[Scoped[BuildRequirements]]
11+
def scopePath: ScopePath
1212
}
1313

1414
object PreprocessedSource {
@@ -76,29 +76,4 @@ object PreprocessedSource {
7676
}
7777
}
7878

79-
final case class ScopePath(
80-
root: String,
81-
path: os.SubPath
82-
) {
83-
def /(subPath: os.PathChunk): ScopePath =
84-
copy(path = path / subPath)
85-
}
86-
87-
object ScopePath {
88-
def fromPath(path: os.Path): ScopePath = {
89-
def root(p: os.Path): os.Path =
90-
if (p.segmentCount > 0) root(p / os.up) else p
91-
val root0 = root(path)
92-
ScopePath(root0.toString, path.subRelativeTo(root0))
93-
}
94-
}
95-
96-
final case class Scoped[T](path: ScopePath, value: T) {
97-
def appliesTo(candidate: ScopePath): Boolean =
98-
path.root == candidate.root &&
99-
candidate.path.startsWith(path.path)
100-
def valueFor(candidate: ScopePath): Option[T] =
101-
if (appliesTo(candidate)) Some(value) else None
102-
}
103-
10479
}

modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ case object ScalaPreprocessor extends Preprocessor {
5959
}
6060
val res = either {
6161
val content = value(maybeRead(f.path))
62-
val scopePath = PreprocessedSource.ScopePath.fromPath(f.path)
62+
val scopePath = ScopePath.fromPath(f.path)
6363
val source = value(process(content, Right(f.path), scopePath / os.up)) match {
6464
case None =>
6565
PreprocessedSource.OnDisk(f.path, None, None, Nil, Some(inferredClsName))
@@ -116,15 +116,15 @@ case object ScalaPreprocessor extends Preprocessor {
116116
def process(
117117
content: String,
118118
path: Either[String, os.Path],
119-
scopeRoot: PreprocessedSource.ScopePath
119+
scopeRoot: ScopePath
120120
): Either[BuildException, Option[(
121121
BuildRequirements,
122-
Seq[PreprocessedSource.Scoped[BuildRequirements]],
122+
Seq[Scoped[BuildRequirements]],
123123
BuildOptions,
124124
Option[String]
125125
)]] = either {
126126

127-
val afterStrictUsing = value(processStrictUsing(content))
127+
val afterStrictUsing = value(processStrictUsing(content, scopeRoot))
128128
val afterUsing = value {
129129
processUsing(path, afterStrictUsing.map(_._2).getOrElse(content), scopeRoot)
130130
.sequence
@@ -153,14 +153,16 @@ case object ScalaPreprocessor extends Preprocessor {
153153
}
154154
}
155155

156-
private def directivesBuildOptions(directives: Seq[Directive])
157-
: Either[BuildException, BuildOptions] = {
156+
private def directivesBuildOptions(
157+
directives: Seq[Directive],
158+
cwd: ScopePath
159+
): Either[BuildException, BuildOptions] = {
158160
val results = directives
159161
.filter(_.tpe == Directive.Using)
160162
.map { dir =>
161163
val fromHandlersOpt = usingDirectiveHandlers
162164
.iterator
163-
.flatMap(_.handle(dir).iterator)
165+
.flatMap(_.handle(dir, cwd).iterator)
164166
.take(1)
165167
.toList
166168
.headOption
@@ -180,17 +182,17 @@ case object ScalaPreprocessor extends Preprocessor {
180182

181183
private def directivesBuildRequirements(
182184
directives: Seq[Directive],
183-
scopeRoot: PreprocessedSource.ScopePath
185+
scopeRoot: ScopePath
184186
): Either[
185187
BuildException,
186-
(BuildRequirements, Seq[PreprocessedSource.Scoped[BuildRequirements]])
188+
(BuildRequirements, Seq[Scoped[BuildRequirements]])
187189
] = {
188190
val results = directives
189191
.filter(_.tpe == Directive.Require)
190192
.map { dir =>
191193
val fromHandlersOpt = requireDirectiveHandlers
192194
.iterator
193-
.flatMap(_.handle(dir).iterator)
195+
.flatMap(_.handle(dir, scopeRoot).iterator)
194196
.take(1)
195197
.toList
196198
.headOption
@@ -203,7 +205,7 @@ case object ScalaPreprocessor extends Preprocessor {
203205
case None => (reqs, Nil)
204206
case Some(sc) =>
205207
val scopePath = scopeRoot / os.RelPath(sc).asSubPath
206-
(BuildRequirements(), Seq(PreprocessedSource.Scoped(scopePath, reqs)))
208+
(BuildRequirements(), Seq(Scoped(scopePath, reqs)))
207209
}
208210
Right(value)
209211
case Some(Left(err)) =>
@@ -224,12 +226,12 @@ case object ScalaPreprocessor extends Preprocessor {
224226
private def processUsing(
225227
path: Either[String, os.Path],
226228
content: String,
227-
scopeRoot: PreprocessedSource.ScopePath
229+
scopeRoot: ScopePath
228230
): Option[Either[
229231
BuildException,
230232
(
231233
BuildRequirements,
232-
Seq[PreprocessedSource.Scoped[BuildRequirements]],
234+
Seq[Scoped[BuildRequirements]],
233235
BuildOptions,
234236
Option[String]
235237
)
@@ -240,7 +242,7 @@ case object ScalaPreprocessor extends Preprocessor {
240242
case (directives, updatedContentOpt) =>
241243
val tuple = (
242244
directivesBuildRequirements(directives, scopeRoot),
243-
directivesBuildOptions(directives),
245+
directivesBuildOptions(directives, scopeRoot),
244246
Right(updatedContentOpt)
245247
)
246248
tuple
@@ -339,7 +341,8 @@ case object ScalaPreprocessor extends Preprocessor {
339341
}
340342

341343
private def processStrictUsing(
342-
content: String
344+
content: String,
345+
cwd: ScopePath
343346
): Either[BuildException, Option[(BuildOptions, String)]] = either {
344347

345348
val processor = {
@@ -364,7 +367,8 @@ case object ScalaPreprocessor extends Preprocessor {
364367
val updatedOptions = value {
365368
DirectivesProcessor.process(
366369
directives0,
367-
usingDirectiveHandlers ++ requireDirectiveHandlers
370+
usingDirectiveHandlers ++ requireDirectiveHandlers,
371+
cwd
368372
)
369373
}
370374
val codeOffset = directives.getCodeOffset()
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package scala.build.preprocessing
2+
3+
final case class ScopePath(
4+
root: Either[String, os.Path],
5+
path: os.SubPath
6+
) {
7+
def /(subPath: os.PathChunk): ScopePath =
8+
copy(path = path / subPath)
9+
}
10+
11+
object ScopePath {
12+
def fromPath(path: os.Path): ScopePath = {
13+
def root(p: os.Path): os.Path =
14+
if (p.segmentCount > 0) root(p / os.up) else p
15+
val root0 = root(path)
16+
ScopePath(Right(root0), path.subRelativeTo(root0))
17+
}
18+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package scala.build.preprocessing
2+
3+
final case class Scoped[T](path: ScopePath, value: T) {
4+
def appliesTo(candidate: ScopePath): Boolean =
5+
path.root == candidate.root &&
6+
candidate.path.startsWith(path.path)
7+
def valueFor(candidate: ScopePath): Option[T] =
8+
if (appliesTo(candidate)) Some(value) else None
9+
}

modules/build/src/main/scala/scala/build/preprocessing/ScriptPreprocessor.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ final case class ScriptPreprocessor(codeWrapper: CodeWrapper) extends Preprocess
2323
content,
2424
codeWrapper,
2525
script.subPath,
26-
PreprocessedSource.ScopePath.fromPath(script.path)
26+
ScopePath.fromPath(script.path)
2727
)
2828
}
2929
preprocessed
@@ -76,7 +76,7 @@ object ScriptPreprocessor {
7676
content: String,
7777
codeWrapper: CodeWrapper,
7878
subPath: os.SubPath,
79-
scopePath: PreprocessedSource.ScopePath
79+
scopePath: ScopePath
8080
): Either[BuildException, List[PreprocessedSource.InMemory]] = either {
8181

8282
val contentIgnoredSheBangLines = ignoreSheBangLines(content)

modules/build/src/main/scala/scala/build/preprocessing/directives/Directive.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package scala.build.preprocessing.directives
22

33
import scala.build.Position
4+
import scala.build.errors.{BuildException, ForbiddenPathReferenceError}
5+
import scala.build.preprocessing.ScopePath
46

57
final case class Directive(
68
tpe: Directive.Type,
@@ -14,4 +16,12 @@ object Directive {
1416
sealed abstract class Type(val name: String) extends Product with Serializable
1517
case object Using extends Type("using")
1618
case object Require extends Type("require")
19+
20+
def osRoot(cwd: ScopePath, pos: Option[Position]): Either[BuildException, os.Path] =
21+
cwd.root match {
22+
case Left(virtualRoot) =>
23+
Left(new ForbiddenPathReferenceError(virtualRoot, pos))
24+
case Right(root) =>
25+
Right(root / cwd.path)
26+
}
1727
}

0 commit comments

Comments
 (0)