Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow shebangs and using directives in scala files #435

Merged
merged 1 commit into from
Dec 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,16 @@ case object ScalaPreprocessor extends Preprocessor {
Option[String]
)]] = either {

val afterStrictUsing = value(processStrictUsing(content, scopeRoot))
val (content0, isSheBang) = SheBang.ignoreSheBangLines(content)

val afterStrictUsing = value(processStrictUsing(content0, scopeRoot))
val afterUsing = value {
processUsing(path, afterStrictUsing.map(_._2).getOrElse(content), scopeRoot)
processUsing(path, afterStrictUsing.map(_._2).getOrElse(content0), scopeRoot)
.sequence
}
val afterProcessImports = value {
processSpecialImports(
afterUsing.flatMap(_._4).orElse(afterStrictUsing.map(_._2)).getOrElse(content),
afterUsing.flatMap(_._4).orElse(afterStrictUsing.map(_._2)).getOrElse(content0),
path
)
}
Expand All @@ -151,6 +153,7 @@ case object ScalaPreprocessor extends Preprocessor {
.map(_._3)
.orElse(afterUsing.flatMap(_._4))
.orElse(afterStrictUsing.map(_._2))
.orElse(if (isSheBang) Some(content0) else None)
val scopedRequirements = afterUsing.map(_._2).getOrElse(Nil)
Some((summedRequirements, scopedRequirements, summedOptions, lastContentOpt))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import scala.build.Inputs
import scala.build.errors.BuildException
import scala.build.internal.{AmmUtil, CodeWrapper, CustomCodeWrapper, Name}
import scala.build.options.{BuildOptions, BuildRequirements}
import scala.util.matching.Regex

final case class ScriptPreprocessor(codeWrapper: CodeWrapper) extends Preprocessor {
def preprocess(input: Inputs.SingleElement)
Expand Down Expand Up @@ -54,23 +53,6 @@ final case class ScriptPreprocessor(codeWrapper: CodeWrapper) extends Preprocess

object ScriptPreprocessor {

private val sheBangRegex: Regex = s"""(^(#!.*(\\r\\n?|\\n)?)+(\\s*!#.*)?)""".r

private def ignoreSheBangLines(content: String): String =
if (content.startsWith("#!")) {
val regexMatch = sheBangRegex.findFirstMatchIn(content)
regexMatch match {
case Some(firstMatch) =>
content.replace(
firstMatch.toString(),
System.lineSeparator() * firstMatch.toString().split(System.lineSeparator()).length
)
case None => content
}
}
else
content

private def preprocess(
reportingPath: Either[String, os.Path],
content: String,
Expand All @@ -79,7 +61,7 @@ object ScriptPreprocessor {
scopePath: ScopePath
): Either[BuildException, List[PreprocessedSource.InMemory]] = either {

val contentIgnoredSheBangLines = ignoreSheBangLines(content)
val (contentIgnoredSheBangLines, _) = SheBang.ignoreSheBangLines(content)

val (pkg, wrapper) = AmmUtil.pathToPackageWrapper(subPath)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package scala.build.preprocessing

import scala.util.matching.Regex

object SheBang {
private val sheBangRegex: Regex = s"""(^(#!.*(\\r\\n?|\\n)?)+(\\s*!#.*)?)""".r

def ignoreSheBangLines(content: String): (String, Boolean) =
if (content.startsWith("#!")) {
val regexMatch = sheBangRegex.findFirstMatchIn(content)
regexMatch match {
case Some(firstMatch) =>
content.replace(
firstMatch.toString(),
System.lineSeparator() * firstMatch.toString().split(System.lineSeparator()).length
) -> true
case None => (content, false)
}
}
else
(content, false)

}
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ class SourcesTests extends munit.FunSuite {
}
}

test("should skip SheBang in .sc") {
test("should skip SheBang in .sc and .scala") {
val testInputs = TestInputs(
os.rel / "something1.sc" ->
"""#!/usr/bin/env scala-cli
Expand All @@ -227,14 +227,14 @@ class SourcesTests extends munit.FunSuite {
|#! nix-shell -i scala-cli
|
|println("Hello World")""".stripMargin,
os.rel / "something4.sc" ->
os.rel / "something4.scala" ->
"""#!/usr/bin/scala-cli
|#! nix-shell -i scala-cli
|
|!#
|
|println("Hello World")""".stripMargin,
os.rel / "something5.sc" ->
os.rel / "something5.scala" ->
"""#!/usr/bin/scala-cli
|
|println("Hello World #!")""".stripMargin
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1207,7 +1207,7 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String])
}
}

if (!Properties.isWin)
if (!Properties.isWin) {
test("CLI args passed to shebang script") {
val inputs = TestInputs(
Seq(
Expand All @@ -1222,6 +1222,25 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String])
expect(p.out.text().trim == "List(1, 2, 3, -v)")
}
}
test("CLI args passed to shebang in Scala file") {
val inputs = TestInputs(
Seq(
os.rel / "f.scala" -> s"""|#!/usr/bin/env -S ${TestUtil.cli.mkString(" ")} shebang
|object Hello {
| def main(args: Array[String]) = {
| println(args.toList)
| }
|}
|""".stripMargin
)
)
inputs.fromRoot { root =>
os.perms.set(root / "f.scala", os.PermSet.fromString("rwx------"))
val p = os.proc("./f.scala", "1", "2", "3", "-v").call(cwd = root)
expect(p.out.text().trim == "List(1, 2, 3, -v)")
}
}
}

test("Runs with JVM 8") {
val inputs = TestInputs(
Expand Down