Skip to content

Commit 5c12773

Browse files
authored
Merge branch 'master' into opaque-type
2 parents c244c88 + 2a40276 commit 5c12773

File tree

15 files changed

+398
-31
lines changed

15 files changed

+398
-31
lines changed

bindgen/Utils.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <clang/AST/AST.h>
55

6+
#include "ir/TypeDef.h"
67
#include "ir/types/Type.h"
78
#include <algorithm>
89
#include <cctype>
@@ -108,6 +109,17 @@ static inline std::string replaceChar(const std::string &str,
108109
return std::string(str).replace(f, c1.length(), c2);
109110
}
110111
return std::string(str);
112+
113+
/**
114+
* Types may be wrapper in a chain of typedefs.
115+
* @return true if given type is of type T or is an alias for type T.
116+
*/
117+
template <typename T> static inline bool isAliasForType(Type *type) {
118+
if (isInstanceOf<TypeDef>(type)) {
119+
auto *typeDef = dynamic_cast<TypeDef *>(type);
120+
return isAliasForType<T>(typeDef->getType().get());
121+
}
122+
return isInstanceOf<T>(type);
111123
}
112124

113125
#endif // UTILS_H

bindgen/ir/Struct.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ std::string Field::generateSetter(int fieldIndex) {
1111
std::string setter = handleReservedWords(getName(), "_=");
1212
std::string parameterType = type->str();
1313
std::string value = "value";
14-
if (isInstanceOf<ArrayType>(type.get()) ||
15-
isInstanceOf<Struct>(type.get())) {
14+
if (isAliasForType<ArrayType>(type.get()) ||
15+
isAliasForType<Struct>(type.get())) {
1616
parameterType = "native.Ptr[" + parameterType + "]";
1717
value = "!" + value;
1818
}
@@ -26,8 +26,8 @@ std::string Field::generateGetter(int fieldIndex) {
2626
std::string getter = handleReservedWords(getName());
2727
std::string returnType = type->str();
2828
std::string methodBody;
29-
if (isInstanceOf<ArrayType>(type.get()) ||
30-
isInstanceOf<Struct>(type.get())) {
29+
if (isAliasForType<ArrayType>(type.get()) ||
30+
isAliasForType<Struct>(type.get())) {
3131
returnType = "native.Ptr[" + returnType + "]";
3232
methodBody = "p._" + std::to_string(fieldIndex + 1);
3333
} else {

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import java.nio.file.Path
55
inThisBuild(
66
Def.settings(
77
organization := "org.scalanative.bindgen",
8-
version := "0.1-SNAPSHOT",
8+
version := "0.2-SNAPSHOT",
99
scalaVersion := "2.11.12",
1010
scalacOptions ++= Seq(
1111
"-deprecation",

docs/_config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
theme: jekyll-theme-minimal

docs/index.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Scala Native Bindgen
2+
3+
This tool generates Scala Native bindings from C headers.
4+
It's built upon clang and [LibTooling] and thus respects the conventions of clang-tools.
5+
6+
[LibTooling]: https://clang.llvm.org/docs/LibTooling.html

docs/obtaining-bindgen.md

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Obtaining bindgen
2+
3+
There are 2 ways to obtain bindgen:
4+
* [Use docker container](#docker-container)
5+
* [Build binary with CMake](#building-with-cmake)
6+
* [Build binary with docker-compose](#building-with-docker-compose)
7+
8+
## Docker container
9+
10+
This option requires [Docker].
11+
12+
Download docker image with the binary:
13+
14+
```sh
15+
docker pull scalabindgen/scala-native-bindgen
16+
```
17+
18+
Mount directories with required header files and run bindgen:
19+
20+
```sh
21+
docker run -v "$(pwd)":/src -v /usr/include:/usr/include \
22+
--rm scalabindgen/scala-native-bindgen \
23+
relative/path/to/my_header.h --name my_header --
24+
```
25+
26+
The docker image does not contain standard headers so it is important to
27+
mount all system include directories that are used by the header file
28+
passed to `scala-native-bindgen`. See the [docker-bindgen.sh] script for
29+
how to wrap the dockerized program. The `$CWD` of the container is
30+
`/src` which should be mounted from `$(pwd)` in case relative paths are
31+
used.
32+
33+
Note, the `scalabindgen/scala-native-bindgen:latest` image is updated on
34+
each merge to the `master` branch.
35+
36+
[Docker]: https://www.docker.com/
37+
[docker-bindgen.sh]: scripts/docker-bindgen.sh
38+
39+
## Building with `CMake`
40+
41+
Building `scala-native-bindgen` requires the following tools and libraries:
42+
43+
- [CMake] version 3.9 or higher
44+
- [LLVM] and [Clang] version 5.0 or 6.0.
45+
46+
```sh
47+
# On apt-based systems
48+
apt install cmake clang-$LLVM_VERSION libclang-$LLVM_VERSION-dev llvm-$LLVM_VERSION-dev
49+
50+
# With `brew`
51+
brew install cmake llvm@6
52+
```
53+
54+
To run the tests you also need the required Scala Native libraries.
55+
See the [Scala Native setup guide] for instructions on installing the dependencies.
56+
57+
```sh
58+
mkdir -p bindgen/target
59+
cd bindgen/target
60+
cmake ..
61+
make
62+
./scala-native-bindgen --name ctype /usr/include/ctype.h --
63+
```
64+
65+
To build a statically linked executable pass `-DSTATIC_LINKING=ON` when invoking `cmake`:
66+
67+
```sh
68+
cmake -DSTATIC_LINKING=ON ..
69+
```
70+
71+
Additional compiler and linker flags may be passed as environment variable sor their CMake
72+
equivalent, e.g. to compile with debug symbols the following are the same:
73+
74+
```sh
75+
cmake -DCMAKE_CXX_FLAGS=-g ..
76+
CXXFLAGS=-g cmake ..
77+
```
78+
79+
## Building with `docker-compose`
80+
81+
Alternatively, you can use [docker-compose] to build and test the program:
82+
83+
```sh
84+
# Build the docker image with LLVM version 6.0.
85+
docker-compose build ubuntu-18.04-llvm-6.0
86+
# Build the bindgen tool and run the tests.
87+
docker-compose run --rm ubuntu-18.04-llvm-6.0 ./script/test.sh
88+
# Run the bindgen tool inside the container.
89+
docker-compose run --rm ubuntu-18.04-llvm-6.0 \
90+
bindgen/target/scala-native-bindgen --name union tests/samples/Union.h --
91+
```
92+
93+
[CMake]: https://cmake.org/
94+
[LLVM]: https://llvm.org/
95+
[Clang]: https://clang.llvm.org/
96+
[Scala Native setup guide]: http://www.scala-native.org/en/latest/user/setup.html
97+
[docker-compose]: https://docs.docker.com/compose/
98+
99+
## Testing
100+
101+
The tests assume that the above instructions for building `scala-native-bindgen` from source
102+
has been followed.
103+
104+
```sh
105+
cd tests
106+
sbt test
107+
```

tests/samples/ReservedWords.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ object ReservedWordsHelpers {
3737
implicit class struct_finally_ops(val p: native.Ptr[struct_finally]) extends AnyVal {
3838
def `val`: `def` = !p._1
3939
def `val_=`(value: `def`): Unit = !p._1 = value
40-
def `finally`: `lazy` = !p._2
41-
def `finally_=`(value: `lazy`): Unit = !p._2 = value
40+
def `finally`: native.Ptr[`lazy`] = p._2
41+
def `finally_=`(value: native.Ptr[`lazy`]): Unit = !p._2 = !value
4242
}
4343

4444
def struct_finally()(implicit z: native.Zone): native.Ptr[struct_finally] = native.alloc[struct_finally]

tests/samples/Struct.c

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,27 @@
11
#include "Struct.h"
22
#include <stdlib.h>
33

4-
point_s getPoint() {
4+
void setPoints(struct points *points, int x1, int y1, int x2, int y2) {
5+
points->p1.x = x1;
6+
points->p1.y = y1;
7+
points->p2.x = x2;
8+
points->p2.y = y2;
9+
}
10+
11+
int getPoint(struct points *points, enum pointIndex pointIndex) {
12+
switch (pointIndex) {
13+
case X1:
14+
return points->p1.x;
15+
case X2:
16+
return points->p2.x;
17+
case Y1:
18+
return points->p1.y;
19+
case Y2:
20+
return points->p2.y;
21+
}
22+
}
23+
24+
point_s createPoint() {
525
point_s point = malloc(sizeof(struct point));
626
point->x = 10;
727
point->y = 20;

tests/samples/Struct.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,22 @@ struct point {
33
int y;
44
};
55

6+
typedef struct point point;
7+
8+
struct points {
9+
struct point p1;
10+
point p2;
11+
};
12+
13+
enum pointIndex { X1, Y1, X2, Y2 };
14+
15+
void setPoints(struct points *points, int x1, int y1, int x2, int y2);
16+
17+
int getPoint(struct points *points, enum pointIndex pointIndex);
18+
619
typedef struct point *point_s;
720

8-
point_s getPoint();
21+
point_s createPoint();
922

1023
struct bigStruct {
1124
long one;

tests/samples/Struct.scala

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,29 @@ import scala.scalanative.native._
77
@native.extern
88
object Struct {
99
type struct_point = native.CStruct2[native.CInt, native.CInt]
10+
type point = struct_point
11+
type struct_points = native.CStruct2[struct_point, point]
12+
type enum_pointIndex = native.CUnsignedInt
1013
type point_s = native.Ptr[struct_point]
1114
type struct_bigStruct = native.CArray[Byte, native.Nat.Digit[native.Nat._1, native.Nat.Digit[native.Nat._1, native.Nat._2]]]
1215
type struct_structWithAnonymousStruct = native.CStruct2[native.CInt, native.CArray[Byte, native.Nat._8]]
13-
def getPoint(): native.Ptr[struct_point] = native.extern
16+
def setPoints(points: native.Ptr[struct_points], x1: native.CInt, y1: native.CInt, x2: native.CInt, y2: native.CInt): Unit = native.extern
17+
def getPoint(points: native.Ptr[struct_points], pointIndex: enum_pointIndex): native.CInt = native.extern
18+
def createPoint(): native.Ptr[struct_point] = native.extern
1419
def getBigStructSize(): native.CInt = native.extern
1520
def getCharFromAnonymousStruct(s: native.Ptr[struct_structWithAnonymousStruct]): native.CChar = native.extern
1621
def getIntFromAnonymousStruct(s: native.Ptr[struct_structWithAnonymousStruct]): native.CChar = native.extern
1722
}
1823

1924
import Struct._
2025

26+
object StructEnums {
27+
final val enum_pointIndex_X1: enum_pointIndex = 0.toUInt
28+
final val enum_pointIndex_Y1: enum_pointIndex = 1.toUInt
29+
final val enum_pointIndex_X2: enum_pointIndex = 2.toUInt
30+
final val enum_pointIndex_Y2: enum_pointIndex = 3.toUInt
31+
}
32+
2133
object StructHelpers {
2234

2335
implicit class struct_point_ops(val p: native.Ptr[struct_point]) extends AnyVal {
@@ -29,6 +41,15 @@ object StructHelpers {
2941

3042
def struct_point()(implicit z: native.Zone): native.Ptr[struct_point] = native.alloc[struct_point]
3143

44+
implicit class struct_points_ops(val p: native.Ptr[struct_points]) extends AnyVal {
45+
def p1: native.Ptr[struct_point] = p._1
46+
def p1_=(value: native.Ptr[struct_point]): Unit = !p._1 = !value
47+
def p2: native.Ptr[point] = p._2
48+
def p2_=(value: native.Ptr[point]): Unit = !p._2 = !value
49+
}
50+
51+
def struct_points()(implicit z: native.Zone): native.Ptr[struct_points] = native.alloc[struct_points]
52+
3253
implicit class struct_structWithAnonymousStruct_ops(val p: native.Ptr[struct_structWithAnonymousStruct]) extends AnyVal {
3354
def a: native.CInt = !p._1
3455
def a_=(value: native.CInt): Unit = !p._1 = value

tests/samples/Union.c

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,55 @@
11
#include "Union.h"
22
#include <stdlib.h>
3+
#include <string.h>
34

4-
void setIntValue(union values *v) { v->i = 10; }
5+
int union_get_sizeof() { return sizeof(union values); }
56

6-
void setLongValue(union values *v) { v->l = 10000000000; }
7+
int union_test_int(union values *v, enum union_op op, int value) {
8+
switch (op) {
9+
case UNION_SET:
10+
v->i = value;
11+
return 1;
12+
case UNION_TEST:
13+
return v->i == value;
14+
}
15+
}
716

8-
int getUnionSize() { return sizeof(union values); }
17+
int union_test_long(union values *v, enum union_op op, long value) {
18+
switch (op) {
19+
case UNION_SET:
20+
v->l = value;
21+
return 1;
22+
case UNION_TEST:
23+
return v->l == value;
24+
}
25+
}
26+
27+
int union_test_long_long(union values *v, enum union_op op, long long value) {
28+
switch (op) {
29+
case UNION_SET:
30+
v->ll = value;
31+
return 1;
32+
case UNION_TEST:
33+
return v->ll == value;
34+
}
35+
}
36+
37+
int union_test_double(union values *v, enum union_op op, double value) {
38+
switch (op) {
39+
case UNION_SET:
40+
v->d = value;
41+
return 1;
42+
case UNION_TEST:
43+
return v->d == value;
44+
}
45+
}
46+
47+
int union_test_string(union values *v, enum union_op op, const char *value) {
48+
switch (op) {
49+
case UNION_SET:
50+
v->s = value;
51+
return 1;
52+
case UNION_TEST:
53+
return v->s == value || !strcmp(v->s, value);
54+
}
55+
}

tests/samples/Union.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@ union values {
22
long l;
33
int i;
44
long long ll;
5+
double d;
6+
const char *s;
57
};
68

7-
void setIntValue(union values *v);
9+
enum union_op { UNION_SET, UNION_TEST };
810

9-
void setLongValue(union values *v);
11+
int union_get_sizeof();
1012

11-
int getUnionSize();
13+
int union_test_int(union values *v, enum union_op op, int value);
14+
int union_test_long(union values *v, enum union_op op, long value);
15+
int union_test_long_long(union values *v, enum union_op op, long long value);
16+
int union_test_double(union values *v, enum union_op op, double value);
17+
int union_test_string(union values *v, enum union_op op, const char *value);

0 commit comments

Comments
 (0)