Skip to content

Commit fa0b9f2

Browse files
committed
Use the builder from the tools project in the sbt plugin
In order to ensure that the sbt plugin supports all options use the same builder in the tools and sbt plugin projects. Separate out the options into a new BindingOptions class to not expose unneeded methods in the sbt plugin.
1 parent 2a3b854 commit fa0b9f2

File tree

8 files changed

+196
-233
lines changed

8 files changed

+196
-233
lines changed

docs/src/paradox/obtaining-bindgen/sbt-plugin.md

+10-14
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,7 @@ resolvers += Resolver.bintrayRepo("scala-native-bindgen", "maven")
1111
```
1212
@@@
1313

14-
Next configure the plugin using the settings scoped to either `Compile` or `Test`:
15-
16-
|---------------------------|-------------------|
17-
|`nativeBindgenHeader` | The C header file to read.
18-
|`nativeBindgenPackage` | Package of the enclosing object. No package by default.
19-
|`name in nativeBindgen` | Name of the enclosing object.
20-
|`nativeBindgenLink` | Name of library to be linked.
14+
Next configure the plugin using the `nativeBindings` setting scoped to either `Compile` or `Test`. The `NativeBinding` type to configure each binding that should be generated.
2115

2216
@@@ note
2317

@@ -37,15 +31,17 @@ Example settings:
3731
enablePlugins(ScalaNativeBindgenPlugin)
3832
inConfig(Compile)(
3933
Def.settings(
40-
nativeBindgenHeader := (resourceDirectory in Compile).value / "header.h",
41-
nativeBindgenPackage := Some("org.example.mylib"),
42-
nativeBindgenLink := Some("mylib"), // Will pass `-lmylib` to the linker
43-
nativeBindgenExclude := Some("__"),
44-
name in nativeBindgen := "MyLib"
34+
nativeBindings += {
35+
NativeBinding((resourceDirectory in Compile).value / "header.h")
36+
.name("MyLib")
37+
.packageName("org.example.mylib")
38+
.link("mylib"), // Will pass `-lmylib` to the linker
39+
.excludePrefix("__")
40+
}
4541
))
4642
```
4743

48-
Running `nativeBindgen` will generate a file named `target/scala-2.x/src_managed/main/sbt-scala-native-bindgen//ScalaNativeBindgen.scala` containing something along the following lines:
44+
Running `nativeBindgen` will generate a file named `target/scala-2.x/src_managed/main/sbt-scala-native-bindgen/MyLib.scala` containing something along the following lines:
4945

