// SPDX-License-Identifier: MIT #[macro_use] extern crate derive_builder; use std::env; use std::path::{Path, PathBuf}; use std::process::Command; use quick_xml::Reader; use quick_xml::events::Event; #[derive(Builder, Default, Debug)] #[builder(try_setter, setter(into), build_fn(validate = "Self::validate"))] pub struct Resource { src_dir: PathBuf, definition_file: PathBuf, } impl Resource { pub fn builder() -> ResourceBuilder { ResourceBuilder::default() } pub fn compile(&self) { let out_dir = env::var_os("OUT_DIR").unwrap(); let src_dir = self.src_dir.as_path(); let definition_basename = self.definition_file.as_path().file_name() .unwrap_or_else(|| panic!("Bad definition path: {}", &self.definition_file.as_path().to_string_lossy())) .to_string_lossy() .to_string(); let mut xml_reader = Reader::from_file(&self.definition_file) .unwrap_or_else(|error| { panic!("Unable to open {}: {}", &self.definition_file.as_path().to_string_lossy(), error ); }); let mut buf = vec![]; let mut inside_file_tag = false; loop { match xml_reader.read_event(&mut buf) { Ok(Event::Start(ref e)) => { if let b"file" = e.name() { inside_file_tag = true; } }, Ok(Event::Text(e)) => { if !inside_file_tag { continue; } inside_file_tag = false; let relative_path = e.unescape_and_decode(&xml_reader).unwrap(); let mut file_path = src_dir.to_path_buf(); file_path.push(relative_path); println!("cargo:rerun-if-changed={}", &file_path.to_string_lossy()); }, Err(e) => panic!("Error at position {}: {:?}", xml_reader.buffer_position(), e), Ok(Event::Eof) => break, _ => (), } buf.clear(); } let cfilename = match definition_basename.strip_suffix(".xml") { Some(basename) => format!("{}.c", basename), None => panic!("Bad extension for definition file, expected .xml: {}", definition_basename), }; let cfile_path = Path::new(&out_dir).join(&cfilename); let dest_path = cfile_path.to_string_lossy(); Command::new("glib-compile-resources") .arg("--generate-source") .arg(self.definition_file.as_path()) .arg(format!("--sourcedir={}", &src_dir.to_string_lossy()).as_str()) .arg(format!("--target={}", &dest_path)) .output() .expect("unable to generate resources source file"); let cflags = Command::new("pkg-config") .arg("--cflags") .arg("--static") .arg("--libs") .arg("gio-2.0") .output() .map_or_else( |e| { panic!("Could not get cflags from pkg-config for gio-2.0: {}", e) }, |output| { std::str::from_utf8(&output.stdout).unwrap().to_string() }); let old_cflags = env::var_os("CFLAGS"); env::set_var("CFLAGS", cflags); cc::Build::new() .file(&*dest_path) .compile(cfilename.strip_suffix(".c").unwrap()); match old_cflags { Some(old_value) => env::set_var("CFLAGS", old_value), None => env::remove_var("CFLAGS"), } println!("cargo:rerun-if-changed={}", &self.definition_file.as_path().to_string_lossy()); println!("cargo:rerun-if-changed=build.rs"); } } impl ResourceBuilder { fn validate(&self) -> Result<(), String> { if let Some(ref src_dir) = self.src_dir { if src_dir.as_path().is_dir() { Ok(()) } else { Err(format!("{} is not a directory", &src_dir.as_os_str().to_string_lossy()) ) } } else { Err("src_dir not provided".to_string()) } } }