Skip to content

Commit c05230d

Browse files
authored
Scala CLI wrapper command (#149)
* Update dependencies and Scala to 3.3.1 * Some fixes for Scala 3.3
1 parent faa27ec commit c05230d

File tree

7 files changed

+204
-32
lines changed

7 files changed

+204
-32
lines changed

README.md

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
- [`bootstrap`](#bootstrap)
1616
- [`install`](#install)
1717
- [`clang` and `clang++`](#clang-and-clang)
18+
- [`scala-cli`](#scala-cli)
1819
- [Docker base image](#docker-base-image)
1920
- [Core](#core)
2021
- [VcpkgRootInit](#vcpkgrootinit)
@@ -76,7 +77,7 @@ There are several modules of interest:
7677
You can quickly test it by running:
7778

7879
```
79-
$ cs launch com.indoorvivants.vcpkg:sn-vcpkg_3:0.0.13 -- install libpq -l -q -c
80+
$ cs launch com.indoorvivants.vcpkg:sn-vcpkg_3:0.0.16 -- install libpq -l -q -c
8081
-I<...>/sbt-vcpkg/vcpkg-install/arm64-osx/lib/pkgconfig/../../include
8182
-L<...>/sbt-vcpkg/vcpkg-install/arm64-osx/lib/pkgconfig/../../lib
8283
-L<...>/sbt-vcpkg/vcpkg-install/arm64-osx/lib/pkgconfig/../../lib/pkgconfig/../../lib
@@ -102,7 +103,7 @@ There are several modules of interest:
102103
For SBT, add this to your `project/plugins.sbt`:
103104

104105
```scala
105-
addSbtPlugin("com.indoorvivants.vcpkg" % "sbt-vcpkg" % "0.0.13")
106+
addSbtPlugin("com.indoorvivants.vcpkg" % "sbt-vcpkg" % "0.0.16")
106107
```
107108

108109
And in your build.sbt:
@@ -138,7 +139,7 @@ Tasks and settings (find them all by doing `help vcpkg*` in SBT shell):
138139
Add dependency to your `build.sc`:
139140

140141
```scala
141-
import $ivy.`com.indoorvivants.vcpkg::mill-vcpkg:0.0.13`
142+
import $ivy.`com.indoorvivants.vcpkg::mill-vcpkg:0.0.16`
142143
```
143144

144145
And use the `VcpkgModule` mixin:
@@ -149,7 +150,7 @@ import com.indoorvivants.vcpkg._
149150

150151
import mill._, mill.scalalib._
151152
object example extends ScalaModule with VcpkgModule {
152-
def scalaVersion = "3.2.2"
153+
def scalaVersion = "3.3.1"
153154
def vcpkgDependencies = T(VcpkgDependencies("cmark", "cjson"))
154155
}
155156
```
@@ -165,7 +166,7 @@ The Mill tasks are the same as in the SBT plugin
165166
In `project/plugins.sbt`:
166167

167168
```scala
168-
addSbtPlugin("com.indoorvivants.vcpkg" % "sbt-vcpkg-native" % "0.0.13")
169+
addSbtPlugin("com.indoorvivants.vcpkg" % "sbt-vcpkg-native" % "0.0.16")
169170
```
170171

171172
In `build.sbt`:
@@ -195,7 +196,7 @@ For real world usage, see [Examples](#examples).
195196
Add dependency to your `build.sc`:
196197

197198
```scala
198-
import $ivy.`com.indoorvivants.vcpkg::mill-vcpkg-native:0.0.13`
199+
import $ivy.`com.indoorvivants.vcpkg::mill-vcpkg-native:0.0.16`
199200
```
200201

201202
And use the `VcpkgNativeModule` mixin:
@@ -357,6 +358,59 @@ sn-vcpkg clang --manifest vcpkg.json -- test-sqlite.c
357358

358359
All the arguments after `--` will be passed to clang/clang++ without modification (_before_ the flags calculated for dependencies)
359360

361+
#### `scala-cli`
362+
363+
This command invokes your local installation of Scala CLI (`scala-cli` must be available on PATH),
364+
and passes all the flags required by the specified dependencies [^1].
365+
366+
For example, say you have a Scala CLI script using [Porcupine](https://github.com/armanbilge/porcupine), a cross-platform functional library for Sqlite3:
367+
368+
**scala-cli-sqlite3.scala**
369+
```scala
370+
371+
//> using dep "com.armanbilge::porcupine::0.0.1"
372+
//> using platform scala-native
373+
//> using scala 3.3.1
374+
375+
import porcupine.*
376+
import cats.effect.IOApp
377+
import cats.effect.IO
378+
import cats.syntax.all.*
379+
import scodec.bits.ByteVector
380+
381+
import Codec.*
382+
383+
object Test extends IOApp.Simple:
384+
val run =
385+
Database
386+
.open[IO](":memory:")
387+
.use: db =>
388+
db.execute(sql"create table porcupine (n, i, r, t, b);".command) *>
389+
db.execute(
390+
sql"insert into porcupine values(${`null`}, $integer, $real, $text, $blob);".command,
391+
(None, 42L, 3.14, "quill-pig", ByteVector(0, 1, 2, 3))
392+
) *>
393+
db.unique(
394+
sql"select b, t, r, i, n from porcupine;"
395+
.query(blob *: text *: real *: integer *: `null` *: nil)
396+
).flatTap(IO.println)
397+
.void
398+
end Test
399+
```
400+
401+
To run it with Scala Native, you must have `sqlite3` native
402+
dependency installed and configured, along with correct flags
403+
passed to Scala Native.
404+
405+
You can run the script like this:
406+
407+
```
408+
sn-vcpkg scala-cli sqlite3 -- run scala-cli-sqlite3.scala
409+
```
410+
411+
The sn-vcpkg CLI will add the required `--native-compile/--native-linking` flags to the _end_ of your argument list automatically.
412+
413+
360414
[^1]: as long as the dependencies themselves provide a well configured pkg-config file, of course
361415

362416
### Docker base image

build.sbt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ organization := "com.indoorvivants.vcpkg"
2121
sonatypeProfileName := "com.indoorvivants"
2222

2323
val V = new {
24-
val scala213 = "2.13.11"
24+
val scala213 = "2.13.12"
2525

2626
val scala212 = "2.12.18"
2727

28-
val scala3 = "3.2.2"
28+
val scala3 = "3.3.1"
2929

3030
val dirs = "26"
3131

@@ -43,9 +43,9 @@ val V = new {
4343

4444
val decline = "2.4.1"
4545

46-
val scribe = "3.11.5"
46+
val scribe = "3.12.2"
4747

48-
val scalaNative = "0.4.15"
48+
val scalaNative = "0.4.16"
4949

5050
val circe = "0.14.6"
5151

@@ -76,7 +76,7 @@ lazy val docs =
7676
.dependsOn(core.jvm(V.scala3), cli.jvm(V.scala3))
7777
.settings(scalaVersion := V.scala3)
7878
.settings(
79-
mdocVariables := Map("VERSION" -> "0.0.13", "SCALA3_VERSION" -> V.scala3)
79+
mdocVariables := Map("VERSION" -> "0.0.16", "SCALA3_VERSION" -> V.scala3)
8080
)
8181

8282
lazy val docsDrifted = taskKey[Boolean]("")

docs/README.in.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
- [`bootstrap`](#bootstrap)
1616
- [`install`](#install)
1717
- [`clang` and `clang++`](#clang-and-clang)
18+
- [`scala-cli`](#scala-cli)
1819
- [Docker base image](#docker-base-image)
1920
- [Core](#core)
2021
- [VcpkgRootInit](#vcpkgrootinit)
@@ -317,6 +318,59 @@ sn-vcpkg clang --manifest vcpkg.json -- test-sqlite.c
317318

318319
All the arguments after `--` will be passed to clang/clang++ without modification (_before_ the flags calculated for dependencies)
319320

321+
#### `scala-cli`
322+
323+
This command invokes your local installation of Scala CLI (`scala-cli` must be available on PATH),
324+
and passes all the flags required by the specified dependencies [^1].
325+
326+
For example, say you have a Scala CLI script using [Porcupine](https://github.com/armanbilge/porcupine), a cross-platform functional library for Sqlite3:
327+
328+
**scala-cli-sqlite3.scala**
329+
```scala
330+
331+
//> using dep "com.armanbilge::porcupine::0.0.1"
332+
//> using platform scala-native
333+
//> using scala 3.3.1
334+
335+
import porcupine.*
336+
import cats.effect.IOApp
337+
import cats.effect.IO
338+
import cats.syntax.all.*
339+
import scodec.bits.ByteVector
340+
341+
import Codec.*
342+
343+
object Test extends IOApp.Simple:
344+
val run =
345+
Database
346+
.open[IO](":memory:")
347+
.use: db =>
348+
db.execute(sql"create table porcupine (n, i, r, t, b);".command) *>
349+
db.execute(
350+
sql"insert into porcupine values(${`null`}, $integer, $real, $text, $blob);".command,
351+
(None, 42L, 3.14, "quill-pig", ByteVector(0, 1, 2, 3))
352+
) *>
353+
db.unique(
354+
sql"select b, t, r, i, n from porcupine;"
355+
.query(blob *: text *: real *: integer *: `null` *: nil)
356+
).flatTap(IO.println)
357+
.void
358+
end Test
359+
```
360+
361+
To run it with Scala Native, you must have `sqlite3` native
362+
dependency installed and configured, along with correct flags
363+
passed to Scala Native.
364+
365+
You can run the script like this:
366+
367+
```
368+
sn-vcpkg scala-cli sqlite3 -- run scala-cli-sqlite3.scala
369+
```
370+
371+
The sn-vcpkg CLI will add the required `--native-compile/--native-linking` flags to the _end_ of your argument list automatically.
372+
373+
320374
[^1]: as long as the dependencies themselves provide a well configured pkg-config file, of course
321375

322376
### Docker base image

modules/cli/src/main/scala/CommandLineApp.scala

Lines changed: 76 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,18 @@ enum Compiler:
1414
enum Action:
1515
case Install(dependencies: VcpkgDependencies, out: OutputOptions)
1616
extends Action
17+
1718
case InvokeCompiler(
1819
compiler: Compiler,
1920
dependencies: VcpkgDependencies,
2021
args: Seq[String]
2122
) extends Action
23+
24+
case InvokeScalaCLI(
25+
dependencies: VcpkgDependencies,
26+
args: Seq[String]
27+
) extends Action
28+
2229
case Bootstrap
2330
end Action
2431

@@ -171,6 +178,8 @@ object Options extends VcpkgPluginImpl:
171178
private def compilerCommand(compiler: Compiler) = deps.map(d =>
172179
SuspendedAction(rest => Action.InvokeCompiler(compiler, d, rest))
173180
)
181+
private def scalaCliCommand =
182+
deps.map(d => SuspendedAction(rest => Action.InvokeScalaCLI(d, rest)))
174183

175184
private val clang =
176185
Opts.subcommand(
@@ -188,9 +197,19 @@ object Options extends VcpkgPluginImpl:
188197
)(
189198
(compilerCommand(Compiler.ClangPP), configOpts).tupled
190199
)
200+
private val scalaCli =
201+
Opts.subcommand(
202+
"scala-cli",
203+
"Invoke Scala CLI with correct flags for passed dependencies. Sets --native automatically!" +
204+
"The format of the command is [sn-vcpkg scala-cli <flags> <dependencies> -- <scala-cli arguments>"
205+
)(
206+
(scalaCliCommand, configOpts).tupled
207+
)
191208

192209
val opts =
193-
Command(name, header)(install orElse bootstrap orElse clang orElse clangPP)
210+
Command(name, header)(
211+
install orElse bootstrap orElse clang orElse clangPP orElse scalaCli
212+
)
194213

195214
end Options
196215

@@ -225,6 +244,14 @@ object C:
225244
}
226245
end C
227246

247+
enum NativeFlag:
248+
case Compilation(value: String)
249+
case Linking(value: String)
250+
251+
def get: String = this match
252+
case Compilation(value) => value
253+
case Linking(value) => value
254+
228255
object VcpkgCLI extends VcpkgPluginImpl, VcpkgPluginNativeImpl:
229256
import Options.*
230257

@@ -263,30 +290,31 @@ object VcpkgCLI extends VcpkgPluginImpl, VcpkgPluginNativeImpl:
263290
def summary(mp: Seq[Dependency]) =
264291
mp.map(_.name).toList.sorted.mkString(", ")
265292

266-
def printOutput(
293+
def computeNativeFlags(
267294
opt: OutputOptions,
268295
deps: List[Dependency],
269296
info: Map[Dependency, FilesInfo]
270-
) =
271-
val flags = List.newBuilder[String]
297+
): List[NativeFlag] =
298+
val flags = List.newBuilder[NativeFlag]
272299

273300
if opt.compile then
274301
flags ++= compilationFlags(
275302
configurator(info),
276303
deps.map(_.short),
277304
logger,
278305
VcpkgNativeConfig()
279-
)
306+
).map(NativeFlag.Compilation(_))
307+
280308
if opt.linking then
281309
flags ++= linkingFlags(
282310
configurator(info),
283311
deps.map(_.short),
284312
logger,
285313
VcpkgNativeConfig()
286-
)
314+
).map(NativeFlag.Linking(_))
287315

288316
flags.result().distinct
289-
end printOutput
317+
end computeNativeFlags
290318

291319
action match
292320
case Action.Bootstrap =>
@@ -306,7 +334,7 @@ object VcpkgCLI extends VcpkgPluginImpl, VcpkgPluginNativeImpl:
306334
decode[VcpkgManifestFile](str).fold(throw _, identity)
307335
)
308336

309-
printOutput(
337+
computeNativeFlags(
310338
output,
311339
allDeps,
312340
result.filter((k, v) => allDeps.contains(k))
@@ -328,19 +356,55 @@ object VcpkgCLI extends VcpkgPluginImpl, VcpkgPluginNativeImpl:
328356

329357
compilerArgs.addAll(rest)
330358

331-
printOutput(
359+
computeNativeFlags(
332360
OutputOptions(compile = true, linking = true),
333361
allDeps,
334362
result.filter((k, v) => allDeps.contains(k))
335-
).foreach(compilerArgs.addOne)
363+
).map(_.get).foreach(compilerArgs.addOne)
336364

337-
338-
scribe.debug(s"Invoking ${compiler} with arguments: [${compilerArgs.result().mkString(" ")}]")
365+
scribe.debug(
366+
s"Invoking ${compiler} with arguments: [${compilerArgs.result().mkString(" ")}]"
367+
)
339368

340369
val exitCode = scala.sys.process.Process(compilerArgs.result()).!
341370

342371
sys.exit(exitCode)
343372

373+
case Action.InvokeScalaCLI(deps, rest) =>
374+
val result = vcpkgInstallImpl(deps, manager, logger)
375+
import io.circe.parser.decode
376+
import C.given
377+
val allDeps =
378+
deps.dependencies(str =>
379+
decode[VcpkgManifestFile](str).fold(throw _, identity)
380+
)
381+
val scalaCliArgs = List.newBuilder[String]
382+
383+
scalaCliArgs.addOne("scala-cli")
384+
scalaCliArgs.addAll(rest)
385+
386+
computeNativeFlags(
387+
OutputOptions(compile = true, linking = true),
388+
allDeps,
389+
result.filter((k, v) => allDeps.contains(k))
390+
).foreach {
391+
case NativeFlag.Compilation(value) =>
392+
scalaCliArgs.addOne("--native-compile")
393+
scalaCliArgs.addOne(value)
394+
395+
case NativeFlag.Linking(value) =>
396+
scalaCliArgs.addOne("--native-linking")
397+
scalaCliArgs.addOne(value)
398+
}
399+
400+
scribe.debug(
401+
s"Invoking Scala CLI with arguments: [${scalaCliArgs.result().mkString(" ")}]"
402+
)
403+
404+
val exitCode = scala.sys.process.Process(scalaCliArgs.result()).!
405+
406+
sys.exit(exitCode)
407+
344408
end match
345409
end match
346410
end main

0 commit comments

Comments
 (0)