Skip to content

Commit 2d1792d

Browse files
Merge pull request #130 from alexarchambault/export-command
Add export command
2 parents 12f1a52 + 08e21fe commit 2d1792d

File tree

18 files changed

+696
-37
lines changed

18 files changed

+696
-37
lines changed

modules/build/src/main/scala/scala/build/preprocessing/ScalaPreprocessor.scala

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,14 @@ import java.util.Locale
88

99
import scala.build.{Inputs, Os, Sources}
1010
import scala.build.internal.AmmUtil
11-
import scala.build.options.{BuildOptions, BuildRequirements, ClassPathOptions, ScalaOptions}
11+
import scala.build.options.{
12+
BuildOptions,
13+
BuildRequirements,
14+
ClassPathOptions,
15+
ScalaJsOptions,
16+
ScalaNativeOptions,
17+
ScalaOptions
18+
}
1219
import scala.collection.JavaConverters._
1320

1421
case object ScalaPreprocessor extends Preprocessor {
@@ -105,8 +112,33 @@ case object ScalaPreprocessor extends Preprocessor {
105112
scalaVersion = Some(scalaVer)
106113
)
107114
)
108-
case _ =>
109-
sys.error(s"Unrecognized using directive: ${dir.values.mkString(" ")}")
115+
case Seq("repository", repo) if repo.nonEmpty =>
116+
BuildOptions(
117+
classPathOptions = ClassPathOptions(
118+
extraRepositories = Seq(repo)
119+
)
120+
)
121+
case other =>
122+
val maybeOptions =
123+
// TODO Accept several platforms for cross-compilation
124+
if (other.lengthCompare(1) == 0)
125+
isPlatform(normalizePlatform(other.head)).map {
126+
case BuildRequirements.Platform.JVM =>
127+
BuildOptions()
128+
case BuildRequirements.Platform.JS =>
129+
BuildOptions(
130+
scalaJsOptions = ScalaJsOptions(enable = true)
131+
)
132+
case BuildRequirements.Platform.Native =>
133+
BuildOptions(
134+
scalaNativeOptions = ScalaNativeOptions(enable = true)
135+
)
136+
}
137+
else
138+
None
139+
maybeOptions.getOrElse {
140+
sys.error(s"Unrecognized using directive: ${other.mkString(" ")}")
141+
}
110142
}
111143
}
112144
.foldLeft(BuildOptions())(_ orElse _)

