diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 61278a0..faf48da 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,11 +46,11 @@ jobs: - name: Run example env: - LLVM_BIN: /usr/local/opt/llvm/bin + LLVM_BIN: /usr/local/opt/llvm@14/bin if: startsWith(matrix.os, 'macos-') run: | set -e - brew install llvm + brew install llvm@14 cd example SBT_VCPKG_VERSION=$(cat ../version) sbt example/run @@ -62,10 +62,12 @@ jobs: curl -fLo cs https://github.com/coursier/launchers/raw/master/coursier && chmod +x cs - ./cs launch com.indoorvivants.vcpkg:scala-vcpkg_3:$(cat version) -- install -v libpq s2n + ./cs launch com.indoorvivants.vcpkg:sn-vcpkg_3:$(cat version) -- install -v libpq s2n echo '{"name": "my-application","version": "0.15.2","dependencies": ["sqlite3"]}' > test-vcpkg.json - ./cs launch com.indoorvivants.vcpkg:scala-vcpkg_3:$(cat version) -- install-manifest -v test-vcpkg.json + ./cs launch com.indoorvivants.vcpkg:sn-vcpkg_3:$(cat version) -- install-manifest -v test-vcpkg.json + + ./cs launch com.indoorvivants.vcpkg:sn-vcpkg_3:$(cat version) -- install -q -c -l libpq s2n windows_build: diff --git a/Dockerfile b/Dockerfile index ac17978..50dcb5d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,16 +6,16 @@ RUN apt update && apt install -y curl && \ curl -Lo llvm.sh https://apt.llvm.org/llvm.sh && \ chmod +x llvm.sh && \ apt install -y lsb-release wget software-properties-common gnupg && \ - ./llvm.sh 13 && \ + ./llvm.sh 14 && \ apt update && \ - apt install -y zip unzip tar make cmake autoconf pkg-config libclang-13-dev + apt install -y zip unzip tar make cmake autoconf pkg-config COPY . /sources RUN apt install -y git -ENV LLVM_BIN "/usr/lib/llvm-13/bin" -ENV CC "/usr/lib/llvm-13/bin/clang" +ENV LLVM_BIN "/usr/lib/llvm-14/bin" +ENV CC "/usr/lib/llvm-14/bin/clang" ENV PATH="${PATH}:/root/.local/share/coursier/bin" ENV SBT_VCPKG_VERSION dev diff --git a/build.sbt b/build.sbt index 4ac58f7..ba0695a 100644 --- a/build.sbt +++ b/build.sbt @@ -45,6 +45,8 @@ val V = new { val scribe = "3.11.1" + val scalaNative = "0.4.10" + val supportedScalaVersions = List(scala213, scala212, scala3) } @@ -56,8 +58,10 @@ lazy val publishing = Seq( lazy val root = project .in(file(".")) .aggregate(core.projectRefs *) - .aggregate(`sbt-plugin`.projectRefs *) - .aggregate(`mill-plugin`.projectRefs *) + .aggregate(`sbt-vcpkg-plugin`.projectRefs *) + .aggregate(`sbt-vcpkg-native-plugin`.projectRefs *) + .aggregate(`mill-vcpkg-plugin`.projectRefs *) + .aggregate(`mill-vcpkg-native-plugin`.projectRefs *) .aggregate(cli.projectRefs *) .settings( publish / skip := true @@ -65,7 +69,7 @@ lazy val root = project lazy val core = projectMatrix .jvmPlatform(scalaVersions = V.supportedScalaVersions) - .in(file("core")) + .in(file("modules/core")) .settings(publishing) .settings( name := "vcpkg-core", @@ -85,23 +89,23 @@ lazy val cli = projectMatrix .jvmPlatform(scalaVersions = Seq(V.scala3)) .defaultAxes(VirtualAxis.scalaABIVersion(V.scala3), VirtualAxis.jvm) .dependsOn(core) - .in(file("cli")) + .in(file("modules/cli")) .settings(publishing) .settings( - name := "scala-vcpkg", + name := "sn-vcpkg", testFrameworks += new TestFramework("weaver.framework.CatsEffect"), libraryDependencies += "com.monovore" %% "decline" % V.decline, libraryDependencies += "com.outr" %% "scribe" % V.scribe ) -lazy val `sbt-plugin` = projectMatrix +lazy val `sbt-vcpkg-plugin` = projectMatrix .jvmPlatform(scalaVersions = Seq(V.scala212)) - .in(file("sbt-plugin")) + .in(file("modules/sbt-vcpkg-plugin")) .dependsOn(core) .enablePlugins(ScriptedPlugin, SbtPlugin) .settings(publishing) .settings( - name := """sbt-vcpkg""", + name := "sbt-vcpkg", sbtPlugin := true, // set up 'scripted; sbt plugin for testing sbt plugins scriptedLaunchOpts ++= Seq( @@ -111,26 +115,60 @@ lazy val `sbt-plugin` = projectMatrix scriptedBufferLog := false ) -lazy val `mill-plugin` = projectMatrix +lazy val `sbt-vcpkg-native-plugin` = projectMatrix + .jvmPlatform(scalaVersions = Seq(V.scala212)) + .in(file("modules/sbt-vcpkg-native-plugin")) + .dependsOn(core, `sbt-vcpkg-plugin`) + .enablePlugins(ScriptedPlugin, SbtPlugin) + .settings(publishing) + .settings( + name := """sbt-vcpkg-native""", + sbtPlugin := true, + // set up 'scripted; sbt plugin for testing sbt plugins + scriptedLaunchOpts ++= Seq( + "-Xmx1024M", + "-Dplugin.version=" + version.value + ), + addSbtPlugin("org.scala-native" % "sbt-scala-native" % V.scalaNative), + scriptedBufferLog := false + ) + +lazy val `mill-vcpkg-plugin` = projectMatrix .jvmPlatform(scalaVersions = Seq(V.scala213)) - .in(file("mill-plugin")) + .in(file("modules/mill-vcpkg-plugin")) .dependsOn(core) .settings(publishing) .settings( - name := """mill-vcpkg""", + name := "mill-vcpkg", libraryDependencies += "com.lihaoyi" %% "mill-scalalib" % V.mill, libraryDependencies += "com.lihaoyi" %% "utest" % V.utest % Test, testFrameworks += new TestFramework("utest.runner.Framework"), Test / fork := true, - Test / envVars := Map( - "MILL_VCPKG_ROOT" -> ((ThisBuild / baseDirectory).value / "mill-plugin" / "src" / "test").toString + Test / envVars += ( + "MILL_VCPKG_ROOT" -> ((ThisBuild / baseDirectory).value / "modules" / "mill-vcpkg-plugin" / "src" / "test").toString + ) + ) + +lazy val `mill-vcpkg-native-plugin` = projectMatrix + .jvmPlatform(scalaVersions = Seq(V.scala213)) + .in(file("modules/mill-vcpkg-native-plugin")) + .dependsOn(core, `mill-vcpkg-plugin` % "test->test;compile->compile") + .settings(publishing) + .settings( + name := "mill-vcpkg", + libraryDependencies += "com.lihaoyi" %% "mill-scalanativelib" % V.mill, + libraryDependencies += "com.lihaoyi" %% "utest" % V.utest % Test, + testFrameworks += new TestFramework("utest.runner.Framework"), + Test / fork := true, + Test / envVars += ( + "MILL_VCPKG_ROOT" -> ((ThisBuild / baseDirectory).value / "modules" / "mill-vcpkg-native-plugin" / "src" / "test").toString ) ) Global / onChangedBuildSource := ReloadOnSourceChanges lazy val versionDump = - taskKey[Unit]("Dumps the version in a file named version") + taskKey[Unit]("Dumps the version in a file named `version`") versionDump := { val file = (ThisBuild / baseDirectory).value / "version" diff --git a/example/build.sbt b/example/build.sbt index 1b4097e..14ab03a 100644 --- a/example/build.sbt +++ b/example/build.sbt @@ -1,97 +1,44 @@ -enablePlugins(VcpkgPlugin, ScalaNativePlugin, BindgenPlugin) +enablePlugins(VcpkgNativePlugin, ScalaNativePlugin, BindgenPlugin) vcpkgDependencies := Set("libuv", "czmq", "cjson", "zeromq") resolvers += Resolver.sonatypeRepo("snapshots") -scalaVersion := "3.2.0" +scalaVersion := "3.2.2" import bindgen.interface.Binding -bindgenBindings := { - val configurator = vcpkgConfigurator.value - Seq( - Binding( - configurator.includes("cjson") / "cjson" / "cJSON.h", - "cjson", - cImports = List("cJSON.h") - ), - Binding( - configurator.includes("libuv") / "uv.h", - "libuv", - cImports = List("uv.h"), - clangFlags = List("-I" + configurator.includes("libuv").toString) - ), - Binding( - configurator.includes("czmq") / "czmq.h", - "czmq", - cImports = List("czmq.h"), - clangFlags = List( - "-I" + configurator.includes("czmq").toString, - "-I" + configurator.includes("zeromq").toString - ) - ) +vcpkgNativeConfig ~= { + _.withRenamedLibraries( + Map("zeromq" -> "libzmq", "czmq" -> "libczmq", "cjson" -> "libcjson") ) } -nativeConfig := { - import com.indoorvivants.detective.Platform +bindgenBindings := { val configurator = vcpkgConfigurator.value - val pkgConfig = configurator.pkgConfig - val conf = nativeConfig.value - val deps = vcpkgDependencies.value.toSeq - - val files = deps.map(d => configurator.files(d)) - - val compileArgsApprox = files.flatMap { f => - List("-I" + f.includeDir.toString) - } - val linkingArgsApprox = files.flatMap { f => - List("-L" + f.libDir) ++ f.staticLibraries.map(_.toString) - } - - import scala.util.control.NonFatal - - def updateLinkingFlags(current: Seq[String], deps: String*) = - try { - pkgConfig.updateLinkingFlags( - current, - deps * - ) - } catch { - case NonFatal(exc) => - linkingArgsApprox - } - - def updateCompilationFlags(current: Seq[String], deps: String*) = - try { - pkgConfig.updateCompilationFlags( - current, - deps * - ) - } catch { - case NonFatal(exc) => - compileArgsApprox - } - - val arch64 = - if ( - Platform.arch == Platform.Arch.Arm && Platform.bits == Platform.Bits.x64 - ) - List("-arch", "arm64") - else Nil - - conf - .withLinkingOptions( - updateLinkingFlags( - conf.linkingOptions ++ arch64, - deps * + Seq( + Binding + .builder(configurator.includes("cjson") / "cjson" / "cJSON.h", "cjson") + .withCImports(List("cJSON.h")) + .build, + Binding + .builder(configurator.includes("libuv") / "uv.h", "libuv") + .withCImports(List("uv.h")) + .withClangFlags( + List( + "-I" + configurator.includes("libuv").toString + ) ) - ) - .withCompileOptions( - updateCompilationFlags( - conf.compileOptions ++ arch64, - deps * + .build, + Binding + .builder(configurator.includes("czmq") / "czmq.h", "czmq") + .withCImports(List("czmq.h")) + .withClangFlags( + List( + "-I" + configurator.includes("czmq").toString, + "-I" + configurator.includes("zeromq").toString + ) ) - ) + .build + ) } diff --git a/example/project/plugins.sbt b/example/project/plugins.sbt index a350d36..3e82596 100644 --- a/example/project/plugins.sbt +++ b/example/project/plugins.sbt @@ -1,11 +1,11 @@ val BindgenVersion = - sys.env.getOrElse("SN_BINDGEN_VERSION", "0.0.14") + sys.env.getOrElse("SN_BINDGEN_VERSION", "0.0.16") val VcpkgVersion = - sys.env.getOrElse("SBT_VCPKG_VERSION", "0.0.7") + sys.env.getOrElse("SBT_VCPKG_VERSION", "0.0.9") addSbtPlugin("com.indoorvivants" % "bindgen-sbt-plugin" % BindgenVersion) addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.4.7") -addSbtPlugin("com.indoorvivants.vcpkg" % "sbt-vcpkg" % VcpkgVersion) +addSbtPlugin("com.indoorvivants.vcpkg" % "sbt-vcpkg-native" % VcpkgVersion) resolvers += Resolver.sonatypeRepo("snapshots") diff --git a/cli/src/main/scala/CommandLineApp.scala b/modules/cli/src/main/scala/CommandLineApp.scala similarity index 58% rename from cli/src/main/scala/CommandLineApp.scala rename to modules/cli/src/main/scala/CommandLineApp.scala index 9bb5891..a0f88f1 100644 --- a/cli/src/main/scala/CommandLineApp.scala +++ b/modules/cli/src/main/scala/CommandLineApp.scala @@ -6,11 +6,13 @@ import com.monovore.decline.* import java.io.File enum Action: - case Install(dependencies: Seq[String]) + case Install(dependencies: Seq[String], out: OutputOptions) case InstallManifest(file: File) +case class OutputOptions(compile: Boolean, linking: Boolean) + object Options extends VcpkgPluginImpl: - private val name = "scala-vcpkg" + private val name = "sn-vcpkg" private val header = """ |Bootstraps and installs vcpkg dependencies in a way compatible with @@ -72,11 +74,43 @@ object Options extends VcpkgPluginImpl: ) .orFalse + private val quiet = Opts + .flag( + long = "quiet", + short = "q", + visibility = Visibility.Normal, + help = "Only error logging" + ) + .orFalse + + private val outputCompile = Opts + .flag( + "output-compilation", + short = "c", + help = + "Output (to STDOUT) compilation flags for installed libraries, one per line" + ) + .orFalse + + private val outputLinking = Opts + .flag( + "output-linking", + short = "l", + help = + "Output (to STDOUT) linking flags for installed libraries, one per line" + ) + .orFalse + + private val out = (outputCompile, outputLinking).mapN(OutputOptions.apply) + private val actionInstall = - Opts - .arguments[String](metavar = "dep") - .map(_.toList) - .map(Action.Install(_)) + ( + Opts + .arguments[String](metavar = "dep") + .map(_.toList), + out, + ) + .mapN(Action.Install.apply) private val actionInstallManifest = Opts @@ -85,7 +119,7 @@ object Options extends VcpkgPluginImpl: ) .map(new File(_)) .validate("File should exist")(f => f.exists() && f.isFile()) - .map(Action.InstallManifest(_)) + .map(Action.InstallManifest.apply) val logger = ExternalLogger( debug = scribe.debug(_), @@ -98,11 +132,12 @@ object Options extends VcpkgPluginImpl: rootInit: VcpkgRootInit, installDir: File, allowBootstrap: Boolean, - verbose: Boolean + verbose: Boolean, + quiet: Boolean ) private val configOpts = - (vcpkgRootInit, vcpkgInstallDir, vcpkgAllowBootstrap, verbose).mapN( + (vcpkgRootInit, vcpkgInstallDir, vcpkgAllowBootstrap, verbose, quiet).mapN( Config.apply ) @@ -122,20 +157,25 @@ object Options extends VcpkgPluginImpl: end Options -object VcpkgCLI extends VcpkgPluginImpl: +object VcpkgCLI extends VcpkgPluginImpl, VcpkgPluginNativeImpl: import Options.* def main(args: Array[String]): Unit = opts.parse(args) match case Left(help) => - System.err.println(help) - if help.errors.nonEmpty then sys.exit(1) - else sys.exit(0) + val (modified, code) = + if help.errors.nonEmpty then help.copy(body = Nil) -> -1 + else help -> 0 + System.err.println(modified) + if code != 0 then sys.exit(code) case Right((action, config)) => import config.* if verbose then scribe.Logger.root.withMinimumLevel(scribe.Level.Trace).replace() + if quiet then + scribe.Logger.root.withMinimumLevel(scribe.Level.Error).replace() + val root = rootInit.locate(logger).fold(sys.error(_), identity) scribe.debug(s"Locating/bootstrapping vcpkg in ${root.file}") @@ -143,21 +183,55 @@ object VcpkgCLI extends VcpkgPluginImpl: scribe.debug(s"Binary is $binary") val manager = VcpkgBootstrap.manager(binary, installDir, logger) + def configurator(info: Map[Dependency, FilesInfo]) = + VcpkgConfigurator(manager.config, info, logger) + + def summary(mp: Map[Dependency, FilesInfo]) = + mp.map(_._1.name).toList.sorted.mkString(", ") + + def out( + opt: OutputOptions, + deps: Seq[String], + info: Map[Dependency, FilesInfo] + ) = + val flags = List.newBuilder[String] + + if opt.compile then + flags ++= compilationFlags( + configurator(info), + deps, + logger, + VcpkgNativeConfig() + ) + if opt.linking then + flags ++= linkingFlags( + configurator(info), + deps, + logger, + VcpkgNativeConfig() + ) + + flags.result().distinct + end out action match - case Action.Install(dependencies) => - scribe.info( - "Installed dependencies: ", + case Action.Install(dependencies, opt) => + val installResult = vcpkgInstallImpl(dependencies.toSet, manager, logger) - .map(_._1.name) - .mkString(", ") + + scribe.info( + "Installed dependencies: " + summary(installResult) ) + + out(opt, dependencies, installResult).foreach(println) + case Action.InstallManifest(file) => - scribe.info( - "Installed dependencies: ", + val installResult = vcpkgInstallManifestImpl(file, manager, logger) - .map(_._1.name) - .mkString(", ") + + scribe.info( + "Installed dependencies: " + summary(installResult) ) + end match end VcpkgCLI diff --git a/core/src/main/scala/CommandParser.scala b/modules/core/src/main/scala/CommandParser.scala similarity index 100% rename from core/src/main/scala/CommandParser.scala rename to modules/core/src/main/scala/CommandParser.scala diff --git a/core/src/main/scala/com/indoorvivants/vcpkg/Configuration.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/Configuration.scala similarity index 100% rename from core/src/main/scala/com/indoorvivants/vcpkg/Configuration.scala rename to modules/core/src/main/scala/com/indoorvivants/vcpkg/Configuration.scala diff --git a/core/src/main/scala/com/indoorvivants/vcpkg/Dependencies.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/Dependencies.scala similarity index 100% rename from core/src/main/scala/com/indoorvivants/vcpkg/Dependencies.scala rename to modules/core/src/main/scala/com/indoorvivants/vcpkg/Dependencies.scala diff --git a/core/src/main/scala/com/indoorvivants/vcpkg/Dependency.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/Dependency.scala similarity index 100% rename from core/src/main/scala/com/indoorvivants/vcpkg/Dependency.scala rename to modules/core/src/main/scala/com/indoorvivants/vcpkg/Dependency.scala diff --git a/core/src/main/scala/com/indoorvivants/vcpkg/ExternalLogger.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/ExternalLogger.scala similarity index 100% rename from core/src/main/scala/com/indoorvivants/vcpkg/ExternalLogger.scala rename to modules/core/src/main/scala/com/indoorvivants/vcpkg/ExternalLogger.scala diff --git a/core/src/main/scala/com/indoorvivants/vcpkg/FilesInfo.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/FilesInfo.scala similarity index 100% rename from core/src/main/scala/com/indoorvivants/vcpkg/FilesInfo.scala rename to modules/core/src/main/scala/com/indoorvivants/vcpkg/FilesInfo.scala diff --git a/core/src/main/scala/com/indoorvivants/vcpkg/InstalledList.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/InstalledList.scala similarity index 100% rename from core/src/main/scala/com/indoorvivants/vcpkg/InstalledList.scala rename to modules/core/src/main/scala/com/indoorvivants/vcpkg/InstalledList.scala diff --git a/core/src/main/scala/com/indoorvivants/vcpkg/Linking.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/Linking.scala similarity index 100% rename from core/src/main/scala/com/indoorvivants/vcpkg/Linking.scala rename to modules/core/src/main/scala/com/indoorvivants/vcpkg/Linking.scala diff --git a/core/src/main/scala/com/indoorvivants/vcpkg/Logs.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/Logs.scala similarity index 100% rename from core/src/main/scala/com/indoorvivants/vcpkg/Logs.scala rename to modules/core/src/main/scala/com/indoorvivants/vcpkg/Logs.scala diff --git a/core/src/main/scala/com/indoorvivants/vcpkg/PkgConfig.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/PkgConfig.scala similarity index 100% rename from core/src/main/scala/com/indoorvivants/vcpkg/PkgConfig.scala rename to modules/core/src/main/scala/com/indoorvivants/vcpkg/PkgConfig.scala diff --git a/core/src/main/scala/com/indoorvivants/vcpkg/Vcpkg.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/Vcpkg.scala similarity index 100% rename from core/src/main/scala/com/indoorvivants/vcpkg/Vcpkg.scala rename to modules/core/src/main/scala/com/indoorvivants/vcpkg/Vcpkg.scala diff --git a/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgBootstrap.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgBootstrap.scala similarity index 100% rename from core/src/main/scala/com/indoorvivants/vcpkg/VcpkgBootstrap.scala rename to modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgBootstrap.scala diff --git a/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgConfigurator.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgConfigurator.scala similarity index 100% rename from core/src/main/scala/com/indoorvivants/vcpkg/VcpkgConfigurator.scala rename to modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgConfigurator.scala diff --git a/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgManifest.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgManifest.scala similarity index 100% rename from core/src/main/scala/com/indoorvivants/vcpkg/VcpkgManifest.scala rename to modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgManifest.scala diff --git a/modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgNativeConfig.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgNativeConfig.scala new file mode 100644 index 0000000..ecc5e35 --- /dev/null +++ b/modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgNativeConfig.scala @@ -0,0 +1,65 @@ +package com.indoorvivants.vcpkg + +class VcpkgNativeConfig private ( + val autoConfigure: Boolean = true, + val approximate: Boolean = true, + val staticLinking: Boolean = false, + val prependCompileOptions: Boolean = true, + val prependLinkingOptions: Boolean = true, + val renamedLibraries: Map[String, String] = Map.empty +) { self => + private def copy( + autoConfigure: Boolean = self.autoConfigure, + approximate: Boolean = self.approximate, + staticLinking: Boolean = self.staticLinking, + prependCompileOptions: Boolean = self.prependCompileOptions, + prependLinkingOptions: Boolean = self.prependLinkingOptions, + renamedLibraries: Map[String, String] = self.renamedLibraries + ) = + new VcpkgNativeConfig( + autoConfigure = autoConfigure, + approximate = approximate, + staticLinking = staticLinking, + prependCompileOptions = prependCompileOptions, + prependLinkingOptions = prependLinkingOptions, + renamedLibraries = renamedLibraries + ) + + def withAutoConfigure(value: Boolean) = copy(autoConfigure = value) + def withApproximate(value: Boolean) = copy(approximate = value) + def withStaticLinking(value: Boolean) = copy(staticLinking = value) + def withPrependCompileOptions(value: Boolean) = + copy(prependCompileOptions = value) + def withPrependLinkingOptions(value: Boolean) = + copy(prependLinkingOptions = value) + + def withRenamedLibraries(renamedLibraries: Map[String, String]) = + copy(renamedLibraries = renamedLibraries) + def addRenamedLibrary(vcpkgName: String, pkgConfigName: String) = + copy(renamedLibraries = + self.renamedLibraries.updated(vcpkgName, pkgConfigName) + ) + + private def toMap: Map[String, String] = + Map( + "autoConfigure" -> autoConfigure.toString, + "approximate" -> approximate.toString, + "staticLinking" -> staticLinking.toString, + "prependCompileOptions" -> prependCompileOptions.toString, + "prependLinkingOptions" -> prependLinkingOptions.toString, + "renamedLibraries" -> renamedLibraries.toString + ) + + override def toString() = + "Vcpkg NativeConfig: \n" + toMap.toSeq + .sortBy(_._1) + .map { case (k, v) => + s" | $k = $v" + } + .mkString("\n") + +} + +object VcpkgNativeConfig { + def apply(): VcpkgNativeConfig = new VcpkgNativeConfig() +} diff --git a/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgPluginImpl.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgPluginImpl.scala similarity index 100% rename from core/src/main/scala/com/indoorvivants/vcpkg/VcpkgPluginImpl.scala rename to modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgPluginImpl.scala diff --git a/modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgPluginNativeImpl.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgPluginNativeImpl.scala new file mode 100644 index 0000000..52433aa --- /dev/null +++ b/modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgPluginNativeImpl.scala @@ -0,0 +1,107 @@ +package com.indoorvivants.vcpkg + +import scala.util.control.NonFatal + +trait VcpkgPluginNativeImpl { + protected def updateCompilationFlags( + conf: VcpkgNativeConfig, + oldFlags: Seq[String], + newFlags: Seq[String] + ) = + if (!conf.autoConfigure) oldFlags + else if (conf.prependCompileOptions) newFlags ++ oldFlags + else oldFlags ++ newFlags + + protected def updateLinkingFlags( + conf: VcpkgNativeConfig, + oldFlags: Seq[String], + newFlags: Seq[String] + ) = + if (!conf.autoConfigure) oldFlags + else if (conf.prependLinkingOptions) newFlags ++ oldFlags + else oldFlags ++ newFlags + + protected def compilationFlags( + configurator: VcpkgConfigurator, + deps: Seq[String], + logger: ExternalLogger, + conf: VcpkgNativeConfig + ) = { + + val result = Seq.newBuilder[String] + + deps.foreach { dep => + val files = configurator.files(dep) + val compileArgsApprox = + List("-I" + files.includeDir.toString) + + val nameOverride = conf.renamedLibraries.get(dep) + + val nameDescr = nameOverride.getOrElse(dep) + nameOverride + .map(_ => s" (renamed from `$dep`)") + .getOrElse("") + + try { + result ++= configurator.pkgConfig.compilationFlags( + nameOverride.getOrElse(dep) + ) + } catch { + case NonFatal(exc) => + if (conf.approximate) { + logger.warn( + s"Compilation flags for `${nameDescr}` dependency were approximate, and might be incorrect. " + + "If you want to disable approximation, set `vcpkgNativeApprxomate := false`" + ) + result ++= compileArgsApprox + } else { + logger.warn( + s"Failed to retrieve compilation flags for `$dep`, most likely due to missing pkg-config file" + ) + } + } + } + result.result() + } + + protected def linkingFlags( + configurator: VcpkgConfigurator, + deps: Seq[String], + logger: ExternalLogger, + conf: VcpkgNativeConfig + ) = { + + val result = Seq.newBuilder[String] + + deps.foreach { dep => + val files = configurator.files(dep) + + val linkingArgsApprox = + List("-L" + files.libDir) ++ files.staticLibraries.map(_.toString) + + val nameOverride = conf.renamedLibraries.get(dep) + + val nameDescr = nameOverride.getOrElse(dep) + nameOverride + .map(_ => s" (renamed from `$dep`)") + .getOrElse("") + + + try { + result ++= configurator.pkgConfig.linkingFlags(nameOverride.getOrElse(dep)) + } catch { + case NonFatal(exc) => + if (conf.approximate) { + logger.warn( + s"Linking flags for `$nameDescr` dependency were approximated, and might be incorrect. " + + "If you want to disable approximation, set `vcpkgNativeApprxomate := false`" + ) + result ++= linkingArgsApprox + } else { + logger.warn( + s"Failed to retrieve linking flags for `$nameDescr`, most likely due to missing pkg-config file" + ) + } + } + } + result.result() + } +} diff --git a/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgRootInit.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgRootInit.scala similarity index 100% rename from core/src/main/scala/com/indoorvivants/vcpkg/VcpkgRootInit.scala rename to modules/core/src/main/scala/com/indoorvivants/vcpkg/VcpkgRootInit.scala diff --git a/core/src/main/scala/com/indoorvivants/vcpkg/package.scala b/modules/core/src/main/scala/com/indoorvivants/vcpkg/package.scala similarity index 100% rename from core/src/main/scala/com/indoorvivants/vcpkg/package.scala rename to modules/core/src/main/scala/com/indoorvivants/vcpkg/package.scala diff --git a/core/src/test/scala/com/indoorvivants/vcpkg/DependenciesSpec.scala b/modules/core/src/test/scala/com/indoorvivants/vcpkg/DependenciesSpec.scala similarity index 100% rename from core/src/test/scala/com/indoorvivants/vcpkg/DependenciesSpec.scala rename to modules/core/src/test/scala/com/indoorvivants/vcpkg/DependenciesSpec.scala diff --git a/core/src/test/scala/com/indoorvivants/vcpkg/DependencySpec.scala b/modules/core/src/test/scala/com/indoorvivants/vcpkg/DependencySpec.scala similarity index 100% rename from core/src/test/scala/com/indoorvivants/vcpkg/DependencySpec.scala rename to modules/core/src/test/scala/com/indoorvivants/vcpkg/DependencySpec.scala diff --git a/core/src/test/scala/com/indoorvivants/vcpkg/InstalledListSpec.scala b/modules/core/src/test/scala/com/indoorvivants/vcpkg/InstalledListSpec.scala similarity index 100% rename from core/src/test/scala/com/indoorvivants/vcpkg/InstalledListSpec.scala rename to modules/core/src/test/scala/com/indoorvivants/vcpkg/InstalledListSpec.scala diff --git a/core/src/test/scala/com/indoorvivants/vcpkg/VcpkgRootInitSpec.scala b/modules/core/src/test/scala/com/indoorvivants/vcpkg/VcpkgRootInitSpec.scala similarity index 100% rename from core/src/test/scala/com/indoorvivants/vcpkg/VcpkgRootInitSpec.scala rename to modules/core/src/test/scala/com/indoorvivants/vcpkg/VcpkgRootInitSpec.scala diff --git a/modules/mill-vcpkg-native-plugin/src/main/scala/MVcpkgNativeConfig.scala b/modules/mill-vcpkg-native-plugin/src/main/scala/MVcpkgNativeConfig.scala new file mode 100644 index 0000000..cf18fa1 --- /dev/null +++ b/modules/mill-vcpkg-native-plugin/src/main/scala/MVcpkgNativeConfig.scala @@ -0,0 +1,40 @@ +package com.indoorvivants.vcpkg.millplugin + +import mill._ +import upickle.default._ +import com.indoorvivants.vcpkg.Vcpkg +import com.indoorvivants.vcpkg._ + +private[vcpkg] case class MVcpkgNativeConfig( + autoConfigure: Boolean, + approximate: Boolean, + staticLinking: Boolean, + prependCompileOptions: Boolean, + prependLinkingOptions: Boolean, + renamedLibraries: Map[String, String] +) { + def toVcpkg: VcpkgNativeConfig = + VcpkgNativeConfig() + .withAutoConfigure(autoConfigure) + .withApproximate(approximate) + .withStaticLinking(staticLinking) + .withPrependCompileOptions(prependCompileOptions) + .withPrependLinkingOptions(prependLinkingOptions) + .withRenamedLibraries(renamedLibraries) +} + +private[vcpkg] object MVcpkgNativeConfig { + + def fromVcpkg(v: VcpkgNativeConfig) = + MVcpkgNativeConfig( + autoConfigure = v.autoConfigure, + approximate = v.approximate, + staticLinking = v.staticLinking, + prependCompileOptions = v.prependCompileOptions, + prependLinkingOptions = v.prependLinkingOptions, + renamedLibraries = v.renamedLibraries + ) + + implicit val readWriter: ReadWriter[MVcpkgNativeConfig] = + macroRW[MVcpkgNativeConfig] +} diff --git a/modules/mill-vcpkg-native-plugin/src/main/scala/VcpkgNativeModule.scala b/modules/mill-vcpkg-native-plugin/src/main/scala/VcpkgNativeModule.scala new file mode 100644 index 0000000..97050cb --- /dev/null +++ b/modules/mill-vcpkg-native-plugin/src/main/scala/VcpkgNativeModule.scala @@ -0,0 +1,53 @@ +package com.indoorvivants.vcpkg.millplugin.native + +import com.indoorvivants.vcpkg.VcpkgPluginNativeImpl +import com.indoorvivants.vcpkg.millplugin.VcpkgModule +import mill._, scalalib._, scalanativelib._, mill.scalanativelib.api._ +import com.indoorvivants.vcpkg.VcpkgNativeConfig +import mill.define.Task +import mill.define.Target + +trait VcpkgNativeModule + extends ScalaNativeModule + with VcpkgModule + with VcpkgPluginNativeImpl { + + def vcpkgNativeLinking: T[Seq[String]] = T { + linkingFlags( + configurator = vcpkgConfigurator(), + deps = vcpkgDependencies().toSeq.sorted, + logger = millLogger(T.log), + conf = vcpkgNativeConfig() + ) + } + + def vcpkgNativeCompilation: T[Seq[String]] = T { + compilationFlags( + configurator = vcpkgConfigurator(), + deps = vcpkgDependencies().toSeq.sorted, + logger = millLogger(T.log), + conf = vcpkgNativeConfig() + ) + } + + def vcpkgNativeConfig: Task[VcpkgNativeConfig] = T.task { + VcpkgNativeConfig() + } + + override def nativeCompileOptions: Target[Array[String]] = T { + updateCompilationFlags( + vcpkgNativeConfig(), + super.nativeCompileOptions().toSeq, + vcpkgNativeCompilation() + ).toArray + } + + override def nativeLinkingOptions: Target[Array[String]] = T { + updateLinkingFlags( + vcpkgNativeConfig(), + super.nativeLinkingOptions().toSeq, + vcpkgNativeLinking() + ).toArray + + } +} diff --git a/modules/mill-vcpkg-native-plugin/src/main/scala/package.scala b/modules/mill-vcpkg-native-plugin/src/main/scala/package.scala new file mode 100644 index 0000000..9706865 --- /dev/null +++ b/modules/mill-vcpkg-native-plugin/src/main/scala/package.scala @@ -0,0 +1,11 @@ +package com.indoorvivants.vcpkg.millplugin + +import upickle.default._ +import com.indoorvivants.vcpkg.VcpkgNativeConfig + +package object native { + implicit val filesInfoRw: ReadWriter[VcpkgNativeConfig] = + implicitly[ReadWriter[MVcpkgNativeConfig]] + .bimap(MVcpkgNativeConfig.fromVcpkg, _.toVcpkg) + +} diff --git a/modules/mill-vcpkg-native-plugin/src/test/scala/VcpkgNativeModuleSpec.scala b/modules/mill-vcpkg-native-plugin/src/test/scala/VcpkgNativeModuleSpec.scala new file mode 100644 index 0000000..8a388e3 --- /dev/null +++ b/modules/mill-vcpkg-native-plugin/src/test/scala/VcpkgNativeModuleSpec.scala @@ -0,0 +1,35 @@ +package com.indoorvivants.vcpkg.millplugin.native + +import utest._ +import mill._ +import mill.util.TestEvaluator +import mill.util.TestUtil +import com.indoorvivants.vcpkg._ + +object VcpkgNativeModuleSpec extends utest.TestSuite { + + val manifestPath = + sys.env.get("MILL_VCPKG_ROOT").map { p => + os.Path(p) / "vcpkg.json" + } + + def tests: Tests = Tests { + test("base") { + object build extends TestUtil.BaseModule { + object foo extends VcpkgNativeModule { + def vcpkgDependencies = T(Set("cjson")) + def scalaVersion = T("3.2.2") + def scalaNativeVersion = T("0.4.10") + + override def vcpkgNativeConfig = + T(super.vcpkgNativeConfig().addRenamedLibrary("cjson", "libcjson")) + } + } + + val eval = new TestEvaluator(build) + val Right((result, _)) = eval(build.foo.vcpkgNativeLinking) + + println(result) + } + } +} diff --git a/mill-plugin/src/main/scala/com/indoorvivants/vcpkg/mill/MFilesInfo.scala b/modules/mill-vcpkg-plugin/src/main/scala/com/indoorvivants/vcpkg/mill/MFilesInfo.scala similarity index 100% rename from mill-plugin/src/main/scala/com/indoorvivants/vcpkg/mill/MFilesInfo.scala rename to modules/mill-vcpkg-plugin/src/main/scala/com/indoorvivants/vcpkg/mill/MFilesInfo.scala diff --git a/mill-plugin/src/main/scala/com/indoorvivants/vcpkg/mill/VcpkgModule.scala b/modules/mill-vcpkg-plugin/src/main/scala/com/indoorvivants/vcpkg/mill/VcpkgModule.scala similarity index 98% rename from mill-plugin/src/main/scala/com/indoorvivants/vcpkg/mill/VcpkgModule.scala rename to modules/mill-vcpkg-plugin/src/main/scala/com/indoorvivants/vcpkg/mill/VcpkgModule.scala index 72afae3..95d4fa6 100644 --- a/mill-plugin/src/main/scala/com/indoorvivants/vcpkg/mill/VcpkgModule.scala +++ b/modules/mill-vcpkg-plugin/src/main/scala/com/indoorvivants/vcpkg/mill/VcpkgModule.scala @@ -99,7 +99,7 @@ trait VcpkgModule extends mill.define.Module with VcpkgPluginImpl { ) } - private def millLogger(log: Logger) = { + protected def millLogger(log: Logger) = { new ExternalLogger( debug = log.debug(_), info = log.info(_), diff --git a/mill-plugin/src/main/scala/com/indoorvivants/vcpkg/mill/package.scala b/modules/mill-vcpkg-plugin/src/main/scala/com/indoorvivants/vcpkg/mill/package.scala similarity index 100% rename from mill-plugin/src/main/scala/com/indoorvivants/vcpkg/mill/package.scala rename to modules/mill-vcpkg-plugin/src/main/scala/com/indoorvivants/vcpkg/mill/package.scala diff --git a/mill-plugin/src/test/scala/com/indoorvivants/vcpkg/mill/VcpkgModuleSpec.scala b/modules/mill-vcpkg-plugin/src/test/scala/com/indoorvivants/vcpkg/mill/VcpkgModuleSpec.scala similarity index 98% rename from mill-plugin/src/test/scala/com/indoorvivants/vcpkg/mill/VcpkgModuleSpec.scala rename to modules/mill-vcpkg-plugin/src/test/scala/com/indoorvivants/vcpkg/mill/VcpkgModuleSpec.scala index 5d3f7f5..dfa0051 100644 --- a/mill-plugin/src/test/scala/com/indoorvivants/vcpkg/mill/VcpkgModuleSpec.scala +++ b/modules/mill-vcpkg-plugin/src/test/scala/com/indoorvivants/vcpkg/mill/VcpkgModuleSpec.scala @@ -15,7 +15,6 @@ object VcpkgModuleSpec extends utest.TestSuite { def tests: Tests = Tests { test("base") { - println(sys.env.get("MILL_VCPKG_ROOT")) object build extends TestUtil.BaseModule { object foo extends VcpkgModule { def vcpkgDependencies = T(Set("cmark")) @@ -37,6 +36,7 @@ object VcpkgModuleSpec extends utest.TestSuite { } val eval = new TestEvaluator(build) + println(manifestPath) val Right((result, _)) = eval(build.foo.vcpkgConfigurator) val pkgConfig = result.pkgConfig diff --git a/mill-plugin/src/test/scala/mill/util/TestEvaluator.scala b/modules/mill-vcpkg-plugin/src/test/scala/mill/util/TestEvaluator.scala similarity index 100% rename from mill-plugin/src/test/scala/mill/util/TestEvaluator.scala rename to modules/mill-vcpkg-plugin/src/test/scala/mill/util/TestEvaluator.scala diff --git a/mill-plugin/src/test/scala/mill/util/TestUtil.scala b/modules/mill-vcpkg-plugin/src/test/scala/mill/util/TestUtil.scala similarity index 100% rename from mill-plugin/src/test/scala/mill/util/TestUtil.scala rename to modules/mill-vcpkg-plugin/src/test/scala/mill/util/TestUtil.scala diff --git a/mill-plugin/src/test/vcpkg.json b/modules/mill-vcpkg-plugin/src/test/vcpkg.json similarity index 100% rename from mill-plugin/src/test/vcpkg.json rename to modules/mill-vcpkg-plugin/src/test/vcpkg.json diff --git a/modules/sbt-vcpkg-native-plugin/src/main/scala/com/indoorvivants/vcpkg/VcpkgNativePlugin.scala b/modules/sbt-vcpkg-native-plugin/src/main/scala/com/indoorvivants/vcpkg/VcpkgNativePlugin.scala new file mode 100644 index 0000000..b04bbc3 --- /dev/null +++ b/modules/sbt-vcpkg-native-plugin/src/main/scala/com/indoorvivants/vcpkg/VcpkgNativePlugin.scala @@ -0,0 +1,82 @@ +package com.indoorvivants.vcpkg.sbtplugin + +import com.indoorvivants.vcpkg +import com.indoorvivants.detective.Platform.OS._ +import com.indoorvivants.vcpkg.Vcpkg +import com.indoorvivants.vcpkg.VcpkgBootstrap +import sbt.Keys._ +import sbt._ +import sbt.plugins.JvmPlugin + +import java.nio.file.Files +import java.util.Arrays +import java.util.stream.Collectors +import scala.sys.process +import sjsonnew.JsonFormat +import scala.util.control.NonFatal +import scala.scalanative.sbtplugin.ScalaNativePlugin + +object VcpkgNativePlugin extends AutoPlugin with vcpkg.VcpkgPluginNativeImpl { + + object autoImport { + val vcpkgNativeLinking = taskKey[Seq[String]]("") + val vcpkgNativeCompilation = taskKey[Seq[String]]("") + val vcpkgNativeConfig = settingKey[vcpkg.VcpkgNativeConfig]("") + } + + override def requires: Plugins = VcpkgPlugin && ScalaNativePlugin + + import autoImport._ + import VcpkgPlugin.{autoImport => VP} + + override lazy val projectSettings = Seq( + vcpkgNativeConfig := vcpkg.VcpkgNativeConfig(), + vcpkgNativeLinking := linkingFlags( + VP.vcpkgConfigurator.value, + VP.vcpkgDependencies.value.toSeq.sorted, + sbtLogger(sLog.value), + vcpkgNativeConfig.value + ), + vcpkgNativeCompilation := compilationFlags( + VP.vcpkgConfigurator.value, + VP.vcpkgDependencies.value.toSeq.sorted, + sbtLogger(sLog.value), + vcpkgNativeConfig.value + ), + ScalaNativePlugin.autoImport.nativeConfig := { + if (vcpkgNativeConfig.value.autoConfigure) { + val conf = ScalaNativePlugin.autoImport.nativeConfig.value + conf + .withLinkingOptions( + updateLinkingFlags( + vcpkgNativeConfig.value, + conf.linkingOptions, + vcpkgNativeLinking.value + ) + ) + .withCompileOptions( + updateCompilationFlags( + vcpkgNativeConfig.value, + conf.compileOptions, + vcpkgNativeCompilation.value + ) + ) + } else ScalaNativePlugin.autoImport.nativeConfig.value + } + ) + + override lazy val buildSettings = Seq() + + override lazy val globalSettings = Seq() + + private def sbtLogger(logger: sbt.Logger): vcpkg.ExternalLogger = { + vcpkg.ExternalLogger( + debug = logger.debug(_), + info = logger.info(_), + warn = logger.warn(_), + error = logger.error(_) + ) + + } + +} diff --git a/sbt-plugin/src/main/scala/com/indoorvivants/vcpkg/sbt/VcpkgPlugin.scala b/modules/sbt-vcpkg-plugin/src/main/scala/com/indoorvivants/vcpkg/sbt/VcpkgPlugin.scala similarity index 100% rename from sbt-plugin/src/main/scala/com/indoorvivants/vcpkg/sbt/VcpkgPlugin.scala rename to modules/sbt-vcpkg-plugin/src/main/scala/com/indoorvivants/vcpkg/sbt/VcpkgPlugin.scala diff --git a/sbt-plugin/src/sbt-test/sbt-vcpkg/simple/build.sbt b/modules/sbt-vcpkg-plugin/src/sbt-test/sbt-vcpkg/simple/build.sbt similarity index 100% rename from sbt-plugin/src/sbt-test/sbt-vcpkg/simple/build.sbt rename to modules/sbt-vcpkg-plugin/src/sbt-test/sbt-vcpkg/simple/build.sbt diff --git a/sbt-plugin/src/sbt-test/sbt-vcpkg/simple/project/plugins.sbt b/modules/sbt-vcpkg-plugin/src/sbt-test/sbt-vcpkg/simple/project/plugins.sbt similarity index 100% rename from sbt-plugin/src/sbt-test/sbt-vcpkg/simple/project/plugins.sbt rename to modules/sbt-vcpkg-plugin/src/sbt-test/sbt-vcpkg/simple/project/plugins.sbt diff --git a/sbt-plugin/src/sbt-test/sbt-vcpkg/simple/src/main/scala/Main.scala b/modules/sbt-vcpkg-plugin/src/sbt-test/sbt-vcpkg/simple/src/main/scala/Main.scala similarity index 100% rename from sbt-plugin/src/sbt-test/sbt-vcpkg/simple/src/main/scala/Main.scala rename to modules/sbt-vcpkg-plugin/src/sbt-test/sbt-vcpkg/simple/src/main/scala/Main.scala diff --git a/sbt-plugin/src/sbt-test/sbt-vcpkg/simple/test b/modules/sbt-vcpkg-plugin/src/sbt-test/sbt-vcpkg/simple/test similarity index 100% rename from sbt-plugin/src/sbt-test/sbt-vcpkg/simple/test rename to modules/sbt-vcpkg-plugin/src/sbt-test/sbt-vcpkg/simple/test diff --git a/sbt-plugin/src/sbt-test/sbt-vcpkg/simple/vcpkg.json b/modules/sbt-vcpkg-plugin/src/sbt-test/sbt-vcpkg/simple/vcpkg.json similarity index 100% rename from sbt-plugin/src/sbt-test/sbt-vcpkg/simple/vcpkg.json rename to modules/sbt-vcpkg-plugin/src/sbt-test/sbt-vcpkg/simple/vcpkg.json diff --git a/sbt-plugin/src/test/scala/com/indoorvivants/sbt/VcpkgSpec.scala b/modules/sbt-vcpkg-plugin/src/test/scala/com/indoorvivants/sbt/VcpkgSpec.scala similarity index 100% rename from sbt-plugin/src/test/scala/com/indoorvivants/sbt/VcpkgSpec.scala rename to modules/sbt-vcpkg-plugin/src/test/scala/com/indoorvivants/sbt/VcpkgSpec.scala