veridian_kernel/pkg/sdk/
generator.rs1#[cfg(feature = "alloc")]
15use alloc::{format, string::String, vec::Vec};
16
17use crate::error::{KernelError, KernelResult};
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum SdkComponent {
26 Headers,
28 StaticLibs,
30 SharedLibs,
32 Tools,
34 Documentation,
36 Examples,
38}
39
40impl SdkComponent {
41 pub fn as_str(&self) -> &'static str {
43 match self {
44 Self::Headers => "headers",
45 Self::StaticLibs => "static-libs",
46 Self::SharedLibs => "shared-libs",
47 Self::Tools => "tools",
48 Self::Documentation => "documentation",
49 Self::Examples => "examples",
50 }
51 }
52
53 pub fn description(&self) -> &'static str {
55 match self {
56 Self::Headers => "C/C++ header files for the VeridianOS API",
57 Self::StaticLibs => "Static libraries for compile-time linking",
58 Self::SharedLibs => "Shared libraries for runtime linking",
59 Self::Tools => "Build tools and utilities",
60 Self::Documentation => "API documentation and developer guides",
61 Self::Examples => "Example programs and code snippets",
62 }
63 }
64}
65
66#[cfg(feature = "alloc")]
75#[derive(Debug, Clone)]
76pub struct SdkManifest {
77 pub version: String,
79 pub target_archs: Vec<String>,
81 pub components: Vec<SdkComponent>,
83 pub total_size: u64,
85}
86
87#[cfg(feature = "alloc")]
88impl SdkManifest {
89 pub fn new(version: &str) -> Self {
91 Self {
92 version: String::from(version),
93 target_archs: Vec::new(),
94 components: Vec::new(),
95 total_size: 0,
96 }
97 }
98
99 pub fn add_component(&mut self, component: SdkComponent) {
101 if !self.components.contains(&component) {
102 self.components.push(component);
103 }
104 }
105
106 pub fn add_target_arch(&mut self, arch: &str) {
108 let arch_string = String::from(arch);
109 if !self.target_archs.contains(&arch_string) {
110 self.target_archs.push(arch_string);
111 }
112 }
113
114 pub fn has_component(&self, component: SdkComponent) -> bool {
116 self.components.contains(&component)
117 }
118
119 pub fn validate(&self) -> KernelResult<()> {
121 if self.components.is_empty() {
122 return Err(KernelError::InvalidArgument {
123 name: "components",
124 value: "manifest must contain at least one component",
125 });
126 }
127 if self.target_archs.is_empty() {
128 return Err(KernelError::InvalidArgument {
129 name: "target_archs",
130 value: "manifest must target at least one architecture",
131 });
132 }
133 Ok(())
134 }
135}
136
137#[cfg(feature = "alloc")]
144#[derive(Debug, Clone)]
145pub struct SdkPackageSpec {
146 pub manifest: SdkManifest,
148 pub header_paths: Vec<String>,
150 pub lib_paths: Vec<String>,
152 pub tool_paths: Vec<String>,
154 pub doc_paths: Vec<String>,
156}
157
158#[cfg(feature = "alloc")]
159impl SdkPackageSpec {
160 pub fn new(manifest: SdkManifest) -> Self {
162 Self {
163 manifest,
164 header_paths: Vec::new(),
165 lib_paths: Vec::new(),
166 tool_paths: Vec::new(),
167 doc_paths: Vec::new(),
168 }
169 }
170
171 pub fn add_header_path(&mut self, path: &str) {
173 self.header_paths.push(String::from(path));
174 }
175
176 pub fn add_lib_path(&mut self, path: &str) {
178 self.lib_paths.push(String::from(path));
179 }
180
181 pub fn add_tool_path(&mut self, path: &str) {
183 self.tool_paths.push(String::from(path));
184 }
185
186 pub fn add_doc_path(&mut self, path: &str) {
188 self.doc_paths.push(String::from(path));
189 }
190}
191
192#[cfg(feature = "alloc")]
206pub fn generate_sdk(spec: &SdkPackageSpec) -> KernelResult<Vec<u8>> {
207 spec.manifest.validate()?;
209
210 let mut package_bytes: Vec<u8> = Vec::new();
211
212 package_bytes.extend_from_slice(b"VSDK");
215
216 let version_bytes = spec.manifest.version.as_bytes();
218 package_bytes.extend_from_slice(&(version_bytes.len() as u32).to_le_bytes());
219 package_bytes.extend_from_slice(version_bytes);
220
221 package_bytes.extend_from_slice(&(spec.manifest.components.len() as u32).to_le_bytes());
223
224 package_bytes.extend_from_slice(&(spec.manifest.target_archs.len() as u32).to_le_bytes());
226
227 let header_count = spec.header_paths.len() as u32;
230 package_bytes.extend_from_slice(&header_count.to_le_bytes());
231
232 let lib_count = spec.lib_paths.len() as u32;
235 package_bytes.extend_from_slice(&lib_count.to_le_bytes());
236
237 let tool_count = spec.tool_paths.len() as u32;
240 package_bytes.extend_from_slice(&tool_count.to_le_bytes());
241
242 for component in &spec.manifest.components {
244 let pc_content = generate_pkg_config_content(
245 component.as_str(),
246 &spec.manifest.version,
247 component.description(),
248 component.as_str(),
249 );
250 let pc_bytes = pc_content.as_bytes();
251 package_bytes.extend_from_slice(&(pc_bytes.len() as u32).to_le_bytes());
252 package_bytes.extend_from_slice(pc_bytes);
253 }
254
255 Ok(package_bytes)
256}
257
258#[cfg(feature = "alloc")]
263pub fn generate_pkg_config_content(
264 name: &str,
265 version: &str,
266 description: &str,
267 lib_name: &str,
268) -> String {
269 let sysroot = super::get_sysroot();
270
271 let mut output = String::new();
272 output.push_str(&format!("prefix={}\n", sysroot));
273 output.push_str("exec_prefix=${prefix}\n");
274 output.push_str("libdir=${exec_prefix}/lib\n");
275 output.push_str("includedir=${prefix}/include\n");
276 output.push('\n');
277 output.push_str(&format!("Name: {}\n", name));
278 output.push_str(&format!("Version: {}\n", version));
279 output.push_str(&format!("Description: {}\n", description));
280 output.push_str(&format!("Cflags: -I{}/include/veridian\n", sysroot));
281 output.push_str(&format!("Libs: -L{}/lib -l{}\n", sysroot, lib_name));
282
283 output
284}