Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add sbt plugin (#60) #111

Merged
merged 2 commits into from
Jul 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 54 additions & 12 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import scala.scalanative.sbtplugin.ScalaNativePluginInternal.nativeWorkdir
import scala.sys.process._
import java.nio.file.Path

addCommandAlias("verify", "; ^test:compile ; ^test ; ^scripted ; docs/makeSite")

val Versions = new {
val scala210 = "2.10.6"
val scala211 = "2.11.12"
val scala212 = "2.12.6"
val sbt013 = "0.13.17"
val sbt1 = "1.1.6"
}

inThisBuild(
Def.settings(
organization := "org.scalanative.bindgen",
version := "0.2-SNAPSHOT",
scalaVersion := "2.11.12",
scalacOptions ++= Seq(
"-deprecation",
"-unchecked",
Expand All @@ -20,10 +27,18 @@ inThisBuild(
git.remoteRepo := scmInfo.value.get.connection.replace("scm:git:", "")
))

val tests = project
.in(file("tests"))
val root = project("scala-native-bindgen")
.in(file("."))
.aggregate(
tests,
samples,
tools,
sbtPlugin,
docs
)

lazy val tests = project("tests")
.dependsOn(tools)
.aggregate(samples)
.settings(
fork in Test := true,
javaOptions in Test += {
Expand All @@ -38,10 +53,11 @@ val tests = project
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.5" % Test
)

lazy val samples = project
lazy val samples = project("samples")
.in(file("tests/samples"))
.enablePlugins(ScalaNativePlugin)
.settings(
scalaVersion := Versions.scala211,
libraryDependencies += "com.lihaoyi" %%% "utest" % "0.6.3" % "test",
testFrameworks += new TestFramework("utest.runner.Framework"),
nativeLinkStubs := true,
Expand Down Expand Up @@ -91,19 +107,45 @@ lazy val samples = project
}
)

lazy val tools = project in file("tools")
lazy val tools = project("tools")

lazy val sbtPlugin = project("sbt-scala-native-bindgen", ScriptedPlugin)
.dependsOn(tools)
.settings(
Keys.sbtPlugin := true,
scriptedLaunchOpts += s"-Dplugin.version=${version.value}",
scriptedLaunchOpts += {
val rootDir = (ThisBuild / baseDirectory).value
s"-Dbindgen.path=$rootDir/bindgen/target/scala-native-bindgen"
},
publishLocal := publishLocal.dependsOn(tools / publishLocal).value
)

lazy val docs = project
.in(file("docs"))
lazy val docs = project("docs")
.enablePlugins(GhpagesPlugin, ParadoxSitePlugin, ParadoxMaterialThemePlugin)
.settings(
paradoxProperties in Paradox ++= Map(
"github.base_url" -> scmInfo.value.get.browseUrl.toString
),
ParadoxMaterialThemePlugin.paradoxMaterialThemeSettings(Paradox),
paradoxMaterialTheme in Paradox := {
(paradoxMaterialTheme in Paradox).value
Paradox / paradoxMaterialTheme := {
(Paradox / paradoxMaterialTheme).value
.withRepository(scmInfo.value.get.browseUrl.toURI)
.withColor("indigo", "indigo")
}
)

def project(name: String, plugged: AutoPlugin*) = {
val unplugged = Seq(ScriptedPlugin).filterNot(plugged.toSet)
Project(id = name, base = file(name))
.disablePlugins(unplugged: _*)
.settings(
crossSbtVersions := List(Versions.sbt013, Versions.sbt1),
scalaVersion := {
(pluginCrossBuild / sbtBinaryVersion).value match {
case "0.13" => Versions.scala210
case _ => Versions.scala212
}
}
)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was the problem with Scala/sbt versions? How does this project function solve it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem was that Scala Native requires scalaVersion 2.11 and sbt either 2.10 or 2.12. Except for the samples project which tests the generated bindings, all other sbt projects use the scalaVersion associated with sbt.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

2 changes: 2 additions & 0 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.3.7")
addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.3.2")
addSbtPlugin("io.github.jonas" % "sbt-paradox-material-theme" % "0.4.0")
addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.6.2")

libraryDependencies += "org.scala-sbt" %% "scripted-plugin" % sbtVersion.value
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package org.scalanative.bindgen.sbt

import sbt._
import sbt.Keys._

import org.scalanative.bindgen.Bindgen

/**
* Generate Scala bindings from C headers.
*
* == Usage ==
*
* This plugin must be explicitly enabled. To enable it add the following line
* to your `.sbt` file:
* {{{
* enablePlugins(ScalaNativeBindgenPlugin)
* }}}
*
* By default, the plugin reads the configured header file and generates
* a single Scala source file in the managed source directory.
*
* See the [[https://github.com/kornilova-l/scala-native-bindgen/tree/master/sbt-scala-native-bindgen/src/sbt-test/bindgen/generate example project]].
*
* == Configuration ==
*
* Keys are defined in [[ScalaNativeBindgenPlugin.autoImport]].
*
* - `nativeBindgenHeader`: The C header file to read.
*
* - `nativeBindgenPackage`: Package of the enclosing object.
* No package by default.
*
* - `name in nativeBindgen`: Name of the enclosing object.
*
* @example
* {{{
* nativeBindgenHeader in Compile := file("/usr/include/ctype.h")
* nativeBindgenPackage in Compile := Some("org.example.app")
* name in (Compile, nativeBindgen) := "ctype"
* }}}
*/
object ScalaNativeBindgenPlugin extends AutoPlugin {

object autoImport {
val nativeBindgenPath =
taskKey[Option[File]]("Path to the scala-native-bindgen executable")
val nativeBindgenHeader = taskKey[File]("C header file")
val nativeBindgenPackage =
settingKey[Option[String]]("Package for the generated code")
val nativeBindgenLink =
settingKey[Option[String]]("Name of library to be linked")
val nativeBindgenExclude = settingKey[Option[String]]("Exclude prefix")
val nativeBindgen = taskKey[File]("Generate Scala Native bindings")
}
import autoImport._

override def requires = plugins.JvmPlugin

override def projectSettings: Seq[Setting[_]] =
nativeBindgenScopedSettings(Compile)

private implicit class BindgenOps(val bindgen: Bindgen) extends AnyVal {
def maybe[T](opt: Option[T], f: Bindgen => T => Bindgen): Bindgen =
opt match {
case None => bindgen
case Some(value) => f(bindgen)(value)
}
}

def nativeBindgenScopedSettings(conf: Configuration): Seq[Setting[_]] =
inConfig(conf)(
Seq(
nativeBindgenHeader := {
sys.error("nativeBindgenHeader not configured")
},
nativeBindgenPath := None,
nativeBindgenPackage := None,
nativeBindgenExclude := None,
resourceDirectories in nativeBindgen := resourceDirectories.value,
sourceGenerators += Def.task { Seq(nativeBindgen.value) },
name in nativeBindgen := "ScalaNativeBindgen",
nativeBindgen := {
val output = sourceManaged.value / "sbt-scala-native-bindgen" / "nativeBindgen.scala"

Bindgen()
.bindgenExecutable(nativeBindgenPath.value.get)
.header(nativeBindgenHeader.value)
.name((name in nativeBindgen).value)
.maybe(nativeBindgenLink.value, _.link)
.maybe(nativeBindgenPackage.value, _.packageName)
.maybe(nativeBindgenExclude.value, _.excludePrefix)
.generate()
.writeToFile(output)

output
}
))
}
35 changes: 35 additions & 0 deletions sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/build.sbt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name := "test"
organization := "org.scalanative.bindgen.sbt.test"
enablePlugins(ScalaNativeBindgenPlugin)
scalaVersion := "2.11.12"

inConfig(Compile)(
Def.settings(
nativeBindgenPath := Option(System.getProperty("bindgen.path")).map(file),
nativeBindgenHeader := (resourceDirectory in Compile).value / "header.h",
nativeBindgenPackage := Some("org.example.app"),
nativeBindgenLink := Some("app"),
nativeBindgenExclude := Some("__"),
name in nativeBindgen := "AppAPI"
))

TaskKey[Unit]("check") := {
val file = (nativeBindgen in Compile).value
val expected =
"""package org.example.app
|
|import scala.scalanative._
|import scala.scalanative.native._
|
|@native.link("app")
|@native.extern
|object AppAPI {
| def access(path: native.CString, mode: native.CInt): native.CInt = native.extern
| def read(fildes: native.CInt, buf: native.Ptr[Byte], nbyte: native.CInt): native.CInt = native.extern
| def printf(format: native.CString, varArgs: native.CVararg*): native.CInt = native.extern
|}
""".stripMargin

assert(file.exists)
assert(IO.read(file).trim == expected.trim)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
addSbtPlugin(
"org.scalanative.bindgen" % "sbt-scala-native-bindgen" % sys.props(
"plugin.version"))
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
int access(const char *path, int mode);
int read(int fildes, void *buf, int nbyte);
int printf(const char *restrict format, ...);
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
> check
2 changes: 1 addition & 1 deletion scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ if [[ ! -e bindgen/target/.llvm-version ]] || [[ "$(<bindgen/target/.llvm-versio
fi

make -C bindgen/target
sbt "${@:-test}"
sbt "${@:-verify}"
2 changes: 1 addition & 1 deletion tools/src/main/scala/org/scalanative/bindgen/Bindgen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ object Bindgen {
withArgs("--extra-arg-before", extraArgBefore) ++
Seq(header.get.getAbsolutePath, "--")

val output = Process(cmd).lineStream.mkString("\n")
val output = Process(cmd).!!

new Bindings(output)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import java.io.{File, PrintWriter}

class Bindings(private val bindings: String) {
def writeToFile(file: File): Unit = {
file.getParentFile.mkdirs()
new PrintWriter(file) {
write(bindings)
close()
Expand Down