Skip to content

Commit a888a1f

Browse files
committed
Move compile --output to SharedOptions and rename it to --compile-output; prevent the -d underlying compiler flag from being set repeatedly and default it to --compile-output
1 parent 75d5093 commit a888a1f

File tree

13 files changed

+91
-40
lines changed

13 files changed

+91
-40
lines changed

modules/cli-options/src/main/scala/scala/cli/commands/CompileOptions.scala

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,6 @@ final case class CompileOptions(
1818
@HelpMessage("Print the resulting class path")
1919
printClassPath: Boolean = false,
2020

21-
@Name("output-directory")
22-
@Name("d")
23-
@Name("destination")
24-
@HelpMessage("Copy compilation results to output directory using either relative or absolute path")
25-
@ValueDescription("/example/path")
26-
output: Option[String] = None,
27-
2821
@HelpMessage("Compile test scope")
2922
test: Boolean = false
3023
)

modules/cli-options/src/main/scala/scala/cli/commands/ScalacOptions.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ object ScalacOptions {
4141
val ScalacPrintOptions: Set[String] =
4242
scalacOptionsPurePrefixes ++ Set("-help", "-Xshow-phases", "-Vphases")
4343

44+
/** This includes all the scalac options which are redirected to native Scala CLI options. */
45+
val ScalaCliRedirectedOptions = Set(
46+
"-classpath", // redirected to --extra-jars
47+
"-d" // redirected to --compilation-output
48+
)
49+
4450
private val scalacOptionsArgument: Argument[List[String]] =
4551
new Argument[List[String]] {
4652

modules/cli-options/src/main/scala/scala/cli/commands/SharedOptions.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,15 @@ final case class SharedOptions(
117117

118118
@Hidden
119119
strictBloopJsonCheck: Option[Boolean] = None,
120+
121+
@Name("output-directory")
122+
@Name("d")
123+
@Name("destination")
124+
@Name("compileOutput")
125+
@Name("compileOut")
126+
@HelpMessage("Copy compilation results to output directory using either relative or absolute path")
127+
@ValueDescription("/example/path")
128+
compilationOutput: Option[String] = None,
120129
)
121130
// format: on
122131

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,15 @@ import java.io.File
77
import scala.build.options.Scope
88
import scala.build.{Build, BuildThreads, Builds, Os}
99
import scala.cli.CurrentParams
10+
import scala.cli.commands.util.BuildCommandHelpers
1011
import scala.cli.commands.util.CommonOps.SharedDirectoriesOptionsOps
1112
import scala.cli.commands.util.SharedOptionsUtil._
1213
import scala.cli.config.{ConfigDb, Keys}
1314

14-
object Compile extends ScalaCommand[CompileOptions] {
15+
object Compile extends ScalaCommand[CompileOptions] with BuildCommandHelpers {
1516
override def group = "Main"
1617
override def sharedOptions(options: CompileOptions): Option[SharedOptions] = Some(options.shared)
1718

18-
def outputPath(options: CompileOptions): Option[os.Path] =
19-
options.output.filter(_.nonEmpty).map(p => os.Path(p, Os.pwd))
20-
2119
def run(options: CompileOptions, args: RemainingArgs): Unit = {
2220
maybePrintGroupHelp(options)
2321
maybePrintSimpleScalacOutput(options, options.shared.buildOptions())
@@ -71,8 +69,7 @@ object Compile extends ScalaCommand[CompileOptions] {
7169
val cp = s.fullClassPath.map(_.toString).mkString(File.pathSeparator)
7270
println(cp)
7371
}
74-
for (output <- outputPath(options); s <- successulBuildOpt)
75-
os.copy.over(s.output, output)
72+
successulBuildOpt.foreach(_.copyOutput(options.shared))
7673
}
7774
}
7875

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ object Package extends ScalaCommand[PackageOptions] with BuildCommandHelpers {
8585
) { res =>
8686
res.orReport(logger).map(_.main).foreach {
8787
case s: Build.Successful =>
88+
s.copyOutput(options.shared)
8889
val mtimeDestPath = doPackage(
8990
logger = logger,
9091
outputOpt = options.output.filter(_.nonEmpty),
@@ -124,6 +125,7 @@ object Package extends ScalaCommand[PackageOptions] with BuildCommandHelpers {
124125
.orExit(logger)
125126
builds.main match {
126127
case s: Build.Successful =>
128+
s.copyOutput(options.shared)
127129
val res0 = doPackage(
128130
logger = logger,
129131
outputOpt = options.output.filter(_.nonEmpty),

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
223223
)
224224
.orReport(logger)
225225
.flatten
226+
s.copyOutput(options.shared)
226227
if (options.sharedRun.watch.restart)
227228
processOpt = maybeProcess
228229
else
@@ -251,6 +252,7 @@ object Run extends ScalaCommand[RunOptions] with BuildCommandHelpers {
251252
.orExit(logger)
252253
builds.main match {
253254
case s: Build.Successful =>
255+
s.copyOutput(options.shared)
254256
val res = maybeRun(
255257
s,
256258
allowTerminate = true,

modules/cli/src/main/scala/scala/cli/commands/util/BuildCommandHelpers.scala

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package scala.cli.commands.util
22

33
import scala.build.errors.MainClassError
4-
import scala.build.{Build, Logger}
5-
import scala.cli.commands.ScalaCommand
4+
import scala.build.{Build, Logger, Os}
5+
import scala.cli.commands.{ScalaCommand, SharedOptions}
66

77
trait BuildCommandHelpers { self: ScalaCommand[_] =>
8+
import scala.cli.commands.util.ScalacOptionsUtil.*
89
extension (successfulBuild: Build.Successful) {
910
def retainedMainClass(
1011
logger: Logger,
@@ -15,5 +16,15 @@ trait BuildCommandHelpers { self: ScalaCommand[_] =>
1516
self.argvOpt.map(_.mkString(" ")).getOrElse(actualFullCommand),
1617
logger
1718
)
19+
20+
/** -O -d defaults to --compile-output; if both are defined, --compile-output takes precedence
21+
*/
22+
def copyOutput(sharedOptions: SharedOptions): Unit =
23+
sharedOptions.compilationOutput.filter(_.nonEmpty)
24+
.orElse(sharedOptions.scalac.scalacOption.toShadowingSeq.getScalacOption("-d"))
25+
.filter(_.nonEmpty)
26+
.map(os.Path(_, Os.pwd)).foreach(output =>
27+
os.copy.over(successfulBuild.output, output, createFolders = true)
28+
)
1829
}
1930
}

modules/cli/src/main/scala/scala/cli/commands/util/ScalacOptionsUtil.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@ object ScalacOptionsUtil {
77
extension (opts: List[String]) {
88
def toShadowingSeq: ShadowingSeq[ScalacOpt] =
99
ShadowingSeq.from(opts.filter(_.nonEmpty).map(ScalacOpt(_)))
10+
1011
}
1112

1213
extension (opts: ShadowingSeq[ScalacOpt]) {
13-
def getScalacOption(key: String): Option[String] =
14-
opts.get(ScalacOpt(key)).headOption.map(_.value)
15-
1614
def filterScalacOptionKeys(f: String => Boolean): ShadowingSeq[ScalacOpt] =
1715
opts.filterKeys(_.key.exists(f))
16+
def filterNonRedirected: ShadowingSeq[ScalacOpt] =
17+
opts.filterScalacOptionKeys(!ScalacOptions.ScalaCliRedirectedOptions.contains(_))
18+
def getScalacOption(key: String): Option[String] =
19+
opts.get(ScalacOpt(key)).headOption.map(_.value)
1820
}
1921
}

modules/cli/src/main/scala/scala/cli/commands/util/SharedOptionsUtil.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,7 @@ object SharedOptionsUtil extends CommandHelpers {
173173
scalacOptions = scalac
174174
.scalacOption
175175
.toShadowingSeq
176-
// -O -classpath should be redirected as --extra-jars instead
177-
.filterScalacOptionKeys(key => key != "-classpath")
176+
.filterNonRedirected
178177
.map(Positioned.commandLine),
179178
compilerPlugins =
180179
SharedOptionsUtil.parseDependencies(

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ abstract class CompileTestDefinitions(val scalaVersionOpt: Option[String])
9494
test("copy compile output") {
9595
mainAndTestInputs.fromRoot { root =>
9696
val tempOutput = root / "output"
97-
os.proc(TestUtil.cli, "compile", "--output", tempOutput, extraOptions, ".").call(cwd = root)
97+
os.proc(TestUtil.cli, "compile", "--compile-output", tempOutput, extraOptions, ".").call(cwd =
98+
root
99+
)
98100
checkIfCompileOutputIsCopied("Main", tempOutput)
99101
}
100102
}
@@ -107,7 +109,7 @@ abstract class CompileTestDefinitions(val scalaVersionOpt: Option[String])
107109
TestUtil.cli,
108110
"compile",
109111
"--test",
110-
"--output",
112+
"--compile-output",
111113
tempOutput,
112114
"--print-class-path",
113115
extraOptions,

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

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2229,15 +2229,15 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String])
22292229
os.rel / preCompileDir / preCompiledInput -> "case class Message(value: String)",
22302230
os.rel / runDir / mainInput -> s"""object Main extends App { println(Message("$expectedOutput").value) }"""
22312231
).fromRoot { (root: os.Path) =>
2232-
val preCompileOutputDir = "out"
2232+
val preCompileOutputDir = os.rel / "outParentDir" / "out"
22332233

22342234
// first, precompile to an explicitly specified output directory with -d
22352235
os.proc(
22362236
TestUtil.cli,
22372237
"compile",
22382238
preCompiledInput,
22392239
"-d",
2240-
preCompileOutputDir,
2240+
preCompileOutputDir.toString,
22412241
extraOptions
22422242
).call(cwd = root / preCompileDir)
22432243

@@ -2251,9 +2251,36 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String])
22512251
extraOptions
22522252
).call(cwd = root / runDir)
22532253
expect(runRes.out.trim == expectedOutput)
2254+
}
2255+
}
2256+
2257+
test("-O -classpath allows to run with scala-cli compile -O -d option pre-compiled classes") {
2258+
val preCompileDir = "PreCompileDir"
2259+
val preCompiledInput = "Message.scala"
2260+
val runDir = "RunDir"
2261+
val mainInput = "Main.scala"
2262+
val expectedOutput = "Hello"
2263+
TestInputs(
2264+
os.rel / preCompileDir / preCompiledInput -> "case class Message(value: String)",
2265+
os.rel / runDir / mainInput -> s"""object Main extends App { println(Message("$expectedOutput").value) }"""
2266+
).fromRoot { (root: os.Path) =>
2267+
val preCompileOutputDir = os.rel / "outParentDir" / "out"
22542268

2255-
// ensure the same behaviour can be expected when passing -classpath with -O
2256-
val runRes2 = os.proc(
2269+
// first, precompile to an explicitly specified output directory with -O -d
2270+
val compileRes = os.proc(
2271+
TestUtil.cli,
2272+
"compile",
2273+
preCompiledInput,
2274+
"-O",
2275+
"-d",
2276+
"-O",
2277+
preCompileOutputDir.toString,
2278+
extraOptions
2279+
).call(cwd = root / preCompileDir, stderr = os.Pipe)
2280+
expect(!compileRes.err.trim.contains("Warning: Flag -d set repeatedly"))
2281+
2282+
// next, run while relying on the pre-compiled class, specifying the path with -O -classpath
2283+
val runRes = os.proc(
22572284
TestUtil.cli,
22582285
"run",
22592286
mainInput,
@@ -2262,8 +2289,9 @@ abstract class RunTestDefinitions(val scalaVersionOpt: Option[String])
22622289
"-O",
22632290
(os.rel / os.up / preCompileDir / preCompileOutputDir).toString,
22642291
extraOptions
2265-
).call(cwd = root / runDir)
2266-
expect(runRes2.out.trim == expectedOutput)
2292+
).call(cwd = root / runDir, stderr = os.Pipe)
2293+
expect(!runRes.err.trim.contains("Warning: Flag -classpath set repeatedly"))
2294+
expect(runRes.out.trim == expectedOutput)
22672295
}
22682296
}
22692297

website/docs/reference/cli-options.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,6 @@ Aliases: `-p`, `--print-classpath`
119119

120120
Print the resulting class path
121121

122-
### `--output`
123-
124-
Aliases: `--output-directory`, `-d`, `--destination`
125-
126-
Copy compilation results to output directory using either relative or absolute path
127-
128122
### `--test`
129123

130124
Compile test scope
@@ -1286,6 +1280,12 @@ Add dependency for stubs needed to make $ivy and $dep imports to work.
12861280
### `--strict-bloop-json-check`
12871281

12881282
[Internal]
1283+
### `--compilation-output`
1284+
1285+
Aliases: `--output-directory`, `-d`, `--destination`, `--compile-output`, `--compile-out`
1286+
1287+
Copy compilation results to output directory using either relative or absolute path
1288+
12891289
## Snippet options
12901290

12911291
Available in commands:

website/docs/reference/scala-command/cli-options.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,6 @@ Aliases: `-p`, `--print-classpath`
103103

104104
Print the resulting class path
105105

106-
### `--output`
107-
108-
Aliases: `--output-directory`, `-d`, `--destination`
109-
110-
Copy compilation results to output directory using either relative or absolute path
111-
112106
### `--test`
113107

114108
Compile test scope
@@ -696,6 +690,12 @@ Add dependency for stubs needed to make $ivy and $dep imports to work.
696690
### `--strict-bloop-json-check`
697691

698692
[Internal]
693+
### `--compilation-output`
694+
695+
Aliases: `--output-directory`, `-d`, `--destination`, `--compile-output`, `--compile-out`
696+
697+
Copy compilation results to output directory using either relative or absolute path
698+
699699
## Snippet options
700700

701701
Available in commands:

0 commit comments

Comments
 (0)