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

Depend on syn #302

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,10 @@ jobs:
- run: |
curl -o bin/bindgen https://raw.githubusercontent.com/Rust-for-Linux/ci-bin/master/bindgen-0.56.0/bin/bindgen
chmod u+x bin/bindgen

# Setup: Procedural macro dependencies
- run: |
make rust-fetch-deps

# Setup: ccache
- run: |
Expand Down
12 changes: 12 additions & 0 deletions Documentation/rust/quick-start.rst
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,18 @@ Install it via (this will build the tool from source)::
cargo install --locked --version 0.56.0 bindgen


Procedural macro dependencies
*****************************

We use procedural macros that need to parse Rust code. We depend on a few
well-established crates in Rust community (`syn`, `quote` and `proc-macro2`)
for this task.

Fetch them via::

make rust-fetch-deps


Requirements: Developing
------------------------

Expand Down
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ no-dot-config-targets := $(clean-targets) \
cscope gtags TAGS tags help% %docs check% coccicheck \
$(version_h) headers headers_% archheaders archscripts \
%asm-generic kernelversion %src-pkg dt_binding_check \
outputmakefile rustfmt rustfmtcheck
outputmakefile rustfmt rustfmtcheck rust-fetch-deps
no-sync-config-targets := $(no-dot-config-targets) %install kernelrelease \
image_name
single-targets := %.a %.i %.ko %.lds %.ll %.lst %.mod %.o %.s %.symtypes %/
Expand Down Expand Up @@ -1832,6 +1832,11 @@ rustfmt:
rustfmtcheck:
find $(srctree) -type f -name '*.rs' | xargs $(RUSTFMT) --check

# Procedural macros dependency fetch
PHONY += rust-fetch-deps
rust-fetch-deps:
$(Q)$(MAKE) $(build)=rust $@

# IDE support targets
PHONY += rust-analyzer
rust-analyzer: prepare0
Expand Down
3 changes: 2 additions & 1 deletion rust/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@

bindings_generated.rs
exports_*_generated.h
doc/
doc/
target/
44 changes: 22 additions & 22 deletions rust/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
obj-$(CONFIG_RUST) += core.o compiler_builtins.o helpers.o
extra-$(CONFIG_RUST) += exports_core_generated.h

extra-$(CONFIG_RUST) += libmodule.so
extra-$(CONFIG_RUST) += libmacros.so

extra-$(CONFIG_RUST) += bindings_generated.rs
obj-$(CONFIG_RUST) += alloc.o kernel.o
Expand Down Expand Up @@ -35,21 +35,16 @@ quiet_cmd_rustdoc = RUSTDOC $<
--output $(objtree)/rust/doc --crate-name $(subst rustdoc-,,$@) \
-Fmissing-docs @$(objtree)/include/generated/rustc_cfg $<

rustdoc: rustdoc-module rustdoc-compiler_builtins rustdoc-kernel

rustdoc-module: private rustdoc_target_flags = --crate-type proc-macro \
--extern proc_macro
rustdoc-module: $(srctree)/rust/module.rs FORCE
$(call if_changed,rustdoc_host)
rustdoc: rustdoc-compiler_builtins rustdoc-kernel

rustdoc-compiler_builtins: $(srctree)/rust/compiler_builtins.rs FORCE
$(call if_changed,rustdoc)

rustdoc-kernel: private rustdoc_target_flags = --extern alloc \
--extern build_error \
--extern module=$(objtree)/rust/libmodule.so
rustdoc-kernel: $(srctree)/rust/kernel/lib.rs rustdoc-module \
$(objtree)/rust/libmodule.so $(objtree)/rust/bindings_generated.rs FORCE
--extern macros=$(objtree)/rust/libmacros.so
rustdoc-kernel: $(srctree)/rust/kernel/lib.rs \
$(objtree)/rust/libmacros.so $(objtree)/rust/bindings_generated.rs FORCE
$(call if_changed,rustdoc)

ifdef CONFIG_CC_IS_CLANG
Expand Down Expand Up @@ -113,22 +108,27 @@ $(objtree)/rust/exports_alloc_generated.h: $(objtree)/rust/alloc.o FORCE
$(objtree)/rust/exports_kernel_generated.h: $(objtree)/rust/kernel.o FORCE
$(call if_changed,exports)

CARGO = cargo

