Merge changes I3e156afa,Id7e4f2eb

* changes:
  cargo_embargo: detect and ignore harness-less tests
  cargo_embargo: use enum for crate type
diff --git a/tools/cargo_embargo/src/cargo_out.rs b/tools/cargo_embargo/src/cargo_out.rs
index 2aa6ad4..fcf467f 100644
--- a/tools/cargo_embargo/src/cargo_out.rs
+++ b/tools/cargo_embargo/src/cargo_out.rs
@@ -22,6 +22,23 @@
 use std::path::Path;
 use std::path::PathBuf;
 
+/// Combined representation of --crate-type and --test flags.
+#[derive(Debug, PartialEq, Eq)]
+pub enum CrateType {
+    // --crate-type types
+    Bin,
+    Lib,
+    RLib,
+    DyLib,
+    CDyLib,
+    StaticLib,
+    ProcMacro,
+    // --test
+    Test,
+    // "--cfg test" without --test. (Assume it is a test with the harness disabled.
+    TestNoHarness,
+}
+
 /// Info extracted from `CargoOut` for a crate.
 ///
 /// Note that there is a 1-to-many relationship between a Cargo.toml file and these `Crate`
@@ -32,11 +49,7 @@
     pub name: String,
     pub package_name: String,
     pub version: Option<String>,
-    // cargo calls rustc with multiple --crate-type flags.
-    // rustc can accept:
-    //   --crate-type [bin|lib|rlib|dylib|cdylib|staticlib|proc-macro]
-    pub types: Vec<String>,
-    pub test: bool,                             // --test
+    pub types: Vec<CrateType>,
     pub target: Option<String>,                 // --target
     pub features: Vec<String>,                  // --cfg feature=
     pub cfgs: Vec<String>,                      // non-feature --cfg
@@ -219,6 +232,21 @@
     }
 }
 
