#6 cargo2rpm + workspaces + binaries required-features error
Closed by decathorpe. Opened by eclipseo.

So I am working with https://github.com/espanso/espanso 2.1.8

It has a workspace in the root directory:

[workspace]

members = [
  "espanso",
  "espanso-detect",
  "espanso-ui",
  "espanso-inject",
  "espanso-ipc",
  "espanso-config",
  "espanso-match",
  "espanso-clipboard",
  "espanso-render",
  "espanso-info",
  "espanso-path",
  "espanso-modulo",
  "espanso-migrate",
  "espanso-mac-utils",
  "espanso-kvs",
  "espanso-engine",
  "espanso-package",
]

In the espanso directory I have customized the Cargo.toml to add two [[bin]] section:

[package]
name = "espanso"
version = "2.1.8"
authors = ["Federico Terzi <federicoterzi96@gmail.com>"]
license = "GPL-3.0"
description = "Cross-platform Text Expander written in Rust"
readme = "README.md"
homepage = "https://github.com/federico-terzi/espanso"
edition = "2018"

[features]
default = ["modulo", "native-tls"]

# These features control whether Espanso will use the native TLS functionality
# or not. On some platforms (currently Linux) we prefer vendoring the SSL
# logic used by the packages to avoid dependency issues.
# https://github.com/espanso/espanso/issues/1056
native-tls = ["espanso-package/default-tls"]
vendored-tls = ["espanso-package/rustls-tls"]

# If the wayland feature is enabled, all X11 dependencies will be dropped
# and only methods suitable for Wayland will be used
wayland = ["espanso-detect/wayland", "espanso-inject/wayland", "espanso-clipboard/wayland", "espanso-info/wayland"]

# Compile modulo and all its dependencies (including wxWidgets). If you don't
# enable it, features like Forms and Search might not be available.
modulo = ["espanso-modulo", "espanso-clipboard/avoid-gdi", "espanso-ui/avoid-gdi"]

[[bin]]
name = "espanso"
path = "src/main.rs"
required-features = ["default"]

[[bin]]
name = "espanso-wayland"
path = "src/main.rs"
required-features = ["wayland"]

[dependencies]
espanso-detect = { path = "../espanso-detect" } 
espanso-ui = { path = "../espanso-ui" } 
espanso-inject = { path = "../espanso-inject" } 
espanso-config = { path = "../espanso-config" } 
espanso-match = { path = "../espanso-match" } 
espanso-clipboard = { path = "../espanso-clipboard" } 
espanso-info = { path = "../espanso-info" } 
espanso-render = { path = "../espanso-render" } 
espanso-path = { path = "../espanso-path" } 
espanso-ipc = { path = "../espanso-ipc" } 
espanso-modulo = { path = "../espanso-modulo", optional = true }
espanso-migrate = { path = "../espanso-migrate" } 
espanso-kvs = { path = "../espanso-kvs" } 
espanso-engine = { path = "../espanso-engine" } 
espanso-package = { path = "../espanso-package"}
maplit = "1.0.2"
simplelog = "0.12.0"
log = "0.4.14"
anyhow = "1.0.38"
thiserror = "1.0.23"
clap = "2.33.3"
lazy_static = "1.4.0"
crossbeam = "0.8.0"
enum-as-inner = "0.5.0"
dirs = "3.0.1"
serde = { version = "1.0.123", features = ["derive"] }
serde_json = "1.0.62"
log-panics = "2.0.0"
fs2 = "0.4.3"
serde_yaml = "0.8.17"
fs_extra = "1.2.0"
dialoguer = "0.10.0"
colored = "2.0.0"
tempdir = "0.3.7"
notify-debouncer-mini = "0.2.0"
opener = "0.5.0"
sysinfo = "0.28.0"
time = "0.3.0"

[target.'cfg(unix)'.dependencies]
libc = "0.2.98"

[target.'cfg(target_os="linux")'.dependencies]
caps = "0.5.2"
const_format = "0.2.14"
regex = "1.5.5"

[package.metadata.deb]
maintainer = "Federico Terzi <federicoterzi96@gmail.com>"
depends = "$auto, systemd, libxtst6, xclip, libnotify-bin, libxkbcommon0, libwxgtk3.0-gtk3-0v5"
section = "utility"
license-file = ["../LICENSE", "1"]

