From a84176fdbbf47d109cc7a10f10e7d033112a7fc2 Mon Sep 17 00:00:00 2001 From: Elijah Newren Date: Wed, 3 May 2023 08:01:34 -0700 Subject: [PATCH] WIP script for checking if includes in headers can be removed Be sure to comment out the forward declares of strbuf in git-compat-util.h first, as well as the function declarations in git-compat-util.h that take a strbuf*. Also, on mac run-cargo-script doesn't work so you have to create a project, and you need to modify temp.c to add a couple #defines: NO_OPENSSL HAVE_FSMONITOR_DAEMON_BACKEND Example command line: for i in $(git ls-files -- '*.h' | grep -v git-compat-util.h); do echo === $i ===; ./check-header-removal.rs $i; done | tee RESULTS.txt --- check-header-removal.rs | 174 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100755 check-header-removal.rs diff --git a/check-header-removal.rs b/check-header-removal.rs new file mode 100755 index 00000000000000..8b2151d1132b7f --- /dev/null +++ b/check-header-removal.rs @@ -0,0 +1,174 @@ +#!/usr/bin/env run-cargo-script +//! ```cargo +//! [dependencies] +//! regex = "1.8.1" +//! ``` +#![allow(unused_imports)] + +extern crate regex; + +use regex::Regex; +use std::env; +use std::fs; +use std::io::{self, Write}; +use std::path::{Path, PathBuf}; +use std::process::{Command, Stdio}; + +fn main() { + let args: Vec = env::args().collect(); + + if args.len() != 2 { + eprintln!("Usage: {} ", args[0]); + std::process::exit(1); + } + + let header_file = &args[1]; + + let header_content = match fs::read_to_string(header_file) { + Ok(content) => content, + Err(err) => { + eprintln!("Failed to read header file '{}': {}", header_file, err); + std::process::exit(1); + } + }; + + let mut includes = Vec::new(); + + for (i, line) in header_content.lines().enumerate() { + if line.trim_start().starts_with("#include") { + includes.push(i); + } + } + + let mut tweaked_line = ""; + + for include_line_number in &includes { + let modified_header_content = { + let mut modified_content = String::new(); + let mut include_commented = false; + + for (i, line) in header_content.lines().enumerate() { + if i == *include_line_number { + if line.trim_start().starts_with("/*") { + include_commented = true; + } else { + modified_content += "//#"; + modified_content += line; + modified_content += "\n"; + tweaked_line = line; + } + } else if include_commented { + if line.trim_start().starts_with("*/") { + include_commented = false; + } + } else { + modified_content += line; + modified_content += "\n"; + } + } + + modified_content + }; + + if !tweaked_line.starts_with("#include \"") { + continue; + } + let filename = tweaked_line.trim_start_matches("#include ") + .trim_matches('"'); + let path = Path::new(filename); + let filename_only = path.file_name().unwrap().to_str().unwrap(); + //println!("Working on {}", filename_only); + + + let header_file_path = Path::new(header_file); + // Get the parent directory of the header file + let parent_dir = header_file_path.parent().unwrap_or_else(|| Path::new(".")); + // Create a temporary file in the same directory as the original header file + let mut temp_file_path = PathBuf::new(); + temp_file_path.push(parent_dir); + temp_file_path.push("TEMPORARY_FILE_NAME.h"); + /* + println!("parent_dir={}, temp_file_path={}", + parent_dir.to_string_lossy(), temp_file_path.to_string_lossy()); + */ + + if let Err(err) = fs::write(&temp_file_path, &modified_header_content) { + eprintln!("Failed to write temporary header file: {}", err); + std::process::exit(1); + } + + let temp_file_name = "temp.c"; + let temp_file = PathBuf::from(&temp_file_name); + fs::write(&temp_file, format!("#include \"git-compat-util.h\"\n#include \"{}\"\nint main() {{}}", temp_file_path.to_string_lossy())).expect("Failed to write temporary file"); + let output = Command::new("gcc") + .arg("-E") + .arg("-I.") + .arg(&temp_file) + .stdin(Stdio::piped()) + .stderr(Stdio::piped()) + .output() + .expect("Failed to run command"); + + let escaped_include = regex::escape(filename_only); + let pattern_str = format!("\\b{}\"", escaped_include); + let regex = Regex::new(&pattern_str).unwrap(); + let stdout = String::from_utf8(output.stdout).expect("Invalid UTF-8"); + let mut match_found = false; + for line in stdout.lines() { + if regex.is_match(line) { + match_found = true; + break; + } + } + if match_found { + //println!("No point removing {}", filename_only); + if let Err(err) = fs::remove_file(&temp_file_path) { + eprintln!("Failed to remove temporary header file: {}", err); + std::process::exit(1); + } + continue; + } + + let output = Command::new("gcc") + .arg("-c") + .arg("-I.") + .arg("-Wmissing-prototypes") + .arg("-Werror=pedantic") + .arg("-Werror") + .arg(&temp_file) + .stdin(Stdio::piped()) + .stderr(Stdio::piped()) + .output() + .expect("Failed to run command"); + + // Report the success or failure of the compilation + if output.status.success() { + println!("{} compiles without: {}", header_file, tweaked_line); + } else { + let output = Command::new("gcc") + .arg("-c") + .arg("-I.") + .arg(&temp_file) + .stdin(Stdio::piped()) + .stderr(Stdio::piped()) + .output() + .expect("Failed to run command"); + + if output.status.success() { + println!("{} almost compiles without: {}", header_file, tweaked_line); + } else { + /* + println!("Compilation failed after removing include: {}", tweaked_line); + println!("Output: {}\n", String::from_utf8_lossy(&output.stdout)); + println!("Stderr: {}\n", String::from_utf8_lossy(&output.stderr)); + */ + } + } + + if let Err(err) = fs::remove_file(&temp_file_path) { + eprintln!("Failed to remove temporary header file: {}", err); + std::process::exit(1); + } + + } +}