# `-Cpanic=unwind -Cforce-unwind-tables=y` overrides `rustc_flags` in order to
# avoid the https://github.com/rust-lang/rust/issues/82320 rustc crash.
quiet_cmd_rustc_procmacro = $(RUSTC_OR_CLIPPY_QUIET) P $@
quiet_cmd_rustc_procmacro = CARGO P $@
cmd_rustc_procmacro = \
$(RUSTC_OR_CLIPPY) $(rustc_flags) \
--emit=dep-info,link --extern proc_macro \
-Cpanic=unwind -Cforce-unwind-tables=y \
--crate-type proc-macro --out-dir $(objtree)/rust/ \
--crate-name $(patsubst lib%.so,%,$(notdir $@)) $<; \
mv $(objtree)/rust/$(patsubst lib%.so,%,$(notdir $@)).d $(depfile); \
$(CARGO) build --locked --offline --release \
--manifest-path $< \
--target-dir $(objtree)/rust/target; \
cp $(objtree)/rust/target/release/$(notdir $@) $@; \
mv $(objtree)/rust/target/release/$(patsubst %.so,%,$(notdir $@)).d $(depfile); \
sed -i 's/build.rs//' $(depfile); \
sed -i '/^\#/d' $(depfile)

rust-fetch-deps:
$(CARGO) fetch --locked --manifest-path $(srctree)/rust/macros/Cargo.toml

# Procedural macros can only be used with the `rustc` that compiled it.
# Therefore, to get `libmodule.so` automatically recompiled when the compiler
# Therefore, to get `libmacros.so` automatically recompiled when the compiler
# version changes, we add `core.o` as a dependency (even if it is not needed).
$(objtree)/rust/libmodule.so: $(srctree)/rust/module.rs \
$(objtree)/rust/libmacros.so: $(srctree)/rust/macros/Cargo.toml \
$(objtree)/rust/core.o FORCE
$(call if_changed_dep,rustc_procmacro)

Expand Down Expand Up @@ -169,11 +169,11 @@ $(objtree)/rust/build_error.o: $(srctree)/rust/build_error.rs \
$(objtree)/rust/compiler_builtins.o FORCE
$(call if_changed_dep,rustc_library)

