diff --git a/build-scripts/kernel_build/Cargo.toml b/build-scripts/kernel_build/Cargo.toml index cc9a0aeb..c5fc7234 100644 --- a/build-scripts/kernel_build/Cargo.toml +++ b/build-scripts/kernel_build/Cargo.toml @@ -8,4 +8,5 @@ edition = "2021" [dependencies] bindgen = "0.61.0" lazy_static = "1.4.0" -cc = { version = "1.0.83", features = ["parallel"] } \ No newline at end of file +cc = { version = "1.0.83", features = ["parallel"] } +toml = "0.8.6" \ No newline at end of file diff --git a/build-scripts/kernel_build/src/kconfig/mod.rs b/build-scripts/kernel_build/src/kconfig/mod.rs new file mode 100644 index 00000000..4f74d4e8 --- /dev/null +++ b/build-scripts/kernel_build/src/kconfig/mod.rs @@ -0,0 +1,195 @@ +use std::{fs, io::Write, path::PathBuf}; + +use toml::Value; + +use crate::utils::cargo_handler::CargoHandler; + +/// 内核编译配置的构建器 +pub struct KConfigBuilder; + +impl KConfigBuilder { + pub fn build() { + // 如果存在kernel.config,才去解析 + if fs::metadata("kernel.config").is_ok() { + // 获取kernel.config所包含的模块 + let modules = ConfigParser::parse_kernel_config(); + + // 扫描各模块下以及其包含模块的d.config,然后将所有d.config路径添加到r中 + let mut r = Vec::new(); + for m in modules.iter() { + if m.enable() { + Self::dfs(m, &mut r); + } + } + + // 扫描所有d.config以获取features + let features = ConfigParser::parse_d_configs(&r); + + // 添加feature + CargoHandler::emit_features(features.as_slice()); + + // 生成最终内核编译配置文件D.config + Self::make_compile_cfg(&features); + } + } + + /// 生成最终编译配置文件D.config + fn make_compile_cfg(features: &Vec) { + let mut cfg_content = String::new(); + for f in features.iter() { + if f.enable() { + cfg_content.push_str(&format!("{} = y\n", f.name())); + } else { + cfg_content.push_str(&format!("{} = n\n", f.name())); + } + } + + let mut file = fs::File::create("D.config").expect("Failed to create file: D.config"); + file.write_all(cfg_content.as_bytes()) + .expect("Failed to write D.config"); + } + + /// 递归找所有模块下的d.config文件路径 + /// + /// ## 参数 + /// + /// `module` - 当前模块 + /// `r` - 保存所有d.config文件路径 + /// ## 返回值 + /// + /// 无 + fn dfs(module: &Module, r: &mut Vec) { + println!("{}", module.name()); + + let path_str = module.path().as_path().to_str().unwrap().to_string(); + let d_config_str = format!("{}/d.config", path_str); + let d_config_path = PathBuf::from(&d_config_str); + let dcfg_content = + fs::read_to_string(&d_config_path).expect(&format!("Failed to read {}", d_config_str)); + let m_include = ConfigParser::include(&dcfg_content); + + for m in m_include.iter() { + if m.enable() { + Self::dfs(m, r); + } + } + + r.push(d_config_path); + } +} + +/// 内核编译配置文件解析器 +struct ConfigParser; + +impl ConfigParser { + /// 扫描kernel.config获取所包含的模块 + pub fn parse_kernel_config() -> Vec { + let cfg_content = + fs::read_to_string("kernel.config").expect("Failed to read kernel.config."); + + let r = Self::include(&cfg_content); + + return r; + } + + /// 扫描所有d.config以获取所有feature + pub fn parse_d_configs(d_configs: &Vec) -> Vec { + let mut r = Vec::new(); + for d_config in d_configs.iter() { + r.extend(Self::parse_d_config(d_config)); + } + return r; + } + + /// 扫描当前d.config文件获取feature + pub fn parse_d_config(d_config: &PathBuf) -> Vec { + let path_str = d_config.as_path().to_str().unwrap().to_string(); + let dcfg_content = + fs::read_to_string(d_config).expect(&format!("Failed to read {}", path_str)); + let dcfg_table: Value = + toml::from_str(&dcfg_content).expect(&format!("Failed to parse {}", path_str)); + + let mut r = Vec::new(); + if let Some(features) = dcfg_table.get("module").unwrap().get("features") { + for f in features.as_array().unwrap().iter() { + let name = f.get("name").unwrap().as_str().unwrap().to_string(); + let enable = f.get("enable").unwrap().as_str().unwrap().to_string() == "y"; + r.push(Feature::new(name, enable)); + } + } + return r; + } + + /// 获取所包含的模块 + /// + /// ## 参数 + /// + /// `cfg_content` -配置文件内容 + /// + /// ## 返回值 + /// + /// 包含的模块集合 + pub fn include(cfg_content: &str) -> Vec { + let cfg_table: Value = toml::from_str(&cfg_content).expect("Failed to parse kernel.config"); + let mut r = Vec::new(); + if let Some(include) = cfg_table.get("module").unwrap().get("include") { + for module in include.as_array().unwrap().iter() { + let name = module.get("name").unwrap().as_str().unwrap().to_string(); + let path = PathBuf::from(module.get("path").unwrap().as_str().unwrap()); + let enable = module.get("enable").unwrap().as_str().unwrap() == "y"; + r.push(Module::new(name, path, enable)); + } + } + return r; + } +} + +/// 模块 +struct Module { + /// 模块名 + name: String, + /// 模块文件路径 + path: PathBuf, + /// 是否启用 + enable: bool, +} + +impl Module { + pub fn new(name: String, path: PathBuf, enable: bool) -> Module { + Module { name, path, enable } + } + + pub fn name(&self) -> String { + self.name.clone() + } + + pub fn path(&self) -> PathBuf { + self.path.clone() + } + + pub fn enable(&self) -> bool { + self.enable.clone() + } +} + +/// feature +pub struct Feature { + /// feature标签名 + name: String, + /// 是否启用 + enable: bool, +} + +impl Feature { + pub fn new(name: String, enable: bool) -> Feature { + Feature { name, enable } + } + + pub fn name(&self) -> String { + self.name.clone() + } + + pub fn enable(&self) -> bool { + self.enable.clone() + } +} diff --git a/build-scripts/kernel_build/src/lib.rs b/build-scripts/kernel_build/src/lib.rs index 8a5bd922..2cc809f1 100644 --- a/build-scripts/kernel_build/src/lib.rs +++ b/build-scripts/kernel_build/src/lib.rs @@ -4,6 +4,7 @@ extern crate cc; mod bindgen; mod cfiles; +mod kconfig; mod utils; /// 运行构建 @@ -12,4 +13,5 @@ pub fn run() { crate::bindgen::generate_bindings(); crate::cfiles::CFilesBuilder::build(); + crate::kconfig::KConfigBuilder::build(); } diff --git a/build-scripts/kernel_build/src/utils/cargo_handler.rs b/build-scripts/kernel_build/src/utils/cargo_handler.rs index 876e34f3..7d196c2f 100644 --- a/build-scripts/kernel_build/src/utils/cargo_handler.rs +++ b/build-scripts/kernel_build/src/utils/cargo_handler.rs @@ -1,5 +1,7 @@ use std::{env, path::PathBuf}; +use crate::kconfig::Feature; + lazy_static! { static ref CARGO_HANDLER_DATA: CargoHandlerData = CargoHandlerData::new(); } @@ -43,6 +45,19 @@ impl CargoHandler { println!("cargo:rerun-if-changed={}", f.to_str().unwrap()); } } + + /// 添加features + /// + /// ## Parameters + /// + /// - `features` - The features to be set + pub fn emit_features(features: &[Feature]) { + for f in features.iter() { + if f.enable() { + println!("cargo:rustc-cfg=feature=\"{}\"", f.name()); + } + } + } } /// 目标架构 diff --git a/docs/index.rst b/docs/index.rst index 3f541229..583ac9e5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -30,6 +30,7 @@ kernel/ktest/index kernel/cpu_arch/index kernel/libs/index + kernel/configuration/index .. toctree:: diff --git a/docs/kernel/configuration/config.md b/docs/kernel/configuration/config.md new file mode 100644 index 00000000..b0ac7f02 --- /dev/null +++ b/docs/kernel/configuration/config.md @@ -0,0 +1,105 @@ +# 内核编译配置说明 + +## 原理 + +  在内核目录下,用kernel.config来设置内核编译配置信息,以类似解析toml文件的方式去解析该文件,然后接着去解析各模块下的d.config以获取feature的启用情况 + +## 示例 + +**kernel.config** + +```toml +[[module.include]] +name = "init" +path = "src/init/" +enable = "y" +description = "" + +[[module.include]] +name = "mm" +path = "src/mm/" +enable = "y" +description = "" +``` + + +- **[[module.include]]:** 将模块加入到include列表中 +- **name:** 模块名 +- **path:** 模块路径,存放着d.config +- **enable:** + - **y:** 启用,解析模块下的d.config + - **n:** 不启用,不解析 +- **description:** 模块的描述信息 + + +**src/mm/d.config** + +```toml +[module] +name = "mm" +description = "" + +[[module.include]] +name = "allocator" +path = "src/mm/allocator/" +enable = "y" +description = "" + +[[module.features]] +name = "mm_debug" +enable = "y" +description = "" +``` + + +- **\[module\]:** 当前模块 + - **name:** 当前模块名称 + - **description:** 模块的描述信息 +- **[[module.include]]:** 当前模块下所包含的模块,与kernel.config下的相同 +- **[[module.features]]:** 当前模块下的feature + - **name:** feature名 + - **enable:** 是否开启 + - **y:** 开启 + - **n:** 不开启 + - **description:** feature的描述信息 + + +*以下是其它模块下的d.config:* + +**src/mm/allocator/d.config** + +```toml +[module] +name = "allocator" +description = "" + +[[module.features]] +name = "allocator_debug" +enable = "y" +description = "" +``` + +**src/init/d.config** + +```toml +[module] +name = "init" +description = "" + +[[module.features]] +name = "init_debug" +enable = "y" +description = "" +``` + + +上面所有已开启模块的d.config中的feature,会最终生成到内核目录下的D.config文件,即D.config是最终内核编译的配置,如下: + + +**D.config** + +``` +init_debug = y +allocator_debug = y +mm_debug = y +``` \ No newline at end of file diff --git a/docs/kernel/configuration/index.rst b/docs/kernel/configuration/index.rst new file mode 100644 index 00000000..17a1243a --- /dev/null +++ b/docs/kernel/configuration/index.rst @@ -0,0 +1,9 @@ +内核编译配置 +==================================== + + +.. toctree:: + :maxdepth: 1 + :caption: 目录 + + config diff --git a/kernel/.gitignore b/kernel/.gitignore index da6eb9f5..923888a0 100644 --- a/kernel/.gitignore +++ b/kernel/.gitignore @@ -1,6 +1,7 @@ target/ src/kernel Cargo.lock +D.config # 将自动生成的C-Rust FFI加到gitignore src/include/bindings/bindings.rs diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 25bae856..8e88bb6e 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -64,4 +64,4 @@ debug = true # Controls whether the compiler passes `-g` # The release profile, used for `cargo build --release` [profile.release] -debug = false \ No newline at end of file +debug = false