From 2e931d1e75d0f93f326a307da5c1038a75b757ba Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Tue, 28 Aug 2018 18:24:59 -0400 Subject: [PATCH 1/3] Improve phrasing in the CLI and sbt docs --- docs/src/paradox/cli.md | 6 +++--- docs/src/paradox/sbt.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/paradox/cli.md b/docs/src/paradox/cli.md index 9d3c39c..70b5db1 100644 --- a/docs/src/paradox/cli.md +++ b/docs/src/paradox/cli.md @@ -45,11 +45,11 @@ The generated bindings can be configured using the different options and it is a | Option | Description |----------------------|---------------------------------------------------------------------------------| -| `--link` | Library to link with, e.g. `--link` uv for libuv. +| `--link` | Library to link with, e.g. `--link uv` for libuv. | `--no-link` | Library does not require linking. -| `--name` | Scala object name that contains bindings. Default value set to library name. +| `--name` | Scala object name that contains bindings. Defaults to the library name. | `--package` | Package name of generated Scala file. -| `--exclude-prefix` | Functions and unused typedefs will be removed if their names have given prefix. +| `--exclude-prefix` | Functions and unused typedefs will be removed if their names have the given prefix. | `--binding-config` | Path to a config file that contains the information about bindings that should be reused. See @ref:[Integrating Bindings](integrating-bindings.md) for more information. | `--extra-arg` | Additional argument to append to the compiler command line. | `--extra-arg-before` | Additional argument to prepend to the compiler command line. diff --git a/docs/src/paradox/sbt.md b/docs/src/paradox/sbt.md index 358811f..fddbb67 100644 --- a/docs/src/paradox/sbt.md +++ b/docs/src/paradox/sbt.md @@ -28,6 +28,6 @@ Given that the `stdlib.h` header file contains: @@snip [sbt] (../../../sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/src/main/resources/stdlib.h) -Running `nativeBindgen` will generate a file named `target/scala-2.11/src_managed/main/sbt-scala-native-bindgen/stdlib.scala` containing something along the following lines: +Running `nativeBindgen` will generate a file named `target/scala-2.11/src_managed/main/sbt-scala-native-bindgen/stdlib.scala` containing the following lines: @@snip [sbt] (../../../sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/expected/stdlib.scala) \ No newline at end of file From c178e72c1e67ec5f7bb03681026c3e5de615cb66 Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Tue, 28 Aug 2018 18:51:24 -0400 Subject: [PATCH 2/3] Improve the binding configuration documentation --- build.sbt | 37 +++++++- docs/src/paradox/cli.md | 2 +- docs/src/paradox/configuration.md | 94 +++++++++++++++++++ docs/src/paradox/index.md | 4 +- docs/src/paradox/integrating-bindings.md | 27 ------ ...enerated-bindings.md => using-bindings.md} | 22 +++-- .../resources/3rd-party-bindings/config.json | 9 ++ .../resources/3rd-party-bindings/geometry.c | 6 ++ .../resources/3rd-party-bindings/geometry.h | 10 ++ .../scala-native-bindings/config.json | 8 ++ .../scala-native-bindings/wordcount.c | 39 ++++++++ .../scala-native-bindings/wordcount.h | 11 +++ .../resources/{ => using-bindings}/vector.c | 0 .../resources/{ => using-bindings}/vector.h | 0 .../com/example/custom/binding/Vector.scala | 61 ++++++++++++ .../src/test/scala/org/example/Geometry.scala | 33 +++++++ .../test/scala/org/example/WordCount.scala | 36 +++++++ .../bindgen/docs/GeometrySpec.scala | 25 +++++ .../scalanative/bindgen/docs/VectorSpec.scala | 4 +- .../bindgen/docs/WordCountSpec.scala | 37 ++++++++ .../src/sbt-test/bindgen/generate/build.sbt | 4 + 21 files changed, 424 insertions(+), 45 deletions(-) create mode 100644 docs/src/paradox/configuration.md delete mode 100644 docs/src/paradox/integrating-bindings.md rename docs/src/paradox/{using-generated-bindings.md => using-bindings.md} (74%) create mode 100644 docs/src/test/resources/3rd-party-bindings/config.json create mode 100644 docs/src/test/resources/3rd-party-bindings/geometry.c create mode 100644 docs/src/test/resources/3rd-party-bindings/geometry.h create mode 100644 docs/src/test/resources/scala-native-bindings/config.json create mode 100644 docs/src/test/resources/scala-native-bindings/wordcount.c create mode 100644 docs/src/test/resources/scala-native-bindings/wordcount.h rename docs/src/test/resources/{ => using-bindings}/vector.c (100%) rename docs/src/test/resources/{ => using-bindings}/vector.h (100%) create mode 100644 docs/src/test/scala/com/example/custom/binding/Vector.scala create mode 100644 docs/src/test/scala/org/example/Geometry.scala create mode 100644 docs/src/test/scala/org/example/WordCount.scala create mode 100644 docs/src/test/scala/org/scalanative/bindgen/docs/GeometrySpec.scala create mode 100644 docs/src/test/scala/org/scalanative/bindgen/docs/WordCountSpec.scala diff --git a/build.sbt b/build.sbt index ed8a538..49376c8 100644 --- a/build.sbt +++ b/build.sbt @@ -138,23 +138,50 @@ lazy val sbtPlugin = project("sbt-scala-native-bindgen", ScriptedPlugin) publishLocal := publishLocal.dependsOn(tools / publishLocal).value ) +lazy val docsUsingBindingsDirectory = settingKey[File]("vector source") +lazy val docs3rdPartyBindingsDirectory = settingKey[File]("geometry source") +lazy val docsScalaNativeBindingsDirectory = settingKey[File]("wordcount source") + lazy val docs = nativeProject("docs") .enablePlugins(GhpagesPlugin, ParadoxSitePlugin, ParadoxMaterialThemePlugin) .enablePlugins(ScalaNativeBindgenPlugin) .settings( publish / skip := true, - Test / nativeBindings += { - NativeBinding((Test / resourceDirectory).value / "vector.h") + docsUsingBindingsDirectory := (Test / resourceDirectory).value / "using-bindings", + docs3rdPartyBindingsDirectory := (Test / resourceDirectory).value / "3rd-party-bindings", + docsScalaNativeBindingsDirectory := (Test / resourceDirectory).value / "scala-native-bindings", + Test / nativeBindings ++= Seq( + NativeBinding(docsUsingBindingsDirectory.value / "vector.h") .name("vector") .link("vector") - .packageName("org.example") - }, + .packageName("org.example"), + NativeBinding(docs3rdPartyBindingsDirectory.value / "geometry.h") + .name("Geometry") + .link("geometry") + .packageName("org.example.geometry") + .bindingConfig(docs3rdPartyBindingsDirectory.value / "config.json"), { + val pathToHeader = docsScalaNativeBindingsDirectory.value / "wordcount.h" + val pathToConfig = docsScalaNativeBindingsDirectory.value / "config.json" + //#sbt-binding-config + NativeBinding(pathToHeader) + //#sbt-binding-config + .name("WordCount") + .link("wordcount") + .packageName("org.example.wordcount") + .excludePrefix("__") + //#sbt-binding-config + .bindingConfig(pathToConfig) + //#sbt-binding-config + } + ), nativeBindgenPath := { Some( (ThisBuild / baseDirectory).value / "bindgen/target/scala-native-bindgen") }, Test / nativeBindgen / target := (Test / scalaSource).value / "org/example", - compileTask("vector", Test / resourceDirectory), + compileTask("vector", docsUsingBindingsDirectory), + compileTask("geometry", docs3rdPartyBindingsDirectory), + compileTask("wordcount", docsScalaNativeBindingsDirectory), libraryDependencies += "org.scalatest" %%% "scalatest" % "3.2.0-SNAP10" % Test, Paradox / paradoxProperties ++= Map( "github.base_url" -> scmInfo.value.get.browseUrl.toString diff --git a/docs/src/paradox/cli.md b/docs/src/paradox/cli.md index 70b5db1..6178744 100644 --- a/docs/src/paradox/cli.md +++ b/docs/src/paradox/cli.md @@ -50,6 +50,6 @@ The generated bindings can be configured using the different options and it is a | `--name` | Scala object name that contains bindings. Defaults to the library name. | `--package` | Package name of generated Scala file. | `--exclude-prefix` | Functions and unused typedefs will be removed if their names have the given prefix. -| `--binding-config` | Path to a config file that contains the information about bindings that should be reused. See @ref:[Integrating Bindings](integrating-bindings.md) for more information. +| `--binding-config` | Path to a config file that contains the information about bindings that should be reused. See @ref:[Configuration](configuration.md) for more information. | `--extra-arg` | Additional argument to append to the compiler command line. | `--extra-arg-before` | Additional argument to prepend to the compiler command line. diff --git a/docs/src/paradox/configuration.md b/docs/src/paradox/configuration.md new file mode 100644 index 0000000..60aa96d --- /dev/null +++ b/docs/src/paradox/configuration.md @@ -0,0 +1,94 @@ +# Configuration + +Bindgen provides several options for configuring what to include and how +to output the generated bindings. The options can be provided to the CLI +or as part of the sbt bindings declaration. + +@@ toc + +## Excluding Definitions By Prefix + +Definitions may be excluded by their prefix. This is useful when private definitions should not be part of the generated binding. This is often the case for definitions starting with `__`. + +sbt +: @@snip [sbt] (../../../sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/build.sbt) { #sbt-exclude-prefix } + +CLI +: ```sh + scala-native-bindgen --binding-config=/path/to/config + ``` + +To exemplify consider the following header file: + +@@snip [sbt] (../../../sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/src/main/resources/stdlib.h) + +When the exclude prefix is set to `__`, then the resulting bindings will be: + +@@snip [sbt] (../../../sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/expected/stdlib.scala) + +## Binding Configuration File + +The binding configuration is a JSON file which allows to map the path of +a header file to the associated object as well as the names of the C +types and symbols to their respective Scala types and definitions. The +configuration file can be used when integrating with third party +bindings. + +sbt +: @@snip [sbt-binding-config](../../../build.sbt) { #sbt-binding-config } + +CLI +: ```sh + scala-native-bindgen --binding-config=/path/to/config + ``` + +### Using Types From Third Party Bindings + +If you need to generate bindings that uses types from bindings that have not been generated with `scala-native-bindgen` you have to provide the mapping between the header path and the Scala object's fully qualified name. And in some cases also the Scala name of the C types. Using the vector library example from @ref:[Using Bindings](using-bindings.md), let's assume that the vector library was created so that `struct point` was named `Point`: + +@@snip [Vector] (../test/scala/com/example/custom/binding/Vector.scala) { #example } + +To use this binding, create a configuration file with the folllowing content, where `path` is the name of the header file (usually the part of the path inside the `/usr/include` or `/usr/local/include` directory), `object` is the fully qualified name of the Scala object (i.e. package name as well as the Scala object name) and finally `names` for each of the types: + +@@snip [vector.h] (../test/resources/3rd-party-bindings/config.json) + +Now in the library you are creating a binding for, any usage of `struct point`: + +@@snip [vector.h] (../test/resources/3rd-party-bindings/geometry.h) { #using-struct-point } + +will reference the `Point` type alias inside the `Vector` object: + +@@snip [Geometry] (../test/scala/org/example/Geometry.scala) { #using-struct-point } + +### Using Types From the Scala Native Bindings + +Similar to the above, the following this example shows how you can use +types defined in the [C standard library] and [C POSIX library] bindings +shipped with Scala Native. Let's assume we have a binding with a method that uses the `FILE` type +from ``: + +@@snip [vector.h] (../test/resources/scala-native-bindings/wordcount.h) { #using-stdio-file } + +We can then write a binding configuration that maps the header name to the object defined in Scala Native. + +@@snip [vector.h] (../test/resources/scala-native-bindings/config.json) + +@@@ note + +In the above binding configuration, we duplicate the mapping to work on both Linux and macOS since on macOS +the definition of `FILE` is found inside `/usr/include/_stdio.h`. + +@@@ + +The generated binding will then use the `stdio.h` binding provided by Scala Native: + +@@snip [WordCount] (../test/scala/org/example/WordCount.scala) { #using-stdio-file } + +And we can use the binding by opening a file using `fopen(...)`: + +@@snip [WordCountSpec] (../test/scala/org/scalanative/bindgen/docs/WordCountSpec.scala) { #example } + + [Scala Native memory management]: http://www.scala-native.org/en/latest/user/interop.html#memory-management + [Scala Native memory layout types]: http://www.scala-native.org/en/latest/user/interop.html#memory-layout-types + [C standard library]: http://www.scala-native.org/en/latest/lib/libc.html + [C POSIX library]: http://www.scala-native.org/en/latest/lib/posixlib.html diff --git a/docs/src/paradox/index.md b/docs/src/paradox/index.md index a5c5212..9794ce9 100644 --- a/docs/src/paradox/index.md +++ b/docs/src/paradox/index.md @@ -29,8 +29,8 @@ This project is distributed under the Scala license. * [sbt](sbt.md) * [cli](cli.md) -* [Using Generated Bindings](using-generated-bindings.md) -* [Integrating Bindings](integrating-bindings.md) +* [usage](using-bindings.md) +* [configuration](configuration.md) * [Limitations](limitations.md) * [bindings](bindings/index.md) * [Contributor's Guide](contrib/index.md) diff --git a/docs/src/paradox/integrating-bindings.md b/docs/src/paradox/integrating-bindings.md deleted file mode 100644 index f07d164..0000000 --- a/docs/src/paradox/integrating-bindings.md +++ /dev/null @@ -1,27 +0,0 @@ -# Integrating Bindings - -To reuse already generated bindings create a `config.json` file that defines which headers correspond to which Scala objects: - -```json -{ - "_stdio.h": "scala.scalanative.native.stdio", - "/full/path/to/regexp4.h": "bindings.regexp4" -} -``` - -Bindgen assumes that type names in header files match type names in generated binding (spaces in struct, union and enum -names are replaces with underscores), but it is possible to specify custom names mapping: - -```json -{ - "hiredis.h": { - "object": "bindings.hiredis.hiredis", - "names": { - "redisContext": "Context" - } - } -} -``` - -Provide a path to `config.json` to bindgen using `--binding-config` command-line option or `NativeBinding.bindingConfig` -sbt plugin option (see @ref:[Using the sbt plugin](sbt.md)). diff --git a/docs/src/paradox/using-generated-bindings.md b/docs/src/paradox/using-bindings.md similarity index 74% rename from docs/src/paradox/using-generated-bindings.md rename to docs/src/paradox/using-bindings.md index f04d675..d9727ea 100644 --- a/docs/src/paradox/using-generated-bindings.md +++ b/docs/src/paradox/using-bindings.md @@ -1,23 +1,30 @@ -# Using Generated Bindings +# Using Bindings + +The following explains how to use bindings generated with Scala Native Bindgen through several examples. + +## A Simple Vector Library Consider following header file: -@@snip [vector.h] (../test/resources/vector.h) +@@snip [vector.h] (../test/resources/using-bindings/vector.h) Bindgen will output + * type aliases for the structs * binding for function `cosine` * helper functions that make usage of structs easier @@snip [vector.h] (../test/scala/org/example/vector.scala) -Let's write code that creates two line segments and prints angel between them. +## Using the Vector Library + +Let's write code that creates two line segments and calculates the angel between them. First we need to create points. We will use `native.Zone` to allocate struct (more information on memory management can be found here: [Scala Native memory management]). -Generated bindings contain helper functions that make struct allocation easier. +The generated bindings contain helper functions that make struct allocation easier. To import them use `import org.example.vector._` Let's create two points and the first line segment: @@ -39,17 +46,16 @@ to your code: @@@ note -Note that `struct_lineSegment` contains fields of value type `struct_point` +`struct_lineSegment` contains fields of value type `struct_point` but setters accept variables of type `native.Ptr[struct_point]`. -It helps to avoid Scala Native limitation that does not allow passing structs +The reason is that Scala Native does not allow passing structs and arrays by value (see @github[scala-native/scala-native#555](scala-native/scala-native#555)). @@@ -Now we can calculate angel between line segments: +Now we can calculate the angel between the line segments: @@snip [step-3] (../test/scala/org/scalanative/bindgen/docs/VectorSpec.scala) { #step-3 } - [Scala Native memory management]: http://www.scala-native.org/en/latest/user/interop.html#memory-management [Scala Native memory layout types]: http://www.scala-native.org/en/latest/user/interop.html#memory-layout-types diff --git a/docs/src/test/resources/3rd-party-bindings/config.json b/docs/src/test/resources/3rd-party-bindings/config.json new file mode 100644 index 0000000..7cb53a4 --- /dev/null +++ b/docs/src/test/resources/3rd-party-bindings/config.json @@ -0,0 +1,9 @@ +{ + "vector.h": { + "object": "com.example.custom.binding.Vector", + "names": { + "struct point": "Point", + "struct lineSegment": "LineSegment" + } + } +} \ No newline at end of file diff --git a/docs/src/test/resources/3rd-party-bindings/geometry.c b/docs/src/test/resources/3rd-party-bindings/geometry.c new file mode 100644 index 0000000..aee5f43 --- /dev/null +++ b/docs/src/test/resources/3rd-party-bindings/geometry.c @@ -0,0 +1,6 @@ +#include "geometry.h" +#include + +float circle_area(struct circle *circle) { + return pow(circle->radius, 2) * M_PI; +} diff --git a/docs/src/test/resources/3rd-party-bindings/geometry.h b/docs/src/test/resources/3rd-party-bindings/geometry.h new file mode 100644 index 0000000..c4e2285 --- /dev/null +++ b/docs/src/test/resources/3rd-party-bindings/geometry.h @@ -0,0 +1,10 @@ +#include "../using-bindings/vector.h" + +//#using-struct-point +struct circle { + struct point point; + double radius; +}; +//#using-struct-point + +float circle_area(struct circle *circle); diff --git a/docs/src/test/resources/scala-native-bindings/config.json b/docs/src/test/resources/scala-native-bindings/config.json new file mode 100644 index 0000000..58948a7 --- /dev/null +++ b/docs/src/test/resources/scala-native-bindings/config.json @@ -0,0 +1,8 @@ +{ + "stdio.h": { + "object": "scala.scalanative.native.stdio" + }, + "_stdio.h": { + "object": "scala.scalanative.native.stdio" + } +} \ No newline at end of file diff --git a/docs/src/test/resources/scala-native-bindings/wordcount.c b/docs/src/test/resources/scala-native-bindings/wordcount.c new file mode 100644 index 0000000..69e7550 --- /dev/null +++ b/docs/src/test/resources/scala-native-bindings/wordcount.c @@ -0,0 +1,39 @@ +#include "wordcount.h" +#include +#include + +int wordcount(struct wordcount *wordcount, FILE *file) { + char buf[4096]; + + // FIXME: This doesn't seem to have any effect + // memset(wordcount, sizeof(*wordcount), 0); + + wordcount->chars = 0; + wordcount->words = 0; + wordcount->lines = 0; + + while (fgets(buf, sizeof(buf), file)) { + char *pos; + int in_word = 0; + + wordcount->lines++; + wordcount->chars += strlen(buf); + + for (pos = buf; *pos; pos++) { + if (isspace(*pos)) { + if (in_word) { + wordcount->words++; + } + in_word = 0; + } else { + in_word = 1; + } + } + + if (in_word) { + wordcount->words++; + } + } + + return 0; +} diff --git a/docs/src/test/resources/scala-native-bindings/wordcount.h b/docs/src/test/resources/scala-native-bindings/wordcount.h new file mode 100644 index 0000000..dbd427e --- /dev/null +++ b/docs/src/test/resources/scala-native-bindings/wordcount.h @@ -0,0 +1,11 @@ +#include + +struct wordcount { + long chars; + long lines; + long words; +}; + +//#using-stdio-file +int wordcount(struct wordcount *wordcount, FILE *file); +//#using-stdio-file diff --git a/docs/src/test/resources/vector.c b/docs/src/test/resources/using-bindings/vector.c similarity index 100% rename from docs/src/test/resources/vector.c rename to docs/src/test/resources/using-bindings/vector.c diff --git a/docs/src/test/resources/vector.h b/docs/src/test/resources/using-bindings/vector.h similarity index 100% rename from docs/src/test/resources/vector.h rename to docs/src/test/resources/using-bindings/vector.h diff --git a/docs/src/test/scala/com/example/custom/binding/Vector.scala b/docs/src/test/scala/com/example/custom/binding/Vector.scala new file mode 100644 index 0000000..e09a104 --- /dev/null +++ b/docs/src/test/scala/com/example/custom/binding/Vector.scala @@ -0,0 +1,61 @@ +//#example +package com.example.custom.binding + +import scala.scalanative._ +import scala.scalanative.native._ + +@native.link("vector") +@native.extern +object Vector { + type Point = native.CStruct2[native.CFloat, native.CFloat] + type LineSegment = native.CStruct2[Point, Point] + // ... + //#example + def cosine(v1: native.Ptr[LineSegment], + v2: native.Ptr[LineSegment]): native.CFloat = native.extern + + object implicits { + implicit class PointOps(val p: native.Ptr[Point]) extends AnyVal { + def x: native.CFloat = !p._1 + def x_=(value: native.CFloat): Unit = !p._1 = value + def y: native.CFloat = !p._2 + def y_=(value: native.CFloat): Unit = !p._2 = value + } + + implicit class LineSegmentOps(val p: native.Ptr[LineSegment]) + extends AnyVal { + def a: native.Ptr[Point] = p._1 + def a_=(value: native.Ptr[Point]): Unit = !p._1 = !value + def b: native.Ptr[Point] = p._2 + def b_=(value: native.Ptr[Point]): Unit = !p._2 = !value + } + } + + object Point { + import implicits._ + def apply()(implicit z: native.Zone): native.Ptr[Point] = + native.alloc[Point] + def apply(x: native.CFloat, y: native.CFloat)( + implicit z: native.Zone): native.Ptr[Point] = { + val ptr = native.alloc[Point] + ptr.x = x + ptr.y = y + ptr + } + } + + object LineSegment { + import implicits._ + def apply()(implicit z: native.Zone): native.Ptr[LineSegment] = + native.alloc[LineSegment] + def apply(a: native.Ptr[Point], b: native.Ptr[Point])( + implicit z: native.Zone): native.Ptr[LineSegment] = { + val ptr = native.alloc[LineSegment] + ptr.a = a + ptr.b = b + ptr + } + } + //#example +} +//#example diff --git a/docs/src/test/scala/org/example/Geometry.scala b/docs/src/test/scala/org/example/Geometry.scala new file mode 100644 index 0000000..4cbcc87 --- /dev/null +++ b/docs/src/test/scala/org/example/Geometry.scala @@ -0,0 +1,33 @@ +package org.example.geometry + +import scala.scalanative._ +import scala.scalanative.native._ + +@native.link("geometry") +@native.extern +object Geometry { + //#using-struct-point + type struct_circle = native.CStruct2[com.example.custom.binding.Vector.Point, native.CDouble] + //#using-struct-point + def circle_area(circle: native.Ptr[struct_circle]): native.CFloat = native.extern + + object implicits { + implicit class struct_circle_ops(val p: native.Ptr[struct_circle]) extends AnyVal { + def point: native.Ptr[com.example.custom.binding.Vector.Point] = p._1 + def point_=(value: native.Ptr[com.example.custom.binding.Vector.Point]): Unit = !p._1 = !value + def radius: native.CDouble = !p._2 + def radius_=(value: native.CDouble): Unit = !p._2 = value + } + } + + object struct_circle { + import implicits._ + def apply()(implicit z: native.Zone): native.Ptr[struct_circle] = native.alloc[struct_circle] + def apply(point: native.Ptr[com.example.custom.binding.Vector.Point], radius: native.CDouble)(implicit z: native.Zone): native.Ptr[struct_circle] = { + val ptr = native.alloc[struct_circle] + ptr.point = point + ptr.radius = radius + ptr + } + } +} diff --git a/docs/src/test/scala/org/example/WordCount.scala b/docs/src/test/scala/org/example/WordCount.scala new file mode 100644 index 0000000..35d4b62 --- /dev/null +++ b/docs/src/test/scala/org/example/WordCount.scala @@ -0,0 +1,36 @@ +package org.example.wordcount + +import scala.scalanative._ +import scala.scalanative.native._ + +@native.link("wordcount") +@native.extern +object WordCount { + type struct_wordcount = native.CStruct3[native.CLong, native.CLong, native.CLong] + //#using-stdio-file + def wordcount(wordcount: native.Ptr[struct_wordcount], file: native.Ptr[scala.scalanative.native.stdio.FILE]): native.CInt = native.extern + //#using-stdio-file + + object implicits { + implicit class struct_wordcount_ops(val p: native.Ptr[struct_wordcount]) extends AnyVal { + def chars: native.CLong = !p._1 + def chars_=(value: native.CLong): Unit = !p._1 = value + def lines: native.CLong = !p._2 + def lines_=(value: native.CLong): Unit = !p._2 = value + def words: native.CLong = !p._3 + def words_=(value: native.CLong): Unit = !p._3 = value + } + } + + object struct_wordcount { + import implicits._ + def apply()(implicit z: native.Zone): native.Ptr[struct_wordcount] = native.alloc[struct_wordcount] + def apply(chars: native.CLong, lines: native.CLong, words: native.CLong)(implicit z: native.Zone): native.Ptr[struct_wordcount] = { + val ptr = native.alloc[struct_wordcount] + ptr.chars = chars + ptr.lines = lines + ptr.words = words + ptr + } + } +} diff --git a/docs/src/test/scala/org/scalanative/bindgen/docs/GeometrySpec.scala b/docs/src/test/scala/org/scalanative/bindgen/docs/GeometrySpec.scala new file mode 100644 index 0000000..b1599e3 --- /dev/null +++ b/docs/src/test/scala/org/scalanative/bindgen/docs/GeometrySpec.scala @@ -0,0 +1,25 @@ +package org.scalanative.bindgen.docs + +import org.scalatest.FunSpec + +class GeometrySpec extends FunSpec { + describe("geometry") { + it("using generated bindings") { + //#example + import com.example.custom.binding.Vector.Point + import org.example.geometry.Geometry._ + import scala.scalanative.native.Zone + + Zone { implicit zone => + val center = Point(1, 1) + val circle = struct_circle(center, 2.0) + + val area = circle_area(circle) + //#example + assert(area - (4 * math.Pi) < 1e-3f) + //#example + } + //#example + } + } +} diff --git a/docs/src/test/scala/org/scalanative/bindgen/docs/VectorSpec.scala b/docs/src/test/scala/org/scalanative/bindgen/docs/VectorSpec.scala index 7616a61..8851fbf 100644 --- a/docs/src/test/scala/org/scalanative/bindgen/docs/VectorSpec.scala +++ b/docs/src/test/scala/org/scalanative/bindgen/docs/VectorSpec.scala @@ -25,12 +25,12 @@ class VectorSpec extends FunSpec { lineSegment2.b = struct_point(5, 0) //#step-2 //#step-3 - println(cosine(lineSegment1, lineSegment2)) + val angle = cosine(lineSegment1, lineSegment2) //#step-3 import org.scalactic.TolerantNumerics val epsilon = 1e-3f assert( - (0.8.toFloat === cosine(lineSegment1, lineSegment2))( + (0.8.toFloat === angle)( TolerantNumerics.tolerantFloatEquality(epsilon))) //#step-1 } diff --git a/docs/src/test/scala/org/scalanative/bindgen/docs/WordCountSpec.scala b/docs/src/test/scala/org/scalanative/bindgen/docs/WordCountSpec.scala new file mode 100644 index 0000000..dddb2dc --- /dev/null +++ b/docs/src/test/scala/org/scalanative/bindgen/docs/WordCountSpec.scala @@ -0,0 +1,37 @@ +package org.scalanative.bindgen.docs + +import org.scalatest.FunSpec + +class WordCountSpec extends FunSpec { + describe("wordcount") { + it("using generated bindings") { + //#example + import org.example.wordcount.WordCount._ + import scalanative.native._ + + //#example + val pathToFile = + c"docs/src/test/resources/scala-native-bindings/wordcount.h" + + import scalanative.posix.unistd.access + import scalanative.posix.fcntl.R_OK + assert(access(pathToFile, R_OK) == 0, "Header file does not exist") + + //#example + Zone { implicit zone => + val result = struct_wordcount() + val file = stdio.fopen(pathToFile, c"r") + val code = wordcount(result, file) + stdio.fclose(file) + //#example + import org.example.wordcount.WordCount.implicits._ + assert(code == 0) + assert(result.chars == 187) + assert(result.words == 20) + assert(result.lines == 11) + //#example + } + //#example + } + } +} diff --git a/sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/build.sbt b/sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/build.sbt index 6599621..9e23ced 100644 --- a/sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/build.sbt +++ b/sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/build.sbt @@ -11,10 +11,14 @@ nativeBindgenPath := Some(file(System.getProperty("bindgen.path"))) inConfig(Compile)( Def.settings( nativeBindings += { + //#sbt-exclude-prefix NativeBinding(resourceDirectory.value / "stdlib.h") + //#sbt-exclude-prefix .name("stdlib") .packageName("org.example.app.stdlib") + //#sbt-exclude-prefix .excludePrefix("__") + //#sbt-exclude-prefix } )) //#example From 4ba6d11193707a193918a277d25d4de61926c705 Mon Sep 17 00:00:00 2001 From: Jonas Fonseca Date: Mon, 3 Sep 2018 16:23:43 -0400 Subject: [PATCH 3/3] Fix indentation of the sbt excludePrefix and bindingConfig examples --- build.sbt | 28 ++++++++++--------- docs/src/paradox/configuration.md | 4 +-- .../src/sbt-test/bindgen/generate/build.sbt | 4 --- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/build.sbt b/build.sbt index 49376c8..4034e7a 100644 --- a/build.sbt +++ b/build.sbt @@ -154,24 +154,26 @@ lazy val docs = nativeProject("docs") NativeBinding(docsUsingBindingsDirectory.value / "vector.h") .name("vector") .link("vector") - .packageName("org.example"), - NativeBinding(docs3rdPartyBindingsDirectory.value / "geometry.h") - .name("Geometry") - .link("geometry") - .packageName("org.example.geometry") - .bindingConfig(docs3rdPartyBindingsDirectory.value / "config.json"), { - val pathToHeader = docsScalaNativeBindingsDirectory.value / "wordcount.h" - val pathToConfig = docsScalaNativeBindingsDirectory.value / "config.json" + .packageName("org.example"), { + val pathToHeader = docs3rdPartyBindingsDirectory.value / "geometry.h" + val pathToConfig = docs3rdPartyBindingsDirectory.value / "config.json" //#sbt-binding-config NativeBinding(pathToHeader) - //#sbt-binding-config + .bindingConfig(pathToConfig) + //#sbt-binding-config + .name("Geometry") + .link("geometry") + .packageName("org.example.geometry") + }, { + val pathToHeader = docsScalaNativeBindingsDirectory.value / "wordcount.h" + //#sbt-exclude-prefix + NativeBinding(pathToHeader) + .excludePrefix("__") + //#sbt-exclude-prefix .name("WordCount") .link("wordcount") .packageName("org.example.wordcount") - .excludePrefix("__") - //#sbt-binding-config - .bindingConfig(pathToConfig) - //#sbt-binding-config + .bindingConfig(docsScalaNativeBindingsDirectory.value / "config.json") } ), nativeBindgenPath := { diff --git a/docs/src/paradox/configuration.md b/docs/src/paradox/configuration.md index 60aa96d..03cff95 100644 --- a/docs/src/paradox/configuration.md +++ b/docs/src/paradox/configuration.md @@ -11,7 +11,7 @@ or as part of the sbt bindings declaration. Definitions may be excluded by their prefix. This is useful when private definitions should not be part of the generated binding. This is often the case for definitions starting with `__`. sbt -: @@snip [sbt] (../../../sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/build.sbt) { #sbt-exclude-prefix } +: @@snip [sbt] (../../../build.sbt) { #sbt-exclude-prefix } CLI : ```sh @@ -62,7 +62,7 @@ will reference the `Point` type alias inside the `Vector` object: ### Using Types From the Scala Native Bindings -Similar to the above, the following this example shows how you can use +Similar to the above, the following example shows how you can use types defined in the [C standard library] and [C POSIX library] bindings shipped with Scala Native. Let's assume we have a binding with a method that uses the `FILE` type from ``: diff --git a/sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/build.sbt b/sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/build.sbt index 9e23ced..6599621 100644 --- a/sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/build.sbt +++ b/sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/build.sbt @@ -11,14 +11,10 @@ nativeBindgenPath := Some(file(System.getProperty("bindgen.path"))) inConfig(Compile)( Def.settings( nativeBindings += { - //#sbt-exclude-prefix NativeBinding(resourceDirectory.value / "stdlib.h") - //#sbt-exclude-prefix .name("stdlib") .packageName("org.example.app.stdlib") - //#sbt-exclude-prefix .excludePrefix("__") - //#sbt-exclude-prefix } )) //#example