Skip to content

Commit 4dc618a

Browse files
philwalkGedochaoPhil
authored
provide a way to access paths of source files (#3799)
* fix for 3789 - script if .sc, or (if no extension) has shebang * verify-non-directory in shebang-test * reorder isDir case ahead of isShebangScript * added integration test for enabled case * restore line endings * adjust new test * quote msg * fix quoted msg * Apply suggestions from code review suggested changes Co-authored-by: Piotr Chabelski <[email protected]> * remove isScript extra paren * add script.sources property * fix failing CI / Checks test due to import order * revert treating .scala files as Script * revert attempt #2 * revert /Inputs.scala changes * Apply suggestions from code review Co-authored-by: Piotr Chabelski <[email protected]> * add docs test to scala-script.md * remove brackets from regex * rename new test as show-sources.md * check fix * sclicheck tests for .sc, .scala, and .java sources * fine-tuned show-sources.md * additional examples in show-sources.md --------- Co-authored-by: Piotr Chabelski <[email protected]> Co-authored-by: Phil <[email protected]>
1 parent 9fec582 commit 4dc618a

File tree

3 files changed

+160
-5
lines changed

3 files changed

+160
-5
lines changed

modules/cli/src/main/scala/scala/cli/commands/run/Run.scala

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import scala.build.*
1313
import scala.build.EitherCps.{either, value}
1414
import scala.build.Ops.*
1515
import scala.build.errors.{BuildException, CompositeBuildException}
16-
import scala.build.input.{Inputs, ScalaCliInvokeData, SubCommand}
16+
import scala.build.input.*
1717
import scala.build.internal.{Constants, Runner, ScalaJsLinkerConfig}
1818
import scala.build.internals.ConsoleUtils.ScalaCliConsole
1919
import scala.build.internals.ConsoleUtils.ScalaCliConsole.warnPrefix
@@ -568,9 +568,22 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
568568
}
569569
value(maybeResult)
570570
case Platform.JVM =>
571+
def fwd(s: String): String = s.replace('\\', '/')
572+
def base(s: String): String = fwd(s).replaceAll(".*/", "")
571573
runMode match {
572574
case RunMode.Default =>
575+
val sourceFiles = builds.head.inputs.sourceFiles().map {
576+
case s: ScalaFile => fwd(s.path.toString)
577+
case s: Script => fwd(s.path.toString)
578+
case s: MarkdownFile => fwd(s.path.toString)
579+
case s: OnDisk => fwd(s.path.toString)
580+
case s => s.getClass.getName
581+
}.filter(_.nonEmpty).distinct
582+
val sources = sourceFiles.mkString(File.pathSeparator)
583+
val sourceNames = sourceFiles.map(base(_)).mkString(File.pathSeparator)
584+
573585
val baseJavaProps = builds.head.options.javaOptions.javaOpts.toSeq.map(_.value.value)
586+
++ Seq(s"-Dscala.sources=$sources", s"-Dscala.source.names=$sourceNames")
574587
val setupPython = builds.head.options.notForBloopOptions.doSetupPython.getOrElse(false)
575588
val (pythonJavaProps, pythonExtraEnv) =
576589
if (setupPython) {

modules/docs-tests/src/main/scala/sclicheck/sclicheck.scala

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ def checkFile(file: os.Path, options: Options): Unit =
201201
|exec $escapedCommand "$$@"
202202
|""".stripMargin
203203
os.write(binDir0 / scriptName, scriptCode)
204-
os.perms.set(binDir0 / scriptName, "rwxr-xr-x")
204+
if !scala.util.Properties.isWin then
205+
os.perms.set(binDir0 / scriptName, "rwxr-xr-x")
205206
}
206207
createHelperScript(options.scalaCliCommand, "scala-cli")
207208
createHelperScript(options.scalaCliCommand, "scala")
@@ -289,9 +290,12 @@ def checkFile(file: os.Path, options: Options): Unit =
289290
.foreach(os.remove.all)
290291
val script = out / ".scala-build" / "run.sh"
291292
os.write.over(script, mkBashScript(cmds), createFolders = true)
292-
os.perms.set(script, "rwxr-xr-x")
293-
294-
val exitCode = run(os.proc(script))
293+
val scriptProc = if scala.util.Properties.isWin then
294+
os.proc("sh.exe", script)
295+
else
296+
os.perms.set(script, "rwxr-xr-x")
297+
os.proc(script)
298+
val exitCode = run(scriptProc)
295299
if shouldFail then
296300
check(exitCode != 0, s"Commands should fail.")
297301
else
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
---
2+
title: Accessing Source File Paths
3+
sidebar_position: 10
4+
---
5+
6+
## Source Names and Paths
7+
8+
System properties provide source file paths, separated by `java.io.File.pathSeparator`.
9+
- `scala.source.names` filenames without a path.
10+
- `scala.sources` full paths to source files
11+
12+
### System property `scala.source.names`
13+
14+
This simple script reports the script file name:
15+
16+
```scala title=reportNames.sc
17+
val name = sys.props("scala.source.names")
18+
println(s"scriptName: $name")
19+
```
20+
21+
```bash
22+
scala-cli reportNames.sc
23+
# scriptName: reportNames.sc
24+
```
25+
26+
<!-- Expected-regex:
27+
scriptName: reportNames.sc
28+
-->
29+
30+
Likewise, for this `.scala` program:
31+
32+
```scala title=SourceNames.scala
33+
object Main {
34+
def main(args: Array[String]): Unit =
35+
val names = sys.props("scala.source.names")
36+
println(names)
37+
}
38+
```
39+
40+
```bash
41+
scala-cli SourceNames.scala
42+
# SourceNames.scala
43+
```
44+
45+
<!-- Expected-regex:
46+
SourceNames[.]scala
47+
-->
48+
49+
This java program prints a comma-separated list of source names:
50+
51+
```java title=NamesLister.java
52+
public class NamesLister {
53+
public static void main(String[] args) {
54+
String sourceNames = System.getProperty("scala.source.names");
55+
String[] names = sourceNames.split(java.io.File.pathSeparator);
56+
String list = String.join(",", names);
57+
System.out.printf("%s\n", list);
58+
}
59+
}
60+
61+
```
62+
63+
```bash
64+
scala-cli NamesLister.java
65+
# NamesLister.java
66+
```
67+
68+
<!-- Expected-regex:
69+
NamesLister.java
70+
-->
71+
72+
73+
If multiple sources are provided, all are displayed:
74+
75+
```bash
76+
scala-cli NamesLister.java reportNames.sc SourceNames.scala --main-class NamesLister
77+
# NamesLister.java,reportNames.sc,SourceNames.scala
78+
```
79+
80+
<!-- Expected-regex:
81+
NamesLister[.]java,reportNames[.]sc,SourceNames[.]scala
82+
-->
83+
84+
### System property `scala.sources`
85+
86+
```java title=PathsLister.java
87+
public class PathsLister {
88+
public static void main(String[] args) {
89+
String paths = System.getProperty("scala.sources");
90+
String list = String.join(",", paths.split(java.io.File.pathSeparator));
91+
System.out.printf("%s\n", list);
92+
}
93+
}
94+
```
95+
The property "scala.sources" displays full source file paths:
96+
97+
```bash
98+
scala-cli PathsLister.java reportNames.sc SourceNames.scala --main-class PathsLister
99+
# /tmp/workdir/PathsLister.java,/tmp/workdir/reportNames.sc,/tmp/workdir/SourceNames.scala
100+
```
101+
102+
<!-- Expected-regex:
103+
.*/PathsLister[.]java,.*/reportNames[.]sc,.*/SourceNames[.]scala
104+
-->
105+
106+
### Other examples
107+
108+
When multiple `.md` files with plain `scala` snippets are being run, names and paths can refer to '.md' files:
109+
110+
````markdown title=MainA.md
111+
# Main class A
112+
```scala
113+
println(s"A: ${sys.props("scala.source.names")}")
114+
```
115+
````
116+
117+
118+
````markdown title=MainB.md
119+
# Main class 2
120+
```scala
121+
println(s"2: ${sys.props("scala.source.names")}")
122+
```
123+
````
124+
125+
When multiple such sources are passed as inputs, the main class has to be passed explicitly with the `--main-class`
126+
option.
127+
128+
```bash
129+
scala-cli --power MainA.md MainB.md --main-class MainA_md
130+
# A: MainA.md:MainB.md
131+
```
132+
133+
```text
134+
1: MainA.md:MainB.md
135+
```
136+
<!-- Expected-regex:
137+
A: MainA[.]md:MainB[.]md
138+
-->

0 commit comments

Comments
 (0)