⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/pkg/sdk/
generator.rs

1//! SDK Generator for VeridianOS Package Development
2//!
3//! Provides an SDK packaging framework for creating distributable development
4//! kits containing headers, libraries, tools, and documentation. The generator
5//! validates SDK manifests and produces serialized SDK packages suitable for
6//! distribution.
7//!
8//! TODO(user-space): Actual file collection requires a functional VFS layer.
9//! Currently the generator validates manifests and produces placeholder package
10//! bytes.
11
12// User-space SDK forward declarations -- see module doc TODO(user-space)
13
14#[cfg(feature = "alloc")]
15use alloc::{format, string::String, vec::Vec};
16
17use crate::error::{KernelError, KernelResult};
18
19// ============================================================================
20// SdkComponent
21// ============================================================================
22
23/// Identifies a category of files within an SDK package.
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum SdkComponent {
26    /// C/C++ header files for the VeridianOS API.
27    Headers,
28    /// Static libraries (.a) for linking.
29    StaticLibs,
30    /// Shared/dynamic libraries (.so) for runtime linking.
31    SharedLibs,
32    /// Build tools and utilities (compilers, linkers, etc.).
33    Tools,
34    /// API documentation and guides.
35    Documentation,
36    /// Example programs and code snippets.
37    Examples,
38}
39
40impl SdkComponent {
41    /// Return a short identifier for this component.
42    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    /// Return a human-readable description of this component.
54    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// ============================================================================
67// SdkManifest
68// ============================================================================
69
70/// Describes the contents and metadata of an SDK package.
71///
72/// The manifest must contain at least one component and one target architecture
73/// to be considered valid.
74#[cfg(feature = "alloc")]
75#[derive(Debug, Clone)]
76pub struct SdkManifest {
77    /// SDK version string (semver).
78    pub version: String,
79    /// Target architectures this SDK supports (e.g. "x86_64-veridian").
80    pub target_archs: Vec<String>,
81    /// Components included in this SDK package.
82    pub components: Vec<SdkComponent>,
83    /// Total size of all included files in bytes.
84    pub total_size: u64,
85}
86
87#[cfg(feature = "alloc")]
88impl SdkManifest {
89    /// Create a new empty SDK manifest with the given version.
90    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    /// Add a component to the manifest if not already present.
100    pub fn add_component(&mut self, component: SdkComponent) {
101        if !self.components.contains(&component) {
102            self.components.push(component);
103        }
104    }
105
106    /// Add a target architecture to the manifest if not already present.
107    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    /// Check whether the manifest includes the given component.
115    pub fn has_component(&self, component: SdkComponent) -> bool {
116        self.components.contains(&component)
117    }
118
119    /// Validate that the manifest has at least one component and one target.
120    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// ============================================================================
138// SdkPackageSpec
139// ============================================================================
140
141/// Full specification for building an SDK package, combining the manifest with
142/// the paths to include.
143#[cfg(feature = "alloc")]
144#[derive(Debug, Clone)]
145pub struct SdkPackageSpec {
146    /// The SDK manifest describing version, targets, and components.
147    pub manifest: SdkManifest,
148    /// Paths to header files to include.
149    pub header_paths: Vec<String>,
150    /// Paths to library files to include.
151    pub lib_paths: Vec<String>,
152    /// Paths to tool binaries to include.
153    pub tool_paths: Vec<String>,
154    /// Paths to documentation files to include.
155    pub doc_paths: Vec<String>,
156}
157
158#[cfg(feature = "alloc")]
159impl SdkPackageSpec {
160    /// Create a new package spec from a manifest with empty path lists.
161    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    /// Add a header file path.
172    pub fn add_header_path(&mut self, path: &str) {
173        self.header_paths.push(String::from(path));
174    }
175
176    /// Add a library file path.
177    pub fn add_lib_path(&mut self, path: &str) {
178        self.lib_paths.push(String::from(path));
179    }
180
181    /// Add a tool binary path.
182    pub fn add_tool_path(&mut self, path: &str) {
183        self.tool_paths.push(String::from(path));
184    }
185
186    /// Add a documentation file path.
187    pub fn add_doc_path(&mut self, path: &str) {
188        self.doc_paths.push(String::from(path));
189    }
190}
191
192// ============================================================================
193// SDK Generation
194// ============================================================================
195
196/// Generate an SDK package from the given specification.
197///
198/// Validates the manifest, then collects headers, libraries, tools, and
199/// documentation into a serialized package byte stream.
200///
201/// TODO(user-space): Actual file collection from `/usr/include/veridian/`,
202/// `/usr/lib/`, and the toolchain registry requires a functional VFS. This
203/// currently produces a placeholder package containing only the manifest
204/// metadata and generated pkg-config content.
205#[cfg(feature = "alloc")]
206pub fn generate_sdk(spec: &SdkPackageSpec) -> KernelResult<Vec<u8>> {
207    // Step 1: Validate the manifest
208    spec.manifest.validate()?;
209
210    let mut package_bytes: Vec<u8> = Vec::new();
211
212    // Step 2: Write a simple header identifying this as an SDK package
213    // Magic: "VSDK" (VeridianOS SDK)
214    package_bytes.extend_from_slice(b"VSDK");
215
216    // Version string length + data
217    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    // Number of components
222    package_bytes.extend_from_slice(&(spec.manifest.components.len() as u32).to_le_bytes());
223
224    // Number of target architectures
225    package_bytes.extend_from_slice(&(spec.manifest.target_archs.len() as u32).to_le_bytes());
226
227    // Step 3: Collect headers from /usr/include/veridian/
228    // TODO(user-space): Read actual header files via VFS
229    let header_count = spec.header_paths.len() as u32;
230    package_bytes.extend_from_slice(&header_count.to_le_bytes());
231
232    // Step 4: Collect libraries from /usr/lib/
233    // TODO(user-space): Read actual library files via VFS
234    let lib_count = spec.lib_paths.len() as u32;
235    package_bytes.extend_from_slice(&lib_count.to_le_bytes());
236
237    // Step 5: Package tools from toolchain registry
238    // TODO(user-space): Enumerate tools from registered toolchains
239    let tool_count = spec.tool_paths.len() as u32;
240    package_bytes.extend_from_slice(&tool_count.to_le_bytes());
241
242    // Step 6: Generate pkg-config content for each component
243    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/// Generate standard pkg-config `.pc` file content for an SDK component.
259///
260/// Produces output compatible with the `pkg-config` tool, defining the
261/// prefix, include, and library paths for a named component.
262#[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}