// Copyright 2019 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. extern crate bindgen; use bindgen::builder; use std::fs::{self, File}; use std::io::Write; use std::path::Path; use std::str; fn copy_headers(src_dir: &Path, dst_dir: &Path) -> Result<(), String> { if dst_dir.is_file() { fs::remove_file(&dst_dir).or_else(|e| { Err(format!( "failed to remove existing file at {:?}: {}", dst_dir, e )) })?; } if !dst_dir.is_dir() { fs::create_dir(&dst_dir).or_else(|e| { Err(format!( "failed to create destination directory: {:?}: {}", dst_dir, e )) })?; } let header_files = vec![ "cras_audio_format.h", "cras_iodev_info.h", "cras_messages.h", "cras_shm.h", "cras_types.h", "cras_util.h", "packet_status_logger.h", ]; for header in &header_files { let src = src_dir.join(&header); let dst = dst_dir.join(&header); fs::copy(&src, &dst).or_else(|e| { Err(format!( "failed to copy header file {:?} to {:?}: {}", src, dst, e )) })?; } Ok(()) } /* * If we use both `packed` and `align(4)` for a struct, bindgen will generate * it as an opaque struct. * * `cras_server_state` is created from C with `packed` and `aligned(4)` and * shared through a shared memory area. * * Structs with `packed` and `align(4)` have the same memory layout as those * with `packed` except for some extra alignment bytes at the end. * * Therefore, using only `packed` for `cras_server_state` from Rust side is safe. * * This function modifies `cras_server_state` from * `__attribute__ ((packed, aligned(4)))` to `__attribute__ ((packed))` */ fn modify_server_state_attributes(dir: &Path) -> Result<(), String> { let cras_types_path = dir.join("cras_types.h"); let bytes = fs::read(&cras_types_path) .or_else(|e| Err(format!("failed to read {:?}: {}", cras_types_path, e)))?; let old = str::from_utf8(&bytes).or_else(|e| { Err(format!( "failed to parse {:?} as utf8: {}", cras_types_path, e )) })?; let new = old.replacen( "struct __attribute__((packed, aligned(4))) cras_server_state {", "struct __attribute__((packed)) cras_server_state {", 1, ); if new.len() >= old.len() { return Err("failed to remove 'aligned(4)' from cras_server_state".to_string()); } fs::write(&cras_types_path, new).or_else(|e| { Err(format!( "failed to write updated contents to {:?}: {}", cras_types_path, e )) })?; Ok(()) } fn gen() -> String { let name = "cras_gen"; let bindings = builder() .header("c_headers/cras_messages.h") .header("c_headers/cras_types.h") .header("c_headers/cras_audio_format.h") .header("c_headers/cras_shm.h") .whitelist_type("cras_.*") .whitelist_var("cras_.*") .whitelist_type("CRAS_.*") .whitelist_var("CRAS_.*") .whitelist_type("audio_message") .whitelist_var("MAX_DEBUG_.*") .rustified_enum("CRAS_.*") .rustified_enum("_snd_pcm_.*") .bitfield_enum("CRAS_STREAM_EFFECT") .generate() .expect(format!("Unable to generate {} code", name).as_str()); bindings.to_string() } fn write_output(output_path: &Path, output: String) -> std::io::Result<()> { let header = b"// Copyright 2019 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /* * generated from files in cras/src/common in adhd: * cras_audio_format.h * cras_iodev_info.h * cras_messages.h * cras_shm.h * cras_types.h * cras_util.h * packet_status_logger.h */ #![allow(clippy::unreadable_literal)] #![allow(clippy::cognitive_complexity)] "; let mut output_file = File::create(output_path)?; output_file.write_all(header)?; output_file.write_all(output.as_bytes())?; Ok(()) } fn main() { let src_header_dir = Path::new("../../../src/common"); let dst_header_dir = Path::new("./c_headers"); copy_headers(src_header_dir, dst_header_dir).expect("failed to copy C headers"); modify_server_state_attributes(dst_header_dir) .expect("failed to modify cras_server_state's attributes"); let generated_code = gen(); write_output(Path::new("lib_gen.rs"), generated_code).expect("failed to write generated code"); }