diff --git a/README.md b/README.md index 21864ec..b644f09 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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 @@ -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: @@ -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: @@ -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")) } ``` @@ -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`: @@ -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: @@ -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 diff --git a/build.sbt b/build.sbt index e5a470f..0579f52 100644 --- a/build.sbt +++ b/build.sbt @@ -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" @@ -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" @@ -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]("") diff --git a/docs/README.in.md b/docs/README.in.md index 47c9c74..91f31b2 100644 --- a/docs/README.in.md +++ b/docs/README.in.md @@ -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) @@ -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 diff --git a/modules/cli/src/main/scala/CommandLineApp.scala b/modules/cli/src/main/scala/CommandLineApp.scala index 2ff27ac..3d3b274 100644 --- a/modules/cli/src/main/scala/CommandLineApp.scala +++ b/modules/cli/src/main/scala/CommandLineApp.scala @@ -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 @@ -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( @@ -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 -- " + )( + (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 @@ -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.* @@ -263,12 +290,12 @@ 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( @@ -276,17 +303,18 @@ object VcpkgCLI extends VcpkgPluginImpl, VcpkgPluginNativeImpl: 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 => @@ -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)) @@ -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 diff --git a/modules/core/src/main/scala/com/indoorvivants/vcpkg/InstalledList.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/InstalledList.scala index 1d4f5d6..a734ba0 100644 --- a/modules/core/src/main/scala/com/indoorvivants/vcpkg/InstalledList.scala +++ b/modules/core/src/main/scala/com/indoorvivants/vcpkg/InstalledList.scala @@ -2,7 +2,7 @@ package com.indoorvivants.vcpkg import scala.collection.immutable -case class InstalledList private (deps: Vector[Dependency]) +class InstalledList private (val deps: Vector[Dependency]) object InstalledList { def parse(lines: Vector[String], logger: ExternalLogger) = { diff --git a/modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgRootInit.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgRootInit.scala index aa92987..669ba4b 100644 --- a/modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgRootInit.scala +++ b/modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgRootInit.scala @@ -36,10 +36,10 @@ trait VcpkgRootInit { self => case class VcpkgRoot(file: File, allowBootstrap: Boolean) object VcpkgRootInit { - case class FromEnv private[vcpkg] ( - name: String, - allowBootstrap: Boolean, - env: Map[String, String] + class FromEnv private[vcpkg] ( + val name: String, + val allowBootstrap: Boolean, + val env: Map[String, String] ) extends VcpkgRootInit { override def locate(log: ExternalLogger): Either[String, VcpkgRoot] = env.get(name) match { @@ -80,9 +80,9 @@ object VcpkgRootInit { s"VcpkgRootInit.Manual[file=$file, allowBootstrap=$allowBootstrap]" } - case class SystemCache private[vcpkg] ( - allowBootstrap: Boolean, - cacheDirDetector: CacheDirDetector + class SystemCache private[vcpkg] ( + val allowBootstrap: Boolean, + val cacheDirDetector: CacheDirDetector ) extends VcpkgRootInit { private val cacheDir = cacheDirDetector.cacheDir diff --git a/project/build.properties b/project/build.properties index 3040987..e8a1e24 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.9.4 +sbt.version=1.9.7