gifnksm/cli-xtask

Support virtual workspaces

Opened this issue · 4 comments

Bug description

cli-xtask doesn't work for Virtual workspaces out of the box as the first thing it does is attempt to load the root package, and there's way to pass in a package name with the default setup.

Following the instructions for cli-xtask leads to a no root package found error.

  • Would you like to work on a fix? [y/n]
    Sure, but I'm not sure if it's a doc issue, or an actual code issue. Need help understanding the issue.

To Reproduce

Details

https://github.com/joshka/cli-xtask-no-root-issue
cd cli-xtask-no-root-issue
cargo xtask dist

Outputs:

Error:
   0: no root package found

Location:
   /Users/joshka/.cargo/registry/src/github.com-1ecc6299db9ec823/cli-xtask-0.6.1/src/config/dist.rs:96

Steps to reproduce the behavior:

Follow the xtask / cli-xtask instructions:

❯ cargo new --bin testing
     Created binary (application) `testing` package

❯ cd testing
❯ mkdir testing
❯ mv src Cargo.toml .gitignore testing
❯ cargo new --bin xtask
     Created binary (application) `xtask` package
❯ >Cargo.toml
[workspace]
members = [
    "testing",
    "xtask",
]

❯ mkdir .cargo
❯ >.cargo/config.toml
[alias]
xtask = "run --package xtask --"
zsh: no matches found: [alias]

❯ cargo add -p xtask cli-xtask --features main,bin-crate
    Updating crates.io index
      Adding cli-xtask v0.6.1 to dependencies.
             Features:
             + archive
             + bin-crate
             + error-handler
             + logger
             + main
             + subcommand-build
             + subcommand-clippy
             + subcommand-dist
             + subcommand-dist-archive
             + subcommand-dist-build-bin
             + subcommand-dist-build-completion
             + subcommand-dist-build-doc
             + subcommand-dist-build-license
             + subcommand-dist-build-man
             + subcommand-dist-build-readme
             + subcommand-dist-clean
             + subcommand-fmt
             + subcommand-lint
             + subcommand-pre-release
             + subcommand-test
             + subcommand-tidy
             - bin-crate-extra
             - lib-crate
             - lib-crate-extra
             - subcommand-doc
             - subcommand-docsrs
             - subcommand-exec
             - subcommand-sync-rdme
             - subcommand-udeps

❯ cargo xtask dist
   Compiling proc-macro2 v1.0.59
   Compiling quote v1.0.28
   Compiling unicode-ident v1.0.9
   Compiling libc v0.2.144
   Compiling cfg-if v1.0.0
   Compiling once_cell v1.17.1
   Compiling memchr v2.5.0
   Compiling io-lifetimes v1.0.11
   Compiling rustix v0.37.19
   Compiling serde v1.0.163
   Compiling log v0.4.17
   Compiling bitflags v1.3.2
   Compiling tracing-core v0.1.31
   Compiling regex-syntax v0.6.29
   Compiling utf8parse v0.2.1
   Compiling lazy_static v1.4.0
   Compiling aho-corasick v1.0.1
   Compiling anstyle-parse v0.2.0
   Compiling regex-syntax v0.7.2
   Compiling syn v2.0.18
   Compiling errno v0.3.1
   Compiling pin-project-lite v0.2.9
   Compiling anstyle-query v1.0.0
   Compiling anstyle v1.0.0
   Compiling overload v0.1.1
   Compiling regex-automata v0.1.10
   Compiling colorchoice v1.0.0
   Compiling adler v1.0.2
   Compiling tracing-log v0.1.3
   Compiling nu-ansi-term v0.46.0
   Compiling sharded-slab v0.1.4
   Compiling thread_local v1.1.7
   Compiling matchers v0.1.0
   Compiling strsim v0.10.0
   Compiling smallvec v1.10.0
   Compiling is-terminal v0.4.7
   Compiling regex v1.8.3
   Compiling heck v0.4.1
   Compiling cc v1.0.79
   Compiling clap_lex v0.5.0
   Compiling gimli v0.27.2
   Compiling anstream v0.3.2
   Compiling camino v1.1.4
   Compiling crc32fast v1.3.2
   Compiling serde_json v1.0.96
   Compiling serde_derive v1.0.163
   Compiling tracing-attributes v0.1.24
   Compiling clap_builder v4.3.0
   Compiling backtrace v0.3.67
   Compiling clap_derive v4.3.0
   Compiling semver v1.0.17
   Compiling thiserror v1.0.40
   Compiling eyre v0.6.8
   Compiling addr2line v0.19.0
   Compiling thiserror-impl v1.0.40
   Compiling miniz_oxide v0.6.2
   Compiling object v0.30.3
   Compiling indenter v0.3.3
   Compiling rustc-demangle v0.1.23
   Compiling ryu v1.0.13
   Compiling itoa v1.0.6
   Compiling owo-colors v3.5.0
   Compiling miniz_oxide v0.7.1
   Compiling xattr v0.2.3
   Compiling filetime v0.2.21
   Compiling roff v0.2.1
   Compiling cli-xtask v0.6.1
   Compiling same-file v1.0.6
   Compiling time-core v0.1.1
   Compiling tar v0.4.38
   Compiling walkdir v2.3.3
   Compiling time v0.3.21
   Compiling flate2 v1.0.26
   Compiling tracing v0.1.37
   Compiling tracing-subscriber v0.3.17
   Compiling tracing-error v0.2.0
   Compiling clap v4.3.0
   Compiling clap_complete v4.3.0
   Compiling clap_mangen v0.2.11
   Compiling color-spantrace v0.2.0
   Compiling color-eyre v0.6.2
   Compiling cargo-platform v0.1.2
   Compiling cargo_metadata v0.15.4
   Compiling xtask v0.1.0 (/Users/joshka/local/testing/xtask)
    Finished dev [unoptimized + debuginfo] target(s) in 11.62s
     Running `target/debug/xtask dist`
