| // 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. |
| |
| // Provides tools for generating a Rust library with D-Bus bindings. The generated bindings are |
| // included in the published crate since the source XML files are only available from the original |
| // path or the ebuild. See the README.md for usage. |
| |
| use std::env; |
| use std::error; |
| use std::fmt::{self, Debug, Display}; |
| use std::fs::{canonicalize, create_dir_all, remove_dir_all, File}; |
| use std::io::Write; |
| use std::path::{Path, PathBuf}; |
| use std::process::Command; |
| |
| const DEFAULT_BINDINGS_DIR: &str = "src/bindings"; |
| |
| pub enum Error { |
| CleanupFailed, |
| CommandFailed, |
| FilePathNotUTF8, |
| SourceDoesntExist(PathBuf), |
| WrappedError(String), |
| } |
| |
| impl Display for Error { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| use self::Error::*; |
| |
| match self { |
| CleanupFailed => write!(f, "Failed to cleanup old bindings."), |
| CommandFailed => write!(f, "Command Failed."), |
| FilePathNotUTF8 => write!(f, "File path is not valid UTF-8."), |
| SourceDoesntExist(file) => write!(f, "Source file doesn't exist: {:?}", file), |
| WrappedError(err) => write!(f, "{}", err), |
| } |
| } |
| } |
| |
| impl Debug for Error { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| // Forward to Display. |
| write!(f, "{}", self) |
| } |
| } |
| |
| impl<T: error::Error> From<T> for Error { |
| fn from(err: T) -> Self { |
| Error::WrappedError(format!("{:?}", err)) |
| } |
| } |
| |
| // sub_modules: &[(<module name>, <relative path to source xml>), ...] |
| pub fn generate_module(source_dir: &Path, sub_modules: &[(&str, &str)]) -> Result<(), Error> { |
| let out_dir = PathBuf::from(env::var("OUT_DIR")?); |
| let bindings_dir = PathBuf::from(DEFAULT_BINDINGS_DIR); |
| |
| let mut errors: String = String::new(); |
| |
| // Clear the bindings unless this is a release build. |
| if bindings_dir.exists() && Ok("release".to_string()) != env::var("PROFILE") { |
| remove_dir_all(&bindings_dir).or(Err(Error::CleanupFailed))?; |
| }; |
| create_dir_all(&bindings_dir)?; |
| |
| // Write header of include file. |
| let mut mod_out = File::create(out_dir.join("include_modules.rs")).unwrap(); |
| writeln!( |
| mod_out, |
| r#"// Do not edit. This is generated by system_api/build.rs. |
| "# |
| )?; |
| |
| for (module, source) in sub_modules { |
| // Generate bindings if they don't already exist. |
| let destination = bindings_dir.join(format!("{}.rs", module)); |
| if !destination.exists() { |
| if let Err(err) = generate_bindings(&source_dir.join(source), &destination) { |
| errors.push_str(&format!( |
| "Failed to generate {:?} from {:?}: {}\n", |
| module, source, err |
| )); |
| // Canonicalize will fail if the destination doesn't exist. |
| continue; |
| } |
| } |
| |
| // Include generated bindings. |
| writeln!( |
| mod_out, |
| "#[path = \"{file}\"]\npub mod {module};\npub use {module}::*;", |
| file = canonicalize(destination)? |
| .to_str() |
| .ok_or(Error::FilePathNotUTF8)?, |
| module = module |
| )?; |
| } |
| |
| if errors.is_empty() { |
| Ok(()) |
| } else { |
| Err(Error::WrappedError(errors)) |
| } |
| } |
| |
| fn generate_bindings(source: &Path, destination: &Path) -> Result<(), Error> { |
| if !source.exists() { |
| return Err(Error::SourceDoesntExist(source.to_path_buf())); |
| } |
| let status = Command::new("dbus-codegen-rust") |
| .arg("-s") // Connect to system bus. |
| .stdin(File::open(source)?) |
| .stdout(File::create(destination)?) |
| .spawn()? |
| .wait()?; |
| if !status.success() { |
| return Err(Error::CommandFailed); |
| } |
| Ok(()) |
| } |