crosvm_control: Add Build Time C Compilation Check

Adds a build time compilation check for the generated crosvm_control.h

BUG=b:271789981
TEST=CQ, apply crrev/4237140 and observe build failure due to C compile
error

Change-Id: I57867894a975aa0a48f989051bb4243bf97f57e8
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/4333206
Reviewed-by: Dennis Kempin <denniskempin@google.com>
Commit-Queue: Kameron Lutes <kalutes@chromium.org>
Reviewed-by: Keiichi Watanabe <keiichiw@chromium.org>
diff --git a/Cargo.lock b/Cargo.lock
index 549b226..3fa2a5e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -693,7 +693,9 @@
  "anyhow",
  "base",
  "cbindgen",
+ "cc",
  "libc",
+ "tempfile",
  "vm_control",
 ]
 
diff --git a/crosvm_control/Cargo.toml b/crosvm_control/Cargo.toml
index 911facb..df4875b 100644
--- a/crosvm_control/Cargo.toml
+++ b/crosvm_control/Cargo.toml
@@ -15,3 +15,5 @@
 [build-dependencies]
 anyhow = "*"
 cbindgen = "0.24"
+cc = "*"
+tempfile = "*"
diff --git a/crosvm_control/build.rs b/crosvm_control/build.rs
index 1dd1554..7727bf3 100644
--- a/crosvm_control/build.rs
+++ b/crosvm_control/build.rs
@@ -3,12 +3,14 @@
 // found in the LICENSE file.
 
 use std::env;
+use std::fs;
 use std::path::PathBuf;
 
 use anyhow::Context;
 use anyhow::Result;
 use cbindgen::Config;
 use cbindgen::Language;
+use tempfile::TempDir;
 
 static COPYRIGHT_CLAUSE: &str = "// Copyright 2022 The ChromiumOS Authors
 // Use of this source code is governed by a BSD-style license that can be
@@ -19,6 +21,8 @@
 
 static INCLUDE_GUARD: &str = "CROSVM_CONTROL_H_";
 
+static CROSVM_CONTROL_HEADER_NAME: &str = "crosvm_control.h";
+
 fn main() -> Result<()> {
     // Skip building dependencies when generating documents.
     if std::env::var("CARGO_DOC").is_ok() {
@@ -27,9 +31,10 @@
 
     let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
 
-    let target_dir = env::var("OUT_DIR").context("failed to get OUT_DIR")?;
-    let output_file = PathBuf::from(target_dir)
-        .join("crosvm_control.h")
+    let output_dir = PathBuf::from(env::var("OUT_DIR").context("failed to get OUT_DIR")?);
+
+    let output_file = output_dir
+        .join(CROSVM_CONTROL_HEADER_NAME)
         .display()
         .to_string();
 
@@ -50,5 +55,29 @@
         .context("Unable to generate bindings")?
         .write_to_file(&output_file);
 
+    // Do not perform the compilation check on Windows since GCC might not be installed.
+    if std::env::var("CARGO_CFG_WINDOWS").is_ok() {
+        return Ok(());
+    }
+
+    // Do a quick compile test of the generated header to ensure it is valid
+    let temp_dir = TempDir::new()?;
+    let test_file = temp_dir
+        .path()
+        .join("crosvm_control_test.c")
+        .display()
+        .to_string();
+
+    fs::write(
+        &test_file,
+        format!("{}{}{}", "#include \"", CROSVM_CONTROL_HEADER_NAME, "\""),
+    )
+    .context("Failed to write crosvm_control test C file")?;
+
+    cc::Build::new()
+        .include(output_dir)
+        .file(test_file)
+        .compile("crosvm_control_test");
+
     Ok(())
 }