Hello, world!>xtask/src/main.rs
use cli_xtask::{Result, Xtask};

fn main() -> Result<()> {
    <Xtask>::main()
}

❯ cargo xtask dist
   Compiling xtask v0.1.0 (/Users/joshka/local/testing/xtask)
    Finished dev [unoptimized + debuginfo] target(s) in 0.95s
     Running `target/debug/xtask dist`
Error:
   0: no root package found

Location:
   /Users/joshka/.cargo/registry/src/github.com-1ecc6299db9ec823/cli-xtask-0.6.1/src/config/dist.rs:96

Backtrace omitted. Run with RUST_BACKTRACE=1 environment variable to display it.
Run with RUST_BACKTRACE=full to include source snippets.

Expected behavior

Either this succeeds or the error message suggests the next steps.

Screenshots

Environment

  • OS: macOS 12.6.5
  • cli-xtask version: 0.6.1

Additional context

Details

❯ tree -a --git-ignore --no-icons
./
├── .cargo/
│  └── config.toml
├── .git/
│  ├── hooks/
│  │  └── README.sample*
│  ├── info/
│  │  └── exclude
│  ├── objects/
│  │  ├── info/
│  │  └── pack/
│  ├── refs/
│  │  ├── heads/
│  │  └── tags/
│  ├── config
│  ├── description
│  └── HEAD
├── testing/
│  ├── src/
│  │  └── main.rs
│  ├── .gitignore
│  └── Cargo.toml
├── xtask/
│  ├── src/
│  │  └── main.rs
│  └── Cargo.toml
├── Cargo.lock
└── Cargo.toml
❯ cargo metadata | jq '.'
{
  "packages": [
...
    {
      "name": "testing",
      "version": "0.1.0",
      "id": "testing 0.1.0 (path+file:///Users/joshka/local/testing/testing)",
      "license": null,
      "license_file": null,
      "description": null,
      "source": null,
      "dependencies": [],
      "targets": [
        {
          "kind": [
            "bin"
          ],
          "crate_types": [
            "bin"
          ],
          "name": "testing",
          "src_path": "/Users/joshka/local/testing/testing/src/main.rs",
          "edition": "2021",
          "doc": true,
          "doctest": false,
          "test": true
        }
      ],
      "features": {},
      "manifest_path": "/Users/joshka/local/testing/testing/Cargo.toml",
      "metadata": null,
      "publish": null,
      "authors": [],
      "categories": [],
      "keywords": [],
      "readme": null,
      "repository": null,
      "homepage": null,
      "documentation": null,
      "edition": "2021",
      "links": null,
      "default_run": null,
      "rust_version": null
    },
...
    {
      "name": "xtask",
      "version": "0.1.0",
      "id": "xtask 0.1.0 (path+file:///Users/joshka/local/testing/xtask)",
      "license": null,
      "license_file": null,
      "description": null,
      "source": null,
      "dependencies": [
        {
          "name": "cli-xtask",
          "source": "registry+https://github.com/rust-lang/crates.io-index",
          "req": "^0.6.1",
          "kind": null,
          "rename": null,
          "optional": false,
          "uses_default_features": true,
          "features": [
            "main",
            "bin-crate"
          ],
          "target": null,
          "registry": null
        }
      ],
      "targets": [
        {
          "kind": [
            "bin"
          ],
          "crate_types": [
            "bin"
          ],
          "name": "xtask",
          "src_path": "/Users/joshka/local/testing/xtask/src/main.rs",
          "edition": "2021",
          "doc": true,
          "doctest": false,
          "test": true
        }
      ],
      "features": {},
      "manifest_path": "/Users/joshka/local/testing/xtask/Cargo.toml",
      "metadata": null,
      "publish": null,
      "authors": [],
      "categories": [],
      "keywords": [],
      "readme": null,
      "repository": null,
      "homepage": null,
      "documentation": null,
      "edition": "2021",
      "links": null,
      "default_run": null,
      "rust_version": null
    }
  ],
  "workspace_members": [
    "testing 0.1.0 (path+file:///Users/joshka/local/testing/testing)",
    "xtask 0.1.0 (path+file:///Users/joshka/local/testing/xtask)"
  ],
  "resolve": {
    "nodes": [
...
      {
        "id": "testing 0.1.0 (path+file:///Users/joshka/local/testing/testing)",
        "dependencies": [],
        "deps": [],
        "features": []
      },
...
      {
        "id": "xtask 0.1.0 (path+file:///Users/joshka/local/testing/xtask)",
        "dependencies": [
          "cli-xtask 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)"
        ],
        "deps": [
          {
            "name": "cli_xtask",
            "pkg": "cli-xtask 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
            "dep_kinds": [
              {
                "kind": null,
                "target": null
              }
            ]
          }
        ],
        "features": []
      }
    ],
   "root": null
  },
  "target_directory": "/Users/joshka/local/ja/target",
  "version": 1,
  "workspace_root": "/Users/joshka/local/ja",
  "metadata": null
}

