From 324f6f3d96aa353c3d38462b1c23c3bd7525cc9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sat, 3 Mar 2018 00:00:00 +0000 Subject: [PATCH 01/10] Cross validate Rust ABI with C ABI. --- src/analysis/types.rs | 4 +- src/codegen/sys/mod.rs | 2 + src/codegen/sys/tests.rs | 249 +++++++++++++++++++++++++++++++++++++++ src/library.rs | 2 + src/parser.rs | 45 ++++--- 5 files changed, 284 insertions(+), 18 deletions(-) create mode 100644 src/codegen/sys/tests.rs diff --git a/src/analysis/types.rs b/src/analysis/types.rs index 7636c02c6..bbc140879 100644 --- a/src/analysis/types.rs +++ b/src/analysis/types.rs @@ -133,11 +133,11 @@ impl IsIncomplete for Type { Type::Class(ref klass) => klass.is_incomplete(lib), Type::Record(ref record) => record.is_incomplete(lib), Type::Union(ref union) => union.is_incomplete(lib), + Type::Interface(..) => true, Type::Custom(..) | Type::Enumeration(..) | Type::Bitfield(..) | Type::Function(..) | - Type::Interface(..) | Type::Array(..) | Type::CArray(..) | Type::PtrArray(..) | @@ -189,12 +189,12 @@ impl IsExternal for Type { Type::Class(ref klass) => klass.is_external(lib), Type::Record(ref record) => record.is_external(lib), Type::Union(ref union) => union.is_external(lib), + Type::Interface(..) => true, Type::Custom(..) | Type::Fundamental(..) | Type::Enumeration(..) | Type::Bitfield(..) | Type::Function(..) | - Type::Interface(..) | Type::Array(..) | Type::CArray(..) | Type::FixedArray(..) | diff --git a/src/codegen/sys/mod.rs b/src/codegen/sys/mod.rs index 7f256222e..b6c55e395 100644 --- a/src/codegen/sys/mod.rs +++ b/src/codegen/sys/mod.rs @@ -6,10 +6,12 @@ mod fields; mod functions; mod lib_; mod statics; +mod tests; pub mod ffi_type; pub fn generate(env: &Env) { lib_::generate(env); build::generate(env); cargo_toml::generate(env); + tests::generate(env); } diff --git a/src/codegen/sys/tests.rs b/src/codegen/sys/tests.rs new file mode 100644 index 000000000..59e30c04c --- /dev/null +++ b/src/codegen/sys/tests.rs @@ -0,0 +1,249 @@ +use std::io::prelude::*; +use std::io; +use std::path::Path; + +use analysis::types::IsIncomplete; +use env::Env; +use file_saver::save_to_file; +use library::{Type, MAIN_NAMESPACE}; +use nameutil::crate_name; + +pub fn generate(env :&Env) { + let tests = env.config.target_path.join("tests"); + let abi_c = tests.join("abi.c"); + let abi_rs = tests.join("abi.rs"); + + let ns = env.library.namespace(MAIN_NAMESPACE); + let ctypes = ns.types + .iter() + .filter_map(|t| t.as_ref()) + .filter(|t| !t.is_incomplete(&env.library)) + .filter_map(|t| match *t { + Type::Alias(_) | + Type::Class(_) | + Type::Record(_) | + Type::Union(_) | + Type::Enumeration(_) | + Type::Bitfield(_) | + Type::Interface(_) + => { + let full_name = format!("{}.{}", &ns.name, t.get_name()); + if !env.type_status_sys(&full_name).ignored() { + t.get_glib_name() + } else { + None + } + }, + _ => None, + }) + .filter(|s| !is_name_made_up(s)) + .collect::>(); + + if ctypes.is_empty() { + return; + } + + save_to_file(&abi_c, env.config.make_backup, |w| { + generate_abi_c(env, &abi_c, w) + }); + save_to_file(&abi_rs, env.config.make_backup, |w| { + generate_abi_rs(env, &abi_rs, w, &ctypes) + }); +} + +/// Checks if type name is unlikely to correspond to a real C type name. +fn is_name_made_up(name: &str) -> bool { + // Unnamed types are assigned name during parsing, those names contain an underscore. + name.contains('_') +} + +fn generate_abi_c(env: &Env, path: &Path, w: &mut Write) -> io::Result<()> { + info!("Generating file {:?}", path); + + writeln!(w, "/* For %z support in printf when using MinGW. */")?; + writeln!(w, "#define _POSIX_C_SOURCE 200809L")?; + writeln!(w, "#include ")?; + writeln!(w, "#include ")?; + + let ns = env.library.namespace(MAIN_NAMESPACE); + for include in ns.c_includes.iter() { + writeln!(w, "#include <{}>", include)?; + } + + writeln!(w, "{}", r##" +int main() { + printf("%zu\n%zu\n", sizeof(ABI_TEST_TYPE), alignof(ABI_TEST_TYPE)); +}"##) + +} + +fn generate_abi_rs(env: &Env, path: &Path, w: &mut Write, ctypes: &[&str]) -> io::Result<()> { + info!("Generating file {:?}", path); + + let name = format!("{}_sys", crate_name(&env.config.library_name)); + writeln!(w, "extern crate {};", &name)?; + writeln!(w, "{}", r##" +use std::collections::BTreeMap; +use std::env; +use std::error::Error; +use std::mem::{align_of, size_of}; +use std::process::Command; +use std::str;"##)?; + writeln!(w, "use {}::*;", &name)?; + writeln!(w, "{}", r##" + +#[derive(Clone, Debug)] +struct Compiler { + pub args: Vec, +} + +impl Compiler { + pub fn new() -> Result> { + let mut args = get_var("CC", "cc")?; + args.extend(get_var("CFLAGS", "")?); + args.extend(get_var("CPPFLAGS", "")?); + Ok(Compiler { args }) + } + + pub fn define<'a, V: Into>>(&mut self, var: &str, val: V) { + let arg = match val.into() { + None => format!("-D{}", var), + Some(val) => format!("-D{}={}", var, val), + }; + self.args.push(arg); + } + + pub fn to_command(&self) -> Command { + let mut cmd = Command::new(&self.args[0]); + cmd.args(&self.args[1..]); + cmd + } +} + +fn get_var(name: &str, default: &str) -> Result, Box> { + match env::var(name) { + Ok(value) => Ok(shell_split(&value)), + Err(env::VarError::NotPresent) => Ok(shell_split(default)), + Err(err) => Err(format!("{} {}", name, err).into()), + } +} + +fn pkg_config_cflags(package: &str) -> Result, Box> { + let mut cmd = Command::new("pkg-config"); + cmd.arg("--cflags"); + cmd.arg(package); + let out = cmd.output()?; + if !out.status.success() { + return Err(format!("command {:?} returned {}", + &cmd, out.status).into()); + } + let stdout = str::from_utf8(&out.stdout)?; + Ok(shell_split(stdout)) +} + +fn shell_split(s: &str) -> Vec { + // FIXME: Use shell word splitting rules. + s.split_whitespace() + .map(|s| s.to_owned()) + .collect() +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +struct ABI { + size: usize, + alignment: usize, +} + +impl ABI { + pub fn from_type() -> ABI { + ABI { + size: size_of::(), + alignment: align_of::(), + } + } +} + +#[test] +fn test_cross_validate_abi_with_c() { + // Configure compiler instance."##)?; + + let ns = env.library.namespace(MAIN_NAMESPACE); + let package_name = ns.package_name.as_ref() + .expect("Missing package name"); + + writeln!(w, "\tlet package_name = \"{}\";", package_name)?; + writeln!(w, "{}", r##" let mut cc = Compiler::new() + .expect("compiler from environment"); + let cflags = pkg_config_cflags(package_name) + .expect("cflags from pkg-config"); + cc.args.extend(cflags); + cc.args.push("-Wno-deprecated-declarations".to_owned()); + + // Sanity check that compilation works. + assert_eq!(ABI {size: 1, alignment: 1}, + get_c_abi(&cc, "char").expect("C ABI for char")); + + let rust = get_rust_abi(); + let mut failed = false; + for (name, rust_abi) in &rust { + match get_c_abi(&cc, name) { + Err(e) => { + failed = true; + eprintln!("{}", e); + continue; + }, + Ok(ref c_abi) => { + if rust_abi != c_abi { + failed = true; + eprintln!("ABI mismatch for {}\nRust: {:?}\nC: {:?}", + name, rust_abi, c_abi); + } + } + }; + } + assert!(!failed); +} + +fn get_c_abi(cc: &Compiler, name: &str) -> Result> { + let mut cc = cc.clone(); + cc.define("ABI_TEST_TYPE", name); + cc.args.push("tests/abi.c".to_owned()); + cc.args.push("-oabi".to_owned()); + + let mut cc_cmd = cc.to_command(); + let status = cc_cmd.spawn()?.wait()?; + if !status.success() { + return Err(format!("compilation command {:?} failed, {}", + &cc_cmd, status).into()); + } + + let mut abi_cmd = Command::new("./abi"); + let output = abi_cmd.output()?; + if !output.status.success() { + return Err(format!("command {:?} failed, {}", + &abi_cmd, output.status).into()); + } + + let stdout = str::from_utf8(&output.stdout)?; + let mut words = stdout.split_whitespace(); + let size = words.next().unwrap().parse().unwrap(); + let alignment = words.next().unwrap().parse().unwrap(); + Ok(ABI {size, alignment}) +} + +fn get_rust_abi() -> BTreeMap { + let mut abi = BTreeMap::new();"##)?; + + for ctype in ctypes { + writeln!(w, r##" abi.insert("{ctype}".to_owned(), ABI::from_type::<{ctype}>());"##, + ctype=ctype)?; + } + + writeln!(w, "{}", r##" abi +} + +"##) + +} + + diff --git a/src/library.rs b/src/library.rs index f5f4e01d7..a5e1ea7da 100644 --- a/src/library.rs +++ b/src/library.rs @@ -669,6 +669,8 @@ pub struct Namespace { pub shared_library: Vec, pub identifier_prefixes: Vec, pub symbol_prefixes: Vec, + /// C headers, relative to include directories provided by pkg-config --cflags. + pub c_includes: Vec, } impl Namespace { diff --git a/src/parser.rs b/src/parser.rs index d8cb0c658..24de30676 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,3 +1,4 @@ +use std::mem::replace; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -25,13 +26,18 @@ impl Library { fn read_repository(&mut self, dir: &Path, parser: &mut XmlParser) -> Result<()> { let mut package = None; + let mut includes = Vec::new(); parser.elements(|parser, elem| match elem.name() { "include" => { - if let (Some(lib), Some(ver)) = (elem.attr("name"), elem.attr("version")) { - if self.find_namespace(lib).is_none() { - let lib = format!("{}-{}", lib, ver); - self.read_file(dir, &lib)?; - } + match (elem.attr("name"), elem.attr("version")) { + (Some(name), Some(ver)) => { + if self.find_namespace(name).is_none() { + let lib = format!("{}-{}", name, ver); + self.read_file(dir, &lib)?; + } + }, + (Some(name), None) => includes.push(name.to_owned()), + _ => {}, } Ok(()) } @@ -43,7 +49,8 @@ impl Library { } Ok(()) } - "namespace" => self.read_namespace(parser, elem, package.take()), + "namespace" => self.read_namespace(parser, elem, package.take(), + replace(&mut includes, Vec::new())), _ => Err(parser.unexpected_element(elem)), })?; Ok(()) @@ -54,19 +61,25 @@ impl Library { parser: &mut XmlParser, elem: &Element, package: Option, + c_includes: Vec, ) -> Result<()> { let ns_name = elem.attr_required("name")?; let ns_id = self.add_namespace(ns_name); - self.namespace_mut(ns_id).package_name = package; - if let Some(s) = elem.attr("shared-library") { - self.namespace_mut(ns_id).shared_library = s.split(',').map(String::from).collect(); - } - if let Some(s) = elem.attr("identifier-prefixes") { - self.namespace_mut(ns_id).identifier_prefixes = - s.split(',').map(String::from).collect(); - } - if let Some(s) = elem.attr("symbol-prefixes") { - self.namespace_mut(ns_id).symbol_prefixes = s.split(',').map(String::from).collect(); + + { + let ns = self.namespace_mut(ns_id); + ns.package_name = package; + ns.c_includes = c_includes; + if let Some(s) = elem.attr("shared-library") { + ns.shared_library = s.split(',').map(String::from).collect(); + } + if let Some(s) = elem.attr("identifier-prefixes") { + ns.identifier_prefixes = + s.split(',').map(String::from).collect(); + } + if let Some(s) = elem.attr("symbol-prefixes") { + ns.symbol_prefixes = s.split(',').map(String::from).collect(); + } } trace!( From 46d604de35406f4974360e2fd7d19d9e46d760d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Fri, 9 Mar 2018 00:00:00 +0000 Subject: [PATCH 02/10] Process CC, CFLAGS and CPPFLAGS using shell parsing rules. Default build rules in GNU Make are constructed based on those environment variables. The rule is eventually evaluated through a shell. To retain compatibility with this behaviour build tools generally use shell parsing rules as well. And so do we! --- src/codegen/sys/cargo_toml.rs | 5 +++++ src/codegen/sys/tests.rs | 14 ++++---------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/codegen/sys/cargo_toml.rs b/src/codegen/sys/cargo_toml.rs index e2fd792a8..ab24fe2c1 100644 --- a/src/codegen/sys/cargo_toml.rs +++ b/src/codegen/sys/cargo_toml.rs @@ -77,6 +77,11 @@ fn fill_in(root: &mut Table, env: &Env) { set_string(build_deps, "pkg-config", "0.3.7"); } + { + let dev_deps = upsert_table(root, "dev-dependencies"); + set_string(dev_deps, "shell-words", "0.1.0"); + } + { let features = upsert_table(root, "features"); features.clear(); diff --git a/src/codegen/sys/tests.rs b/src/codegen/sys/tests.rs index 59e30c04c..4c2606703 100644 --- a/src/codegen/sys/tests.rs +++ b/src/codegen/sys/tests.rs @@ -82,6 +82,7 @@ fn generate_abi_rs(env: &Env, path: &Path, w: &mut Write, ctypes: &[&str]) -> io let name = format!("{}_sys", crate_name(&env.config.library_name)); writeln!(w, "extern crate {};", &name)?; + writeln!(w, "extern crate shell_words;")?; writeln!(w, "{}", r##" use std::collections::BTreeMap; use std::env; @@ -122,8 +123,8 @@ impl Compiler { fn get_var(name: &str, default: &str) -> Result, Box> { match env::var(name) { - Ok(value) => Ok(shell_split(&value)), - Err(env::VarError::NotPresent) => Ok(shell_split(default)), + Ok(value) => Ok(shell_words::split(&value)?), + Err(env::VarError::NotPresent) => Ok(shell_words::split(default)?), Err(err) => Err(format!("{} {}", name, err).into()), } } @@ -138,14 +139,7 @@ fn pkg_config_cflags(package: &str) -> Result, Box> { &cmd, out.status).into()); } let stdout = str::from_utf8(&out.stdout)?; - Ok(shell_split(stdout)) -} - -fn shell_split(s: &str) -> Vec { - // FIXME: Use shell word splitting rules. - s.split_whitespace() - .map(|s| s.to_owned()) - .collect() + Ok(shell_words::split(stdout)?) } #[derive(Copy, Clone, Debug, Eq, PartialEq)] From 05eae56d5bddc3eadb5c26b17fd6363feee82e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Fri, 9 Mar 2018 00:00:00 +0000 Subject: [PATCH 03/10] Generate "DO NOT EDIT" header for ABI tests. --- src/codegen/sys/tests.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/codegen/sys/tests.rs b/src/codegen/sys/tests.rs index 4c2606703..37ad533fb 100644 --- a/src/codegen/sys/tests.rs +++ b/src/codegen/sys/tests.rs @@ -7,6 +7,7 @@ use env::Env; use file_saver::save_to_file; use library::{Type, MAIN_NAMESPACE}; use nameutil::crate_name; +use codegen::general; pub fn generate(env :&Env) { let tests = env.config.target_path.join("tests"); @@ -59,7 +60,8 @@ fn is_name_made_up(name: &str) -> bool { fn generate_abi_c(env: &Env, path: &Path, w: &mut Write) -> io::Result<()> { info!("Generating file {:?}", path); - + general::start_comments(w, &env.config)?; + writeln!(w, "")?; writeln!(w, "/* For %z support in printf when using MinGW. */")?; writeln!(w, "#define _POSIX_C_SOURCE 200809L")?; writeln!(w, "#include ")?; @@ -79,7 +81,8 @@ int main() { fn generate_abi_rs(env: &Env, path: &Path, w: &mut Write, ctypes: &[&str]) -> io::Result<()> { info!("Generating file {:?}", path); - + general::start_comments(w, &env.config)?; + writeln!(w, "")?; let name = format!("{}_sys", crate_name(&env.config.library_name)); writeln!(w, "extern crate {};", &name)?; writeln!(w, "extern crate shell_words;")?; From 9af42e26ed76b0c5205a593648d85105d171dd00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Fri, 9 Mar 2018 00:00:00 +0000 Subject: [PATCH 04/10] Use cfg attributes when generating ABI tests. When combinined with proper configuration changes this makes it possible to skip testing types which are known not to be available. --- src/codegen/sys/tests.rs | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/codegen/sys/tests.rs b/src/codegen/sys/tests.rs index 37ad533fb..63ccbb579 100644 --- a/src/codegen/sys/tests.rs +++ b/src/codegen/sys/tests.rs @@ -9,6 +9,13 @@ use library::{Type, MAIN_NAMESPACE}; use nameutil::crate_name; use codegen::general; +struct CType { + /// Name of type, as used in C. + name: String, + /// Expression describing when type is available (when defined only conditionally). + cfg_condition: Option, +} + pub fn generate(env :&Env) { let tests = env.config.target_path.join("tests"); let abi_c = tests.join("abi.c"); @@ -29,15 +36,26 @@ pub fn generate(env :&Env) { Type::Interface(_) => { let full_name = format!("{}.{}", &ns.name, t.get_name()); - if !env.type_status_sys(&full_name).ignored() { - t.get_glib_name() - } else { - None + if env.type_status_sys(&full_name).ignored() { + return None; + } + let name = match t.get_glib_name() { + None => return None, + Some(name) => name, + }; + if is_name_made_up(name) { + return None; } + let cfg_condition = env.config.objects.get(&full_name).and_then(|obj| { + obj.cfg_condition.clone() + }); + Some(CType { + name: name.to_owned(), + cfg_condition, + }) }, _ => None, }) - .filter(|s| !is_name_made_up(s)) .collect::>(); if ctypes.is_empty() { @@ -79,7 +97,7 @@ int main() { } -fn generate_abi_rs(env: &Env, path: &Path, w: &mut Write, ctypes: &[&str]) -> io::Result<()> { +fn generate_abi_rs(env: &Env, path: &Path, w: &mut Write, ctypes: &[CType]) -> io::Result<()> { info!("Generating file {:?}", path); general::start_comments(w, &env.config)?; writeln!(w, "")?; @@ -232,8 +250,9 @@ fn get_rust_abi() -> BTreeMap { let mut abi = BTreeMap::new();"##)?; for ctype in ctypes { + general::cfg_condition(w, &ctype.cfg_condition, false, 1)?; writeln!(w, r##" abi.insert("{ctype}".to_owned(), ABI::from_type::<{ctype}>());"##, - ctype=ctype)?; + ctype=ctype.name)?; } writeln!(w, "{}", r##" abi From 48bc9638cfac3d0d216b837a90088a2c50233995 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Fri, 9 Mar 2018 00:00:00 +0000 Subject: [PATCH 05/10] Show ABI tests results summary on failure. --- src/codegen/sys/tests.rs | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/src/codegen/sys/tests.rs b/src/codegen/sys/tests.rs index 63ccbb579..60772e36f 100644 --- a/src/codegen/sys/tests.rs +++ b/src/codegen/sys/tests.rs @@ -178,6 +178,16 @@ impl ABI { } } +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] +struct TestResults { + /// Number of successfully completed tests. + passed: usize, + /// Total number of failed tests (including those that failed to compile). + failed: usize, + /// Number of tests that failed to compile. + failed_compile: usize, +} + #[test] fn test_cross_validate_abi_with_c() { // Configure compiler instance."##)?; @@ -196,27 +206,36 @@ fn test_cross_validate_abi_with_c() { // Sanity check that compilation works. assert_eq!(ABI {size: 1, alignment: 1}, - get_c_abi(&cc, "char").expect("C ABI for char")); + get_c_abi(&cc, "char").expect("C ABI for char"), + "failed to obtain correct ABI for char type"); - let rust = get_rust_abi(); - let mut failed = false; - for (name, rust_abi) in &rust { + let mut results : TestResults = Default::default(); + for (name, rust_abi) in &get_rust_abi() { match get_c_abi(&cc, name) { Err(e) => { - failed = true; + results.failed += 1; + results.failed_compile += 1; eprintln!("{}", e); continue; }, Ok(ref c_abi) => { - if rust_abi != c_abi { - failed = true; + if rust_abi == c_abi { + results.passed += 1; + } else { + results.failed += 1; eprintln!("ABI mismatch for {}\nRust: {:?}\nC: {:?}", name, rust_abi, c_abi); } } }; } - assert!(!failed); + + if results.failed + results.failed_compile != 0 { + panic!("FAILED\nABI test results: {} passed; {} failed (compilation errors: {})", + results.passed, + results.failed, + results.failed_compile); + } } fn get_c_abi(cc: &Compiler, name: &str) -> Result> { @@ -257,9 +276,6 @@ fn get_rust_abi() -> BTreeMap { writeln!(w, "{}", r##" abi } - "##) } - - From d6338e15f054d2a90db007d30d784017781d98e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Fri, 9 Mar 2018 00:00:00 +0000 Subject: [PATCH 06/10] Include header for manual customization of ABI tests. --- src/codegen/sys/tests.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/codegen/sys/tests.rs b/src/codegen/sys/tests.rs index 60772e36f..47b9e60b1 100644 --- a/src/codegen/sys/tests.rs +++ b/src/codegen/sys/tests.rs @@ -20,6 +20,7 @@ pub fn generate(env :&Env) { let tests = env.config.target_path.join("tests"); let abi_c = tests.join("abi.c"); let abi_rs = tests.join("abi.rs"); + let manual_h = tests.join("manual.h"); let ns = env.library.namespace(MAIN_NAMESPACE); let ctypes = ns.types @@ -62,6 +63,12 @@ pub fn generate(env :&Env) { return; } + if !manual_h.exists() { + save_to_file(&manual_h, env.config.make_backup, |w| { + info!("Generating file {:?}", &manual_h); + writeln!(w, "// Insert manual customizations here, they won't be overwritten.") + }); + } save_to_file(&abi_c, env.config.make_backup, |w| { generate_abi_c(env, &abi_c, w) }); @@ -84,6 +91,7 @@ fn generate_abi_c(env: &Env, path: &Path, w: &mut Write) -> io::Result<()> { writeln!(w, "#define _POSIX_C_SOURCE 200809L")?; writeln!(w, "#include ")?; writeln!(w, "#include ")?; + writeln!(w, "#include \"manual.h\"")?; let ns = env.library.namespace(MAIN_NAMESPACE); for include in ns.c_includes.iter() { From 6baf5682c107c07a5816357969978c0f414b9805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Fri, 9 Mar 2018 00:00:00 +0000 Subject: [PATCH 07/10] Include stdout and stderr in addition to exit status when command execution fails. --- src/codegen/sys/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/codegen/sys/tests.rs b/src/codegen/sys/tests.rs index 47b9e60b1..249eaaffb 100644 --- a/src/codegen/sys/tests.rs +++ b/src/codegen/sys/tests.rs @@ -262,8 +262,8 @@ fn get_c_abi(cc: &Compiler, name: &str) -> Result> { let mut abi_cmd = Command::new("./abi"); let output = abi_cmd.output()?; if !output.status.success() { - return Err(format!("command {:?} failed, {}", - &abi_cmd, output.status).into()); + return Err(format!("command {:?} failed, {:?}", + &abi_cmd, &output).into()); } let stdout = str::from_utf8(&output.stdout)?; From 69d932dbc551c933b7e947009d27af0a84eb1bec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Fri, 9 Mar 2018 00:00:00 +0000 Subject: [PATCH 08/10] Time for C99 is not yet ripe. --- src/codegen/sys/tests.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/codegen/sys/tests.rs b/src/codegen/sys/tests.rs index 249eaaffb..47bbb5fff 100644 --- a/src/codegen/sys/tests.rs +++ b/src/codegen/sys/tests.rs @@ -101,6 +101,7 @@ fn generate_abi_c(env: &Env, path: &Path, w: &mut Write) -> io::Result<()> { writeln!(w, "{}", r##" int main() { printf("%zu\n%zu\n", sizeof(ABI_TEST_TYPE), alignof(ABI_TEST_TYPE)); + return 0; }"##) } From 3abb3a8e60fef67567898ee722f7150244397189 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sat, 10 Mar 2018 00:00:00 +0000 Subject: [PATCH 09/10] Exercise ABI testing when running under Appveyor CI. Limit ABI tests to atk-sys, which is relatively small, to avoid excessive impact on CI runtime. ABI tests are disabled on Travis, because there have been ABI breaking changes since Ubuntu Trusty. --- appveyor.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 4c277a63e..2d6f15f95 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,7 +14,7 @@ install: - SET LIBGIT2_SYS_USE_PKG_CONFIG=1 - rustc -Vv - cargo -Vv - - pacman --noconfirm -S mingw-w64-%ARCH%-gtk3 mingw-w64-%ARCH%-libgit2 + - pacman --noconfirm -S mingw-w64-%ARCH%-gtk3 mingw-w64-%ARCH%-libgit2 mingw-w64-%ARCH%-atk build_script: - git clone -q https://github.com/gtk-rs/gir-files tests/gir-files @@ -25,5 +25,7 @@ build_script: - cd sys_build - cargo build - cargo build --features v3_20 + - cd ../atk-sys + - cargo test test: false From 65e66912793786e9a61566dab46c947a3e3c22fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mi=C4=85sko?= Date: Sat, 10 Mar 2018 00:00:00 +0000 Subject: [PATCH 10/10] Put all headers from gir xml into manual.h Headers generally need some manual tweaking. Some should be removed and others added, but generally don't change too often. Generate a separate file with headers, but don't overwrite it again unless it was removed. --- src/codegen/sys/tests.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/codegen/sys/tests.rs b/src/codegen/sys/tests.rs index 47bbb5fff..3aaed9166 100644 --- a/src/codegen/sys/tests.rs +++ b/src/codegen/sys/tests.rs @@ -65,8 +65,7 @@ pub fn generate(env :&Env) { if !manual_h.exists() { save_to_file(&manual_h, env.config.make_backup, |w| { - info!("Generating file {:?}", &manual_h); - writeln!(w, "// Insert manual customizations here, they won't be overwritten.") + generate_manual_h(env, &manual_h, w) }); } save_to_file(&abi_c, env.config.make_backup, |w| { @@ -83,6 +82,19 @@ fn is_name_made_up(name: &str) -> bool { name.contains('_') } +fn generate_manual_h(env: &Env, path: &Path, w: &mut Write) -> io::Result<()> { + info!("Generating file {:?}", path); + writeln!(w, "// Feel free to edit this file, it won't be regenerated by gir generator unless removed.")?; + writeln!(w, "")?; + + let ns = env.library.namespace(MAIN_NAMESPACE); + for include in &ns.c_includes { + writeln!(w, "#include <{}>", include)?; + } + + Ok(()) +} + fn generate_abi_c(env: &Env, path: &Path, w: &mut Write) -> io::Result<()> { info!("Generating file {:?}", path); general::start_comments(w, &env.config)?; @@ -93,11 +105,6 @@ fn generate_abi_c(env: &Env, path: &Path, w: &mut Write) -> io::Result<()> { writeln!(w, "#include ")?; writeln!(w, "#include \"manual.h\"")?; - let ns = env.library.namespace(MAIN_NAMESPACE); - for include in ns.c_includes.iter() { - writeln!(w, "#include <{}>", include)?; - } - writeln!(w, "{}", r##" int main() { printf("%zu\n%zu\n", sizeof(ABI_TEST_TYPE), alignof(ABI_TEST_TYPE));