Browse Source

Kconfig (#432)

* 内核编译配置

* 将kernel.config的解析代码搬入crate

* 将设置feature函数放入CargoHandler中
Jomo 1 year ago
parent
commit
e4600f7f7d

+ 2 - 1
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"] }
+cc = { version = "1.0.83", features = ["parallel"] }
+toml = "0.8.6"

+ 195 - 0
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<Feature>) {
+        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<PathBuf>) {
+        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<Module> {
+        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<PathBuf>) -> Vec<Feature> {
+        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<Feature> {
+        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<Module> {
+        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()
+    }
+}

+ 2 - 0
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();
 }

+ 15 - 0
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());
+            }
+        }
+    }
 }
 
 /// 目标架构

+ 1 - 0
docs/index.rst

@@ -30,6 +30,7 @@
    kernel/ktest/index
    kernel/cpu_arch/index
    kernel/libs/index
+   kernel/configuration/index
 
 
 .. toctree::

+ 105 - 0
docs/kernel/configuration/config.md

@@ -0,0 +1,105 @@
+# 内核编译配置说明
+
+## 原理
+
+&emsp;&emsp;在内核目录下,用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
+```

+ 9 - 0
docs/kernel/configuration/index.rst

@@ -0,0 +1,9 @@
+内核编译配置
+====================================
+    
+   
+.. toctree::
+   :maxdepth: 1
+   :caption: 目录
+
+   config

+ 1 - 0
kernel/.gitignore

@@ -1,6 +1,7 @@
 target/
 src/kernel
 Cargo.lock
+D.config
 
 # 将自动生成的C-Rust FFI加到gitignore
 src/include/bindings/bindings.rs

+ 1 - 1
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
+debug = false