├── bindgen ├── .dockerignore ├── ir │ ├── Define.cpp │ ├── LocatableType.cpp │ ├── Location.cpp │ ├── PossibleVarDefine.cpp │ ├── Define.h │ ├── Location.h │ ├── Variable.cpp │ ├── Variable.h │ ├── PossibleVarDefine.h │ ├── LocatableType.h │ ├── VarDefine.cpp │ ├── VarDefine.h │ ├── LiteralDefine.h │ ├── LiteralDefine.cpp │ ├── types │ │ ├── Type.cpp │ │ ├── ArrayType.h │ │ ├── PrimitiveType.h │ │ ├── PointerType.h │ │ ├── PrimitiveType.cpp │ │ ├── FunctionPointerType.h │ │ ├── ArrayType.cpp │ │ ├── PointerType.cpp │ │ └── Type.h │ ├── TypeAndName.h │ ├── TypeAndName.cpp │ ├── LocationManager.h │ ├── Union.h │ ├── Enum.h │ ├── Record.cpp │ ├── Record.h │ ├── Function.h │ ├── Enum.cpp │ ├── TypeDef.h │ ├── Union.cpp │ ├── Function.cpp │ └── TypeDef.cpp ├── defines │ ├── DefineFinderActionFactory.cpp │ ├── DefineFinderAction.cpp │ ├── DefineFinderAction.h │ ├── DefineFinderActionFactory.h │ └── DefineFinder.h ├── visitor │ ├── ScalaFrontendActionFactory.cpp │ ├── ScalaFrontendAction.cpp │ ├── TreeConsumer.h │ ├── ScalaFrontendActionFactory.h │ ├── ScalaFrontendAction.h │ └── TreeVisitor.h ├── Dockerfile ├── TypeTranslator.h ├── CMakeLists.txt ├── Main.cpp └── Utils.h ├── project ├── build.properties ├── BindingHelpers.scala ├── plugins.sbt └── ParadoxSupport.scala ├── docs └── src │ ├── paradox │ ├── contrib │ │ ├── guidelines.md │ │ ├── index.md │ │ ├── docker-compose.md │ │ ├── releasing.md │ │ ├── cmake.md │ │ └── bindings.md │ ├── bindings │ │ ├── index.md │ │ ├── utf8proc.md │ │ ├── iconv.md │ │ └── posix.md │ ├── index.md │ ├── limitations.md │ ├── sbt.md │ ├── using-bindings.md │ ├── cli.md │ └── configuration.md │ └── test │ ├── resources │ ├── 3rd-party-bindings │ │ ├── geometry.c │ │ ├── config.json │ │ └── geometry.h │ ├── scala-native-bindings │ │ ├── config.json │ │ ├── wordcount.h │ │ └── wordcount.c │ └── using-bindings │ │ ├── vector.h │ │ └── vector.c │ └── scala │ ├── org │ ├── scalanative │ │ └── bindgen │ │ │ └── docs │ │ │ ├── GeometrySpec.scala │ │ │ ├── VectorSpec.scala │ │ │ └── WordCountSpec.scala │ └── example │ │ ├── Geometry.scala │ │ ├── WordCount.scala │ │ └── vector.scala │ └── com │ └── example │ └── custom │ └── binding │ └── Vector.scala ├── .gitignore ├── tests ├── samples │ ├── native.h │ ├── VarDefine.c │ ├── IncludesHeader.h │ ├── include │ │ ├── OpaqueTypes.h │ │ ├── CustomNames.h │ │ └── included.h │ ├── Extern.c │ ├── Extern.h │ ├── Enum.c │ ├── Typedef.h │ ├── AnonymousTypes.h │ ├── ReuseBindings.json │ ├── native.scala │ ├── VarDefine.h │ ├── src │ │ └── test │ │ │ └── scala │ │ │ └── org │ │ │ └── scalanative │ │ │ └── bindgen │ │ │ └── samples │ │ │ ├── VarDefineSpec.scala │ │ │ ├── EnumSpec.scala │ │ │ ├── ExternSpec.scala │ │ │ ├── FunctionSpec.scala │ │ │ └── UnionSpec.scala │ ├── Enum.h │ ├── ReuseBindings.h │ ├── ReservedWords.h │ ├── CustomNames.scala │ ├── Function.h │ ├── VarDefine.scala │ ├── Function.c │ ├── Union.h │ ├── Typedef.scala │ ├── LiteralDefine.scala │ ├── NativeTypes.h │ ├── PrivateMembers.h │ ├── NativeTypes.scala │ ├── OpaqueTypes.h │ ├── Extern.scala │ ├── Enum.scala │ ├── Function.scala │ ├── Union.c │ ├── LiteralDefine.h │ ├── Struct.c │ ├── Cycles.h │ ├── Struct.h │ ├── ReuseBindings.scala │ ├── IncludesHeader.scala │ ├── Union.scala │ ├── AnonymousTypes.scala │ └── ReservedWords.scala └── src │ └── test │ └── scala │ └── org │ └── scalanative │ └── bindgen │ ├── BindgenSpec.scala │ └── BindgenReportingSpec.scala ├── sbt-scala-native-bindgen └── src │ └── sbt-test │ └── bindgen │ └── generate │ ├── src │ └── main │ │ └── resources │ │ ├── core.h │ │ └── stdlib.h │ ├── project │ └── plugins.sbt │ ├── expected │ ├── core.scala │ └── stdlib.scala │ ├── build.sbt │ └── test ├── .clang-format ├── .scalafmt.conf ├── tools └── src │ └── main │ └── scala │ └── org │ └── scalanative │ └── bindgen │ ├── Bindings.scala │ ├── Bindgen.scala │ └── BindingOptions.scala ├── scripts ├── docker-bindgen.sh ├── test.sh ├── scalafmt ├── docker-test.sh ├── prepare-release.sh └── clangfmt ├── bindings ├── posix │ └── src │ │ ├── main │ │ └── scala │ │ │ └── org │ │ │ └── scalanative │ │ │ └── bindings │ │ │ └── posix │ │ │ ├── fnmatch.scala │ │ │ └── regex.scala │ │ └── test │ │ └── scala │ │ └── org │ │ └── scalanative │ │ └── bindings │ │ └── tests │ │ ├── FnmatchSpec.scala │ │ └── RegexSpec.scala ├── iconv │ └── src │ │ ├── main │ │ └── scala │ │ │ └── org │ │ │ └── scalanative │ │ │ └── bindings │ │ │ └── iconv.scala │ │ └── test │ │ └── scala │ │ └── org │ │ └── scalanative │ │ └── bindings │ │ └── tests │ │ └── IconvSpec.scala └── utf8proc │ └── src │ └── test │ └── scala │ └── org │ └── scalanative │ └── bindings │ └── tests │ └── Utf8procSpec.scala ├── docker-compose.yml ├── .travis.yml ├── Dockerfile ├── LICENSE.txt ├── CONTRIBUTING.md └── README.md /bindgen/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | target/ 3 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.2.3 2 | -------------------------------------------------------------------------------- /docs/src/paradox/contrib/guidelines.md: -------------------------------------------------------------------------------- 1 | ../../../../CONTRIBUTING.md -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | scripts/.coursier 3 | scripts/.scalafmt-* 4 | /scala-native-bindgen-* 5 | -------------------------------------------------------------------------------- /tests/samples/native.h: -------------------------------------------------------------------------------- 1 | void native(int); 2 | void nativeFunc(float); 3 | 4 | typedef int nativeFunc0; 5 | -------------------------------------------------------------------------------- /tests/samples/VarDefine.c: -------------------------------------------------------------------------------- 1 | #include "VarDefine.h" 2 | 3 | int a = 23; 4 | const int constInt = 10; 5 | const int *constIntPointer = 0; 6 | -------------------------------------------------------------------------------- /sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/src/main/resources/core.h: -------------------------------------------------------------------------------- 1 | int count_words(const char *text); 2 | void __not_excluded(void); -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # Documentation: https://clang.llvm.org/docs/ClangFormatStyleOptions.html 3 | BasedOnStyle: LLVM 4 | IndentWidth: 4 5 | --- 6 | Language: Cpp 7 | ... 8 | -------------------------------------------------------------------------------- /bindgen/ir/Define.cpp: -------------------------------------------------------------------------------- 1 | #include "Define.h" 2 | 3 | Define::Define(std::string name) : name(std::move(name)) {} 4 | 5 | std::string Define::getName() const { return name; } 6 | -------------------------------------------------------------------------------- /tests/samples/IncludesHeader.h: -------------------------------------------------------------------------------- 1 | #include "include/included.h" 2 | 3 | size getSize(struct document *d); 4 | 5 | struct courseInfo { 6 | char *name; 7 | enum semester s; 8 | }; 9 | -------------------------------------------------------------------------------- /sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin( 2 | "org.scala-native.bindgen" % "sbt-scala-native-bindgen" % sys.props( 3 | "plugin.version")) 4 | -------------------------------------------------------------------------------- /docs/src/test/resources/3rd-party-bindings/geometry.c: -------------------------------------------------------------------------------- 1 | #include "geometry.h" 2 | #include 3 | 4 | float circle_area(struct circle *circle) { 5 | return pow(circle->radius, 2) * M_PI; 6 | } 7 | -------------------------------------------------------------------------------- /docs/src/test/resources/scala-native-bindings/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "stdio.h": { 3 | "object": "scala.scalanative.native.stdio" 4 | }, 5 | "_stdio.h": { 6 | "object": "scala.scalanative.native.stdio" 7 | } 8 | } -------------------------------------------------------------------------------- /sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/src/main/resources/stdlib.h: -------------------------------------------------------------------------------- 1 | int access(const char *path, int mode); 2 | int read(int fildes, void *buf, int nbyte); 3 | int printf(const char *restrict format, ...); 4 | int __excluded(void); -------------------------------------------------------------------------------- /docs/src/test/resources/3rd-party-bindings/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "vector.h": { 3 | "object": "com.example.custom.binding.Vector", 4 | "names": { 5 | "struct point": "Point", 6 | "struct lineSegment": "LineSegment" 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /tests/samples/include/OpaqueTypes.h: -------------------------------------------------------------------------------- 1 | struct s; 2 | 3 | extern struct s externVar; // removed. No warning printed 4 | 5 | typedef struct undefinedIncludedStruct undefinedIncludedStruct; 6 | 7 | void useUndefinedIncludedStruct(undefinedIncludedStruct); 8 | -------------------------------------------------------------------------------- /tests/samples/Extern.c: -------------------------------------------------------------------------------- 1 | #include "Extern.h" 2 | 3 | int forty_two = 42; 4 | const char version[] = "0.1.0"; 5 | 6 | enum mode mode = USER; 7 | 8 | struct version semver_instance = {.major = 1, .minor = 2, .patch = 3}; 9 | struct version *semver = &semver_instance; -------------------------------------------------------------------------------- /docs/src/test/resources/using-bindings/vector.h: -------------------------------------------------------------------------------- 1 | struct point { 2 | float x; 3 | float y; 4 | }; 5 | 6 | struct lineSegment { 7 | struct point a; 8 | struct point b; 9 | }; 10 | 11 | float cosine(struct lineSegment *v1, struct lineSegment *v2); 12 | -------------------------------------------------------------------------------- /tests/samples/Extern.h: -------------------------------------------------------------------------------- 1 | extern int forty_two; 2 | extern const char version[]; 3 | 4 | enum mode { SYSTEM, USER }; 5 | extern enum mode mode; 6 | 7 | struct version { 8 | int major; 9 | int minor; 10 | int patch; 11 | }; 12 | extern struct version *semver; -------------------------------------------------------------------------------- /bindgen/ir/LocatableType.cpp: -------------------------------------------------------------------------------- 1 | #include "LocatableType.h" 2 | 3 | LocatableType::LocatableType(std::shared_ptr location) 4 | : location(std::move(location)) {} 5 | 6 | std::shared_ptr LocatableType::getLocation() const { 7 | return location; 8 | } 9 | -------------------------------------------------------------------------------- /tests/samples/Enum.c: -------------------------------------------------------------------------------- 1 | #include "Enum.h" 2 | 3 | enum days get_WEDNESDAY() { return WEDNESDAY; } 4 | 5 | char *check_BIG_NEG_A(enum bigNegativeValues big_neg_a) { 6 | if (big_neg_a == BIG_NEG_A) { 7 | return "OK"; 8 | } 9 | return "FAIL"; 10 | } 11 | -------------------------------------------------------------------------------- /docs/src/test/resources/3rd-party-bindings/geometry.h: -------------------------------------------------------------------------------- 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); 11 | -------------------------------------------------------------------------------- /bindgen/ir/Location.cpp: -------------------------------------------------------------------------------- 1 | #include "Location.h" 2 | 3 | Location::Location(std::string path, int lineNumber) 4 | : path(std::move(path)), lineNumber(lineNumber) {} 5 | 6 | std::string Location::getPath() const { return path; } 7 | 8 | int Location::getLineNumber() const { return lineNumber; } 9 | -------------------------------------------------------------------------------- /docs/src/test/resources/scala-native-bindings/wordcount.h: -------------------------------------------------------------------------------- 1 | #include 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 12 | -------------------------------------------------------------------------------- /bindgen/defines/DefineFinderActionFactory.cpp: -------------------------------------------------------------------------------- 1 | #include "DefineFinderActionFactory.h" 2 | #include "DefineFinderAction.h" 3 | 4 | DefineFinderActionFactory::DefineFinderActionFactory(IR &ir) : ir(ir) {} 5 | 6 | clang::FrontendAction *DefineFinderActionFactory::create() { 7 | return new DefineFinderAction(ir); 8 | } 9 | -------------------------------------------------------------------------------- /bindgen/visitor/ScalaFrontendActionFactory.cpp: -------------------------------------------------------------------------------- 1 | #include "ScalaFrontendActionFactory.h" 2 | #include "ScalaFrontendAction.h" 3 | 4 | ScalaFrontendActionFactory::ScalaFrontendActionFactory(IR &ir) : ir(ir) {} 5 | 6 | clang::FrontendAction *ScalaFrontendActionFactory::create() { 7 | return new ScalaFrontendAction(ir); 8 | } 9 | -------------------------------------------------------------------------------- /tests/samples/include/CustomNames.h: -------------------------------------------------------------------------------- 1 | struct page { 2 | char *content; 3 | struct page *nextStruct; 4 | }; 5 | 6 | struct book { 7 | struct page *firstPage; 8 | }; 9 | 10 | union weight { 11 | int kilos; 12 | float grams; 13 | }; 14 | 15 | typedef int myInt; 16 | typedef enum { CONST } enumWithTypedef; 17 | -------------------------------------------------------------------------------- /tests/samples/Typedef.h: -------------------------------------------------------------------------------- 1 | enum days { 2 | MONDAY, 3 | TUESDAY, 4 | WEDNESDAY, 5 | THURSDAY, 6 | FRIDAY, 7 | SATURDAY, 8 | SUNDAY, 9 | }; 10 | 11 | typedef enum { OFF, ON } toggle_e; 12 | 13 | typedef int (*int2int)(int); 14 | typedef const char *(*day2string)(enum days); 15 | typedef void (*toggle)(toggle_e state); -------------------------------------------------------------------------------- /bindgen/ir/PossibleVarDefine.cpp: -------------------------------------------------------------------------------- 1 | #include "PossibleVarDefine.h" 2 | 3 | PossibleVarDefine::PossibleVarDefine(const std::string &name, 4 | const std::string &variableName) 5 | : Define(name), variableName(variableName) {} 6 | 7 | std::string PossibleVarDefine::getVariableName() const { return variableName; } 8 | -------------------------------------------------------------------------------- /tests/samples/AnonymousTypes.h: -------------------------------------------------------------------------------- 1 | /* structs */ 2 | 3 | void foo(struct { char a; } * s); 4 | 5 | struct StructWithAnonymousStruct { 6 | struct { 7 | union { 8 | long a; 9 | } * innerUnion; 10 | } * innerStruct; 11 | 12 | enum { A, B, C } innerEnum; 13 | }; 14 | 15 | struct { 16 | int result; 17 | } * bar(); 18 | -------------------------------------------------------------------------------- /bindgen/ir/Define.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_DEFINE_H 2 | #define SCALA_NATIVE_BINDGEN_DEFINE_H 3 | 4 | #include 5 | 6 | class Define { 7 | public: 8 | explicit Define(std::string name); 9 | 10 | std::string getName() const; 11 | 12 | protected: 13 | std::string name; 14 | }; 15 | 16 | #endif // SCALA_NATIVE_BINDGEN_DEFINE_H 17 | -------------------------------------------------------------------------------- /sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/expected/core.scala: -------------------------------------------------------------------------------- 1 | package org.example.app.core 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.link("core") 7 | @native.extern 8 | object core { 9 | def count_words(text: native.CString): native.CInt = native.extern 10 | def __not_excluded(): Unit = native.extern 11 | } 12 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | style = defaultWithAlign 2 | docstrings = JavaDoc 3 | assumeStandardLibraryStripMargin = true 4 | project.excludeFilters = [ 5 | "bindings/[^/]+/src/main/.*[.]scala" 6 | "docs/src/test/scala/org/example/.*[.]scala" 7 | "sbt-scala-native-bindgen/.*/expected/.*[.]scala" 8 | "tests/samples/[^/]*[.]scala" 9 | ] 10 | project.git = true 11 | runner.dialect = Scala211 12 | -------------------------------------------------------------------------------- /docs/src/paradox/bindings/index.md: -------------------------------------------------------------------------------- 1 | # Bindings 2 | 3 | The following bindings have been generated with bindgen. 4 | 5 | If you would like to contribute a binding see the @ref:[Contributing Bindings] page. 6 | 7 | @@ toc { depth=1 } 8 | 9 | @@@ index 10 | 11 | * [posix](posix.md) 12 | * [iconv](iconv.md) 13 | * [utf8proc](utf8proc.md) 14 | 15 | @@@ 16 | 17 | [Contributing Bindings]: ../contrib/bindings.md -------------------------------------------------------------------------------- /tests/samples/ReuseBindings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Struct.h": "org.scalanative.bindgen.samples.Struct", 3 | "CustomNames.h": { 4 | "object": "org.scalanative.bindgen.samples.CustomNames", 5 | "names": { 6 | "struct book": "book", 7 | "struct page": "page", 8 | "union weight": "weight", 9 | "myInt": "MY_INT", 10 | "enumWithTypedef": "EnumWithTypedef" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /bindgen/visitor/ScalaFrontendAction.cpp: -------------------------------------------------------------------------------- 1 | #include "ScalaFrontendAction.h" 2 | 3 | ScalaFrontendAction::ScalaFrontendAction(IR &ir) : ir(ir) {} 4 | 5 | std::unique_ptr 6 | ScalaFrontendAction::CreateASTConsumer(clang::CompilerInstance &CI, 7 | clang::StringRef file) { 8 | return std::unique_ptr(new TreeConsumer(&CI, ir)); 9 | } 10 | -------------------------------------------------------------------------------- /tests/samples/native.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.link("bindgentests") 7 | @native.extern 8 | object nativeLib { 9 | type nativeFunc0 = native.CInt 10 | @native.link("native") 11 | def nativeFunc0(p0: native.CInt): Unit = native.extern 12 | def nativeFunc(p0: native.CFloat): Unit = native.extern 13 | } 14 | -------------------------------------------------------------------------------- /tests/samples/VarDefine.h: -------------------------------------------------------------------------------- 1 | extern int a; 2 | #define A a 3 | 4 | extern const int constInt; 5 | #define CONST_INT constInt 6 | 7 | extern const int *constIntPointer; 8 | #define CONST_INT_POINTER constIntPointer 9 | 10 | #define B b // ignored because there is no such variable 11 | 12 | extern int c; 13 | #define C c 14 | #undef C // removed 15 | 16 | extern float f; 17 | #define __PRIVATE f // should be filtered 18 | -------------------------------------------------------------------------------- /docs/src/paradox/contrib/index.md: -------------------------------------------------------------------------------- 1 | # Contributor's Guide 2 | 3 | Contributions to the project is very welcome. This section provides more 4 | information about how to build and contribute to the project. 5 | 6 | @@ toc 7 | 8 | @@@ index 9 | 10 | * [guidelines](guidelines.md) 11 | * [cmake](cmake.md) 12 | * [docker-compose](docker-compose.md) 13 | * [bindings](bindings.md) 14 | * [releasing](releasing.md) 15 | 16 | @@@ 17 | -------------------------------------------------------------------------------- /tools/src/main/scala/org/scalanative/bindgen/Bindings.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen 2 | 3 | import java.io.{File, PrintWriter} 4 | import scala.collection.immutable.Seq 5 | 6 | class Bindings(val name: String, val source: String, val errors: Seq[String]) { 7 | def writeToFile(file: File): Unit = { 8 | file.getParentFile.mkdirs() 9 | new PrintWriter(file) { 10 | write(source) 11 | close() 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /bindgen/ir/Location.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_LOCATION_H 2 | #define SCALA_NATIVE_BINDGEN_LOCATION_H 3 | 4 | #include 5 | 6 | class Location { 7 | public: 8 | Location(std::string path, int lineNumber); 9 | 10 | std::string getPath() const; 11 | 12 | int getLineNumber() const; 13 | 14 | private: 15 | std::string path; // may be empty 16 | int lineNumber; 17 | }; 18 | 19 | #endif // SCALA_NATIVE_BINDGEN_LOCATION_H 20 | -------------------------------------------------------------------------------- /project/BindingHelpers.scala: -------------------------------------------------------------------------------- 1 | import org.scalanative.bindgen.BindingOptions 2 | 3 | object BindingHelpers { 4 | implicit class BindingOptionsOps(val options: BindingOptions) extends AnyVal { 5 | type F[T] = BindingOptions => T => BindingOptions 6 | 7 | def maybe[T](opt: Option[T], f: F[T]): BindingOptions = 8 | opt match { 9 | case None => options 10 | case Some(value) => f(options)(value) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests/samples/src/test/scala/org/scalanative/bindgen/samples/VarDefineSpec.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import org.scalatest.FunSpec 4 | import scala.scalanative.native._ 5 | 6 | class VarDefineSpec extends FunSpec { 7 | describe("variable #define bindings") { 8 | it("should have the value of the referenced variable") { 9 | assert(VarDefine.A == 23) 10 | assert(VarDefine.CONST_INT == 10) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /scripts/docker-bindgen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Wrapper script for running the dockerized scala-native-bindgen binary. 4 | 5 | set -euo pipefail 6 | IFS=$'\n\t' 7 | 8 | volumes=( 9 | "--volume=$(pwd):/src" 10 | "--volume=/usr/include:/usr/include" 11 | ) 12 | 13 | if [[ "$#" -gt 1 ]] && [[ "$1" == /* ]]; then 14 | volumes+="--volume=$1:$1" 15 | fi 16 | 17 | exec docker run --rm "${volumes[@]}" "scalabindgen/scala-native-bindgen:${VERSION:-latest}" "$@" 18 | -------------------------------------------------------------------------------- /bindings/posix/src/main/scala/org/scalanative/bindings/posix/fnmatch.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindings.posix 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.extern 7 | object fnmatch { 8 | def fnmatch(__pattern: native.CString, __name: native.CString, __flags: native.CInt): native.CInt = native.extern 9 | 10 | object defines { 11 | val _FNMATCH_H: native.CInt = 1 12 | val FNM_NOMATCH: native.CInt = 1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /bindgen/defines/DefineFinderAction.cpp: -------------------------------------------------------------------------------- 1 | #include "DefineFinderAction.h" 2 | 3 | DefineFinderAction::DefineFinderAction(IR &ir) : ir(ir) {} 4 | 5 | void DefineFinderAction::ExecuteAction() { 6 | getCompilerInstance().getPreprocessor().addPPCallbacks( 7 | std::unique_ptr( 8 | new DefineFinder(ir, getCompilerInstance(), 9 | getCompilerInstance().getPreprocessor()))); 10 | 11 | clang::PreprocessOnlyAction::ExecuteAction(); 12 | } 13 | -------------------------------------------------------------------------------- /docs/src/paradox/bindings/utf8proc.md: -------------------------------------------------------------------------------- 1 | # utf8proc 2 | 3 | The [utf8proc] binding provides Unicode normalization, case-folding, and other operations for strings in the UTF-8 encoding. 4 | 5 | To use this binding add the following resolver and dependency: 6 | 7 | @@binding[utf8proc] 8 | 9 | ## Example 10 | 11 | @@snip [utf8proc](../../../../bindings/utf8proc/src/test/scala/org/scalanative/bindings/tests/Utf8procSpec.scala) { #usage-example } 12 | 13 | [utf8proc]: https://juliastrings.github.io/utf8proc/doc/ -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Bash strict mode 4 | # http://redsymbol.net/articles/unofficial-bash-strict-mode/ 5 | set -euo pipefail 6 | IFS=$'\n\t' 7 | 8 | if [[ ! -e bindgen/target/.llvm-version ]] || [[ "$( bindgen/target/.llvm-version 12 | (cd bindgen/target && cmake ..) 13 | fi 14 | 15 | make -C bindgen/target 16 | sbt "${@:-verify}" 17 | -------------------------------------------------------------------------------- /bindgen/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG UBUNTU_VERSION=18.04 2 | ARG LLVM_VERSION=6.0 3 | ARG BUILD_TAG=ubuntu-$UBUNTU_VERSION-llvm-$LLVM_VERSION 4 | FROM scalabindgen/scala-native-bindgen-builder:$BUILD_TAG as builder 5 | 6 | WORKDIR /src 7 | COPY . /src 8 | RUN set -x \ 9 | && mkdir target \ 10 | && cd target \ 11 | && cmake -DSTATIC_LINKING=ON .. \ 12 | && make 13 | 14 | FROM scratch 15 | 16 | COPY --from=builder /src/target/scala-native-bindgen /scala-native-bindgen 17 | WORKDIR /src 18 | ENTRYPOINT ["/scala-native-bindgen"] 19 | -------------------------------------------------------------------------------- /sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/expected/stdlib.scala: -------------------------------------------------------------------------------- 1 | package org.example.app.stdlib 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.extern 7 | object stdlib { 8 | def access(path: native.CString, mode: native.CInt): native.CInt = native.extern 9 | def read(fildes: native.CInt, buf: native.Ptr[Byte], nbyte: native.CInt): native.CInt = native.extern 10 | def printf(format: native.CString, varArgs: native.CVararg*): native.CInt = native.extern 11 | } 12 | -------------------------------------------------------------------------------- /scripts/scalafmt: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # set -x 4 | 5 | HERE="`dirname $0`" 6 | VERSION="1.5.1" 7 | COURSIER="$HERE/.coursier" 8 | SCALAFMT="$HERE/.scalafmt-$VERSION" 9 | 10 | if [ ! -f $COURSIER ]; then 11 | curl -L -o $COURSIER https://git.io/vgvpD 12 | chmod +x $COURSIER 13 | fi 14 | 15 | if [ ! -f $SCALAFMT ]; then 16 | $COURSIER bootstrap com.geirsson:scalafmt-cli_2.11:$VERSION --main org.scalafmt.cli.Cli -o $SCALAFMT 17 | chmod +x $SCALAFMT 18 | fi 19 | 20 | exit $($SCALAFMT "$@" >/dev/null) 21 | -------------------------------------------------------------------------------- /bindgen/defines/DefineFinderAction.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_DEFINEFINDERACTION_H 2 | #define SCALA_NATIVE_BINDGEN_DEFINEFINDERACTION_H 3 | 4 | #include "DefineFinder.h" 5 | #include 6 | 7 | class DefineFinderAction : public clang::PreprocessOnlyAction { 8 | public: 9 | explicit DefineFinderAction(IR &ir); 10 | 11 | protected: 12 | void ExecuteAction() override; 13 | 14 | private: 15 | IR &ir; 16 | }; 17 | 18 | #endif // SCALA_NATIVE_BINDGEN_DEFINEFINDERACTION_H 19 | -------------------------------------------------------------------------------- /bindgen/ir/Variable.cpp: -------------------------------------------------------------------------------- 1 | #include "Variable.h" 2 | #include "../Utils.h" 3 | 4 | Variable::Variable(const std::string &name, std::shared_ptr type) 5 | : TypeAndName(name, type) {} 6 | 7 | std::string 8 | Variable::getDefinition(const LocationManager &locationManager) const { 9 | return " val " + name + ": " + type->str(locationManager) + 10 | " = native.extern\n"; 11 | } 12 | 13 | bool Variable::hasIllegalUsageOfOpaqueType() const { 14 | return isAliasForOpaqueType(type.get()); 15 | } 16 | -------------------------------------------------------------------------------- /bindgen/ir/Variable.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_VARIABLE_H 2 | #define SCALA_NATIVE_BINDGEN_VARIABLE_H 3 | 4 | #include "TypeAndName.h" 5 | #include 6 | 7 | class Variable : public TypeAndName { 8 | public: 9 | Variable(const std::string &name, std::shared_ptr type); 10 | 11 | std::string getDefinition(const LocationManager &locationManager) const; 12 | 13 | bool hasIllegalUsageOfOpaqueType() const; 14 | }; 15 | 16 | #endif // SCALA_NATIVE_BINDGEN_VARIABLE_H 17 | -------------------------------------------------------------------------------- /bindgen/ir/PossibleVarDefine.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_POSSIBLEVARDEFINE_H 2 | #define SCALA_NATIVE_BINDGEN_POSSIBLEVARDEFINE_H 3 | 4 | #include "Define.h" 5 | 6 | /** 7 | * Stores name of possible variable. 8 | */ 9 | class PossibleVarDefine : public Define { 10 | public: 11 | PossibleVarDefine(const std::string &name, const std::string &variableName); 12 | 13 | std::string getVariableName() const; 14 | 15 | private: 16 | std::string variableName; 17 | }; 18 | 19 | #endif // SCALA_NATIVE_BINDGEN_POSSIBLEVARDEFINE_H 20 | -------------------------------------------------------------------------------- /docs/src/test/resources/using-bindings/vector.c: -------------------------------------------------------------------------------- 1 | #include "vector.h" 2 | #include 3 | #include 4 | 5 | float cosine(struct lineSegment *v1, struct lineSegment *v2) { 6 | float v1x = fabsf(v1->b.x - v1->a.x); 7 | float v1y = fabsf(v1->b.y - v1->a.y); 8 | float v1Length = sqrtf(v1x * v1x + v1y * v1y); 9 | float v2x = fabsf(v2->b.x - v2->a.x); 10 | float v2y = fabsf(v2->b.y - v2->a.y); 11 | float v2Length = sqrtf(v2x * v2x + v2y * v2y); 12 | return (v1x * v2x + v1y * v2y) / (v1Length * v2Length); 13 | } 14 | -------------------------------------------------------------------------------- /bindgen/defines/DefineFinderActionFactory.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_DEFINEFINDERACTIONFACTORY_H 2 | #define SCALA_NATIVE_BINDGEN_DEFINEFINDERACTIONFACTORY_H 3 | 4 | #include "../ir/IR.h" 5 | #include 6 | 7 | class DefineFinderActionFactory : public clang::tooling::FrontendActionFactory { 8 | public: 9 | explicit DefineFinderActionFactory(IR &ir); 10 | 11 | clang::FrontendAction *create() override; 12 | 13 | private: 14 | IR &ir; 15 | }; 16 | 17 | #endif // SCALA_NATIVE_BINDGEN_DEFINEFINDERACTIONFACTORY_H 18 | -------------------------------------------------------------------------------- /bindgen/ir/LocatableType.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_LOCATABLE_H 2 | #define SCALA_NATIVE_BINDGEN_LOCATABLE_H 3 | 4 | #include "Location.h" 5 | #include "types/Type.h" 6 | 7 | class LocatableType : virtual public Type { 8 | public: 9 | explicit LocatableType(std::shared_ptr location); 10 | 11 | virtual std::shared_ptr getLocation() const; 12 | 13 | protected: 14 | /** 15 | * nullptr if type is generated 16 | */ 17 | std::shared_ptr location; 18 | }; 19 | 20 | #endif // SCALA_NATIVE_BINDGEN_LOCATABLE_H 21 | -------------------------------------------------------------------------------- /tests/samples/src/test/scala/org/scalanative/bindgen/samples/EnumSpec.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import org.scalatest.FunSpec 4 | import scalanative.native._ 5 | 6 | class EnumSpec extends FunSpec { 7 | describe("enum bindings") { 8 | it("should bind to C enum values") { 9 | assert(Enum.get_WEDNESDAY() == Enum.enum_days.WEDNESDAY) 10 | } 11 | 12 | it("should handle large negative values") { 13 | assert( 14 | Enum.check_BIG_NEG_A(Enum.enum_bigNegativeValues.BIG_NEG_A) == c"OK") 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /bindgen/ir/VarDefine.cpp: -------------------------------------------------------------------------------- 1 | #include "VarDefine.h" 2 | 3 | VarDefine::VarDefine(std::string name, std::shared_ptr variable) 4 | : Define(std::move(name)), variable(variable) {} 5 | 6 | std::string 7 | VarDefine::getDefinition(const LocationManager &locationManager) const { 8 | return " @name(\"" + variable->getName() + "\")\n" + " val " + name + 9 | ": " + variable->getType()->str(locationManager) + 10 | " = native.extern\n"; 11 | } 12 | 13 | bool VarDefine::hasIllegalUsageOfOpaqueType() const { 14 | return variable->hasIllegalUsageOfOpaqueType(); 15 | } 16 | -------------------------------------------------------------------------------- /tests/samples/Enum.h: -------------------------------------------------------------------------------- 1 | enum days { 2 | MONDAY, // = 0 3 | TUESDAY = 200, 4 | WEDNESDAY, // = 201 5 | THURSDAY = 4, 6 | FRIDAY = 5, 7 | SATURDAY = 3, 8 | SUNDAY // = 4 9 | }; 10 | 11 | enum bigValues { 12 | BIG_A = 10000000000, // does not fit into int 13 | BIG_B = 1 14 | }; 15 | 16 | enum { // anonymous enum 17 | ANON_A, 18 | ANON_B 19 | }; 20 | 21 | enum negativeValues { NEG_A = -1, NEG_B = -2 }; 22 | 23 | enum bigNegativeValues { BIG_NEG_A = -10000000000, BIG_NEG_B = -1 }; 24 | 25 | enum days get_WEDNESDAY(); 26 | 27 | char *check_BIG_NEG_A(enum bigNegativeValues big_neg_a); 28 | -------------------------------------------------------------------------------- /tests/samples/ReuseBindings.h: -------------------------------------------------------------------------------- 1 | #include "Struct.h" 2 | #include "include/CustomNames.h" 3 | 4 | void useStruct(struct point *); 5 | 6 | point_s returnTypedef_point_s(); // struct point will be references instead of 7 | // point_s because clang replaces the typedef 8 | point *returnTypedef_point(); 9 | 10 | typedef struct bigStruct aliasForBigStruct; 11 | 12 | struct usesImportedEnum { 13 | enum pointIndex index; 14 | union weight weight; 15 | }; 16 | 17 | void readBook(struct book *book); 18 | void getWeight(union weight *weight); 19 | 20 | myInt getMyInt(); 21 | enumWithTypedef getEnum(); -------------------------------------------------------------------------------- /docs/src/paradox/contrib/docker-compose.md: -------------------------------------------------------------------------------- 1 | # Building the executable with `docker-compose` 2 | 3 | You can use [docker-compose] to build and test the program: 4 | 5 | ```sh 6 | # Build the docker image with LLVM version 6.0. 7 | docker-compose build ubuntu-18.04-llvm-6.0 8 | # Build the bindgen tool and run the tests. 9 | docker-compose run --rm ubuntu-18.04-llvm-6.0 ./script/test.sh 10 | # Run the bindgen tool inside the container. 11 | docker-compose run --rm ubuntu-18.04-llvm-6.0 \ 12 | bindgen/target/scala-native-bindgen --name union tests/samples/Union.h -- 13 | ``` 14 | 15 | [docker-compose]: https://docs.docker.com/compose/ 16 | -------------------------------------------------------------------------------- /tests/samples/ReservedWords.h: -------------------------------------------------------------------------------- 1 | typedef int match; 2 | typedef match var[5]; 3 | 4 | struct object { 5 | match yield; 6 | int val; 7 | }; 8 | 9 | typedef struct object object; 10 | typedef struct object type; 11 | 12 | union lazy { 13 | object *instance; 14 | match forSome; 15 | struct { 16 | char def; 17 | type *super; 18 | } * implicit; 19 | }; 20 | 21 | typedef union lazy lazy; 22 | 23 | type *with(match sealed, var implicit, lazy *forSome); 24 | 25 | typedef match def; 26 | typedef struct { 27 | def val; 28 | lazy finally; 29 | } finally; 30 | match implicit(finally type[12]); 31 | void _1(); 32 | -------------------------------------------------------------------------------- /tests/samples/CustomNames.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.link("bindgentests") 7 | @native.extern 8 | object CustomNames { 9 | type enum_enumWithTypedef = native.CUnsignedInt 10 | object enum_enumWithTypedef { 11 | final val CONST: enum_enumWithTypedef = 0.toUInt 12 | } 13 | 14 | type EnumWithTypedef = enum_enumWithTypedef 15 | type page = native.CStruct2[native.CString, native.Ptr[Byte]] 16 | type book = native.CStruct1[native.Ptr[page]] 17 | type weight = native.CArray[Byte, native.Nat._4] 18 | type MY_INT = native.CInt 19 | } 20 | -------------------------------------------------------------------------------- /tests/samples/Function.h: -------------------------------------------------------------------------------- 1 | int no_args(); 2 | float void_arg(void); 3 | char one_arg(int a); 4 | void *two_args(float a, int b); 5 | double anonymous_args(float, int); 6 | double variadic_args(double a, char *varArgs, ...); 7 | 8 | struct s { 9 | int val; 10 | }; 11 | 12 | void acceptsStructValue(struct s); // function is skipped with warning 13 | 14 | typedef struct s s; 15 | 16 | s returnsStructValue(); // function is skipped with warning 17 | 18 | union u { 19 | int a; 20 | }; 21 | 22 | void acceptsUnionValue(union u); // function is skipped with warning 23 | 24 | void acceptsArray(int[10]); // it's okay because the type is pointer to int 25 | -------------------------------------------------------------------------------- /tests/samples/src/test/scala/org/scalanative/bindgen/samples/ExternSpec.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import org.scalatest.FunSpec 4 | import scalanative.native._ 5 | 6 | class ExternSpec extends FunSpec { 7 | describe("extern variable bindings") { 8 | it("should bind to C variable") { 9 | assert(Extern.forty_two == 42) 10 | assert(Extern.mode == Extern.enum_mode.USER) 11 | } 12 | 13 | it("should bind to C struct variable") { 14 | import Extern.implicits._ 15 | assert( 16 | Extern.semver.major == 1 && Extern.semver.minor == 2 && Extern.semver.patch == 3) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /docs/src/paradox/contrib/releasing.md: -------------------------------------------------------------------------------- 1 | # Release Workflow 2 | 3 | First build the `scala-native-bindgen` executable for both macOS and 4 | Linux: 5 | 6 | ```sh 7 | scripts/prepare-release.sh 8 | ``` 9 | 10 | You should now have `scala-native-bindgen-linux` and 11 | `scala-native-bindgen-darwin` if you ran the script on a macOS machine. 12 | 13 | Then release version `x.y.z` by running: 14 | 15 | ```sh 16 | sbt -Dproject.version=x.y.z release 17 | ``` 18 | 19 | Finally, upload the `scala-native-bindgen-linux` and 20 | `scala-native-bindgen-darwin` executables to the release page at: 21 | -------------------------------------------------------------------------------- /bindings/posix/src/test/scala/org/scalanative/bindings/tests/FnmatchSpec.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindings.tests 2 | 3 | import org.scalatest.FunSpec 4 | 5 | class FnmatchSpec extends FunSpec { 6 | describe("fnmatch") { 7 | it("should match patterns") { 8 | //#usage-example 9 | import scala.scalanative.native._ 10 | import org.scalanative.bindings.posix.fnmatch._ 11 | 12 | assert(fnmatch(c"*.md", c"README.md", 0) == 0) 13 | assert(fnmatch(c"*.[ch]", c"main.h", 0) == 0) 14 | assert(fnmatch(c"*.[ch]", c"main.c", 0) == 0) 15 | assert(fnmatch(c"*.md", c"README_md", 0) == defines.FNM_NOMATCH) 16 | //#usage-example 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests/samples/VarDefine.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.link("bindgentests") 7 | @native.extern 8 | object VarDefine { 9 | val a: native.CInt = native.extern 10 | val constInt: native.CInt = native.extern 11 | val constIntPointer: native.Ptr[native.CInt] = native.extern 12 | val c: native.CInt = native.extern 13 | val f: native.CFloat = native.extern 14 | @name("a") 15 | val A: native.CInt = native.extern 16 | @name("constInt") 17 | val CONST_INT: native.CInt = native.extern 18 | @name("constIntPointer") 19 | val CONST_INT_POINTER: native.Ptr[native.CInt] = native.extern 20 | } 21 | -------------------------------------------------------------------------------- /bindgen/visitor/TreeConsumer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../Utils.h" 4 | #include "../ir/IR.h" 5 | #include "TreeVisitor.h" 6 | 7 | class TreeConsumer : public clang::ASTConsumer { 8 | private: 9 | TreeVisitor visitor; 10 | 11 | clang::SourceManager &smanager; 12 | 13 | public: 14 | TreeConsumer(clang::CompilerInstance *CI, IR &ir) 15 | : visitor(CI, ir), smanager(CI->getASTContext().getSourceManager()) {} 16 | 17 | bool HandleTopLevelDecl(clang::DeclGroupRef DG) override { 18 | // a DeclGroupRef may have multiple Decls, so we iterate through each 19 | // one 20 | for (auto D : DG) { 21 | visitor.TraverseDecl(D); 22 | } 23 | return true; 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /bindings/iconv/src/main/scala/org/scalanative/bindings/iconv.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindings 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.extern 7 | object iconv { 8 | type iconv_t = native.Ptr[Byte] 9 | def iconv_open(__tocode: native.CString, __fromcode: native.CString): native.Ptr[Byte] = native.extern 10 | def iconv(__cd: native.Ptr[Byte], __inbuf: native.Ptr[native.CString], __inbytesleft: native.Ptr[native.CSize], __outbuf: native.Ptr[native.CString], __outbytesleft: native.Ptr[native.CSize]): native.CSize = native.extern 11 | def iconv_close(__cd: native.Ptr[Byte]): native.CInt = native.extern 12 | 13 | object defines { 14 | val _ICONV_H: native.CInt = 1 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docs/src/paradox/bindings/iconv.md: -------------------------------------------------------------------------------- 1 | # iconv 2 | 3 | The [`iconv.h`] binding allows to convert text between different character sets, for example convert text from UTF-8 to ISO-8859-1. 4 | 5 | To use this binding add the following resolver and the dependency: 6 | 7 | @@binding[iconv] 8 | 9 | You also need to configure your build tool to link with the iconv library on macOS. 10 | For sbt, you can use the following code: 11 | 12 | @@snip[sbt-iconv](../../../../build.sbt) { #sbt-iconv-linking-options } 13 | 14 | ## Example 15 | 16 | @@snip [iconv](../../../../bindings/iconv/src/test/scala/org/scalanative/bindings/tests/IconvSpec.scala) { #usage-example } 17 | 18 | [`iconv.h`]: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/iconv.h.html 19 | -------------------------------------------------------------------------------- /docs/src/test/scala/org/scalanative/bindgen/docs/GeometrySpec.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.docs 2 | 3 | import org.scalatest.FunSpec 4 | 5 | class GeometrySpec extends FunSpec { 6 | describe("geometry") { 7 | it("using generated bindings") { 8 | //#example 9 | import com.example.custom.binding.Vector.Point 10 | import org.example.geometry.Geometry._ 11 | import scala.scalanative.native.Zone 12 | 13 | Zone { implicit zone => 14 | val center = Point(1, 1) 15 | val circle = struct_circle(center, 2.0) 16 | 17 | val area = circle_area(circle) 18 | //#example 19 | assert(area - (4 * math.Pi) < 1e-3f) 20 | //#example 21 | } 22 | //#example 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /tests/samples/Function.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "Function.h" 6 | 7 | int no_args() { return 42; } 8 | 9 | float void_arg(void) { return 1.5; } 10 | 11 | char one_arg(int a) { return (char)a; } 12 | 13 | void *two_args(float a, int b) { 14 | static char buf[128]; 15 | if (snprintf(buf, sizeof(buf), "%.2f %d", a, b) > sizeof(buf)) 16 | return "oops"; 17 | return buf; 18 | } 19 | 20 | double anonymous_args(float a, int b) { return a + b; } 21 | 22 | double variadic_args(double a, char *varArgs, ...) { 23 | va_list args; 24 | 25 | va_start(args, varArgs); 26 | for (; *varArgs; varArgs++) 27 | a += (*varArgs - '0') * va_arg(args, int); 28 | return a; 29 | } 30 | -------------------------------------------------------------------------------- /tests/samples/Union.h: -------------------------------------------------------------------------------- 1 | struct s { 2 | int a; 3 | }; 4 | 5 | union values { 6 | long l; 7 | int i; 8 | long long ll; 9 | double d; 10 | const char *s; 11 | struct s structInUnion; 12 | }; 13 | 14 | enum union_op { UNION_SET, UNION_TEST }; 15 | 16 | int union_get_sizeof(); 17 | 18 | int union_test_int(union values *v, enum union_op op, int value); 19 | int union_test_long(union values *v, enum union_op op, long value); 20 | int union_test_long_long(union values *v, enum union_op op, long long value); 21 | int union_test_double(union values *v, enum union_op op, double value); 22 | int union_test_string(union values *v, enum union_op op, const char *value); 23 | int union_test_struct(union values *v, enum union_op op, struct s *value); 24 | -------------------------------------------------------------------------------- /bindgen/visitor/ScalaFrontendActionFactory.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_SCALAFRONTENDACTIONFACTORY_H 2 | #define SCALA_NATIVE_BINDGEN_SCALAFRONTENDACTIONFACTORY_H 3 | 4 | #include "ScalaFrontendAction.h" 5 | #include 6 | #include 7 | 8 | /** 9 | * Stores instance of IR and passes it 10 | * to a new instance of ScalaFrontendAction when create method 11 | * is executed. 12 | */ 13 | class ScalaFrontendActionFactory 14 | : public clang::tooling::FrontendActionFactory { 15 | public: 16 | ScalaFrontendActionFactory(IR &ir); 17 | 18 | clang::FrontendAction *create() override; 19 | 20 | private: 21 | IR &ir; 22 | }; 23 | 24 | #endif // SCALA_NATIVE_BINDGEN_SCALAFRONTENDACTIONFACTORY_H 25 | -------------------------------------------------------------------------------- /bindgen/ir/VarDefine.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_VARDEFINE_H 2 | #define SCALA_NATIVE_BINDGEN_VARDEFINE_H 3 | 4 | #include "Define.h" 5 | #include "Variable.h" 6 | 7 | /** 8 | * Stores a pointer to Variable instance from IR 9 | */ 10 | class VarDefine : public Define { 11 | public: 12 | VarDefine(std::string name, std::shared_ptr variable); 13 | 14 | std::string getDefinition(const LocationManager &locationManager) const; 15 | 16 | friend llvm::raw_ostream &operator<<(llvm::raw_ostream &s, 17 | const VarDefine &varDefine); 18 | 19 | bool hasIllegalUsageOfOpaqueType() const; 20 | 21 | private: 22 | std::shared_ptr variable; 23 | }; 24 | 25 | #endif // SCALA_NATIVE_BINDGEN_VARDEFINE_H 26 | -------------------------------------------------------------------------------- /tests/samples/include/included.h: -------------------------------------------------------------------------------- 1 | void func(); // removed 2 | 3 | extern int mode; // removed 4 | 5 | typedef int size; 6 | 7 | struct metadata { 8 | unsigned year; 9 | char *publisher; 10 | }; 11 | 12 | typedef struct metadata metadata; 13 | 14 | struct document { 15 | metadata m; 16 | }; 17 | 18 | typedef struct document unusedTypedef; // removed 19 | 20 | union data { 21 | int i; 22 | float f; 23 | }; // removed 24 | 25 | union data getData(); // removed 26 | 27 | enum { ONE, TWO }; // anonymous enum will be removed 28 | 29 | enum unusedEnum { UNUSED }; // removed 30 | 31 | typedef enum unusedEnum unusedEnum; // removed 32 | 33 | struct unusedStruct { 34 | enum unusedEnum e; 35 | }; // removed 36 | 37 | enum semester { AUTUMN, SPRING }; 38 | -------------------------------------------------------------------------------- /bindgen/visitor/ScalaFrontendAction.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_SCALAFRONTENDACTION_H 2 | #define SCALA_NATIVE_BINDGEN_SCALAFRONTENDACTION_H 3 | 4 | #include "TreeConsumer.h" 5 | 6 | #include "../ir/IR.h" 7 | #include 8 | 9 | /** 10 | * Creates ASTConsumer which will go through all top-level 11 | * declarations and execute visitor on some of them. 12 | */ 13 | class ScalaFrontendAction : public clang::ASTFrontendAction { 14 | public: 15 | ScalaFrontendAction(IR &ir); 16 | 17 | std::unique_ptr 18 | CreateASTConsumer(clang::CompilerInstance &CI, 19 | clang::StringRef file) override; 20 | 21 | private: 22 | IR &ir; 23 | }; 24 | 25 | #endif // SCALA_NATIVE_BINDGEN_SCALAFRONTENDACTION_H 26 | -------------------------------------------------------------------------------- /bindgen/ir/LiteralDefine.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_LITERALDEFINE_H 2 | #define SCALA_NATIVE_BINDGEN_LITERALDEFINE_H 3 | 4 | #include "Define.h" 5 | #include "types/Type.h" 6 | #include 7 | 8 | class LiteralDefine : public Define { 9 | public: 10 | LiteralDefine(std::string name, std::string literal, 11 | std::shared_ptr type); 12 | 13 | std::string getDefinition(const LocationManager &locationManager) const; 14 | 15 | bool usesType(const std::shared_ptr &type, bool stopOnTypeDefs, 16 | std::vector> &visitedTypes) const; 17 | 18 | private: 19 | std::string literal; 20 | std::shared_ptr type; 21 | }; 22 | 23 | #endif // SCALA_NATIVE_BINDGEN_LITERALDEFINE_H 24 | -------------------------------------------------------------------------------- /bindgen/ir/LiteralDefine.cpp: -------------------------------------------------------------------------------- 1 | #include "LiteralDefine.h" 2 | 3 | LiteralDefine::LiteralDefine(std::string name, std::string literal, 4 | std::shared_ptr type) 5 | : Define(std::move(name)), literal(std::move(literal)), type(type) {} 6 | 7 | std::string 8 | LiteralDefine::getDefinition(const LocationManager &locationManager) const { 9 | return " val " + name + ": " + type->str(locationManager) + " = " + 10 | literal + "\n"; 11 | } 12 | 13 | bool LiteralDefine::usesType( 14 | const std::shared_ptr &type, bool stopOnTypeDefs, 15 | std::vector> &visitedTypes) const { 16 | if (*this->type == *type) { 17 | return true; 18 | } 19 | visitedTypes.clear(); 20 | return this->type->usesType(type, stopOnTypeDefs, visitedTypes); 21 | } 22 | -------------------------------------------------------------------------------- /scripts/docker-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Run tests similar to the BindgenSpec using the dockerized bindgen 4 | # program. 5 | # 6 | # Assumes you have built the bindgen docker image by running: 7 | # 8 | # docker-compose build bindgen 9 | 10 | set -euo pipefail 11 | IFS=$'\n\t' 12 | 13 | outdir="tests/target/docker-samples" 14 | rm -rf "$outdir" 15 | mkdir -p "$outdir" 16 | 17 | echo "Using version '${VERSION:-latest}'" 18 | 19 | for input in tests/samples/*.h; do 20 | name="$(basename "$input" .h)" 21 | output="$outdir/$name.scala" 22 | expected="tests/samples/$name.scala" 23 | 24 | echo "Testing $name" 25 | scripts/docker-bindgen.sh "$input" \ 26 | --name="$name" \ 27 | --link=bindgentests \ 28 | --package=org.scalanative.bindgen.samples \ 29 | --exclude-prefix=__ \ 30 | -- > "$output" 31 | git diff --exit-code "$expected" "$output" 32 | done 33 | -------------------------------------------------------------------------------- /bindgen/ir/types/Type.cpp: -------------------------------------------------------------------------------- 1 | #include "Type.h" 2 | #include 3 | 4 | CycleNode::CycleNode(std::shared_ptr s, const Field *field) 5 | : s(std::move(s)), field(field) {} 6 | 7 | bool Type::operator!=(const Type &other) const { return !(*this == other); } 8 | 9 | bool Type::findAllCycles( 10 | const std::shared_ptr &startStruct, CycleNode &cycleNode, 11 | std::vector> &visitedTypes) const { 12 | return false; 13 | } 14 | 15 | std::shared_ptr Type::unrollTypedefs() const { return nullptr; } 16 | 17 | std::shared_ptr 18 | Type::replaceType(const std::shared_ptr &type, 19 | const std::shared_ptr &replacement) const { 20 | assert( 21 | false && 22 | "Base class implementation of replaceType method must not be executed"); 23 | return nullptr; 24 | } 25 | -------------------------------------------------------------------------------- /bindings/posix/src/test/scala/org/scalanative/bindings/tests/RegexSpec.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindings.tests 2 | 3 | import org.scalatest.FunSpec 4 | 5 | class RegexSpec extends FunSpec { 6 | describe("regex") { 7 | it("should match regular expressions") { 8 | //#usage-example 9 | import scala.scalanative.native._ 10 | import org.scalanative.bindings.posix.regex._ 11 | 12 | val reg = stackalloc[regex_t] 13 | 14 | val compResult = 15 | regcomp(reg, c"Scala \(J\(S\|VM\)\|Native\)", defines.REG_EXTENDED) 16 | assert(compResult == 0) 17 | 18 | assert(regexec(reg, c"Scala JVM", 0, null, 0) == 0) 19 | assert(regexec(reg, c"Scala JS", 0, null, 0) == 0) 20 | assert(regexec(reg, c"Scala Native", 0, null, 0) == 0) 21 | assert(regexec(reg, c"Scala .NET", 0, null, 0) != 0) 22 | 23 | regfree(reg) 24 | //#usage-example 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/samples/Typedef.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | object Typedef { 7 | type enum_days = native.CUnsignedInt 8 | object enum_days { 9 | final val MONDAY: enum_days = 0.toUInt 10 | final val TUESDAY: enum_days = 1.toUInt 11 | final val WEDNESDAY: enum_days = 2.toUInt 12 | final val THURSDAY: enum_days = 3.toUInt 13 | final val FRIDAY: enum_days = 4.toUInt 14 | final val SATURDAY: enum_days = 5.toUInt 15 | final val SUNDAY: enum_days = 6.toUInt 16 | } 17 | 18 | type enum_toggle_e = native.CUnsignedInt 19 | object enum_toggle_e { 20 | final val OFF: enum_toggle_e = 0.toUInt 21 | final val ON: enum_toggle_e = 1.toUInt 22 | } 23 | 24 | type toggle_e = enum_toggle_e 25 | type int2int = native.CFunctionPtr1[native.CInt, native.CInt] 26 | type day2string = native.CFunctionPtr1[enum_days, native.CString] 27 | type toggle = native.CFunctionPtr1[toggle_e, Unit] 28 | } 29 | -------------------------------------------------------------------------------- /bindgen/ir/TypeAndName.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_TYPEANDNAME_H 2 | #define SCALA_NATIVE_BINDGEN_TYPEANDNAME_H 3 | 4 | #include "types/Type.h" 5 | #include 6 | #include 7 | 8 | /** 9 | * Base class for function parameters, struct fields 10 | * and type declarations 11 | */ 12 | class TypeAndName { 13 | public: 14 | TypeAndName(std::string name, std::shared_ptr type); 15 | 16 | std::shared_ptr getType() const; 17 | 18 | void setType(std::shared_ptr name); 19 | 20 | std::string getName() const; 21 | 22 | bool operator==(const TypeAndName &other) const; 23 | 24 | bool operator!=(const TypeAndName &other) const; 25 | 26 | bool usesType(const std::shared_ptr &type, bool stopOnTypeDefs, 27 | std::vector> &visitedTypes) const; 28 | 29 | protected: 30 | std::string name; 31 | std::shared_ptr type; 32 | }; 33 | 34 | #endif // SCALA_NATIVE_BINDGEN_TYPEANDNAME_H 35 | -------------------------------------------------------------------------------- /bindgen/ir/types/ArrayType.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_ARRAYTYPE_H 2 | #define SCALA_NATIVE_BINDGEN_ARRAYTYPE_H 3 | 4 | #include "Type.h" 5 | 6 | class ArrayType : public virtual Type { 7 | public: 8 | ArrayType(std::shared_ptr elementsType, uint64_t size); 9 | 10 | bool usesType( 11 | const std::shared_ptr &type, bool stopOnTypeDefs, 12 | std::vector> &visitedTypes) const override; 13 | 14 | std::string str(const LocationManager &locationManager) const override; 15 | 16 | bool operator==(const Type &other) const override; 17 | 18 | std::shared_ptr unrollTypedefs() const override; 19 | 20 | std::shared_ptr 21 | replaceType(const std::shared_ptr &type, 22 | const std::shared_ptr &replacement) const override; 23 | 24 | private: 25 | const uint64_t size; 26 | std::shared_ptr elementsType; 27 | }; 28 | 29 | #endif // SCALA_NATIVE_BINDGEN_ARRAYTYPE_H 30 | -------------------------------------------------------------------------------- /docs/src/test/resources/scala-native-bindings/wordcount.c: -------------------------------------------------------------------------------- 1 | #include "wordcount.h" 2 | #include 3 | #include 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 | } 40 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.3.8") 2 | addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.3.2") 3 | addSbtPlugin("io.github.jonas" % "sbt-paradox-material-theme" % "0.5.0") 4 | addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.6.2") 5 | addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "1.0.0") 6 | addSbtPlugin("com.github.gseitz" % "sbt-release" % "1.0.9") 7 | addSbtPlugin("org.foundweekends" % "sbt-bintray" % "0.5.4") 8 | addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.9.0") 9 | 10 | libraryDependencies += "org.scala-sbt" %% "scripted-plugin" % sbtVersion.value 11 | 12 | val VERSION = "0.3.1" 13 | 14 | //#sbt-plugin-example 15 | addSbtPlugin("org.scala-native.bindgen" % "sbt-scala-native-bindgen" % VERSION) 16 | 17 | resolvers += Resolver.bintrayIvyRepo("scala-native-bindgen", "sbt-plugins") 18 | resolvers += Resolver.bintrayRepo("scala-native-bindgen", "maven") 19 | //#sbt-plugin-example 20 | -------------------------------------------------------------------------------- /bindgen/visitor/TreeVisitor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../TypeTranslator.h" 4 | #include "../ir/IR.h" 5 | #include 6 | #include 7 | 8 | class TreeVisitor : public clang::RecursiveASTVisitor { 9 | private: 10 | clang::ASTContext *astContext; 11 | TypeTranslator typeTranslator; 12 | IR &ir; 13 | uint anonymousEnumId = 0; 14 | 15 | bool isAliasForAnonymousEnum(clang::TypedefDecl *tpdef) const; 16 | 17 | public: 18 | TreeVisitor(clang::CompilerInstance *CI, IR &ir) 19 | : astContext(&(CI->getASTContext())), typeTranslator(astContext, ir), 20 | ir(ir) {} 21 | 22 | virtual ~TreeVisitor() = default; 23 | 24 | virtual bool VisitFunctionDecl(clang::FunctionDecl *func); 25 | virtual bool VisitTypedefDecl(clang::TypedefDecl *tpdef); 26 | virtual bool VisitEnumDecl(clang::EnumDecl *enumDecl); 27 | virtual bool VisitRecordDecl(clang::RecordDecl *record); 28 | virtual bool VisitVarDecl(clang::VarDecl *VD); 29 | }; 30 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | bindgen: 5 | image: scalabindgen/scala-native-bindgen:${VERSION:-latest} 6 | build: 7 | context: bindgen 8 | args: 9 | - UBUNTU_VERSION=18.04 10 | - LLVM_VERSION=6.0 11 | entrypoint: [/scala-native-bindgen] 12 | 13 | ubuntu-18.04-llvm-6.0: 14 | image: scalabindgen/scala-native-bindgen-builder:ubuntu-18.04-llvm-6.0 15 | build: 16 | context: . 17 | args: 18 | - UBUNTU_VERSION=18.04 19 | - LLVM_VERSION=6.0 20 | command: scripts/test.sh 21 | volumes: 22 | - .:/src 23 | - ${HOME}/.ivy2:/root/.ivy2 24 | - ${HOME}/.sbt:/root/.sbt 25 | 26 | ubuntu-18.04-llvm-5.0: 27 | image: scalabindgen/scala-native-bindgen-builder:ubuntu-18.04-llvm-5.0 28 | build: 29 | context: . 30 | args: 31 | - UBUNTU_VERSION=18.04 32 | - LLVM_VERSION=5.0 33 | command: scripts/test.sh 34 | volumes: 35 | - .:/src 36 | - ${HOME}/.ivy2:/root/.ivy2 37 | - ${HOME}/.sbt:/root/.sbt 38 | -------------------------------------------------------------------------------- /sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/build.sbt: -------------------------------------------------------------------------------- 1 | name := "test" 2 | organization := "org.scala-native.bindgen.sbt.test" 3 | //#example 4 | enablePlugins(ScalaNativeBindgenPlugin) 5 | 6 | //#example 7 | scalaVersion := "2.11.12" 8 | nativeBindgenPath := Some(file(System.getProperty("bindgen.path"))) 9 | 10 | //#example 11 | inConfig(Compile)( 12 | Def.settings( 13 | nativeBindings += { 14 | NativeBinding(resourceDirectory.value / "stdlib.h") 15 | .name("stdlib") 16 | .packageName("org.example.app.stdlib") 17 | .excludePrefix("__") 18 | } 19 | )) 20 | //#example 21 | 22 | val nativeBindgenCustomTarget = SettingKey[File]("nativeBindgenCustomTarget") 23 | nativeBindgenCustomTarget := baseDirectory.value / "src/main/scala/org/example" 24 | 25 | val nativeBindgenCoreBinding = 26 | SettingKey[NativeBinding]("nativeBindgenCoreBinding") 27 | nativeBindgenCoreBinding := { 28 | NativeBinding((resourceDirectory in Compile).value / "core.h") 29 | .packageName("org.example.app.core") 30 | .link("core") 31 | } -------------------------------------------------------------------------------- /scripts/prepare-release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Build Linux and if possible macOS executables. 4 | 5 | # Bash strict mode 6 | # http://redsymbol.net/articles/unofficial-bash-strict-mode/ 7 | set -euo pipefail 8 | IFS=$'\n\t' 9 | 10 | ROOT="$(cd "$(dirname "$0")/.." && pwd)" 11 | 12 | LINUX_EXEC="$ROOT/scala-native-bindgen-linux" 13 | if [[ ! -e "$LINUX_EXEC" ]]; then 14 | docker-compose build bindgen 15 | container="$(docker container create "scalabindgen/scala-native-bindgen:${VERSION:-latest}")" 16 | docker cp "$container:/scala-native-bindgen" "$LINUX_EXEC" 17 | docker container rm "$container" 18 | fi 19 | 20 | MACOS_EXEC="$ROOT/scala-native-bindgen-darwin" 21 | if [[ "$(uname -s)" = "Darwin" ]] && [[ ! -e "$MACOS_EXEC" ]]; then 22 | json_dir="$(ls /usr/local/Cellar/nlohmann_json/ | sort | tail -n 1)/include" 23 | rm -rf bindgen/target 24 | mkdir -p bindgen/target 25 | (cd bindgen/target && cmake -DSTATIC_LINKING=ON -DCMAKE_CXX_FLAGS="-isystem $json_dir" ..) 26 | make -C bindgen/target 27 | cp bindgen/target/scala-native-bindgen "$MACOS_EXEC" 28 | fi 29 | -------------------------------------------------------------------------------- /sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/test: -------------------------------------------------------------------------------- 1 | # Generate bindings in managed source mode 2 | > sources 3 | $ must-mirror target/scala-2.11/src_managed/main/sbt-scala-native-bindgen/stdlib.scala expected/stdlib.scala 4 | 5 | # Generate bindings when nativeBindgen is explicitly run 6 | > clean 7 | > nativeBindgen 8 | $ must-mirror target/scala-2.11/src_managed/main/sbt-scala-native-bindgen/stdlib.scala expected/stdlib.scala 9 | 10 | # Not generate bindings when not in managed source mode 11 | > clean 12 | > set target in (Compile, nativeBindgen) := nativeBindgenCustomTarget.value 13 | > sources 14 | $ absent src/main/scala/org/example/stdlib.scala 15 | 16 | # Support custom output path 17 | > clean 18 | > nativeBindgen 19 | $ must-mirror src/main/scala/org/example/stdlib.scala expected/stdlib.scala 20 | 21 | # Support multiple bindings 22 | > clean 23 | > set nativeBindings in Compile += nativeBindgenCoreBinding.value 24 | > nativeBindgen 25 | $ must-mirror src/main/scala/org/example/core.scala expected/core.scala 26 | $ must-mirror src/main/scala/org/example/stdlib.scala expected/stdlib.scala 27 | -------------------------------------------------------------------------------- /docs/src/paradox/bindings/posix.md: -------------------------------------------------------------------------------- 1 | # POSIX 2 | 3 | An addition to Scala Native's [POSIX](http://www.scala-native.org/en/latest/lib/posixlib.html) bindings. 4 | 5 | To use this binding add the following resolver and dependency: 6 | 7 | @@binding[posix] 8 | 9 | Binding objects are available under the package name ` 10 | 11 | | Header | Description 12 | |----------------------|------------------------------------------------------| 13 | | [`fnmatch.h`] | Check if a file or path name matches a shell pattern 14 | | [`regex.h`] | Regular expression library 15 | 16 | [`fnmatch.h`]: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/fnmatch.h.html 17 | [`regex.h`]: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/regex.h.html 18 | 19 | ## Examples 20 | 21 | Using `fnmatch.h`: 22 | 23 | @@snip [fnmatch](../../../../bindings/posix/src/test/scala/org/scalanative/bindings/tests/FnmatchSpec.scala) { #usage-example } 24 | 25 | Using `regex.h`: 26 | 27 | @@snip [regex](../../../../bindings/posix/src/test/scala/org/scalanative/bindings/tests/RegexSpec.scala) { #usage-example } 28 | -------------------------------------------------------------------------------- /bindgen/ir/types/PrimitiveType.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_SIMPLETYPE_H 2 | #define SCALA_NATIVE_BINDGEN_SIMPLETYPE_H 3 | 4 | #include "Type.h" 5 | #include 6 | 7 | /** 8 | * For example native.CInt 9 | */ 10 | class PrimitiveType : virtual public Type { 11 | public: 12 | explicit PrimitiveType(std::string type); 13 | 14 | std::string getType() const; 15 | 16 | bool usesType( 17 | const std::shared_ptr &type, bool stopOnTypeDefs, 18 | std::vector> &visitedTypes) const override; 19 | 20 | std::string str() const; 21 | 22 | std::string str(const LocationManager &locationManager) const override; 23 | 24 | bool operator==(const Type &other) const override; 25 | 26 | std::shared_ptr unrollTypedefs() const override; 27 | 28 | std::shared_ptr 29 | replaceType(const std::shared_ptr &type, 30 | const std::shared_ptr &replacement) const override; 31 | 32 | private: 33 | std::string type; 34 | }; 35 | 36 | #endif // SCALA_NATIVE_BINDGEN_SIMPLETYPE_H 37 | -------------------------------------------------------------------------------- /bindgen/ir/TypeAndName.cpp: -------------------------------------------------------------------------------- 1 | #include "TypeAndName.h" 2 | #include 3 | 4 | TypeAndName::TypeAndName(std::string name, std::shared_ptr type) 5 | : name(std::move(name)), type(std::move(type)) {} 6 | 7 | std::shared_ptr TypeAndName::getType() const { return type; } 8 | 9 | std::string TypeAndName::getName() const { return name; } 10 | 11 | void TypeAndName::setType(std::shared_ptr type) { 12 | this->type = type; 13 | } 14 | 15 | bool TypeAndName::operator==(const TypeAndName &other) const { 16 | if (this == &other) { 17 | return true; 18 | } 19 | return name == other.name && *type == *other.type; 20 | } 21 | 22 | bool TypeAndName::operator!=(const TypeAndName &other) const { 23 | return !(*this == other); 24 | } 25 | 26 | bool TypeAndName::usesType( 27 | const std::shared_ptr &type, bool stopOnTypeDefs, 28 | std::vector> &visitedTypes) const { 29 | if (*this->type == *type) { 30 | return true; 31 | } 32 | visitedTypes.clear(); 33 | return this->type->usesType(type, stopOnTypeDefs, visitedTypes); 34 | } 35 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: scala 2 | sudo: required 3 | os: linux 4 | dist: trusty 5 | jdk: oraclejdk8 6 | 7 | scala: 8 | - "2.11.12" 9 | 10 | before_script: 11 | - docker-compose build $TEST_ENV 12 | 13 | script: 14 | - docker-compose run $TEST_ENV 15 | - if [[ "$TEST_ENV" = *llvm-6.0 ]]; then 16 | export VERSION="${TRAVIS_COMMIT}"; 17 | docker-compose build bindgen; 18 | find . -name target | xargs sudo rm -rf; 19 | scripts/docker-test.sh; 20 | fi 21 | 22 | matrix: 23 | include: 24 | - env: TEST_ENV=ubuntu-18.04-llvm-6.0 25 | - env: TEST_ENV=ubuntu-18.04-llvm-5.0 26 | - env: TEST_ENV=formatting 27 | before_script: 28 | - clang-format --version 29 | script: 30 | - scripts/scalafmt --test; 31 | - scripts/clangfmt --test; 32 | 33 | before_cache: 34 | # See https://www.scala-sbt.org/1.0/docs/Travis-CI-with-sbt.html 35 | # Tricks to avoid unnecessary cache updates 36 | - find $HOME/.sbt -name "*.lock" | xargs sudo rm 37 | - find $HOME/.ivy2 -name "ivydata-*.properties" | xargs sudo rm 38 | 39 | cache: 40 | directories: 41 | - "$HOME/.ivy2/cache" 42 | - "$HOME/.sbt/boot/scala-*" 43 | - "$HOME/.coursier" 44 | -------------------------------------------------------------------------------- /bindgen/ir/types/PointerType.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_POINTERTYPE_H 2 | #define SCALA_NATIVE_BINDGEN_POINTERTYPE_H 3 | 4 | #include "Type.h" 5 | 6 | class PointerType : public Type { 7 | public: 8 | explicit PointerType(std::shared_ptr type); 9 | 10 | bool usesType( 11 | const std::shared_ptr &type, bool stopOnTypeDefs, 12 | std::vector> &visitedTypes) const override; 13 | 14 | bool findAllCycles( 15 | const std::shared_ptr &startStruct, CycleNode &cycleNode, 16 | std::vector> &visitedTypes) const override; 17 | 18 | std::string str(const LocationManager &locationManager) const override; 19 | 20 | bool operator==(const Type &other) const override; 21 | 22 | std::shared_ptr unrollTypedefs() const override; 23 | 24 | std::shared_ptr 25 | replaceType(const std::shared_ptr &type, 26 | const std::shared_ptr &replacement) const override; 27 | 28 | private: 29 | std::shared_ptr type; 30 | }; 31 | 32 | #endif // SCALA_NATIVE_BINDGEN_POINTERTYPE_H 33 | -------------------------------------------------------------------------------- /docs/src/test/scala/org/scalanative/bindgen/docs/VectorSpec.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.docs 2 | 3 | import org.scalatest.FunSpec 4 | 5 | class VectorSpec extends FunSpec { 6 | describe("vector") { 7 | it("using generated bindings") { 8 | //#step-1 9 | import org.example.vector._ 10 | import scala.scalanative.native.Zone 11 | 12 | Zone { implicit zone => 13 | val p1 = struct_point(1, 1) 14 | val p2 = struct_point(7, 4) 15 | 16 | val lineSegment1 = struct_lineSegment(p1, p2) 17 | //#step-1 18 | //#step-2 19 | import org.example.vector.implicits._ 20 | 21 | val lineSegment2 = struct_lineSegment() 22 | 23 | lineSegment2.a.x = 3 24 | lineSegment2.a.y = 4 25 | lineSegment2.b = struct_point(5, 0) 26 | //#step-2 27 | //#step-3 28 | val angle = cosine(lineSegment1, lineSegment2) 29 | //#step-3 30 | import org.scalactic.TolerantNumerics 31 | val epsilon = 1e-3f 32 | assert( 33 | (0.8.toFloat === angle)( 34 | TolerantNumerics.tolerantFloatEquality(epsilon))) 35 | //#step-1 36 | } 37 | //#step-1 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /docs/src/test/scala/org/scalanative/bindgen/docs/WordCountSpec.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.docs 2 | 3 | import org.scalatest.FunSpec 4 | 5 | class WordCountSpec extends FunSpec { 6 | describe("wordcount") { 7 | it("using generated bindings") { 8 | //#example 9 | import org.example.wordcount.WordCount._ 10 | import scalanative.native._ 11 | 12 | //#example 13 | val pathToFile = 14 | c"docs/src/test/resources/scala-native-bindings/wordcount.h" 15 | 16 | import scalanative.posix.unistd.access 17 | import scalanative.posix.fcntl.R_OK 18 | assert(access(pathToFile, R_OK) == 0, "Header file does not exist") 19 | 20 | //#example 21 | Zone { implicit zone => 22 | val result = struct_wordcount() 23 | val file = stdio.fopen(pathToFile, c"r") 24 | val code = wordcount(result, file) 25 | stdio.fclose(file) 26 | //#example 27 | import org.example.wordcount.WordCount.implicits._ 28 | assert(code == 0) 29 | assert(result.chars == 187) 30 | assert(result.words == 20) 31 | assert(result.lines == 11) 32 | //#example 33 | } 34 | //#example 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/samples/LiteralDefine.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | object LiteralDefine { 7 | 8 | object defines { 9 | val STRING: native.CString = c"Hello, World!" 10 | val LONG: native.CLong = 1000000000000L 11 | val LONG_WITHOUT_ENDING: native.CLong = 1000000000000L 12 | val LONG_LONG: native.CLongLong = 1000000000000L 13 | val MAXIMUM_SIGNED_LONG: native.CLong = 9223372036854775807L 14 | val MINIMUM_SIGNED_LONG: native.CLong = -9223372036854775808L 15 | val FLOAT: native.CDouble = 5.6 16 | val INT: native.CInt = 42 17 | val MAXIMUM_INT: native.CInt = 2147483647 18 | val NEW_INT: native.CInt = 42 19 | val NEG_INT: native.CInt = -42 20 | val SHOULD_BE_DEFINED: native.CString = c"Because INT is not equal to 0" 21 | val OCTAL: native.CInt = 139 22 | val HEXADECIMAL: native.CInt = 75 23 | val EXPONENT: native.CDouble = 1e-10 24 | val DOT_EXPONENT: native.CDouble = 0.01 25 | val HEXADECIMAL_WITHOUT_RADIX: native.CDouble = 523264 26 | val HEXADECIMAL_WITH_RADIX: native.CDouble = 7.5 27 | val HEXADECIMAL_FRACTIONAL_WITH_RADIX: native.CDouble = 0.0355225 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/samples/NativeTypes.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Until generated type aliases are pruned the following dummy typedefs 3 | * are here to avoid including standard headers: 4 | * 5 | * #include 6 | * #include 7 | */ 8 | typedef unsigned int size_t; 9 | typedef unsigned int ptrdiff_t; 10 | typedef unsigned short char16_t; 11 | typedef unsigned int char32_t; 12 | 13 | typedef void void_type; 14 | typedef char char_type; 15 | typedef signed char signed_char_type; 16 | typedef unsigned char unsigned_char_type; 17 | typedef short short_type; 18 | typedef unsigned short unsigned_short_type; 19 | typedef int int_type; 20 | typedef unsigned int unsigned_int_type; 21 | typedef long long_type; 22 | typedef long int long_int_type; 23 | typedef unsigned long unsigned_long_type; 24 | typedef unsigned long int unsigned_long_int_type; 25 | typedef long long long_long_type; 26 | typedef unsigned long long unsigned_long_long_type; 27 | typedef float float_type; 28 | typedef double double_type; 29 | typedef void *ptr_byte_type; 30 | typedef int *ptr_int_type; 31 | typedef char *cstring_type; 32 | 33 | typedef size_t size_t_type; 34 | typedef ptrdiff_t ptrdiff_t_type; 35 | typedef char16_t char16_t_type; 36 | typedef char32_t char32_t_type; -------------------------------------------------------------------------------- /tests/samples/src/test/scala/org/scalanative/bindgen/samples/FunctionSpec.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import org.scalatest.FunSpec 4 | import scalanative.native._ 5 | 6 | class FunctionSpec extends FunSpec { 7 | describe("function bindings") { 8 | it("should bind to function with no args") { 9 | assert(Function.no_args() == 42) 10 | } 11 | 12 | it("should bind to function with void args") { 13 | assert(Function.void_arg() == 1.5) 14 | } 15 | 16 | it("should bind to function with one arg") { 17 | assert(Function.one_arg(42) == '*') 18 | } 19 | 20 | it("should bind to function with two arg") { 21 | val result = Function.two_args(3.14.toFloat, 1024) 22 | val string = fromCString(result.cast[CString]) 23 | assert(string == "3.14 1024") 24 | } 25 | 26 | it("should bind to function with anonymous args") { 27 | assert(Function.anonymous_args(1.5.toFloat, 42) == 43.5) 28 | } 29 | 30 | it("should bind to function with variadic args") { 31 | Zone { implicit z => 32 | val result = 33 | Function.variadic_args(0.2, toCString("0123"), 1, 10, 100, 1000) 34 | assert(result == 3210.2) 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /docs/src/paradox/index.md: -------------------------------------------------------------------------------- 1 | # Scala Native Bindgen 2 | 3 | Scala Native Bindgen is a binding generator for Scala Native that uses [Clang] to 4 | parse C header files and generates Scala binding definitions. 5 | 6 | ## Features 7 | 8 | * possibility to reuse types from existing bindings. 9 | * type casts that make recursive structs be valid Scala Native structs. 10 | * implicit classes for structs and unions that make fields access easier. 11 | * implicit classes that add setters and getters to structs with more than 22 fields (such structs in Scala 12 | Native are represented as arrays of bytes). 13 | * literal defines embedding `#define MY_CONSTANT 42` → `val MY_CONSTANT: native.CInt = 42`. 14 | * read-only bindings for extern variables (such variables cannot be updated due to Scala Native limitation). 15 | * declarations filtering by prefix. 16 | 17 | ## License 18 | 19 | This project is distributed under the Scala license. 20 | @github[See LICENSE.txt for details](/LICENSE.txt) 21 | 22 | [Clang]: https://clang.llvm.org/ 23 | 24 | ## Table of Contents 25 | 26 | @@ toc 27 | 28 | @@@ index 29 | 30 | * [sbt](sbt.md) 31 | * [cli](cli.md) 32 | * [usage](using-bindings.md) 33 | * [configuration](configuration.md) 34 | * [Limitations](limitations.md) 35 | * [bindings](bindings/index.md) 36 | * [Contributor's Guide](contrib/index.md) 37 | 38 | @@@ 39 | -------------------------------------------------------------------------------- /tests/samples/PrivateMembers.h: -------------------------------------------------------------------------------- 1 | typedef int __int32_t; 2 | typedef __int32_t __darwin_pid_t; 3 | typedef __darwin_pid_t pid_t; // should associate pid_t with int 4 | 5 | typedef int __private_type; 6 | 7 | struct structWithPrivateType { 8 | int field1; 9 | __private_type field2; 10 | }; // will not be removed 11 | 12 | union __unionWithPrivateName { 13 | int a; 14 | }; // will not be removed 15 | 16 | struct structWithPrivateStruct { 17 | struct structWithPrivateType *s; 18 | }; // will not be removed 19 | 20 | struct normalStruct { 21 | int a; 22 | }; 23 | 24 | enum __privateEnum { A, B }; // will not be removed 25 | 26 | enum enumWithPrivateMembers { __C, D }; // will not be removed 27 | 28 | enum { __E, F }; // will not be removed 29 | 30 | typedef struct { 31 | __private_type *a; 32 | } privateStructWithTypedef, *privateStructWithTypedefPtr; // will not be removed 33 | 34 | pid_t getTypeThatUsesPrivateTypes(); // will not be removed 35 | 36 | // functions that should be removed: 37 | void __privateFunction(); 38 | 39 | // functions that should not be removed: 40 | __private_type *getPrivateType(); 41 | void usesPrivateUnion(union __unionWithPrivateName *); 42 | void usesPrivateStruct(struct structWithPrivateType *, struct normalStruct *); 43 | void usesPrivateEnum(enum __privateEnum *); 44 | 45 | extern int __private_var; 46 | -------------------------------------------------------------------------------- /bindings/utf8proc/src/test/scala/org/scalanative/bindings/tests/Utf8procSpec.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindings.tests 2 | 3 | import org.scalatest.FunSpec 4 | 5 | class Utf8procSpec extends FunSpec { 6 | describe("utf8proc") { 7 | it("should iterate UTF-8 and count character width") { 8 | //#usage-example 9 | import org.scalanative.bindings.utf8proc._ 10 | import scala.scalanative.native._ 11 | 12 | val text = c"Spørge" 13 | val textlen = string.strlen(text) 14 | 15 | val codepoint = stackalloc[utf8proc_int32_t] 16 | var textpos: CSize = 0 17 | var textwidth: CSize = 0 18 | 19 | while (textpos < textlen) { 20 | val bytes = utf8proc_iterate( 21 | text.cast[Ptr[UByte]] + textpos, 22 | textlen - textpos, 23 | codepoint 24 | ) 25 | textwidth += utf8proc_charwidth(!codepoint) 26 | textpos += bytes 27 | } 28 | 29 | assert(textlen == 7) 30 | assert(textwidth == 6) 31 | //#usage-example 32 | } 33 | 34 | it("utf8proc_tolower") { 35 | import org.scalanative.bindings.utf8proc._ 36 | 37 | val `Ø` = 0x00D8 38 | val `ø` = 0x00F8 39 | val `😱` = 0x1F631 40 | 41 | assert(utf8proc_tolower(`Ø`) == `ø`) 42 | assert(utf8proc_tolower(`😱`) == `😱`) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /bindgen/ir/LocationManager.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_LOCATIONMANAGER_H 2 | #define SCALA_NATIVE_BINDGEN_LOCATIONMANAGER_H 3 | 4 | #include "Location.h" 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using json = nlohmann::json; 11 | 12 | class LocationManager { 13 | public: 14 | explicit LocationManager(std::string mainHeaderPath); 15 | 16 | void loadConfig(const std::string &path); 17 | 18 | bool inMainFile(const Location &location) const; 19 | 20 | /** 21 | * @return true if given type is imported from another Scala object 22 | */ 23 | bool isImported(const Location &location) const; 24 | 25 | std::string getImportedType(const Location &location, 26 | const std::string &name) const; 27 | 28 | private: 29 | std::string mainHeaderPath; 30 | json config; 31 | 32 | json getHeaderEntry(const Location &location) const; 33 | 34 | void validateConfig(const json &config) const; 35 | 36 | void validateHeaderEntry(const json &headerEntry) const; 37 | 38 | void validateNames(const json &names) const; 39 | 40 | void validateKeys(const json &object, 41 | const std::unordered_set &keys) const; 42 | }; 43 | 44 | #endif // SCALA_NATIVE_BINDGEN_LOCATIONMANAGER_H 45 | -------------------------------------------------------------------------------- /tests/samples/NativeTypes.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | object NativeTypes { 7 | type size_t = native.CUnsignedInt 8 | type ptrdiff_t = native.CUnsignedInt 9 | type char16_t = native.CUnsignedShort 10 | type char32_t = native.CUnsignedInt 11 | type void_type = Unit 12 | type char_type = native.CChar 13 | type signed_char_type = native.CSignedChar 14 | type unsigned_char_type = native.CUnsignedChar 15 | type short_type = native.CShort 16 | type unsigned_short_type = native.CUnsignedShort 17 | type int_type = native.CInt 18 | type unsigned_int_type = native.CUnsignedInt 19 | type long_type = native.CLong 20 | type long_int_type = native.CLong 21 | type unsigned_long_type = native.CUnsignedLong 22 | type unsigned_long_int_type = native.CUnsignedLong 23 | type long_long_type = native.CLongLong 24 | type unsigned_long_long_type = native.CUnsignedLongLong 25 | type float_type = native.CFloat 26 | type double_type = native.CDouble 27 | type ptr_byte_type = native.Ptr[Byte] 28 | type ptr_int_type = native.Ptr[native.CInt] 29 | type cstring_type = native.CString 30 | type size_t_type = native.CSize 31 | type ptrdiff_t_type = native.CPtrDiff 32 | type char16_t_type = native.CChar16 33 | type char32_t_type = native.CChar32 34 | } 35 | -------------------------------------------------------------------------------- /docs/src/test/scala/org/example/Geometry.scala: -------------------------------------------------------------------------------- 1 | package org.example.geometry 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.link("geometry") 7 | @native.extern 8 | object Geometry { 9 | //#using-struct-point 10 | type struct_circle = native.CStruct2[com.example.custom.binding.Vector.Point, native.CDouble] 11 | //#using-struct-point 12 | def circle_area(circle: native.Ptr[struct_circle]): native.CFloat = native.extern 13 | 14 | object implicits { 15 | implicit class struct_circle_ops(val p: native.Ptr[struct_circle]) extends AnyVal { 16 | def point: native.Ptr[com.example.custom.binding.Vector.Point] = p._1 17 | def point_=(value: native.Ptr[com.example.custom.binding.Vector.Point]): Unit = !p._1 = !value 18 | def radius: native.CDouble = !p._2 19 | def radius_=(value: native.CDouble): Unit = !p._2 = value 20 | } 21 | } 22 | 23 | object struct_circle { 24 | import implicits._ 25 | def apply()(implicit z: native.Zone): native.Ptr[struct_circle] = native.alloc[struct_circle] 26 | def apply(point: native.Ptr[com.example.custom.binding.Vector.Point], radius: native.CDouble)(implicit z: native.Zone): native.Ptr[struct_circle] = { 27 | val ptr = native.alloc[struct_circle] 28 | ptr.point = point 29 | ptr.radius = radius 30 | ptr 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /bindgen/ir/Union.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_UNION_H 2 | #define SCALA_NATIVE_BINDGEN_UNION_H 3 | 4 | #include "Record.h" 5 | #include "TypeAndName.h" 6 | #include "TypeDef.h" 7 | #include "types/ArrayType.h" 8 | #include "types/PointerType.h" 9 | #include 10 | #include 11 | 12 | class Union : public Record, public ArrayType { 13 | public: 14 | Union(std::string name, std::vector> fields, 15 | uint64_t maxSize, std::shared_ptr location); 16 | 17 | std::shared_ptr generateTypeDef() override; 18 | 19 | std::string 20 | generateHelperClass(const LocationManager &locationManager) const override; 21 | 22 | bool operator==(const Type &other) const override; 23 | 24 | std::string getTypeName() const override; 25 | 26 | bool usesType( 27 | const std::shared_ptr &type, bool stopOnTypeDefs, 28 | std::vector> &visitedTypes) const override; 29 | 30 | private: 31 | std::string generateGetter(const std::shared_ptr &field, 32 | const LocationManager &locationManager) const; 33 | 34 | std::string generateSetter(const std::shared_ptr &field, 35 | const LocationManager &locationManager) const; 36 | }; 37 | 38 | #endif // SCALA_NATIVE_BINDGEN_UNION_H 39 | -------------------------------------------------------------------------------- /bindgen/ir/Enum.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_ENUM_H 2 | #define SCALA_NATIVE_BINDGEN_ENUM_H 3 | 4 | #include "LocatableType.h" 5 | #include "TypeDef.h" 6 | #include "types/PrimitiveType.h" 7 | #include 8 | 9 | class Enumerator { 10 | public: 11 | Enumerator(std::string name, int64_t value); 12 | 13 | std::string getName(); 14 | 15 | int64_t getValue(); 16 | 17 | private: 18 | std::string name; 19 | int64_t value; 20 | }; 21 | 22 | class Enum : public PrimitiveType, public LocatableType { 23 | public: 24 | Enum(std::string name, std::string type, 25 | std::vector enumerators, 26 | std::shared_ptr location); 27 | 28 | /** 29 | * @return a string that contains all enumerators. 30 | * If enum is not anonymous then enumerators are inside an object 31 | * with the same name as enum type. 32 | */ 33 | std::string getEnumerators() const; 34 | 35 | std::string str(const LocationManager &locationManager) const override; 36 | 37 | std::string getName() const; 38 | 39 | std::string getDefinition() const; 40 | 41 | private: 42 | std::string name; // non-empty 43 | std::vector enumerators; 44 | 45 | std::string getTypeCastSuffix() const; 46 | 47 | std::string getTypeAlias() const; 48 | }; 49 | 50 | #endif // SCALA_NATIVE_BINDGEN_ENUM_H 51 | -------------------------------------------------------------------------------- /bindgen/ir/types/PrimitiveType.cpp: -------------------------------------------------------------------------------- 1 | #include "PrimitiveType.h" 2 | #include "../../Utils.h" 3 | #include "../Enum.h" 4 | 5 | PrimitiveType::PrimitiveType(std::string type) : type(std::move(type)) {} 6 | 7 | std::string PrimitiveType::str() const { return handleReservedWords(type); } 8 | 9 | std::string PrimitiveType::str(const LocationManager &locationManager) const { 10 | return str(); 11 | } 12 | 13 | std::string PrimitiveType::getType() const { return type; } 14 | 15 | bool PrimitiveType::usesType( 16 | const std::shared_ptr &type, bool stopOnTypeDefs, 17 | std::vector> &visitedTypes) const { 18 | return false; 19 | } 20 | 21 | bool PrimitiveType::operator==(const Type &other) const { 22 | if (this == &other) { 23 | return true; 24 | } 25 | if (isInstanceOf(&other) && !isInstanceOf(&other)) { 26 | auto *primitiveType = dynamic_cast(&other); 27 | return type == primitiveType->type; 28 | } 29 | return false; 30 | } 31 | 32 | std::shared_ptr PrimitiveType::unrollTypedefs() const { 33 | return std::make_shared(type); 34 | } 35 | 36 | std::shared_ptr PrimitiveType::replaceType( 37 | const std::shared_ptr &type, 38 | const std::shared_ptr &replacement) const { 39 | return shared_from_this(); 40 | } 41 | -------------------------------------------------------------------------------- /bindgen/ir/types/FunctionPointerType.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_FUNCTIONPOINTERTYPE_H 2 | #define SCALA_NATIVE_BINDGEN_FUNCTIONPOINTERTYPE_H 3 | 4 | #include "Type.h" 5 | #include 6 | 7 | class FunctionPointerType : public Type { 8 | public: 9 | FunctionPointerType( 10 | std::shared_ptr returnType, 11 | std::vector> ¶metersTypes, 12 | bool isVariadic); 13 | 14 | bool usesType( 15 | const std::shared_ptr &type, bool stopOnTypeDefs, 16 | std::vector> &visitedTypes) const override; 17 | 18 | bool findAllCycles( 19 | const std::shared_ptr &startStruct, CycleNode &cycleNode, 20 | std::vector> &visitedTypes) const override; 21 | 22 | std::string str(const LocationManager &locationManager) const override; 23 | 24 | bool operator==(const Type &other) const override; 25 | 26 | std::shared_ptr unrollTypedefs() const override; 27 | 28 | std::shared_ptr 29 | replaceType(const std::shared_ptr &type, 30 | const std::shared_ptr &replacement) const override; 31 | 32 | private: 33 | std::shared_ptr returnType; 34 | std::vector> parametersTypes; 35 | bool isVariadic; 36 | }; 37 | 38 | #endif // SCALA_NATIVE_BINDGEN_FUNCTIONPOINTERTYPE_H 39 | -------------------------------------------------------------------------------- /docs/src/test/scala/org/example/WordCount.scala: -------------------------------------------------------------------------------- 1 | package org.example.wordcount 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.link("wordcount") 7 | @native.extern 8 | object WordCount { 9 | type struct_wordcount = native.CStruct3[native.CLong, native.CLong, native.CLong] 10 | //#using-stdio-file 11 | def wordcount(wordcount: native.Ptr[struct_wordcount], file: native.Ptr[scala.scalanative.native.stdio.FILE]): native.CInt = native.extern 12 | //#using-stdio-file 13 | 14 | object implicits { 15 | implicit class struct_wordcount_ops(val p: native.Ptr[struct_wordcount]) extends AnyVal { 16 | def chars: native.CLong = !p._1 17 | def chars_=(value: native.CLong): Unit = !p._1 = value 18 | def lines: native.CLong = !p._2 19 | def lines_=(value: native.CLong): Unit = !p._2 = value 20 | def words: native.CLong = !p._3 21 | def words_=(value: native.CLong): Unit = !p._3 = value 22 | } 23 | } 24 | 25 | object struct_wordcount { 26 | import implicits._ 27 | def apply()(implicit z: native.Zone): native.Ptr[struct_wordcount] = native.alloc[struct_wordcount] 28 | def apply(chars: native.CLong, lines: native.CLong, words: native.CLong)(implicit z: native.Zone): native.Ptr[struct_wordcount] = { 29 | val ptr = native.alloc[struct_wordcount] 30 | ptr.chars = chars 31 | ptr.lines = lines 32 | ptr.words = words 33 | ptr 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /bindgen/ir/Record.cpp: -------------------------------------------------------------------------------- 1 | #include "Record.h" 2 | #include "../Utils.h" 3 | 4 | Field::Field(std::string name, std::shared_ptr type) 5 | : TypeAndName(std::move(name), std::move(type)) {} 6 | 7 | Field::Field(std::string name, std::shared_ptr type, 8 | uint64_t offsetInBits) 9 | : TypeAndName(std::move(name), std::move(type)), 10 | offsetInBits(offsetInBits) {} 11 | 12 | uint64_t Field::getOffsetInBits() const { return offsetInBits; } 13 | 14 | Record::Record(std::string name, std::vector> fields, 15 | std::shared_ptr location) 16 | : LocatableType(std::move(location)), name(std::move(name)), 17 | fields(std::move(fields)) {} 18 | 19 | bool Record::usesType( 20 | const std::shared_ptr &type, bool stopOnTypeDefs, 21 | std::vector> &visitedTypes) const { 22 | 23 | if (contains(this, visitedTypes)) { 24 | return false; 25 | } 26 | visitedTypes.push_back(shared_from_this()); 27 | 28 | for (const auto &field : fields) { 29 | if (*field->getType() == *type || 30 | field->getType()->usesType(type, stopOnTypeDefs, visitedTypes)) { 31 | visitedTypes.pop_back(); 32 | return true; 33 | } 34 | } 35 | visitedTypes.pop_back(); 36 | return false; 37 | } 38 | 39 | std::string Record::getName() const { return name; } 40 | 41 | bool Record::hasHelperMethods() const { return !fields.empty(); } 42 | -------------------------------------------------------------------------------- /tests/samples/OpaqueTypes.h: -------------------------------------------------------------------------------- 1 | #include "include/OpaqueTypes.h" 2 | 3 | typedef struct points points; 4 | 5 | struct point; 6 | struct point *move(struct point *point, int x, int y); 7 | 8 | typedef union u u; 9 | 10 | union u *processPoints(points *p); 11 | 12 | union u { 13 | int i; 14 | float f; 15 | }; 16 | 17 | struct points { 18 | struct point *point1; 19 | struct point *point2; 20 | }; 21 | 22 | struct point { 23 | int x; 24 | int y; 25 | }; 26 | 27 | struct undefinedStruct; 28 | 29 | void usePointerToUndefinedStruct(struct undefinedStruct *); 30 | 31 | struct structWithPointerToUndefinedStruct { 32 | struct undefinedStruct *field; 33 | }; 34 | 35 | union unionWithPointerToUndefinedStruct { 36 | struct undefinedStruct *field; 37 | }; 38 | 39 | typedef union undefinedUnion undefinedUnion; 40 | 41 | typedef undefinedUnion *aliasToPointerOfUndefinedUnion; 42 | 43 | aliasToPointerOfUndefinedUnion *fun(); 44 | 45 | typedef struct undefinedStruct aliasForUndefinedStruct; // okay 46 | 47 | aliasForUndefinedStruct *returnPointerToAliasOfUndefinedStruct(); 48 | 49 | void usePointerToUndefinedIncludedStruct(undefinedIncludedStruct *); 50 | 51 | typedef aliasToPointerOfUndefinedUnion ( 52 | *functionPointerWithPointerToOpaqueType)(struct undefinedStruct **); 53 | 54 | void useUndefinedStruct( 55 | struct undefinedStruct); // removed. Error message is printed 56 | 57 | extern struct undefinedStruct removedExtern; // removed 58 | 59 | #define removedExternAlias removedExtern // removed 60 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG UBUNTU_VERSION=18.04 2 | FROM ubuntu:$UBUNTU_VERSION 3 | 4 | RUN set -x \ 5 | && : Remove pre-bundled libunwind \ 6 | && find /usr -name "*libunwind*" -delete \ 7 | && apt update \ 8 | && apt install -y --no-install-recommends apt-transport-https gnupg2 ca-certificates \ 9 | && echo "deb https://dl.bintray.com/sbt/debian /" > /etc/apt/sources.list.d/sbt.list \ 10 | && apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2EE0EA64E40A89B84B2DF73499E82A75642AC823 \ 11 | && apt update \ 12 | && apt install -y --no-install-recommends \ 13 | locales locales-all \ 14 | g++ openjdk-8-jdk-headless sbt cmake make curl git \ 15 | zlib1g-dev \ 16 | libgc-dev libunwind8-dev libre2-dev \ 17 | nlohmann-json-dev \ 18 | && rm -rf /var/lib/apt/lists/* 19 | 20 | ARG LOCALE=en_US.UTF-8 21 | ENV LANG=$LOCALE 22 | ENV LANGUAGE=$LOCALE 23 | ENV LC_ALL=$LOCALE 24 | 25 | ARG LLVM_VERSION=6.0 26 | ENV LLVM_VERSION=$LLVM_VERSION 27 | RUN set -x \ 28 | && apt update \ 29 | && apt install -y --no-install-recommends \ 30 | clang-$LLVM_VERSION clang-format-$LLVM_VERSION \ 31 | libclang-$LLVM_VERSION-dev llvm-$LLVM_VERSION-dev \ 32 | && rm -rf /var/lib/apt/lists/* 33 | 34 | ENV PATH=$PATH:/usr/lib/llvm-$LLVM_VERSION/bin 35 | 36 | ##bindings-dev-package 37 | # Install binding dependencies 38 | RUN set -x \ 39 | && apt update \ 40 | && apt install -y --no-install-recommends \ 41 | libutf8proc-dev \ 42 | && rm -rf /var/lib/apt/lists/* 43 | ##bindings-dev-package 44 | 45 | WORKDIR /src 46 | -------------------------------------------------------------------------------- /bindgen/ir/Record.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_RECORD_H 2 | #define SCALA_NATIVE_BINDGEN_RECORD_H 3 | 4 | #include "LocatableType.h" 5 | #include "TypeAndName.h" 6 | #include "TypeDef.h" 7 | #include 8 | #include 9 | 10 | class Field : public TypeAndName { 11 | public: 12 | Field(std::string name, std::shared_ptr type); 13 | 14 | Field(std::string name, std::shared_ptr type, 15 | uint64_t offsetInBits); 16 | 17 | uint64_t getOffsetInBits() const; 18 | 19 | protected: 20 | /** 21 | * Offset in bytes from address of struct/union. 22 | */ 23 | uint64_t offsetInBits = 0; 24 | }; 25 | 26 | class Record : public LocatableType { 27 | public: 28 | Record(std::string name, std::vector> fields, 29 | std::shared_ptr location); 30 | 31 | virtual std::shared_ptr generateTypeDef() = 0; 32 | 33 | virtual std::string 34 | generateHelperClass(const LocationManager &locationManager) const = 0; 35 | 36 | std::string getName() const; 37 | 38 | virtual std::string getTypeName() const = 0; 39 | 40 | virtual bool hasHelperMethods() const; 41 | 42 | bool usesType( 43 | const std::shared_ptr &type, bool stopOnTypeDefs, 44 | std::vector> &visitedTypes) const override; 45 | 46 | protected: 47 | std::string name; // does not contain 'struct' or 'union' word 48 | std::vector> fields; 49 | }; 50 | 51 | #endif // SCALA_NATIVE_BINDGEN_RECORD_H 52 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2018 EPFL 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of the EPFL nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 21 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 22 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 23 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /docs/src/paradox/contrib/cmake.md: -------------------------------------------------------------------------------- 1 | # Building the executable with `CMake` 2 | 3 | Building `scala-native-bindgen` requires the following tools and libraries: 4 | 5 | - [CMake] version 3.9 or higher 6 | - [GNU Make] 7 | - [LLVM] and [Clang] version 5.0 or 6.0. 8 | - [nlohmann/json] version 3.1.2 or higher 9 | 10 | ```sh 11 | # On apt-based systems 12 | apt install cmake make clang-6.0 libclang-6.0-dev llvm-6.0-dev \ 13 | nlohmann-json-dev 14 | 15 | # With `brew` 16 | brew tap nlohmann/json 17 | brew install cmake llvm@6 nlohmann_json 18 | ``` 19 | 20 | To run the tests you also need the required Scala Native libraries. 21 | See the [Scala Native setup guide] for instructions on installing the dependencies. 22 | 23 | ```sh 24 | mkdir -p bindgen/target 25 | cd bindgen/target 26 | cmake .. 27 | make 28 | ./scala-native-bindgen --name ctype /usr/include/ctype.h -- 29 | ``` 30 | 31 | To build a statically linked executable pass `-DSTATIC_LINKING=ON` when invoking `cmake`: 32 | 33 | ```sh 34 | cmake -DSTATIC_LINKING=ON .. 35 | ``` 36 | 37 | Additional compiler and linker flags may be passed as environment variable sor their CMake 38 | equivalent, e.g. to compile with debug symbols the following are the same: 39 | 40 | ```sh 41 | cmake -DCMAKE_CXX_FLAGS=-g .. 42 | CXXFLAGS=-g cmake .. 43 | ``` 44 | 45 | [CMake]: https://cmake.org/ 46 | [GNU Make]: https://www.gnu.org/software/make/ 47 | [LLVM]: https://llvm.org/ 48 | [Clang]: https://clang.llvm.org/ 49 | [Scala Native setup guide]: http://www.scala-native.org/en/latest/user/setup.html 50 | [nlohmann/json]: https://github.com/nlohmann/json 51 | -------------------------------------------------------------------------------- /tests/samples/Extern.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.link("bindgentests") 7 | @native.extern 8 | object Extern { 9 | type enum_mode = native.CUnsignedInt 10 | object enum_mode { 11 | final val SYSTEM: enum_mode = 0.toUInt 12 | final val USER: enum_mode = 1.toUInt 13 | } 14 | 15 | type struct_version = native.CStruct3[native.CInt, native.CInt, native.CInt] 16 | val forty_two: native.CInt = native.extern 17 | val version: native.CString = native.extern 18 | val mode: enum_mode = native.extern 19 | val semver: native.Ptr[struct_version] = native.extern 20 | 21 | object implicits { 22 | implicit class struct_version_ops(val p: native.Ptr[struct_version]) extends AnyVal { 23 | def major: native.CInt = !p._1 24 | def major_=(value: native.CInt): Unit = !p._1 = value 25 | def minor: native.CInt = !p._2 26 | def minor_=(value: native.CInt): Unit = !p._2 = value 27 | def patch: native.CInt = !p._3 28 | def patch_=(value: native.CInt): Unit = !p._3 = value 29 | } 30 | } 31 | 32 | object struct_version { 33 | import implicits._ 34 | def apply()(implicit z: native.Zone): native.Ptr[struct_version] = native.alloc[struct_version] 35 | def apply(major: native.CInt, minor: native.CInt, patch: native.CInt)(implicit z: native.Zone): native.Ptr[struct_version] = { 36 | val ptr = native.alloc[struct_version] 37 | ptr.major = major 38 | ptr.minor = minor 39 | ptr.patch = patch 40 | ptr 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/samples/Enum.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.link("bindgentests") 7 | @native.extern 8 | object Enum { 9 | type enum_days = native.CUnsignedInt 10 | object enum_days { 11 | final val MONDAY: enum_days = 0.toUInt 12 | final val TUESDAY: enum_days = 200.toUInt 13 | final val WEDNESDAY: enum_days = 201.toUInt 14 | final val THURSDAY: enum_days = 4.toUInt 15 | final val FRIDAY: enum_days = 5.toUInt 16 | final val SATURDAY: enum_days = 3.toUInt 17 | final val SUNDAY: enum_days = 4.toUInt 18 | } 19 | 20 | type enum_bigValues = native.CUnsignedLong 21 | object enum_bigValues { 22 | final val BIG_A: enum_bigValues = 10000000000L.toULong 23 | final val BIG_B: enum_bigValues = 1L.toULong 24 | } 25 | 26 | type enum_anonymous_0 = native.CUnsignedInt 27 | object enum_anonymous_0 { 28 | final val ANON_A: enum_anonymous_0 = 0.toUInt 29 | final val ANON_B: enum_anonymous_0 = 1.toUInt 30 | } 31 | 32 | type enum_negativeValues = native.CInt 33 | object enum_negativeValues { 34 | final val NEG_A: enum_negativeValues = -1 35 | final val NEG_B: enum_negativeValues = -2 36 | } 37 | 38 | type enum_bigNegativeValues = native.CLong 39 | object enum_bigNegativeValues { 40 | final val BIG_NEG_A: enum_bigNegativeValues = -10000000000L 41 | final val BIG_NEG_B: enum_bigNegativeValues = -1L 42 | } 43 | 44 | def get_WEDNESDAY(): enum_days = native.extern 45 | def check_BIG_NEG_A(big_neg_a: enum_bigNegativeValues): native.CString = native.extern 46 | } 47 | -------------------------------------------------------------------------------- /bindgen/ir/Function.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_FUNCTION_H 2 | #define SCALA_NATIVE_BINDGEN_FUNCTION_H 3 | 4 | #include "TypeAndName.h" 5 | #include "TypeDef.h" 6 | #include 7 | #include 8 | #include 9 | 10 | class Parameter : public TypeAndName { 11 | public: 12 | Parameter(std::string name, std::shared_ptr type); 13 | }; 14 | 15 | class Function { 16 | public: 17 | Function(const std::string &name, 18 | std::vector> parameters, 19 | std::shared_ptr retType, bool isVariadic); 20 | 21 | std::string getDefinition(const LocationManager &locationManager) const; 22 | 23 | bool usesType(std::shared_ptr type, bool stopOnTypeDefs, 24 | std::vector> &visitedTypes) const; 25 | 26 | std::string getName() const; 27 | 28 | void setScalaName(std::string scalaName); 29 | 30 | /** 31 | * @return true if the function does not use values of structs or arrays 32 | * (note: unions are represented as arrays) 33 | */ 34 | bool isLegalScalaNativeFunction() const; 35 | 36 | private: 37 | std::string getVarargsParameterName() const; 38 | 39 | bool existsParameterWithName(const std::string ¶meterName) const; 40 | 41 | std::string name; // real name of the function 42 | std::string scalaName; // not empty 43 | std::vector> parameters; 44 | std::shared_ptr retType; 45 | bool isVariadic; 46 | }; 47 | 48 | #endif // SCALA_NATIVE_BINDGEN_FUNCTION_H 49 | -------------------------------------------------------------------------------- /docs/src/paradox/limitations.md: -------------------------------------------------------------------------------- 1 | # Limitations 2 | 3 | There are multiple unsupported cases that should be considered when generating bindings. 4 | 5 | @@ toc 6 | 7 | ## Passing structs by value 8 | 9 | Scala Native does not support passing structs by value, bindgen skips such functions. 10 | ```c 11 | struct MyStruct { 12 | int a; 13 | }; 14 | 15 | struct MyStruct returnStruct(); // skipped 16 | 17 | void handleStruct(struct MyStruct mystr); // skipped 18 | ``` 19 | To support such cases one should generate bindings for C wrapper functions that use pointers to structs instead of 20 | actual structs. 21 | 22 | ## Limited support of `#define`s 23 | 24 | `#define`s for literals and variables are supported. For other types of `#define`s, 25 | write wrapper functions that return defined values. 26 | 27 | ```c 28 | // Supported 29 | #define ESC 0x1b /* Defines for numerical and string literals. */ 30 | extern const int pi_const; 31 | #define PI pi_const /* Defines aliasing extern variables. */ 32 | 33 | // Not supported (non-exhaustive list) 34 | #define COLS (getenv("COLS") ? atoi(getenv("COLS")) : 80) 35 | #define MAX(a, b) (a > b ? a : b) 36 | ``` 37 | 38 | ## Reusing generated bindings 39 | 40 | There is no way to reuse already generated bindings. 41 | Bindgen outputs bindings also for headers that were included in a given header. See @github[#2](#2). 42 | 43 | ## Type qualifiers 44 | 45 | Type qualifiers `const`, `volatile` and `restrict` are not supported. 46 | 47 | ## Updating extern variables 48 | 49 | Extern variables are read-only. See @github[scala-native/scala-native#202](scala-native/scala-native#202). 50 | -------------------------------------------------------------------------------- /docs/src/paradox/sbt.md: -------------------------------------------------------------------------------- 1 | # Generating Bindings with sbt 2 | 3 | To add the sbt plugin to your project add the following lines in `project/plugins.sbt`: 4 | 5 | @@snip [sbt-plugin] (../../../project/plugins.sbt) { #sbt-plugin-example } 6 | 7 | Replacing `VERSION` with @var[project.version] to use the latest version. 8 | 9 | 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. 10 | 11 | @@@ note 12 | 13 | By default the `scala-native-bindgen` executable is downloaded automatically for supported platforms. Set `version in nativeBindgen` (unscoped) to configure the version of the `scala-native-bindgen` to use if you want a version different from the version of the sbt plugin. 14 | 15 | In case your platform is not supported, you must compile `scala-native-bindgen` yourself and configure the path to the executable using `nativeBindgenPath`, e.g.: 16 | 17 | ```sbt 18 | nativeBindgenPath := file("/path/to/scala-native-bindgen") 19 | ``` 20 | 21 | @@@ 22 | 23 | Example settings: 24 | 25 | @@snip [sbt] (../../../sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/build.sbt) { #example } 26 | 27 | Given that the `stdlib.h` header file contains: 28 | 29 | @@snip [sbt] (../../../sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/src/main/resources/stdlib.h) 30 | 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: 32 | 33 | @@snip [sbt] (../../../sbt-scala-native-bindgen/src/sbt-test/bindgen/generate/expected/stdlib.scala) -------------------------------------------------------------------------------- /tests/samples/Function.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.link("bindgentests") 7 | @native.extern 8 | object Function { 9 | type struct_s = native.CStruct1[native.CInt] 10 | type s = struct_s 11 | type union_u = native.CArray[Byte, native.Nat._4] 12 | def no_args(): native.CInt = native.extern 13 | def void_arg(): native.CFloat = native.extern 14 | def one_arg(a: native.CInt): native.CChar = native.extern 15 | def two_args(a: native.CFloat, b: native.CInt): native.Ptr[Byte] = native.extern 16 | def anonymous_args(p0: native.CFloat, p1: native.CInt): native.CDouble = native.extern 17 | def variadic_args(a: native.CDouble, varArgs: native.CString, varArgs0: native.CVararg*): native.CDouble = native.extern 18 | def acceptsArray(p0: native.Ptr[native.CInt]): Unit = native.extern 19 | 20 | object implicits { 21 | implicit class struct_s_ops(val p: native.Ptr[struct_s]) extends AnyVal { 22 | def `val`: native.CInt = !p._1 23 | def `val_=`(value: native.CInt): Unit = !p._1 = value 24 | } 25 | 26 | implicit class union_u_pos(val p: native.Ptr[union_u]) extends AnyVal { 27 | def a: native.Ptr[native.CInt] = p.cast[native.Ptr[native.CInt]] 28 | def a_=(value: native.CInt): Unit = !p.cast[native.Ptr[native.CInt]] = value 29 | } 30 | } 31 | 32 | object struct_s { 33 | import implicits._ 34 | def apply()(implicit z: native.Zone): native.Ptr[struct_s] = native.alloc[struct_s] 35 | def apply(`val`: native.CInt)(implicit z: native.Zone): native.Ptr[struct_s] = { 36 | val ptr = native.alloc[struct_s] 37 | ptr.`val` = `val` 38 | ptr 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /bindgen/TypeTranslator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ir/IR.h" 4 | #include 5 | 6 | class TypeTranslator { 7 | public: 8 | TypeTranslator(clang::ASTContext *ctx, IR &ir); 9 | 10 | /** 11 | * @brief Translate the qualified type from c to a scala type 12 | * @param tpe The type to translate 13 | * @return the type translated or nullptr if type is function type. 14 | */ 15 | std::shared_ptr translate(const clang::QualType &tpe); 16 | 17 | std::string getTypeFromTypeMap(std::string cType); 18 | 19 | std::shared_ptr addUnionDefinition(clang::RecordDecl *record, 20 | std::string name); 21 | 22 | std::shared_ptr addStructDefinition(clang::RecordDecl *record, 23 | std::string name); 24 | 25 | std::shared_ptr getLocation(clang::Decl *decl); 26 | 27 | private: 28 | clang::ASTContext *ctx; 29 | IR &ir; 30 | int anonymousStructId = 0; 31 | int anonymousUnionId = 0; 32 | 33 | /** 34 | * Primitive types 35 | */ 36 | std::map typeMap; 37 | 38 | std::shared_ptr 39 | translateNonAnonymousRecord(const clang::QualType &qtpe); 40 | 41 | std::shared_ptr translateRecord(const clang::QualType &qtpe); 42 | 43 | std::shared_ptr translateFunctionPointer(const clang::QualType &qtpe); 44 | 45 | std::shared_ptr translatePointer(const clang::QualType &pointee); 46 | 47 | std::shared_ptr 48 | translateConstantArray(const clang::ConstantArrayType *ar); 49 | 50 | std::shared_ptr translateEnum(const clang::QualType &type); 51 | }; 52 | -------------------------------------------------------------------------------- /tests/samples/Union.c: -------------------------------------------------------------------------------- 1 | #include "Union.h" 2 | #include 3 | #include 4 | 5 | int union_get_sizeof() { return sizeof(union values); } 6 | 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 | } 16 | 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 | } 56 | 57 | int union_test_struct(union values *v, enum union_op op, struct s *value) { 58 | switch (op) { 59 | case UNION_SET: 60 | v->structInUnion.a = value->a; 61 | return 1; 62 | case UNION_TEST: 63 | return v->structInUnion.a == value->a; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tests/samples/LiteralDefine.h: -------------------------------------------------------------------------------- 1 | #define STRING "Hello, World!" 2 | 3 | #define LONG 1000000000000l 4 | #define LONG_WITHOUT_ENDING 1000000000000 5 | #define LONG_LONG 1000000000000ll 6 | 7 | /* maximum unsigned long does not fit into long type 8 | * therefore warning will be printed and definition will be ignored. */ 9 | #define MAXIMUM_UNSIGNED_LONG 18446744073709551615 10 | #define MAXIMUM_SIGNED_LONG 9223372036854775807 // OK 11 | 12 | #define MINIMUM_SIGNED_LONG -9223372036854775808 // OK 13 | #define LESS_THEN_MINIMUM_SIGNED_LONG -9223372036854775809 // excluded 14 | 15 | #define FLOAT 5.6f 16 | 17 | #define INT 42 18 | #define MAXIMUM_INT 2147483647 19 | #define NEW_INT INT 20 | #define NEG_INT -INT 21 | 22 | #define __PRIVATE 1 23 | 24 | #define wait_for_it(cond) \ 25 | do { \ 26 | sleep(1000); \ 27 | } while (!cond) // unsupported 28 | 29 | #ifdef NOT_DEFINED 30 | #define SHOULD_NOT_BE_DEFINED "Because NOT_DEFINED is not defined" 31 | #endif 32 | 33 | #define DEFINED_ONLY_IN_HEADER "Defined only inside the header file" 34 | 35 | #if INT == 0 36 | #define SHOULD_NOT_BE_DEFINED "Because INT is 42" 37 | #else 38 | #define SHOULD_BE_DEFINED "Because INT is not equal to 0" 39 | #endif 40 | 41 | #undef DEFINED_ONLY_IN_HEADER 42 | 43 | // integer literals 44 | #define OCTAL 0213 // 139 45 | #define HEXADECIMAL 0x4b // 75 46 | 47 | // floating point literals 48 | #define EXPONENT 1e-10L 49 | #define DOT_EXPONENT 1.e-2 50 | #define HEXADECIMAL_WITHOUT_RADIX 0x1ffp10 51 | #define HEXADECIMAL_WITH_RADIX 0xf.p-1 52 | #define HEXADECIMAL_FRACTIONAL_WITH_RADIX 0x0.123p-1 53 | -------------------------------------------------------------------------------- /tests/src/test/scala/org/scalanative/bindgen/BindgenSpec.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen 2 | 3 | import java.io.File 4 | import org.scalatest.FunSpec 5 | import scala.io.Source 6 | 7 | class BindgenSpec extends FunSpec { 8 | describe("Bindgen") { 9 | val bindgen = Bindgen(new File(System.getProperty("bindgen.path"))) 10 | val inputDirectory = new File("samples") 11 | 12 | val outputDir = new File("target/bindgen-samples") 13 | Option(outputDir.listFiles()).foreach(_.foreach(_.delete())) 14 | outputDir.mkdirs() 15 | 16 | it("executable should exist") { 17 | assert(bindgen.executable.exists, s"${bindgen.executable} does not exist") 18 | } 19 | 20 | def contentOf(file: File) = 21 | Source.fromFile(file).getLines.mkString("\n").trim() 22 | 23 | for (input <- inputDirectory.listFiles() if input.getName.endsWith(".h")) { 24 | it(s"should generate bindings for ${input.getName}") { 25 | val testName = input.getName.replace(".h", "") 26 | val expected = new File(inputDirectory, testName + ".scala") 27 | val config = new File(inputDirectory, testName + ".json") 28 | var options = BindingOptions(input) 29 | .name(testName) 30 | .link("bindgentests") 31 | .packageName("org.scalanative.bindgen.samples") 32 | .excludePrefix("__") 33 | 34 | if (config.exists()) { 35 | options = options.bindingConfig(config) 36 | } 37 | 38 | bindgen.generate(options) match { 39 | case Right(binding) => 40 | assert(binding.source.trim() == contentOf(expected)) 41 | case Left(errors) => 42 | fail("scala-native-bindgen failed: " + errors.mkString("\n")) 43 | } 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/samples/Struct.c: -------------------------------------------------------------------------------- 1 | #include "Struct.h" 2 | #include 3 | 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() { 25 | point_s point = malloc(sizeof(struct point)); 26 | point->x = 10; 27 | point->y = 20; 28 | return point; 29 | } 30 | 31 | int getBigStructSize() { return sizeof(struct bigStruct); } 32 | 33 | char getCharFromAnonymousStruct(struct structWithAnonymousStruct *s) { 34 | return s->anonymousStruct.c; 35 | } 36 | 37 | char getIntFromAnonymousStruct(struct structWithAnonymousStruct *s) { 38 | return s->anonymousStruct.i; 39 | } 40 | 41 | int struct_test_long(struct bigStruct *s, enum struct_op op, long value) { 42 | switch (op) { 43 | case STRUCT_SET: 44 | s->one = value; 45 | return 1; 46 | case STRUCT_TEST: 47 | return s->one == value; 48 | } 49 | } 50 | 51 | int struct_test_double(struct bigStruct *s, enum struct_op op, double value) { 52 | switch (op) { 53 | case STRUCT_SET: 54 | s->five = value; 55 | return 1; 56 | case STRUCT_TEST: 57 | return s->five == value; 58 | } 59 | } 60 | 61 | int struct_test_point(struct bigStruct *s, enum struct_op op, 62 | struct point *value) { 63 | switch (op) { 64 | case STRUCT_SET: 65 | s->six = *value; 66 | return 1; 67 | case STRUCT_TEST: 68 | return (s->six.x == value->x) && (s->six.y == value->y); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /docs/src/test/scala/org/example/vector.scala: -------------------------------------------------------------------------------- 1 | package org.example 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.link("vector") 7 | @native.extern 8 | object vector { 9 | type struct_point = native.CStruct2[native.CFloat, native.CFloat] 10 | type struct_lineSegment = native.CStruct2[struct_point, struct_point] 11 | def cosine(v1: native.Ptr[struct_lineSegment], v2: native.Ptr[struct_lineSegment]): native.CFloat = native.extern 12 | 13 | object implicits { 14 | implicit class struct_point_ops(val p: native.Ptr[struct_point]) extends AnyVal { 15 | def x: native.CFloat = !p._1 16 | def x_=(value: native.CFloat): Unit = !p._1 = value 17 | def y: native.CFloat = !p._2 18 | def y_=(value: native.CFloat): Unit = !p._2 = value 19 | } 20 | 21 | implicit class struct_lineSegment_ops(val p: native.Ptr[struct_lineSegment]) extends AnyVal { 22 | def a: native.Ptr[struct_point] = p._1 23 | def a_=(value: native.Ptr[struct_point]): Unit = !p._1 = !value 24 | def b: native.Ptr[struct_point] = p._2 25 | def b_=(value: native.Ptr[struct_point]): Unit = !p._2 = !value 26 | } 27 | } 28 | 29 | object struct_point { 30 | import implicits._ 31 | def apply()(implicit z: native.Zone): native.Ptr[struct_point] = native.alloc[struct_point] 32 | def apply(x: native.CFloat, y: native.CFloat)(implicit z: native.Zone): native.Ptr[struct_point] = { 33 | val ptr = native.alloc[struct_point] 34 | ptr.x = x 35 | ptr.y = y 36 | ptr 37 | } 38 | } 39 | 40 | object struct_lineSegment { 41 | import implicits._ 42 | def apply()(implicit z: native.Zone): native.Ptr[struct_lineSegment] = native.alloc[struct_lineSegment] 43 | def apply(a: native.Ptr[struct_point], b: native.Ptr[struct_point])(implicit z: native.Zone): native.Ptr[struct_lineSegment] = { 44 | val ptr = native.alloc[struct_lineSegment] 45 | ptr.a = a 46 | ptr.b = b 47 | ptr 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /bindgen/ir/types/ArrayType.cpp: -------------------------------------------------------------------------------- 1 | #include "ArrayType.h" 2 | #include "../../Utils.h" 3 | #include "../Union.h" 4 | 5 | ArrayType::ArrayType(std::shared_ptr elementsType, uint64_t size) 6 | : size(size), elementsType(std::move(elementsType)) {} 7 | 8 | std::string ArrayType::str(const LocationManager &locationManager) const { 9 | return "native.CArray[" + elementsType->str(locationManager) + ", " + 10 | uint64ToScalaNat(size) + "]"; 11 | } 12 | 13 | bool ArrayType::usesType( 14 | const std::shared_ptr &type, bool stopOnTypeDefs, 15 | std::vector> &visitedTypes) const { 16 | if (contains(this, visitedTypes)) { 17 | return false; 18 | } 19 | visitedTypes.push_back(shared_from_this()); 20 | bool result = *elementsType == *type || 21 | elementsType->usesType(type, stopOnTypeDefs, visitedTypes); 22 | visitedTypes.pop_back(); 23 | return result; 24 | } 25 | 26 | bool ArrayType::operator==(const Type &other) const { 27 | if (this == &other) { 28 | return true; 29 | } 30 | if (isInstanceOf(&other) && !isInstanceOf(&other)) { 31 | auto *arrayType = dynamic_cast(&other); 32 | if (size != arrayType->size) { 33 | return false; 34 | } 35 | return *elementsType == *arrayType->elementsType; 36 | } 37 | return false; 38 | } 39 | 40 | std::shared_ptr ArrayType::unrollTypedefs() const { 41 | return std::make_shared(elementsType->unrollTypedefs(), size); 42 | } 43 | 44 | std::shared_ptr 45 | ArrayType::replaceType(const std::shared_ptr &type, 46 | const std::shared_ptr &replacement) const { 47 | 48 | if (*elementsType == *replacement) { 49 | return std::make_shared(replacement, size); 50 | } 51 | return std::make_shared( 52 | elementsType->replaceType(type, replacement), size); 53 | } 54 | -------------------------------------------------------------------------------- /bindgen/ir/Enum.cpp: -------------------------------------------------------------------------------- 1 | #include "Enum.h" 2 | 3 | Enumerator::Enumerator(std::string name, int64_t value) 4 | : name(std::move(name)), value(value) {} 5 | 6 | std::string Enumerator::getName() { return name; } 7 | 8 | int64_t Enumerator::getValue() { return value; } 9 | 10 | Enum::Enum(std::string name, std::string type, 11 | std::vector enumerators, 12 | std::shared_ptr location) 13 | : PrimitiveType(std::move(type)), LocatableType(std::move(location)), 14 | name(std::move(name)), enumerators(std::move(enumerators)) {} 15 | 16 | std::string Enum::getEnumerators() const { 17 | std::stringstream s; 18 | std::string typeCastSuffix = getTypeCastSuffix(); 19 | std::string type = getTypeAlias(); 20 | s << " object " << type << " {\n"; 21 | for (auto enumerator : enumerators) { 22 | s << " final val " << enumerator.getName(); 23 | s << ": " << type << " = " << std::to_string(enumerator.getValue()) 24 | << typeCastSuffix << "\n"; 25 | } 26 | s << " }\n"; 27 | return s.str(); 28 | } 29 | 30 | std::string Enum::getTypeCastSuffix() const { 31 | std::string primitiveType = PrimitiveType::getType(); 32 | if (primitiveType == "native.CLong") { 33 | return "L"; 34 | } else if (primitiveType == "native.CUnsignedInt") { 35 | return ".toUInt"; 36 | } else if (primitiveType == "native.CUnsignedLong") { 37 | return "L.toULong"; 38 | } 39 | return ""; 40 | } 41 | 42 | std::string Enum::str(const LocationManager &locationManager) const { 43 | if (locationManager.isImported(*location)) { 44 | return locationManager.getImportedType(*location, getTypeAlias()); 45 | } 46 | return getTypeAlias(); 47 | } 48 | 49 | std::string Enum::getTypeAlias() const { return "enum_" + name; } 50 | 51 | std::string Enum::getDefinition() const { 52 | return " type " + getTypeAlias() + " = " + PrimitiveType::str() + "\n"; 53 | } 54 | 55 | std::string Enum::getName() const { return name; } 56 | -------------------------------------------------------------------------------- /bindgen/ir/TypeDef.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_TYPEDEF_H 2 | #define SCALA_NATIVE_BINDGEN_TYPEDEF_H 3 | 4 | #include "LocatableType.h" 5 | #include "Location.h" 6 | #include "TypeAndName.h" 7 | #include 8 | #include 9 | 10 | class TypeDef : public TypeAndName, public LocatableType { 11 | public: 12 | /** 13 | * @param location nullptr if typedef is generated 14 | */ 15 | TypeDef(std::string name, std::shared_ptr type, 16 | std::shared_ptr location); 17 | 18 | std::string getDefinition(const LocationManager &locationManager) const; 19 | 20 | bool usesType( 21 | const std::shared_ptr &type, bool stopOnTypeDefs, 22 | std::vector> &visitedTypes) const override; 23 | 24 | std::string str(const LocationManager &locationManager) const override; 25 | 26 | bool operator==(const Type &other) const override; 27 | 28 | /** 29 | * @return true if the typedef is not generated alias for opaque type. 30 | */ 31 | bool hasLocation() const; 32 | 33 | /** 34 | * @return true if typedef directly references an opaque type 35 | */ 36 | bool wrapperForOpaqueType() const; 37 | 38 | bool findAllCycles( 39 | const std::shared_ptr &startStruct, CycleNode &cycleNode, 40 | std::vector> &visitedTypes) const override; 41 | 42 | std::shared_ptr unrollTypedefs() const override; 43 | 44 | std::shared_ptr 45 | replaceType(const std::shared_ptr &type, 46 | const std::shared_ptr &replacement) const override; 47 | 48 | /** 49 | * @return location of the typedef is it is not generated otherwise 50 | * location of referenced record. 51 | */ 52 | std::shared_ptr getLocation() const override; 53 | 54 | private: 55 | bool isGenerated() const; 56 | }; 57 | 58 | #endif // SCALA_NATIVE_BINDGEN_TYPEDEF_H 59 | -------------------------------------------------------------------------------- /tests/samples/Cycles.h: -------------------------------------------------------------------------------- 1 | struct node { 2 | int value; 3 | struct node *next; 4 | }; 5 | 6 | struct b; 7 | struct c; 8 | 9 | struct a { 10 | struct b *bb; 11 | }; 12 | 13 | struct b { 14 | struct c **cc; // type will be replace with Ptr[Ptr[Byte]] 15 | }; 16 | 17 | struct c { 18 | struct a aa; 19 | }; 20 | 21 | /* function pointer type */ 22 | 23 | struct FuncPointerCycle2; 24 | 25 | struct FuncPointerCycle1 { 26 | struct FuncPointerCycle2 *s; 27 | }; 28 | 29 | struct FuncPointerCycle2 { 30 | struct FuncPointerCycle1 *(*memberFunction)( 31 | void); // type will be replace with CFunctionPtr0[Ptr[Byte]] 32 | }; 33 | 34 | /* type has typedef alias */ 35 | 36 | struct TypeWithTypedef1; 37 | 38 | typedef struct TypeWithTypedef1 TypeWithTypedef1; 39 | 40 | struct TypeWithTypedef2 { 41 | TypeWithTypedef1 *s; // replaced with Ptr[Byte] 42 | }; 43 | 44 | struct TypeWithTypedef1 { 45 | struct TypeWithTypedef2 *s; 46 | }; 47 | 48 | /* two types should be replaced */ 49 | 50 | struct TwoTypesReplaced1; 51 | struct TwoTypesReplaced2; 52 | 53 | struct TwoTypesReplaced3 { 54 | struct TwoTypesReplaced1 *(*memberFunction)(struct TwoTypesReplaced2 *); 55 | }; 56 | 57 | struct TwoTypesReplaced1 { 58 | struct TwoTypesReplaced2 *s; 59 | }; 60 | 61 | struct TwoTypesReplaced2 { 62 | struct TwoTypesReplaced3 *s; 63 | }; 64 | 65 | /* cycle contains union. 66 | * unions are represented as arrays therefore they cannot belong to Scala Native 67 | * cycle of types */ 68 | 69 | union cycleWithUnionU; 70 | 71 | struct cycleWithUnionS { 72 | union cycleWithUnionU *u; 73 | }; 74 | 75 | union cycleWithUnionU { 76 | struct cycleWithUnionS *s; 77 | }; 78 | 79 | /* function pointer uses value type instead of pointer type */ 80 | 81 | struct FuncPointerWithValueType2; 82 | 83 | struct FuncPointerWithValueType1 { 84 | struct FuncPointerWithValueType2 *s; 85 | }; 86 | 87 | struct FuncPointerWithValueType2 { 88 | struct FuncPointerWithValueType1 (*memberFunction)( 89 | void); // return type will be replaced by CStruct0 90 | }; -------------------------------------------------------------------------------- /docs/src/test/scala/com/example/custom/binding/Vector.scala: -------------------------------------------------------------------------------- 1 | //#example 2 | package com.example.custom.binding 3 | 4 | import scala.scalanative._ 5 | import scala.scalanative.native._ 6 | 7 | @native.link("vector") 8 | @native.extern 9 | object Vector { 10 | type Point = native.CStruct2[native.CFloat, native.CFloat] 11 | type LineSegment = native.CStruct2[Point, Point] 12 | // ... 13 | //#example 14 | def cosine(v1: native.Ptr[LineSegment], 15 | v2: native.Ptr[LineSegment]): native.CFloat = native.extern 16 | 17 | object implicits { 18 | implicit class PointOps(val p: native.Ptr[Point]) extends AnyVal { 19 | def x: native.CFloat = !p._1 20 | def x_=(value: native.CFloat): Unit = !p._1 = value 21 | def y: native.CFloat = !p._2 22 | def y_=(value: native.CFloat): Unit = !p._2 = value 23 | } 24 | 25 | implicit class LineSegmentOps(val p: native.Ptr[LineSegment]) 26 | extends AnyVal { 27 | def a: native.Ptr[Point] = p._1 28 | def a_=(value: native.Ptr[Point]): Unit = !p._1 = !value 29 | def b: native.Ptr[Point] = p._2 30 | def b_=(value: native.Ptr[Point]): Unit = !p._2 = !value 31 | } 32 | } 33 | 34 | object Point { 35 | import implicits._ 36 | def apply()(implicit z: native.Zone): native.Ptr[Point] = 37 | native.alloc[Point] 38 | def apply(x: native.CFloat, y: native.CFloat)( 39 | implicit z: native.Zone): native.Ptr[Point] = { 40 | val ptr = native.alloc[Point] 41 | ptr.x = x 42 | ptr.y = y 43 | ptr 44 | } 45 | } 46 | 47 | object LineSegment { 48 | import implicits._ 49 | def apply()(implicit z: native.Zone): native.Ptr[LineSegment] = 50 | native.alloc[LineSegment] 51 | def apply(a: native.Ptr[Point], b: native.Ptr[Point])( 52 | implicit z: native.Zone): native.Ptr[LineSegment] = { 53 | val ptr = native.alloc[LineSegment] 54 | ptr.a = a 55 | ptr.b = b 56 | ptr 57 | } 58 | } 59 | //#example 60 | } 61 | //#example 62 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Guide for contributors 2 | 3 | Scala Native Bindgen follows Scala Native's [contributing guidelines]. 4 | Larger contributions should always begin by creating an issue to 5 | ensure that it is properly scoped. 6 | 7 | Important to note is that all contributors must have signed the [Scala CLA]. 8 | 9 | [contributing guidelines]: http://www.scala-native.org/en/latest/contrib/contributing.html 10 | [Scala CLA]: https://www.lightbend.com/contribute/cla/scala 11 | 12 | ## Developer Workflow 13 | 14 | Build the `scalaBindgen` tool: 15 | 16 | ```sh 17 | mkdir bindgen/target 18 | cd bindgen/target 19 | cmake .. 20 | make 21 | 22 | # Alternatively rerun on change 23 | watchman-make -p '*.cpp' '*.h' --run 'make -C bindgen/target' 24 | ``` 25 | 26 | In another terminal, run the test suite: 27 | 28 | ```sh 29 | cd tests 30 | sbt ~test 31 | ``` 32 | 33 | To run the same tests as is run by Travis CI use the `test.sh` script, which will both build the executable and run `sbt verify`: 34 | 35 | ```sh 36 | ./scripts/test.sh 37 | ``` 38 | 39 | ## Coding Guidelines 40 | 41 | Code should be formatted with `./scripts/scalafmt` and `./scripts/clangfmt`. 42 | Make sure that all of your contributions are properly formatted before 43 | suggesting any changes. You can check the formatting using either: 44 | 45 | ```sh 46 | scripts/scalafmt --test 47 | scripts/clangfmt --test 48 | ``` 49 | 50 | or: 51 | 52 | ```sh 53 | docker-compose run --rm ubuntu-18.04-llvm-6.0 scripts/scalafmt --test 54 | docker-compose run --rm ubuntu-18.04-llvm-6.0 scripts/clangfmt --test 55 | ``` 56 | 57 | The C++ tool is built on Clang and Libtooling and should respect the conventions of 58 | LLVM and Clang tools. The code itself should adhere to the [LLVM Coding Standards], 59 | specifically: 60 | 61 | - For code generation and error reporting use `llvm::outs()` and `llvm::errs()`. 62 | - [Use `\n` instead of `std::endl`](https://llvm.org/docs/CodingStandards.html#avoid-std-endl) 63 | and remember to flush when reporting errors. 64 | 65 | [LLVM Coding Standards]: https://llvm.org/docs/CodingStandards.html 66 | -------------------------------------------------------------------------------- /tests/samples/Struct.h: -------------------------------------------------------------------------------- 1 | struct point { 2 | int x; 3 | int y; 4 | }; 5 | 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 | 19 | typedef struct point *point_s; 20 | 21 | point_s createPoint(); 22 | 23 | struct bigStruct { 24 | long one; 25 | char two; 26 | int three; 27 | float four; 28 | double five; 29 | struct point six; 30 | struct point *seven; 31 | int eight; 32 | int nine; 33 | int ten; 34 | int eleven; 35 | int twelve; 36 | int thirteen; 37 | int fourteen; 38 | int fifteen; 39 | int sixteen; 40 | int seventeen; 41 | int eighteen; 42 | int nineteen; 43 | int twenty; 44 | int twentyOne; 45 | int twentyTwo; 46 | int twentyThree; 47 | }; 48 | 49 | int getBigStructSize(); 50 | 51 | struct structWithAnonymousStruct { 52 | int a; 53 | struct { 54 | char c; 55 | int i; 56 | } anonymousStruct; 57 | }; 58 | 59 | struct __attribute__((__packed__)) packedStruct { // no helper methods 60 | char a; 61 | }; 62 | 63 | struct bitFieldStruct { // no helper methods 64 | unsigned char b1 : 3; 65 | unsigned char : 0; // start a new byte 66 | unsigned char b2 : 6; 67 | unsigned char b3 : 2; 68 | }; 69 | 70 | struct bitFieldOffsetDivByEight { // no helper methods 71 | unsigned b1 : 8; 72 | unsigned b2 : 8; 73 | unsigned char b3 : 8; 74 | }; 75 | 76 | char getCharFromAnonymousStruct(struct structWithAnonymousStruct *s); 77 | 78 | char getIntFromAnonymousStruct(struct structWithAnonymousStruct *s); 79 | 80 | enum struct_op { STRUCT_SET, STRUCT_TEST }; 81 | 82 | int struct_test_long(struct bigStruct *s, enum struct_op op, long value); 83 | int struct_test_double(struct bigStruct *s, enum struct_op op, double value); 84 | int struct_test_point(struct bigStruct *s, enum struct_op op, 85 | struct point *value); 86 | -------------------------------------------------------------------------------- /scripts/clangfmt: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Format C/C++ code using clang-format. 4 | # 5 | # To ensure reproducible formatting the script checks that clang-format 6 | # is from the most recent version of LLVM supported by Scala Native. 7 | # 8 | # Usage: $0 [--test] 9 | # 10 | # Set CLANG_FORMAT_PATH to configure path to clang-format. 11 | 12 | set -euo pipefail 13 | IFS=$'\n\t' 14 | 15 | # The required version of clang-format. 16 | CLANG_FORMAT_VERSION=5.0 17 | CLANG_FORMAT_VERSION_STRING="clang-format version $CLANG_FORMAT_VERSION" 18 | 19 | die() { 20 | while [ "$#" -gt 0 ]; do 21 | echo >&2 "$1"; shift 22 | done 23 | exit 1 24 | } 25 | 26 | check_clang_format_version() { 27 | cmd="$1" 28 | [ -e "$(type -P "$cmd")" ] && \ 29 | "$cmd" --version 2> /dev/null | grep -q "$CLANG_FORMAT_VERSION_STRING" 30 | } 31 | 32 | clang_format= 33 | 34 | if [ -n "${CLANG_FORMAT_PATH:-}" ]; then 35 | check_clang_format_version "$CLANG_FORMAT_PATH" || \ 36 | die "CLANG_FORMAT_PATH does not have required version $CLANG_FORMAT_VERSION" \ 37 | "CLANG_FORMAT_PATH points to $CLANG_FORMAT_PATH" 38 | clang_format="$CLANG_FORMAT_PATH" 39 | else 40 | for cmd in "clang-format-$CLANG_FORMAT_VERSION" clang-format; do 41 | if check_clang_format_version "$cmd"; then 42 | clang_format="$cmd" 43 | break 44 | fi 45 | done 46 | fi 47 | 48 | if [ -z "$clang_format" ]; then 49 | die "clang-format version $CLANG_FORMAT_VERSION not found" \ 50 | "Install LLVM version $CLANG_FORMAT_VERSION and rerun." 51 | fi 52 | 53 | test_mode= 54 | 55 | while [ "$#" -gt 0 ]; do 56 | arg="$1" 57 | case "$arg" in 58 | --test) test_mode=true; shift ;; 59 | --*) die "Unknown argument: $arg" "Usage: $0 [--test]" ;; 60 | *) break ;; 61 | esac 62 | done 63 | 64 | if [ "$#" -gt 0 ]; then 65 | "$clang_format" --style=file -i "$@" 66 | else 67 | find . -name "*.[ch]" -or -name "*.cpp" | xargs "$clang_format" --style=file -i 68 | fi 69 | 70 | if [ "$test_mode" = true ]; then 71 | git diff --quiet --exit-code || \ 72 | die "C/C++ code formatting changes detected" \ 73 | "Run \`$0\` to reformat." 74 | fi 75 | -------------------------------------------------------------------------------- /bindgen/ir/types/PointerType.cpp: -------------------------------------------------------------------------------- 1 | #include "PointerType.h" 2 | #include "../../Utils.h" 3 | 4 | PointerType::PointerType(std::shared_ptr type) 5 | : type(std::move(type)) {} 6 | 7 | std::string PointerType::str(const LocationManager &locationManager) const { 8 | return "native.Ptr[" + type->str(locationManager) + "]"; 9 | } 10 | 11 | bool PointerType::usesType( 12 | const std::shared_ptr &type, bool stopOnTypeDefs, 13 | std::vector> &visitedTypes) const { 14 | if (contains(this, visitedTypes)) { 15 | return false; 16 | } 17 | visitedTypes.push_back(shared_from_this()); 18 | bool result = *this->type == *type || 19 | this->type->usesType(type, stopOnTypeDefs, visitedTypes); 20 | visitedTypes.pop_back(); 21 | return result; 22 | } 23 | 24 | bool PointerType::operator==(const Type &other) const { 25 | if (this == &other) { 26 | return true; 27 | } 28 | if (isInstanceOf(&other)) { 29 | auto *pointerType = dynamic_cast(&other); 30 | return *type == *pointerType->type; 31 | } 32 | return false; 33 | } 34 | 35 | bool PointerType::findAllCycles( 36 | const std::shared_ptr &startStruct, CycleNode &cycleNode, 37 | std::vector> &visitedTypes) const { 38 | if (contains(this, visitedTypes)) { 39 | return false; 40 | } 41 | visitedTypes.push_back(shared_from_this()); 42 | bool result = 43 | this->type->findAllCycles(startStruct, cycleNode, visitedTypes); 44 | visitedTypes.pop_back(); 45 | return result; 46 | } 47 | 48 | std::shared_ptr PointerType::unrollTypedefs() const { 49 | return std::make_shared(type->unrollTypedefs()); 50 | } 51 | 52 | std::shared_ptr 53 | PointerType::replaceType(const std::shared_ptr &type, 54 | const std::shared_ptr &replacement) const { 55 | if (*this->type == *type) { 56 | return std::make_shared(replacement); 57 | } 58 | return std::make_shared( 59 | this->type->replaceType(type, replacement)); 60 | } 61 | -------------------------------------------------------------------------------- /bindgen/defines/DefineFinder.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_DEFINEFINDER_H 2 | #define SCALA_NATIVE_BINDGEN_DEFINEFINDER_H 3 | 4 | #include "../ir/IR.h" 5 | #include 6 | #include 7 | #include 8 | 9 | class DefineFinder : public clang::PPCallbacks { 10 | public: 11 | DefineFinder(IR &ir, const clang::CompilerInstance &compiler, 12 | clang::Preprocessor &pp); 13 | 14 | void MacroDefined(const clang::Token ¯oNameTok, 15 | const clang::MacroDirective *md) override; 16 | 17 | void MacroUndefined(const clang::Token ¯oNameTok, 18 | const clang::MacroDefinition &md, 19 | const clang::MacroDirective *undef) override; 20 | 21 | private: 22 | IR &ir; 23 | const clang::CompilerInstance &compiler; 24 | clang::Preprocessor &pp; 25 | 26 | void addNumericConstantDefine(const std::string ¯oName, 27 | std::string literal, 28 | const clang::Token &token, 29 | bool positive = true); 30 | 31 | /** 32 | * Check if the number fits into int or long variable. 33 | * 34 | * @return type of the number 35 | */ 36 | std::string 37 | getTypeOfIntegerLiteral(const clang::NumericLiteralParser &parser, 38 | const std::string &literal, bool positive); 39 | 40 | std::vector *expandDefine(const clang::MacroDirective &md); 41 | 42 | bool isMacro(const clang::Token &token); 43 | 44 | bool isFunctionLikeMacro(const clang::Token &token); 45 | 46 | /** 47 | * @return true if number contained in parser fits into int type 48 | */ 49 | template 50 | bool integerFitsIntoType(clang::NumericLiteralParser parser, bool positive); 51 | 52 | std::string getDecimalLiteral(clang::NumericLiteralParser parser); 53 | 54 | std::string getDoubleLiteral(clang::NumericLiteralParser parser); 55 | 56 | bool fitsIntoDouble(clang::NumericLiteralParser parser); 57 | }; 58 | 59 | #endif // SCALA_NATIVE_BINDGEN_DEFINEFINDER_H 60 | -------------------------------------------------------------------------------- /docs/src/paradox/contrib/bindings.md: -------------------------------------------------------------------------------- 1 | # Contributing Bindings 2 | 3 | ## Adding a new Bindings 4 | 5 | To add a new binding, add a new project in `build.sbt` with the name of the artifact as well as a binding configuration for each header file and optionally the library to link with. 6 | 7 | @@snip [build.sbt](../../../../build.sbt) { #sbt-binding-project } 8 | 9 | It is also possible to generate multiple bindings per project. For most libraries a single binding should be sufficient. 10 | 11 | @@snip [build.sbt](../../../../build.sbt) { #sbt-binding-project-multi-header } 12 | 13 | The new sbt project should also be added to the `bindings` project's list of aggregated projects. 14 | 15 | Optionally update the top-level `Dockerfile` to install the binding library's `-dev` package. 16 | 17 | @@snip [Dockerfile](../../../../Dockerfile) { #bindings-dev-package } 18 | 19 | Remember to rebuild the development docker image if you change the `Dockerfile`. 20 | 21 | ```sh 22 | docker-compose build ubuntu-18.04-llvm-6.0 23 | ``` 24 | 25 | Next, run `nativeBindgen` to generate the bindings. Note, to ensure consistency the bindings should be generated in the Linux docker environment. 26 | This can be done by running: 27 | 28 | ```sh 29 | docker-compose run --rm ubuntu-18.04-llvm-6.0 sbt lib$BINDING/nativeBindgen 30 | ``` 31 | 32 | Then write tests for the generated bindings. The test class should be in the `org.scalanative.bindgen.bindings.tests` package to make it easier to write examples for the binding documentation. Verify that the tests pass: 33 | 34 | ```sh 35 | docker-compose run --rm ubuntu-18.04-llvm-6.0 sbt lib$BINDING/test 36 | ``` 37 | 38 | Finally, add a Markdown file in the `bindings` folder and document the binding, ideally with an example or two of how to use it. Remember to add the new file to the index in `bindings/index.md`. 39 | 40 | ## Update all Bindings 41 | 42 | To update the bindings, build the Linux docker environment and run `bindings/nativeBindgen`: 43 | 44 | ```sh 45 | docker-compose build ubuntu-18.04-llvm-6.0 46 | docker-compose run --rm ubuntu-18.04-llvm-6.0 sbt bindings/nativeBindgen 47 | ``` 48 | 49 | Run the tests afterwards to verify that everything works: 50 | 51 | ```sh 52 | docker-compose run --rm ubuntu-18.04-llvm-6.0 sbt bindings/test 53 | ``` 54 | -------------------------------------------------------------------------------- /tests/samples/ReuseBindings.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.link("bindgentests") 7 | @native.extern 8 | object ReuseBindings { 9 | type aliasForBigStruct = org.scalanative.bindgen.samples.Struct.struct_bigStruct 10 | type struct_usesImportedEnum = native.CStruct2[org.scalanative.bindgen.samples.Struct.enum_pointIndex, org.scalanative.bindgen.samples.CustomNames.weight] 11 | def useStruct(p0: native.Ptr[org.scalanative.bindgen.samples.Struct.struct_point]): Unit = native.extern 12 | def returnTypedef_point_s(): native.Ptr[org.scalanative.bindgen.samples.Struct.struct_point] = native.extern 13 | def returnTypedef_point(): native.Ptr[org.scalanative.bindgen.samples.Struct.point] = native.extern 14 | def readBook(book: native.Ptr[org.scalanative.bindgen.samples.CustomNames.book]): Unit = native.extern 15 | def getWeight(weight: native.Ptr[org.scalanative.bindgen.samples.CustomNames.weight]): Unit = native.extern 16 | def getMyInt(): org.scalanative.bindgen.samples.CustomNames.MY_INT = native.extern 17 | def getEnum(): org.scalanative.bindgen.samples.CustomNames.EnumWithTypedef = native.extern 18 | 19 | object implicits { 20 | implicit class struct_usesImportedEnum_ops(val p: native.Ptr[struct_usesImportedEnum]) extends AnyVal { 21 | def index: org.scalanative.bindgen.samples.Struct.enum_pointIndex = !p._1 22 | def index_=(value: org.scalanative.bindgen.samples.Struct.enum_pointIndex): Unit = !p._1 = value 23 | def weight: native.Ptr[org.scalanative.bindgen.samples.CustomNames.weight] = p._2 24 | def weight_=(value: native.Ptr[org.scalanative.bindgen.samples.CustomNames.weight]): Unit = !p._2 = !value 25 | } 26 | } 27 | 28 | object struct_usesImportedEnum { 29 | import implicits._ 30 | def apply()(implicit z: native.Zone): native.Ptr[struct_usesImportedEnum] = native.alloc[struct_usesImportedEnum] 31 | def apply(index: org.scalanative.bindgen.samples.Struct.enum_pointIndex, weight: native.Ptr[org.scalanative.bindgen.samples.CustomNames.weight])(implicit z: native.Zone): native.Ptr[struct_usesImportedEnum] = { 32 | val ptr = native.alloc[struct_usesImportedEnum] 33 | ptr.index = index 34 | ptr.weight = weight 35 | ptr 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tools/src/main/scala/org/scalanative/bindgen/Bindgen.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen 2 | 3 | import java.io.File 4 | 5 | import scala.collection.immutable.Seq 6 | import scala.collection.mutable.ListBuffer 7 | import scala.sys.process.{Process, ProcessLogger} 8 | 9 | class Bindgen(val executable: File) { 10 | 11 | /** 12 | * Run binding generator using the given scala-native-bindgen executable. 13 | * @return errors if exit code was not 0, otherwise return bindings 14 | */ 15 | def generate(options: BindingOptions): Either[Seq[String], Bindings] = { 16 | options match { 17 | case impl: BindingOptions.Impl => 18 | import impl._ 19 | 20 | require(executable.exists, "The executable does not exist") 21 | 22 | val nameOrLibrary = name.orElse(library) 23 | require(nameOrLibrary.isDefined, 24 | "Name must be specified when no library name is given") 25 | 26 | def withArgs(arg: String, values: Iterable[String]) = 27 | values.toSeq.flatMap(Seq(arg, _)) 28 | 29 | val options = 30 | withArgs("--name", nameOrLibrary) ++ 31 | withArgs("--link", library) ++ 32 | library.fold(Seq("--no-link"))(_ => Seq.empty) ++ 33 | withArgs("--package", packageName) ++ 34 | withArgs("--exclude-prefix", excludePrefix) ++ 35 | withArgs("--extra-arg", extraArgs) ++ 36 | withArgs("--extra-arg-before", extraArgsBefore) ++ 37 | withArgs("--binding-config", bindingConfig.map(_.getAbsolutePath)) ++ 38 | Seq(header.getAbsolutePath, "--") 39 | 40 | val cmd = Seq(executable.getAbsolutePath) ++ options 41 | val stdout = ListBuffer[String]() 42 | val stderr = ListBuffer[String]() 43 | val nl = System.lineSeparator() 44 | val logger = ProcessLogger(stdout.+=, stderr.+=) 45 | 46 | Process(cmd).!(logger) match { 47 | case 0 => 48 | Right( 49 | new Bindings(nameOrLibrary.get, 50 | stdout.mkString(nl), 51 | Seq(stderr: _*))) 52 | case _ => Left(Seq(stderr: _*)) 53 | } 54 | 55 | case _ => Left(Seq("Illtyped BindingOptions")) 56 | } 57 | } 58 | 59 | } 60 | 61 | object Bindgen { 62 | def apply(executable: File) = 63 | new Bindgen(executable) 64 | } 65 | -------------------------------------------------------------------------------- /bindings/iconv/src/test/scala/org/scalanative/bindings/tests/IconvSpec.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindings.tests 2 | 3 | import org.scalatest.FunSpec 4 | 5 | class IconvSpec extends FunSpec { 6 | describe("iconv") { 7 | it("should convert back and forth between UTF-8 and ISO-8859-1") { 8 | //#usage-example 9 | import org.scalanative.bindings.iconv._ 10 | import scala.scalanative.native._ 11 | import java.nio.charset.Charset 12 | 13 | val UTF8 = Charset.forName("UTF-8") 14 | val encode = iconv_open(c"UTF-8", c"ISO-8859-1") 15 | val decode = iconv_open(c"ISO-8859-1", c"UTF-8") 16 | 17 | Zone { implicit zone => 18 | val originalBuf = toCString("øre", UTF8) // Ear in Danish 19 | val originalBufPtr = alloc[CString] 20 | !originalBufPtr = originalBuf 21 | val originalBytesLeft = alloc[CSize] 22 | !originalBytesLeft = string.strlen(originalBuf) 23 | //#usage-example 24 | assert(!originalBytesLeft == 4) 25 | //#usage-example 26 | 27 | val translatedBuf = alloc[Byte](32) 28 | val translatedBufPtr = alloc[CString] 29 | !translatedBufPtr = translatedBuf 30 | val translatedBytesLeft = alloc[CSize] 31 | !translatedBytesLeft = 32 32 | 33 | val translatedCode = iconv( 34 | encode, 35 | originalBufPtr, 36 | originalBytesLeft, 37 | translatedBufPtr, 38 | translatedBytesLeft 39 | ) 40 | //#usage-example 41 | 42 | assert(translatedCode == 0) 43 | 44 | !translatedBufPtr = translatedBuf 45 | !translatedBytesLeft = string.strlen(translatedBuf) 46 | 47 | val roundtripBuf = alloc[Byte](32) 48 | val roundtripBufPtr = alloc[CString] 49 | !roundtripBufPtr = roundtripBuf 50 | val roundtripBytesLeft = alloc[CSize] 51 | !roundtripBytesLeft = 32 52 | 53 | val roundtripCode = iconv( 54 | decode, 55 | translatedBufPtr, 56 | translatedBytesLeft, 57 | roundtripBufPtr, 58 | roundtripBytesLeft 59 | ) 60 | 61 | assert(string.strcmp(originalBuf, roundtripBuf) == 0) 62 | //#usage-example 63 | } 64 | //#usage-example 65 | 66 | //#usage-example 67 | iconv_close(encode) 68 | //#usage-example 69 | iconv_close(decode) 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /docs/src/paradox/using-bindings.md: -------------------------------------------------------------------------------- 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 6 | 7 | Consider following header file: 8 | 9 | @@snip [vector.h] (../test/resources/using-bindings/vector.h) 10 | 11 | Bindgen will output 12 | 13 | * type aliases for the structs 14 | * binding for function `cosine` 15 | * helper functions that make usage of structs easier 16 | 17 | @@snip [vector.h] (../test/scala/org/example/vector.scala) 18 | 19 | ## Using the Vector Library 20 | 21 | Let's write code that creates two line segments and calculates the angel between them. 22 | 23 | First we need to create points. We will use `native.Zone` to 24 | allocate struct (more information on memory management can be found 25 | here: [Scala Native memory management]). 26 | 27 | The generated bindings contain helper functions that make struct allocation easier. 28 | To import them use `import org.example.vector._` 29 | 30 | Let's create two points and the first line segment: 31 | 32 | @@snip [step-1] (../test/scala/org/scalanative/bindgen/docs/VectorSpec.scala) { #step-1 } 33 | 34 | There is no need to create points manually, just call `struct_lineSegment` 35 | constructor and set point coordinates using fields setters. 36 | 37 | Scala Native allows us to access a field by using `_N` method where `N` is index of a field 38 | (see [Scala Native memory layout types]) but it is not convenient because we have to 39 | match indices with fields names. 40 | 41 | Bindgen provides implicit helper classes that wrap calls to `_N` in functions 42 | with meaningful names. To import these classes add `import org.example.vector.implicits._` 43 | to your code: 44 | 45 | @@snip [step-2] (../test/scala/org/scalanative/bindgen/docs/VectorSpec.scala) { #step-2 } 46 | 47 | @@@ note 48 | 49 | `struct_lineSegment` contains fields of value type `struct_point` 50 | but setters accept variables of type `native.Ptr[struct_point]`. 51 | The reason is that Scala Native does not allow passing structs 52 | and arrays by value (see @github[scala-native/scala-native#555](scala-native/scala-native#555)). 53 | 54 | @@@ 55 | 56 | Now we can calculate the angel between the line segments: 57 | 58 | @@snip [step-3] (../test/scala/org/scalanative/bindgen/docs/VectorSpec.scala) { #step-3 } 59 | 60 | [Scala Native memory management]: http://www.scala-native.org/en/latest/user/interop.html#memory-management 61 | [Scala Native memory layout types]: http://www.scala-native.org/en/latest/user/interop.html#memory-layout-types 62 | -------------------------------------------------------------------------------- /docs/src/paradox/cli.md: -------------------------------------------------------------------------------- 1 | # Generating Bindings with the CLI 2 | 3 | Statically linked executables are provided with each release for Linux and macOS. Head over to the [releases page] to download the latest version for your platform. 4 | 5 | [releases page]: https://github.com/scala-native/scala-native-bindgen/releases 6 | 7 | @@@ note 8 | 9 | In the following we assume you have renamed the downloaded `scala-native-bindgen-$PLATFORM` file to `scala-native-bindgen`. 10 | 11 | @@@ 12 | 13 | ## Command Line Usage 14 | 15 | When generating bindings with the CLI, you need to specify the header file and provide the name of the created bindings using the `--name` option: 16 | 17 | ```sh 18 | scala-native-bindgen --name fnmatch /usr/include/fnmatch.h -- 19 | ``` 20 | 21 | When running the CLI it will also yield warnings along with the translation. To keep only the bindings please redirect the output to a file like this: 22 | 23 | ```sh 24 | scala-native-bindgen --name fnmatch /usr/include/fnmatch.h -- > fnmatch.scala 25 | ``` 26 | 27 | By default it is assumed that you want to link with a library based on the name option. 28 | In case the name of the library to link with is different from the binding name provide the library name using `--link`: 29 | 30 | ```sh 31 | scala-native-bindgen --name zlib --link z /usr/include/zlib.h -- 32 | ``` 33 | 34 | If the binding does not require any linking, pass `--no-link`: 35 | 36 | ```sh 37 | scala-native-bindgen --name fnmatch --no-link /usr/include/fnmatch.h -- 38 | ``` 39 | 40 | For libraries that require linking you m 41 | 42 | ## Options 43 | 44 | The generated bindings can be configured using the different options and it is also possible to pass arguments directly to the Clang compiler using the `--extra-arg*` options. 45 | 46 | | Option | Description 47 | |----------------------|---------------------------------------------------------------------------------| 48 | | `--link` | Library to link with, e.g. `--link uv` for libuv. 49 | | `--no-link` | Library does not require linking. 50 | | `--name` | Scala object name that contains bindings. Defaults to the library name. 51 | | `--package` | Package name of generated Scala file. 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. 54 | | `--extra-arg` | Additional argument to append to the compiler command line. 55 | | `--extra-arg-before` | Additional argument to prepend to the compiler command line. 56 | -------------------------------------------------------------------------------- /tests/samples/IncludesHeader.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.link("bindgentests") 7 | @native.extern 8 | object IncludesHeader { 9 | type enum_semester = native.CUnsignedInt 10 | object enum_semester { 11 | final val AUTUMN: enum_semester = 0.toUInt 12 | final val SPRING: enum_semester = 1.toUInt 13 | } 14 | 15 | type size = native.CInt 16 | type struct_metadata = native.CStruct2[native.CUnsignedInt, native.CString] 17 | type metadata = struct_metadata 18 | type struct_document = native.CStruct1[metadata] 19 | type struct_courseInfo = native.CStruct2[native.CString, enum_semester] 20 | def getSize(d: native.Ptr[struct_document]): size = native.extern 21 | 22 | object implicits { 23 | implicit class struct_metadata_ops(val p: native.Ptr[struct_metadata]) extends AnyVal { 24 | def year: native.CUnsignedInt = !p._1 25 | def year_=(value: native.CUnsignedInt): Unit = !p._1 = value 26 | def publisher: native.CString = !p._2 27 | def publisher_=(value: native.CString): Unit = !p._2 = value 28 | } 29 | 30 | implicit class struct_document_ops(val p: native.Ptr[struct_document]) extends AnyVal { 31 | def m: native.Ptr[metadata] = p._1 32 | def m_=(value: native.Ptr[metadata]): Unit = !p._1 = !value 33 | } 34 | 35 | implicit class struct_courseInfo_ops(val p: native.Ptr[struct_courseInfo]) extends AnyVal { 36 | def name: native.CString = !p._1 37 | def name_=(value: native.CString): Unit = !p._1 = value 38 | def s: enum_semester = !p._2 39 | def s_=(value: enum_semester): Unit = !p._2 = value 40 | } 41 | } 42 | 43 | object struct_metadata { 44 | import implicits._ 45 | def apply()(implicit z: native.Zone): native.Ptr[struct_metadata] = native.alloc[struct_metadata] 46 | def apply(year: native.CUnsignedInt, publisher: native.CString)(implicit z: native.Zone): native.Ptr[struct_metadata] = { 47 | val ptr = native.alloc[struct_metadata] 48 | ptr.year = year 49 | ptr.publisher = publisher 50 | ptr 51 | } 52 | } 53 | 54 | object struct_document { 55 | import implicits._ 56 | def apply()(implicit z: native.Zone): native.Ptr[struct_document] = native.alloc[struct_document] 57 | def apply(m: native.Ptr[metadata])(implicit z: native.Zone): native.Ptr[struct_document] = { 58 | val ptr = native.alloc[struct_document] 59 | ptr.m = m 60 | ptr 61 | } 62 | } 63 | 64 | object struct_courseInfo { 65 | import implicits._ 66 | def apply()(implicit z: native.Zone): native.Ptr[struct_courseInfo] = native.alloc[struct_courseInfo] 67 | def apply(name: native.CString, s: enum_semester)(implicit z: native.Zone): native.Ptr[struct_courseInfo] = { 68 | val ptr = native.alloc[struct_courseInfo] 69 | ptr.name = name 70 | ptr.s = s 71 | ptr 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/samples/Union.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.link("bindgentests") 7 | @native.extern 8 | object Union { 9 | type enum_union_op = native.CUnsignedInt 10 | object enum_union_op { 11 | final val UNION_SET: enum_union_op = 0.toUInt 12 | final val UNION_TEST: enum_union_op = 1.toUInt 13 | } 14 | 15 | type struct_s = native.CStruct1[native.CInt] 16 | type union_values = native.CArray[Byte, native.Nat._8] 17 | def union_get_sizeof(): native.CInt = native.extern 18 | def union_test_int(v: native.Ptr[union_values], op: enum_union_op, value: native.CInt): native.CInt = native.extern 19 | def union_test_long(v: native.Ptr[union_values], op: enum_union_op, value: native.CLong): native.CInt = native.extern 20 | def union_test_long_long(v: native.Ptr[union_values], op: enum_union_op, value: native.CLongLong): native.CInt = native.extern 21 | def union_test_double(v: native.Ptr[union_values], op: enum_union_op, value: native.CDouble): native.CInt = native.extern 22 | def union_test_string(v: native.Ptr[union_values], op: enum_union_op, value: native.CString): native.CInt = native.extern 23 | def union_test_struct(v: native.Ptr[union_values], op: enum_union_op, value: native.Ptr[struct_s]): native.CInt = native.extern 24 | 25 | object implicits { 26 | implicit class struct_s_ops(val p: native.Ptr[struct_s]) extends AnyVal { 27 | def a: native.CInt = !p._1 28 | def a_=(value: native.CInt): Unit = !p._1 = value 29 | } 30 | 31 | implicit class union_values_pos(val p: native.Ptr[union_values]) extends AnyVal { 32 | def l: native.Ptr[native.CLong] = p.cast[native.Ptr[native.CLong]] 33 | def l_=(value: native.CLong): Unit = !p.cast[native.Ptr[native.CLong]] = value 34 | def i: native.Ptr[native.CInt] = p.cast[native.Ptr[native.CInt]] 35 | def i_=(value: native.CInt): Unit = !p.cast[native.Ptr[native.CInt]] = value 36 | def ll: native.Ptr[native.CLongLong] = p.cast[native.Ptr[native.CLongLong]] 37 | def ll_=(value: native.CLongLong): Unit = !p.cast[native.Ptr[native.CLongLong]] = value 38 | def d: native.Ptr[native.CDouble] = p.cast[native.Ptr[native.CDouble]] 39 | def d_=(value: native.CDouble): Unit = !p.cast[native.Ptr[native.CDouble]] = value 40 | def s: native.Ptr[native.CString] = p.cast[native.Ptr[native.CString]] 41 | def s_=(value: native.CString): Unit = !p.cast[native.Ptr[native.CString]] = value 42 | def structInUnion: native.Ptr[struct_s] = p.cast[native.Ptr[struct_s]] 43 | def structInUnion_=(value: native.Ptr[struct_s]): Unit = !p.cast[native.Ptr[struct_s]] = !value 44 | } 45 | } 46 | 47 | object struct_s { 48 | import implicits._ 49 | def apply()(implicit z: native.Zone): native.Ptr[struct_s] = native.alloc[struct_s] 50 | def apply(a: native.CInt)(implicit z: native.Zone): native.Ptr[struct_s] = { 51 | val ptr = native.alloc[struct_s] 52 | ptr.a = a 53 | ptr 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /tests/src/test/scala/org/scalanative/bindgen/BindgenReportingSpec.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen 2 | 3 | import java.io.{File, PrintWriter} 4 | 5 | import org.scalatest.FunSpec 6 | 7 | class BindgenReportingSpec extends FunSpec { 8 | describe("Bindgen") { 9 | 10 | val bindgen = Bindgen(new File(System.getProperty("bindgen.path"))) 11 | 12 | def writeToFile(file: File, input: String): Unit = { 13 | new PrintWriter(file) { 14 | try { 15 | write(input) 16 | } finally { 17 | close() 18 | } 19 | } 20 | } 21 | 22 | def assertBindgenError(input: String, errors: Seq[String]): Unit = { 23 | val tempFile = File.createTempFile("scala-native-bindgen-tests", ".h") 24 | try { 25 | writeToFile(tempFile, input) 26 | 27 | val options = BindingOptions(tempFile) 28 | .name("BindgenTests") 29 | .link("bindgentests") 30 | .packageName("org.scalanative.bindgen.samples") 31 | .excludePrefix("__") 32 | 33 | bindgen.generate(options) match { 34 | case Right(binding) => 35 | assert(binding.errors == errors) 36 | case Left(errors) => 37 | fail(s"Non-zero exit code:\n${errors.mkString("\n")}") 38 | } 39 | } finally { 40 | tempFile.delete() 41 | } 42 | } 43 | 44 | it("Skips functions that pass struct or union by value") { 45 | assertBindgenError( 46 | """struct s { int a; }; 47 | |void useStruct(struct s); 48 | |typedef struct s s; 49 | |s returnStruct(); 50 | | 51 | |union u { int a; }; 52 | |void useUnion(union u); 53 | |typedef union u u; 54 | |u returnUnion(); 55 | |""".stripMargin, 56 | Seq( 57 | "Warning: Function useStruct is skipped because Scala Native does not support passing structs and arrays by value.", 58 | "Warning: Function returnStruct is skipped because Scala Native does not support passing structs and arrays by value.", 59 | "Warning: Function useUnion is skipped because Scala Native does not support passing structs and arrays by value.", 60 | "Warning: Function returnUnion is skipped because Scala Native does not support passing structs and arrays by value." 61 | ) 62 | ) 63 | } 64 | 65 | it("Skips variable with opaque type") { 66 | assertBindgenError( 67 | """struct undefinedStruct; 68 | |extern struct undefinedStruct removedExtern; 69 | |#define removedExternAlias removedExtern 70 | |""".stripMargin, 71 | Seq( 72 | "Error: Variable removedExtern is skipped because it has incomplete type.", 73 | "Error: Variable alias removedExternAlias is skipped because it has incomplete type." 74 | ) 75 | ) 76 | } 77 | 78 | it("Skips unused alias for opaque type") { 79 | assertBindgenError( 80 | """union undefinedUnion; 81 | |typedef union undefinedUnion aliasForUndefinedUnion; 82 | |void foo(); 83 | |""".stripMargin, 84 | Seq( 85 | "Warning: type alias aliasForUndefinedUnion is skipped because it is an unused alias for incomplete type.") 86 | ) 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /bindings/posix/src/main/scala/org/scalanative/bindings/posix/regex.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindings.posix 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.extern 7 | object regex { 8 | type enum_reg_errcode_t = native.CUnsignedInt 9 | object enum_reg_errcode_t { 10 | final val REG_NOERROR: enum_reg_errcode_t = 0.toUInt 11 | final val REG_NOMATCH: enum_reg_errcode_t = 1.toUInt 12 | final val REG_BADPAT: enum_reg_errcode_t = 2.toUInt 13 | final val REG_ECOLLATE: enum_reg_errcode_t = 3.toUInt 14 | final val REG_ECTYPE: enum_reg_errcode_t = 4.toUInt 15 | final val REG_EESCAPE: enum_reg_errcode_t = 5.toUInt 16 | final val REG_ESUBREG: enum_reg_errcode_t = 6.toUInt 17 | final val REG_EBRACK: enum_reg_errcode_t = 7.toUInt 18 | final val REG_EPAREN: enum_reg_errcode_t = 8.toUInt 19 | final val REG_EBRACE: enum_reg_errcode_t = 9.toUInt 20 | final val REG_BADBR: enum_reg_errcode_t = 10.toUInt 21 | final val REG_ERANGE: enum_reg_errcode_t = 11.toUInt 22 | final val REG_ESPACE: enum_reg_errcode_t = 12.toUInt 23 | final val REG_BADRPT: enum_reg_errcode_t = 13.toUInt 24 | final val REG_EEND: enum_reg_errcode_t = 14.toUInt 25 | final val REG_ESIZE: enum_reg_errcode_t = 15.toUInt 26 | final val REG_ERPAREN: enum_reg_errcode_t = 16.toUInt 27 | } 28 | 29 | type s_reg_t = native.CLong 30 | type active_reg_t = native.CUnsignedLong 31 | type reg_syntax_t = native.CUnsignedLong 32 | type reg_errcode_t = enum_reg_errcode_t 33 | type struct_re_pattern_buffer = native.CArray[Byte, native.Nat.Digit[native.Nat._6, native.Nat._4]] 34 | type regex_t = struct_re_pattern_buffer 35 | type regoff_t = native.CInt 36 | type struct_regmatch_t = native.CStruct2[regoff_t, regoff_t] 37 | type regmatch_t = struct_regmatch_t 38 | val re_syntax_options: reg_syntax_t = native.extern 39 | def regcomp(__preg: native.Ptr[regex_t], __pattern: native.CString, __cflags: native.CInt): native.CInt = native.extern 40 | def regexec(__preg: native.Ptr[regex_t], __string: native.CString, __nmatch: native.CSize, __pmatch: native.Ptr[regmatch_t], __eflags: native.CInt): native.CInt = native.extern 41 | def regerror(__errcode: native.CInt, __preg: native.Ptr[regex_t], __errbuf: native.CString, __errbuf_size: native.CSize): native.CSize = native.extern 42 | def regfree(__preg: native.Ptr[regex_t]): Unit = native.extern 43 | 44 | object defines { 45 | val _REGEX_H: native.CInt = 1 46 | val REG_EXTENDED: native.CInt = 1 47 | val REG_NOTBOL: native.CInt = 1 48 | } 49 | 50 | object implicits { 51 | implicit class struct_regmatch_t_ops(val p: native.Ptr[struct_regmatch_t]) extends AnyVal { 52 | def rm_so: regoff_t = !p._1 53 | def rm_so_=(value: regoff_t): Unit = !p._1 = value 54 | def rm_eo: regoff_t = !p._2 55 | def rm_eo_=(value: regoff_t): Unit = !p._2 = value 56 | } 57 | } 58 | 59 | object struct_regmatch_t { 60 | import implicits._ 61 | def apply()(implicit z: native.Zone): native.Ptr[struct_regmatch_t] = native.alloc[struct_regmatch_t] 62 | def apply(rm_so: regoff_t, rm_eo: regoff_t)(implicit z: native.Zone): native.Ptr[struct_regmatch_t] = { 63 | val ptr = native.alloc[struct_regmatch_t] 64 | ptr.rm_so = rm_so 65 | ptr.rm_eo = rm_eo 66 | ptr 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scala Native Binding Generator 2 | 3 | [![Build Status](https://travis-ci.org/scala-native/scala-native-bindgen.svg?branch=master)](https://travis-ci.org/scala-native/scala-native-bindgen) 4 | 5 | The tool generates Scala Native bindings from C headers. 6 | 7 | ## Documentation 8 | 9 | Documentation can be found at [scala-native.github.io/scala-native-bindgen](https://scala-native.github.io/scala-native-bindgen/). 10 | 11 | ## Bindgen Features 12 | 13 | * possibility to reuse types from existing bindings. 14 | * type casts that make recursive structs be valid Scala Native structs. 15 | * implicit classes for structs and unions that make fields access easier. 16 | * implicit classes that add setters and getters to structs with more than 22 fields (such structs in Scala 17 | Native are represented as arrays of bytes). 18 | * literal defines embedding `#define MY_CONSTANT 42` → `val MY_CONSTANT: native.CInt = 42`. 19 | * read-only bindings for extern variables (such variables cannot be updated due to Scala Native limitation). 20 | * declarations filtering by prefix. 21 | 22 | ## Example 23 | 24 | ```c 25 | struct point { 26 | float x; 27 | float y; 28 | }; 29 | 30 | struct vector { 31 | struct point a; 32 | struct point b; 33 | }; 34 | 35 | struct vector *add(struct vector *v1, struct vector *v2); 36 | ``` 37 | 38 | ```scala 39 | import scala.scalanative._ 40 | import scala.scalanative.native._ 41 | 42 | @native.link("vector") 43 | @native.extern 44 | object vector { 45 | type struct_point = native.CStruct2[native.CFloat, native.CFloat] 46 | type struct_vector = native.CStruct2[struct_point, struct_point] 47 | def add(v1: native.Ptr[struct_vector], v2: native.Ptr[struct_vector]): native.Ptr[struct_vector] = native.extern 48 | 49 | object implicits { 50 | implicit class struct_point_ops(val p: native.Ptr[struct_point]) extends AnyVal { 51 | def x: native.CFloat = !p._1 52 | def x_=(value: native.CFloat): Unit = !p._1 = value 53 | def y: native.CFloat = !p._2 54 | def y_=(value: native.CFloat): Unit = !p._2 = value 55 | } 56 | 57 | implicit class struct_vector_ops(val p: native.Ptr[struct_vector]) extends AnyVal { 58 | def a: native.Ptr[struct_point] = p._1 59 | def a_=(value: native.Ptr[struct_point]): Unit = !p._1 = !value 60 | def b: native.Ptr[struct_point] = p._2 61 | def b_=(value: native.Ptr[struct_point]): Unit = !p._2 = !value 62 | } 63 | } 64 | 65 | object struct_point { 66 | import implicits._ 67 | def apply()(implicit z: native.Zone): native.Ptr[struct_point] = native.alloc[struct_point] 68 | def apply(x: native.CFloat, y: native.CFloat)(implicit z: native.Zone): native.Ptr[struct_point] = { 69 | val ptr = native.alloc[struct_point] 70 | ptr.x = x 71 | ptr.y = y 72 | ptr 73 | } 74 | } 75 | 76 | object struct_vector { 77 | import implicits._ 78 | def apply()(implicit z: native.Zone): native.Ptr[struct_vector] = native.alloc[struct_vector] 79 | def apply(a: native.Ptr[struct_point], b: native.Ptr[struct_point])(implicit z: native.Zone): native.Ptr[struct_vector] = { 80 | val ptr = native.alloc[struct_vector] 81 | ptr.a = a 82 | ptr.b = b 83 | ptr 84 | } 85 | } 86 | } 87 | ``` 88 | 89 | ## License 90 | 91 | This project is distributed under the Scala license. 92 | [See LICENSE.txt for details](LICENSE.txt) 93 | -------------------------------------------------------------------------------- /project/ParadoxSupport.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.build 2 | 3 | import com.lightbend.paradox.markdown._ 4 | import com.lightbend.paradox.sbt.ParadoxPlugin.autoImport.paradoxDirectives 5 | import org.pegdown.Printer 6 | import org.pegdown.ast.{DirectiveNode, TextNode, Visitor} 7 | import scala.collection.JavaConverters._ 8 | 9 | object ParadoxSupport { 10 | val paradoxWithCustomDirectives = Seq( 11 | paradoxDirectives ++= Seq( 12 | { context: Writer.Context ⇒ 13 | new BindingDependencyDirective(context.location.tree.label, 14 | context.properties) 15 | } 16 | ) 17 | ) 18 | 19 | /* Based on the DependencyDirective from Paradox. */ 20 | case class BindingDependencyDirective(page: Page, 21 | variables: Map[String, String]) 22 | extends LeafBlockDirective("binding") { 23 | val projectVersion = variables("project.version") 24 | val scalaBinaryVersion = variables("scala.binary.version") 25 | 26 | def render(node: DirectiveNode, 27 | visitor: Visitor, 28 | printer: Printer): Unit = { 29 | node.contentsNode.getChildren.asScala.headOption match { 30 | case Some(text: TextNode) => 31 | renderBindingDependency(text.getText, printer) 32 | case _ => node.contentsNode.accept(visitor) 33 | } 34 | } 35 | 36 | def renderBindingDependency(binding: String, printer: Printer): Unit = { 37 | val group = "org.scala-native.bindings" 38 | val artifactName = binding 39 | val artifactId = s"${artifactName}_native0.3_${scalaBinaryVersion}" 40 | val bintrayRepo = "http://dl.bintray.com/scala-native-bindgen/maven" 41 | 42 | printer.print( 43 | s""" 44 | |
45 | |
sbt
46 | |
47 | |
resolvers += Resolver.bintrayRepo("scala-native-bindgen", "maven")
48 |            |libraryDependencies += "${group}" %%% "${artifactName}" % "${projectVersion}"
49 |            |
50 | |
51 | | 52 | |
Maven
53 | |
54 | |
<repositories>
55 |            |  <repository>
56 |            |    <id>maven</id>
57 |            |    <url>${bintrayRepo}</url>
58 |            |  </repository>
59 |            |</repositories>
60 |            |
61 |            |<dependencies>
62 |            |  <dependency>
63 |            |    <groupId>${group}</groupId>
64 |            |    <artifactId>${artifactId}</artifactId>
65 |            |    <version>${projectVersion}</version>
66 |            |  </dependency>
67 |            |</dependencies>
68 |            |
69 | |
70 | | 71 | |
Gradle
72 | |
73 | |
repositories {
74 |            |  maven {
75 |            |    url "${bintrayRepo}"
76 |            |  }
77 |            |}
78 |            |
79 |            |dependencies {
80 |            |  compile group: '${group}', name: '${artifactId}', version: '${projectVersion}'
81 |            |}
82 |            |
83 | |
84 | |
85 | |""".stripMargin 86 | ) 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /bindgen/ir/Union.cpp: -------------------------------------------------------------------------------- 1 | #include "Union.h" 2 | #include "../Utils.h" 3 | #include "Struct.h" 4 | #include "types/ArrayType.h" 5 | #include "types/FunctionPointerType.h" 6 | #include "types/PointerType.h" 7 | #include "types/PrimitiveType.h" 8 | #include 9 | 10 | Union::Union(std::string name, std::vector> fields, 11 | uint64_t maxSize, std::shared_ptr location) 12 | : Record(std::move(name), std::move(fields), std::move(location)), 13 | ArrayType(std::make_shared("Byte"), maxSize) {} 14 | 15 | std::shared_ptr Union::generateTypeDef() { 16 | return std::make_shared(getTypeName(), shared_from_this(), 17 | nullptr); 18 | } 19 | 20 | std::string 21 | Union::generateHelperClass(const LocationManager &locationManager) const { 22 | assert(hasHelperMethods()); 23 | std::stringstream s; 24 | std::string type = replaceChar(getTypeName(), " ", "_"); 25 | s << " implicit class " << type << "_pos" 26 | << "(val p: native.Ptr[" << type << "]) extends AnyVal {\n"; 27 | for (const auto &field : fields) { 28 | if (!field->getName().empty()) { 29 | s << generateGetter(field, locationManager); 30 | s << generateSetter(field, locationManager); 31 | } 32 | } 33 | s << " }\n"; 34 | return s.str(); 35 | } 36 | 37 | std::string Union::getTypeName() const { return "union " + name; } 38 | 39 | bool Union::operator==(const Type &other) const { 40 | if (this == &other) { 41 | return true; 42 | } 43 | auto *u = dynamic_cast(&other); 44 | if (u) { 45 | /* unions have unique names */ 46 | return name == u->name; 47 | } 48 | return false; 49 | } 50 | 51 | bool Union::usesType( 52 | const std::shared_ptr &type, bool stopOnTypeDefs, 53 | std::vector> &visitedTypes) const { 54 | 55 | if (contains(this, visitedTypes)) { 56 | return false; 57 | } 58 | visitedTypes.push_back(shared_from_this()); 59 | 60 | if (ArrayType::usesType(type, stopOnTypeDefs, visitedTypes)) { 61 | visitedTypes.pop_back(); 62 | return true; 63 | } 64 | visitedTypes.pop_back(); 65 | 66 | return Record::usesType(type, stopOnTypeDefs, visitedTypes); 67 | } 68 | 69 | std::string 70 | Union::generateGetter(const std::shared_ptr &field, 71 | const LocationManager &locationManager) const { 72 | std::string getter = handleReservedWords(field->getName()); 73 | std::string ftype = field->getType()->str(locationManager); 74 | return " def " + getter + ": native.Ptr[" + ftype + 75 | "] = p.cast[native.Ptr[" + ftype + "]]\n"; 76 | } 77 | 78 | std::string 79 | Union::generateSetter(const std::shared_ptr &field, 80 | const LocationManager &locationManager) const { 81 | std::string setter = handleReservedWords(field->getName(), "_="); 82 | std::string ftype = field->getType()->str(locationManager); 83 | if (isAliasForType(field->getType().get()) || 84 | isAliasForType(field->getType().get())) { 85 | return " def " + setter + "(value: native.Ptr[" + ftype + 86 | "]): Unit = !p.cast[native.Ptr[" + ftype + "]] = !value\n"; 87 | } 88 | return " def " + setter + "(value: " + ftype + 89 | "): Unit = !p.cast[native.Ptr[" + ftype + "]] = value\n"; 90 | } 91 | -------------------------------------------------------------------------------- /bindgen/ir/types/Type.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALA_NATIVE_BINDGEN_TYPE_H 2 | #define SCALA_NATIVE_BINDGEN_TYPE_H 3 | 4 | #include "../LocationManager.h" 5 | #include 6 | #include 7 | #include 8 | 9 | class Struct; 10 | class Field; 11 | 12 | struct CycleNode { 13 | CycleNode(std::shared_ptr s, const Field *field); 14 | std::shared_ptr s; 15 | const Field *field; 16 | std::vector cycleNodes; 17 | bool isValueType = false; 18 | }; 19 | 20 | /** 21 | * Base class for types. 22 | */ 23 | class Type : public std::enable_shared_from_this { 24 | public: 25 | virtual ~Type() = default; 26 | 27 | virtual std::string str(const LocationManager &locationManager) const = 0; 28 | 29 | /** 30 | * @param type search type. 31 | * @param stopOnTypeDefs if this parameter is true then TypeDefs instances 32 | * will not be checked. This parameter is needed when 33 | * usages of TypeDefs are checked, it helps to avoid 34 | * false positives when usages if aliases for the 35 | * typedef are found. 36 | * 37 | * @return true if type uses search type. 38 | */ 39 | virtual bool 40 | usesType(const std::shared_ptr &type, bool stopOnTypeDefs, 41 | std::vector> &visitedTypes) const = 0; 42 | 43 | virtual bool operator==(const Type &other) const = 0; 44 | 45 | virtual bool operator!=(const Type &other) const; 46 | 47 | /** 48 | * @param startStruct struct that should belong to found 49 | * cycles. 50 | * @param visitedTypes is used to avoid endless cycle of function calls in 51 | * the case of cyclic types. 52 | * @return true if current type belongs to one or more cycles that contain 53 | * startStruct. If current type is struct or union then 54 | * cycleNode is updated (see Record::findAllCycles) 55 | */ 56 | virtual bool 57 | findAllCycles(const std::shared_ptr &startStruct, 58 | CycleNode &cycleNode, 59 | std::vector> &visitedTypes) const; 60 | 61 | /** 62 | * Execution stops at typedefs of structs and unions therefore it cannot 63 | * stuck in infinite recursion. 64 | * @return copy of current type in which all typedefs are omitted except 65 | * typedefs that wrap structs, unions, enums and opaque types. 66 | */ 67 | virtual std::shared_ptr unrollTypedefs() const; 68 | 69 | /** 70 | * Execution stops at typedefs of structs and unions therefore it cannot 71 | * stuck in infinite recursion. 72 | * @return copy of current type in which given type is replaced with 73 | * replacement type. 74 | */ 75 | virtual std::shared_ptr 76 | replaceType(const std::shared_ptr &type, 77 | const std::shared_ptr &replacement) const; 78 | 79 | protected: 80 | template std::shared_ptr shared_from_base() { 81 | return std::dynamic_pointer_cast(shared_from_this()); 82 | } 83 | 84 | template 85 | std::shared_ptr shared_from_base() const { 86 | return std::dynamic_pointer_cast(shared_from_this()); 87 | } 88 | }; 89 | 90 | #endif // SCALA_NATIVE_BINDGEN_TYPE_H 91 | -------------------------------------------------------------------------------- /bindgen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.9) 2 | project(scala-native-bindgen) 3 | 4 | option(STATIC_LINKING "Statically link the executable" OFF) 5 | 6 | # Locate LLVMConfig.cmake 7 | find_package(LLVM REQUIRED CONFIG) 8 | message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") 9 | message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") 10 | 11 | message(STATUS "Using LLVM include dirs: ${LLVM_INCLUDE_DIRS}") 12 | include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}) 13 | 14 | message(STATUS "Using LLVM defs: ${LLVM_DEFINITIONS}") 15 | add_definitions(${LLVM_DEFINITIONS}) 16 | 17 | message(STATUS "Using LLVM library directories: ${LLVM_LIBRARY_DIRS}") 18 | link_directories(${LLVM_LIBRARY_DIRS}) 19 | 20 | add_compile_options(-fexceptions -std=c++11 -Wall -Wconversion -Werror) 21 | 22 | add_executable(bindgen 23 | Main.cpp 24 | visitor/ScalaFrontendAction.h 25 | visitor/ScalaFrontendAction.cpp 26 | visitor/ScalaFrontendActionFactory.h 27 | visitor/ScalaFrontendActionFactory.cpp 28 | visitor/TreeVisitor.h 29 | visitor/TreeVisitor.cpp 30 | visitor/TreeConsumer.h 31 | defines/DefineFinderAction.h 32 | defines/DefineFinderAction.cpp 33 | defines/DefineFinder.cpp 34 | defines/DefineFinder.h 35 | defines/DefineFinderActionFactory.cpp 36 | defines/DefineFinderActionFactory.h 37 | TypeTranslator.h 38 | TypeTranslator.cpp 39 | Utils.h 40 | ir/IR.h 41 | ir/IR.cpp 42 | ir/Record.cpp 43 | ir/Record.h 44 | ir/Struct.cpp 45 | ir/Struct.h 46 | ir/Union.cpp 47 | ir/Union.h 48 | ir/Function.cpp 49 | ir/Function.h 50 | ir/TypeDef.cpp 51 | ir/TypeDef.h 52 | ir/Enum.cpp 53 | ir/Enum.h 54 | ir/TypeAndName.cpp 55 | ir/TypeAndName.h 56 | ir/Define.h 57 | ir/Define.cpp 58 | ir/LiteralDefine.cpp 59 | ir/LiteralDefine.h 60 | ir/VarDefine.cpp 61 | ir/VarDefine.h 62 | ir/Variable.cpp 63 | ir/Variable.h 64 | ir/PossibleVarDefine.cpp 65 | ir/PossibleVarDefine.h 66 | ir/types/Type.cpp 67 | ir/types/Type.h 68 | ir/types/PrimitiveType.cpp 69 | ir/types/PrimitiveType.h 70 | ir/types/PointerType.cpp 71 | ir/types/PointerType.h 72 | ir/types/FunctionPointerType.cpp 73 | ir/types/FunctionPointerType.h 74 | ir/types/ArrayType.cpp 75 | ir/types/ArrayType.h 76 | ir/Location.h 77 | ir/Location.cpp 78 | ir/LocationManager.h 79 | ir/LocationManager.cpp 80 | ir/LocatableType.cpp 81 | ir/LocatableType.h 82 | ) 83 | 84 | if (STATIC_LINKING) 85 | set(USE_SHARED OFF) 86 | if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 87 | # macOS does not guarantee backwards compatible system calls and therefore 88 | # discourages statically linked binaries. Instead add -L/usr/lib before the 89 | # LLVM LDFLAGS to link against the dynamic system libc++ instead of the one 90 | # from LLVM. 91 | set(BINDGEN_LINK_FLAG "-L/usr/lib") 92 | else() 93 | set(BINDGEN_LINK_FLAG "-static") 94 | endif() 95 | llvm_map_components_to_libnames(LLVM_LIBS support core irreader object option profiledata) 96 | else() 97 | set(LLVM_LIBS LLVM) 98 | endif() 99 | message(STATUS "Using link flag: ${BINDGEN_LINK_FLAG}") 100 | 101 | set_target_properties(bindgen 102 | PROPERTIES 103 | OUTPUT_NAME scala-native-bindgen 104 | LINK_FLAGS "${BINDGEN_LINK_FLAG}" 105 | ) 106 | 107 | target_link_libraries(bindgen 108 | PRIVATE 109 | clangFrontend 110 | clangTooling 111 | clangSerialization 112 | clangDriver 113 | clangParse 114 | clangSema 115 | clangAnalysis 116 | clangEdit 117 | clangAST 118 | clangLex 119 | clangBasic 120 | ${LLVM_LIBS} 121 | ) 122 | -------------------------------------------------------------------------------- /bindgen/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "defines/DefineFinderActionFactory.h" 2 | #include "ir/LocationManager.h" 3 | #include "visitor/ScalaFrontendActionFactory.h" 4 | #include 5 | 6 | int main(int argc, const char *argv[]) { 7 | llvm::cl::OptionCategory Category("Scala Native Binding Generator"); 8 | llvm::cl::extrahelp CommonHelp( 9 | clang::tooling::CommonOptionsParser::HelpMessage); 10 | llvm::cl::extrahelp MoreHelp("\nProduce Bindings for scala native. Please " 11 | "specify lib name with parameter name\n"); 12 | 13 | llvm::cl::opt LibName("name", llvm::cl::cat(Category), 14 | llvm::cl::desc("Library name")); 15 | 16 | llvm::cl::opt ExcludePrefix( 17 | "exclude-prefix", llvm::cl::cat(Category), 18 | llvm::cl::desc("Functions and unused typedefs will be removed if their " 19 | "names\nhave given prefix")); 20 | llvm::cl::opt Package( 21 | "package", llvm::cl::cat(Category), 22 | llvm::cl::desc("Package name of generated Scala file")); 23 | llvm::cl::opt NoLinkName( 24 | "no-link", llvm::cl::cat(Category), 25 | llvm::cl::desc("Library does not require linking")); 26 | llvm::cl::opt LinkName( 27 | "link", llvm::cl::cat(Category), 28 | llvm::cl::desc("Library to link with, e.g. -luv for libuv")); 29 | llvm::cl::opt ReuseBindingsConfig( 30 | "binding-config", llvm::cl::cat(Category), 31 | llvm::cl::desc("Path to a config file that contains the information " 32 | "about bindings that should be reused")); 33 | clang::tooling::CommonOptionsParser op(argc, argv, Category); 34 | clang::tooling::ClangTool Tool(op.getCompilations(), 35 | op.getSourcePathList()); 36 | 37 | if (op.getSourcePathList().size() != 1) { 38 | llvm::errs() << "Error: Only one file may be processed at a time.\n"; 39 | llvm::errs().flush(); 40 | return -1; 41 | } 42 | 43 | auto libName = LibName.getValue(); 44 | if (libName.empty()) { 45 | llvm::errs() 46 | << "Error: Please specify the lib name using -name parameter\n"; 47 | llvm::errs().flush(); 48 | return -1; 49 | } 50 | 51 | auto linkName = LinkName.getValue(); 52 | if (linkName.empty()) { 53 | linkName = libName; 54 | } 55 | if (NoLinkName.getValue()) { 56 | linkName = ""; 57 | } 58 | 59 | auto objectName = libName; 60 | if (objectName == "native") { 61 | /* there are at most 3 objects in the file. 62 | * All of them will have distinct names. */ 63 | objectName = "nativeLib"; 64 | } 65 | 66 | std::string resolved = getRealPath(op.getSourcePathList()[0].c_str()); 67 | LocationManager locationManager(resolved); 68 | 69 | auto reuseBindingsConfig = ReuseBindingsConfig.getValue(); 70 | if (!reuseBindingsConfig.empty()) { 71 | locationManager.loadConfig(reuseBindingsConfig); 72 | } 73 | 74 | IR ir(libName, linkName, objectName, Package.getValue(), locationManager); 75 | 76 | DefineFinderActionFactory defineFinderActionFactory(ir); 77 | int result = Tool.run(&defineFinderActionFactory); 78 | if (result) { 79 | return result; 80 | } 81 | 82 | ScalaFrontendActionFactory actionFactory(ir); 83 | result = Tool.run(&actionFactory); 84 | 85 | ir.generate(ExcludePrefix.getValue()); 86 | llvm::outs() << ir; 87 | llvm::outs().flush(); 88 | return result; 89 | } 90 | -------------------------------------------------------------------------------- /bindgen/ir/Function.cpp: -------------------------------------------------------------------------------- 1 | #include "Function.h" 2 | #include "../Utils.h" 3 | #include "Struct.h" 4 | #include "Union.h" 5 | #include 6 | 7 | Parameter::Parameter(std::string name, std::shared_ptr type) 8 | : TypeAndName(std::move(name), type) {} 9 | 10 | Function::Function(const std::string &name, 11 | std::vector> parameters, 12 | std::shared_ptr retType, bool isVariadic) 13 | : name(name), scalaName(name), parameters(std::move(parameters)), 14 | retType(std::move(retType)), isVariadic(isVariadic) {} 15 | 16 | std::string 17 | Function::getDefinition(const LocationManager &locationManager) const { 18 | std::stringstream s; 19 | if (scalaName != name) { 20 | s << " @native.link(\"" << name << "\")\n"; 21 | } 22 | s << " def " << handleReservedWords(scalaName) << "("; 23 | std::string sep = ""; 24 | for (const auto ¶m : parameters) { 25 | s << sep << handleReservedWords(param->getName()) << ": " 26 | << param->getType()->str(locationManager); 27 | sep = ", "; 28 | } 29 | if (isVariadic) { 30 | /* the C Iso require at least one argument in a variadic function, so 31 | * the comma is fine */ 32 | s << ", " << getVarargsParameterName() << ": native.CVararg*"; 33 | } 34 | s << "): " << retType->str(locationManager) << " = native.extern\n"; 35 | return s.str(); 36 | } 37 | 38 | bool Function::usesType( 39 | std::shared_ptr type, bool stopOnTypeDefs, 40 | std::vector> &visitedTypes) const { 41 | visitedTypes.clear(); 42 | if (*retType == *type || 43 | retType.get()->usesType(type, stopOnTypeDefs, visitedTypes)) { 44 | return true; 45 | } 46 | for (const auto ¶meter : parameters) { 47 | visitedTypes.clear(); 48 | if (*parameter->getType() == *type || 49 | parameter->getType().get()->usesType(type, stopOnTypeDefs, 50 | visitedTypes)) { 51 | return true; 52 | } 53 | } 54 | return false; 55 | } 56 | 57 | std::string Function::getName() const { return name; } 58 | 59 | std::string Function::getVarargsParameterName() const { 60 | std::string parameterName = "varArgs"; 61 | int i = 0; 62 | while (existsParameterWithName(parameterName)) { 63 | parameterName = "varArgs" + std::to_string(i++); 64 | } 65 | return parameterName; 66 | } 67 | 68 | bool Function::existsParameterWithName(const std::string ¶meterName) const { 69 | for (const auto ¶meter : parameters) { 70 | if (parameter->getName() == parameterName) { 71 | return true; 72 | } 73 | } 74 | return false; 75 | } 76 | 77 | void Function::setScalaName(std::string scalaName) { 78 | this->scalaName = std::move(scalaName); 79 | } 80 | 81 | bool Function::isLegalScalaNativeFunction() const { 82 | /* Return type and parameters types cannot be array types because array type 83 | * in this case is always represented as a pointer to element type */ 84 | if (isAliasForType(retType.get()) || 85 | isAliasForType(retType.get()) || 86 | isAliasForOpaqueType(retType.get())) { 87 | return false; 88 | } 89 | for (const auto ¶meter : parameters) { 90 | if (isAliasForType(parameter->getType().get()) || 91 | isAliasForType(parameter->getType().get()) || 92 | isAliasForOpaqueType(parameter->getType().get())) { 93 | return false; 94 | } 95 | } 96 | return true; 97 | } 98 | -------------------------------------------------------------------------------- /tools/src/main/scala/org/scalanative/bindgen/BindingOptions.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen 2 | 3 | import java.io.File 4 | 5 | sealed trait BindingOptions { 6 | 7 | /** 8 | * Library to link with, e.g. -luv for libuv 9 | */ 10 | def link(library: String): BindingOptions 11 | 12 | /** 13 | * Name of Scala object that contains bindings. 14 | * Default is set to library name. 15 | */ 16 | def name(name: String): BindingOptions 17 | 18 | /** 19 | * Package name of generated Scala file 20 | */ 21 | def packageName(packageName: String): BindingOptions 22 | 23 | /** 24 | * Declarations will be removed if their names 25 | * contain given prefix 26 | */ 27 | def excludePrefix(prefix: String): BindingOptions 28 | 29 | /** 30 | * Additional arguments to append to the compiler command line 31 | */ 32 | def extraArgs(args: String*): BindingOptions 33 | 34 | /** 35 | * Additional arguments to prepend to the compiler command line 36 | */ 37 | def extraArgsBefore(args: String*): BindingOptions 38 | 39 | /** 40 | * Reuse types from already generated bindings. 41 | * @param config file that contains information about generated bindings. 42 | */ 43 | def bindingConfig(config: File): BindingOptions 44 | 45 | } 46 | 47 | object BindingOptions { 48 | def apply(header: File): BindingOptions = { 49 | require(header.exists(), s"Header does not exist: `$header`") 50 | Impl(header = header) 51 | } 52 | 53 | private[bindgen] final case class Impl(header: File, 54 | library: Option[String] = None, 55 | name: Option[String] = None, 56 | packageName: Option[String] = None, 57 | excludePrefix: Option[String] = None, 58 | extraArgs: Seq[String] = Seq.empty, 59 | extraArgsBefore: Seq[String] = 60 | Seq.empty, 61 | bindingConfig: Option[File] = None) 62 | extends BindingOptions { 63 | 64 | override def link(library: String): BindingOptions = { 65 | require(!library.isEmpty, "Library name must be non-empty") 66 | copy(library = Option(library)) 67 | } 68 | 69 | override def name(name: String): BindingOptions = { 70 | require(!name.isEmpty, "Scala object name must be non-empty") 71 | copy(name = Option(name)) 72 | } 73 | 74 | override def packageName(packageName: String): BindingOptions = { 75 | require(!packageName.isEmpty, "Package name must be non-empty") 76 | copy(packageName = Option(packageName)) 77 | } 78 | 79 | override def excludePrefix(prefix: String): BindingOptions = { 80 | require(!prefix.isEmpty, "Exclude prefix must be non-empty") 81 | copy(excludePrefix = Option(prefix)) 82 | } 83 | 84 | override def extraArgs(args: String*): BindingOptions = { 85 | require(args.forall(_.nonEmpty), "All extra-args must be non-empty") 86 | copy(extraArgs = extraArgs ++ args) 87 | } 88 | 89 | override def extraArgsBefore(args: String*): BindingOptions = { 90 | require(args.forall(_.nonEmpty), 91 | "All extra-args-before must be non-empty") 92 | copy(extraArgsBefore = extraArgsBefore ++ args) 93 | } 94 | 95 | override def bindingConfig(config: File): BindingOptions = { 96 | require(config.exists(), s"Config file must exist: $config") 97 | copy(bindingConfig = Some(config)) 98 | } 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /tests/samples/AnonymousTypes.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.link("bindgentests") 7 | @native.extern 8 | object AnonymousTypes { 9 | type struct_anonymous_0 = native.CStruct1[native.CChar] 10 | type union_anonymous_0 = native.CArray[Byte, native.Nat._8] 11 | type struct_anonymous_1 = native.CStruct1[native.Ptr[union_anonymous_0]] 12 | type struct_StructWithAnonymousStruct = native.CStruct2[native.Ptr[struct_anonymous_1], native.CUnsignedInt] 13 | type struct_anonymous_2 = native.CStruct1[native.CInt] 14 | def foo(s: native.Ptr[struct_anonymous_0]): Unit = native.extern 15 | def bar(): native.Ptr[struct_anonymous_2] = native.extern 16 | 17 | object implicits { 18 | implicit class struct_anonymous_0_ops(val p: native.Ptr[struct_anonymous_0]) extends AnyVal { 19 | def a: native.CChar = !p._1 20 | def a_=(value: native.CChar): Unit = !p._1 = value 21 | } 22 | 23 | implicit class struct_anonymous_1_ops(val p: native.Ptr[struct_anonymous_1]) extends AnyVal { 24 | def innerUnion: native.Ptr[union_anonymous_0] = !p._1 25 | def innerUnion_=(value: native.Ptr[union_anonymous_0]): Unit = !p._1 = value 26 | } 27 | 28 | implicit class struct_StructWithAnonymousStruct_ops(val p: native.Ptr[struct_StructWithAnonymousStruct]) extends AnyVal { 29 | def innerStruct: native.Ptr[struct_anonymous_1] = !p._1 30 | def innerStruct_=(value: native.Ptr[struct_anonymous_1]): Unit = !p._1 = value 31 | def innerEnum: native.CUnsignedInt = !p._2 32 | def innerEnum_=(value: native.CUnsignedInt): Unit = !p._2 = value 33 | } 34 | 35 | implicit class struct_anonymous_2_ops(val p: native.Ptr[struct_anonymous_2]) extends AnyVal { 36 | def result: native.CInt = !p._1 37 | def result_=(value: native.CInt): Unit = !p._1 = value 38 | } 39 | 40 | implicit class union_anonymous_0_pos(val p: native.Ptr[union_anonymous_0]) extends AnyVal { 41 | def a: native.Ptr[native.CLong] = p.cast[native.Ptr[native.CLong]] 42 | def a_=(value: native.CLong): Unit = !p.cast[native.Ptr[native.CLong]] = value 43 | } 44 | } 45 | 46 | object struct_anonymous_0 { 47 | import implicits._ 48 | def apply()(implicit z: native.Zone): native.Ptr[struct_anonymous_0] = native.alloc[struct_anonymous_0] 49 | def apply(a: native.CChar)(implicit z: native.Zone): native.Ptr[struct_anonymous_0] = { 50 | val ptr = native.alloc[struct_anonymous_0] 51 | ptr.a = a 52 | ptr 53 | } 54 | } 55 | 56 | object struct_anonymous_1 { 57 | import implicits._ 58 | def apply()(implicit z: native.Zone): native.Ptr[struct_anonymous_1] = native.alloc[struct_anonymous_1] 59 | def apply(innerUnion: native.Ptr[union_anonymous_0])(implicit z: native.Zone): native.Ptr[struct_anonymous_1] = { 60 | val ptr = native.alloc[struct_anonymous_1] 61 | ptr.innerUnion = innerUnion 62 | ptr 63 | } 64 | } 65 | 66 | object struct_StructWithAnonymousStruct { 67 | import implicits._ 68 | def apply()(implicit z: native.Zone): native.Ptr[struct_StructWithAnonymousStruct] = native.alloc[struct_StructWithAnonymousStruct] 69 | def apply(innerStruct: native.Ptr[struct_anonymous_1], innerEnum: native.CUnsignedInt)(implicit z: native.Zone): native.Ptr[struct_StructWithAnonymousStruct] = { 70 | val ptr = native.alloc[struct_StructWithAnonymousStruct] 71 | ptr.innerStruct = innerStruct 72 | ptr.innerEnum = innerEnum 73 | ptr 74 | } 75 | } 76 | 77 | object struct_anonymous_2 { 78 | import implicits._ 79 | def apply()(implicit z: native.Zone): native.Ptr[struct_anonymous_2] = native.alloc[struct_anonymous_2] 80 | def apply(result: native.CInt)(implicit z: native.Zone): native.Ptr[struct_anonymous_2] = { 81 | val ptr = native.alloc[struct_anonymous_2] 82 | ptr.result = result 83 | ptr 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /tests/samples/ReservedWords.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import scala.scalanative._ 4 | import scala.scalanative.native._ 5 | 6 | @native.link("bindgentests") 7 | @native.extern 8 | object ReservedWords { 9 | type `match` = native.CInt 10 | type `var` = native.CArray[`match`, native.Nat._5] 11 | type struct_object = native.CStruct2[`match`, native.CInt] 12 | type `object` = struct_object 13 | type `type` = struct_object 14 | type struct_anonymous_0 = native.CStruct2[native.CChar, native.Ptr[`type`]] 15 | type union_lazy = native.CArray[Byte, native.Nat._8] 16 | type `lazy` = union_lazy 17 | type `def` = `match` 18 | type struct_finally = native.CStruct2[`def`, `lazy`] 19 | type `finally` = struct_finally 20 | def `with`(`sealed`: `match`, `implicit`: native.Ptr[`match`], `forSome`: native.Ptr[`lazy`]): native.Ptr[`type`] = native.extern 21 | def `implicit`(`type`: native.Ptr[`finally`]): `match` = native.extern 22 | def _1(): Unit = native.extern 23 | 24 | object implicits { 25 | implicit class struct_object_ops(val p: native.Ptr[struct_object]) extends AnyVal { 26 | def `yield`: `match` = !p._1 27 | def `yield_=`(value: `match`): Unit = !p._1 = value 28 | def `val`: native.CInt = !p._2 29 | def `val_=`(value: native.CInt): Unit = !p._2 = value 30 | } 31 | 32 | implicit class struct_anonymous_0_ops(val p: native.Ptr[struct_anonymous_0]) extends AnyVal { 33 | def `def`: native.CChar = !p._1 34 | def `def_=`(value: native.CChar): Unit = !p._1 = value 35 | def `super`: native.Ptr[`type`] = !p._2 36 | def `super_=`(value: native.Ptr[`type`]): Unit = !p._2 = value 37 | } 38 | 39 | implicit class struct_finally_ops(val p: native.Ptr[struct_finally]) extends AnyVal { 40 | def `val`: `def` = !p._1 41 | def `val_=`(value: `def`): Unit = !p._1 = value 42 | def `finally`: native.Ptr[`lazy`] = p._2 43 | def `finally_=`(value: native.Ptr[`lazy`]): Unit = !p._2 = !value 44 | } 45 | 46 | implicit class union_lazy_pos(val p: native.Ptr[union_lazy]) extends AnyVal { 47 | def instance: native.Ptr[native.Ptr[`object`]] = p.cast[native.Ptr[native.Ptr[`object`]]] 48 | def instance_=(value: native.Ptr[`object`]): Unit = !p.cast[native.Ptr[native.Ptr[`object`]]] = value 49 | def `forSome`: native.Ptr[`match`] = p.cast[native.Ptr[`match`]] 50 | def `forSome_=`(value: `match`): Unit = !p.cast[native.Ptr[`match`]] = value 51 | def `implicit`: native.Ptr[native.Ptr[struct_anonymous_0]] = p.cast[native.Ptr[native.Ptr[struct_anonymous_0]]] 52 | def `implicit_=`(value: native.Ptr[struct_anonymous_0]): Unit = !p.cast[native.Ptr[native.Ptr[struct_anonymous_0]]] = value 53 | } 54 | } 55 | 56 | object struct_object { 57 | import implicits._ 58 | def apply()(implicit z: native.Zone): native.Ptr[struct_object] = native.alloc[struct_object] 59 | def apply(`yield`: `match`, `val`: native.CInt)(implicit z: native.Zone): native.Ptr[struct_object] = { 60 | val ptr = native.alloc[struct_object] 61 | ptr.`yield` = `yield` 62 | ptr.`val` = `val` 63 | ptr 64 | } 65 | } 66 | 67 | object struct_anonymous_0 { 68 | import implicits._ 69 | def apply()(implicit z: native.Zone): native.Ptr[struct_anonymous_0] = native.alloc[struct_anonymous_0] 70 | def apply(`def`: native.CChar, `super`: native.Ptr[`type`])(implicit z: native.Zone): native.Ptr[struct_anonymous_0] = { 71 | val ptr = native.alloc[struct_anonymous_0] 72 | ptr.`def` = `def` 73 | ptr.`super` = `super` 74 | ptr 75 | } 76 | } 77 | 78 | object struct_finally { 79 | import implicits._ 80 | def apply()(implicit z: native.Zone): native.Ptr[struct_finally] = native.alloc[struct_finally] 81 | def apply(`val`: `def`, `finally`: native.Ptr[`lazy`])(implicit z: native.Zone): native.Ptr[struct_finally] = { 82 | val ptr = native.alloc[struct_finally] 83 | ptr.`val` = `val` 84 | ptr.`finally` = `finally` 85 | ptr 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /bindgen/Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include "ir/TypeDef.h" 5 | #include "ir/types/Type.h" 6 | #include 7 | 8 | inline std::string uint64ToScalaNat(uint64_t v, std::string accumulator = "") { 9 | if (v == 0) 10 | return accumulator; 11 | 12 | auto last_digit = v % 10; 13 | auto rest = v / 10; 14 | 15 | if (accumulator.empty()) { 16 | return uint64ToScalaNat(rest, 17 | "native.Nat._" + std::to_string(last_digit)); 18 | } else { 19 | return uint64ToScalaNat(rest, "native.Nat.Digit[native.Nat._" + 20 | std::to_string(last_digit) + ", " + 21 | accumulator + "]"); 22 | } 23 | } 24 | 25 | static std::array reserved_words = { 26 | {"abstract", "case", "catch", "class", "def", "do", 27 | "else", "extends", "false", "final", "finally", "for", 28 | "forSome", "if", "implicit", "import", "lazy", "match", 29 | "new", "null", "object", "override", "package", "private", 30 | "protected", "return", "sealed", "super", "this", "throw", 31 | "trait", "try", "true", "type", "val", "var", 32 | "while", "with", "yield"}}; 33 | 34 | inline std::string handleReservedWords(const std::string &name, 35 | const std::string &suffix = "") { 36 | auto found = std::find(reserved_words.begin(), reserved_words.end(), name); 37 | if (found != reserved_words.end()) { 38 | return "`" + name + suffix + "`"; 39 | } else { 40 | return name + suffix; 41 | } 42 | } 43 | 44 | /** 45 | * @return true if str starts with given prefix 46 | */ 47 | static inline bool startsWith(const std::string &str, 48 | const std::string &prefix) { 49 | return str.substr(0, prefix.size()) == prefix; 50 | } 51 | 52 | /** 53 | * @return true if str ends with given prefix 54 | */ 55 | static inline bool endsWith(const std::string &str, const std::string &suffix) { 56 | return str.substr(str.length() - suffix.size(), str.length()) == suffix; 57 | } 58 | 59 | template static inline bool isInstanceOf(PT *type) { 60 | auto *p = dynamic_cast(type); 61 | return p != nullptr; 62 | } 63 | 64 | static inline std::string replaceChar(const std::string &str, 65 | const std::string &c1, 66 | const std::string &c2) { 67 | auto f = str.find(c1); 68 | if (f != std::string::npos) { 69 | return std::string(str).replace(f, c1.length(), c2); 70 | } 71 | return std::string(str); 72 | } 73 | 74 | /** 75 | * Types may be wrapper in a chain of typedefs. 76 | * @return true if given type is of type T or is an alias for type T. 77 | */ 78 | template static inline bool isAliasForType(const Type *type) { 79 | if (isInstanceOf(type)) { 80 | return true; 81 | } 82 | auto *typeDef = dynamic_cast(type); 83 | if (typeDef) { 84 | return isAliasForType(typeDef->getType().get()); 85 | } 86 | return false; 87 | } 88 | 89 | /** 90 | * @return true if typedef references opaque type directly or through a 91 | * chain of typedefs. 92 | */ 93 | static inline bool isAliasForOpaqueType(const Type *type) { 94 | assert(type); 95 | auto *typeDef = dynamic_cast(type); 96 | if (typeDef) { 97 | if (!typeDef->getType()) { 98 | return true; 99 | } 100 | return isAliasForOpaqueType(typeDef->getType().get()); 101 | } 102 | return false; 103 | } 104 | 105 | static inline bool contains(const Type *type, 106 | std::vector> &types) { 107 | for (const auto &t : types) { 108 | if (*type == *t) { 109 | return true; 110 | } 111 | } 112 | return false; 113 | } 114 | 115 | static inline std::string getRealPath(const char *filename) { 116 | char *p = realpath(filename, nullptr); 117 | std::string path; 118 | if (p) { 119 | path = p; 120 | delete[] p; 121 | } else { 122 | // TODO: check when it happens 123 | path = ""; 124 | } 125 | return path; 126 | } 127 | 128 | #endif // UTILS_H 129 | -------------------------------------------------------------------------------- /bindgen/ir/TypeDef.cpp: -------------------------------------------------------------------------------- 1 | #include "TypeDef.h" 2 | #include "../Utils.h" 3 | #include "Struct.h" 4 | #include "Union.h" 5 | #include 6 | #include 7 | 8 | TypeDef::TypeDef(std::string name, std::shared_ptr type, 9 | std::shared_ptr location) 10 | : TypeAndName(std::move(name), std::move(type)), 11 | LocatableType(std::move(location)) {} 12 | 13 | std::string 14 | TypeDef::getDefinition(const LocationManager &locationManager) const { 15 | std::stringstream s; 16 | s << " type " << this->str(locationManager) << " = "; 17 | if (type) { 18 | s << type->str(locationManager); 19 | } else { 20 | s << "native.CStruct0 // incomplete type"; 21 | } 22 | s << "\n"; 23 | return s.str(); 24 | } 25 | 26 | bool TypeDef::usesType( 27 | const std::shared_ptr &type, bool stopOnTypeDefs, 28 | std::vector> &visitedTypes) const { 29 | if (stopOnTypeDefs) { 30 | return false; 31 | } 32 | if (!this->type) { 33 | return false; 34 | } 35 | if (contains(this, visitedTypes)) { 36 | return false; 37 | } 38 | visitedTypes.push_back(shared_from_this()); 39 | bool result = *this->type == *type || 40 | this->type->usesType(type, stopOnTypeDefs, visitedTypes); 41 | visitedTypes.pop_back(); 42 | return result; 43 | } 44 | 45 | std::string TypeDef::str(const LocationManager &locationManager) const { 46 | if (hasLocation()) { 47 | std::shared_ptr location = getLocation(); 48 | if (locationManager.isImported(*location)) { 49 | return locationManager.getImportedType(*location, name); 50 | } 51 | } 52 | return handleReservedWords(replaceChar(name, " ", "_")); 53 | } 54 | 55 | bool TypeDef::operator==(const Type &other) const { 56 | if (this == &other) { 57 | return true; 58 | } 59 | if (isInstanceOf(&other)) { 60 | auto *typDef = dynamic_cast(&other); 61 | if (name != typDef->name) { 62 | return false; 63 | } 64 | return *type == *typDef->type; 65 | } 66 | return false; 67 | } 68 | 69 | std::shared_ptr TypeDef::getLocation() const { 70 | if (!type) { 71 | throw std::logic_error( 72 | "TypeDef::getLocation method should not be called on typedef that " 73 | "references an opaque type because location cannot be " 74 | "defined."); 75 | } 76 | if (location) { 77 | /* if typedef is not generated */ 78 | return location; 79 | } 80 | auto recordPointer = std::dynamic_pointer_cast(type); 81 | if (recordPointer) { 82 | return recordPointer->getLocation(); 83 | } 84 | throw std::logic_error("Generated typedef may reference only records."); 85 | } 86 | 87 | bool TypeDef::hasLocation() const { return location || type; } 88 | 89 | bool TypeDef::findAllCycles( 90 | const std::shared_ptr &startStruct, CycleNode &cycleNode, 91 | std::vector> &visitedTypes) const { 92 | if (contains(this, visitedTypes) || !type) { 93 | return false; 94 | } 95 | visitedTypes.push_back(shared_from_this()); 96 | bool result = type->findAllCycles(startStruct, cycleNode, visitedTypes); 97 | visitedTypes.pop_back(); 98 | return result; 99 | } 100 | 101 | std::shared_ptr TypeDef::unrollTypedefs() const { 102 | if (isGenerated()) { 103 | return std::make_shared(name, type, nullptr); 104 | } 105 | return type->unrollTypedefs(); 106 | } 107 | 108 | std::shared_ptr 109 | TypeDef::replaceType(const std::shared_ptr &type, 110 | const std::shared_ptr &replacement) const { 111 | if (isGenerated()) { 112 | return std::make_shared(name, this->type, nullptr); 113 | } 114 | if (*this->type == *type) { 115 | return std::make_shared(name, replacement, nullptr); 116 | } 117 | return std::make_shared( 118 | name, this->type->replaceType(type, replacement), nullptr); 119 | } 120 | 121 | bool TypeDef::wrapperForOpaqueType() const { return !type; } 122 | 123 | bool TypeDef::isGenerated() const { 124 | return !this->type || isInstanceOf(this->type.get()) || 125 | isInstanceOf(this->type.get()); 126 | } 127 | -------------------------------------------------------------------------------- /docs/src/paradox/configuration.md: -------------------------------------------------------------------------------- 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 ``: 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 95 | -------------------------------------------------------------------------------- /tests/samples/src/test/scala/org/scalanative/bindgen/samples/UnionSpec.scala: -------------------------------------------------------------------------------- 1 | package org.scalanative.bindgen.samples 2 | 3 | import org.scalatest.FunSpec 4 | import scala.scalanative.native._ 5 | 6 | import org.scalanative.bindgen.samples.Union.implicits._ 7 | 8 | class UnionSpec extends FunSpec { 9 | describe("union bindings") { 10 | it("should match size of C memory layout") { 11 | assert(Union.union_get_sizeof() == sizeof[Union.union_values]) 12 | } 13 | 14 | it("should provide field getters") { 15 | Zone { implicit zone => 16 | val unionPtr = alloc[Union.union_values] 17 | 18 | for (value <- Seq(Int.MinValue, -1, 0, 1, Int.MaxValue)) { 19 | Union.union_test_int(unionPtr, Union.enum_union_op.UNION_SET, value) 20 | assert(!unionPtr.i == value) 21 | } 22 | 23 | for (value <- Seq(Long.MinValue, -1, 0, 1, Long.MaxValue)) { 24 | Union.union_test_long(unionPtr, Union.enum_union_op.UNION_SET, value) 25 | assert(!unionPtr.l == value) 26 | } 27 | 28 | val llValues = 29 | Seq[CLongLong](Long.MinValue - 100, -1, 0, 1, Long.MaxValue + 100) 30 | for (value <- llValues) { 31 | Union.union_test_long_long(unionPtr, 32 | Union.enum_union_op.UNION_SET, 33 | value) 34 | assert(!unionPtr.ll == value) 35 | } 36 | 37 | for (value <- Seq(Double.MinValue, -1, 0, 1, Double.MaxValue)) { 38 | Union.union_test_double(unionPtr, 39 | Union.enum_union_op.UNION_SET, 40 | value) 41 | assert(!unionPtr.d == value) 42 | } 43 | 44 | for (value <- Seq("", "bindgen")) { 45 | val s = toCString(value) 46 | Union.union_test_string(unionPtr, Union.enum_union_op.UNION_SET, s) 47 | assert(fromCString(!unionPtr.s) == value) 48 | } 49 | 50 | Union.union_test_string(unionPtr, Union.enum_union_op.UNION_SET, null) 51 | assert(!unionPtr.s == null) 52 | 53 | val struct = alloc[Union.struct_s] 54 | struct.a = 10 55 | Union.union_test_struct(unionPtr, Union.enum_union_op.UNION_SET, struct) 56 | assert(unionPtr.structInUnion.a == 10) 57 | } 58 | } 59 | 60 | it("should provide field setters") { 61 | Zone { implicit zone => 62 | val unionPtr = alloc[Union.union_values] 63 | 64 | for (value <- Seq(Int.MinValue, -1, 0, 1, Int.MaxValue)) { 65 | !unionPtr.i = value 66 | assert( 67 | Union.union_test_int(unionPtr, 68 | Union.enum_union_op.UNION_TEST, 69 | value) == 1) 70 | } 71 | 72 | for (value <- Seq(Long.MinValue, -1, 0, 1, Long.MaxValue)) { 73 | !unionPtr.l = value 74 | assert( 75 | Union.union_test_long(unionPtr, 76 | Union.enum_union_op.UNION_TEST, 77 | value) == 1) 78 | } 79 | 80 | val llValues = 81 | Seq[CLongLong](Long.MinValue - 100, -1, 0, 1, Long.MaxValue + 100) 82 | for (value <- llValues) { 83 | !unionPtr.ll = value 84 | assert( 85 | Union.union_test_long_long(unionPtr, 86 | Union.enum_union_op.UNION_TEST, 87 | value) == 1) 88 | } 89 | 90 | for (value <- Seq(Double.MinValue, -1, 0, 1, Double.MaxValue)) { 91 | !unionPtr.d = value 92 | assert( 93 | Union.union_test_double(unionPtr, 94 | Union.enum_union_op.UNION_TEST, 95 | value) == 1) 96 | } 97 | 98 | for (value <- Seq("", "bindgen")) { 99 | val s = toCString(value) 100 | !unionPtr.s = s 101 | val expected = toCString(value) 102 | assert( 103 | Union.union_test_string(unionPtr, 104 | Union.enum_union_op.UNION_TEST, 105 | expected) == 1) 106 | } 107 | 108 | !unionPtr.s = null 109 | assert( 110 | Union.union_test_string(unionPtr, 111 | Union.enum_union_op.UNION_TEST, 112 | null) == 1) 113 | 114 | val struct = alloc[Union.struct_s] 115 | struct.a = 10 116 | unionPtr.structInUnion = struct 117 | assert( 118 | Union.union_test_struct(unionPtr, 119 | Union.enum_union_op.UNION_TEST, 120 | struct) == 1) 121 | } 122 | } 123 | } 124 | } 125 | --------------------------------------------------------------------------------