mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-21 00:06:34 +00:00
Refactor project structure
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
bd878dd1c9
commit
e3c227ae06
35
kernel/libs/comp-sys/component-macro/src/init_comp.rs
Normal file
35
kernel/libs/comp-sys/component-macro/src/init_comp.rs
Normal file
@ -0,0 +1,35 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use proc_macro2::{TokenStream, TokenTree};
|
||||
use quote::{ToTokens, TokenStreamExt};
|
||||
use syn::parse::Parse;
|
||||
|
||||
/// The content inside typeflag macro
|
||||
pub struct ComponentInitFunction {
|
||||
function: Vec<TokenTree>,
|
||||
pub function_name: TokenTree,
|
||||
}
|
||||
|
||||
impl Parse for ComponentInitFunction {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let mut vec: Vec<TokenTree> = Vec::new();
|
||||
vec.push(input.parse().unwrap());
|
||||
let function_name: TokenTree = input.parse().unwrap();
|
||||
vec.push(function_name.clone());
|
||||
while !input.is_empty() {
|
||||
vec.push(input.parse().unwrap())
|
||||
}
|
||||
Ok(ComponentInitFunction {
|
||||
function: vec,
|
||||
function_name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ComponentInitFunction {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
for token in &self.function {
|
||||
tokens.append(token.clone());
|
||||
}
|
||||
}
|
||||
}
|
87
kernel/libs/comp-sys/component-macro/src/lib.rs
Normal file
87
kernel/libs/comp-sys/component-macro/src/lib.rs
Normal file
@ -0,0 +1,87 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//!This crate defines the component system related macros.
|
||||
//!
|
||||
|
||||
#![feature(proc_macro_diagnostic)]
|
||||
#![allow(dead_code)]
|
||||
#![forbid(unsafe_code)]
|
||||
|
||||
mod init_comp;
|
||||
mod priority;
|
||||
|
||||
use init_comp::ComponentInitFunction;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::parse_macro_input;
|
||||
|
||||
pub(crate) const COMPONENT_FILE_NAME: &str = "Components.toml";
|
||||
|
||||
/// Register a function to be called when the component system is initialized. The function should not public.
|
||||
///
|
||||
/// Example:
|
||||
/// ```rust
|
||||
/// #[init_component]
|
||||
/// fn init() -> Result<(), component::ComponentInitError> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// It will expand to
|
||||
/// ```rust
|
||||
/// fn init() -> Result<(), component::ComponentInitError> {
|
||||
/// Ok(())
|
||||
/// }
|
||||
///
|
||||
/// const fn file() -> &'static str{
|
||||
/// file!()
|
||||
/// }
|
||||
///
|
||||
/// component::submit!(component::ComponentRegistry::new(&init,file()));
|
||||
/// ```
|
||||
/// The priority will calculate automatically
|
||||
///
|
||||
#[proc_macro_attribute]
|
||||
pub fn init_component(_: TokenStream, input: TokenStream) -> proc_macro::TokenStream {
|
||||
let function = parse_macro_input!(input as ComponentInitFunction);
|
||||
let function_name = &function.function_name;
|
||||
quote! {
|
||||
#function
|
||||
|
||||
const fn file() -> &'static str{
|
||||
file!()
|
||||
}
|
||||
|
||||
component::submit!(component::ComponentRegistry::new(&#function_name,file()));
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Automatically generate all component information required by the component system.
|
||||
///
|
||||
/// It mainly uses the output of the command `cargo metadata` to automatically generate information about all components, and also checks whether `Components.toml` contains all the components.
|
||||
///
|
||||
/// It is often used with `component::init_all`.
|
||||
///
|
||||
/// Example:
|
||||
///
|
||||
/// ```rust
|
||||
/// component::init_all(component::parse_metadata!());
|
||||
/// ```
|
||||
///
|
||||
#[proc_macro]
|
||||
pub fn parse_metadata(_: TokenStream) -> proc_macro::TokenStream {
|
||||
let out = priority::component_generate();
|
||||
let path = priority::get_component_toml_path();
|
||||
quote! {
|
||||
{
|
||||
include_str!(#path);
|
||||
extern crate alloc;
|
||||
alloc::vec![
|
||||
#(component::ComponentInfo::new #out),*
|
||||
]
|
||||
}
|
||||
}
|
||||
.into()
|
||||
}
|
224
kernel/libs/comp-sys/component-macro/src/priority.rs
Normal file
224
kernel/libs/comp-sys/component-macro/src/priority.rs
Normal file
@ -0,0 +1,224 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use std::{
|
||||
collections::HashMap, fs::File, io::Read, ops::Add, path::PathBuf, process::Command,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
use json::JsonValue;
|
||||
use proc_macro2::{Group, TokenStream};
|
||||
use quote::{ToTokens, TokenStreamExt};
|
||||
|
||||
use crate::COMPONENT_FILE_NAME;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ComponentInfo {
|
||||
name: String,
|
||||
/// The absolute path to the component
|
||||
path: String,
|
||||
priority: u16,
|
||||
}
|
||||
|
||||
impl ToTokens for ComponentInfo {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let token = TokenStream::from_str(
|
||||
format!("\"{}\",\"{}\",{}", self.name, self.path, self.priority).as_str(),
|
||||
)
|
||||
.unwrap();
|
||||
tokens.append(Group::new(proc_macro2::Delimiter::Parenthesis, token));
|
||||
}
|
||||
}
|
||||
|
||||
/// Automatic generate all the component information
|
||||
pub fn component_generate() -> Vec<ComponentInfo> {
|
||||
// extract components information
|
||||
let mut metadata = metadata();
|
||||
|
||||
let mut component_packages = vec![];
|
||||
let workspace_root = metadata["workspace_root"].as_str().unwrap();
|
||||
let workspace_root = String::from_str(workspace_root).unwrap().replace('\\', "/");
|
||||
|
||||
let comps_name = get_components_name(&workspace_root, &metadata["packages"]);
|
||||
for package in metadata["packages"].members_mut() {
|
||||
let name = package["name"].as_str().unwrap();
|
||||
if comps_name.contains(&name.to_string()) {
|
||||
// remove useless depend
|
||||
let mut depends = JsonValue::Array(Vec::new());
|
||||
loop {
|
||||
let depend = package["dependencies"].pop();
|
||||
if depend == JsonValue::Null {
|
||||
break;
|
||||
}
|
||||
if comps_name.contains(&depend["name"].as_str().unwrap().to_string()) {
|
||||
depends.push(depend).unwrap();
|
||||
}
|
||||
}
|
||||
package["dependencies"] = depends;
|
||||
component_packages.push(package);
|
||||
}
|
||||
}
|
||||
|
||||
// calculate priority
|
||||
let mut mapping: HashMap<String, u16> = HashMap::new();
|
||||
let mut component_packages_map: HashMap<String, &mut JsonValue> = HashMap::new();
|
||||
for i in component_packages.iter_mut() {
|
||||
component_packages_map.insert(i["name"].as_str().unwrap().to_string(), i);
|
||||
}
|
||||
|
||||
for (name, package) in component_packages_map.iter() {
|
||||
if mapping.contains_key(package["name"].as_str().unwrap()) {
|
||||
continue;
|
||||
}
|
||||
calculate_priority(&mut mapping, &component_packages_map, name.clone());
|
||||
}
|
||||
drop(component_packages_map);
|
||||
|
||||
// priority calculation complete
|
||||
let mut components_info = Vec::new();
|
||||
for package in component_packages {
|
||||
let temp_id = package["id"].as_str().unwrap();
|
||||
// extract path, let's take `(path+file:///path/to/comps/pci)` as an example
|
||||
let path = {
|
||||
// use the last element, `pci)`
|
||||
let mut paths: Vec<&str> = temp_id.split(&workspace_root).collect();
|
||||
// remove the last character
|
||||
let mut path1 = paths.pop().unwrap().to_string();
|
||||
path1.pop();
|
||||
if path1.starts_with('/') {
|
||||
path1.remove(0);
|
||||
}
|
||||
path1
|
||||
};
|
||||
let component_info = ComponentInfo {
|
||||
name: package["name"].as_str().unwrap().to_string(),
|
||||
path: PathBuf::from(&workspace_root)
|
||||
.join(path)
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
priority: *mapping
|
||||
.get(&package["name"].as_str().unwrap().to_string())
|
||||
.unwrap(),
|
||||
};
|
||||
components_info.push(component_info)
|
||||
}
|
||||
|
||||
components_info
|
||||
}
|
||||
|
||||
/// Get the path to the Components.toml file
|
||||
pub fn get_component_toml_path() -> TokenStream {
|
||||
let metadata = metadata();
|
||||
let workspace_root = metadata["workspace_root"].as_str().unwrap();
|
||||
let mut workspace_root = String::from_str(workspace_root)
|
||||
.unwrap()
|
||||
.replace('\\', "/")
|
||||
.add("/")
|
||||
.add(COMPONENT_FILE_NAME)
|
||||
.add("\"");
|
||||
workspace_root.insert(0, '\"');
|
||||
TokenStream::from_str(workspace_root.as_str()).unwrap()
|
||||
}
|
||||
|
||||
fn is_component(package: &JsonValue) -> bool {
|
||||
for depend in package["dependencies"].members() {
|
||||
if depend["name"].as_str().unwrap() == "component" {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Get all the components name, this function will also check if the Components.toml contain all the components.
|
||||
fn get_components_name(workspace_root: &str, packages: &JsonValue) -> Vec<String> {
|
||||
let file_components_name = read_component_file(workspace_root);
|
||||
let mut comps_name = Vec::new();
|
||||
for package in packages.members() {
|
||||
if is_component(package) {
|
||||
if !file_components_name.contains(&package["name"].as_str().unwrap().to_string()) {
|
||||
// if the package is in the workspace_root
|
||||
if package["id"].as_str().unwrap().contains(workspace_root) {
|
||||
panic!(
|
||||
"Package {} in the workspace that not written in the {} file",
|
||||
package["name"].as_str().unwrap(),
|
||||
COMPONENT_FILE_NAME
|
||||
);
|
||||
}
|
||||
}
|
||||
comps_name.push(package["name"].as_str().unwrap().to_string());
|
||||
}
|
||||
}
|
||||
comps_name
|
||||
}
|
||||
|
||||
/// read component file, return all the components name
|
||||
fn read_component_file(workspace_root: &str) -> Vec<String> {
|
||||
let component_toml: toml::Value = {
|
||||
let mut component_file_path = workspace_root.to_owned();
|
||||
component_file_path.push('/');
|
||||
component_file_path.push_str(COMPONENT_FILE_NAME);
|
||||
let mut file = File::open(component_file_path)
|
||||
.expect("Components.toml file not found, please check if the file exists");
|
||||
let mut str_val = String::new();
|
||||
file.read_to_string(&mut str_val).unwrap();
|
||||
toml::from_str(&str_val).unwrap()
|
||||
};
|
||||
for (name, value) in component_toml.as_table().unwrap() {
|
||||
if name.as_str() == "components" {
|
||||
return value
|
||||
.as_table()
|
||||
.unwrap()
|
||||
.values()
|
||||
.map(|value| {
|
||||
value
|
||||
.as_table()
|
||||
.unwrap()
|
||||
.values()
|
||||
.map(|str_val| str_val.as_str().unwrap().to_string())
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
panic!("Componets.toml file not valid")
|
||||
}
|
||||
|
||||
/// calculate the priority of one node
|
||||
fn calculate_priority(
|
||||
prioritys: &mut HashMap<String, u16>,
|
||||
package_mapping: &HashMap<String, &mut JsonValue>,
|
||||
node_name: String,
|
||||
) -> u16 {
|
||||
if prioritys.contains_key(&node_name) {
|
||||
return *prioritys.get(&node_name).unwrap();
|
||||
}
|
||||
|
||||
let package = &package_mapping[&node_name];
|
||||
let mut lowest_priority: u16 = 0;
|
||||
for depends in package["dependencies"].members() {
|
||||
lowest_priority = lowest_priority.max(
|
||||
calculate_priority(
|
||||
prioritys,
|
||||
package_mapping,
|
||||
depends["name"].as_str().unwrap().to_string(),
|
||||
) + 1,
|
||||
);
|
||||
}
|
||||
|
||||
prioritys.insert(node_name.to_string(), lowest_priority);
|
||||
lowest_priority
|
||||
}
|
||||
|
||||
fn metadata() -> json::JsonValue {
|
||||
let mut cmd = Command::new(env!("CARGO"));
|
||||
cmd.arg("metadata");
|
||||
cmd.arg("--format-version").arg("1");
|
||||
let output = cmd.output().unwrap();
|
||||
|
||||
if !output.status.success() {
|
||||
panic!("cannot get metadata");
|
||||
}
|
||||
|
||||
let output = String::from_utf8(output.stdout).unwrap();
|
||||
json::parse(&output).unwrap()
|
||||
}
|
Reference in New Issue
Block a user