Skip to content

Commit a4d814b

Browse files
philwalkGedochaoPhil
authored
script extension can be either '.sc' or empty string (#3802)
* 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]> * legal script extension restricted to .sc or empty string * remove unrelated code * cleanup imports * corrected hasShebang check * corrected hasShebang test * hasShebang adapted for /dev/fd * input sc script def * fix tests * avoid hasShebang test of streamed bytes * disable hasShebang test for streams * Update modules/integration/src/test/scala/scala/cli/integration/RunScriptTestDefinitions.scala Co-authored-by: Piotr Chabelski <[email protected]> --------- Co-authored-by: Piotr Chabelski <[email protected]> Co-authored-by: Phil <[email protected]>
1 parent b83cc64 commit a4d814b

File tree

4 files changed

+19
-12
lines changed

4 files changed

+19
-12
lines changed

modules/build/src/main/scala/scala/build/input/ElementsUtils.scala

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ import scala.build.internal.Constants
99

1010
object ElementsUtils {
1111
extension (p: os.Path) {
12-
def hasShebang: Boolean = os.read.bytes(p, offset = 0, count = 2) == Array('#', '!')
13-
def isScript: Boolean = p.ext == "sc" || (p.hasShebang && p.ext != "scala")
12+
def hasShebang: Boolean =
13+
os.isFile(p) && !p.toString.startsWith("/dev/fd/") &&
14+
String(os.read.bytes(p, offset = 0, count = 2)) == "#!"
15+
def isScript: Boolean = p.ext == "sc" || (p.hasShebang && p.ext.isEmpty)
1416
}
1517

1618
extension (d: Directory) {
@@ -25,12 +27,13 @@ object ElementsUtils {
2527
ProjectScalaFile(d.path, p.subRelativeTo(d.path))
2628
case p if p.last.endsWith(".scala") =>
2729
SourceScalaFile(d.path, p.subRelativeTo(d.path))
28-
case p if p.last.endsWith(".sc") || p.hasShebang =>
29-
Script(d.path, p.subRelativeTo(d.path), None)
3030
case p if p.last.endsWith(".c") || p.last.endsWith(".h") =>
3131
CFile(d.path, p.subRelativeTo(d.path))
3232
case p if p.last.endsWith(".md") && enableMarkdown =>
3333
MarkdownFile(d.path, p.subRelativeTo(d.path))
34+
case p if p.last.endsWith(".sc") =>
35+
// TODO: hasShebang test without consuming 1st 2 bytes of Stream
36+
Script(d.path, p.subRelativeTo(d.path), None)
3437
}
3538
.toVector
3639
.sortBy(_.subPath.segments)

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,15 +276,16 @@ object Inputs {
276276
else if path.last == Constants.projectFileName then
277277
Right(Seq(ProjectScalaFile(dir, subPath)))
278278
else if os.isDir(path) then Right(Seq(Directory(path)))
279-
else if arg.endsWith(".sc") || (os.exists(path) && isShebangScript(String(content))) then
280-
Right(Seq(Script(dir, subPath, Some(arg))))
279+
else if arg.endsWith(".sc") then Right(Seq(Script(dir, subPath, Some(arg))))
281280
else if arg.endsWith(".scala") then Right(Seq(SourceScalaFile(dir, subPath)))
282281
else if arg.endsWith(".java") then Right(Seq(JavaFile(dir, subPath)))
283282
else if arg.endsWith(".jar") then Right(Seq(JarFile(dir, subPath)))
284283
else if arg.endsWith(".c") || arg.endsWith(".h") then Right(Seq(CFile(dir, subPath)))
285284
else if arg.endsWith(".md") then Right(Seq(MarkdownFile(dir, subPath)))
286285
else if acceptFds && arg.startsWith("/dev/fd/") then
287286
Right(Seq(VirtualScript(content, arg, os.sub / s"input-${idx + 1}.sc")))
287+
else if path.ext.isEmpty && os.exists(path) && path.isScript then
288+
Right(Seq(Script(dir, subPath, Some(arg))))
288289
else if programInvokeData.subCommand == SubCommand.Shebang && os.exists(path) then
289290
if isShebangScript(String(content)) then Right(Seq(Script(dir, subPath, Some(arg))))
290291
else

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,7 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
590590
}
591591
else
592592
(Nil, Map.empty[String, String])
593+
593594
val allJavaOpts = pythonJavaProps ++ baseJavaProps
594595
if showCommand then
595596
Left {

modules/integration/src/test/scala/scala/cli/integration/RunScriptTestDefinitions.scala

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -574,17 +574,19 @@ trait RunScriptTestDefinitions { _: RunTestDefinitions =>
574574
}
575575

576576
test("script file with shebang header and no extension run with scala-cli") {
577-
val msg = "compiled and ran"
578-
val inputs = TestInputs(
579-
os.rel / "scriptWithShebang" ->
577+
val expected = "success"
578+
val scriptName = "scriptWithShebang"
579+
val inputs = TestInputs(
580+
os.rel / scriptName ->
580581
s"""|#!/usr/bin/env -S ${TestUtil.cli.mkString(" ")} shebang
581582
|//> using scala $actualScalaVersion
582-
|println(s"$msg")""".stripMargin
583+
|println(s"$expected")""".stripMargin
583584
)
585+
val cmd = TestUtil.cli ++ Seq(scriptName)
584586
inputs.fromRoot { root =>
585-
val output = os.proc(TestUtil.cli, "scriptWithShebang")
587+
val actual = os.proc(cmd)
586588
.call(cwd = root).out.trim()
587-
expect(output == msg)
589+
expect(actual == expected)
588590
}
589591
}
590592

0 commit comments

Comments
 (0)