+impl CrateType {
+    fn from_str(s: &str) -> CrateType {
+        match s {
+            "bin" => CrateType::Bin,
+            "lib" => CrateType::Lib,
+            "rlib" => CrateType::RLib,
+            "dylib" => CrateType::DyLib,
+            "cdylib" => CrateType::CDyLib,
+            "staticlib" => CrateType::StaticLib,
+            "proc-macro" => CrateType::ProcMacro,
+            _ => panic!("unexpected --crate-type: {}", s),
+        }
+    }
+}
+
 impl Crate {
     fn from_rustc_invocation(rustc: &str, metadata: &WorkspaceMetadata) -> Result<Crate> {
         let mut out = Crate::default();
@@ -239,8 +267,10 @@
         while let Some(arg) = arg_iter.next() {
             match arg {
                 "--crate-name" => out.name = arg_iter.next().unwrap().to_string(),
-                "--crate-type" => out.types.push(arg_iter.next().unwrap().to_string()),
-                "--test" => out.test = true,
+                "--crate-type" => out
+                    .types
+                    .push(CrateType::from_str(arg_iter.next().unwrap().to_string().as_str())),
+                "--test" => out.types.push(CrateType::Test),
                 "--target" => out.target = Some(arg_iter.next().unwrap().to_string()),
                 "--cfg" => {
                     // example: feature=\"sink\"
@@ -355,10 +385,18 @@
         if out.main_src.as_os_str().is_empty() {
             bail!("missing main source file");
         }
-        if out.types.is_empty() != out.test {
-            bail!("expected exactly one of either --crate-type or --test");
+        // Must have at least one type.
+        if out.types.is_empty() {
+            if out.cfgs.contains(&"test".to_string()) {
+                out.types.push(CrateType::TestNoHarness);
+            } else {
+                bail!("failed to detect crate type. did not have --crate-type or --test or '--cfg test'");
+            }
         }
-        if out.types.iter().any(|x| x == "lib") && out.types.iter().any(|x| x == "rlib") {
+        if out.types.contains(&CrateType::Test) && out.types.len() != 1 {
+            bail!("cannot specify both --test and --crate-type");
+        }
+        if out.types.contains(&CrateType::Lib) && out.types.contains(&CrateType::RLib) {
             bail!("cannot both have lib and rlib crate types");
         }
 
diff --git a/tools/cargo_embargo/src/main.rs b/tools/cargo_embargo/src/main.rs
index d024345..dd2caf5 100644
--- a/tools/cargo_embargo/src/main.rs
+++ b/tools/cargo_embargo/src/main.rs
@@ -360,7 +360,14 @@
     };
 
     for c in crates {
-        modules.extend(crate_to_bp_modules(c, cfg, package_cfg, &extra_srcs)?);
+        modules.extend(crate_to_bp_modules(c, cfg, package_cfg, &extra_srcs).with_context(
+            || {
+                format!(
+                    "failed to generate bp module for crate \"{}\" with package name \"{}\"",
+                    c.name, c.package_name
+                )
+            },
+        )?);
     }
     if modules.is_empty() {
         return Ok(());
@@ -417,42 +424,46 @@
     extra_srcs: &[String],
 ) -> Result<Vec<BpModule>> {
     let mut modules = Vec::new();
-    let mut types = crate_.types.clone();
-    if crate_.test {
-        types.push("test".to_string());
-    }
-    for crate_type in types {
+    for crate_type in &crate_.types {
         let host = if package_cfg.device_supported.unwrap_or(true) { "" } else { "_host" };
         let rlib = if package_cfg.force_rlib { "_rlib" } else { "" };
-        let (module_type, module_name, stem) = match crate_type.as_str() {
-            "bin" => ("rust_binary".to_string() + host, crate_.name.clone(), crate_.name.clone()),
-            "lib" | "rlib" => {
+        let (module_type, module_name, stem) = match crate_type {
+            CrateType::Bin => {
+                ("rust_binary".to_string() + host, crate_.name.clone(), crate_.name.clone())
+            }
+            CrateType::Lib | CrateType::RLib => {
                 let stem = "lib".to_string() + &crate_.name;
                 ("rust_library".to_string() + rlib + host, stem.clone(), stem)
             }
-            "dylib" => {
+            CrateType::DyLib => {
                 let stem = "lib".to_string() + &crate_.name;
                 ("rust_library".to_string() + host + "_dylib", stem.clone() + "_dylib", stem)
             }
-            "cdylib" => {
+            CrateType::CDyLib => {
                 let stem = "lib".to_string() + &crate_.name;
                 ("rust_ffi".to_string() + host + "_shared", stem.clone() + "_shared", stem)
             }
-            "staticlib" => {
+            CrateType::StaticLib => {
                 let stem = "lib".to_string() + &crate_.name;
                 ("rust_ffi".to_string() + host + "_static", stem.clone() + "_static", stem)
             }
-            "proc-macro" => {
+            CrateType::ProcMacro => {
                 let stem = "lib".to_string() + &crate_.name;
                 ("rust_proc_macro".to_string(), stem.clone(), stem)
             }
-            "test" => {
+            CrateType::Test | CrateType::TestNoHarness => {
                 let suffix = crate_.main_src.to_string_lossy().into_owned();
                 let suffix = suffix.replace('/', "_").replace(".rs", "");
                 let stem = crate_.package_name.clone() + "_test_" + &suffix;
+                if crate_type == &CrateType::TestNoHarness {
+                    eprintln!(
+                        "WARNING: ignoring test \"{}\" with harness=false. not supported yet",
+                        stem
+                    );
+                    return Ok(Vec::new());
+                }
                 ("rust_test".to_string() + host, stem.clone(), stem)
             }
-            _ => panic!("unexpected crate type: {}", crate_type),
         };
 
         let mut m = BpModule::new(module_type.clone());
@@ -483,7 +494,7 @@
             m.props.set("cargo_pkg_version", version.clone());
         }
 
-        if crate_.test {
+        if crate_.types.contains(&CrateType::Test) {
             m.props.set("test_suites", vec!["general-tests"]);
             m.props.set("auto_gen_config", true);
             if package_cfg.host_supported.unwrap_or(true) {
@@ -567,7 +578,14 @@
         }
 
         if !cfg.apex_available.is_empty()
-            && ["lib", "rlib", "dylib", "staticlib", "cdylib"].contains(&crate_type.as_str())
+            && [
+                CrateType::Lib,
+                CrateType::RLib,
+                CrateType::DyLib,
+                CrateType::CDyLib,
+                CrateType::StaticLib,
+            ]
+            .contains(crate_type)
         {
             m.props.set("apex_available", cfg.apex_available.clone());
         }