Skip to content

Commit ba55c06

Browse files
authored
using classpath: resolve relatively to the declaring file (#161)
1 parent 948f200 commit ba55c06

File tree

7 files changed

+60
-36
lines changed

7 files changed

+60
-36
lines changed

core/src/main/scala/replpp/UsingDirectives.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,13 @@ object UsingDirectives {
4747
def findResolvers(lines: IterableOnce[String]): Seq[String] =
4848
scanFor(ResolverDirective, lines)
4949

50-
def findClasspathEntries(lines: IterableOnce[String]): Seq[String] =
51-
scanFor(ClasspathDirective, lines)
50+
def findClasspathEntries(files: IterableOnce[Path]): Seq[Path] = {
51+
for {
52+
file <- files.iterator.toSeq
53+
classpathEntry <- scanFor(ClasspathDirective, util.linesFromFile(file))
54+
pathRelativeToDeclaringFile = file.getParent.resolve(classpathEntry)
55+
} yield pathRelativeToDeclaringFile
56+
}
5257

5358
private def scanFor(directive: String, lines: IterableOnce[String]): Seq[String] = {
5459
lines

core/src/main/scala/replpp/package.scala

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,29 +41,34 @@ package object replpp {
4141
compilerArgs.result()
4242
}
4343

44+
/** recursively find all relevant source files from main script, global predef file,
45+
* provided predef files, other scripts that were imported with `using file` directive */
46+
def allSourceFiles(config: Config): Seq[Path] =
47+
(allPredefFiles(config) ++ config.scriptFile).distinct.sorted
48+
4449
def allPredefFiles(config: Config): Seq[Path] = {
4550
val allPredefFiles = mutable.Set.empty[Path]
4651
allPredefFiles ++= config.predefFiles
4752
globalPredefFileMaybe.foreach(allPredefFiles.addOne)
4853

4954
// the directly resolved predef files might reference additional files via `using` directive
5055
val predefFilesDirect = allPredefFiles.toSet
51-
predefFilesDirect.foreach { file =>
52-
val importedFiles = UsingDirectives.findImportedFilesRecursively(file, visited = allPredefFiles.toSet)
56+
predefFilesDirect.foreach { predefFile =>
57+
val importedFiles = UsingDirectives.findImportedFilesRecursively(predefFile, visited = allPredefFiles.toSet)
5358
allPredefFiles ++= importedFiles
5459
}
5560

5661
// the script (if any) might also reference additional files via `using` directive
57-
config.scriptFile.foreach { file =>
58-
val importedFiles = UsingDirectives.findImportedFilesRecursively(file, visited = allPredefFiles.toSet)
62+
config.scriptFile.foreach { scriptFile =>
63+
val importedFiles = UsingDirectives.findImportedFilesRecursively(scriptFile, visited = allPredefFiles.toSet)
5964
allPredefFiles ++= importedFiles
6065
}
6166

6267
allPredefFiles.toSeq.sorted
6368
}
6469

65-
def allPredefLines(config: Config): Seq[String] =
66-
allPredefFiles(config).flatMap(linesFromFile)
70+
def allSourceLines(config: Config): Seq[String] =
71+
allSourceFiles(config).flatMap(linesFromFile)
6772

6873
/** precompile given predef files (if any) and update Config to include the results in the classpath */
6974
def precompilePredefFiles(config: Config): Config = {

core/src/main/scala/replpp/scripting/NonForkingScriptRunner.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package replpp.scripting
22

3-
import replpp.{Config, allPredefFiles}
3+
import replpp.{Config, allPredefFiles, allSourceFiles}
44

55
import java.nio.file.Files
66
import scala.util.{Failure, Success}

core/src/main/scala/replpp/util/ClasspathHelper.scala

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,23 +54,21 @@ object ClasspathHelper {
5454
Using.resource(Source.fromFile(path.toFile))(_.getLines.toSeq)
5555
}.getOrElse(Seq.empty)
5656

57-
val fromDependencies = dependencyArtifacts(config, scriptLines)
57+
val fromDependencies = dependencyArtifacts(config)
5858
fromDependencies.foreach(entries.addOne)
5959
if (fromDependencies.nonEmpty && !quiet) {
6060
println(s"resolved dependencies - adding ${fromDependencies.size} artifact(s) to classpath - to list them, enable verbose mode")
6161
if (verboseEnabled(config)) fromDependencies.foreach(println)
6262
}
6363

64-
val allLines = allPredefLines(config) ++ scriptLines
65-
val additionalEntries = config.classpathConfig.additionalClasspathEntries ++ UsingDirectives.findClasspathEntries(allLines)
66-
additionalEntries.map(Paths.get(_)).foreach(entries.addOne)
64+
config.classpathConfig.additionalClasspathEntries.map(Paths.get(_)).foreach(entries.addOne)
65+
UsingDirectives.findClasspathEntries(allSourceFiles(config)).iterator.foreach(entries.addOne)
6766

68-
entries.result().sorted
67+
entries.result().distinct.sorted
6968
}
7069

71-
private[util] def dependencyArtifacts(config: Config, scriptLines: Seq[String]): Seq[Path] = {
72-
val allLines = allPredefLines(config) ++ scriptLines
73-
70+
private[util] def dependencyArtifacts(config: Config): Seq[Path] = {
71+
val allLines = allSourceLines(config)
7472
val resolvers = config.classpathConfig.resolvers ++ UsingDirectives.findResolvers(allLines)
7573
val allDependencies = config.classpathConfig.dependencies ++ UsingDirectives.findDeclaredDependencies(allLines)
7674
Dependencies.resolve(allDependencies, resolvers, verboseEnabled(config)).get

core/src/test/scala/replpp/PredefTests.scala

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class PredefTests extends AnyWordSpec with Matchers {
77
given Colors = Colors.BlackWhite
88

99
"recursively resolve `//> using file` directive" in {
10+
val script = os.temp("val mainScript = 5")
1011
val additionalScript1 = os.temp("val additionalScript1 = 10")
1112
val additionalScript2 = os.temp("val additionalScript2 = 20")
1213
val predefFile = os.temp(
@@ -15,17 +16,19 @@ class PredefTests extends AnyWordSpec with Matchers {
1516
|//> using file $additionalScript2
1617
|""".stripMargin)
1718

18-
allPredefLines(Config(predefFiles = Seq(predefFile.toNIO))).sorted shouldBe
19+
allSourceLines(Config(predefFiles = Seq(predefFile.toNIO), scriptFile = Some(script.toNIO))).sorted shouldBe
1920
Seq(
2021
s"//> using file $additionalScript1",
2122
s"//> using file $additionalScript2",
2223
"val additionalScript1 = 10",
2324
"val additionalScript2 = 20",
24-
"val predefCode = 1"
25+
"val mainScript = 5",
26+
"val predefCode = 1",
2527
).sorted
2628
}
2729

2830
"recursively resolve `//> using file` directive - with recursive loops" in {
31+
val script = os.temp("val mainScript = 5")
2932
val additionalScript1 = os.temp(suffix = "-script1")
3033
val additionalScript2 = os.temp(suffix = "-script2")
3134

@@ -43,13 +46,14 @@ class PredefTests extends AnyWordSpec with Matchers {
4346
|""".stripMargin)
4447

4548
// most importantly, this should not loop endlessly due to the recursive imports
46-
allPredefLines(Config(predefFiles = Seq(predefFile.toNIO))).distinct.sorted shouldBe
49+
allSourceLines(Config(predefFiles = Seq(predefFile.toNIO), scriptFile = Some(script.toNIO))).distinct.sorted shouldBe
4750
Seq(
4851
s"//> using file $additionalScript1",
4952
s"//> using file $additionalScript2",
5053
"val additionalScript1 = 10",
5154
"val additionalScript2 = 20",
52-
"val predefCode = 1"
55+
"val mainScript = 5",
56+
"val predefCode = 1",
5357
).sorted
5458
}
5559
}

core/src/test/scala/replpp/UsingDirectivesTests.scala

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package replpp
22

3-
import java.nio.file.Paths
3+
import java.nio.file.{Path, Paths}
44
import org.scalatest.matchers.should.Matchers
55
import org.scalatest.wordspec.AnyWordSpec
6+
67
import scala.jdk.CollectionConverters.*
78

89
class UsingDirectivesTests extends AnyWordSpec with Matchers {
@@ -78,18 +79,28 @@ class UsingDirectivesTests extends AnyWordSpec with Matchers {
7879
results should not contain "https://commented.out/repo"
7980
}
8081

81-
"find declared classpath entries" in {
82-
val source =
83-
"""
84-
|//> using classpath /path/to/cp1
85-
|//> using classpath ../path/to/cp2
86-
|// //> using classpath cp3
87-
|""".stripMargin
88-
89-
val results = UsingDirectives.findClasspathEntries(source.linesIterator)
90-
results should contain("/path/to/cp1")
91-
results should contain("../path/to/cp2")
92-
results should not contain "cp3"
82+
if (scala.util.Properties.isWin) {
83+
info("paths work differently on windows - ignoring some tests")
84+
} else {
85+
"find declared classpath entries" in {
86+
val scriptFile = os.temp(
87+
"""
88+
|//> using classpath /path/to/cp1
89+
|//> using classpath path/to/cp2
90+
|//> using classpath ../path/to/cp3
91+
|// //> using classpath cp4
92+
|""".stripMargin
93+
).toNIO
94+
95+
val scriptParentDir = scriptFile.getParent
96+
97+
val results = UsingDirectives.findClasspathEntries(Seq(scriptFile))
98+
results should contain(Path.of("/path/to/cp1"))
99+
results should contain(scriptParentDir.resolve("path/to/cp2"))
100+
results should contain(scriptParentDir.resolve("../path/to/cp3"))
101+
results should not contain Path.of("cp3")
102+
results.size shouldBe 3 // just to triple check
103+
}
93104
}
94105

95106
}

core/src/test/scala/replpp/util/ClasspathHelperTests.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,16 @@ class ClasspathHelperTests extends AnyWordSpec with Matchers {
2929
"org.scala-lang:scala-library:2.13.10",
3030
"org.scala-lang::scala3-library:3.3.0",
3131
)))
32-
val deps = ClasspathHelper.dependencyArtifacts(config, scriptLines = Seq.empty)
32+
val deps = ClasspathHelper.dependencyArtifacts(config)
3333
deps.size shouldBe 2
3434

3535
assert(deps.find(_.endsWith("scala3-library_3-3.3.0.jar")).isDefined)
3636
assert(deps.find(_.endsWith("scala-library-2.13.10.jar")).isDefined)
3737
}
3838

3939
"declared in scriptFile" in {
40-
val deps = ClasspathHelper.dependencyArtifacts(Config(), scriptLines = Seq("//> using dep com.michaelpollmeier::colordiff:0.36"))
40+
val script = os.temp("//> using dep com.michaelpollmeier::colordiff:0.36")
41+
val deps = ClasspathHelper.dependencyArtifacts(Config(scriptFile = Some(script.toNIO)))
4142
deps.size shouldBe 4
4243

4344
assert(deps.find(_.endsWith("colordiff_3-0.36.jar")).isDefined)

0 commit comments

Comments
 (0)