Skip to content

cleanup modinfo generation in module! #373

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

Merged
merged 1 commit into from
Jul 2, 2021
Merged
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
196 changes: 86 additions & 110 deletions rust/macros/module.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0

use proc_macro::{token_stream, Delimiter, Group, TokenStream, TokenTree};
use proc_macro::{token_stream, Delimiter, Group, Literal, TokenStream, TokenTree};

fn try_ident(it: &mut token_stream::IntoIter) -> Option<String> {
if let Some(TokenTree::Ident(ident)) = it.next() {
Expand Down Expand Up @@ -110,92 +110,78 @@ fn get_byte_string(it: &mut token_stream::IntoIter, expected_name: &str) -> Stri
byte_string
}

fn __build_modinfo_string_base(
module: &str,
field: &str,
content: &str,
variable: &str,
builtin: bool,
) -> String {
let string = if builtin {
// Built-in modules prefix their modinfo strings by `module.`.
format!(
"{module}.{field}={content}",
module = module,
field = field,
content = content
)
} else {
// Loadable modules' modinfo strings go as-is.
format!("{field}={content}", field = field, content = content)
};
struct ModInfoBuilder<'a> {
module: &'a str,
counter: usize,
buffer: String,
}

format!(
"
{cfg}
#[doc(hidden)]
#[link_section = \".modinfo\"]
#[used]
pub static {variable}: [u8; {length}] = *b\"{string}\\0\";
",
cfg = if builtin {
"#[cfg(not(MODULE))]"
impl<'a> ModInfoBuilder<'a> {
fn new(module: &'a str) -> Self {
ModInfoBuilder {
module,
counter: 0,
buffer: String::new(),
}
}

fn emit_base(&mut self, field: &str, content: &str, builtin: bool) {
use std::fmt::Write;

let string = if builtin {
// Built-in modules prefix their modinfo strings by `module.`.
format!(
"{module}.{field}={content}\0",
module = self.module,
field = field,
content = content
)
} else {
"#[cfg(MODULE)]"
},
variable = variable,
length = string.len() + 1,
string = string,
)
}
// Loadable modules' modinfo strings go as-is.
format!("{field}={content}\0", field = field, content = content)
};

fn __build_modinfo_string_variable(module: &str, field: &str) -> String {
format!("__{module}_{field}", module = module, field = field)
}
write!(
&mut self.buffer,
"
{cfg}
#[doc(hidden)]
#[link_section = \".modinfo\"]
#[used]
pub static __{module}_{counter}: [u8; {length}] = *{string};
",
cfg = if builtin {
"#[cfg(not(MODULE))]"
} else {
"#[cfg(MODULE)]"
},
module = self.module,
counter = self.counter,
length = string.len(),
string = Literal::byte_string(string.as_bytes()),
)
.unwrap();

fn build_modinfo_string_only_builtin(module: &str, field: &str, content: &str) -> String {
__build_modinfo_string_base(
module,
field,
content,
&__build_modinfo_string_variable(module, field),
true,
)
}
self.counter += 1;
}

fn build_modinfo_string_only_loadable(module: &str, field: &str, content: &str) -> String {
__build_modinfo_string_base(
module,
field,
content,
&__build_modinfo_string_variable(module, field),
false,
)
}
fn emit_only_builtin(&mut self, field: &str, content: &str) {
self.emit_base(field, content, true)
}

fn build_modinfo_string(module: &str, field: &str, content: &str) -> String {
build_modinfo_string_only_builtin(module, field, content)
+ &build_modinfo_string_only_loadable(module, field, content)
}
fn emit_only_loadable(&mut self, field: &str, content: &str) {
self.emit_base(field, content, false)
}

fn build_modinfo_string_optional(module: &str, field: &str, content: Option<&str>) -> String {
if let Some(content) = content {
build_modinfo_string(module, field, content)
} else {
"".to_string()
fn emit(&mut self, field: &str, content: &str) {
self.emit_only_builtin(field, content);
self.emit_only_loadable(field, content);
}
}

fn build_modinfo_string_param(module: &str, field: &str, param: &str, content: &str) -> String {
let variable = format!(
"__{module}_{field}_{param}",
module = module,
field = field,
param = param
);
let content = format!("{param}:{content}", param = param, content = content);
__build_modinfo_string_base(module, field, &content, &variable, true)
+ &__build_modinfo_string_base(module, field, &content, &variable, false)
fn emit_param(&mut self, field: &str, param: &str, content: &str) {
let content = format!("{param}:{content}", param = param, content = content);
self.emit(field, &content);
}
}

fn permissions_are_readonly(perms: &str) -> bool {
Expand Down Expand Up @@ -398,8 +384,24 @@ pub fn module(ts: TokenStream) -> TokenStream {

let name = info.name.clone();

let mut modinfo = ModInfoBuilder::new(&name);
if let Some(author) = info.author {
modinfo.emit("author", &author);
}
if let Some(description) = info.description {
modinfo.emit("description", &description);
}
modinfo.emit("license", &info.license);
if let Some(alias) = info.alias {
modinfo.emit("alias", &alias);
}

// Built-in modules also export the `file` modinfo string
let file =
std::env::var("RUST_MODFILE").expect("Unable to fetch RUST_MODFILE environmental variable");
modinfo.emit_only_builtin("file", &file);

let mut array_types_to_generate = Vec::new();
let mut params_modinfo = String::new();
if let Some(params) = info.params {
assert_eq!(params.delimiter(), Delimiter::Brace);

Expand Down Expand Up @@ -444,18 +446,8 @@ pub fn module(ts: TokenStream) -> TokenStream {
}
};

params_modinfo.push_str(&build_modinfo_string_param(
&name,
"parmtype",
&param_name,
&param_kernel_type,
));
params_modinfo.push_str(&build_modinfo_string_param(
&name,
"parm",
&param_name,
&param_description,
));
modinfo.emit_param("parmtype", &param_name, &param_kernel_type);
modinfo.emit_param("parm", &param_name, &param_description);
let param_type_internal = match param_type {
ParamType::Ident(ref param_type) => match param_type.as_ref() {
"str" => "kernel::module_param::StringParam".to_string(),
Expand Down Expand Up @@ -504,7 +496,7 @@ pub fn module(ts: TokenStream) -> TokenStream {
name = name,
param_name = param_name,
);
params_modinfo.push_str(
modinfo.buffer.push_str(
&format!(
"
static mut __{name}_{param_name}_value: {param_type_internal} = {param_default};
Expand Down Expand Up @@ -580,9 +572,6 @@ pub fn module(ts: TokenStream) -> TokenStream {
));
}

let file =
std::env::var("RUST_MODFILE").expect("Unable to fetch RUST_MODFILE environmental variable");

format!(
"
/// The module name.
Expand Down Expand Up @@ -667,26 +656,13 @@ pub fn module(ts: TokenStream) -> TokenStream {
}}
}}

{author}
{description}
{license}
{alias}

// Built-in modules also export the `file` modinfo string
{file}

{params_modinfo}
{modinfo}

{generated_array_types}
",
type_ = info.type_,
name = info.name,
author = &build_modinfo_string_optional(&name, "author", info.author.as_deref()),
description = &build_modinfo_string_optional(&name, "description", info.description.as_deref()),
license = &build_modinfo_string(&name, "license", &info.license),
alias = &build_modinfo_string_optional(&name, "alias", info.alias.as_deref()),
file = &build_modinfo_string_only_builtin(&name, "file", &file),
params_modinfo = params_modinfo,
modinfo = modinfo.buffer,
generated_array_types = generated_array_types,
initcall_section = ".initcall6.init"
).parse().expect("Error parsing formatted string into token stream.")
Expand Down