Skip to content
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

Merged
merged 18 commits into from
Sep 6, 2024
Merged

Conversation

d3zd3z
Copy link
Collaborator

@d3zd3z d3zd3z commented Jul 26, 2024

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 with gpio_, although the code currently only uses k_str_out for the printk/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.

@teburd
Copy link
Collaborator

teburd commented Jul 26, 2024

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.

include/zephyr/spinlock.h Outdated Show resolved Hide resolved
// <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")
Copy link
Contributor

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?

Copy link
Collaborator Author

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.

Comment on lines +41 to +42
// <inttypes.h> seems to come from somewhere mysterious in Zephyr. For us, just pull in the
// one from the minimal libc.
Copy link
Contributor

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?

Copy link
Collaborator Author

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.

Comment on lines 67 to 70
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)
Copy link
Contributor

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)

Copy link
Collaborator Author

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.

cmake/modules/rust.cmake Outdated Show resolved Hide resolved
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 <david.brown@linaro.org>
Add documentation on the bindings between Rust and C, and the bindgen
tool used to generate them.

Signed-off-by: David Brown <david.brown@linaro.org>
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 <david.brown@linaro.org>
Fix `WRAPPER_FiLE` to `WRAPPER_FILE`.

Signed-off-by: David Brown <david.brown@linaro.org>
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 <david.brown@linaro.org>
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 <david.brown@linaro.org>
andyross
andyross previously approved these changes Aug 28, 2024
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 <david.brown@linaro.org>
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 <david.brown@linaro.org>
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 <david.brown@linaro.org>
@cfriedt
Copy link
Member

cfriedt commented Aug 29, 2024

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 <david.brown@linaro.org>
add_custom_target(librustapp ALL
DEPENDS ${DUMMY_FILE}
# The variables, defined at the top level, don't seem to be accessible here.
Copy link
Collaborator Author

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.

Copy link
Contributor

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:
image

Then comes the Zephyr CMakeLists.txt defining SYSCALL_LIST_H_TARGET:
image

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:
image

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?

Copy link
Collaborator Author

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.

Copy link
Collaborator Author

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.

Copy link
Contributor

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.

teburd
teburd previously approved these changes Sep 5, 2024
Copy link
Collaborator

@kartben kartben left a 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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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

@@ -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.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
# 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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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

Comment on lines 35 to 36
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
Copy link
Collaborator

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" :)

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)
Copy link
Collaborator

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
: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 <david.brown@linaro.org>
Remove a redundant call that does exactly what the previous function
call accomplishes.

Signed-off-by: David Brown <david.brown@linaro.org>
Fix a mispelled word "al" -> "all".

Signed-off-by: David Brown <david.brown@linaro.org>
@d3zd3z
Copy link
Collaborator Author

d3zd3z commented Sep 6, 2024

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.

@d3zd3z d3zd3z merged commit f67ff24 into zephyrproject-rtos:collab-rust Sep 6, 2024
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done in 3.4
Status: Done
Development

Successfully merging this pull request may close these issues.

7 participants