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