Skip to main content

osal_rs_build/
lib.rs

1/***************************************************************************
2 *
3 * osal-rs
4 * Copyright (C) 2026 Antonio Salsi <passy.linux@zresa.it>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
18 *
19 ***************************************************************************/
20
21use std::env;
22use std::fs;
23use std::path::PathBuf;
24use std::process::Command;
25
26pub struct FreeRtosTypeGenerator {
27    out_dir: PathBuf,
28    config_path: Option<PathBuf>,
29}
30
31impl FreeRtosTypeGenerator {
32    pub fn new() -> Self {
33        let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set"));
34        Self { 
35            out_dir,
36            config_path: None,
37        }
38    }
39
40    /// Create a new generator with a custom FreeRTOSConfig.h path
41    pub fn with_config_path<P: Into<PathBuf>>(config_path: P) -> Self {
42        let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set"));
43        Self {
44            out_dir,
45            config_path: Some(config_path.into()),
46        }
47    }
48
49    /// Set the FreeRTOSConfig.h path
50    pub fn set_config_path<P: Into<PathBuf>>(&mut self, config_path: P) {
51        self.config_path = Some(config_path.into());
52    }
53
54    /// Query FreeRTOS type sizes and generate Rust type mappings
55    pub fn generate_types(&self) {
56        let (tick_size, ubase_size, base_size, base_signed, stack_size) = self.query_type_sizes();
57        
58        let tick_type = Self::size_to_type(tick_size, false);
59        let ubase_type = Self::size_to_type(ubase_size, false);
60        let base_type = Self::size_to_type(base_size, base_signed);
61        let stack_type = Self::size_to_type(stack_size, true);
62
63        
64        self.write_generated_types(tick_size, tick_type, ubase_size, ubase_type, base_size, base_type, stack_size, stack_type);
65        
66        println!("cargo:warning=Generated FreeRTOS types: TickType={}, UBaseType={}, BaseType={} StackType={}", 
67                 tick_type, ubase_type, base_type, stack_type);
68    }
69
70    /// Query FreeRTOS configuration values and generate Rust constants
71    // pub fn generate_config(&self) {
72    //     let (cpu_clock_hz, tick_rate_hz, max_priorities, minimal_stack_size, max_task_name_len) = self.query_config_values();
73        
74    //     self.write_generated_config(cpu_clock_hz, tick_rate_hz, max_priorities, minimal_stack_size, max_task_name_len);
75        
76    //     println!("cargo:warning=Generated FreeRTOS config: CPU={}Hz, Tick={}Hz, MaxPrio={}, MinStack={}, MaxTaskNameLen={}", 
77    //              cpu_clock_hz, tick_rate_hz, max_priorities, minimal_stack_size, max_task_name_len);
78    // }
79
80    /// Generate both types and config
81    pub fn generate_all(&self) {
82        self.generate_types();
83        // self.generate_config();
84    }
85
86    /// Query the sizes of FreeRTOS types
87    fn query_type_sizes(&self) -> (u16, u16, u16, bool, u16) {
88        // Create a small C program to query the type sizes
89        let query_program = r#"
90#include <stdio.h>
91#include <stdint.h>
92
93// We need to include FreeRTOS headers - path will be provided by the main build
94// For now, we'll use the compiled library approach
95// This is a placeholder - we'll use the already compiled C library
96
97int main() {
98    // Since we can't easily compile against FreeRTOS in the build script,
99    // we'll use a different approach: parse the compile_commands.json or
100    // use predefined types based on common configurations
101    
102    // Common FreeRTOS configurations:
103    // TickType_t is usually uint32_t (4 bytes) on 32-bit systems
104    // UBaseType_t is usually uint32_t (4 bytes) on 32-bit systems  
105    // BaseType_t is usually int32_t (4 bytes) on 32-bit systems
106    // StackType_t is usually long (4 bytes) on 32-bit systems
107    
108    printf("TICK_TYPE_SIZE=%d\n", 4);
109    printf("UBASE_TYPE_SIZE=%d\n", 4);
110    printf("BASE_TYPE_SIZE=%d\n", 4);
111    printf("BASE_TYPE_SIGNED=1\n");
112    printf("STACK_TYPE_SIZE=%d\n", 4);
113    
114    return 0;
115}
116"#;
117        
118        let query_c = self.out_dir.join("query_types.c");
119        fs::write(&query_c, query_program).expect("Failed to write query program");
120        
121        // Compile the query program
122        let query_exe = self.out_dir.join("query_types");
123        let compile_status = Command::new("gcc")
124            .arg(&query_c)
125            .arg("-o")
126            .arg(&query_exe)
127            .status();
128        
129        if compile_status.is_ok() && compile_status.unwrap().success() {
130            // Run the query program
131            let output = Command::new(&query_exe)
132                .output()
133                .expect("Failed to run query program");
134            
135            let stdout = String::from_utf8_lossy(&output.stdout);
136            let mut tick_size = 4u16;
137            let mut ubase_size = 4u16;
138            let mut base_size = 4u16;
139            let mut base_signed = true;
140            let mut stack_type = 4u16;
141            
142            for line in stdout.lines() {
143                if let Some(val) = line.strip_prefix("TICK_TYPE_SIZE=") {
144                    tick_size = val.parse().unwrap_or(4);
145                } else if let Some(val) = line.strip_prefix("UBASE_TYPE_SIZE=") {
146                    ubase_size = val.parse().unwrap_or(4);
147                } else if let Some(val) = line.strip_prefix("BASE_TYPE_SIZE=") {
148                    base_size = val.parse().unwrap_or(4);
149                } else if let Some(val) = line.strip_prefix("BASE_TYPE_SIGNED=") {
150                    base_signed = val.parse::<u8>().unwrap_or(1) == 1;
151                } else if let Some(val) = line.strip_prefix("STACK_TYPE_SIZE=") {
152                    stack_type = val.parse().unwrap_or(4);
153                } 
154            }
155            
156            (tick_size, ubase_size, base_size, base_signed, stack_type)
157        } else {
158            // Default values for 32-bit ARM Cortex-M (typical for Raspberry Pi Pico)
159            (4, 4, 4, true, 4)
160        }
161    }
162
163    /// Query FreeRTOS configuration values by parsing FreeRTOSConfig.h
164    // fn query_config_values(&self) -> (u64, u64, u64, u64, u64) {
165    //     // Use the provided config path or try to auto-detect it
166    //     let config_file = if let Some(ref path) = self.config_path {
167    //         path.clone()
168    //     } else {
169    //         // Try to get the workspace root
170    //         let workspace_root = env::var("CARGO_MANIFEST_DIR")
171    //             .map(|p| PathBuf::from(p).parent().unwrap().parent().unwrap().to_path_buf())
172    //             .ok();
173            
174    //         if let Some(root) = workspace_root {
175    //             root.join("inc/FreeRTOSConfig.h")
176    //         } else {
177    //             // Fallback: try current directory
178    //             PathBuf::from("FreeRTOSConfig.h")
179    //         }
180    //     };
181        
182    //     // Default values
183    //     let mut cpu_clock_hz = 150_000_000u64;
184    //     let mut tick_rate_hz = 1000u64;
185    //     let mut max_priorities = 32u64;
186    //     let mut minimal_stack_size = 512u64;
187    //     let mut max_task_name_len = 16u64;
188        
189    //     // Try to parse the config file
190    //     if config_file.exists() {
191    //         if let Ok(contents) = fs::read_to_string(&config_file) {
192    //             for line in contents.lines() {
193    //                 let line = line.trim();
194                    
195    //                 // Parse #define configCPU_CLOCK_HZ value
196    //                 if line.starts_with("#define") && line.contains("configCPU_CLOCK_HZ") {
197    //                     if let Some(value) = Self::extract_define_value(line) {
198    //                         cpu_clock_hz = value;
199    //                     }
200    //                 }
201    //                 // Parse #define configTICK_RATE_HZ value
202    //                 else if line.starts_with("#define") && line.contains("configTICK_RATE_HZ") {
203    //                     if let Some(value) = Self::extract_define_value(line) {
204    //                         tick_rate_hz = value;
205    //                     }
206    //                 }
207    //                 // Parse #define configMAX_PRIORITIES value
208    //                 else if line.starts_with("#define") && line.contains("configMAX_PRIORITIES") {
209    //                     if let Some(value) = Self::extract_define_value(line) {
210    //                         max_priorities = value;
211    //                     }
212    //                 }
213    //                 // Parse #define configMINIMAL_STACK_SIZE value
214    //                 else if line.starts_with("#define") && line.contains("configMINIMAL_STACK_SIZE") {
215    //                     if let Some(value) = Self::extract_define_value(line) {
216    //                         minimal_stack_size = value;
217    //                     }
218    //                 }
219    //                 // Parse #define configMAX_TASK_NAME_LEN value
220    //                 else if line.starts_with("#define") && line.contains("configMAX_TASK_NAME_LEN") {
221    //                     if let Some(value) = Self::extract_define_value(line) {
222    //                         max_task_name_len = value;
223    //                     }
224    //                 }
225    //             }
226    //             println!("cargo:warning=Successfully parsed FreeRTOS config from {}", config_file.display());
227    //         } else {
228    //             println!("cargo:warning=Failed to read FreeRTOS config file, using defaults");
229    //         }
230    //     } else {
231    //         println!("cargo:warning=FreeRTOS config file not found at {}, using defaults", config_file.display());
232    //     }
233        
234    //     (cpu_clock_hz, tick_rate_hz, max_priorities, minimal_stack_size, max_task_name_len)
235    // }
236    
237    /// Extract numeric value from a #define line
238    // fn extract_define_value(line: &str) -> Option<u64> {
239    //     // Split by whitespace and get the value part (after the macro name)
240    //     let parts: Vec<&str> = line.split_whitespace().collect();
241    //     if parts.len() >= 3 {
242    //         let value_str = parts[2];
243    //         // Remove common suffixes and parentheses
244    //         let cleaned = value_str
245    //             .trim_end_matches('U')
246    //             .trim_end_matches('L')
247    //             .trim_matches('(')
248    //             .trim_matches(')');
249            
250    //         // Try to parse as decimal or hex
251    //         if let Ok(val) = cleaned.parse::<u64>() {
252    //             return Some(val);
253    //         }
254    //         // Try hex format (0x...)
255    //         if cleaned.starts_with("0x") || cleaned.starts_with("0X") {
256    //             if let Ok(val) = u64::from_str_radix(&cleaned[2..], 16) {
257    //                 return Some(val);
258    //             }
259    //         }
260    //     }
261    //     None
262    // }
263
264    /// Convert a size to the corresponding Rust type
265    fn size_to_type(size: u16, signed: bool) -> &'static str {
266        match (size, signed) {
267            (1, false) => "u8",
268            (1, true) => "i8",
269            (2, false) => "u16",
270            (2, true) => "i16",
271            (4, false) => "u32",
272            (4, true) => "i32",
273            (8, false) => "u64",
274            (8, true) => "i64",
275            // Default to u32 for unknown sizes
276            _ => if signed { "i32" } else { "u32" },
277        }
278    }
279
280    /// Write the generated types to a file
281    fn write_generated_types(
282        &self,
283        tick_size: u16,
284        tick_type: &str,
285        ubase_size: u16,
286        ubase_type: &str,
287        base_size: u16,
288        base_type: &str,
289        stack_size: u16,
290        stack_type: &str,
291    ) {
292        let generated_code = format!(r#"
293// Auto-generated by build.rs - DO NOT EDIT MANUALLY
294// This file contains FreeRTOS type mappings based on the actual type sizes
295
296// FreeRTOS type mappings (auto-detected)
297// TickType_t: {} bytes -> {}
298// UBaseType_t: {} bytes -> {}
299// BaseType_t: {} bytes -> {}
300// StackType_t: {} bytes -> {}
301
302pub type TickType = {};
303pub type UBaseType = {};
304pub type BaseType = {};
305pub type StackType = {};
306
307"#,
308            tick_size, tick_type,
309            ubase_size, ubase_type,
310            base_size, base_type,
311            stack_size, stack_type,
312            tick_type,
313            ubase_type,
314            base_type,
315            stack_type
316        );
317        
318        let types_rs = self.out_dir.join("types_generated.rs");
319        fs::write(&types_rs, generated_code).expect("Failed to write generated types");
320    }
321
322//     /// Write the generated config constants to a file
323//     fn write_generated_config(
324//         &self,
325//         cpu_clock_hz: u64,
326//         tick_rate_hz: u64,
327//         max_priorities: u64,
328//         minimal_stack_size: u64,
329//         max_task_name_len: u64,
330//     ) {
331//         let generated_code = format!(r#"
332// // Auto-generated by build.rs - DO NOT EDIT MANUALLY
333// // This file contains FreeRTOS configuration constants
334
335// /// FreeRTOS CPU clock frequency in Hz
336// pub const CPU_CLOCK_HZ: u64 = {};
337
338// /// FreeRTOS tick rate in Hz
339// pub const TICK_RATE_HZ: u64 = {};
340
341// /// Maximum number of FreeRTOS priorities
342// pub const MAX_PRIORITIES: u64 = {};
343
344// /// Minimal stack size for FreeRTOS tasks
345// pub const MINIMAL_STACK_SIZE: u64 = {};
346
347// /// Maximum task name length
348// pub const MAX_TASK_NAME_LEN: u64 = {};
349// "#,
350//             cpu_clock_hz,
351//             tick_rate_hz,
352//             max_priorities,
353//             minimal_stack_size,
354//             max_task_name_len
355//         );
356        
357//         let config_rs = self.out_dir.join("config_generated.rs");
358//         fs::write(&config_rs, generated_code).expect("Failed to write generated config");
359//     }
360}
361
362impl Default for FreeRtosTypeGenerator {
363    fn default() -> Self {
364        Self::new()
365    }
366}