[package.metadata.deb.variants.wayland]
depends = "$auto, systemd, libnotify-bin, libxkbcommon0, libwxgtk3.0-gtk3-0v5, wl-clipboard"
# TODO: once this issue [1] is fixed, we should create a variant for 
# wayland to automatically run the setcap script.
# [1]: https://github.com/mmstick/cargo-deb/issues/151

When I run cargo2rpm in the workspace I have the following error:

cargo2rpm --path ./Cargo.toml buildrequires                                                                                                                                                                                                         (base)  0s  fish 
Traceback (most recent call last):
  File "/usr/bin/cargo2rpm", line 8, in <module>
    sys.exit(main())
             ^^^^^^
  File "/usr/lib/python3.12/site-packages/cargo2rpm/__main__.py", line 111, in main
    action_buildrequires(args)
  File "/usr/lib/python3.12/site-packages/cargo2rpm/__main__.py", line 50, in action_buildrequires
    brs = workspace_buildrequires(metadata, flags, args.with_check)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/cargo2rpm/rpm.py", line 234, in workspace_buildrequires
    member_flags = metadata.get_feature_flags_for_workspace_members(flags)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.12/site-packages/cargo2rpm/metadata.py", line 617, in get_feature_flags_for_workspace_members
    required_features[package.name].update(reqs)
    ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
KeyError: 'espanso'

If I remove required-features = ["default"] and required-features = ["wayland"] from the [[bin]] section it works, but I need them.


Thanks for reporting this, I'll investigate (and add the metadata for espanso as a test case to prevent future regressions).

Note that required-features = ["default"] should be a noop, unless you build with -n / --no-default-features.

I found the issue, it was a logic bug in the resolver for enabled features across workspace members. It was easy to fix: https://pagure.io/fedora-rust/cargo2rpm/c/8f9ad961cfec09f504748e1c7aa086d7be62b781?branch=main

I'll tag a new version of cargo2rpm and push it to Fedora shortly.


Note that I would still handle this differently myself, but at least it should now also not crash the way you're doing it :)

Metadata Update from @decathorpe:
- Issue status updated to: Closed (was: Open)

Thank you!

Note that I would still handle this differently myself, but at least it should now also not crash the way you're doing it :)

How would you do it?

I would probably try something like this:

%build
%cargo_build
mv target/release/espanso ./espanso
%cargo_build -f wayland
mv target/release/espanso ./espanso-wayland

and then not use %cargo_install but install these files manually.

That wouldn't require patching in custom binary targets (not sure if it's worth it, since you need to patch out windows and Mac dependencies anyway)

OK thanks.

I have to patch the source a lot anyway:

diff -up espanso-2.1.8/espanso/src/cli/daemon/watcher.rs.orig espanso-2.1.8/espanso/src/cli/daemon/watcher.rs
--- espanso-2.1.8/espanso/src/cli/daemon/watcher.rs.orig    2022-11-01 11:23:19.000000000 +0100
+++ espanso-2.1.8/espanso/src/cli/daemon/watcher.rs 2023-09-21 20:03:25.199873322 +0200
@@ -19,7 +19,11 @@

 use std::{path::Path, time::Duration};