modules/cli/src/main/scala/scala/cli/ScalaCli.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ object ScalaCli extends CommandsEntryPoint {
2020
Clean,
2121
Compile,
2222
Directories,
23+
Export,
2324
InstallCompletions,
2425
Metabrowse,
2526
Repl,
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package scala.cli.commands
2+
3+
import caseapp._
4+
5+
import scala.build.{BloopBuildClient, Build, CrossSources, Inputs, Logger, Sources}
6+
import scala.build.internal.CustomCodeWrapper
7+
import scala.build.options.BuildOptions
8+
import scala.build.GeneratedSource
9+
10+
import scala.cli.export._
11+
12+
object Export extends ScalaCommand[ExportOptions] {
13+
14+
private def prepareBuild(
15+
inputs: Inputs,
16+
buildOptions: BuildOptions,
17+
logger: Logger,
18+
verbosity: Int
19+
): (Sources, BuildOptions) = {
20+
21+
logger.log("Preparing build")
22+
23+
val crossSources = CrossSources.forInputs(
24+
inputs,
25+
Sources.defaultPreprocessors(
26+
buildOptions.scriptOptions.codeWrapper.getOrElse(CustomCodeWrapper)
27+
)
28+
)
29+
val sources = crossSources.sources(buildOptions)
30+
31+
if (verbosity >= 3)
32+
pprint.better.log(sources)
33+
34+
val options0 = buildOptions.orElse(sources.buildOptions)
35+
36+
(sources, options0)
37+
}
38+
39+
def sbtBuildTool = Sbt("1.5.5")
40+
def defaultBuildTool = sbtBuildTool
41+
42+
def run(options: ExportOptions, args: RemainingArgs): Unit = {
43+
44+
val logger = options.shared.logger
45+
val inputs = options.shared.inputsOrExit(args)
46+
val baseOptions = options.buildOptions
47+
48+
val (sources, options0) =
49+
prepareBuild(inputs, baseOptions, logger, options.shared.logging.verbosity)
50+
51+
val buildTool =
52+
if (options.sbt.getOrElse(true))
53+
sbtBuildTool
54+
else
55+
defaultBuildTool
56+
57+
val project = buildTool.export(options0, sources)
58+
59+
val output = options.output.getOrElse("dest")
60+
val dest = os.Path(output, os.pwd)
61+
if (os.exists(dest)) {
62+
System.err.println(s"Error: $output already exists.")
63+
sys.exit(1)
64+
}
65+
66+
os.makeDir.all(dest)
67+
project.writeTo(dest)
68+
}
69+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package scala.cli.commands
2+
3+
import caseapp._
4+
5+
import scala.build.options.BuildOptions
6+
7+
// format: off
8+
final case class ExportOptions(
9+
// FIXME There might be too many options for 'scala-cli export' there
10+
@Recurse
11+
shared: SharedOptions = SharedOptions(),
12+
@Recurse
13+
mainClass: MainClassOptions = MainClassOptions(),
14+
15+
sbt: Option[Boolean] = None,
16+
17+
@Name("o")
18+
output: Option[String] = None
19+
) {
20+
// format: on
21+
22+
def buildOptions: BuildOptions = {
23+
val baseOptions = shared.buildOptions(enableJmh = false, None, ignoreErrors = false)
24+
baseOptions.copy(
25+
mainClass = mainClass.mainClass.filter(_.nonEmpty)
26+
)
27+
}
28+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package scala.cli.commands
2+
3+
import caseapp._
4+
5+
// format: off
6+
final case class MainClassOptions(
7+
@Group("Entrypoint")
8+
@HelpMessage("Specify which main class to run")
9+
@ValueDescription("main-class")
10+
@Name("M")
11+
mainClass: Option[String] = None
12+
)
13+
// format: on
14+
15+
object MainClassOptions {
16+
implicit val parser = Parser[MainClassOptions]
17+
implicit val help = Help[MainClassOptions]
18+
}

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ final case class PackageOptions(
1515
watch: SharedWatchOptions = SharedWatchOptions(),
1616
@Recurse
1717
compileCross: CompileCrossOptions = CompileCrossOptions(),
18+
@Recurse
19+
mainClass: MainClassOptions = MainClassOptions(),
1820

1921
@Group("Package")
2022
@HelpMessage("Set destination path")
@@ -32,11 +34,6 @@ final case class PackageOptions(
3234
@HelpMessage("Generate an assembly JAR")
3335
assembly: Boolean = false,
3436

35-
@Group("Package")
36-
@HelpMessage("Specify which main class to run")
37-
@ValueDescription("main-class")
38-
@Name("M")
39-
mainClass: Option[String] = None,
4037
@Recurse
4138
packager: PackagerOptions = PackagerOptions(),
4239
@Group("Package")
@@ -72,7 +69,7 @@ final case class PackageOptions(
7269
def buildOptions: BuildOptions = {
7370
val baseOptions = shared.buildOptions(enableJmh = false, jmhVersion = None)
7471
baseOptions.copy(
75-
mainClass = mainClass.filter(_.nonEmpty),
72+
mainClass = mainClass.mainClass.filter(_.nonEmpty),
7673
packageOptions = baseOptions.packageOptions.copy(
7774
version = Some(packager.version),
7875
launcherApp = packager.launcherApp,

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

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,8 @@ final case class RunOptions(
2222
watch: SharedWatchOptions = SharedWatchOptions(),
2323
@Recurse
2424
compileCross: CompileCrossOptions = CompileCrossOptions(),
25-
26-
@Group("Runner")
27-
@HelpMessage("Specify which main class to run")
28-
@ValueDescription("main-class")
29-
@Name("M")
30-
mainClass: Option[String] = None
25+
@Recurse
26+
mainClass: MainClassOptions = MainClassOptions()
3127
) {
3228
// format: on
3329

@@ -37,7 +33,7 @@ final case class RunOptions(
3733
jmhVersion = benchmarking.jmhVersion
3834
)
3935
baseOptions.copy(
40-
mainClass = mainClass,
36+
mainClass = mainClass.mainClass,
4137
javaOptions = baseOptions.javaOptions.copy(
4238
javaOpts = baseOptions.javaOptions.javaOpts ++ sharedJava.allJavaOpts
4339
)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package scala.cli.export
2+
3+
import scala.build.options.BuildOptions
4+
import scala.build.Sources
5+
6+
sealed abstract class BuildTool extends Product with Serializable {
7+
def export(options: BuildOptions, sources: Sources): Project
8+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package scala.cli.export
2+
3+
abstract class Project extends Product with Serializable {
4+
def writeTo(dir: os.Path): Unit
5+
}

0 commit comments

Comments
 (0)