-
Notifications
You must be signed in to change notification settings - Fork 7.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add bindgen build zephyr-sys #76337
Add bindgen build zephyr-sys #76337
Conversation
It's nice you got this working, it's hard to see how manually wrapping every header we have with custom bindings would have worked out. I'm interested to see how this looks in practice. |
// <inttypes.h> seems to come from somewhere mysterious in Zephyr. For us, just pull in the | ||
// one from the minimal libc. | ||
.clang_arg("-DRUST_BINDGEN") | ||
.clang_arg("-I../../../lib/libc/minimal/include") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this makes things much simpler compared to my solution. Just to double check: Can we consider it safe for the binding generation if the libc header files are not the ones actually used by the build?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It depends a little bit on what types are being used. I would suspect that for all of the possible libc implementations basic types will be the same. In fact, there are a bunch of size checks in kernel.h that will fail if this is not the case.
// <inttypes.h> seems to come from somewhere mysterious in Zephyr. For us, just pull in the | ||
// one from the minimal libc. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default configuration in Zephyr has CONFIG_ENFORCE_ZEPHYR_STDINT
active which adds -imacros${ZEPHYR_BASE}/include/zephyr/toolchain/zephyr_stdint.h
. Do we need to consider this as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can look at pulling in this file instead of one from libc.
cmake/modules/rust.cmake
Outdated
get_include_dirs(zephyr_interface include_dirs) | ||
|
||
get_target_property(include_dirs, zephyr_interface INTERFACE_INCLUDE_DIRECTORIES) | ||
get_property(include_defines TARGET zephyr_interface PROPERTY INTERFACE_COMPILE_DEFINITIONS) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason not to use the Zephyr functions here?
zephyr_get_system_include_directories_for_lang(C system_includes)
zephyr_get_include_directories_for_lang(C includes)
zephyr_get_compile_definitions_for_lang(C definitions)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked at them, but they were formatted for C. I might still be able to parse them, so I will look at it.
Change the header passed into bindgen to be an absolute path. This will cause the generated wrapper to refer to this file also using an absolute path. As such, remove the explicit include path added as part of the build. Signed-off-by: David Brown <[email protected]>
Add documentation on the bindings between Rust and C, and the bindgen tool used to generate them. Signed-off-by: David Brown <[email protected]>
Re-export all of the bindgen generated bindings in the zephyr-sys crate into `zephyr::raw`. This keeps things easier, as users of `zephyr` only need to worry about the single `zephyr` crate. Signed-off-by: David Brown <[email protected]>
Fix `WRAPPER_FiLE` to `WRAPPER_FILE`. Signed-off-by: David Brown <[email protected]>
Zephyr takes advantage of a gcc/clang extension that allows structs that have no elements. Rust is perfectly happy with this (it is quite common in Rust code), but generates a warning when a struct containing no elements is passed to C code. For now, suppress this warning on the generated bindings. This has the disadvantage of suppressing it entirely, which might possibly detect other cases of invalid structs. However, the bindings are auto-generated from C structs so should always be valid. Signed-off-by: David Brown <[email protected]>
This reverts commit 2046760. Put these back so we get the zero element structures when using just rust and not CPP. A subsequent patch will suppress the warning. Signed-off-by: David Brown <[email protected]>
GCC automatically defines a `__SOFTFP__` define on targets that are using software floating point. The clang compiler does not do this by default, so check this, and define it. Signed-off-by: David Brown <[email protected]>
Rustc for RISCV encodes optional features on the CPU available as part of the target tuple. Clang, on the other hand does not. In order to be able to use libclang with bindgen on RISCV, we need to simplify the target tuples a bit. Do this by just matching 'riscv32' or 'riscv64' and then passing in a generic tuple. We aren't generating any code, and the structs should always match between the targets. Signed-off-by: David Brown <[email protected]>
Although the Cmake rules to build Rust applications keeps the target directory inside of the build directory, some IDE tools may generate a target directory while editing the code. Ignore these so they never get checked in. Signed-off-by: David Brown <[email protected]>
I would guess the mps2/an521 errors might be due to stack size. |
With bindgen needing to read the headers, make sure CMake knows about this. Signed-off-by: David Brown <[email protected]>
add_custom_target(librustapp ALL | ||
DEPENDS ${DUMMY_FILE} | ||
# The variables, defined at the top level, don't seem to be accessible here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does anyone know how to do this right? I'd like to use the ${SYSCALL_LIST_H_TARGET}
instead of its expansion, but that variable doesn't seem to be defined at this point in this module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rust.cmake
is executed first, defining the rust_cargo_application()
function. At this point SYSCALL_LIST_H_TARGET
is not defined yet:
Then comes the Zephyr CMakeLists.txt
defining SYSCALL_LIST_H_TARGET
:
Finally the rust_cargo_application()
function is executed, but the variables from Zephyr CMakeLists.txt
are alreadyout of scope as can be seen with the stack trace:
I don't see how this could be done without modifying something elsewhere. Would it maybe be reasonable to just add zephyr_interface
as a dependency?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try adding zephyr_interface
as a dependency, and see if it works. It's a little frustrating because the failure are somewhat non-deterministic, although it does seem to fail pretty consistently in CI.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So zephyr_interface
doesn't really work as a dependency, as it seems to just hold the flags needed to set include paths for the zephyr interfaces. It doesn't actually have any header files in it. It seems that the generated targest are what is needed to have the dependencies work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you tried with add_dependencies(librustapp zephyr_interface)
? I think the DEPENDS
parameter only works for files, not for targets.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some really minor comments -- note that my review concerns mostly the doc aspects of the PR
Bindings to Zephyr for Rust | ||
########################### | ||
|
||
Zephyr is written in C, and it's primary API is also made available to C. It is written as a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Zephyr is written in C, and it's primary API is also made available to C. It is written as a | |
Zephyr is written in C, and its primary API is also made available to C. It is written as a |
cmake/modules/rust.cmake
Outdated
@@ -142,14 +168,19 @@ ${config_paths} | |||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} | |||
) | |||
|
|||
# Be sure we don't try building this until al of the generated headers have been generated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
# Be sure we don't try building this until al of the generated headers have been generated. | |
# Be sure we don't try building this until all of the generated headers have been generated. |
Using the Bindings | ||
****************** | ||
|
||
In general, using direct bindings to C function from Rust is a bit more difficult than calling them |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, using direct bindings to C function from Rust is a bit more difficult than calling them | |
In general, using direct bindings to C functions from Rust is a bit more difficult than calling them |
|
||
In general, using direct bindings to C function from Rust is a bit more difficult than calling them | ||
from C. Although all of these calls are considered "unsafe", and must be placed in an ``unsafe`` | ||
block, the rust language has stricter constraints on what is allowed, even by unsafe code. Although |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
block, the rust language has stricter constraints on what is allowed, even by unsafe code. Although | |
block, the Rust language has stricter constraints on what is allowed, even by unsafe code. Although |
block, the rust language has stricter constraints on what is allowed, even by unsafe code. Although | ||
the intent of the Rust on Zephyr project is to allow full use of Zephyr, without needing to resort |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't necessarily dislike the idea of calling this "Rust on Zephyr", but probably better to say something like "... supporting the Rust language in Zephyr ..." or something.
Also, you have two consecutive sentences starting with "Although" :)
cmake/modules/rust.cmake
Outdated
function(rust_cargo_application) | ||
# For now, hard-code the Zephyr crate directly here. Once we have | ||
# more than one crate, these should be added by the modules | ||
# themselves. | ||
set(LIB_RUST_CRATES zephyr zephyr-build) | ||
|
||
get_include_dirs(zephyr_interface include_dirs) | ||
|
||
get_target_property(include_dirs, zephyr_interface INTERFACE_INCLUDE_DIRECTORIES) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be dropped, I think? That's what get_include_dirs already does (L53)?
|
||
Because the Zephyr headers use numerous conditional compilation macros, the bindings needed will be | ||
very specialized to a given board, and even a given configuration. To do this, the file | ||
:file:`lib/rust/zephyr-sys/build.rs`, which ``cargo`` knows to run at build time will |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:file:`lib/rust/zephyr-sys/build.rs`, which ``cargo`` knows to run at build time will | |
:file:`lib/rust/zephyr-sys/build.rs`, which ``cargo`` knows to run at build time, will |
Minor fixes to the documentation from review feedback. Signed-off-by: David Brown <[email protected]>
Remove a redundant call that does exactly what the previous function call accomplishes. Signed-off-by: David Brown <[email protected]>
Fix a mispelled word "al" -> "all". Signed-off-by: David Brown <[email protected]>
I'm going to go ahead an merge this, and we can focus on reviews of the cmake changes as a whole before merging the branch. |
With a lot of help and inspiration from @mjaun, generate a zephyr-sys crate using bindgen.
This currently exposes Zpehyr functions starting with
k_
and withgpio_
, although the code currently only usesk_str_out
for theprintk/printkln
implementation.The bindgen tool generates a wrapper C file that has wrappers for functions declared as static inline in the Zephyr headers. Although there will be a bit of an efficiency hit, this gives us a stable way of accessing the Zephyr interfaces. If there are any that would be better handled directly in Rust, they can be done by hand, and bindgen instructed to skip them.