Skip to content

Improve the binding configuration documentation #182

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 3, 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
39 changes: 34 additions & 5 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -138,23 +138,52 @@ 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"), {
val pathToHeader = docs3rdPartyBindingsDirectory.value / "geometry.h"
val pathToConfig = docs3rdPartyBindingsDirectory.value / "config.json"
//#sbt-binding-config
NativeBinding(pathToHeader)
.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")
.bindingConfig(docsScalaNativeBindingsDirectory.value / "config.json")
}
),
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
Expand Down
8 changes: 4 additions & 4 deletions docs/src/paradox/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
| `--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.
| `--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:[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.
94 changes: 94 additions & 0 deletions docs/src/paradox/configuration.md
Original file line number Diff line number Diff line change
@@ -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] (../../../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 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 `<stdio.h>`:

@@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
4 changes: 2 additions & 2 deletions docs/src/paradox/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
27 changes: 0 additions & 27 deletions docs/src/paradox/integrating-bindings.md

This file was deleted.

2 changes: 1 addition & 1 deletion docs/src/paradox/sbt.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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
9 changes: 9 additions & 0 deletions docs/src/test/resources/3rd-party-bindings/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"vector.h": {
"object": "com.example.custom.binding.Vector",
"names": {
"struct point": "Point",
"struct lineSegment": "LineSegment"
}
}
}
6 changes: 6 additions & 0 deletions docs/src/test/resources/3rd-party-bindings/geometry.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#include "geometry.h"
#include <math.h>

float circle_area(struct circle *circle) {
return pow(circle->radius, 2) * M_PI;
}
10 changes: 10 additions & 0 deletions docs/src/test/resources/3rd-party-bindings/geometry.h
Original file line number Diff line number Diff line change
@@ -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);
8 changes: 8 additions & 0 deletions docs/src/test/resources/scala-native-bindings/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"stdio.h": {
"object": "scala.scalanative.native.stdio"
},
"_stdio.h": {
"object": "scala.scalanative.native.stdio"
}
}
39 changes: 39 additions & 0 deletions docs/src/test/resources/scala-native-bindings/wordcount.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "wordcount.h"
#include <ctype.h>
#include <string.h>

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;
}
11 changes: 11 additions & 0 deletions docs/src/test/resources/scala-native-bindings/wordcount.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <stdio.h>

struct wordcount {
long chars;
long lines;
long words;
};

//#using-stdio-file
int wordcount(struct wordcount *wordcount, FILE *file);
//#using-stdio-file
Loading