#![allow(clippy::uninlined_format_args)]
use std::io::Write;
use std::io::BufRead;
use quick_xml::{
Reader, Writer,
events::{BytesStart, Event},
};
use translating::PO_DIR;
use translating::error::TransError;
macro_rules! script_warning {
($($tokens: tt)*) => {
println!("cargo::warning={}", format!($($tokens)*))
}
}
macro_rules! script_error {
($($tokens: tt)*) => {
println!("cargo::error={}", format!($($tokens)*))
}
}
fn main() {
compile_resources(
&["data"],
"data/resources/resources.gresource.xml",
"sysd-manager.gresource",
);
#[cfg(feature = "flatpak")]
compile_resources(
&["sysd-manager-proxy/data"],
"sysd-manager-proxy/data/resources.gresource.xml",
"sysd-manager-proxy.gresource",
);
compile_schema();
if let Err(error) = generate_notes() {
script_error!("Generate release notes error : {:?}", error);
}
if let Err(error) = generate_mo() {
script_error!("Generate release mo files error : {:?}", error);
}
}
pub fn check_linguas() -> Result<(), TransError> {
let set1 = translating::lingas_from_files()?;
let set2 = translating::lingas_from_lingua_file()?;
let mut vec: Vec<_> = set1.iter().filter(move |s| !set2.contains(*s)).collect();
vec.sort();
if !vec.is_empty() {
script_warning!("Those languages {:?} not in LINGUAS file!", vec);
}
Ok(())
}
fn generate_mo() -> Result<(), TransError> {
println!("generate_mo");
println!("cargo::rerun-if-changed={PO_DIR}");
check_linguas()?;
translating::generate_mo()?;
Ok(())
}
use std::{
env, fs,
path::{Path, PathBuf},
process::Command,
};
pub fn compile_resources<P: AsRef<Path>>(source_dirs: &[P], gresource: &str, target: &str) {
let out_dir = env::var("OUT_DIR").unwrap();
let out_dir = Path::new(&out_dir);
let mut command = Command::new("glib-compile-resources");
for source_dir in source_dirs {
command.arg("--sourcedir").arg(source_dir.as_ref());
}
let output = command
.arg("--target")
.arg(out_dir.join(target))
.arg(gresource)
.output()
.unwrap();
let path = env::current_dir().expect("env::current_dir() FAIL");
println!("The current directory is {}", path.display());
println!("CMD Output: {:#?}", output);
assert!(
output.status.success(),
"glib-compile-resources failed with exit status {} and stderr:\n{}",
output.status,
String::from_utf8_lossy(&output.stderr)
);
println!("cargo::rerun-if-changed={gresource}");
let mut command = Command::new("glib-compile-resources");
for source_dir in source_dirs {
command.arg("--sourcedir").arg(source_dir.as_ref());
}
let output = command
.arg("--generate-dependencies")
.arg(gresource)
.output()
.unwrap()
.stdout;
let output = String::from_utf8(output).unwrap();
for dep in output.split_whitespace() {
println!("cargo::rerun-if-changed={dep}");
}
}
fn compile_schema() {
const GLIB_SCHEMAS_DIR: &str = ".local/share/glib-2.0/schemas/";
const GLIB_SCHEMAS_FILE: &str = "data/schemas/io.github.plrigaux.sysd-manager.gschema.xml";
let path = Path::new(GLIB_SCHEMAS_FILE);
println!("Path {:?}", path);
let schema_file = match fs::canonicalize(path) {
Ok(s) => s,
Err(e) => {
println!("Error: {:?}", e);
return;
}
};
let home_dir = env::var("HOME").unwrap();
let out_dir = PathBuf::from(home_dir).join(GLIB_SCHEMAS_DIR);
println!("print out_dir {:?}", out_dir);
println!("cargo::rerun-if-changed={GLIB_SCHEMAS_FILE}");
let mut command = Command::new("install");
let output = command
.arg("-v")
.arg("-D")
.arg(schema_file)
.arg("-t")
.arg(&out_dir)
.output()
.unwrap();
println!("Install Schema");
println!(
"Install Schema stdout {}",
String::from_utf8_lossy(&output.stdout)
);
println!(
"Install Schema stderr {}",
String::from_utf8_lossy(&output.stderr)
);
println!("Install Schema status {}", output.status);
const GLIB_COMPILE_SCHEMAS: &str = "glib-compile-schemas";
let mut command = Command::new(GLIB_COMPILE_SCHEMAS);
let output = command.arg("--strict").arg(&out_dir).output().unwrap();
if output.status.success() {
println!("Compile Schema Succeed on {:?}", out_dir);
} else {
script_error!(
"Compile Schema with {GLIB_COMPILE_SCHEMAS} Failed (status {}), directory {:?}",
output.status,
out_dir
);
script_warning!(
"Compile Schema stdout {}",
String::from_utf8_lossy(&output.stdout)
);
script_error!(
"Compile Schema stderr {}",
String::from_utf8_lossy(&output.stderr)
);
}
}
#[derive(Debug)]
pub enum ScriptError {
FtmError(std::fmt::Error),
IoError(std::io::Error),
XmlError(quick_xml::Error),
}
impl From<std::io::Error> for ScriptError {
fn from(error: std::io::Error) -> Self {
ScriptError::IoError(error)
}
}
impl From<quick_xml::Error> for ScriptError {
fn from(error: quick_xml::Error) -> Self {
ScriptError::XmlError(error)
}
}
fn generate_notes() -> Result<(), ScriptError> {
const METAINFO: &str = "data/metainfo/io.github.plrigaux.sysd-manager.metainfo.xml";
println!("cargo::rerun-if-changed={METAINFO}");
let release_notes = match get_release_notes(METAINFO) {
Ok(list) => list,
Err(error) => {
script_error!("Error parsing metainfo: {:?}", error);
return Ok(());
}
};
generate_release_notes_rs(&release_notes)?;
Ok(())
}
fn generate_release_notes_rs(release_notes: &[Release]) -> Result<(), ScriptError> {
let (version, description) = if let Some(first) = release_notes.first() {
(
format!("Some(\"{}\")", first.version),
format!("Some(\"{}\")", first.description),
)
} else {
("None".to_owned(), "None".to_owned())
};
let Some(out_dir) = env::var_os("OUT_DIR") else {
script_error!("No OUT_DIR");
return Ok(());
};
let dest_path = Path::new(&out_dir).join("release_notes.rs");
println!("dest_path {:?}", dest_path);
let mut w = Vec::new();
writeln!(
&mut w,
"pub const RELEASE_NOTES_VERSION : Option<&str> = {};",
version
)?;
writeln!(
&mut w,
"pub const RELEASE_NOTES : Option<&str> = {};",
description
)?;
fs::write(&dest_path, w)?;
Ok(())
}
#[derive(Debug, Default, Clone)]
struct Release {
version: String,
date: String,
description: String,
}
fn get_release_notes(metainfo: &str) -> Result<Vec<Release>, quick_xml::Error> {
let mut reader = Reader::from_file(metainfo)?;
reader.config_mut().trim_text(true);
let mut buf = Vec::new();
let mut junk_buf: Vec<u8> = Vec::new();
let mut release = Release::default();
let mut in_release = false;
let mut release_notes = Vec::new();
loop {
match reader.read_event_into(&mut buf) {
Err(e) => panic!("Error at position {}: {:?}", reader.error_position(), e),
Ok(Event::Start(e)) => match e.name().as_ref() {
b"release" => {
in_release = true;
release = Release::default();
for attr in e.attributes() {
let attr = attr.unwrap();
match attr.key.local_name().as_ref() {
b"version" => {
release.version = String::from_utf8_lossy(&attr.value).to_string()
}
b"date" => {
release.date = String::from_utf8_lossy(&attr.value).to_string()
}
_ => (),
}
}
}
b"description" => {
if !in_release {
continue;
}
let content =
read_to_end_into_buffer_inner(&mut reader, e, &mut junk_buf).unwrap();
release.description = content;
println!("Release: {:?}", release);
}
_ => (),
},
Ok(Event::End(e)) if e.name().as_ref() == b"release" => {
release_notes.push(release.clone());
in_release = false
}
Ok(Event::Eof) => break,
_ => (),
}
}
Ok(release_notes)
}
fn read_to_end_into_buffer_inner<R: BufRead>(
reader: &mut Reader<R>,
start_tag: BytesStart,
junk_buf: &mut Vec<u8>,
) -> Result<String, quick_xml::Error> {
let mut depth = 0;
let mut output_buf: Vec<u8> = Vec::new();
let mut w = Writer::new(&mut output_buf);
let tag_name = start_tag.name();
loop {
junk_buf.clear();
let event = reader.read_event_into(junk_buf)?;
match event {
Event::Start(ref e) => {
if e.name() == tag_name {
depth += 1
}
w.write_event(event.borrow())?;
}
Event::End(ref e) => {
if e.name() == tag_name {
if depth == 0 {
break;
}
depth -= 1;
} else {
w.write_event(event.borrow())?;
}
}
Event::Text(ref _e) => {
w.write_event(event)?;
}
Event::Eof => {
panic!("oh no")
}
_ => {}
}
}
let s = String::from_utf8_lossy(&output_buf);
Ok(s.to_string())
}