-use notify::{DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
+use notify_debouncer_mini::{
+  new_debouncer_opt,
+  notify::{Config, RecommendedWatcher, RecursiveMode},
+  Debouncer,
+};

 use anyhow::Result;
 use crossbeam::{channel::Sender, select};
@@ -51,11 +55,16 @@ pub fn initialize_and_spawn(config_dir:
 fn watcher_main(config_dir: &Path, debounce_tx: crossbeam::channel::Sender<()>) {
   let (tx, rx) = std::sync::mpsc::channel();

-  let mut watcher: RecommendedWatcher =
-    Watcher::new(tx, Duration::from_millis(WATCHER_NOTIFY_DELAY_MS))
-      .expect("unable to create file watcher");
+  let mut watcher: Debouncer<RecommendedWatcher> = new_debouncer_opt(
+    Duration::from_millis(WATCHER_DEBOUNCE_DURATION_MS),
+    None,
+    tx,
+    Config::default().with_poll_interval(Duration::from_millis(WATCHER_NOTIFY_DELAY_MS)),
+  )
+  .expect("unable to create file watcher");

   watcher
+    .watcher()
     .watch(&config_dir, RecursiveMode::Recursive)
     .expect("unable to start file watcher");

@@ -63,32 +72,19 @@ fn watcher_main(config_dir: &Path, debou

   loop {
     let should_reload = match rx.recv() {
-      Ok(event) => {
-        let path = match event {
-          DebouncedEvent::Create(path) => Some(path),
-          DebouncedEvent::Write(path) => Some(path),
-          DebouncedEvent::Remove(path) => Some(path),
-          DebouncedEvent::Rename(_, path) => Some(path),
-          _ => None,
-        };
-
-        if let Some(path) = path {
-          let extension = path
-            .extension()
-            .unwrap_or_default()
-            .to_string_lossy()
-            .to_ascii_lowercase();
-
-          if ["yml", "yaml"].iter().any(|ext| ext == &extension) {
-            // Only load non-hidden yml files
-            !is_file_hidden(&path)
-          } else {
-            // If there is no extension, it's probably a folder
-            extension.is_empty()
-          }
-        } else {
-          false
-        }
+      Ok(Ok(events)) => events.iter().any(|one_event| {
+        let path = &one_event.path; // Changed this line
+        let extension = path
+          .extension()
+          .unwrap_or_default()
+          .to_string_lossy()
+          .to_ascii_lowercase();
+
+        ["yml", "yaml"].iter().any(|ext| ext == &extension) && !is_file_hidden(path)
+      }),
+      Ok(Err(e)) => {
+        warn!("error while watching files: {:?}", e);
+        false
       }
       Err(e) => {
         warn!("error while watching files: {:?}", e);
diff -up espanso-2.1.8/espanso/src/cli/service/linux.rs.orig espanso-2.1.8/espanso/src/cli/service/linux.rs
--- espanso-2.1.8/espanso/src/cli/service/linux.rs.orig 2022-11-01 11:23:19.000000000 +0100
+++ espanso-2.1.8/espanso/src/cli/service/linux.rs  2023-09-21 20:03:25.199873322 +0200
@@ -27,7 +27,12 @@ use thiserror::Error;

 use crate::{error_eprintln, info_println, warn_eprintln};

+#[cfg(feature = "wayland")]
+const LINUX_SERVICE_NAME: &str = "espanso-wayland";
+
+#[cfg(not(feature = "wayland"))]
 const LINUX_SERVICE_NAME: &str = "espanso";
+
 const LINUX_SERVICE_CONTENT: &str = include_str!("../../res/linux/systemd.service");
 #[allow(clippy::transmute_bytes_to_str)]
 const LINUX_SERVICE_FILENAME: &str = formatcp!("{}.service", LINUX_SERVICE_NAME);
diff -up espanso-2.1.8/espanso/src/main.rs.orig espanso-2.1.8/espanso/src/main.rs
--- espanso-2.1.8/espanso/src/main.rs.orig  2022-11-01 11:23:19.000000000 +0100
+++ espanso-2.1.8/espanso/src/main.rs   2023-09-21 20:03:25.200873315 +0200
@@ -30,8 +30,11 @@ use cli::{CliAlias, CliModule, CliModule
 use log::{error, info, warn};
 use logging::FileProxy;
 use simplelog::{
-  CombinedLogger, ConfigBuilder, LevelFilter, SharedLogger, TermLogger, TerminalMode, WriteLogger,
+  ColorChoice, CombinedLogger, ConfigBuilder, LevelFilter, SharedLogger, TermLogger, TerminalMode,
+  WriteLogger,
 };
+use std::convert::identity;
+use time::macros::format_description;

 use crate::{
   cli::{LogMode, PathsOverrides},
@@ -524,12 +527,9 @@ For example, specifying 'email' is equiv
     let log_proxy = FileProxy::new();
     if handler.enable_logs {
       let config = ConfigBuilder::new()
-        .set_time_to_local(true)
-        .set_time_format(format!(
-          "%H:%M:%S [{}({})]",
-          handler.subcommand,
-          std::process::id()
-        ))
+        .set_time_offset_to_local()
+        .unwrap_or_else(identity)
+        .set_time_format_custom(format_description!("[hour]:[minute]:[second]]"))
         .set_location_level(LevelFilter::Off)
         .add_filter_ignore_str("html5ever")
         .build();
@@ -541,7 +541,10 @@ For example, specifying 'email' is equiv
       )];

       if !handler.disable_logs_terminal_output {
-        outputs.insert(0, TermLogger::new(log_level, config, TerminalMode::Mixed));
+        outputs.insert(
+          0,
+          TermLogger::new(log_level, config, TerminalMode::Mixed, ColorChoice::Auto),
+        );
       }

       CombinedLogger::init(outputs).expect("unable to initialize logs");

Yeah, I thought patches will already be the case so ... it doesn't save you much.

Log in to comment on this ticket.

Metadata