Skip to content

Commit 1879288

Browse files
committed
Add support of running script files with no extension using shebang subcommand
1 parent bb30ec9 commit 1879288

File tree

19 files changed

+126
-17
lines changed

19 files changed

+126
-17
lines changed

modules/build/src/main/scala/scala/build/Build.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ object Build {
233233
() => options.javaHome().value.javaCommand
234234
),
235235
logger,
236+
options.scriptOptions.runWithShebang,
236237
options.suppressWarningOptions.suppressDirectivesInMultipleFilesWarning
237238
)
238239
}

modules/build/src/main/scala/scala/build/CrossSources.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ object CrossSources {
126126
inputs: Inputs,
127127
preprocessors: Seq[Preprocessor],
128128
logger: Logger,
129+
isRunWithShebang: Option[Boolean],
129130
suppressDirectivesInMultipleFilesWarning: Option[Boolean],
130131
maybeRecoverOnError: BuildException => Option[BuildException] = e => Some(e)
131132
): Either[BuildException, (CrossSources, Inputs)] = either {
@@ -161,7 +162,10 @@ object CrossSources {
161162
.flatMap(_.internal.extraSourceFiles)
162163
.distinct
163164
val inputsElemFromDirectives =
164-
value(resolveInputsFromSources(sourcesFromDirectives, inputs.enableMarkdown))
165+
value(resolveInputsFromSources(
166+
sourcesFromDirectives,
167+
inputs.enableMarkdown
168+
))
165169
val preprocessedSourcesFromDirectives = value(preprocessSources(inputsElemFromDirectives))
166170
val allInputs = inputs.add(inputsElemFromDirectives)
167171

@@ -261,7 +265,10 @@ object CrossSources {
261265
(CrossSources(paths, inMemory, defaultMainClassOpt, resourceDirs, buildOptions), allInputs)
262266
}
263267

264-
private def resolveInputsFromSources(sources: Seq[Positioned[os.Path]], enableMarkdown: Boolean) =
268+
private def resolveInputsFromSources(
269+
sources: Seq[Positioned[os.Path]],
270+
enableMarkdown: Boolean
271+
) =
265272
sources.map { source =>
266273
val sourcePath = source.value
267274
lazy val dir = sourcePath / os.up

modules/build/src/main/scala/scala/build/bsp/BspImpl.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ final class BspImpl(
101101
() => buildOptions.javaHome().value.javaCommand
102102
),
103103
persistentLogger,
104+
isRunWithShebang = None,
104105
suppressDirectivesInMultipleFilesWarning = None,
105106
maybeRecoverOnError(Scope.Main)
106107
).left.map((_, Scope.Main))

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

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,8 @@ object Inputs {
261261
download: String => Either[String, Array[Byte]],
262262
stdinOpt: => Option[Array[Byte]],
263263
acceptFds: Boolean,
264-
enableMarkdown: Boolean
264+
enableMarkdown: Boolean,
265+
isRunWithShebang: Boolean
265266
): Seq[Either[String, Seq[Element]]] = args.zipWithIndex.map {
266267
case (arg, idx) =>
267268
lazy val path = os.Path(arg, cwd)
@@ -297,6 +298,8 @@ object Inputs {
297298
else if os.isDir(path) then Right(Seq(Directory(path)))
298299
else if acceptFds && arg.startsWith("/dev/fd/") then
299300
Right(Seq(VirtualScript(content, arg, os.sub / s"input-${idx + 1}.sc")))
301+
else if isRunWithShebang && String(content).startsWith("#!") then
302+
Right(Seq(Script(dir, subPath)))
300303
else {
301304
val msg =
302305
if (os.exists(path))
@@ -320,10 +323,11 @@ object Inputs {
320323
forcedWorkspace: Option[os.Path],
321324
enableMarkdown: Boolean,
322325
allowRestrictedFeatures: Boolean,
323-
extraClasspathWasPassed: Boolean
326+
extraClasspathWasPassed: Boolean,
327+
isRunWithShebang: Boolean
324328
): Either[BuildException, Inputs] = {
325329
val validatedArgs: Seq[Either[String, Seq[Element]]] =
326-
validateArgs(args, cwd, download, stdinOpt, acceptFds, enableMarkdown)
330+
validateArgs(args, cwd, download, stdinOpt, acceptFds, enableMarkdown, isRunWithShebang)
327331
val validatedSnippets: Seq[Either[String, Seq[Element]]] =
328332
validateSnippets(scriptSnippetList, scalaSnippetList, javaSnippetList, markdownSnippetList)
329333
val validatedArgsAndSnippets = validatedArgs ++ validatedSnippets
@@ -364,7 +368,8 @@ object Inputs {
364368
forcedWorkspace: Option[os.Path] = None,
365369
enableMarkdown: Boolean = false,
366370
allowRestrictedFeatures: Boolean,
367-
extraClasspathWasPassed: Boolean
371+
extraClasspathWasPassed: Boolean,
372+
isRunWithShebang: Boolean
368373
): Either[BuildException, Inputs] =
369374
if (
370375
args.isEmpty && scriptSnippetList.isEmpty && scalaSnippetList.isEmpty && javaSnippetList.isEmpty &&
@@ -388,7 +393,8 @@ object Inputs {
388393
forcedWorkspace,
389394
enableMarkdown,
390395
allowRestrictedFeatures,
391-
extraClasspathWasPassed
396+
extraClasspathWasPassed,
397+
isRunWithShebang
392398
)
393399

394400
def default(): Option[Inputs] = None

modules/build/src/test/scala/scala/build/tests/SourcesTests.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class SourcesTests extends munit.FunSuite {
5656
inputs,
5757
preprocessors,
5858
TestLogger(),
59+
isRunWithShebang = None,
5960
suppressDirectivesInMultipleFilesWarning = None
6061
).orThrow
6162
val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow
@@ -90,6 +91,7 @@ class SourcesTests extends munit.FunSuite {
9091
inputs,
9192
preprocessors,
9293
TestLogger(),
94+
isRunWithShebang = None,
9395
suppressDirectivesInMultipleFilesWarning = None
9496
).orThrow
9597
val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow
@@ -122,6 +124,7 @@ class SourcesTests extends munit.FunSuite {
122124
inputs,
123125
preprocessors,
124126
TestLogger(),
127+
isRunWithShebang = None,
125128
suppressDirectivesInMultipleFilesWarning = None
126129
).orThrow
127130
val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow
@@ -154,6 +157,7 @@ class SourcesTests extends munit.FunSuite {
154157
inputs,
155158
preprocessors,
156159
TestLogger(),
160+
isRunWithShebang = None,
157161
suppressDirectivesInMultipleFilesWarning = None
158162
).orThrow
159163
val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow
@@ -189,6 +193,7 @@ class SourcesTests extends munit.FunSuite {
189193
inputs,
190194
preprocessors,
191195
TestLogger(),
196+
isRunWithShebang = None,
192197
suppressDirectivesInMultipleFilesWarning = None
193198
).orThrow
194199
val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow
@@ -226,6 +231,7 @@ class SourcesTests extends munit.FunSuite {
226231
inputs,
227232
preprocessors,
228233
TestLogger(),
234+
isRunWithShebang = None,
229235
suppressDirectivesInMultipleFilesWarning = None
230236
).orThrow
231237
val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow
@@ -255,6 +261,7 @@ class SourcesTests extends munit.FunSuite {
255261
inputs,
256262
preprocessors,
257263
TestLogger(),
264+
isRunWithShebang = None,
258265
suppressDirectivesInMultipleFilesWarning = None
259266
)
260267
expect(crossSources.isLeft)
@@ -277,6 +284,7 @@ class SourcesTests extends munit.FunSuite {
277284
inputs,
278285
preprocessors,
279286
TestLogger(),
287+
isRunWithShebang = None,
280288
suppressDirectivesInMultipleFilesWarning = None
281289
).isLeft)
282290
}
@@ -334,6 +342,7 @@ class SourcesTests extends munit.FunSuite {
334342
inputs,
335343
preprocessors,
336344
TestLogger(),
345+
isRunWithShebang = None,
337346
suppressDirectivesInMultipleFilesWarning = None
338347
).orThrow
339348
val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow
@@ -367,6 +376,7 @@ class SourcesTests extends munit.FunSuite {
367376
inputs,
368377
preprocessors,
369378
TestLogger(),
379+
isRunWithShebang = None,
370380
suppressDirectivesInMultipleFilesWarning = None
371381
).orThrow
372382
val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow
@@ -403,6 +413,7 @@ class SourcesTests extends munit.FunSuite {
403413
inputs,
404414
preprocessors,
405415
TestLogger(),
416+
isRunWithShebang = None,
406417
suppressDirectivesInMultipleFilesWarning = None
407418
).orThrow
408419
val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow
@@ -430,6 +441,7 @@ class SourcesTests extends munit.FunSuite {
430441
inputs,
431442
preprocessors,
432443
TestLogger(),
444+
isRunWithShebang = None,
433445
suppressDirectivesInMultipleFilesWarning = None
434446
).orThrow
435447
val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow
@@ -468,6 +480,7 @@ class SourcesTests extends munit.FunSuite {
468480
inputs,
469481
preprocessors,
470482
TestLogger(),
483+
isRunWithShebang = None,
471484
suppressDirectivesInMultipleFilesWarning = None
472485
).orThrow
473486
val scopedSources = crossSources.scopedSources(BuildOptions()).orThrow
@@ -508,6 +521,7 @@ class SourcesTests extends munit.FunSuite {
508521
inputs,
509522
preprocessors,
510523
TestLogger(),
524+
isRunWithShebang = None,
511525
suppressDirectivesInMultipleFilesWarning = None
512526
)
513527
crossSources match {
@@ -529,6 +543,7 @@ class SourcesTests extends munit.FunSuite {
529543
inputs,
530544
preprocessors,
531545
TestLogger(),
546+
isRunWithShebang = None,
532547
suppressDirectivesInMultipleFilesWarning = None
533548
)
534549
crossSources match {

modules/build/src/test/scala/scala/build/tests/TestInputs.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ final case class TestInputs(
4444
tmpDir,
4545
forcedWorkspace = forcedWorkspaceOpt.map(_.resolveFrom(tmpDir)),
4646
allowRestrictedFeatures = true,
47-
extraClasspathWasPassed = false
47+
extraClasspathWasPassed = false,
48+
isRunWithShebang = false
4849
)
4950
res match {
5051
case Left(err) => throw new Exception(err)

modules/cli/src/main/scala/scala/cli/commands/bsp/Bsp.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ object Bsp extends ScalaCommand[BspOptions] {
5959
() => buildOptions0.javaHome().value.javaCommand
6060
),
6161
persistentLogger,
62+
buildOptions0.scriptOptions.runWithShebang,
6263
buildOptions0.suppressWarningOptions.suppressDirectivesInMultipleFilesWarning
6364
).map(_._2).getOrElse(initialInputs)
6465

modules/cli/src/main/scala/scala/cli/commands/clean/Clean.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ object Clean extends ScalaCommand[CleanOptions] {
2121
defaultInputs = () => Inputs.default(),
2222
forcedWorkspace = options.workspace.forcedWorkspaceOpt,
2323
allowRestrictedFeatures = ScalaCli.allowRestrictedFeatures,
24-
extraClasspathWasPassed = false
24+
extraClasspathWasPassed = false,
25+
isRunWithShebang = false
2526
) match {
2627
case Left(message) =>
2728
System.err.println(message)

modules/cli/src/main/scala/scala/cli/commands/dependencyupdate/DependencyUpdate.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ object DependencyUpdate extends ScalaCommand[DependencyUpdateOptions] {
3636
() => buildOptions.javaHome().value.javaCommand
3737
),
3838
logger,
39+
buildOptions.scriptOptions.runWithShebang,
3940
buildOptions.suppressWarningOptions.suppressDirectivesInMultipleFilesWarning
4041
).orExit(logger)
4142

modules/cli/src/main/scala/scala/cli/commands/export/Export.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ object Export extends ScalaCommand[ExportOptions] {
3838
() => buildOptions.javaHome().value.javaCommand
3939
),
4040
logger,
41+
buildOptions.scriptOptions.runWithShebang,
4142
buildOptions.suppressWarningOptions.suppressDirectivesInMultipleFilesWarning
4243
)
4344
}

0 commit comments

Comments
 (0)