Skip to content

Commit 6847156

Browse files
Merge pull request #163 from alexarchambault/resources
Allow to add resource directories via the command-line and using directives
2 parents acb0af0 + 8fe4ed5 commit 6847156

File tree

20 files changed

+251
-136
lines changed

20 files changed

+251
-136
lines changed

.scalafmt.conf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ fileOverride {
2121
"glob:**/scala-3-unstable/**" {
2222
runner.dialect = scala3
2323
}
24+
"glob:**/docs_checker/**" {
25+
runner.dialect = scala3
26+
}
2427
}
2528
project.excludeFilters = [
2629
".metals"

docs_checker/check.scala

Lines changed: 95 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -11,112 +11,115 @@ import scala.util.matching.Regex
1111
import munit.Assertions.assert
1212
import java.io.File
1313

14-
1514
val ScalaCodeBlock = """ *```scala name\:([\w\.]+)+""".r
16-
val CodeBlockEnds = """ *```""".r
17-
val ScalaCliBlock = """ *```scala-cli""".r
18-
val CheckBlock = """ *\<\!\-\- Expected(-regex):""".r
19-
val CheckBlockEnd = """ *\-\-\>""".r
15+
val CodeBlockEnds = """ *```""".r
16+
val ScalaCliBlock = """ *```scala-cli""".r
17+
val CheckBlock = """ *\<\!\-\- Expected(-regex):""".r
18+
val CheckBlockEnd = """ *\-\-\>""".r
2019

2120
enum Commands:
22-
def context: Context
21+
def context: Context
2322

24-
case Snippet(name: String, lines: Seq[String], context: Context)
25-
case Run(cmd: Seq[String], context: Context)
26-
case Check(patterns: Seq[String], regex: Boolean, context: Context)
23+
case Snippet(name: String, lines: Seq[String], context: Context)
24+
case Run(cmd: Seq[String], context: Context)
25+
case Check(patterns: Seq[String], regex: Boolean, context: Context)
2726

2827
case class Context(file: String, line: Int)
2928

3029
def msg(txt: String)(using c: Context): String = s"From ${c.file}:${c.line}: $txt"
3130

3231
def untilEndOfSnippet[T](
33-
lines: Seq[String],
34-
regex: Regex = CodeBlockEnds)(using c: Context): (Seq[String], Seq[String], Context) =
35-
val codeLines = lines.takeWhile(l => !regex.matches(l))
36-
assert(codeLines.size > 0, msg("Block cannot be empty!"))
37-
assert(codeLines.size < lines.size, msg("Block should end!"))
38-
(codeLines, lines.drop(codeLines.size + 1), c.copy(line = c.line + codeLines.size + 2))
32+
lines: Seq[String],
33+
regex: Regex = CodeBlockEnds
34+
)(using c: Context): (Seq[String], Seq[String], Context) =
35+
val codeLines = lines.takeWhile(l => !regex.matches(l))
36+
assert(codeLines.size > 0, msg("Block cannot be empty!"))
37+
assert(codeLines.size < lines.size, msg("Block should end!"))
38+
(codeLines, lines.drop(codeLines.size + 1), c.copy(line = c.line + codeLines.size + 2))
3939

4040
def parse(content: Seq[String], currentCommands: Seq[Commands], context: Context): Seq[Commands] =
41-
given Context = context
42-
content match
43-
case Nil => currentCommands
44-
case ScalaCodeBlock(name) :: tail =>
45-
val (codeLines, rest, newContext) = untilEndOfSnippet(tail)(using context)
46-
47-
parse(rest, currentCommands :+ Commands.Snippet(name, codeLines, context), newContext)
48-
case ScalaCliBlock() :: tail =>
49-
val (codeLines, rest, newContext) = untilEndOfSnippet(tail)
50-
assert(codeLines.size != 0)
51-
val runCmd = Commands.Run(codeLines.head.split(" ").toList, newContext)
52-
parse(rest, currentCommands :+ runCmd, newContext)
53-
case CheckBlock(regexOpt) :: tail =>
54-
val isRegex = regexOpt == "-regex"
55-
val (patterns, rest, newContext) = untilEndOfSnippet(tail, CheckBlockEnd)
56-
parse(rest, currentCommands :+ Commands.Check(patterns, isRegex, context), newContext)
57-
case _ :: tail => parse(tail, currentCommands, context.copy(line = context.line + 1))
41+
given Context = context
42+
content match
43+
case Nil => currentCommands
44+
case ScalaCodeBlock(name) :: tail =>
45+
val (codeLines, rest, newContext) = untilEndOfSnippet(tail)(using context)
46+
47+
parse(rest, currentCommands :+ Commands.Snippet(name, codeLines, context), newContext)
48+
case ScalaCliBlock() :: tail =>
49+
val (codeLines, rest, newContext) = untilEndOfSnippet(tail)
50+
assert(codeLines.size != 0)
51+
val runCmd = Commands.Run(codeLines.head.split(" ").toList, newContext)
52+
parse(rest, currentCommands :+ runCmd, newContext)
53+
case CheckBlock(regexOpt) :: tail =>
54+
val isRegex = regexOpt == "-regex"
55+
val (patterns, rest, newContext) = untilEndOfSnippet(tail, CheckBlockEnd)
56+
parse(rest, currentCommands :+ Commands.Check(patterns, isRegex, context), newContext)
57+
case _ :: tail => parse(tail, currentCommands, context.copy(line = context.line + 1))
5858

