use std::fs::read_to_string; use std::path::PathBuf; use std::process::Command; static CBINDGEN_PATH: &str = env!("CARGO_BIN_EXE_cbindgen"); fn test_project(project_path: &str) { let mut cmake_cmd = Command::new("cmake"); cmake_cmd.arg("--version"); cmake_cmd .output() .expect("CMake --version failed - Is CMake installed?"); let mut cmake_configure = Command::new("cmake"); let build_dir = PathBuf::from(project_path).join("build"); if build_dir.exists() { std::fs::remove_dir_all(&build_dir).expect("Failed to remove old build directory"); } let project_dir = PathBuf::from(project_path); let cbindgen_define = format!("-DCBINDGEN_PATH={}", CBINDGEN_PATH); cmake_configure .arg("-S") .arg(project_path) .arg("-B") .arg(&build_dir) .arg(cbindgen_define); let output = cmake_configure.output().expect("Failed to execute process"); let stdout_str = String::from_utf8(output.stdout).unwrap(); let stderr_str = String::from_utf8(output.stderr).unwrap(); assert!( output.status.success(), "Configuring test project failed: stdout: `{}`, stderr: `{}`", stdout_str, stderr_str ); let depfile_path = build_dir.join("depfile.d"); assert!( !depfile_path.exists(), "depfile should not exist before building" ); // Do the clean first build let mut cmake_build = Command::new("cmake"); cmake_build.arg("--build").arg(&build_dir); let output = cmake_build.output().expect("Failed to execute process"); assert!(output.status.success(), "Building test project failed"); let out_str = String::from_utf8(output.stdout).unwrap(); assert!( out_str.contains("Running cbindgen"), "cbindgen rule did not run. Output: {}", out_str ); assert!( depfile_path.exists(), "depfile does not exist after building" ); let expected_dependencies_filepath = PathBuf::from(project_path) .join("expectations") .join("dependencies"); assert!( expected_dependencies_filepath.exists(), "Test did not define expected dependencies. Please read the Readme.md" ); let expected_deps = read_to_string(expected_dependencies_filepath).expect("Failed to read dependencies"); let depinfo = read_to_string(depfile_path).expect("Failed to read dependencies"); // Assumes a single rule in the file - all deps are listed to the rhs of the `:`. let actual_deps = depinfo.split(':').collect::>()[1]; // Strip the line breaks. let actual_deps = actual_deps.replace("\\\n", " "); // I don't want to deal with supporting escaped whitespace when splitting at whitespace, // so the tests don't support being run in a directory containing whitespace. assert!( !actual_deps.contains("\\ "), "The tests directory may not contain any whitespace" ); let dep_list: Vec<&str> = actual_deps.split_ascii_whitespace().collect(); let expected_dep_list: Vec = expected_deps .lines() .map(|dep| project_dir.join(dep).to_str().unwrap().to_string()) .collect(); assert_eq!(dep_list, expected_dep_list); let output = cmake_build.output().expect("Failed to execute process"); assert!(output.status.success(), "Building test project failed"); let out_str = String::from_utf8(output.stdout).unwrap(); assert!( !out_str.contains("Running cbindgen"), "cbindgen rule ran on second build" ); std::fs::remove_dir_all(build_dir).expect("Failed to remove old build directory"); } macro_rules! test_file { ($test_function_name:ident, $name:expr, $file:tt) => { #[test] fn $test_function_name() { test_project($file); } }; } // This file is generated by build.rs include!(concat!(env!("OUT_DIR"), "/depfile_tests.rs"));