Skip to content

Commit 383da84

Browse files
authored
Merge pull request #182 from scala-native/binding-config-docs
Improve the binding configuration documentation
2 parents 634c370 + 4ba6d11 commit 383da84

File tree

21 files changed

+426
-49
lines changed

21 files changed

+426
-49
lines changed

build.sbt

+34-5
Original file line numberDiff line numberDiff line change
@@ -138,23 +138,52 @@ lazy val sbtPlugin = project("sbt-scala-native-bindgen", ScriptedPlugin)
138138
publishLocal := publishLocal.dependsOn(tools / publishLocal).value
139139
)
140140

141+
lazy val docsUsingBindingsDirectory = settingKey[File]("vector source")
142+
lazy val docs3rdPartyBindingsDirectory = settingKey[File]("geometry source")
143+
lazy val docsScalaNativeBindingsDirectory = settingKey[File]("wordcount source")
144+
141145
lazy val docs = nativeProject("docs")
142146
.enablePlugins(GhpagesPlugin, ParadoxSitePlugin, ParadoxMaterialThemePlugin)
143147
.enablePlugins(ScalaNativeBindgenPlugin)
144148
.settings(
145149
publish / skip := true,
146-
Test / nativeBindings += {
147-
NativeBinding((Test / resourceDirectory).value / "vector.h")
150+
docsUsingBindingsDirectory := (Test / resourceDirectory).value / "using-bindings",
151+
docs3rdPartyBindingsDirectory := (Test / resourceDirectory).value / "3rd-party-bindings",
152+
docsScalaNativeBindingsDirectory := (Test / resourceDirectory).value / "scala-native-bindings",
153+
Test / nativeBindings ++= Seq(
154+
NativeBinding(docsUsingBindingsDirectory.value / "vector.h")
148155
.name("vector")
149156
.link("vector")
150-
.packageName("org.example")
151-
},
157+
.packageName("org.example"), {
158+
val pathToHeader = docs3rdPartyBindingsDirectory.value / "geometry.h"
159+
val pathToConfig = docs3rdPartyBindingsDirectory.value / "config.json"
160+
//#sbt-binding-config
161+
NativeBinding(pathToHeader)
162+
.bindingConfig(pathToConfig)
163+
//#sbt-binding-config
164+
.name("Geometry")
165+
.link("geometry")
166+
.packageName("org.example.geometry")
167+
}, {
168+
val pathToHeader = docsScalaNativeBindingsDirectory.value / "wordcount.h"
169+
//#sbt-exclude-prefix
170+
NativeBinding(pathToHeader)
171+
.excludePrefix("__")
172+
//#sbt-exclude-prefix
173+
.name("WordCount")
174+
.link("wordcount")
175+
.packageName("org.example.wordcount")
176+
.bindingConfig(docsScalaNativeBindingsDirectory.value / "config.json")
177+
}
178+
),
152179
nativeBindgenPath := {
153180
Some(
154181
(ThisBuild / baseDirectory).value / "bindgen/target/scala-native-bindgen")
155182
},
156183
Test / nativeBindgen / target := (Test / scalaSource).value / "org/example",
157-
compileTask("vector", Test / resourceDirectory),
184+
compileTask("vector", docsUsingBindingsDirectory),
185+
compileTask("geometry", docs3rdPartyBindingsDirectory),
186+
compileTask("wordcount", docsScalaNativeBindingsDirectory),
158187
libraryDependencies += "org.scalatest" %%% "scalatest" % "3.2.0-SNAP10" % Test,
159188
Paradox / paradoxProperties ++= Map(
160189
"github.base_url" -> scmInfo.value.get.browseUrl.toString

docs/src/paradox/cli.md

+4-4
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ The generated bindings can be configured using the different options and it is a
4545

4646
| Option | Description
4747
|----------------------|---------------------------------------------------------------------------------|
48-
| `--link` | Library to link with, e.g. `--link` uv for libuv.
48+
| `--link` | Library to link with, e.g. `--link uv` for libuv.
4949
| `--no-link` | Library does not require linking.
50-
| `--name` | Scala object name that contains bindings. Default value set to library name.
50+
| `--name` | Scala object name that contains bindings. Defaults to the library name.
5151
| `--package` | Package name of generated Scala file.
52-
| `--exclude-prefix` | Functions and unused typedefs will be removed if their names have given prefix.
53-
| `--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.
52+
| `--exclude-prefix` | Functions and unused typedefs will be removed if their names have the given prefix.
53+
| `--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.
5454
| `--extra-arg` | Additional argument to append to the compiler command line.
5555
| `--extra-arg-before` | Additional argument to prepend to the compiler command line.

docs/src/paradox/configuration.md

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Configuration
2+
3+
Bindgen provides several options for configuring what to include and how
4+
to output the generated bindings. The options can be provided to the CLI
5+
or as part of the sbt bindings declaration.
6+
7+
@@ toc
8+
9+
## Excluding Definitions By Prefix
10+
11+
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 `__`.
12+
13+
sbt
14+
: @@snip [sbt] (../../../build.sbt) { #sbt-exclude-prefix }
15+
16+
CLI
17+
: ```sh
18+
scala-native-bindgen --binding-config=/path/to/config
19+
```
20+
21+
To exemplify consider the following header file:
22+
23+
@@snip [sbt] (../../../sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/src/main/resources/stdlib.h)
24+
25+
When the exclude prefix is set to `__`, then the resulting bindings will be:
26+
27+
@@snip [sbt] (../../../sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/expected/stdlib.scala)
28+
29+
## Binding Configuration File
30+
31+
The binding configuration is a JSON file which allows to map the path of
32+
a header file to the associated object as well as the names of the C
33+
types and symbols to their respective Scala types and definitions. The
34+
configuration file can be used when integrating with third party
35+
bindings.
36+
37+
sbt
38+
: @@snip [sbt-binding-config](../../../build.sbt) { #sbt-binding-config }
39+
40+
CLI
41+
: ```sh
42+
scala-native-bindgen --binding-config=/path/to/config
43+
```
44+
45+
### Using Types From Third Party Bindings
46+
47+
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`:
48+
49+
@@snip [Vector] (../test/scala/com/example/custom/binding/Vector.scala) { #example }
50+
51+
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:
52+
53+
@@snip [vector.h] (../test/resources/3rd-party-bindings/config.json)
54+
55+
Now in the library you are creating a binding for, any usage of `struct point`:
56+
57+
@@snip [vector.h] (../test/resources/3rd-party-bindings/geometry.h) { #using-struct-point }
58+
59+
will reference the `Point` type alias inside the `Vector` object:
60+
61+
@@snip [Geometry] (../test/scala/org/example/Geometry.scala) { #using-struct-point }
62+
63+
### Using Types From the Scala Native Bindings
64+
65+
Similar to the above, the following example shows how you can use
66+
types defined in the [C standard library] and [C POSIX library] bindings
67+
shipped with Scala Native. Let's assume we have a binding with a method that uses the `FILE` type
68+
from `<stdio.h>`:
69+
70+
@@snip [vector.h] (../test/resources/scala-native-bindings/wordcount.h) { #using-stdio-file }
71+
72+
We can then write a binding configuration that maps the header name to the object defined in Scala Native.
73+
74+
@@snip [vector.h] (../test/resources/scala-native-bindings/config.json)
75+
76+
@@@ note
77+
78+
In the above binding configuration, we duplicate the mapping to work on both Linux and macOS since on macOS
79+
the definition of `FILE` is found inside `/usr/include/_stdio.h`.
80+
81+
@@@
82+
83+
The generated binding will then use the `stdio.h` binding provided by Scala Native:
84+
85+
@@snip [WordCount] (../test/scala/org/example/WordCount.scala) { #using-stdio-file }
86+
87+
And we can use the binding by opening a file using `fopen(...)`:
88+
89+
@@snip [WordCountSpec] (../test/scala/org/scalanative/bindgen/docs/WordCountSpec.scala) { #example }
90+
91+
[Scala Native memory management]: http://www.scala-native.org/en/latest/user/interop.html#memory-management
92+
[Scala Native memory layout types]: http://www.scala-native.org/en/latest/user/interop.html#memory-layout-types
93+
[C standard library]: http://www.scala-native.org/en/latest/lib/libc.html
94+
[C POSIX library]: http://www.scala-native.org/en/latest/lib/posixlib.html

docs/src/paradox/index.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ This project is distributed under the Scala license.
2929

3030
* [sbt](sbt.md)
3131
* [cli](cli.md)
32-
* [Using Generated Bindings](using-generated-bindings.md)
33-
* [Integrating Bindings](integrating-bindings.md)
32+
* [usage](using-bindings.md)
33+
* [configuration](configuration.md)
3434
* [Limitations](limitations.md)
3535
* [bindings](bindings/index.md)
3636
* [Contributor's Guide](contrib/index.md)

docs/src/paradox/integrating-bindings.md

-27
This file was deleted.

docs/src/paradox/sbt.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,6 @@ Given that the `stdlib.h` header file contains:
2828

2929
@@snip [sbt] (../../../sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/src/main/resources/stdlib.h)
3030

31-
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:
31+
Running `nativeBindgen` will generate a file named `target/scala-2.11/src_managed/main/sbt-scala-native-bindgen/stdlib.scala` containing the following lines:
3232

3333
@@snip [sbt] (../../../sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/expected/stdlib.scala)

docs/src/paradox/using-generated-bindings.md renamed to docs/src/paradox/using-bindings.md

+14-8
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,30 @@
1-
# Using Generated Bindings
1+
# Using Bindings
2+
3+
The following explains how to use bindings generated with Scala Native Bindgen through several examples.
4+
5+
## A Simple Vector Library
26

37
Consider following header file:
48

5-
@@snip [vector.h] (../test/resources/vector.h)
9+
@@snip [vector.h] (../test/resources/using-bindings/vector.h)
610

711
Bindgen will output
12+
813
* type aliases for the structs
914
* binding for function `cosine`
1015
* helper functions that make usage of structs easier
1116

1217
@@snip [vector.h] (../test/scala/org/example/vector.scala)
1318

14-
Let's write code that creates two line segments and prints angel between them.
19+
## Using the Vector Library
20+
21+
Let's write code that creates two line segments and calculates the angel between them.
1522

1623
First we need to create points. We will use `native.Zone` to
1724
allocate struct (more information on memory management can be found
1825
here: [Scala Native memory management]).
1926

20-
Generated bindings contain helper functions that make struct allocation easier.
27+
The generated bindings contain helper functions that make struct allocation easier.
2128
To import them use `import org.example.vector._`
2229

2330
Let's create two points and the first line segment:
@@ -39,17 +46,16 @@ to your code:
3946

4047
@@@ note
4148

42-
Note that `struct_lineSegment` contains fields of value type `struct_point`
49+
`struct_lineSegment` contains fields of value type `struct_point`
4350
but setters accept variables of type `native.Ptr[struct_point]`.
44-
It helps to avoid Scala Native limitation that does not allow passing structs
51+
The reason is that Scala Native does not allow passing structs
4552
and arrays by value (see @github[scala-native/scala-native#555](scala-native/scala-native#555)).
4653

4754
@@@
4855

49-
Now we can calculate angel between line segments:
56+
Now we can calculate the angel between the line segments:
5057

5158
@@snip [step-3] (../test/scala/org/scalanative/bindgen/docs/VectorSpec.scala) { #step-3 }
5259

53-
5460
[Scala Native memory management]: http://www.scala-native.org/en/latest/user/interop.html#memory-management
5561
[Scala Native memory layout types]: http://www.scala-native.org/en/latest/user/interop.html#memory-layout-types
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"vector.h": {
3+
"object": "com.example.custom.binding.Vector",
4+
"names": {
5+
"struct point": "Point",
6+
"struct lineSegment": "LineSegment"
7+
}
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#include "geometry.h"
2+
#include <math.h>
3+
4+
float circle_area(struct circle *circle) {
5+
return pow(circle->radius, 2) * M_PI;
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#include "../using-bindings/vector.h"
2+
3+
//#using-struct-point
4+
struct circle {
5+
struct point point;
6+
double radius;
7+
};
8+
//#using-struct-point
9+
10+
float circle_area(struct circle *circle);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"stdio.h": {
3+
"object": "scala.scalanative.native.stdio"
4+
},
5+
"_stdio.h": {
6+
"object": "scala.scalanative.native.stdio"
7+
}
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#include "wordcount.h"
2+
#include <ctype.h>
3+
#include <string.h>
4+
5+
int wordcount(struct wordcount *wordcount, FILE *file) {
6+
char buf[4096];
7+
8+
// FIXME: This doesn't seem to have any effect
9+
// memset(wordcount, sizeof(*wordcount), 0);
10+
11+
wordcount->chars = 0;
12+
wordcount->words = 0;
13+
wordcount->lines = 0;
14+
15+
while (fgets(buf, sizeof(buf), file)) {
16+
char *pos;
17+
int in_word = 0;
18+
19+
wordcount->lines++;
20+
wordcount->chars += strlen(buf);
21+
22+
for (pos = buf; *pos; pos++) {
23+
if (isspace(*pos)) {
24+
if (in_word) {
25+
wordcount->words++;
26+
}
27+
in_word = 0;
28+
} else {
29+
in_word = 1;
30+
}
31+
}
32+
33+
if (in_word) {
34+
wordcount->words++;
35+
}
36+
}
37+
38+
return 0;
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#include <stdio.h>
2+
3+
struct wordcount {
4+
long chars;
5+
long lines;
6+
long words;
7+
};
8+
9+
//#using-stdio-file
10+
int wordcount(struct wordcount *wordcount, FILE *file);
11+
//#using-stdio-file

0 commit comments

Comments
 (0)