5959
case class TestCase(path: Path, failure: Option[Throwable])
6060

61-
def checkPath(path: Path): Seq[TestCase] =
62-
try
63-
if !Files.isDirectory(path) then
64-
if path.getFileName.toString.endsWith(".md") then
65-
checkFile(path)
66-
Seq(TestCase(path, None))
67-
else Nil
68-
else
69-
val toCheck = Files.list(path).iterator.asScala.filterNot(_.getFileName.toString.startsWith("."))
70-
toCheck.toList.flatMap(checkPath)
71-
catch
72-
case e: Throwable =>
73-
e.printStackTrace()
74-
Seq(TestCase(path, Some(e)))
75-
76-
def checkFile(file: Path) =
77-
val content = Files.lines(file).iterator.asScala.toList
78-
val commands = parse(content, Vector(), Context(file.toString, 1))
79-
val out = Files.createTempDirectory("scala-cli-tests")
80-
println(s"Using $out as output to process $file")
81-
var lastOutput = ""
82-
commands.foreach { cmd =>
83-
given Context = cmd.context
84-
cmd match
85-
case Commands.Run(cmd, _) =>
86-
println(s"### Running: ${cmd.mkString(" ")}")
87-
try lastOutput = Process(cmd, Some(out.toFile)).!!
88-
catch
89-
case e: Throwable =>
90-
throw new RuntimeException(msg(s"Error running ${cmd.mkString(" ")}"), e)
91-
case Commands.Snippet(name, code, c) =>
92-
println(s"### Writting $name with:\n${code.mkString("\n")}\n---")
93-
val prefix = "\n" * c.line
94-
Files.write(out.resolve(name), code.mkString(prefix, "\n", "").getBytes)
95-
case Commands.Check(patterns, regex, line) =>
96-
assert(lastOutput != "")
97-
val lines = lastOutput.linesIterator.toList
98-
99-
if regex then
100-
patterns.foreach { pattern =>
101-
val regex = pattern.r
102-
assert(
103-
lines.exists(regex.matches),
104-
msg(s"Regex: $pattern, does not matches any line in:\n$lastOutput"))
105-
}
106-
else
107-
patterns.foreach { pattern => assert(
108-
lines.exists(_.contains(pattern)),
109-
msg(s"Pattern: $pattern does not exisits in any line in:\n$lastOutput")
110-
)}
111-
112-
}
61+
def checkPath(path: Path): Seq[TestCase] =
62+
try
63+
if !Files.isDirectory(path) then
64+
if path.getFileName.toString.endsWith(".md") then
65+
checkFile(path)
66+
Seq(TestCase(path, None))
67+
else Nil
68+
else
69+
val toCheck =
70+
Files.list(path).iterator.asScala.filterNot(_.getFileName.toString.startsWith("."))
71+
toCheck.toList.flatMap(checkPath)
72+
catch
73+
case e: Throwable =>
74+
e.printStackTrace()
75+
Seq(TestCase(path, Some(e)))
76+
77+
def checkFile(file: Path) =
78+
val content = Files.lines(file).iterator.asScala.toList
79+
val commands = parse(content, Vector(), Context(file.toString, 1))
80+
val out = Files.createTempDirectory("scala-cli-tests")
81+
println(s"Using $out as output to process $file")
82+
var lastOutput = ""
83+
commands.foreach { cmd =>
84+
given Context = cmd.context
85+
cmd match
86+
case Commands.Run(cmd, _) =>
87+
println(s"### Running: ${cmd.mkString(" ")}")
88+
try lastOutput = Process(cmd, Some(out.toFile)).!!
89+
catch
90+
case e: Throwable =>
91+
throw new RuntimeException(msg(s"Error running ${cmd.mkString(" ")}"), e)
92+
case Commands.Snippet(name, code, c) =>
93+
println(s"### Writting $name with:\n${code.mkString("\n")}\n---")
94+
val prefix = "\n" * c.line
95+
Files.write(out.resolve(name), code.mkString(prefix, "\n", "").getBytes)
96+
case Commands.Check(patterns, regex, line) =>
97+
assert(lastOutput != "")
98+
val lines = lastOutput.linesIterator.toList
99+
100+
if regex then
101+
patterns.foreach { pattern =>
102+
val regex = pattern.r
103+
assert(
104+
lines.exists(regex.matches),
105+
msg(s"Regex: $pattern, does not matches any line in:\n$lastOutput")
106+
)
107+
}
108+
else
109+
patterns.foreach { pattern =>
110+
assert(
111+
lines.exists(_.contains(pattern)),
112+
msg(s"Pattern: $pattern does not exisits in any line in:\n$lastOutput")
113+
)
114+
}
115+
116+
}
113117

