From 76920aa742739115bb998fa6be4349cdb9952135 Mon Sep 17 00:00:00 2001
From: John Baublitz <jbaublitz@redhat.com>
Date: Wed, 8 Jan 2025 11:04:49 -0500
Subject: [PATCH] Fix bugs in --clang-macro-fallback

This commit resolves a bug where -include was not respected and a bug
where -MMD was passed to the fallback translation unit which would cause
the dependency file to be overwritten with useless information about
temporary files.
---
 bindgen/clang.rs       |  4 ---
 bindgen/ir/context.rs  | 76 +++++++++++++++---------------------------
 bindgen/lib.rs         | 12 +++++++
 bindgen/options/mod.rs |  5 +++
 4 files changed, 44 insertions(+), 53 deletions(-)

diff --git a/bindgen/clang.rs b/bindgen/clang.rs
index 9afc6e93b1..04fe3e1538 100644
--- a/bindgen/clang.rs
+++ b/bindgen/clang.rs
@@ -1914,7 +1914,6 @@ impl Drop for TranslationUnit {
 /// Translation unit used for macro fallback parsing
 pub(crate) struct FallbackTranslationUnit {
     file_path: String,
-    header_path: String,
     pch_path: String,
     idx: Box<Index>,
     tu: TranslationUnit,
@@ -1930,7 +1929,6 @@ impl FallbackTranslationUnit {
     /// Create a new fallback translation unit
     pub(crate) fn new(
         file: String,
-        header_path: String,
         pch_path: String,
         c_args: &[Box<str>],
     ) -> Option<Self> {
@@ -1952,7 +1950,6 @@ impl FallbackTranslationUnit {
         )?;
         Some(FallbackTranslationUnit {
             file_path: file,
-            header_path,
             pch_path,
             tu: f_translation_unit,
             idx: f_index,
@@ -1991,7 +1988,6 @@ impl FallbackTranslationUnit {
 impl Drop for FallbackTranslationUnit {
     fn drop(&mut self) {
         let _ = std::fs::remove_file(&self.file_path);
-        let _ = std::fs::remove_file(&self.header_path);
         let _ = std::fs::remove_file(&self.pch_path);
     }
 }
diff --git a/bindgen/ir/context.rs b/bindgen/ir/context.rs
index c6bc9025ec..78790d61c4 100644
--- a/bindgen/ir/context.rs
+++ b/bindgen/ir/context.rs
@@ -29,8 +29,6 @@ use quote::ToTokens;
 use std::borrow::Cow;
 use std::cell::{Cell, RefCell};
 use std::collections::{BTreeSet, HashMap as StdHashMap};
-use std::fs::OpenOptions;
-use std::io::Write;
 use std::mem;
 use std::path::Path;
 
@@ -2054,8 +2052,11 @@ If you encounter an error missing from this list, please file an issue or a PR!"
 
             let mut header_names_to_compile = Vec::new();
             let mut header_paths = Vec::new();
-            let mut header_contents = String::new();
-            for input_header in &self.options.input_headers {
+            let mut header_includes = Vec::new();
+            let single_header = self.options().input_headers.last().cloned()?;
+            for input_header in &self.options.input_headers
+                [..self.options.input_headers.len() - 1]
+            {
                 let path = Path::new(input_header.as_ref());
                 if let Some(header_path) = path.parent() {
                     if header_path == Path::new("") {
@@ -2067,50 +2068,32 @@ If you encounter an error missing from this list, please file an issue or a PR!"
                     header_paths.push(".");
                 }
                 let header_name = path.file_name()?.to_str()?;
+                header_includes.push(header_name.to_string());
                 header_names_to_compile
                     .push(header_name.split(".h").next()?.to_string());
-                header_contents +=
-                    format!("\n#include <{header_name}>").as_str();
             }
-            let header_to_precompile = format!(
+            let pch = format!(
                 "{}/{}",
                 match self.options().clang_macro_fallback_build_dir {
                     Some(ref path) => path.as_os_str().to_str()?,
                     None => ".",
                 },
-                header_names_to_compile.join("-") + "-precompile.h"
+                header_names_to_compile.join("-") + "-precompile.h.pch"
             );
-            let pch = header_to_precompile.clone() + ".pch";
-
-            let mut header_to_precompile_file = OpenOptions::new()
-                .create(true)
-                .truncate(true)
-                .write(true)
-                .open(&header_to_precompile)
-                .ok()?;
-            header_to_precompile_file
-                .write_all(header_contents.as_bytes())
-                .ok()?;
-
-            let mut c_args = Vec::new();
+
+            let mut c_args = self.options.fallback_clang_args.clone();
             c_args.push("-x".to_string().into_boxed_str());
             c_args.push("c-header".to_string().into_boxed_str());
             for header_path in header_paths {
                 c_args.push(format!("-I{header_path}").into_boxed_str());
             }
-            c_args.extend(
-                self.options
-                    .clang_args
-                    .iter()
-                    .filter(|next| {
-                        !self.options.input_headers.contains(next) &&
-                            next.as_ref() != "-include"
-                    })
-                    .cloned(),
-            );
+            for header_include in header_includes {
+                c_args.push("-include".to_string().into_boxed_str());
+                c_args.push(header_include.into_boxed_str());
+            }
             let mut tu = clang::TranslationUnit::parse(
                 &index,
-                &header_to_precompile,
+                &single_header,
                 &c_args,
                 &[],
                 clang_sys::CXTranslationUnit_ForSerialization,
@@ -2121,23 +2104,18 @@ If you encounter an error missing from this list, please file an issue or a PR!"
                 "-include-pch".to_string().into_boxed_str(),
                 pch.clone().into_boxed_str(),
             ];
-            c_args.extend(
-                self.options
-                    .clang_args
-                    .clone()
-                    .iter()
-                    .filter(|next| {
-                        !self.options.input_headers.contains(next) &&
-                            next.as_ref() != "-include"
-                    })
-                    .cloned(),
-            );
-            self.fallback_tu = Some(clang::FallbackTranslationUnit::new(
-                file,
-                header_to_precompile,
-                pch,
-                &c_args,
-            )?);
+            let mut skip_next = false;
+            for arg in self.options.fallback_clang_args.iter() {
+                if arg.as_ref() == "-include" {
+                    skip_next = true;
+                } else if skip_next {
+                    skip_next = false;
+                } else {
+                    c_args.push(arg.clone())
+                }
+            }
+            self.fallback_tu =
+                Some(clang::FallbackTranslationUnit::new(file, pch, &c_args)?);
         }
 
         self.fallback_tu.as_mut()
diff --git a/bindgen/lib.rs b/bindgen/lib.rs
index 0a8f29d158..1a15d51d67 100644
--- a/bindgen/lib.rs
+++ b/bindgen/lib.rs
@@ -348,6 +348,18 @@ impl Builder {
         }
 
         // Transform input headers to arguments on the clang command line.
+        self.options.fallback_clang_args = self
+            .options
+            .clang_args
+            .iter()
+            .filter(|arg| {
+                !arg.starts_with("-MMD") &&
+                    !arg.starts_with("-MD") &&
+                    !arg.starts_with("--write-user-dependencies") &&
+                    !arg.starts_with("--user-dependencies")
+            })
+            .cloned()
+            .collect::<Vec<_>>();
         self.options.clang_args.extend(
             self.options.input_headers
                 [..self.options.input_headers.len().saturating_sub(1)]
diff --git a/bindgen/options/mod.rs b/bindgen/options/mod.rs
index 6bf652d4e1..9d1d195980 100644
--- a/bindgen/options/mod.rs
+++ b/bindgen/options/mod.rs
@@ -1234,6 +1234,11 @@ options! {
         // This field is handled specially inside the macro.
         as_args: ignore,
     },
+    /// The set of arguments to be passed straight through to Clang for the macro fallback code.
+    fallback_clang_args: Vec<Box<str>> {
+        methods: {},
+        as_args: ignore,
+    },
     /// Tuples of unsaved file contents of the form (name, contents).
     input_header_contents: Vec<(Box<str>, Box<str>)> {
         methods: {