Particularly note the "root": null there

A bit of a dive through the source of cli-xtask and cargo-workspace suggests that the issue is that resolve is Some(Resolve), but the root is set to None within the resolve part. I don't understand why this is so.

This is my first time attempting to use a workspace in a rust project, so I'm assuming that I'm missing a very small and obvious detail that would be obvious for someone more experience. However, I wasn't able to quite understand from the combination of xtask / xtask-cli docs / xtask-cli code / cargo-workspace code / the workspaces section of the cargo book and the workspaces section of the rust programming language book.

The answer is that this doesn't support virtual workspaces (yet ;))

Hey there, reopening this as I'd like to actually have this work for a package that we're going to be using virtual workspaces for soon. When I initially raised this issue, my rust-fu was fairly basic, so I wasn't really sure how this should work and how the pieces fit together.

The cargo way of running commands is that if there's a root package, then that should be used as the default package for any command, but this should be able to be overridden using a --package argument. When there is no root package (in a virtual workspace), then the commands should act as-if they're called to run against the default members, or all packages if there is no default members list. This also can be overridden with --package / --workspace (and --all if you want to include the deprecated flag that is common).

The missing parts here seem like:

  • --package args are only on the commands at lower levels than the main, so require a root package
  • --workspace args only work if there's a root package

I propose to do:

  • move the package / workspace args up into the main command parser
  • make the default distconfigbuilder setup do the right thing for selecting packages based on defaults and args
  • parse the cli args before constructing the distconfig
  • perhaps add a small affordance where if there's only two packages in a virtual workspace (some main package and xtask), treat the default single package approach as operating just on the main package, regardless of whether this is the root or just a plain package) - this point is debatable (and possibly the wrong thing to do - what do you think @gifnksm)

I suspect some of this might entail breaking changes to existing usages of this package, but there don't seem to be a large amount, so this is likely a small concern. https://crates.io/crates/cli-xtask/reverse_dependencies / https://github.com/search?q=cli-xtask+path%3Acargo.toml&type=code

Alternative workaround:

  • As a workaround, users of this crate could define their own main command parser with the package and workspace args and pass these through to the cli-xtask parsing code. This seems janky though.

You can use <Xtask>::main_with_config() to specify the packages to be distributed.

Also, I've added the virtual workspace example and some utility methods in cli-xtask v0.9.0.
In this example, binaries in workspace default members are built for distribution.
You can choose arbitrary packages by using DistConfigBuilder::package_by_name().

Do you think this will satisfy your use case?

Maybe. I ended up going fairly manual right for expediency (https://github.com/ratatui/ratatui/blob/main/xtask/src/main.rs), so I'll have to take another look at this to work out the overlap.