Repository: zng-ui/zng (language: rust)

Issue:
Cargo zng res does not search for metadata in cdylib (Android) crates
<!--
Please, make sure:

- The issue happens in the latest crate release or newer (master branch).
- The issue happens after `cargo update`.
-->

#### I tried this code:

```toml
[package]
name = "miscomp-issue-mobile"
version = "0.1.0"
edition = "2021"
license = "TODO"
[package.metadata.zng.about]
app = "miscomp-issue"
org = "Zng Project"
qualifier = "rs.zng"

[lib]
# required by Android
crate-type = ["cdylib"]
```

#### Following these steps:

1 - Call `cargo zng res --metadata-dump`

#### I saw this happen:

No metadata found, because it only searches in `bin` crates.

#### I expected this to happen:

Use metadata from `lib` crates. (maybe only if it has `zng.about` metadata.


Discussion / hints:
Workaround, pass `--metadata crates/lib-crate-foo/Cargo.toml`

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cc9161da1..53986a5d8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,7 @@
 # Unreleased
 
+* Fix `cargo zng res` not getting explicit metadata from lib crates.
+* Implement `--verbose` for `cargo zng res`.
 * Localize settings search box placeholder text.
 
 # 0.12.1
diff --git a/crates/cargo-zng/src/res.rs b/crates/cargo-zng/src/res.rs
index 516bc5746..c62f73f03 100644
--- a/crates/cargo-zng/src/res.rs
+++ b/crates/cargo-zng/src/res.rs
@@ -65,6 +65,10 @@ pub struct ResArgs {
     /// Writes the metadata extracted the workspace or --metadata
     #[arg(long, action)]
     metadata_dump: bool,
+
+    /// Use verbose output.
+    #[arg(short, long, action)]
+    verbose: bool,
 }
 
 fn canonicalize(path: &Path) -> PathBuf {
@@ -83,7 +87,7 @@ pub(crate) fn run(mut args: ResArgs) {
     }
 
     if args.metadata_dump {
-        let about = about::find_about(args.metadata.as_deref());
+        let about = about::find_about(args.metadata.as_deref(), args.verbose);
         crate::res::tool::visit_about_vars(&about, |key, value| {
             println!("{key}={value}");
         });
@@ -113,7 +117,7 @@ pub(crate) fn run(mut args: ResArgs) {
         fatal!("cannot build res to same dir");
     }
 
-    let about = about::find_about(args.metadata.as_deref());
+    let about = about::find_about(args.metadata.as_deref(), args.verbose);
 
     // tool request paths are relative to the workspace root
     if let Some(p) = util::workspace_dir() {
@@ -121,7 +125,7 @@ pub(crate) fn run(mut args: ResArgs) {
             fatal!("cannot change dir, {e}");
         }
     } else {
-        warn!("source is not in a Cargo workspace, tools will run using source as root");
+        warn!("source is not in a cargo workspace, tools will run using source as root");
         if let Err(e) = std::env::set_current_dir(&args.source) {
             fatal!("cannot change dir, {e}");
         }
@@ -144,7 +148,7 @@ pub(crate) fn run(mut args: ResArgs) {
 }
 
 fn build(args: &ResArgs, about: About) -> anyhow::Result<()> {
-    let tools = Tools::capture(&args.tool_dir, args.tool_cache.clone(), about)?;
+    let tools = Tools::capture(&args.tool_dir, args.tool_cache.clone(), about, args.verbose)?;
     source_to_target_pass(args, &tools, &args.source, &args.target)?;
 
     let mut passes = 0;
diff --git a/crates/cargo-zng/src/res/about.rs b/crates/cargo-zng/src/res/about.rs
index 41d4e269d..dcd57f766 100644
--- a/crates/cargo-zng/src/res/about.rs
+++ b/crates/cargo-zng/src/res/about.rs
@@ -1,9 +1,19 @@
-use std::{cmp::Ordering, fmt::Write as _, fs, path::Path};
+use std::{
+    cmp::Ordering,
+    fmt::Write as _,
+    fs,
+    path::{Path, PathBuf},
+    process::Stdio,
+};
 
 use crate::util::workspace_dir;
 
-pub fn find_about(metadata: Option<&Path>) -> zng_env::About {
+pub fn find_about(metadata: Option<&Path>, verbose: bool) -> zng_env::About {
     if let Some(m) = metadata {
+        if verbose {
+            println!("parsing `{}`", m.display());
+        }
+
         let cargo_toml = fs::read_to_string(m).unwrap_or_else(|e| fatal!("cannot read `{}`, {e}", m.display()));
         return zng_env::About::parse_manifest(&cargo_toml).unwrap_or_else(|e| fatal!("cannot parse `{}`, {e}", m.display()));
     }
@@ -12,28 +22,64 @@ pub fn find_about(metadata: Option<&Path>) -> zng_env::About {
 
     let workspace_manifest =
         workspace_dir().unwrap_or_else(|| fatal!("cannot locate workspace, use --metadata if source is not in a cargo project"));
-    for bin in glob::glob("**/src/main.rs").unwrap_or_else(|e| fatal!("cannot search metadata, {e}")) {
-        let bin = bin.unwrap_or_else(|e| fatal!("error searching metadata, {e}"));
-        let manifest = bin.parent().unwrap().parent().unwrap().join("Cargo.toml");
-        if manifest.exists() {
-            let mut cmd = std::process::Command::new("cargo");
-            cmd.arg("locate-project").arg("--workspace").arg("--message-format=plain");
-            let manifest_dir = manifest.parent().unwrap();
-            if !manifest_dir.as_os_str().is_empty() {
-                cmd.current_dir(manifest_dir);
+    if verbose {
+        println!("workspace `{}`", workspace_manifest.display())
+    }
+
+    for manifest in glob::glob(&format!(
+        "{}/**/Cargo.toml",
+        workspace_manifest.display().to_string().replace("\\", "/").trim_end_matches('/')
+    ))
+    .unwrap_or_else(|e| fatal!("cannot search metadata, {e}"))
+    {
+        let manifest = manifest.unwrap_or_else(|e| fatal!("error searching metadata, {e}"));
+        let _empty = PathBuf::new();
+        let manifest_dir = manifest.parent().unwrap_or(&_empty);
+        if manifest_dir.as_os_str().is_empty() {
+            continue;
+        }
+
+        let output = std::process::Command::new("cargo")
+            .arg("locate-project")
+            .arg("--workspace")
+            .arg("--message-format=plain")
+            .current_dir(manifest_dir)
+            .stderr(Stdio::inherit())
+            .output()
+            .unwrap_or_else(|e| fatal!("cannot locate workspace in `{}`, {e}", manifest_dir.display()));
+        if !output.status.success() {
+            continue;
+        }
+        let w2 = Path::new(std::str::from_utf8(&output.stdout).unwrap().trim()).parent().unwrap();
+        if w2 != workspace_manifest {
+            if verbose {
+                println!("skip `{}`, not a workspace member", manifest.display())
             }
-            let output = cmd.output().unwrap_or_else(|e| fatal!("cannot locate workspace, {e}"));
+            continue;
+        }
 
-            if output.status.success() {
-                let w2 = Path::new(std::str::from_utf8(&output.stdout).unwrap().trim()).parent().unwrap();
-                if workspace_manifest == w2 {
-                    let cargo_toml = fs::read_to_string(&manifest).unwrap_or_else(|e| fatal!("cannot read `{}`, {e}", manifest.display()));
-                    options.push(
-                        zng_env::About::parse_manifest(&cargo_toml)
-                            .unwrap_or_else(|e| fatal!("cannot parse `{}`, {e}", manifest.display())),
-                    );
+        let cargo_toml = fs::read_to_string(&manifest).unwrap_or_else(|e| fatal!("cannot read `{}`, {e}", manifest.display()));
+        let about = match zng_env::About::parse_manifest(&cargo_toml) {
+            Ok(a) => a,
+            Err(e) => {
+                if e.message().contains("missing field `package`") {
+                    if verbose {
+                        println!("skip `{}`, no package metadata", manifest.display());
+                    }
+                } else {
+                    error!("cannot parse `{}`, {e}", manifest.display());
                 }
+                continue;
             }
+        };
+
+        if about.has_about || manifest_dir.join("src/main.rs").exists() {
+            options.push(about);
+        } else if verbose {
+            println!(
+                "skip `{}` cause it has no zng metadata and/or it is not a bin crate",
+                manifest.display()
+            );
         }
     }
 
diff --git a/crates/cargo-zng/src/res/tool.rs b/crates/cargo-zng/src/res/tool.rs
index 6c65d292b..534fa50da 100644
--- a/crates/cargo-zng/src/res/tool.rs
+++ b/crates/cargo-zng/src/res/tool.rs
@@ -299,9 +299,12 @@ pub struct Tools {
     about: About,
 }
 impl Tools {
-    pub fn capture(local: &Path, cache: PathBuf, about: About) -> anyhow::Result<Self> {
+    pub fn capture(local: &Path, cache: PathBuf, about: About, verbose: bool) -> anyhow::Result<Self> {
         let mut tools = vec![];
         visit_tools(local, |t| {
+            if verbose {
+                println!("found tool `{}` in `{}`", t.name, t.path.display())
+            }
             tools.push(t);
             Ok(ControlFlow::Continue(()))
         })?;


Test patch: