Skip to content

Commit 1ef9a26

Browse files
Merge pull request #580 from alexarchambault/sip-cli
Hide some commands when called as 'scala'
2 parents 9d78e40 + f8676a3 commit 1ef9a26

File tree

22 files changed

+251
-153
lines changed

22 files changed

+251
-153
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ out/
33
.metals/
44
.vscode/
55
.scala
6+
.scala-build/
67
.bsp
78
.idea/
89

Lines changed: 22 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,27 @@
11
package scala.cli
22

3-
import caseapp.core.app.CommandsEntryPoint
4-
import caseapp.core.help.{Help, RuntimeCommandsHelp}
53
import sun.misc.{Signal, SignalHandler}
64

7-
import java.io.{ByteArrayOutputStream, PrintStream}
5+
import java.io.{ByteArrayOutputStream, File, PrintStream}
86
import java.nio.charset.StandardCharsets
9-
import java.nio.file.InvalidPathException
107

118
import scala.build.internal.Constants
12-
import scala.cli.commands._
139
import scala.cli.internal.Argv0
1410
import scala.cli.launcher.{LauncherCli, LauncherOptions}
1511
import scala.util.Properties
1612

17-
object ScalaCli extends CommandsEntryPoint {
18-
19-
def actualDefaultCommand = Default
20-
21-
val commands: Seq[ScalaCommand[_]] = Seq(
22-
About,
23-
AddPath,
24-
BloopExit,
25-
BloopStart,
26-
Bsp,
27-
Clean,
28-
Compile,
29-
Directories,
30-
Export,
31-
Fmt,
32-
HelpCmd,
33-
InstallCompletions,
34-
InstallHome,
35-
Metabrowse,
36-
Repl,
37-
Package,
38-
Run,
39-
SetupIde,
40-
Shebang,
41-
Test,
42-
Update,
43-
Version
44-
)
45-
46-
lazy val progName = (new Argv0).get("scala-cli")
47-
override def description =
48-
"Scala CLI is a command-line tool to interact with the Scala language. It lets you compile, run, test, and package your Scala code."
49-
override def summaryDesc =
50-
"""|See 'scala-cli <command> --help' to read about a specific subcommand. To see full help run 'scala-cli <command> --help-full'.
51-
|To run another Scala CLI version, specify it with '--cli-version' before any other argument, like 'scala-cli --cli-version <version> args'.""".stripMargin
52-
final override def defaultCommand = Some(actualDefaultCommand)
53-
54-
// FIXME Report this in case-app default NameFormatter
55-
override lazy val help: RuntimeCommandsHelp = {
56-
val parent = super.help
57-
parent.withDefaultHelp(Help[Unit]())
58-
}
13+
object ScalaCli {
5914

60-
override def enableCompleteCommand = true
61-
override def enableCompletionsCommand = true
15+
val progName = (new Argv0).get("scala-cli")
6216

63-
override def helpFormat = actualDefaultCommand.helpFormat
17+
private var isSipScala =
18+
progName == "scala" ||
19+
progName.endsWith("/scala") ||
20+
progName.endsWith(File.separator + "scala")
6421

6522
private def isGraalvmNativeImage: Boolean =
6623
sys.props.contains("org.graalvm.nativeimage.imagecode")
6724

68-
private def isShebangFile(arg: String): Boolean = {
69-
val pathOpt =
70-
try Some(os.Path(arg, os.pwd))
71-
catch {
72-
case _: InvalidPathException => None
73-
}
74-
pathOpt.filter(os.isFile(_)).filter(_.toIO.canRead).exists { path =>
75-
val content = os.read(path) // FIXME Charset?
76-
content.startsWith(s"#!/usr/bin/env $progName" + System.lineSeparator())
77-
}
78-
}
79-
8025
private def partitionArgs(args: Array[String]): (Array[String], Array[String]) = {
8126
val systemProps = args.takeWhile(_.startsWith("-D"))
8227
(systemProps, args.drop(systemProps.size))
@@ -122,7 +67,7 @@ object ScalaCli extends CommandsEntryPoint {
12267
.takeWhile(_.isDigit)
12368
.toInt
12469

125-
override def main(args: Array[String]): Unit = {
70+
def main(args: Array[String]): Unit = {
12671
try main0(args)
12772
catch {
12873
case e: Throwable if !isCI =>
@@ -146,9 +91,9 @@ object ScalaCli extends CommandsEntryPoint {
14691

14792
e match {
14893
case _: NoClassDefFoundError
149-
if isJava17ClassName(
150-
e.getMessage
151-
) && CurrentParams.verbosity <= 1 && javaMajorVersion < 16 =>
94+
if isJava17ClassName(e.getMessage) &&
95+
CurrentParams.verbosity <= 1 &&
96+
javaMajorVersion < 16 =>
15297
// Actually Java >= 16, but let's recommend a LTS version…
15398
System.err.println(
15499
s"Java >= 17 is required to run Scala CLI (found Java $javaMajorVersion)"
@@ -169,8 +114,15 @@ object ScalaCli extends CommandsEntryPoint {
169114
case Right((launcherOpts, args0)) =>
170115
launcherOpts.cliVersion.map(_.trim).filter(_.nonEmpty) match {
171116
case Some(ver) =>
172-
LauncherCli.runAndExit(ver, launcherOpts, args0)
173-
case None => args0.toArray
117+
val powerArgs =
118+
if (launcherOpts.power) Seq("--power")
119+
else Nil
120+
val newArgs = powerArgs ++ args0
121+
LauncherCli.runAndExit(ver, launcherOpts, newArgs)
122+
case None =>
123+
if (launcherOpts.power)
124+
isSipScala = false
125+
args0.toArray
174126
}
175127
}
176128
val (systemProps, scalaCliArgs) = partitionArgs(remainingArgs)
@@ -192,19 +144,7 @@ object ScalaCli extends CommandsEntryPoint {
192144
// Enable ANSI output in Windows terminal
193145
coursier.jniutils.WindowsAnsiTerminal.enableAnsiOutput()
194146

195-
// quick hack, until the raw args are kept in caseapp.RemainingArgs by case-app
196-
actualDefaultCommand.anyArgs = scalaCliArgs.nonEmpty
197-
198-
commands.foreach {
199-
case c: NeedsArgvCommand => c.setArgv(progName +: scalaCliArgs)
200-
case _ =>
201-
}
202-
203-
val processedArgs =
204-
if (scalaCliArgs.lengthCompare(1) > 0 && isShebangFile(scalaCliArgs(0)))
205-
Array(scalaCliArgs(0), "--") ++ scalaCliArgs.tail
206-
else
207-
scalaCliArgs
208-
super.main(processedArgs)
147+
new ScalaCliCommands(progName, isSipScala)
148+
.main(scalaCliArgs)
209149
}
210150
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package scala.cli
2+
3+
import caseapp.core.app.CommandsEntryPoint
4+
import caseapp.core.help.{Help, RuntimeCommandsHelp}
5+
6+
import java.nio.file.InvalidPathException
7+
8+
import scala.cli.commands._
9+
10+
class ScalaCliCommands(
11+
val progName: String,
12+
isSipScala: Boolean
13+
) extends CommandsEntryPoint {
14+
15+
lazy val actualDefaultCommand = new Default(help)
16+
17+
private def allCommands = Seq[ScalaCommand[_]](
18+
new About(isSipScala = isSipScala),
19+
AddPath,
20+
BloopExit,
21+
BloopStart,
22+
Bsp,
23+
Clean,
24+
Compile,
25+
Directories,
26+
Export,
27+
Fmt,
28+
new HelpCmd(help),
29+
InstallCompletions,
30+
InstallHome,
31+
Metabrowse,
32+
Repl,
33+
Package,
34+
Run,
35+
SetupIde,
36+
Shebang,
37+
Test,
38+
Update,
39+
Version
40+
)
41+
42+
def commands = allCommands.filter(c => !isSipScala || c.inSipScala)
43+
44+
override def description =
45+
"Scala CLI is a command-line tool to interact with the Scala language. It lets you compile, run, test, and package your Scala code."
46+
override def summaryDesc =
47+
"""|See 'scala-cli <command> --help' to read about a specific subcommand. To see full help run 'scala-cli <command> --help-full'.
48+
|To run another Scala CLI version, specify it with '--cli-version' before any other argument, like 'scala-cli --cli-version <version> args'.""".stripMargin
49+
final override def defaultCommand = Some(actualDefaultCommand)
50+
51+
// FIXME Report this in case-app default NameFormatter
52+
override lazy val help: RuntimeCommandsHelp = {
53+
val parent = super.help
54+
parent.withDefaultHelp(Help[Unit]())
55+
}
56+
57+
override def enableCompleteCommand = true
58+
override def enableCompletionsCommand = true
59+
60+
override def helpFormat = ScalaCliHelp.helpFormat
61+
62+
private def isShebangFile(arg: String): Boolean = {
63+
val pathOpt =
64+
try Some(os.Path(arg, os.pwd))
65+
catch {
66+
case _: InvalidPathException => None
67+
}
68+
pathOpt.filter(os.isFile(_)).filter(_.toIO.canRead).exists { path =>
69+
val content = os.read(path) // FIXME Charset?
70+
content.startsWith(s"#!/usr/bin/env $progName" + System.lineSeparator())
71+
}
72+
}
73+
74+
override def main(args: Array[String]): Unit = {
75+
76+
// quick hack, until the raw args are kept in caseapp.RemainingArgs by case-app
77+
actualDefaultCommand.anyArgs = args.nonEmpty
78+
79+
commands.foreach {
80+
case c: NeedsArgvCommand => c.setArgv(progName +: args)
81+
case _ =>
82+
}
83+
84+
val processedArgs =
85+
if (args.lengthCompare(1) > 0 && isShebangFile(args(0)))
86+
Array(args(0), "--") ++ args.tail
87+
else
88+
args
89+
super.main(processedArgs)
90+
}
91+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package scala.cli
2+
3+
import caseapp.core.help.HelpFormat
4+
5+
object ScalaCliHelp {
6+
val helpFormat = HelpFormat.default()
7+
}

modules/cli/src/main/scala/scala/cli/commands/About.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@ import caseapp._
55
import scala.build.internal.Constants
66
import scala.cli.CurrentParams
77

8-
object About extends ScalaCommand[AboutOptions] {
8+
class About(isSipScala: Boolean) extends ScalaCommand[AboutOptions] {
99
override def group = "Miscellaneous"
1010
def run(options: AboutOptions, args: RemainingArgs): Unit = {
1111
CurrentParams.verbosity = options.verbosity.verbosity
1212
val version = Constants.version
1313
val detailedVersionOpt = Some(Constants.detailedVersion).filter(_ != version)
14-
println(s"Scala CLI version $version" + detailedVersionOpt.fold("")(" (" + _ + ")"))
14+
val appName =
15+
if (isSipScala) "Scala command"
16+
else "Scala CLI"
17+
println(s"$appName version $version" + detailedVersionOpt.fold("")(" (" + _ + ")"))
1518
}
1619
}

modules/cli/src/main/scala/scala/cli/commands/AddPath.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ import scala.cli.CurrentParams
99
import scala.util.Properties
1010

1111
object AddPath extends ScalaCommand[AddPathOptions] {
12-
override def hidden = true
12+
override def hidden = true
13+
override def inSipScala = false
1314
def run(options: AddPathOptions, args: RemainingArgs): Unit = {
1415
CurrentParams.verbosity = options.verbosity
1516

modules/cli/src/main/scala/scala/cli/commands/BloopExit.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import scala.build.blooprifle.BloopRifle
77
import scala.cli.CurrentParams
88

99
object BloopExit extends ScalaCommand[BloopExitOptions] {
10-
override def hidden = true
10+
override def hidden = true
11+
override def inSipScala = false
1112
override def names: List[List[String]] = List(
1213
List("bloop", "exit")
1314
)

modules/cli/src/main/scala/scala/cli/commands/BloopStart.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import scala.concurrent.Await
1111
import scala.concurrent.duration.Duration
1212

1313
object BloopStart extends ScalaCommand[BloopStartOptions] {
14-
override def hidden = true
14+
override def hidden = true
15+
override def inSipScala = false
1516
override def names: List[List[String]] = List(
1617
List("bloop", "start")
1718
)
Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,43 @@
11
package scala.cli.commands
22

3-
import scala.cli.ScalaCli
4-
5-
// FIXME scalafmt formats that really weirdly, need to investigate why
6-
// format: off
7-
object Default extends DefaultBase(
8-
ScalaCli.help.help(ScalaCli.helpFormat),
9-
ScalaCli.help.help(ScalaCli.helpFormat, showHidden = true)
10-
)
11-
// format: on
3+
import caseapp._
4+
import caseapp.core.help.RuntimeCommandsHelp
5+
import caseapp.core.{Error, RemainingArgs}
6+
7+
import scala.build.internal.Constants
8+
import scala.cli.{CurrentParams, ScalaCliHelp}
9+
10+
class Default(
11+
actualHelp: => RuntimeCommandsHelp
12+
) extends ScalaCommand[DefaultOptions] {
13+
14+
private def defaultHelp: String = actualHelp.help(ScalaCliHelp.helpFormat)
15+
private def defaultFullHelp: String = actualHelp.help(ScalaCliHelp.helpFormat, showHidden = true)
16+
17+
override protected def commandLength = 0
18+
19+
override def group = "Main"
20+
override def sharedOptions(options: DefaultOptions) =
21+
Some[scala.cli.commands.SharedOptions](options.runOptions.shared)
22+
private[cli] var anyArgs = false
23+
override def helpAsked(progName: String, maybeOptions: Either[Error, DefaultOptions]): Nothing = {
24+
println(defaultHelp)
25+
sys.exit(0)
26+
}
27+
override def fullHelpAsked(progName: String): Nothing = {
28+
println(defaultFullHelp)
29+
sys.exit(0)
30+
}
31+
def run(options: DefaultOptions, args: RemainingArgs): Unit = {
32+
CurrentParams.verbosity = options.runOptions.shared.logging.verbosity
33+
if (options.version)
34+
println(Constants.version)
35+
else if (anyArgs)
36+
Run.run(
37+
options.runOptions,
38+
args
39+
)
40+
else
41+
helpAsked(finalHelp.progName, Right(options))
42+
}
43+
}

0 commit comments

Comments
 (0)