114118
@main def check(args: String*) =
115-
val testCases = args.flatMap(a => checkPath(Paths.get(a)))
116-
val (failed, ok) = testCases.partition(_.failure.nonEmpty)
117-
println(s"Completed:\n\t${ok.map(_.path).mkString("\n\t")}")
118-
if failed.nonEmpty then
119-
println(s"Failed:\n\t${failed.map(_.path).mkString("\n\t")}")
120-
sys.exit(1)
121-
println("---")
122-
119+
val testCases = args.flatMap(a => checkPath(Paths.get(a)))
120+
val (failed, ok) = testCases.partition(_.failure.nonEmpty)
121+
println(s"Completed:\n\t${ok.map(_.path).mkString("\n\t")}")
122+
if failed.nonEmpty then
123+
println(s"Failed:\n\t${failed.map(_.path).mkString("\n\t")}")
124+
sys.exit(1)
125+
println("---")

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ final case class Artifacts(
2222
compilerPlugins: Seq[(AnyDependency, String, Path)],
2323
dependencies: Seq[AnyDependency],
2424
detailedArtifacts: Seq[(CsDependency, csCore.Publication, csUtil.Artifact, Path)],
25-
extraJars: Seq[Path],
25+
extraClassPath: Seq[Path],
2626
extraCompileOnlyJars: Seq[Path],
2727
extraSourceJars: Seq[Path],
2828
params: ScalaParameters
@@ -46,9 +46,9 @@ final case class Artifacts(
4646
lazy val compilerClassPath: Seq[Path] =
4747
compilerArtifacts.map(_._2)
4848
lazy val classPath: Seq[Path] =
49-
artifacts.map(_._2) ++ extraJars
49+
artifacts.map(_._2) ++ extraClassPath
5050
lazy val compileClassPath: Seq[Path] =
51-
artifacts.map(_._2) ++ extraJars ++ extraCompileOnlyJars
51+
artifacts.map(_._2) ++ extraClassPath ++ extraCompileOnlyJars
5252
lazy val sourcePath: Seq[Path] =
5353
sourceArtifacts.map(_._2) ++ extraSourceJars
5454
}
@@ -59,7 +59,7 @@ object Artifacts {
5959
params: ScalaParameters,
6060
compilerPlugins: Seq[AnyDependency],
6161
dependencies: Seq[AnyDependency],
62-
extraJars: Seq[Path],
62+
extraClassPath: Seq[Path],
6363
extraCompileOnlyJars: Seq[Path],
6464
extraSourceJars: Seq[Path],
6565
fetchSources: Boolean,
@@ -166,7 +166,7 @@ object Artifacts {
166166
compilerPlugins0,
167167
updatedDependencies,
168168
fetchRes.fullDetailedArtifacts.collect { case (d, p, a, Some(f)) => (d, p, a, f.toPath) },
169-
extraJars ++ extraStubsJars,
169+
extraClassPath ++ extraStubsJars,
170170
extraCompileOnlyJars,
171171
extraSourceJars,
172172
params

modules/build/src/main/scala/scala/build/ReplArtifacts.scala

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,17 @@ import scala.build.errors.BuildException
99

1010
final case class ReplArtifacts(
1111
replArtifacts: Seq[(String, Path)],
12-
extraJars: Seq[Path],
12+
extraClassPath: Seq[Path],
1313
extraSourceJars: Seq[Path],
1414
replMainClass: String,
1515
replJavaOpts: Seq[String],
1616
addSourceJars: Boolean
1717
) {
1818
lazy val replClassPath: Seq[Path] =
1919
if (addSourceJars)
20-
extraJars ++ extraSourceJars ++ replArtifacts.map(_._2)
20+
extraClassPath ++ extraSourceJars ++ replArtifacts.map(_._2)
2121
else
22-
extraJars ++ replArtifacts.map(_._2)
22+
extraClassPath ++ replArtifacts.map(_._2)
2323
}
2424

2525
object ReplArtifacts {
@@ -36,7 +36,7 @@ object ReplArtifacts {
3636
scalaParams: ScalaParameters,
3737
ammoniteVersion: String,
3838
dependencies: Seq[AnyDependency],
39-
extraJars: Seq[Path],
39+
extraClassPath: Seq[Path],
4040
extraSourceJars: Seq[Path],
4141
logger: Logger,
4242
directories: Directories
@@ -53,7 +53,7 @@ object ReplArtifacts {
5353
)
5454
ReplArtifacts(
5555
value(replArtifacts) ++ value(replSourceArtifacts),
56-
extraJars,
56+
extraClassPath,
5757
extraSourceJars,
5858
"ammonite.Main",
5959
Nil,
@@ -64,7 +64,7 @@ object ReplArtifacts {
6464
def default(
6565
scalaParams: ScalaParameters,
6666
dependencies: Seq[AnyDependency],
67-
extraJars: Seq[Path],
67+
extraClassPath: Seq[Path],
6868
logger: Logger,
6969
directories: Directories
7070
): Either[BuildException, ReplArtifacts] = either {
@@ -80,7 +80,7 @@ object ReplArtifacts {
8080
else "dotty.tools.repl.Main"
8181
ReplArtifacts(
8282
value(replArtifacts),
83-
extraJars,
83+
extraClassPath,
8484
Nil,
8585
mainClass,
8686
Seq("-Dscala.usejavacp=true"),

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ final case class BuildOptions(
7878
scalaOptions.compilerPlugins
7979

8080
def allExtraJars: Seq[Path] =
81-
classPathOptions.extraJars.map(_.toNIO)
81+
classPathOptions.extraClassPath.map(_.toNIO)
8282
def allExtraCompileOnlyJars: Seq[Path] =
8383
classPathOptions.extraCompileOnlyJars.map(_.toNIO)
8484
def allExtraSourceJars: Seq[Path] =
@@ -201,7 +201,7 @@ final case class BuildOptions(
201201
params = scalaParams,
202202
compilerPlugins = compilerPlugins,
203203
dependencies = dependencies,
204-
extraJars = allExtraJars,
204+
extraClassPath = allExtraJars,
205205
extraCompileOnlyJars = allExtraCompileOnlyJars,
206206
extraSourceJars = allExtraSourceJars,
207207
fetchSources = classPathOptions.fetchSources.getOrElse(false),

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import dependency._
44

55
final case class ClassPathOptions(
66
extraRepositories: Seq[String] = Nil,
7-
extraJars: Seq[os.Path] = Nil,
7+
extraClassPath: Seq[os.Path] = Nil,
88
extraCompileOnlyJars: Seq[os.Path] = Nil,
99
extraSourceJars: Seq[os.Path] = Nil,
1010
fetchSources: Option[Boolean] = None,

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ case object ScalaPreprocessor extends Preprocessor {
2929
UsingJavaOptionsDirectiveHandler,
3030
UsingJavaHomeDirectiveHandler,
3131
UsingTestFrameworkDirectiveHandler,
32-
UsingCustomJarDirectiveHandler
32+
UsingCustomJarDirectiveHandler,
33+
UsingResourcesDirectiveHandler
3334
)
3435

3536
val requireDirectiveHandlers = Seq[RequireDirectiveHandler](

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ case object UsingCustomJarDirectiveHandler extends UsingDirectiveHandler {
1919
val paths0 = paths.map(os.Path(_, Os.pwd))
2020
val options = BuildOptions(
2121
classPathOptions = ClassPathOptions(
22-
extraJars = paths0
22+
extraClassPath = paths0
2323
)
2424
)
2525
Some(Right(options))
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package scala.build.preprocessing.directives
2+
3+
import scala.build.Os
4+
import scala.build.options.{BuildOptions, ClassPathOptions}
5+
6+
case object UsingResourcesDirectiveHandler extends UsingDirectiveHandler {
7+
def name = "Resources"
8+
def description = "Manually adds a resource directory to the class path"
9+
def usage = "using resource _path_ | using resources _path1_ _path2_ …"
10+
override def usageMd =
11+
"`using resource `_path_ | `using resources `_path1_ _path2_ …"
12+
override def examples = Seq(
13+
"using resource \"./resources\""
14+
)
15+
16+
def handle(directive: Directive): Option[Either[String, BuildOptions]] =
17+
directive.values match {
18+
case Seq("resource" | "resources", paths @ _*) =>
19+
val paths0 = paths.map(os.Path(_, Os.pwd))
20+
val options = BuildOptions(
21+
classPathOptions = ClassPathOptions(
22+
extraClassPath = paths0
23+
)
24+
)
25+
Some(Right(options))
26+
case _ =>
27+
None
28+
}
29+
}

0 commit comments

Comments
 (0)