Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a cabal target command #9744

Open
wants to merge 36 commits into
base: master
Choose a base branch
from

Conversation

philderbeast
Copy link
Collaborator

@philderbeast philderbeast commented Feb 26, 2024

Fixes #8953. Adds a cabal target command for showing targets that can be supplied to other commands like the cabal build command.

Motivation

When a project has but one package the targets are easy to find by looking inside the .cabal file but when a project has hundreds of packages that method is hard with information under layers of folders and files.

Using grep in .cabal files is tough to get right when we need to exclude stuff that is not in the project.

image

Here's all:exes found with this new target command:

$ cabal run cabal-install:exe:cabal -- target all:exes
...
Fully qualified target forms:
 - buildinfo-reference-generator:exe:buildinfo-reference-generator
 - cabal-install:exe:cabal
 - cabal-testsuite:exe:cabal-tests
 - cabal-testsuite:exe:setup
 - cabal-testsuite:exe:test-runtime-deps
 - cabal-validate:exe:cabal-validate
 - solver-benchmarks:exe:hackage-benchmark
Found 7 targets matching all:exes.

The initial motivation would have been #8953. Other related issues are #8683, #9732, #1382 and #4070.

Help

Here's the help;

$ cabal --help
...
  [project building and installing]
+  target                 Disclose selected targets.
   build                  Compile targets within the project.

The command specific help, a lot of which is taken straight from the user guide on target forms;

$ cabal -- target --help
...
Discover targets in a project for use with other commands taking [TARGETS].

Discloses fully-qualified targets from a selection of target form [TARGETS]
(or 'all' if none given). Can also check if a target form is unique as some
commands require a unique TARGET.

A [TARGETS] item can be one of these target forms:
 - a package target (e.g. [pkg:]package)
 - a component target (e.g. [package:][ctype:]component)
 - all packages (e.g. all)
 - components of a particular type (e.g. package:ctypes or all:ctypes)
 - a module target: (e.g. [package:][ctype:]module)
 - a filepath target: (e.g. [package:][ctype:]filepath)

The ctypes, in short form and (long form), can be one of:
 - libs (libraries)
 - exes (executables)
 - tests
 - benches (benchmarks)
 - flibs (foreign-libraries)

For a package, all, module or filepath target, cabal target [TARGETS] will
*only* show 'libs' and 'exes' of the [TARGETS]. To also show tests and
benchmarks, enable them with '--enable-tests' and '--enable-benchmarks'.

Flags for target:
 -h, --help                     Show this help text

...
Examples:
  cabal target all
    Targets of the package in the current directory or all packages in the project
  cabal target pkgname
    Targets of the package named pkgname in the project
  cabal target ./pkgfoo
    Targets of the package in the ./pkgfoo directory
  cabal target cname
    Targets of the component named cname in the project

Use

The command in action on the haskell/cabal project:

$ cabal target
...
Fully qualified target forms:
 - Cabal-QuickCheck:lib:Cabal-QuickCheck
 - Cabal-described:lib:Cabal-described
 - Cabal-hooks:lib:Cabal-hooks
 - Cabal-syntax:lib:Cabal-syntax
 - Cabal-tests:lib:Cabal-tests
 - Cabal-tests:test:check-tests
 - Cabal-tests:test:custom-setup-tests
 - Cabal-tests:test:hackage-tests
 - Cabal-tests:test:no-thunks-test
 - Cabal-tests:test:parser-tests
 - Cabal-tests:test:rpmvercmp
 - Cabal-tests:test:unit-tests
 - Cabal-tree-diff:lib:Cabal-tree-diff
 - Cabal:lib:Cabal
 - cabal-benchmarks:test:cabal-benchmarks
 - cabal-install-solver:lib:cabal-install-solver
 - cabal-install-solver:test:unit-tests
 - cabal-install:lib:cabal-install
 - cabal-install:test:integration-tests2
 - cabal-install:test:long-tests
 - cabal-install:test:mem-use-tests
 - cabal-install:test:unit-tests
 - cabal-testsuite:lib:cabal-testsuite
 - solver-benchmarks:lib:solver-benchmarks
 - solver-benchmarks:test:unit-tests
Found 25 targets matching all.

Note

Tests are included above because the project enables them:

tests: True

The query 'all' is used when none is given.

QA Notes

cabal target is a new command that produces a list of fully-qualified targets. Please check that these are unique. I haven't added a module test to cabal-testsuite/PackageTests/Target/cabal.test.hs but it does work:

$ cabal run cabal-install:exe:cabal -- target Distribution.Client.CmdTarget
...
Fully qualified target forms:
 - cabal-install:lib:cabal-install
Found 1 target matching Distribution.Client.CmdTarget.

@fendor
Copy link
Collaborator

fendor commented Feb 26, 2024

Related PR: #7500

@philderbeast
Copy link
Collaborator Author

Thanks for the link @fendor. I can see you've put a lot of effort into a larger scoped change.

@alt-romes
Copy link
Collaborator

alt-romes commented Feb 27, 2024

