summaryrefslogtreecommitdiff
path: root/doc/interoperability/rust-from-c
diff options
context:
space:
mode:
Diffstat (limited to 'doc/interoperability/rust-from-c')
-rw-r--r--doc/interoperability/rust-from-c/README.md202
-rw-r--r--doc/interoperability/rust-from-c/ROOT0
-rw-r--r--doc/interoperability/rust-from-c/TARGETS8
-rw-r--r--doc/interoperability/rust-from-c/etc/repos.json26
-rw-r--r--doc/interoperability/rust-from-c/foo/TARGETS9
-rw-r--r--doc/interoperability/rust-from-c/foo/foo.h2
-rw-r--r--doc/interoperability/rust-from-c/foo/foo.rs4
-rw-r--r--doc/interoperability/rust-from-c/main.c14
8 files changed, 265 insertions, 0 deletions
diff --git a/doc/interoperability/rust-from-c/README.md b/doc/interoperability/rust-from-c/README.md
new file mode 100644
index 0000000..19661db
--- /dev/null
+++ b/doc/interoperability/rust-from-c/README.md
@@ -0,0 +1,202 @@
+# Call Rust code from a C binary
+
+The purpose of this tutorial is to showcase how to mix different
+programming languages using `justbuild`, along with the features that
+these Rust rules have to foster the interoperability. For the sake of
+simplicity, in this example we are going to write a library in Rust
+that is consumed by a C binary. In particular, the program will read a
+number from `stdin` and will print the absolute value, which is
+computed via the call to a Rust function.
+
+Spoiler: developers should pay attention to the definition of the
+target for the `foo` library.
+
+## Project structure
+
+Let's start with the following structure.
+
+```sh
+$ tree
+.
+├── etc
+│ └── repos.json
+├── foo
+│ ├── foo.h
+│ ├── foo.rs
+│ └── TARGETS
+├── main.c
+├── README.md
+├── ROOT
+└── TARGETS
+
+2 directories, 8 files
+```
+
+## Required repositories
+
+In the `repos.json` file we need import both the rules for compiling Rust and C code
+
+```jsonc
+// file: etc/repos.json
+
+{ "main": "example"
+, "repositories":
+ { "example":
+ { "repository": {"type": "file", "path": "."}
+ , "bindings": {"rules-rust": "rules-rust", "rules-cc": "rules-cc"}
+ }
+ , "rules-rust":
+ { "repository":
+ { "type": "git"
+ , "repository": "https://github.com/just-buildsystem/rules-rust"
+ , "branch": "master"
+ , "commit": "ed652442176aea086104479bb31aced501df48a2"
+ , "subdir": "rules"
+ }
+ }
+ , "rules-cc":
+ { "repository":
+ { "type": "git"
+ , "repository": "https://github.com/just-buildsystem/rules-cc"
+ , "branch": "master"
+ , "commit": "fac7e7680e00dfc63eec41a33dff86d31571eb4b"
+ , "subdir": "rules"
+ }
+ }
+ }
+}
+```
+
+## The `foo` library
+
+This library is made by a simple function that returns the absolute value of a number
+
+```rust
+// file: foo/foo.rs
+
+#[no_mangle]
+pub extern "C" fn foo(x: i32) -> i32 {
+ return x.abs();
+}
+```
+
+As you may know, in C, before calling a function its signature must be
+declared. Typically, this kind of information is written in header
+files. The `justbuild` Rust rules allow for the definition of
+companion header files, that can be maintained by the Rust library
+developers. In this case, `foo.h` looks as follows
+
+```C
+/* file: foo/foo.h */
+
+#pragma once
+int foo(int);
+```
+
+Of course, we are not forced to write the corresponding header file,
+and we could, for example, put the declaration of the functions we
+want to use in the `main.c` file. However, having the headers make the
+usage of the library much easier. Therefore, the Rust rules feature a
+dedicated field `"c_hdrs"`, so, the target definition for the library
+can be as follows
+
+```jsonc
+// file foo/TARGETS
+
+{ "foo":
+ { "type": ["@", "rules-rust", "rust", "library"]
+ , "name": ["foo"]
+ , "crate_root": ["foo.rs"]
+ , "native": ["true"]
+ , "c_hdrs": ["foo.h"]
+ , "stage": ["foo"]
+ }
+}
+```
+
+With respect to the example of the [getting started
+tutorial](../../getting-started/README.md), the above target
+definition contains two new fields: `"native"` and `"c_hdrs"`. When
+the field `"native"` evaluates to `true`, a native library is
+generated instead of a Rust one. In the field `"c_hdrs"`, there are
+listed the headers required to use the library. Since it makes sense
+to set the field `"c_hdrs"` only for a native library, when `"c_hdrs"`
+evaluates to a true value, a native library is implied. Therefore, for
+this case, we can drop the `"native"` field
+
+```jsonc
+// file: foo/TARGETS
+
+{ "foo":
+ { "type": ["@", "rules-rust", "rust", "library"]
+ , "name": ["foo"]
+ , "crate_root": ["foo.rs"]
+ , "c_hdrs": ["foo.h"]
+ , "stage": ["foo"]
+ }
+}
+```
+
+## The main
+
+From the C consumer, the `foo` library can be seen as a normal C library
+
+```C
+/* file: main.c */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "foo/foo.h"
+
+int main(int argc, char **argv) {
+ if (argc < 2) {
+ fprintf(stderr, "Please provide one number as argument\n");
+ exit(1);
+ }
+ int x = atoi(argv[1]);
+ printf("absolute value of %d is %d\n", x, foo(x));
+ return 0;
+}
+```
+
+and the target definition is as simple as follows:
+
+```jsonc
+// file: TARGETS
+
+{ "main":
+ { "type": ["@", "rules-cc", "CC", "binary"]
+ , "pure C": ["true"]
+ , "name": ["main"]
+ , "srcs": ["main.c"]
+ , "private-deps": [["foo", "foo"]]
+ }
+}
+```
+
+It' worth to highlight that we simply stated that `"main"` depends on
+the target `["foo", "foo"]`, and, at this level, we don't care how it
+is defined.
+
+## How to compile
+
+The `"main"` target can be built issuing the following command
+
+```sh
+$ just-mr build main
+```
+
+Please refer to the "Let's build" section of the [getting-started
+tutorial](../../getting-started/README.md) for more details on how to
+find/select the `rustc` compiler.
+
+## Additional exercises
+
+To get more familiarity with the rules, you may want to:
+
+ - add a `["@", "rules-rust", "rust", "test"]`, which tests the `foo`
+ library.
+
+ - add a `["@", "rules-cc", "shell/script", "test"]` to test that the
+ main actually works.
diff --git a/doc/interoperability/rust-from-c/ROOT b/doc/interoperability/rust-from-c/ROOT
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/doc/interoperability/rust-from-c/ROOT
diff --git a/doc/interoperability/rust-from-c/TARGETS b/doc/interoperability/rust-from-c/TARGETS
new file mode 100644
index 0000000..76681d7
--- /dev/null
+++ b/doc/interoperability/rust-from-c/TARGETS
@@ -0,0 +1,8 @@
+{ "main":
+ { "type": ["@", "rules-cc", "CC", "binary"]
+ , "pure C": ["true"]
+ , "name": ["main"]
+ , "srcs": ["main.c"]
+ , "private-deps": [["foo", "foo"]]
+ }
+}
diff --git a/doc/interoperability/rust-from-c/etc/repos.json b/doc/interoperability/rust-from-c/etc/repos.json
new file mode 100644
index 0000000..4a3d134
--- /dev/null
+++ b/doc/interoperability/rust-from-c/etc/repos.json
@@ -0,0 +1,26 @@
+{ "main": "example"
+, "repositories":
+ { "example":
+ { "repository": {"type": "file", "path": "."}
+ , "bindings": {"rules-rust": "rules-rust", "rules-cc": "rules-cc"}
+ }
+ , "rules-rust":
+ { "repository":
+ { "type": "git"
+ , "repository": "https://github.com/just-buildsystem/rules-rust"
+ , "branch": "master"
+ , "commit": "ed652442176aea086104479bb31aced501df48a2"
+ , "subdir": "rules"
+ }
+ }
+ , "rules-cc":
+ { "repository":
+ { "type": "git"
+ , "repository": "https://github.com/just-buildsystem/rules-cc"
+ , "branch": "master"
+ , "commit": "fac7e7680e00dfc63eec41a33dff86d31571eb4b"
+ , "subdir": "rules"
+ }
+ }
+ }
+}
diff --git a/doc/interoperability/rust-from-c/foo/TARGETS b/doc/interoperability/rust-from-c/foo/TARGETS
new file mode 100644
index 0000000..8d19b35
--- /dev/null
+++ b/doc/interoperability/rust-from-c/foo/TARGETS
@@ -0,0 +1,9 @@
+{ "foo":
+ { "type": ["@", "rules-rust", "rust", "library"]
+ , "name": ["foo"]
+ , "crate_root": ["foo.rs"]
+ , "native": ["true"]
+ , "c_hdrs": ["foo.h"]
+ , "stage": ["foo"]
+ }
+}
diff --git a/doc/interoperability/rust-from-c/foo/foo.h b/doc/interoperability/rust-from-c/foo/foo.h
new file mode 100644
index 0000000..88deb2e
--- /dev/null
+++ b/doc/interoperability/rust-from-c/foo/foo.h
@@ -0,0 +1,2 @@
+#pragma once
+int foo(int);
diff --git a/doc/interoperability/rust-from-c/foo/foo.rs b/doc/interoperability/rust-from-c/foo/foo.rs
new file mode 100644
index 0000000..65cbffc
--- /dev/null
+++ b/doc/interoperability/rust-from-c/foo/foo.rs
@@ -0,0 +1,4 @@
+#[no_mangle]
+pub extern "C" fn foo(x: i32) -> i32 {
+ return x.abs();
+}
diff --git a/doc/interoperability/rust-from-c/main.c b/doc/interoperability/rust-from-c/main.c
new file mode 100644
index 0000000..fb29b38
--- /dev/null
+++ b/doc/interoperability/rust-from-c/main.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "foo/foo.h"
+
+int main(int argc, char **argv) {
+ if (argc < 2) {
+ fprintf(stderr, "Please provide one number as argument\n");
+ exit(1);
+ }
+ int x = atoi(argv[1]);
+ printf("absolute value of %d is %d\n", x, foo(x));
+ return 0;
+}