1 // Copyright 2019 The Chromium OS Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 extern crate bindgen;
5 
6 use bindgen::builder;
7 
8 use std::fs::{self, File};
9 use std::io::Write;
10 use std::path::Path;
11 use std::str;
12 
copy_headers(src_dir: &Path, dst_dir: &Path) -> Result<(), String>13 fn copy_headers(src_dir: &Path, dst_dir: &Path) -> Result<(), String> {
14     if dst_dir.is_file() {
15         fs::remove_file(&dst_dir).or_else(|e| {
16             Err(format!(
17                 "failed to remove existing file at {:?}: {}",
18                 dst_dir, e
19             ))
20         })?;
21     }
22 
23     if !dst_dir.is_dir() {
24         fs::create_dir(&dst_dir).or_else(|e| {
25             Err(format!(
26                 "failed to create destination directory: {:?}: {}",
27                 dst_dir, e
28             ))
29         })?;
30     }
31 
32     let header_files = vec![
33         "cras_audio_format.h",
34         "cras_iodev_info.h",
35         "cras_messages.h",
36         "cras_shm.h",
37         "cras_types.h",
38         "cras_util.h",
39         "packet_status_logger.h",
40     ];
41 
42     for header in &header_files {
43         let src = src_dir.join(&header);
44         let dst = dst_dir.join(&header);
45         fs::copy(&src, &dst).or_else(|e| {
46             Err(format!(
47                 "failed to copy header file {:?} to {:?}: {}",
48                 src, dst, e
49             ))
50         })?;
51     }
52     Ok(())
53 }
54 
55 /*
56  * If we use both `packed` and `align(4)` for a struct, bindgen will generate
57  * it as an opaque struct.
58  *
59  * `cras_server_state` is created from C with `packed` and `aligned(4)` and
60  * shared through a shared memory area.
61  *
62  * Structs with `packed` and `align(4)` have the same memory layout as those
63  * with `packed` except for some extra alignment bytes at the end.
64  *
65  * Therefore, using only `packed` for `cras_server_state` from Rust side is safe.
66  *
67  * This function modifies `cras_server_state` from
68  * `__attribute__ ((packed, aligned(4)))` to `__attribute__ ((packed))`
69  */
modify_server_state_attributes(dir: &Path) -> Result<(), String>70 fn modify_server_state_attributes(dir: &Path) -> Result<(), String> {
71     let cras_types_path = dir.join("cras_types.h");
72     let bytes = fs::read(&cras_types_path)
73         .or_else(|e| Err(format!("failed to read {:?}: {}", cras_types_path, e)))?;
74 
75     let old = str::from_utf8(&bytes).or_else(|e| {
76         Err(format!(
77             "failed to parse {:?} as utf8: {}",
78             cras_types_path, e
79         ))
80     })?;
81 
82     let new = old.replacen(
83         "struct __attribute__((packed, aligned(4))) cras_server_state {",
84         "struct __attribute__((packed)) cras_server_state {",
85         1,
86     );
87 
88     if new.len() >= old.len() {
89         return Err("failed to remove 'aligned(4)' from cras_server_state".to_string());
90     }
91 
92     fs::write(&cras_types_path, new).or_else(|e| {
93         Err(format!(
94             "failed to write updated contents to {:?}: {}",
95             cras_types_path, e
96         ))
97     })?;
98 
99     Ok(())
100 }
101 
gen() -> String102 fn gen() -> String {
103     let name = "cras_gen";
104     let bindings = builder()
105         .header("c_headers/cras_messages.h")
106         .header("c_headers/cras_types.h")
107         .header("c_headers/cras_audio_format.h")
108         .header("c_headers/cras_shm.h")
109         .whitelist_type("cras_.*")
110         .whitelist_var("cras_.*")
111         .whitelist_type("CRAS_.*")
112         .whitelist_var("CRAS_.*")
113         .whitelist_type("audio_message")
114         .whitelist_var("MAX_DEBUG_.*")
115         .rustified_enum("CRAS_.*")
116         .rustified_enum("_snd_pcm_.*")
117         .bitfield_enum("CRAS_STREAM_EFFECT")
118         .generate()
119         .expect(format!("Unable to generate {} code", name).as_str());
120 
121     bindings.to_string()
122 }
123 
write_output(output_path: &Path, output: String) -> std::io::Result<()>124 fn write_output(output_path: &Path, output: String) -> std::io::Result<()> {
125     let header = b"// Copyright 2019 The Chromium OS Authors. All rights reserved.
126 // Use of this source code is governed by a BSD-style license that can be
127 // found in the LICENSE file.
128 
129 /*
130  * generated from files in cras/src/common in adhd:
131  * cras_audio_format.h
132  * cras_iodev_info.h
133  * cras_messages.h
134  * cras_shm.h
135  * cras_types.h
136  * cras_util.h
137  * packet_status_logger.h
138  */
139 
140 #![allow(clippy::unreadable_literal)]
141 #![allow(clippy::cognitive_complexity)]
142 ";
143 
144     let mut output_file = File::create(output_path)?;
145     output_file.write_all(header)?;
146     output_file.write_all(output.as_bytes())?;
147     Ok(())
148 }
149 
main()150 fn main() {
151     let src_header_dir = Path::new("../../../src/common");
152     let dst_header_dir = Path::new("./c_headers");
153 
154     copy_headers(src_header_dir, dst_header_dir).expect("failed to copy C headers");
155     modify_server_state_attributes(dst_header_dir)
156         .expect("failed to modify cras_server_state's attributes");
157     let generated_code = gen();
158     write_output(Path::new("lib_gen.rs"), generated_code).expect("failed to write generated code");
159 }
160