This looks plausible to me. We definitely need a test and changelog entry -- though I understand this only makes sense to do if the feature is generally agreed upon, which I can't guarantee by myself. I'll bring it up in the dev meeting on Thursday.

@philderbeast
Copy link
Collaborator Author

philderbeast commented Feb 27, 2024

Warning

These problems with an older implementation no longer exist.

Because this uses --dry-run effectively, if dependencies haven't been built (I got there by changing branches, cabal clean won't invalidate already built dependencies) then these too will show up as targets.

Note

I'm suprised to see Cabal:lib there twice.

$ cabal target all
Resolving dependencies...
 - Cabal-QuickCheck:lib
 - Cabal-described:lib
 - Cabal-syntax:lib
 - Cabal-tests:lib
 - Cabal-tests:test:check-tests
 - Cabal-tests:test:custom-setup-tests
 - Cabal-tests:test:hackage-tests
 - Cabal-tests:test:no-thunks-test
 - Cabal-tests:test:parser-tests
 - Cabal-tests:test:rpmvercmp
 - Cabal-tests:test:unit-tests
 - Cabal-tree-diff:lib
 - Cabal:lib
 - Cabal:lib
 - Diff:lib
 - OneTuple:lib
 - QuickCheck:lib
 - StateVar:lib
 - aeson:lib
 - ansi-terminal-types:lib
 - ansi-terminal:lib
 - ansi-wl-pprint:lib
 - assoc:lib
 - attoparsec:lib
 - attoparsec:lib:attoparsec-internal
 - base-compat:lib
 - base-orphans:lib
 - bifunctors:lib
 - bitvec:lib
 - bytestring-builder:lib
 - cabal-benchmarks:test:cabal-benchmarks
 - cabal-install-solver:lib
 - cabal-install-solver:test:unit-tests
 - cabal-install:exe:cabal
 - cabal-install:lib
 - cabal-install:test:integration-tests2
 - cabal-install:test:long-tests
 - cabal-install:test:mem-use-tests
 - cabal-install:test:unit-tests
 - cabal-testsuite
 - call-stack:lib
 - charset:lib
 - clock:lib
 - colour:lib
 - comonad:lib
 - contravariant:lib
 - data-default-class(lib:data-default-class)
 - data-fix:lib
 - dense-linear-algebra:lib
 - distributive:lib
 - dlist:lib
 - generically:lib
 - hackage-security:lib
 - happy:exe:happy
 - haskell-lexer:lib
 - indexed-traversable-instances:lib
 - indexed-traversable:lib
 - integer-conversion:lib
 - integer-logarithms:lib
 - math-functions:lib
 - mtl-compat:lib
 - mwc-random:lib
 - network-wait:lib
 - nothunks:lib
 - optparse-applicative:lib
 - parallel:lib
 - parsers:lib
 - pretty-show:lib
 - prettyprinter-ansi-terminal:lib
 - prettyprinter-compat-ansi-wl-pprint:lib
 - prettyprinter:lib
 - primitive:lib
 - regex-tdfa:lib
 - rere:lib
 - retry:lib
 - scientific:lib
 - semialign:lib
 - semigroupoids:lib
 - solver-benchmarks:exe:hackage-benchmark
 - solver-benchmarks:lib
 - solver-benchmarks:test:unit-tests
 - statistics:lib
 - strict:lib
 - tagged:lib
 - tasty-bench:lib
 - tasty-expected-failure:lib
 - tasty-golden:lib
 - tasty-hunit:lib
 - tasty-quickcheck:lib
 - tasty:lib
 - temporary:lib
 - text-iso8601:lib
 - text-short:lib
 - th-abstraction:lib
 - these:lib
 - time-compat:lib
 - transformers-compat:lib
 - tree-diff:lib
 - typed-process:lib
 - unbounded-delays:lib
 - unliftio-core:lib
 - unordered-containers:lib
 - uuid-types:lib
 - vector-algorithms:lib
 - vector-binary-instances:lib
 - vector-stream:lib
 - vector-th-unbox:lib
 - vector:lib
 - wherefrom-compat:lib
 - witherable:lib

@philderbeast
Copy link
Collaborator Author

In investigating further, trying to build only the dependencies, I hit an error I've never seen before;

$ git rev-parse HEAD
6d9267d78b35c5075171309170825d42e4fd16e6

$ cabal build all --only-dependencies
Warning: this is a debug build of cabal-install with assertions enabled.
Error: [Cabal-7072]
Cannot select only the dependencies (as requested by the '--only-dependencies' flag),
the package Cabal-syntax-3.11.0.0 is required by a dependency of one of the other targets.

@andreabedini
Copy link
Collaborator

Maybe list-target(s)? It seems to follow naturally from the help message:

target                 List target forms within the project.
list-bin               List the path to a single executable.

Let me emphasise that those are only the targets within the project (as the help already points out) and that different commands can interpret their arguments more liberally (e.g. any package-id is a valid target for cabal install).

@andreabedini
Copy link
Collaborator