5046
```scala
5147
package org.example.mylib
@@ -58,4 +54,4 @@ import scala.scalanative.native._
5854
object MyLib {
5955
// ... left out for brevity ...
6056
}
61-
```
57+
```

sbt-scala-native-bindgen/src/main/scala/org/scalanative/bindgen/sbt/ScalaNativeBindgenPlugin.scala

+18-35
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import sbt.Keys._
66
import java.nio.file.Files
77
import java.nio.file.attribute.{PosixFileAttributeView, PosixFilePermission}
88

9-
import org.scalanative.bindgen.Bindgen
9+
import org.scalanative.bindgen.{Bindgen, BindingOptions}
1010

1111
/**
1212
* Generate Scala bindings from C headers.
@@ -37,21 +37,20 @@ import org.scalanative.bindgen.Bindgen
3737
*
3838
* @example
3939
* {{{
40-
* nativeBindgenHeader in Compile := file("/usr/include/ctype.h")
41-
* nativeBindgenPackage in Compile := Some("org.example.app")
42-
* name in (Compile, nativeBindgen) := "ctype"
40+
* nativeBindings += {
41+
* NativeBinding(file("/usr/include/uv.h"))
42+
* .name("uv")
43+
* .packageName("org.example.uv")
44+
* .link("uv"),
45+
* .excludePrefix("__")
46+
* }
4347
* }}}
4448
*/
4549
object ScalaNativeBindgenPlugin extends AutoPlugin {
4650

4751
object autoImport {
48-
case class NativeBinding(
49-
name: String,
50-
header: File,
51-
packageName: Option[String],
52-
link: Option[String],
53-
excludePrefix: Option[String]
54-
)
52+
type NativeBinding = BindingOptions
53+
val NativeBinding = BindingOptions
5554
val ScalaNativeBindgen = config("scala-native-bindgen").hide
5655
val nativeBindgenPath =
5756
taskKey[File]("Path to the scala-native-bindgen executable")
@@ -105,14 +104,6 @@ object ScalaNativeBindgenPlugin extends AutoPlugin {
105104
}
106105
)
107106

108-
private implicit class BindgenOps(val bindgen: Bindgen) extends AnyVal {
109-
def maybe[T](opt: Option[T], f: Bindgen => T => Bindgen): Bindgen =
110-
opt match {
111-
case None => bindgen
112-
case Some(value) => f(bindgen)(value)
113-
}
114-
}
115-
116107
private val artifactName =
117108
Option(System.getProperty("os.name")).collect {
118109
case "Mac OS X" => "scala-native-bindgen-darwin"
@@ -126,34 +117,26 @@ object ScalaNativeBindgenPlugin extends AutoPlugin {
126117
sourceGenerators += Def.task { nativeBindgen.value },
127118
target in nativeBindgen := sourceManaged.value / "sbt-scala-native-bindgen",
128119
nativeBindgen := {
129-
val bindgenPath = nativeBindgenPath.value
130-
val bindings = nativeBindings.value
120+
val bindgen = Bindgen(nativeBindgenPath.value)
121+
val optionsList = nativeBindings.value
131122
val outputDirectory = (target in nativeBindgen).value
132123
val logger = streams.value.log
133124

134-
bindings.map {
135-
binding =>
136-
val output = outputDirectory / s"${binding.name}.scala"
137-
val result = Bindgen()
138-
.bindgenExecutable(bindgenPath)
139-
.header(binding.header)
140-
.name(binding.name)
141-
.maybe(binding.link, _.link)
142-
.maybe(binding.packageName, _.packageName)
143-
.maybe(binding.excludePrefix, _.excludePrefix)
144-
.generate()
125+
// FIXME: Check uniqueness of names.
145126

146-
result match {
127+
optionsList.map {
128+
case options: BindingOptions.Impl =>
129+
bindgen.generate(options) match {
147130
case Right(bindings) =>
131+
val output = outputDirectory / s"${bindings.name}.scala"
148132
bindings.writeToFile(output)
149133
bindings.errors.foreach(error => logger.error(error))
134+
output
150135
case Left(errors) =>
151136
errors.foreach(error => logger.error(error))
152137
sys.error(
153138
"scala-native-bindgen failed with non-zero exit code")
154139
}
155-
156-
output
157140
}
158141
}
159142
))

sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/build.sbt

+12-19
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,12 @@ scalaVersion := "2.11.12"
66
inConfig(Compile)(
77
Def.settings(
88
nativeBindgenPath := file(System.getProperty("bindgen.path")),
9-
nativeBindings := Seq(
10-
NativeBinding(
11-
name = "stdlib",
12-
header = (resourceDirectory in Compile).value / "stdlib.h",
13-
packageName = Some("org.example.app.stdlib"),
14-
link = None,
15-
excludePrefix = Some("__")
16-
)
17-
)
9+
nativeBindings += {
10+
NativeBinding((resourceDirectory in Compile).value / "stdlib.h")
11+
.name("stdlib")
12+
.packageName("org.example.app.stdlib")
13+
.excludePrefix("__")
14+
}
1815
))
1916

2017
val nativeBindgenCustomTarget = SettingKey[File]("nativeBindgenCustomTarget")
@@ -23,13 +20,9 @@ nativeBindgenCustomTarget := baseDirectory.value / "src/main/scala/org/example"
2320
val nativeBindgenCoreBinding =
2421
SettingKey[NativeBinding]("nativeBindgenCoreBinding")
2522
nativeBindgenCoreBinding := {
26-
NativeBinding(
27-
name = "core",
28-
header = (resourceDirectory in Compile).value / "core.h",
29-
packageName = Some("org.example.app.core"),
30-
link = Some("core"),
31-
excludePrefix = None
32-
)
23+
NativeBinding((resourceDirectory in Compile).value / "core.h")
24+
.packageName("org.example.app.core")
25+
.link("core")
3326
}
3427

3528
val StdlibOutput =
@@ -47,14 +40,14 @@ val StdlibOutput =
4740
""".stripMargin
4841

4942
def assertFileContent(file: File, expected: String): Unit = {
50-
val actual = IO.read(file).trim
43+
val actual = IO.read(file).trim()
5144
if (actual != expected.trim) {
5245
println(s"== [ actual ${file.getName} ] ========")
5346
println(actual)
5447
println(s"== [ expected ${file.getName} ] ========")
55-
println(expected.trim)
48+
println(expected.trim())
5649
}
57-
assert(actual == expected.trim)
50+
assert(actual == expected.trim())
5851
}
5952

6053
TaskKey[Unit]("checkSingle") := {

tests/src/test/scala/org/scalanative/bindgen/BindgenReportingSpec.scala

+3-6
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import org.scalatest.FunSpec
77
class BindgenReportingSpec extends FunSpec {
88
describe("Bindgen") {
99

10-
val bindgenPath = System.getProperty("bindgen.path")
10+
val bindgen = Bindgen(new File(System.getProperty("bindgen.path")))
1111

1212
def writeToFile(file: File, input: String): Unit = {
1313
new PrintWriter(file) {
@@ -24,16 +24,13 @@ class BindgenReportingSpec extends FunSpec {
2424
try {
2525
writeToFile(tempFile, input)
2626

27-
val result = Bindgen()
28-
.bindgenExecutable(new File(bindgenPath))
29-
.header(tempFile)
27+
val options = BindingOptions(tempFile)
3028
.name("BindgenTests")
3129
.link("bindgentests")
3230
.packageName("org.scalanative.bindgen.samples")
3331
.excludePrefix("__")
34-
.generate()
3532

36-
result match {
33+
bindgen.generate(options) match {
3734
case Right(binding) =>
3835
assert(binding.errors == errors)
3936
case Left(errors) =>

tests/src/test/scala/org/scalanative/bindgen/BindgenSpec.scala

+15-27
Original file line numberDiff line numberDiff line change
@@ -6,33 +6,15 @@ import scala.io.Source
66

77
class BindgenSpec extends FunSpec {
88
describe("Bindgen") {
9-
val bindgenPath = System.getProperty("bindgen.path")
9+
val bindgen = Bindgen(new File(System.getProperty("bindgen.path")))
1010
val inputDirectory = new File("samples")
1111

1212
val outputDir = new File("target/bindgen-samples")
1313
Option(outputDir.listFiles()).foreach(_.foreach(_.delete()))
1414
outputDir.mkdirs()
1515

16-
it("should exist") {
17-
assert(new File(bindgenPath).exists, s"$bindgenPath does not exist")
18-
}
19-
20-
def bindgen(inputFile: File, name: String, outputFile: File): Unit = {
21-
val result = Bindgen()
22-
.bindgenExecutable(new File(bindgenPath))
23-
.header(inputFile)
24-
.name(name)
25-
.link("bindgentests")
26-
.packageName("org.scalanative.bindgen.samples")
27-
.excludePrefix("__")
28-
.generate()
29-
30-
result match {
31-
case Right(binding) =>
32-
binding.writeToFile(outputFile)
33-
case Left(errors) =>
34-
fail("scala-native-bindgen failed: " + errors.mkString("\n"))
35-
}
16+
it("executable should exist") {
17+
assert(bindgen.executable.exists, s"${bindgen.executable} does not exist")
3618
}
3719

3820
def contentOf(file: File) =
@@ -42,12 +24,18 @@ class BindgenSpec extends FunSpec {
4224
it(s"should generate bindings for ${input.getName}") {
4325
val testName = input.getName.replace(".h", "")
4426
val expected = new File(inputDirectory, testName + ".scala")
45-
val output = new File(outputDir, testName + ".scala")
46-
47-
bindgen(input, testName, output)
48-
49-
assert(output.exists())
50-
assert(contentOf(output) == contentOf(expected))
27+
val options = BindingOptions(input)
28+
.name(testName)
29+
.link("bindgentests")
30+
.packageName("org.scalanative.bindgen.samples")
31+
.excludePrefix("__")
32+
33+
bindgen.generate(options) match {
34+
case Right(binding) =>
35+
assert(binding.source.trim() == contentOf(expected))
36+
case Left(errors) =>
37+
fail("scala-native-bindgen failed: " + errors.mkString("\n"))
38+
}
5139
}
5240
}
5341
}

0 commit comments

Comments
 (0)