Skip to content

Commit

Permalink
Scala CLI wrapper command (#149)
Browse files Browse the repository at this point in the history
* Update dependencies and Scala to 3.3.1
* Some fixes for Scala 3.3
  • Loading branch information
keynmol authored Oct 28, 2023
1 parent faa27ec commit c05230d
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 32 deletions.
66 changes: 60 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- [`bootstrap`](#bootstrap)
- [`install`](#install)
- [`clang` and `clang++`](#clang-and-clang)
- [`scala-cli`](#scala-cli)
- [Docker base image](#docker-base-image)
- [Core](#core)
- [VcpkgRootInit](#vcpkgrootinit)
Expand Down Expand Up @@ -76,7 +77,7 @@ There are several modules of interest:
You can quickly test it by running:

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

```scala
addSbtPlugin("com.indoorvivants.vcpkg" % "sbt-vcpkg" % "0.0.13")
addSbtPlugin("com.indoorvivants.vcpkg" % "sbt-vcpkg" % "0.0.16")
```

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

```scala
import $ivy.`com.indoorvivants.vcpkg::mill-vcpkg:0.0.13`
import $ivy.`com.indoorvivants.vcpkg::mill-vcpkg:0.0.16`
```

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

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

```scala
addSbtPlugin("com.indoorvivants.vcpkg" % "sbt-vcpkg-native" % "0.0.13")
addSbtPlugin("com.indoorvivants.vcpkg" % "sbt-vcpkg-native" % "0.0.16")
```

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

```scala
import $ivy.`com.indoorvivants.vcpkg::mill-vcpkg-native:0.0.13`
import $ivy.`com.indoorvivants.vcpkg::mill-vcpkg-native:0.0.16`
```

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

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

#### `scala-cli`

This command invokes your local installation of Scala CLI (`scala-cli` must be available on PATH),
and passes all the flags required by the specified dependencies [^1].

For example, say you have a Scala CLI script using [Porcupine](https://github.com/armanbilge/porcupine), a cross-platform functional library for Sqlite3:

**scala-cli-sqlite3.scala**
```scala

//> using dep "com.armanbilge::porcupine::0.0.1"
//> using platform scala-native
//> using scala 3.3.1

import porcupine.*
import cats.effect.IOApp
import cats.effect.IO
import cats.syntax.all.*
import scodec.bits.ByteVector

import Codec.*

object Test extends IOApp.Simple:
val run =
Database
.open[IO](":memory:")
.use: db =>
db.execute(sql"create table porcupine (n, i, r, t, b);".command) *>
db.execute(
sql"insert into porcupine values(${`null`}, $integer, $real, $text, $blob);".command,
(None, 42L, 3.14, "quill-pig", ByteVector(0, 1, 2, 3))
) *>
db.unique(
sql"select b, t, r, i, n from porcupine;"
.query(blob *: text *: real *: integer *: `null` *: nil)
).flatTap(IO.println)
.void
end Test
```

To run it with Scala Native, you must have `sqlite3` native
dependency installed and configured, along with correct flags
passed to Scala Native.

You can run the script like this:

```
sn-vcpkg scala-cli sqlite3 -- run scala-cli-sqlite3.scala
```

The sn-vcpkg CLI will add the required `--native-compile/--native-linking` flags to the _end_ of your argument list automatically.


[^1]: as long as the dependencies themselves provide a well configured pkg-config file, of course

### Docker base image
Expand Down
10 changes: 5 additions & 5 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ organization := "com.indoorvivants.vcpkg"
sonatypeProfileName := "com.indoorvivants"

val V = new {
val scala213 = "2.13.11"
val scala213 = "2.13.12"

val scala212 = "2.12.18"

val scala3 = "3.2.2"
val scala3 = "3.3.1"

val dirs = "26"

Expand All @@ -43,9 +43,9 @@ val V = new {

val decline = "2.4.1"

val scribe = "3.11.5"
val scribe = "3.12.2"

val scalaNative = "0.4.15"
val scalaNative = "0.4.16"

val circe = "0.14.6"

Expand Down Expand Up @@ -76,7 +76,7 @@ lazy val docs =
.dependsOn(core.jvm(V.scala3), cli.jvm(V.scala3))
.settings(scalaVersion := V.scala3)
.settings(
mdocVariables := Map("VERSION" -> "0.0.13", "SCALA3_VERSION" -> V.scala3)
mdocVariables := Map("VERSION" -> "0.0.16", "SCALA3_VERSION" -> V.scala3)
)

lazy val docsDrifted = taskKey[Boolean]("")
Expand Down
54 changes: 54 additions & 0 deletions docs/README.in.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- [`bootstrap`](#bootstrap)
- [`install`](#install)
- [`clang` and `clang++`](#clang-and-clang)
- [`scala-cli`](#scala-cli)
- [Docker base image](#docker-base-image)
- [Core](#core)
- [VcpkgRootInit](#vcpkgrootinit)
Expand Down Expand Up @@ -317,6 +318,59 @@ sn-vcpkg clang --manifest vcpkg.json -- test-sqlite.c

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

#### `scala-cli`

This command invokes your local installation of Scala CLI (`scala-cli` must be available on PATH),
and passes all the flags required by the specified dependencies [^1].

For example, say you have a Scala CLI script using [Porcupine](https://github.com/armanbilge/porcupine), a cross-platform functional library for Sqlite3:

**scala-cli-sqlite3.scala**
```scala

//> using dep "com.armanbilge::porcupine::0.0.1"
//> using platform scala-native
//> using scala 3.3.1

import porcupine.*
import cats.effect.IOApp
import cats.effect.IO
import cats.syntax.all.*
import scodec.bits.ByteVector

import Codec.*

object Test extends IOApp.Simple:
val run =
Database
.open[IO](":memory:")
.use: db =>
db.execute(sql"create table porcupine (n, i, r, t, b);".command) *>
db.execute(
sql"insert into porcupine values(${`null`}, $integer, $real, $text, $blob);".command,
(None, 42L, 3.14, "quill-pig", ByteVector(0, 1, 2, 3))
) *>
db.unique(
sql"select b, t, r, i, n from porcupine;"
.query(blob *: text *: real *: integer *: `null` *: nil)
).flatTap(IO.println)
.void
end Test
```

To run it with Scala Native, you must have `sqlite3` native
dependency installed and configured, along with correct flags
passed to Scala Native.

You can run the script like this:

```
sn-vcpkg scala-cli sqlite3 -- run scala-cli-sqlite3.scala
```

The sn-vcpkg CLI will add the required `--native-compile/--native-linking` flags to the _end_ of your argument list automatically.


[^1]: as long as the dependencies themselves provide a well configured pkg-config file, of course

### Docker base image
Expand Down
88 changes: 76 additions & 12 deletions modules/cli/src/main/scala/CommandLineApp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,18 @@ enum Compiler:
enum Action:
case Install(dependencies: VcpkgDependencies, out: OutputOptions)
extends Action

case InvokeCompiler(
compiler: Compiler,
dependencies: VcpkgDependencies,
args: Seq[String]
) extends Action

case InvokeScalaCLI(
dependencies: VcpkgDependencies,
args: Seq[String]
) extends Action

case Bootstrap
end Action

Expand Down Expand Up @@ -171,6 +178,8 @@ object Options extends VcpkgPluginImpl:
private def compilerCommand(compiler: Compiler) = deps.map(d =>
SuspendedAction(rest => Action.InvokeCompiler(compiler, d, rest))
)
private def scalaCliCommand =
deps.map(d => SuspendedAction(rest => Action.InvokeScalaCLI(d, rest)))

private val clang =
Opts.subcommand(
Expand All @@ -188,9 +197,19 @@ object Options extends VcpkgPluginImpl:
)(
(compilerCommand(Compiler.ClangPP), configOpts).tupled
)
private val scalaCli =
Opts.subcommand(
"scala-cli",
"Invoke Scala CLI with correct flags for passed dependencies. Sets --native automatically!" +
"The format of the command is [sn-vcpkg scala-cli <flags> <dependencies> -- <scala-cli arguments>"
)(
(scalaCliCommand, configOpts).tupled
)

val opts =
Command(name, header)(install orElse bootstrap orElse clang orElse clangPP)
Command(name, header)(
install orElse bootstrap orElse clang orElse clangPP orElse scalaCli
)

end Options

Expand Down Expand Up @@ -225,6 +244,14 @@ object C:
}
end C

enum NativeFlag:
case Compilation(value: String)
case Linking(value: String)

def get: String = this match
case Compilation(value) => value
case Linking(value) => value

object VcpkgCLI extends VcpkgPluginImpl, VcpkgPluginNativeImpl:
import Options.*

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

def printOutput(
def computeNativeFlags(
opt: OutputOptions,
deps: List[Dependency],
info: Map[Dependency, FilesInfo]
) =
val flags = List.newBuilder[String]
): List[NativeFlag] =
val flags = List.newBuilder[NativeFlag]

if opt.compile then
flags ++= compilationFlags(
configurator(info),
deps.map(_.short),
logger,
VcpkgNativeConfig()
)
).map(NativeFlag.Compilation(_))

if opt.linking then
flags ++= linkingFlags(
configurator(info),
deps.map(_.short),
logger,
VcpkgNativeConfig()
)
).map(NativeFlag.Linking(_))

flags.result().distinct
end printOutput
end computeNativeFlags

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

printOutput(
computeNativeFlags(
output,
allDeps,
result.filter((k, v) => allDeps.contains(k))
Expand All @@ -328,19 +356,55 @@ object VcpkgCLI extends VcpkgPluginImpl, VcpkgPluginNativeImpl:

compilerArgs.addAll(rest)

printOutput(
computeNativeFlags(
OutputOptions(compile = true, linking = true),
allDeps,
result.filter((k, v) => allDeps.contains(k))
).foreach(compilerArgs.addOne)
).map(_.get).foreach(compilerArgs.addOne)


scribe.debug(s"Invoking ${compiler} with arguments: [${compilerArgs.result().mkString(" ")}]")
scribe.debug(
s"Invoking ${compiler} with arguments: [${compilerArgs.result().mkString(" ")}]"
)

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

sys.exit(exitCode)

case Action.InvokeScalaCLI(deps, rest) =>
val result = vcpkgInstallImpl(deps, manager, logger)
import io.circe.parser.decode
import C.given
val allDeps =
deps.dependencies(str =>
decode[VcpkgManifestFile](str).fold(throw _, identity)
)
val scalaCliArgs = List.newBuilder[String]

scalaCliArgs.addOne("scala-cli")
scalaCliArgs.addAll(rest)

computeNativeFlags(
OutputOptions(compile = true, linking = true),
allDeps,
result.filter((k, v) => allDeps.contains(k))
).foreach {
case NativeFlag.Compilation(value) =>
scalaCliArgs.addOne("--native-compile")
scalaCliArgs.addOne(value)

case NativeFlag.Linking(value) =>
scalaCliArgs.addOne("--native-linking")
scalaCliArgs.addOne(value)
}

scribe.debug(
s"Invoking Scala CLI with arguments: [${scalaCliArgs.result().mkString(" ")}]"
)

val exitCode = scala.sys.process.Process(scalaCliArgs.result()).!

sys.exit(exitCode)

end match
end match
end main
Expand Down
Loading

0 comments on commit c05230d

Please sign in to comment.