use std::env; use std::fs; use std::path::{Path, PathBuf}; fn main() { println!("cargo:rerun-if-env-changed=FORCE_BINDGEN"); let generator = BindingsGenerator::new(); generator.generate_cmsis_os_bindings(); generator.generate_stm32_hal_bindings(); generator.generate_stm32_hal_statics(); } struct BindingsGenerator { clib_dir: PathBuf, workspace_dir: PathBuf, out_dir: PathBuf, gcc_include_dir: PathBuf, force_bindgen: bool, } impl BindingsGenerator { pub fn new() -> Self { let out_dir = env::var("OUT_DIR").unwrap(); let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); let workspace_dir = Path::new(&crate_dir).parent().unwrap(); let clib_dir = workspace_dir.parent().unwrap().join("target_f1"); assert!(clib_dir.is_dir()); let force_bindgen: bool = std::env::var_os("FORCE_BINDGEN").is_some(); let gcc_include_dir = detect_gcc_inclide_dir(); Self { clib_dir: clib_dir.to_path_buf(), workspace_dir: workspace_dir.to_path_buf(), out_dir: Path::new(&out_dir).to_path_buf(), gcc_include_dir, force_bindgen, } } fn builder(&self) -> bindgen::Builder { let stm32_sdk_includes = [ "Inc", "Drivers/STM32L4xx_HAL_Driver/Inc", "Drivers/STM32L4xx_HAL_Driver/Inc/Legacy", "Middlewares/Third_Party/FreeRTOS/Source/include", "Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS", "Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM4F", "Drivers/CMSIS/Device/ST/STM32L4xx/Include", "Drivers/CMSIS/Include", "Middlewares/ST/STM32_USB_Device_Library/Core/Inc", "Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc", ]; let stm32_sdk_includes = stm32_sdk_includes .iter() .map(|stm32_include| format!("{}/{}", self.clib_dir.to_string_lossy(), stm32_include)); let flipper_core_bindings = self.workspace_dir.join("flipper-core").join("bindings"); let includes = [ // This are bindings generated by cbindgen nearby &flipper_core_bindings.to_string_lossy(), &self.gcc_include_dir.to_string_lossy(), ]; #[rustfmt::skip] return bindgen::Builder::default() .use_core() .ctypes_prefix("self") .blacklist_type("__uint8_t") .blacklist_type("__uint32_t") .blacklist_type("c_int") .blacklist_type("__int32_t") // TODO there's no .no_debug method, to disable only for specific type .derive_debug(false) .clang_arg("-DUSE_HAL_DRIVER") .clang_arg("-DSTM32L476xx") .clang_arg("-DBUTON_INVERT=false") .clang_arg("-DDEBUG_UART=huart1") .clang_args( (includes.iter().map(|x| From::from(x as &str)).chain(stm32_sdk_includes)) .map(|include| format!("-I{}", include)) ) .clang_arg("--target=thumbv7em-none-eabihf") .clang_arg("--verbose") //.clang_arg("-nostdinc") ; } pub fn generate_cmsis_os_bindings(&self) { let result_path = self.out_dir.join("cmsis_os_bindings.rs"); let should_build = !result_path.is_file() || self.force_bindgen; if !should_build { return; } println!("cargo:warning=writing cmsis os bindings"); #[rustfmt::skip] let builder = self.builder() .whitelist_recursively(false) .header(format!("{}/Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS/cmsis_os.h", self.clib_dir.to_string_lossy())) .whitelist_type("osStatus") .rustified_enum("osStatus") .whitelist_type("osEvent") .whitelist_type("os_pthread") .whitelist_type("osThreadId") .opaque_type("osThreadId") .whitelist_type("os_thread_def") .whitelist_type("osThreadDef_t") .whitelist_type("osStaticThreadDef_t") .opaque_type("osStaticThreadDef_t") .whitelist_type("osPriority") .rustified_enum("osPriority") .whitelist_function("osThreadCreate") .whitelist_function("osThreadGetId") .whitelist_function("osThreadTerminate") .whitelist_function("osThreadYield") .whitelist_type("osMutexId") .opaque_type("osMutexId") .whitelist_type("os_mutex_def") .whitelist_type("osMutexDef_t") .whitelist_type("osStaticMutexDef_t") .opaque_type("osStaticMutexDef_t") .whitelist_function("osMutexCreate") .whitelist_function("osMutexWait") .whitelist_function("osMutexRelease") .whitelist_type("osSemaphoreId") .opaque_type("osSemaphoreId") .whitelist_type("os_semaphore_def") .whitelist_type("osSemaphoreDef_t") .whitelist_type("osStaticSemaphoreDef_t") .opaque_type("osStaticSemaphoreDef_t") .whitelist_function("osSemaphoreCreate") .whitelist_function("osSemaphoreWait") .whitelist_function("osSemaphoreRelease") .whitelist_type("osMessageQId") .opaque_type("osMessageQId") .whitelist_type("osMailQId") .opaque_type("osMailQId") .whitelist_type("os_mailQ_def") .whitelist_type("osMailQDef_t") .whitelist_type("os_mailQ_cb") .whitelist_function("osMailCreate") .whitelist_function("osMailAlloc") .whitelist_function("osMailFree") .whitelist_function("osMailPut") .whitelist_function("osMailGet") .whitelist_var("osWaitForever") .whitelist_function("osDelay") // TODO for some reason, bindgen wont generate osKernelSysTickFrequency .whitelist_var("osKernelSysTickFrequency") .whitelist_function("osKernelSysTick") ; let bindings = builder.generate().expect("Unable to generate bindings"); bindings .write_to_file(result_path) .expect("Couldn't write bindings!"); } pub fn generate_stm32_hal_bindings(&self) { let result_path = self.out_dir.join("stm32_hal_bindings.rs"); let should_build = !result_path.is_file() || self.force_bindgen; if !should_build { return; } println!("cargo:warning=writing STM32 HAL bindings"); #[rustfmt::skip] let builder = self.builder() .whitelist_recursively(false) .header(format!("{}/Drivers/STM32L4xx_HAL_Driver/Inc/stm32l4xx_hal.h", self.clib_dir.to_string_lossy())) .whitelist_type("HAL_StatusTypeDef") .rustified_enum("HAL_StatusTypeDef") .whitelist_type("GPIO_TypeDef") .opaque_type("GPIO_TypeDef") .whitelist_type("GPIO_PinState") .rustified_enum("GPIO_PinState") .whitelist_function("HAL_GPIO_WritePin") .whitelist_function("HAL_GPIO_TogglePin") .whitelist_function("HAL_GPIO_ReadPin") .whitelist_type("UART_HandleTypeDef") .opaque_type("UART_HandleTypeDef") .whitelist_function("HAL_UART_Transmit_IT") .whitelist_type("SPI_HandleTypeDef") .opaque_type("SPI_HandleTypeDef") .whitelist_function("HAL_SPI_Transmit_IT") .whitelist_function("HAL_SPI_Receive_IT") .whitelist_function("HAL_SPI_TransmitReceive_IT") .whitelist_type("ADC_HandleTypeDef") .opaque_type("ADC_HandleTypeDef") .whitelist_type("COMP_HandleTypeDef") .opaque_type("COMP_HandleTypeDef") .whitelist_type("DAC_HandleTypeDef") .opaque_type("DAC_HandleTypeDef") .whitelist_type("TIM_HandleTypeDef") .opaque_type("TIM_HandleTypeDef") ; let bindings = builder.generate().expect("Unable to generate bindings"); bindings .write_to_file(result_path) .expect("Couldn't write bindings!"); } pub fn generate_stm32_hal_statics(&self) { let result_path = self.out_dir.join("stm32_hal_statics.rs"); let should_build = !result_path.is_file() || self.force_bindgen; if !should_build { return; } println!("cargo:warning=writing STM32 HAL bindings"); #[rustfmt::skip] let builder = self.builder() .whitelist_recursively(false) .header(format!("{}/Src/main.c", self.clib_dir.to_string_lossy())) .whitelist_var(".*_Pin") .whitelist_var(".*_Port") .whitelist_var("HAL_.*_Pin") .whitelist_var("HAL_.*_Port") .whitelist_var("hadc[0-9]+") .whitelist_var("hcomp[0-9]+") .whitelist_var("hdac[0-9]+") .whitelist_var("hspi[0-9]+") .whitelist_var("htim[0-9]+") .whitelist_var("huart[0-9]+") ; let bindings = builder.generate().expect("Unable to generate bindings"); bindings .write_to_file(result_path) .expect("Couldn't write bindings!"); } } fn detect_gcc_inclide_dir() -> PathBuf { let base_path = Path::new("/usr/lib/gcc/arm-none-eabi"); if !base_path.is_dir() { panic!("Can't find arm-none-eabi-gcc lib directory"); } let entries = fs::read_dir(base_path).expect("Can't read arm-none-eabi-gcc lib directory"); for entry in entries { let entry = entry.expect("Can't read dir entry"); let path = entry.path(); if path.is_dir() { let include_dir = path.join("include"); if include_dir.is_dir() { return include_dir.to_path_buf(); } } } panic!("Can't find arm-none-eabi-gcc include directory"); }