# ICE on `--extern module`: https://github.com/rust-lang/rust/issues/56935
# ICE on `--extern macros`: https://github.com/rust-lang/rust/issues/56935
$(objtree)/rust/kernel.o: private rustc_target_flags = --extern alloc \
--extern build_error \
--extern module=$(objtree)/rust/libmodule.so
--extern macros=$(objtree)/rust/libmacros.so
$(objtree)/rust/kernel.o: $(srctree)/rust/kernel/lib.rs $(objtree)/rust/alloc.o \
$(objtree)/rust/build_error.o \
$(objtree)/rust/libmodule.so $(objtree)/rust/bindings_generated.rs FORCE
$(objtree)/rust/libmacros.so $(objtree)/rust/bindings_generated.rs FORCE
$(call if_changed_dep,rustc_library)
4 changes: 2 additions & 2 deletions rust/kernel/module_param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub trait ModuleParam: core::fmt::Display + core::marker::Sized {
/// Get the current value of the parameter for use in the kernel module.
///
/// This function should not be used directly. Instead use the wrapper
/// `read` which will be generated by [`module::module`].
/// `read` which will be generated by [`macros::module`].
fn value(&self) -> &Self::Value;

/// Set the module parameter from a string.
Expand Down Expand Up @@ -428,7 +428,7 @@ impl<T: Copy + core::fmt::Display + ModuleParam, const N: usize> ModuleParam
/// A C-style string parameter.
///
/// The Rust version of the [`charp`] parameter. This type is meant to be
/// used by the [`module::module`] macro, not handled directly. Instead use the
/// used by the [`macros::module`] macro, not handled directly. Instead use the
/// `read` method generated by that macro.
///
/// [`charp`]: ../../../include/linux/moduleparam.h
Expand Down
2 changes: 1 addition & 1 deletion rust/kernel/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub use alloc::{borrow::ToOwned, string::String};

pub use super::build_assert;

pub use module::{module, module_misc_device};
pub use macros::{module, module_misc_device};

pub use super::{pr_alert, pr_cont, pr_crit, pr_emerg, pr_err, pr_info, pr_notice, pr_warn};

Expand Down
47 changes: 47 additions & 0 deletions rust/macros/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions rust/macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[package]
name = "macros"
version = "0.0.0"
edition = "2018"
license = "GPL-2.0-only"

[lib]
path = "lib.rs"
proc_macro = true

[dependencies]
syn = { version = "1.0", features = ["full"] }
quote = "1.0"
proc-macro2 = "1.0"
126 changes: 126 additions & 0 deletions rust/macros/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
// SPDX-License-Identifier: GPL-2.0

//! Crate for all kernel procedural macros.
mod module;

use proc_macro::TokenStream;

/// Declares a kernel module.
///
/// The `type` argument should be a type which implements the [`KernelModule`]
/// trait. Also accepts various forms of kernel metadata.
///
/// C header: [`include/linux/moduleparam.h`](../../../include/linux/moduleparam.h)
///
/// [`KernelModule`]: ../kernel/trait.KernelModule.html
///
/// # Examples
///
/// ```rust,no_run
/// use kernel::prelude::*;
///
/// module!{
/// type: MyKernelModule,
/// name: b"my_kernel_module",
/// author: b"Rust for Linux Contributors",
/// description: b"My very own kernel module!",
/// license: b"GPL v2",
/// params: {
/// my_i32: i32 {
/// default: 42,
/// permissions: 0o000,
/// description: b"Example of i32",
/// },
/// writeable_i32: i32 {
/// default: 42,
/// permissions: 0o644,
/// description: b"Example of i32",
/// },
/// },
/// }
///
/// struct MyKernelModule;
///
/// impl KernelModule for MyKernelModule {
/// fn init() -> Result<Self> {
/// // If the parameter is writeable, then the kparam lock must be
/// // taken to read the parameter:
/// {
/// let lock = THIS_MODULE.kernel_param_lock();
/// pr_info!("i32 param is: {}\n", writeable_i32.read(&lock));
/// }
/// // If the parameter is read only, it can be read without locking
/// // the kernel parameters:
/// pr_info!("i32 param is: {}\n", my_i32.read());
/// Ok(MyKernelModule)
/// }
/// }
/// ```
///
/// # Supported argument types
/// - `type`: type which implements the [`KernelModule`] trait (required).
/// - `name`: byte array of the name of the kernel module (required).
/// - `author`: byte array of the author of the kernel module.
/// - `description`: byte array of the description of the kernel module.
/// - `license`: byte array of the license of the kernel module (required).
/// - `alias`: byte array of alias name of the kernel module.
/// - `alias_rtnl_link`: byte array of the `rtnl_link_alias` of the kernel module (mutually exclusive with `alias`).
/// - `params`: parameters for the kernel module, as described below.
///
/// # Supported parameter types
///
/// - `bool`: Corresponds to C `bool` param type.
/// - `i8`: No equivalent C param type.
/// - `u8`: Corresponds to C `char` param type.
/// - `i16`: Corresponds to C `short` param type.
/// - `u16`: Corresponds to C `ushort` param type.
/// - `i32`: Corresponds to C `int` param type.
/// - `u32`: Corresponds to C `uint` param type.
/// - `i64`: No equivalent C param type.
/// - `u64`: Corresponds to C `ullong` param type.
/// - `isize`: No equivalent C param type.
/// - `usize`: No equivalent C param type.
/// - `str`: Corresponds to C `charp` param type. Reading returns a byte slice.
/// - `ArrayParam<T,N>`: Corresponds to C parameters created using `module_param_array`. An array
/// of `T`'s of length at **most** `N`.
///
/// `invbool` is unsupported: it was only ever used in a few modules.
/// Consider using a `bool` and inverting the logic instead.
#[proc_macro]
pub fn module(ts: TokenStream) -> TokenStream {
module::module(ts)
}

/// Declares a kernel module that exposes a single misc device.
///
/// The `type` argument should be a type which implements the [`FileOpener`] trait. Also accepts
/// various forms of kernel metadata.
///
/// C header: [`include/linux/moduleparam.h`](../../../include/linux/moduleparam.h)
///
/// [`FileOpener`]: ../kernel/file_operations/trait.FileOpener.html
///
/// # Examples
///
/// ```rust,no_run
/// use kernel::prelude::*;
///
/// module_misc_device! {
/// type: MyFile,
/// name: b"my_miscdev_kernel_module",
/// author: b"Rust for Linux Contributors",
/// description: b"My very own misc device kernel module!",
/// license: b"GPL v2",
/// }
///
/// #[derive(Default)]
/// struct MyFile;
///
/// impl kernel::file_operations::FileOperations for MyFile {
/// kernel::declare_file_operations!();
/// }
/// ```
#[proc_macro]
pub fn module_misc_device(ts: TokenStream) -> TokenStream {
module::module_misc_device(ts)
}
Loading