Support several Rust toolchain versions
Subsystems (7)
Miguel Ojeda
maintainerv2 cover Support several Rust toolchain versions
Hi all,
A few things improved here and there, and rebased on top of rust-next.
The changelog is attached to each patch.
I kept the Tested-bys since most of the changes are on documentation or comments, though I did remove them on the patch that changed the most just in case (even for that one, I think Benno's and Andreas' setup would not have made a difference).
I plan to put this series into rust-next very soon so that it goes into the merge window.
Previous cover letter:
v1: https://lore.kernel.org/rust-for-linux/[email protected]/
Thanks!
Cheers,
Miguel
Miguel Ojeda (13):
rust: macros: indent list item in paste!'s docs
rust: init: simplify from map_err to inspect_err
rust: allow dead_code for never constructed bindings
rust: relax most deny-level lints to warnings
rust: simplify Clippy warning flags set
rust: start supporting several compiler versions
rust: avoid assuming a particular bindgen build
rust: work around bindgen 0.69.0 issue
rust: start supporting several bindgen versions
rust: warn about bindgen versions 0.66.0 and 0.66.1
kbuild: rust: add rustc-version support
rust: support the new -Zub-checks flag
docs: rust: quick-start: add section on Linux distributions
Documentation/process/changes.rst | 9 +- Documentation/rust/quick-start.rst | 136 +++++++++++++++++------ Makefile | 31 +++--- init/Kconfig | 11 +- lib/Kconfig.debug | 18 +++ rust/Makefile | 4 +- rust/bindings/lib.rs | 1 + rust/kernel/init.rs | 13 +-- rust/macros/lib.rs | 2 +- rust/uapi/lib.rs | 1 + scripts/Kconfig.include | 6 + scripts/Makefile.compiler | 4 + scripts/rust_is_available.sh | 33 +++--- scripts/rust_is_available_bindgen_0_66.h | 2 + scripts/rust_is_available_test.py | 59 ++++++---- scripts/rustc-version.sh | 52 +++++++++ 16 files changed, 274 insertions(+), 108 deletions(-) create mode 100644 scripts/rust_is_available_bindgen_0_66.h create mode 100755 scripts/rustc-version.sh
base-commit: fc6e66f4696b63b8a2645a2bcea407cb04bd0666 --
2.45.2
Miguel Ojeda
maintainerPATCH v2 1/13 rust: macros: indent list item in `paste!`'s docs
A new style lint, doc_lazy_continuation [1], has been introduced in the upcoming Rust 1.80 (currently in beta), which detects missing indentation in code documentation.
We have one such case:
error: doc list item missing indentation
--> rust/macros/lib.rs:315:5
|
315 | /// default the span of the [< >] group is used.
| ^
|
= help: if this is supposed to be its own paragraph, add a blank line
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation
= note: -D clippy::doc-lazy-continuation implied by -D clippy::style
= help: to override -D clippy::style add #[allow(clippy::doc_lazy_continuation)]
help: indent this line
|
315 | /// default the span of the [< >] group is used.
| ++
While the rendering of the docs by rustdoc is not affected, we apply this kind of indentation elsewhere since it looks better.
Thus clean it up.
--- rust/macros/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
rust/macros/lib.rs +1 -1
@@ -345,7 +345,7 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {/// /// Currently supported modifiers are: /// * `span`: change the span of concatenated identifier to the span of the specified token. By -/// default the span of the `[< >]` group is used. +/// default the span of the `[< >]` group is used. /// * `lower`: change the identifier to lower case. /// * `upper`: change the identifier to upper case. ///
Signature · 1 line
2.45.2
Miguel Ojeda
maintainerPATCH v2 2/13 rust: init: simplify from `map_err` to `inspect_err`
In reply to Miguel OjedaA new complexity lint, manual_inspect [1], has been introduced in the upcoming Rust 1.81 (currently in nightly), which checks for uses of map* which return the original item:
error:
--> rust/kernel/init.rs:846:23
|
846 | (self.1)(val).map_err(|e| {
| ^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_inspect
= note: `-D clippy::manual-inspect` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::manual_inspect)]`
help: try
|
846 ~ (self.1)(val).inspect_err(|e| {
847 | // SAFETY: `slot` was initialized above.
848 ~ unsafe { core::ptr::drop_in_place(slot) };
|Thus clean them up.
---
v2:
- Moved SAFETY comment out of the closure, in one of the instances only. In the other, it would mean putting two SAFETY comments in a row, which is even more confusing. Open coding it may be clearer, though -- happy to get a patch for that later on (with some docs about guidelines for using or avoiding inspect_*). (Björn, Benno, Alice)
rust/kernel/init.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-)
rust/kernel/init.rs +4 -9
@@ -843,11 +843,8 @@ unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {let val =unsafe { &mut *slot };// SAFETY: `slot` is considered pinned. let val =unsafe {Pin ::new_unchecked (val) }; - (self.1 )(val).map_err (|e| { -// SAFETY: `slot` was initialized above. -unsafe { core::ptr::drop_in_place (slot) }; - e - }) +// SAFETY: `slot` was initialized above. + (self.1 )(val).inspect_err (|_|unsafe { core::ptr::drop_in_place (slot) }) } }
@@ -941,11 +938,9 @@ unsafe fn __init(self, slot: *mut T) -> Result<(), E> {// SAFETY: All requirements fulfilled since this function is `__init`. unsafe { self.0 .__pinned_init (slot)? };// SAFETY: The above call initialized `slot` and we still have unique access. - (self.1 )(unsafe { &mut *slot }).map_err (|e| { + (self.1 )(unsafe { &mut *slot }).inspect_err (|_|// SAFETY: `slot` was initialized above. -unsafe { core::ptr::drop_in_place (slot) }; - e - }) +unsafe { core::ptr::drop_in_place (slot) }) } }
--
2.45.2
Miguel Ojeda
maintainerPATCH v2 3/13 rust: allow `dead_code` for never constructed bindings
In reply to Miguel OjedaStarting with the upcoming Rust 1.80.0 (since upstream commit 35130d7233e9 ("Detect pub structs never constructed and unused associated constants in traits")), the dead_code pass detects more cases, which triggers in the bindings crate:
warning: struct `boot_params` is never constructed
--> rust/bindings/bindings_generated.rs:10684:12
|
10684 | pub struct boot_params {
| ^^^^^^^^^^^
|
= note: `#[warn(dead_code)]` on by defaultAs well as in the uapi one:
warning: struct `boot_params` is never constructed
--> rust/uapi/uapi_generated.rs:10392:12
|
10392 | pub struct boot_params {
| ^^^^^^^^^^^
|
= note: `#[warn(dead_code)]` on by defaultThese are all expected, since we do not use all the structs in the bindings that bindgen generates from the C headers.
Therefore, allow them.
--- rust/bindings/lib.rs | 1 + rust/uapi/lib.rs | 1 + 2 files changed, 2 insertions(+)
rust/bindings/lib.rs +1 -0
@@ -24,6 +24,7 @@ unsafe_op_in_unsafe_fn )] +#[allow(dead_code)] mod bindings_raw {// Use glob import here to expose all helpers. // Symbols defined within the module will take precedence to the glob import.
rust/uapi/lib.rs +1 -0
@@ -14,6 +14,7 @@#![cfg_attr(test, allow(unsafe_op_in_unsafe_fn))] #![allow( clippy::all, + dead_code, missing_docs, non_camel_case_types, non_upper_case_globals,
Signature · 1 line
2.45.2
Miguel Ojeda
maintainerPATCH v2 4/13 rust: relax most deny-level lints to warnings
In reply to Miguel OjedaSince we are starting to support several Rust toolchains, lints (including Clippy ones) now may behave differently and lint groups may include new lints.
Therefore, to maximize the chances a given version works, relax some deny-level lints to warnings. It may also make our lives a bit easier while developing new code or refactoring.
To be clear, the requirements for in-tree code are still the same, since Rust code still needs to be warning-free (patches should be clean under WERROR=y) and the set of lints is not changed.
unsafe_op_in_unsafe_fn is left unmodified, i.e. as an error, since it is becoming the default in the language (warn-by-default in Rust 2024 [1] and ideally an error later on) and thus it should also be very well tested. In addition, it is simple enough that it should not have false positives (unlike e.g. rust_2018_idioms's explicit_outlives_requirements).
non_ascii_idents is left unmodified as well, i.e. as an error, since it is unlikely one gains any productivity during development if it were a warning (in fact, it may be worse, since it is likely one made a typo). In addition, it should not have false positives.
Finally, put the two -D ones at the top and take the chance to do one per line.
---
v2:
- Kept non_ascii_idents as an error. (Björn, Finn)
Makefile | 24 +++++++++++++----------- rust/Makefile | 4 ++-- 2 files changed, 15 insertions(+), 13 deletions(-)
Makefile +13 -11
@@ -461,17 +461,19 @@ KBUILD_USERLDFLAGS := $(USERLDFLAGS) # host programs. export rust_common_flags := --edition=2021 \ -Zbinary_dep_depinfo=y \ - -Dunsafe_op_in_unsafe_fn -Drust_2018_idioms \ - -Dunreachable_pub -Dnon_ascii_idents \ + -Dunsafe_op_in_unsafe_fn \ + -Dnon_ascii_idents \ + -Wrust_2018_idioms \ + -Wunreachable_pub \ -Wmissing_docs \ - -Drustdoc::missing_crate_level_docs \ - -Dclippy::correctness -Dclippy::style \ - -Dclippy::suspicious -Dclippy::complexity \ - -Dclippy::perf \ - -Dclippy::let_unit_value -Dclippy::mut_mut \ - -Dclippy::needless_bitwise_bool \ - -Dclippy::needless_continue \ - -Dclippy::no_mangle_with_rust_abi \ + -Wrustdoc::missing_crate_level_docs \ + -Wclippy::correctness -Wclippy::style \ + -Wclippy::suspicious -Wclippy::complexity \ + -Wclippy::perf \ + -Wclippy::let_unit_value -Wclippy::mut_mut \ + -Wclippy::needless_bitwise_bool \ + -Wclippy::needless_continue \ + -Wclippy::no_mangle_with_rust_abi \ -Wclippy::dbg_macro KBUILD_HOSTCFLAGS := $(KBUILD_USERHOSTCFLAGS) $(HOST_LFS_CFLAGS) $(HOSTCFLAGS)
@@ -572,7 +574,7 @@ KBUILD_RUSTFLAGS := $(rust_common_flags) \ -Csymbol-mangling-version=v0 \ -Crelocation-model=static \ -Zfunction-sections=n \ - -Dclippy::float_arithmetic + -Wclippy::float_arithmetic KBUILD_AFLAGS_KERNEL := KBUILD_CFLAGS_KERNEL :=
rust/Makefile +2 -2
@@ -367,7 +367,7 @@ ifneq ($(or $(CONFIG_ARM64),$(and $(CONFIG_RISCV),$(CONFIG_64BIT))),) endif $(obj)/core.o: private skip_clippy = 1 -$(obj)/core.o: private skip_flags = -Dunreachable_pub +$(obj)/core.o: private skip_flags = -Wunreachable_pub $(obj)/core.o: private rustc_objcopy = $(foreach sym,$(redirect-intrinsics),--redefine-sym $(sym)=__rust$(sym)) $(obj)/core.o: private rustc_target_flags = $(core-cfgs) $(obj)/core.o: $(RUST_LIB_SRC)/core/src/lib.rs FORCE
@@ -381,7 +381,7 @@ $(obj)/compiler_builtins.o: $(src)/compiler_builtins.rs $(obj)/core.o FORCE +$(call if_changed_dep,rustc_library) $(obj)/alloc.o: private skip_clippy = 1 -$(obj)/alloc.o: private skip_flags = -Dunreachable_pub +$(obj)/alloc.o: private skip_flags = -Wunreachable_pub $(obj)/alloc.o: private rustc_target_flags = $(alloc-cfgs) $(obj)/alloc.o: $(RUST_LIB_SRC)/alloc/src/lib.rs $(obj)/compiler_builtins.o FORCE +$(call if_changed_dep,rustc_library)
--
2.45.2
Miguel Ojeda
maintainerPATCH v2 5/13 rust: simplify Clippy warning flags set
In reply to Miguel OjedaAll Clippy lint groups that we enable, except correctness, have a default warn level, thus they may be removed now that we relaxed all lints to warn.
Moreover, Clippy provides an all lint group that covers the groups we enable by default. Thus just use all instead -- the only change is that, if Clippy introduces a new lint group or splits an existing one, we will cover that one automatically.
In addition, let_unit_value is in style since Rust 1.62.0, thus it does not need to be enabled manually.
--- Makefile | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-)
Makefile +2 -4
@@ -467,10 +467,8 @@ export rust_common_flags := --edition=2021 \ -Wunreachable_pub \ -Wmissing_docs \ -Wrustdoc::missing_crate_level_docs \ - -Wclippy::correctness -Wclippy::style \ - -Wclippy::suspicious -Wclippy::complexity \ - -Wclippy::perf \ - -Wclippy::let_unit_value -Wclippy::mut_mut \ + -Wclippy::all \ + -Wclippy::mut_mut \ -Wclippy::needless_bitwise_bool \ -Wclippy::needless_continue \ -Wclippy::no_mangle_with_rust_abi \
Signature · 1 line
2.45.2
Miguel Ojeda
maintainerPATCH v2 6/13 rust: start supporting several compiler versions
In reply to Miguel OjedaIt is time to start supporting several Rust compiler versions and thus establish a minimum Rust version.
We may still want to upgrade the minimum sometimes in the beginning since there may be important features coming into the language that improve how we write code (e.g. field projections), which may or may not make sense to support conditionally.
We will start with a window of two stable releases, and widen it over time. Thus this patch does not move the current minimum (1.78.0), but instead adds support for the recently released 1.79.0.
This should already be enough for kernel developers in distributions that provide recent Rust compiler versions routinely, such as Arch Linux, Debian Unstable (outside the freeze period), Fedora Linux, Gentoo Linux (especially the testing channel), Nix (unstable) and openSUSE Tumbleweed. See the documentation patch about it later in this series.
In addition, Rust for Linux is now being built-tested in Rust's pre-merge CI [1]. That is, every change that is attempting to land into the Rust compiler is tested against the kernel, and it is merged only if it passes -- thanks to the Rust project for that!
Thus, with the pre-merge CI in place, both projects hope to avoid unintentional changes to Rust that break the kernel. This means that, in general, apart from intentional changes on their side (that we will need to workaround conditionally on our side), the upcoming Rust compiler versions should generally work.
For instance, currently, the beta (1.80.0) and nightly (1.81.0) branches work as well.
Of course, the Rust for Linux CI job in the Rust toolchain may still need to be temporarily disabled for different reasons, but the intention is to help bring Rust for Linux into stable Rust.
---
v2:
- Update suggested rustup command to just install stable. (Finn)
- Update suggested command for installing manually the Rust standard library sources with the version that rustc reports, rather than the minimum (since now the user may have installed e.g. stable).
Documentation/process/changes.rst | 4 +--- Documentation/rust/quick-start.rst | 15 +++++++-------- scripts/rust_is_available.sh | 8 -------- scripts/rust_is_available_test.py | 5 ----- 4 files changed, 8 insertions(+), 24 deletions(-)
Documentation/process/changes.rst +1 -3
@@ -88,9 +88,7 @@ docs on :ref:`Building Linux with Clang/LLVM <kbuild_llvm>`. Rust (optional) --------------- -A particular version of the Rust toolchain is required. Newer versions may or -may not work because the kernel depends on some unstable Rust features, for -the moment. +A recent version of the Rust compiler is required. Each Rust toolchain comes with several "components", some of which are required (like ``rustc``) and some that are optional. The ``rust-src`` component (which
Documentation/rust/quick-start.rst +7 -8
@@ -36,16 +36,15 @@ if that is the case. rustc ***** -A particular version of the Rust compiler is required. Newer versions may or -may not work because, for the moment, the kernel depends on some unstable -Rust features. +A recent version of the Rust compiler is required. If ``rustup`` is being used, enter the kernel build directory (or use -``--path=<build-dir>`` argument to the ``set`` sub-command) and run:: +``--path=<build-dir>`` argument to the ``set`` sub-command) and run, +for instance:: - rustup override set $(scripts/min-tool-version.sh rustc) + rustup override set stable -This will configure your working directory to use the correct version of +This will configure your working directory to use the given version of ``rustc`` without affecting your default toolchain. Note that the override applies to the current working directory (and its
@@ -72,9 +71,9 @@ version later on requires re-adding the component. Otherwise, if a standalone installer is used, the Rust source tree may be downloaded into the toolchain's installation folder:: - curl -L "https://static.rust-lang.org/dist/rust-src-$(scripts/min-tool-version.sh rustc).tar.gz" | + curl -L "https://static.rust-lang.org/dist/rust-src-$(rustc --version | cut -d' ' -f2).tar.gz" | tar -xzf - -C "$(rustc --print sysroot)/lib" \ - "rust-src-$(scripts/min-tool-version.sh rustc)/rust-src/lib/" \ + "rust-src-$(rustc --version | cut -d' ' -f2)/rust-src/lib/" \ --strip-components=3 In this case, upgrading the Rust compiler version later on requires manually
scripts/rust_is_available.sh +0 -8
@@ -117,14 +117,6 @@ if [ "$rust_compiler_cversion" -lt "$rust_compiler_min_cversion" ]; then echo >&2 "***" exit 1 fi -if [ "$rust_compiler_cversion" -gt "$rust_compiler_min_cversion" ]; then - echo >&2 "***" - echo >&2 "*** Rust compiler '$RUSTC' is too new. This may or may not work." - echo >&2 "*** Your version: $rust_compiler_version" - echo >&2 "*** Expected version: $rust_compiler_min_version" - echo >&2 "***" - warning=1 -fi # Check that the Rust bindings generator is suitable. #
scripts/rust_is_available_test.py +0 -5
@@ -193,11 +193,6 @@ else: result = self.run_script(self.Expected.FAILURE, { "RUSTC": rustc }) self.assertIn(f"Rust compiler '{rustc}' is too old.", result.stderr) - def test_rustc_new_version(self): - rustc = self.generate_rustc("rustc 1.999.0 (a8314ef7d 2099-06-27)") - result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "RUSTC": rustc }) - self.assertIn(f"Rust compiler '{rustc}' is too new. This may or may not work.", result.stderr) - def test_bindgen_nonexecutable(self): result = self.run_script(self.Expected.FAILURE, { "BINDGEN": self.nonexecutable }) self.assertIn(f"Running '{self.nonexecutable}' to check the Rust bindings generator version failed with", result.stderr)
--
2.45.2
Miguel Ojeda
maintainerPATCH v2 7/13 rust: avoid assuming a particular `bindgen` build
In reply to Miguel Ojedabindgen's logic to find libclang (via clang-sys) may change over time, and depends on how it was built (e.g. Linux distributions may decide to build it differently, and we are going to provide documentation on installing it via distributions later in this series).
Therefore, clarify that bindgen may be built in several ways and simplify the documentation by only mentioning the most prominent environment variable (LIBCLANG_PATH) as an example on how to tweak the search of the library at runtime (i.e. when bindgen is built as our documentation explains). This also avoids duplicating the documentation, like bindgen itself does (i.e. it refers to clang-sys).
Similarly, replace the test we had for this (which used the real program) with a mocked one, to avoid depending on the particular build as well.
--- Documentation/rust/quick-start.rst | 23 +++++++++-------------- scripts/rust_is_available_test.py | 25 +++++++++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-)
Documentation/rust/quick-start.rst +9 -14
@@ -113,20 +113,15 @@ Install it via (note that this will download and build the tool from source):: cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen-cli -``bindgen`` needs to find a suitable ``libclang`` in order to work. If it is -not found (or a different ``libclang`` than the one found should be used), -the process can be tweaked using the environment variables understood by -``clang-sys`` (the Rust bindings crate that ``bindgen`` uses to access -``libclang``): - -* ``LLVM_CONFIG_PATH`` can be pointed to an ``llvm-config`` executable. - -* Or ``LIBCLANG_PATH`` can be pointed to a ``libclang`` shared library - or to the directory containing it. - -* Or ``CLANG_PATH`` can be pointed to a ``clang`` executable. - -For details, please see ``clang-sys``'s documentation at: +``bindgen`` uses the ``clang-sys`` crate to find a suitable ``libclang`` (which +may be linked statically, dynamically or loaded at runtime). By default, the +``cargo`` command above will produce a ``bindgen`` binary that will load +``libclang`` at runtime. If it is not found (or a different ``libclang`` than +the one found should be used), the process can be tweaked, e.g. by using the +``LIBCLANG_PATH`` environment variable. For details, please see ``clang-sys``'s +documentation at: + + https://github.com/KyleMayes/clang-sys#linking https://github.com/KyleMayes/clang-sys#environment-variables
scripts/rust_is_available_test.py +15 -10
@@ -55,10 +55,15 @@ else: @classmethod def generate_bindgen(cls, version_stdout, libclang_stderr): + if libclang_stderr is None: + libclang_case = f"raise SystemExit({cls.bindgen_default_bindgen_libclang_failure_exit_code})" + else: + libclang_case = f"print({repr(libclang_stderr)}, file=sys.stderr)" + return cls.generate_executable(f"""#!/usr/bin/env python3 import sys if "rust_is_available_bindgen_libclang.h" in " ".join(sys.argv): - print({repr(libclang_stderr)}, file=sys.stderr) + {libclang_case} else: print({repr(version_stdout)}) """)
@@ -67,6 +72,10 @@ else: def generate_bindgen_version(cls, stdout): return cls.generate_bindgen(stdout, cls.bindgen_default_bindgen_libclang_stderr) + @classmethod + def generate_bindgen_libclang_failure(cls): + return cls.generate_bindgen(cls.bindgen_default_bindgen_version_stdout, None) + @classmethod def generate_bindgen_libclang(cls, stderr): return cls.generate_bindgen(cls.bindgen_default_bindgen_version_stdout, stderr)
@@ -89,6 +98,7 @@ else: cls.rust_default_sysroot = subprocess.check_output(("rustc", "--print", "sysroot")).decode().strip() cls.bindgen_default_bindgen_version_stdout = f"bindgen {cls.bindgen_default_version}" + cls.bindgen_default_bindgen_libclang_failure_exit_code = 42 cls.bindgen_default_bindgen_libclang_stderr = f"scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version {cls.llvm_default_version} [-W#pragma-messages], err: false" cls.default_rustc = cls.generate_rustc(f"rustc {cls.rustc_default_version}")
@@ -227,15 +237,10 @@ else: self.assertIn(f"Rust bindings generator '{bindgen}' is too new. This may or may not work.", result.stderr) def test_bindgen_libclang_failure(self): - for env in ( - { "LLVM_CONFIG_PATH": self.missing }, - { "LIBCLANG_PATH": self.missing }, - { "CLANG_PATH": self.missing }, - ): - with self.subTest(env=env): - result = self.run_script(self.Expected.FAILURE, env | { "PATH": os.environ["PATH"], "BINDGEN": "bindgen" }) - self.assertIn("Running 'bindgen' to check the libclang version (used by the Rust", result.stderr) - self.assertIn("bindings generator) failed with code ", result.stderr) + bindgen = self.generate_bindgen_libclang_failure() + result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen }) + self.assertIn(f"Running '{bindgen}' to check the libclang version (used by the Rust", result.stderr) + self.assertIn(f"bindings generator) failed with code {self.bindgen_default_bindgen_libclang_failure_exit_code}. This may be caused by", result.stderr) def test_bindgen_libclang_unexpected_version(self): bindgen = self.generate_bindgen_libclang("scripts/rust_is_available_bindgen_libclang.h:2:9: warning: clang version unexpected [-W#pragma-messages], err: false")
Signature · 1 line
2.45.2
Miguel Ojeda
maintainerPATCH v2 8/13 rust: work around `bindgen` 0.69.0 issue
In reply to Miguel Ojedabindgen 0.69.0 contains a bug: --version does not work without providing a header [1]:
error: the following required arguments were not provided:
<HEADER>
Usage: bindgen <FLAGS> <OPTIONS> <HEADER> -- <CLANG_ARGS>...
Thus, in preparation for supporting several bindgen versions, work around the issue by passing a dummy argument.
Include a comment so that we can remove the workaround in the future.
--- init/Kconfig | 5 ++++- scripts/rust_is_available.sh | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-)
init/Kconfig +4 -1
@@ -1928,7 +1928,10 @@ config RUSTC_VERSION_TEXT config BINDGEN_VERSION_TEXT string depends on RUST - default $(shell,command -v $(BINDGEN) >/dev/null 2>&1 && $(BINDGEN) --version || echo n) + # The dummy parameter `workaround-for-0.69.0` is required to support 0.69.0 + # (https://github.com/rust-lang/rust-bindgen/pull/2678). It can be removed when + # the minimum version is upgraded past that (0.69.1 already fixed the issue). + default $(shell,command -v $(BINDGEN) >/dev/null 2>&1 && $(BINDGEN) --version workaround-for-0.69.0 || echo n) # # Place an empty function call at each tracepoint site. Can be
scripts/rust_is_available.sh +5 -1
@@ -121,8 +121,12 @@ fi # Check that the Rust bindings generator is suitable. # # Non-stable and distributions' versions may have a version suffix, e.g. `-dev`. +# +# The dummy parameter `workaround-for-0.69.0` is required to support 0.69.0 +# (https://github.com/rust-lang/rust-bindgen/pull/2678). It can be removed when +# the minimum version is upgraded past that (0.69.1 already fixed the issue). rust_bindings_generator_output=$( \ - LC_ALL=C "$BINDGEN" --version 2>/dev/null + LC_ALL=C "$BINDGEN" --version workaround-for-0.69.0 2>/dev/null ) || rust_bindings_generator_code=$? if [ -n "$rust_bindings_generator_code" ]; then echo >&2 "***"
Signature · 1 line
2.45.2
Miguel Ojeda
maintainerPATCH v2 9/13 rust: start supporting several `bindgen` versions
In reply to Miguel OjedaWith both the workaround for bindgen 0.69.0 and the warning about 0.66.0 and 0.66.1 in place, start supporting several bindgen versions, like it was done for the Rust compiler in a previous patch.
All other versions, including the latest 0.69.4, build without errors.
The bindgen project, like Rust, has also agreed to have the kernel in their CI [1] -- thanks! This should help both projects: bindgen will be able to detect early issues like those mentioned above, and the kernel will be very likely build with new releases (at least for the basic configuration being tested).
---
v2:
- Update suggested cargo command to just install the latest, i.e. removing the --version flag, following the same pattern as Finn's suggestion to use stable for the rustup command.
Documentation/rust/quick-start.rst | 7 ++++--- scripts/rust_is_available.sh | 8 -------- scripts/rust_is_available_test.py | 5 ----- 3 files changed, 4 insertions(+), 16 deletions(-)
Documentation/rust/quick-start.rst +4 -3
@@ -107,11 +107,12 @@ bindgen ******* The bindings to the C side of the kernel are generated at build time using -the ``bindgen`` tool. A particular version is required. +the ``bindgen`` tool. -Install it via (note that this will download and build the tool from source):: +Install it, for instance, via (note that this will download and build the tool +from source):: - cargo install --locked --version $(scripts/min-tool-version.sh bindgen) bindgen-cli + cargo install --locked bindgen-cli ``bindgen`` uses the ``clang-sys`` crate to find a suitable ``libclang`` (which may be linked statically, dynamically or loaded at runtime). By default, the
scripts/rust_is_available.sh +0 -8
@@ -161,14 +161,6 @@ if [ "$rust_bindings_generator_cversion" -lt "$rust_bindings_generator_min_cvers echo >&2 "***" exit 1 fi -if [ "$rust_bindings_generator_cversion" -gt "$rust_bindings_generator_min_cversion" ]; then - echo >&2 "***" - echo >&2 "*** Rust bindings generator '$BINDGEN' is too new. This may or may not work." - echo >&2 "*** Your version: $rust_bindings_generator_version" - echo >&2 "*** Expected version: $rust_bindings_generator_min_version" - echo >&2 "***" - warning=1 -fi # Check that the `libclang` used by the Rust bindings generator is suitable. #
scripts/rust_is_available_test.py +0 -5
@@ -231,11 +231,6 @@ else: result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen }) self.assertIn(f"Rust bindings generator '{bindgen}' is too old.", result.stderr) - def test_bindgen_new_version(self): - bindgen = self.generate_bindgen_version("bindgen 0.999.0") - result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen }) - self.assertIn(f"Rust bindings generator '{bindgen}' is too new. This may or may not work.", result.stderr) - def test_bindgen_libclang_failure(self): bindgen = self.generate_bindgen_libclang_failure() result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
--
2.45.2
Miguel Ojeda
maintainerPATCH v2 10/13 rust: warn about `bindgen` versions 0.66.0 and 0.66.1
In reply to Miguel Ojedabindgen versions 0.66.0 and 0.66.1 panic due to C string literals with NUL characters [1]:
panicked at .cargo/registry/src/index.crates.io-6f17d22bba15001f/bindgen-0.66.0/codegen/mod.rs:717:71:
called `Result::unwrap()` on an `Err` value: FromBytesWithNulError { kind: InteriorNul(4) }Thus, in preparation for supporting several bindgen versions, add a version check to warn the user about it.
Since some distributions may have patched it (e.g. Debian did [2]), check if that seems to be the case (after the version check matches), in order to avoid printing a warning in that case.
We could make it an error, but 1) it is going to fail anyway later in the build, 2) we would disable RUST, which is also painful, 3) someone could have patched it in a way that still makes our extra check fail (however unlikely), 4) the interior NUL may go away in the headers (however unlikely). Thus just warn about it so that users know why it is failing.
In addition, add a couple tests for the new cases.
---
v2:
- If the version matches, then also check whether the bindgen binary fails with a simple interior NUL case, so that we can avoid the warning if it has been patched (like Debian's). Update the comment for the warning as well, and add a test for it too.
- Move test later in the series so that the new test does not fail due to the "too new" check.
- Split if condition in 2 lines.
scripts/rust_is_available.sh | 13 ++++++++++++ scripts/rust_is_available_bindgen_0_66.h | 2 ++ scripts/rust_is_available_test.py | 26 +++++++++++++++++++++--- 3 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 scripts/rust_is_available_bindgen_0_66.h
scripts/rust_is_available.sh +13 -0
@@ -161,6 +161,19 @@ if [ "$rust_bindings_generator_cversion" -lt "$rust_bindings_generator_min_cvers echo >&2 "***" exit 1 fi +if [ "$rust_bindings_generator_cversion" -eq 6600 ] || + [ "$rust_bindings_generator_cversion" -eq 6601 ]; then + # Distributions may have patched the issue (e.g. Debian did). + if ! "$BINDGEN" $(dirname $0)/rust_is_available_bindgen_0_66.h >/dev/null; then + echo >&2 "***" + echo >&2 "*** Rust bindings generator '$BINDGEN' versions 0.66.0 and 0.66.1 may not" + echo >&2 "*** work due to a bug (https://github.com/rust-lang/rust-bindgen/pull/2567)," + echo >&2 "*** unless patched (like Debian's)." + echo >&2 "*** Your version: $rust_bindings_generator_version" + echo >&2 "***" + warning=1 + fi +fi # Check that the `libclang` used by the Rust bindings generator is suitable. #
scripts/rust_is_available_bindgen_0_66.h new file +2 -0
@@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#define A "\0"
scripts/rust_is_available_test.py +23 -3
@@ -54,23 +54,30 @@ else: """) @classmethod - def generate_bindgen(cls, version_stdout, libclang_stderr): + def generate_bindgen(cls, version_stdout, libclang_stderr, version_0_66_patched=False): if libclang_stderr is None: libclang_case = f"raise SystemExit({cls.bindgen_default_bindgen_libclang_failure_exit_code})" else: libclang_case = f"print({repr(libclang_stderr)}, file=sys.stderr)" + if version_0_66_patched: + version_0_66_case = "pass" + else: + version_0_66_case = "raise SystemExit(1)" + return cls.generate_executable(f"""#!/usr/bin/env python3 import sys if "rust_is_available_bindgen_libclang.h" in " ".join(sys.argv): {libclang_case} +elif "rust_is_available_bindgen_0_66.h" in " ".join(sys.argv): + {version_0_66_case} else: print({repr(version_stdout)}) """) @classmethod - def generate_bindgen_version(cls, stdout): - return cls.generate_bindgen(stdout, cls.bindgen_default_bindgen_libclang_stderr) + def generate_bindgen_version(cls, stdout, version_0_66_patched=False): + return cls.generate_bindgen(stdout, cls.bindgen_default_bindgen_libclang_stderr, version_0_66_patched) @classmethod def generate_bindgen_libclang_failure(cls):
@@ -231,6 +238,19 @@ else: result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen }) self.assertIn(f"Rust bindings generator '{bindgen}' is too old.", result.stderr) + def test_bindgen_bad_version_0_66_0_and_0_66_1(self): + for version in ("0.66.0", "0.66.1"): + with self.subTest(version=version): + bindgen = self.generate_bindgen_version(f"bindgen {version}") + result = self.run_script(self.Expected.SUCCESS_WITH_WARNINGS, { "BINDGEN": bindgen }) + self.assertIn(f"Rust bindings generator '{bindgen}' versions 0.66.0 and 0.66.1 may not", result.stderr) + + def test_bindgen_bad_version_0_66_0_and_0_66_1_patched(self): + for version in ("0.66.0", "0.66.1"): + with self.subTest(version=version): + bindgen = self.generate_bindgen_version(f"bindgen {version}", True) + result = self.run_script(self.Expected.SUCCESS, { "BINDGEN": bindgen }) + def test_bindgen_libclang_failure(self): bindgen = self.generate_bindgen_libclang_failure() result = self.run_script(self.Expected.FAILURE, { "BINDGEN": bindgen })
--
2.45.2
Miguel Ojeda
maintainerPATCH v2 11/13 kbuild: rust: add `rustc-version` support
In reply to Miguel OjedaNow that we are starting to support several Rust versions, introduce rustc-version support, mimicking the C side:
- scripts/rustc-version.sh, that mimics the other version scripts
(with one more digit, e.g. Rust 1.79.0 is 107900).
- rustc-{info,name,version} Kbuild macros.
- CONFIG_RUSTC_VERSION Kconfig symbol that calls rustc-version.
- rustc-min-version Kbuild macro that uses CONFIG_RUSTC_VERSION.
With these, we can easily support flags conditionally depending on rustc's version -- a user comes in the next patch.
Another user will be the -Ctarget-feature=+reserve-x18/-Zfixed-x18 arm64 flags [1].
---
v2:
- Fix comment from "5 or 6-digit" to "6 to 7-digit".
init/Kconfig | 6 +++++ scripts/Kconfig.include | 6 +++++ scripts/Makefile.compiler | 4 +++ scripts/rustc-version.sh | 52 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+) create mode 100755 scripts/rustc-version.sh
init/Kconfig +6 -0
@@ -1920,6 +1920,12 @@ config RUST If unsure, say N. +config RUSTC_VERSION + int + depends on RUST + default $(rustc-version) + default 0 + config RUSTC_VERSION_TEXT string depends on RUST
scripts/Kconfig.include +6 -0
@@ -45,6 +45,12 @@ $(error-if,$(success,test -z "$(cc-info)"),Sorry$(comma) this C compiler is not cc-name := $(shell,set -- $(cc-info) && echo $1) cc-version := $(shell,set -- $(cc-info) && echo $2) +# Get the Rust compiler name, version, and error out if it is not supported. +rustc-info := $(shell,$(srctree)/scripts/rustc-version.sh $(RUSTC)) +$(error-if,$(success,test -z "$(rustc-info)"),Sorry$(comma) this Rust compiler is not supported.) +rustc-name := $(shell,set -- $(rustc-info) && echo $1) +rustc-version := $(shell,set -- $(rustc-info) && echo $2) + # Get the assembler name, version, and error out if it is not supported. as-info := $(shell,$(srctree)/scripts/as-version.sh $(CC) $(CLANG_FLAGS)) $(error-if,$(success,test -z "$(as-info)"),Sorry$(comma) this assembler is not supported.)
scripts/Makefile.compiler +4 -0
@@ -69,6 +69,10 @@ gcc-min-version = $(call test-ge, $(CONFIG_GCC_VERSION), $1) # Usage: cflags-$(call clang-min-version, 110000) += -foo clang-min-version = $(call test-ge, $(CONFIG_CLANG_VERSION), $1) +# rustc-min-version +# Usage: rustflags-$(call rustc-min-version, 107900) += -foo +rustc-min-version = $(call test-ge, $(CONFIG_RUSTC_VERSION), $1) + # ld-option # Usage: KBUILD_LDFLAGS += $(call ld-option, -X, -Y) ld-option = $(call try-run, $(LD) $(KBUILD_LDFLAGS) $(1) -v,$(1),$(2),$(3))
scripts/rustc-version.sh new file +52 -0
52 lines not on this page · raw message
--
2.45.2
Re: [PATCH v2 11/13] kbuild: rust: add `rustc-version` support
Quoted 2 lines · Miguel Ojeda
+$(error-if,$(success,test -z "$(rustc-info)"),Sorry$(comma) this Rust compiler is not supported.)
Bah, this is broken, I just noticed in my CI that I didn't handle the "Rust not installed" case.
Anyway, this patch (and the next one) are not important for the series, I will just drop them and send them independently next cycle to Kbuild instead. I should have done that anyway, even if they were correct.
Cheers,
Miguel
Miguel Ojeda
maintainerPATCH v2 12/13 rust: support the new `-Zub-checks` flag
In reply to Miguel OjedaRust 1.79.0 has introduced a new codegen flag, -Zub-checks [1], to allow to independently configure (from -Cdebug-assertions) whether the extra runtime checks for UB are emitted, in a similar fashion to -Coverflow-checks.
This allows to configure the kernel with only the UB checks enabled, but not the debug_assert!s; or vice versa, e.g. [2].
It also showcases how RUSTC_VERSION and the Kbuild macros, introduced in the previous commit, can be used.
--- Makefile | 9 +++++++-- lib/Kconfig.debug | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-)
Makefile +7 -2
@@ -821,10 +821,15 @@ KBUILD_CFLAGS += -Os KBUILD_RUSTFLAGS += -Copt-level=s endif -# Always set `debug-assertions` and `overflow-checks` because their default -# depends on `opt-level` and `debug-assertions`, respectively. +# Always set `debug-assertions` because its default depends on `opt-level`. KBUILD_RUSTFLAGS += -Cdebug-assertions=$(if $(CONFIG_RUST_DEBUG_ASSERTIONS),y,n) + +# Always set `overflow-checks` and `ub-checks` because their default depends on +# `debug-assertions`. KBUILD_RUSTFLAGS += -Coverflow-checks=$(if $(CONFIG_RUST_OVERFLOW_CHECKS),y,n) +ifeq ($(call rustc-min-version, 107900),y) +KBUILD_RUSTFLAGS += -Zub-checks=$(if $(CONFIG_RUST_UNDEFINED_BEHAVIOR_CHECKS),y,n) +endif # Tell gcc to never replace conditional load with a non-conditional one ifdef CONFIG_CC_IS_GCC
lib/Kconfig.debug +18 -0
@@ -3020,6 +3020,24 @@ config RUST_OVERFLOW_CHECKS If unsure, say Y. +config RUST_UNDEFINED_BEHAVIOR_CHECKS + bool "Undefined Behavior checks" + depends on RUST && RUSTC_VERSION >= 107900 + help + Enables rustc's `-Zub-checks` codegen option. + + This flag allows you to control whether additional runtime checks that + detect some causes of Undefined Behavior at runtime will be emitted. + When enabled, a Rust panic will occur if UB is detected. + + All checks are generated on a best-effort basis; even if there is a check + implemented for some cause of Undefined Behavior, it may be possible for + the check to not fire. + + Note that this will apply to all Rust code, including `core`. + + If unsure, say N. + config RUST_BUILD_ASSERT_ALLOW bool "Allow unoptimized build-time assertions" depends on RUST
Signature · 1 line
2.45.2
Miguel Ojeda
maintainerPATCH v2 13/13 docs: rust: quick-start: add section on Linux distributions
In reply to Miguel OjedaNow that we are starting to support several Rust compiler and bindgen versions, there is a good chance some Linux distributions work out of the box.
Thus, provide some instructions on how to set the toolchain up for a few major Linux distributions. This simplifies the setup users need to build the kernel.
In addition, add an introduction to the document so that it is easier to understand its structure and move the LLVM+Rust kernel.org toolchains paragraph there (removing "depending on the Linux version"). We may want to reorganize the document or split it in the future, but I wanted to focus this commit on the new information added about each particular distribution.
Finally, remove the rustup's components mention in changes.rst since users do not need it if they install the toolchain via the distributions (and anyway it was too detailed for that main document).
---
v2:
- Add openSUSE Slowroll.
- Update openSUSE docs: both Slowroll and Tumbleweed now provide rustfmt, Clippy and the Rust standard library sources, so the comment about those can be dropped. (William)
- I ended up leaving the Debian/Ubuntu docs as-is for the time being. Happy to get a patch for that later. We should definitely add them if they start working for the current kernel (currently: Ubuntu's latest versioned package is 1.76, and Debian's web one is 1.70). (Andrea, Fabian)
- Move the LLVM+Rust kernel.org toolchains paragraph to the introduction that this commit adds, now that we have access to that paragraph since the series is rebased on top of rust-next.
- Use plural for "release".
Documentation/process/changes.rst | 5 -- Documentation/rust/quick-start.rst | 93 ++++++++++++++++++++++++++---- 2 files changed, 81 insertions(+), 17 deletions(-)
Documentation/process/changes.rst +0 -5
@@ -90,11 +90,6 @@ Rust (optional) A recent version of the Rust compiler is required. -Each Rust toolchain comes with several "components", some of which are required -(like ``rustc``) and some that are optional. The ``rust-src`` component (which -is optional) needs to be installed to build the kernel. Other components are -useful for developing. - Please see Documentation/rust/quick-start.rst for instructions on how to satisfy the build requirements of Rust support. In particular, the ``Makefile`` target ``rustavailable`` is useful to check why the Rust toolchain may not
Documentation/rust/quick-start.rst +81 -12
105 lines not on this page · raw message
--
2.45.2
Miguel Ojeda
AppliedQuoted 5 lines · Miguel Ojeda
A few things improved here and there, and rebased on top of
rust-next.The changelog is attached to each patch.
Quoted 7 lines
I kept the
Tested-bys since most of the changes are on documentation or comments, though I did remove them on the patch that changed the most just in case (even for that one, I think Benno's and Andreas' setup would not have made a difference).I plan to put this series into
rust-nextvery soon so that it goes into the merge window.
Applied into rust-next -- thanks everyone!
Cheers,
Miguel