Skip to content

Commit 67ef777

Browse files
authored
Merge pull request #111 from kornilova-l/sbt-plugin
Add sbt plugin (#60)
2 parents 7c61f98 + ac28616 commit 67ef777

File tree

10 files changed

+199
-14
lines changed

10 files changed

+199
-14
lines changed

build.sbt

+54-12
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
1-
import scala.scalanative.sbtplugin.ScalaNativePluginInternal.nativeWorkdir
21
import scala.sys.process._
3-
import java.nio.file.Path
2+
3+
addCommandAlias("verify", "; ^test:compile ; ^test ; ^scripted ; docs/makeSite")
4+
5+
val Versions = new {
6+
val scala210 = "2.10.6"
7+
val scala211 = "2.11.12"
8+
val scala212 = "2.12.6"
9+
val sbt013 = "0.13.17"
10+
val sbt1 = "1.1.6"
11+
}
412

513
inThisBuild(
614
Def.settings(
715
organization := "org.scalanative.bindgen",
816
version := "0.2-SNAPSHOT",
9-
scalaVersion := "2.11.12",
1017
scalacOptions ++= Seq(
1118
"-deprecation",
1219
"-unchecked",
@@ -20,10 +27,18 @@ inThisBuild(
2027
git.remoteRepo := scmInfo.value.get.connection.replace("scm:git:", "")
2128
))
2229

23-
val tests = project
24-
.in(file("tests"))
30+
val root = project("scala-native-bindgen")
31+
.in(file("."))
32+
.aggregate(
33+
tests,
34+
samples,
35+
tools,
36+
sbtPlugin,
37+
docs
38+
)
39+
40+
lazy val tests = project("tests")
2541
.dependsOn(tools)
26-
.aggregate(samples)
2742
.settings(
2843
fork in Test := true,
2944
javaOptions in Test += {
@@ -38,10 +53,11 @@ val tests = project
3853
libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.5" % Test
3954
)
4055

41-
lazy val samples = project
56+
lazy val samples = project("samples")
4257
.in(file("tests/samples"))
4358
.enablePlugins(ScalaNativePlugin)
4459
.settings(
60+
scalaVersion := Versions.scala211,
4561
libraryDependencies += "com.lihaoyi" %%% "utest" % "0.6.3" % "test",
4662
testFrameworks += new TestFramework("utest.runner.Framework"),
4763
nativeLinkStubs := true,
@@ -91,19 +107,45 @@ lazy val samples = project
91107
}
92108
)
93109

94-
lazy val tools = project in file("tools")
110+
lazy val tools = project("tools")
111+
112+
lazy val sbtPlugin = project("sbt-scala-native-bindgen", ScriptedPlugin)
113+
.dependsOn(tools)
114+
.settings(
115+
Keys.sbtPlugin := true,
116+
scriptedLaunchOpts += s"-Dplugin.version=${version.value}",
117+
scriptedLaunchOpts += {
118+
val rootDir = (ThisBuild / baseDirectory).value
119+
s"-Dbindgen.path=$rootDir/bindgen/target/scala-native-bindgen"
120+
},
121+
publishLocal := publishLocal.dependsOn(tools / publishLocal).value
122+
)
95123

96-
lazy val docs = project
97-
.in(file("docs"))
124+
lazy val docs = project("docs")
98125
.enablePlugins(GhpagesPlugin, ParadoxSitePlugin, ParadoxMaterialThemePlugin)
99126
.settings(
100127
paradoxProperties in Paradox ++= Map(
101128
"github.base_url" -> scmInfo.value.get.browseUrl.toString
102129
),
103130
ParadoxMaterialThemePlugin.paradoxMaterialThemeSettings(Paradox),
104-
paradoxMaterialTheme in Paradox := {
105-
(paradoxMaterialTheme in Paradox).value
131+
Paradox / paradoxMaterialTheme := {
132+
(Paradox / paradoxMaterialTheme).value
106133
.withRepository(scmInfo.value.get.browseUrl.toURI)
107134
.withColor("indigo", "indigo")
108135
}
109136
)
137+
138+
def project(name: String, plugged: AutoPlugin*) = {
139+
val unplugged = Seq(ScriptedPlugin).filterNot(plugged.toSet)
140+
Project(id = name, base = file(name))
141+
.disablePlugins(unplugged: _*)
142+
.settings(
143+
crossSbtVersions := List(Versions.sbt013, Versions.sbt1),
144+
scalaVersion := {
145+
(pluginCrossBuild / sbtBinaryVersion).value match {
146+
case "0.13" => Versions.scala210
147+
case _ => Versions.scala212
148+
}
149+
}
150+
)
151+
}

project/plugins.sbt

+2
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,5 @@ addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.3.7")
22
addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.3.2")
33
addSbtPlugin("io.github.jonas" % "sbt-paradox-material-theme" % "0.4.0")
44
addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.6.2")
5+
6+
libraryDependencies += "org.scala-sbt" %% "scripted-plugin" % sbtVersion.value
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package org.scalanative.bindgen.sbt
2+
3+
import sbt._
4+
import sbt.Keys._
5+
6+
import org.scalanative.bindgen.Bindgen
7+
8+
/**
9+
* Generate Scala bindings from C headers.
10+
*
11+
* == Usage ==
12+
*
13+
* This plugin must be explicitly enabled. To enable it add the following line
14+
* to your `.sbt` file:
15+
* {{{
16+
* enablePlugins(ScalaNativeBindgenPlugin)
17+
* }}}
18+
*
19+
* By default, the plugin reads the configured header file and generates
20+
* a single Scala source file in the managed source directory.
21+
*
22+
* See the [[https://github.com/kornilova-l/scala-native-bindgen/tree/master/sbt-scala-native-bindgen/src/sbt-test/bindgen/generate example project]].
23+
*
24+
* == Configuration ==
25+
*
26+
* Keys are defined in [[ScalaNativeBindgenPlugin.autoImport]].
27+
*
28+
* - `nativeBindgenHeader`: The C header file to read.
29+
*
30+
* - `nativeBindgenPackage`: Package of the enclosing object.
31+
* No package by default.
32+
*
33+
* - `name in nativeBindgen`: Name of the enclosing object.
34+
*
35+
* @example
36+
* {{{
37+
* nativeBindgenHeader in Compile := file("/usr/include/ctype.h")
38+
* nativeBindgenPackage in Compile := Some("org.example.app")
39+
* name in (Compile, nativeBindgen) := "ctype"
40+
* }}}
41+
*/
42+
object ScalaNativeBindgenPlugin extends AutoPlugin {
43+
44+
object autoImport {
45+
val nativeBindgenPath =
46+
taskKey[Option[File]]("Path to the scala-native-bindgen executable")
47+
val nativeBindgenHeader = taskKey[File]("C header file")
48+
val nativeBindgenPackage =
49+
settingKey[Option[String]]("Package for the generated code")
50+
val nativeBindgenLink =
51+
settingKey[Option[String]]("Name of library to be linked")
52+
val nativeBindgenExclude = settingKey[Option[String]]("Exclude prefix")
53+
val nativeBindgen = taskKey[File]("Generate Scala Native bindings")
54+
}
55+
import autoImport._
56+
57+
override def requires = plugins.JvmPlugin
58+
59+
override def projectSettings: Seq[Setting[_]] =
60+
nativeBindgenScopedSettings(Compile)
61+
62+
private implicit class BindgenOps(val bindgen: Bindgen) extends AnyVal {
63+
def maybe[T](opt: Option[T], f: Bindgen => T => Bindgen): Bindgen =
64+
opt match {
65+
case None => bindgen
66+
case Some(value) => f(bindgen)(value)
67+
}
68+
}
69+
70+
def nativeBindgenScopedSettings(conf: Configuration): Seq[Setting[_]] =
71+
inConfig(conf)(
72+
Seq(
73+
nativeBindgenHeader := {
74+
sys.error("nativeBindgenHeader not configured")
75+
},
76+
nativeBindgenPath := None,
77+
nativeBindgenPackage := None,
78+
nativeBindgenExclude := None,
79+
resourceDirectories in nativeBindgen := resourceDirectories.value,
80+
sourceGenerators += Def.task { Seq(nativeBindgen.value) },
81+
name in nativeBindgen := "ScalaNativeBindgen",
82+
nativeBindgen := {
83+
val output = sourceManaged.value / "sbt-scala-native-bindgen" / "nativeBindgen.scala"
84+
85+
Bindgen()
86+
.bindgenExecutable(nativeBindgenPath.value.get)
87+
.header(nativeBindgenHeader.value)
88+
.name((name in nativeBindgen).value)
89+
.maybe(nativeBindgenLink.value, _.link)
90+
.maybe(nativeBindgenPackage.value, _.packageName)
91+
.maybe(nativeBindgenExclude.value, _.excludePrefix)
92+
.generate()
93+
.writeToFile(output)
94+
95+
output
96+
}
97+
))
98+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name := "test"
2+
organization := "org.scalanative.bindgen.sbt.test"
3+
enablePlugins(ScalaNativeBindgenPlugin)
4+
scalaVersion := "2.11.12"
5+
6+
inConfig(Compile)(
7+
Def.settings(
8+
nativeBindgenPath := Option(System.getProperty("bindgen.path")).map(file),
9+
nativeBindgenHeader := (resourceDirectory in Compile).value / "header.h",
10+
nativeBindgenPackage := Some("org.example.app"),
11+
nativeBindgenLink := Some("app"),
12+
nativeBindgenExclude := Some("__"),
13+
name in nativeBindgen := "AppAPI"
14+
))
15+
16+
TaskKey[Unit]("check") := {
17+
val file = (nativeBindgen in Compile).value
18+
val expected =
19+
"""package org.example.app
20+
|
21+
|import scala.scalanative._
22+
|import scala.scalanative.native._
23+
|
24+
|@native.link("app")
25+
|@native.extern
26+
|object AppAPI {
27+
| def access(path: native.CString, mode: native.CInt): native.CInt = native.extern
28+
| def read(fildes: native.CInt, buf: native.Ptr[Byte], nbyte: native.CInt): native.CInt = native.extern
29+
| def printf(format: native.CString, varArgs: native.CVararg*): native.CInt = native.extern
30+
|}
31+
""".stripMargin
32+
33+
assert(file.exists)
34+
assert(IO.read(file).trim == expected.trim)
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
addSbtPlugin(
2+
"org.scalanative.bindgen" % "sbt-scala-native-bindgen" % sys.props(
3+
"plugin.version"))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
int access(const char *path, int mode);
2+
int read(int fildes, void *buf, int nbyte);
3+
int printf(const char *restrict format, ...);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
> check

scripts/test.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ if [[ ! -e bindgen/target/.llvm-version ]] || [[ "$(<bindgen/target/.llvm-versio
1313
fi
1414

1515
make -C bindgen/target
16-
sbt "${@:-test}"
16+
sbt "${@:-verify}"

tools/src/main/scala/org/scalanative/bindgen/Bindgen.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ object Bindgen {
129129
withArgs("--extra-arg-before", extraArgBefore) ++
130130
Seq(header.get.getAbsolutePath, "--")
131131

132-
val output = Process(cmd).lineStream.mkString("\n")
132+
val output = Process(cmd).!!
133133

134134
new Bindings(output)
135135
}

tools/src/main/scala/org/scalanative/bindgen/Bindings.scala

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import java.io.{File, PrintWriter}
44

55
class Bindings(private val bindings: String) {
66
def writeToFile(file: File): Unit = {
7+
file.getParentFile.mkdirs()
78
new PrintWriter(file) {
89
write(bindings)
910
close()

0 commit comments

Comments
 (0)