@philderbeast Ill give it a proper review ASAP

@philderbeast
Copy link
Collaborator Author

philderbeast commented Feb 28, 2024

Maybe list-target(s)? It seems to follow naturally from the help message:

target                 List target forms within the project.
list-bin               List the path to a single executable.

Let me emphasise that those are only the targets within the project (as the help already points out) and that different commands can interpret their arguments more liberally (e.g. any package-id is a valid target for cabal install).

I dislike the list-bin command name and wish it was named something else, like bin-path. I went with target instead of targets because none of the other command names are plural. They're all verbs aren't they?

@michaelpj
Copy link
Collaborator

What about cabal target list? There is some consensus that subcommands with the form <cmd> <noun> <verb> are the way to go. But then perhaps "target" is not a noun that we want to attach many verbs to 🤷

@andreasabel
Copy link
Member

Can we have a cabal list command that allows subcommands that all display some information about what cabal thinks about the world?

  • cabal list targets This PR.
  • cabal list bins Alt. name of cabal list-bin.
  • cabal list paths Alt. name of cabal path.
  • etc.

@philderbeast
Copy link
Collaborator Author

philderbeast commented Feb 29, 2024

What about cabal target list?

Yes @michaelpj a subcommand instead is a possibility. As implemented this is acting as cabal build --dry-run but printing something different at the end, printing the targets rather than what would be built. So that would be something like cabal build --print-targets.

@Mikolaj
Copy link
Member

Mikolaj commented Mar 5, 2024

I like cabal build --print-targets, because then we don't have to think whether it's going to be used a lot or only in very special circumstances (a top-level command would probably require establishing it's going to be used often enough). Maybe let's make it even more non-intrusive (though harder to use) and let cabal build --print-targets really build and then print the built targets (or separately the targets that are built and the targets that could be built) and cabal build --dry-run --print-targets show only all the possible targets? That's a more radical interpretation of your "but printing something different at the end" (or maybe it's "and printing some extra info at the end"). Makes sense? Then it's even more fine if the result with and without --dry-run or with and without stuff already built differs (cabal commands are stateful).

@philderbeast
Copy link
Collaborator Author

I favour --print-targets too. Saw that this is a new make option;

As of Jan 8th, 2024, Make has a --print-targets option that should do this properly without hacky regexes. The current version is Make 4.4.1 so the next release after that will have this feature.
SOURCE: How do you get the list of targets in a makefile?

@michaelpj
Copy link
Collaborator

-1 for attaching this to cabal build

  • Sometimes you don't want to build! Now you need cabal build --dont-build --print-targets. Yes, we already have --dry-run, but that's supposed to tell you what it was going to build, so how are we going to glue the output together? Awkward. It's just generally difficult making a command do double duty.
  • Most importantly, the targets are not uniquely associated with building. You can also use them in repl and many other commands. So do we add cabal repl --print-targets too? It's just not a build thing. In contrast, --dry-run tells you what would have been built, so really does make sense to belong to cabal build.

Make is different because a) it doesn't have sub-commands and b) the only operation on targets is building them.

Food for thought: https://doc.rust-lang.org/cargo/commands/cargo-metadata.html

@philderbeast
Copy link
Collaborator Author

My main motivation for this change, no matter which command or subcommand or option we put it under, is to help interactive use of cabal itself and not for producing a machine readable format for IDE tooling like the cabal status enchancement of #7500. Lots of commands take targets. This change shows me those targets.

@fendor
Copy link
Collaborator

fendor commented Mar 10, 2024

I understand and support this motivation. cabal needs to be easier to hack around with. The cabal status command was also intended to improve the cabal UX, allowing exactly this use-case, but I deferred the decision of what the UX should look like, in favour of a well-defined machine-readable output.

I think we should avoid too many top-level commands, as I fear trouble down the line with UX and discoverability.

While cabal list is already taken as a command, perhaps cabal project targets could be nice?

@philderbeast
Copy link
Collaborator Author

perhaps cabal project targets could be nice?

We have targets when there is no actual project file, when there is just a .cabal file.

@philderbeast
Copy link
Collaborator Author

  • Most importantly, the targets are not uniquely associated with building.

So a top-level targets command makes sense, not subordinate to some other command.

@philderbeast
Copy link
Collaborator Author

philderbeast commented Mar 17, 2024

What about cabal target list?

What else would we do with targets rather than list them that is not already covered by other commands taking targets? Perhaps we should pluralize it as cabal targets. We're asking them to identify themselves with this command.

@Mikolaj
Copy link
Member

Mikolaj commented May 1, 2024

Let's keep discussing this. Does anybody remember the conclusions of the discussion of the command names (related to cabal path, but also more general) we had at the open fortnightly cabal devs meeting recently?

@philderbeast philderbeast force-pushed the add/command-target branch from adeb5cf to ed75265 Compare May 1, 2024 10:52
@philderbeast philderbeast dismissed andreabedini’s stale review January 4, 2025 17:12

The suggested changes were made.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Is there a way to list project test suites?
9 participants