Stack: "ls dependencies" should have an option to maintain the tree structure

Created on 20 Jun 2018  Β·  22Comments  Β·  Source: commercialhaskell/stack

Currently, "ls dependencies" just outputs a flat list of package names and versions of all transitive dependencies of a project. However, it would be nice to maintain the tree hierarchy of dependencies to see their parent / child relationship. On the console, the tree output could look similar to Maven's mvn dependency:tree output, but ideally there would also be an option to output to a machine readable format like JSON.

output (UUX) package index help wanted newcomer friendly enhancement

Most helpful comment

I think file:// is a good idea for local packages.
For git and hg repositories, even if we go with any URL specification, we still won't be able to show the commit in the URL. So, I'm leaning towards the second approach of additional metadata.

Maybe it could look something like this for git (and similar for hg):

{
  "name": "foo",
  "version": "1.0.0.0",
  "license": "MIT",
  "location": {
    "type": "git",
    "url": "<URL provided by user>",
    "commit": "<sha>"
  }
}

For hackage:

{
  "name": "foo",
  "version": "1.0.0.0",
  "license": "MIT",
  "location": {
    "type": "hackage",
    "url": "<computed hackage URL>"
  }
}

All 22 comments

Hello @sschuberth . That sounds like a helpful enhancement. How much of what you would like to do can you achieve with stack dot --external (perhaps with --include-base, too)?

I was thinking about (mis-)using stack dot, too, but while it basically provides the parent -> child relationship, it's a bit inconvenient to parse, also because it repeats the parent for each child. The versions are missing, too, but together with the output from stack ls dependencies I could probably get what I want. I'll give it a try.

That's all useful to know. It may be relatively straightforward to reuse the code required for the dot output. Would you be willing to contribute a PR yourself?

I'm sorry, I'm not a Haskell guy (and I don't intend to become one πŸ˜‰), I'm just trying to add basic Haskell support to the OSS Review Toolkit.

Oh, that's an interesting project. Is there anything else that would be helpful for you to achieve that, from stack?

It might also be possible for you to do what you intend using the hackage or stackage APIs directly (depending on scale), though they are quite limited. If stack meets your other requirements, then great.

Is there anything else that would be helpful for you to achieve that, from stack?

Thanks for asking. Besides listing dependencies as a tree (optionally in a machine readable format), it would be nice if the shown meta-data would not only include the name and version, but also the dependency's declared license (as an SPDX identifier). That would be the very minimum, IMO. More meta-data like the download URL would also be interesting and could be added, or simply a link to the respective hackage or stackage (REST?) APIs endpoints where more information can be gathered by the client (the program using the meta-data output) on demand.

In general, from a performance perspective it would be nice if all this meta-data could be gathered without internally downloading and unpacking the whole (source code of a) package, i.e. ideally meta-data for a package and the package itself should be separated, similar to Maven POM and JAR files.

using the hackage or stackage APIs directly

While I've found http://hackage.haskell.org/api, I didn't find something similar for stackage. Is there? Edit: I just found https://groups.google.com/forum/#!topic/stackage/9h_RA-jNz_g.

Perhaps we should broaden the issue, then. It seems like a useful addition, though it may not be a priority for the core team.

So to summarize,

  • [x] List dependencies as tree
  • [ ] List dependencies in machine readable format
  • [ ] List license as SPDX in addition to name and version

    Most cabal files don't use these identifiers; internally Cabal (which processes these files and is different to cabal-install, the provider of the command line program cabal) uses an enumeration, though, so you may wish to take a look thee.

  • [ ] List URI too

In general, from a performance perspective it would be nice if all this meta-data could be gathered without internally downloading and unpacking the whole (source code of a) package, i.e. ideally meta-data for a package and the package itself should be separated, similar to Maven POM and JAR files.

stack downloads and unpacks the hackage tarball (~100MB compressed), which contains the cabal files where the metadata is located; this might update incrementally (I've never investigated). The cabal files themselves are reachable directly (/package/:package/:cabal.cabal). So the trade-off is a space vs latency one. If you're happy with the space use, stack would be useful for this.

I didn't find something similar for stackage

I think I might have just tried the hackage endpoints when I was looking for snapshot information… or I may have checked the api calls in the debugger. I think the hackage api is most relevant, though β€” I only needed stackage because of stack snapshots. That said, it may be useful to add the hackage mirrors, if you go that way.

@borsboom β€” it may be worth supporting this enhancement to support the OSS review toolkit project mentioned earlier.

I'm very much in favour. I've often wished for an ls-dependencies that shows the tree structure. That said, I'm going to un-assign myself as it's very unlikely I'll actually be able to work on implementing it.

$ cargo tree
lefortovo v0.1.0 (file:///home/callen/work/lefortovo)
β”œβ”€β”€ bytes v0.4.8
β”‚   β”œβ”€β”€ byteorder v1.0.0
β”‚   └── iovec v0.1.2
β”‚       └── libc v0.2.42
β”œβ”€β”€ clap v2.25.0
β”‚   β”œβ”€β”€ ansi_term v0.9.0
β”‚   β”œβ”€β”€ atty v0.2.2
β”‚   β”‚   └── libc v0.2.42 (*)
β”‚   β”œβ”€β”€ bitflags v0.9.1
β”‚   β”œβ”€β”€ strsim v0.6.0
β”‚   β”œβ”€β”€ term_size v0.3.0
β”‚   β”‚   └── libc v0.2.42 (*)
β”‚   β”œβ”€β”€ textwrap v0.6.0
β”‚   β”‚   β”œβ”€β”€ term_size v0.3.0 (*)
β”‚   β”‚   └── unicode-width v0.1.4
β”‚   β”œβ”€β”€ unicode-segmentation v1.1.0
β”‚   β”œβ”€β”€ unicode-width v0.1.4 (*)
β”‚   └── vec_map v0.8.0
β”œβ”€β”€ hyper v0.12.5
β”‚   β”œβ”€β”€ bytes v0.4.8 (*)
β”‚   β”œβ”€β”€ futures v0.1.21
β”‚   β”œβ”€β”€ futures-cpupool v0.1.8
β”‚   β”‚   β”œβ”€β”€ futures v0.1.21 (*)
β”‚   β”‚   └── num_cpus v1.6.2
β”‚   β”‚       └── libc v0.2.42 (*)
β”‚   β”œβ”€β”€ h2 v0.1.10
β”‚   β”‚   β”œβ”€β”€ byteorder v1.0.0 (*)
β”‚   β”‚   β”œβ”€β”€ bytes v0.4.8 (*)
β”‚   β”‚   β”œβ”€β”€ fnv v1.0.6
β”‚   β”‚   β”œβ”€β”€ futures v0.1.21 (*)
β”‚   β”‚   β”œβ”€β”€ http v0.1.7
β”‚   β”‚   β”‚   β”œβ”€β”€ bytes v0.4.8 (*)
β”‚   β”‚   β”‚   β”œβ”€β”€ fnv v1.0.6 (*)
β”‚   β”‚   β”‚   └── itoa v0.4.1
β”‚   β”‚   β”œβ”€β”€ indexmap v1.0.1
β”‚   β”‚   β”œβ”€β”€ log v0.4.3
β”‚   β”‚   β”‚   └── cfg-if v0.1.4
β”‚   β”‚   β”œβ”€β”€ slab v0.4.0
β”‚   β”‚   β”œβ”€β”€ string v0.1.0
β”‚   β”‚   └── tokio-io v0.1.7
β”‚   β”‚       β”œβ”€β”€ bytes v0.4.8 (*)
β”‚   β”‚       β”œβ”€β”€ futures v0.1.21 (*)
β”‚   β”‚       └── log v0.4.3 (*)
β”‚   β”œβ”€β”€ http v0.1.7 (*)
β”‚   β”œβ”€β”€ httparse v1.2.3
β”‚   β”œβ”€β”€ iovec v0.1.2 (*)
β”‚   β”œβ”€β”€ itoa v0.4.1 (*)
β”‚   β”œβ”€β”€ log v0.4.3 (*)
β”‚   β”œβ”€β”€ net2 v0.2.33
β”‚   β”‚   β”œβ”€β”€ cfg-if v0.1.4 (*)
β”‚   β”‚   └── libc v0.2.42 (*)
β”‚   β”œβ”€β”€ time v0.1.37
β”‚   β”‚   └── libc v0.2.42 (*)
β”‚   β”œβ”€β”€ tokio v0.1.7
β”‚   β”‚   β”œβ”€β”€ futures v0.1.21 (*)
β”‚   β”‚   β”œβ”€β”€ mio v0.6.14
β”‚   β”‚   β”‚   β”œβ”€β”€ iovec v0.1.2 (*)
β”‚   β”‚   β”‚   β”œβ”€β”€ lazycell v0.6.0
β”‚   β”‚   β”‚   β”œβ”€β”€ libc v0.2.42 (*)
β”‚   β”‚   β”‚   β”œβ”€β”€ log v0.4.3 (*)
β”‚   β”‚   β”‚   β”œβ”€β”€ net2 v0.2.33 (*)
β”‚   β”‚   β”‚   └── slab v0.4.0 (*)
β”‚   β”‚   β”œβ”€β”€ tokio-executor v0.1.2
β”‚   β”‚   β”‚   └── futures v0.1.21 (*)
β”‚   β”‚   β”œβ”€β”€ tokio-fs v0.1.1
β”‚   β”‚   β”‚   β”œβ”€β”€ futures v0.1.21 (*)
β”‚   β”‚   β”‚   β”œβ”€β”€ tokio-io v0.1.7 (*)
β”‚   β”‚   β”‚   └── tokio-threadpool v0.1.4
β”‚   β”‚   β”‚       β”œβ”€β”€ crossbeam-deque v0.3.1
β”‚   β”‚   β”‚       β”‚   β”œβ”€β”€ crossbeam-epoch v0.4.3
β”‚   β”‚   β”‚       β”‚   β”‚   β”œβ”€β”€ arrayvec v0.4.7
β”‚   β”‚   β”‚       β”‚   β”‚   β”‚   └── nodrop v0.1.12
β”‚   β”‚   β”‚       β”‚   β”‚   β”œβ”€β”€ cfg-if v0.1.4 (*)
β”‚   β”‚   β”‚       β”‚   β”‚   β”œβ”€β”€ crossbeam-utils v0.3.2
β”‚   β”‚   β”‚       β”‚   β”‚   β”‚   └── cfg-if v0.1.4 (*)
β”‚   β”‚   β”‚       β”‚   β”‚   β”œβ”€β”€ lazy_static v1.0.1
β”‚   β”‚   β”‚       β”‚   β”‚   β”œβ”€β”€ memoffset v0.2.1
β”‚   β”‚   β”‚       β”‚   β”‚   └── scopeguard v0.3.3
β”‚   β”‚   β”‚       β”‚   └── crossbeam-utils v0.3.2 (*)
β”‚   β”‚   β”‚       β”œβ”€β”€ futures v0.1.21 (*)
β”‚   β”‚   β”‚       β”œβ”€β”€ log v0.4.3 (*)
β”‚   β”‚   β”‚       β”œβ”€β”€ num_cpus v1.6.2 (*)
β”‚   β”‚   β”‚       β”œβ”€β”€ rand v0.4.2
β”‚   β”‚   β”‚       β”‚   └── libc v0.2.42 (*)
β”‚   β”‚   β”‚       └── tokio-executor v0.1.2 (*)
β”‚   β”‚   β”‚   [dev-dependencies]
β”‚   β”‚   β”‚   └── tokio-io v0.1.7 (*)
β”‚   β”‚   β”œβ”€β”€ tokio-io v0.1.7 (*)
β”‚   β”‚   β”œβ”€β”€ tokio-reactor v0.1.2
β”‚   β”‚   β”‚   β”œβ”€β”€ futures v0.1.21 (*)
β”‚   β”‚   β”‚   β”œβ”€β”€ log v0.4.3 (*)
β”‚   β”‚   β”‚   β”œβ”€β”€ mio v0.6.14 (*)
β”‚   β”‚   β”‚   β”œβ”€β”€ slab v0.4.0 (*)
β”‚   β”‚   β”‚   β”œβ”€β”€ tokio-executor v0.1.2 (*)
β”‚   β”‚   β”‚   └── tokio-io v0.1.7 (*)
β”‚   β”‚   β”œβ”€β”€ tokio-tcp v0.1.0
β”‚   β”‚   β”‚   β”œβ”€β”€ bytes v0.4.8 (*)
β”‚   β”‚   β”‚   β”œβ”€β”€ futures v0.1.21 (*)
β”‚   β”‚   β”‚   β”œβ”€β”€ iovec v0.1.2 (*)
β”‚   β”‚   β”‚   β”œβ”€β”€ mio v0.6.14 (*)
β”‚   β”‚   β”‚   β”œβ”€β”€ tokio-io v0.1.7 (*)
β”‚   β”‚   β”‚   └── tokio-reactor v0.1.2 (*)
β”‚   β”‚   β”œβ”€β”€ tokio-threadpool v0.1.4 (*)
β”‚   β”‚   β”œβ”€β”€ tokio-timer v0.2.4
β”‚   β”‚   β”‚   β”œβ”€β”€ futures v0.1.21 (*)
β”‚   β”‚   β”‚   └── tokio-executor v0.1.2 (*)
β”‚   β”‚   └── tokio-udp v0.1.1
β”‚   β”‚       β”œβ”€β”€ bytes v0.4.8 (*)
β”‚   β”‚       β”œβ”€β”€ futures v0.1.21 (*)
β”‚   β”‚       β”œβ”€β”€ log v0.4.3 (*)
β”‚   β”‚       β”œβ”€β”€ mio v0.6.14 (*)
β”‚   β”‚       β”œβ”€β”€ tokio-codec v0.1.0
β”‚   β”‚       β”‚   β”œβ”€β”€ bytes v0.4.8 (*)
β”‚   β”‚       β”‚   β”œβ”€β”€ futures v0.1.21 (*)
β”‚   β”‚       β”‚   └── tokio-io v0.1.7 (*)
β”‚   β”‚       β”œβ”€β”€ tokio-io v0.1.7 (*)
β”‚   β”‚       └── tokio-reactor v0.1.2 (*)
β”‚   β”œβ”€β”€ tokio-executor v0.1.2 (*)
β”‚   β”œβ”€β”€ tokio-io v0.1.7 (*)
β”‚   β”œβ”€β”€ tokio-reactor v0.1.2 (*)
β”‚   β”œβ”€β”€ tokio-tcp v0.1.0 (*)
β”‚   β”œβ”€β”€ tokio-timer v0.2.4 (*)
β”‚   └── want v0.0.5
β”‚       β”œβ”€β”€ futures v0.1.21 (*)
β”‚       β”œβ”€β”€ log v0.4.3 (*)
β”‚       └── try-lock v0.2.2
β”œβ”€β”€ hyper-tls v0.2.1
β”‚   β”œβ”€β”€ bytes v0.4.8 (*)
β”‚   β”œβ”€β”€ futures v0.1.21 (*)
β”‚   β”œβ”€β”€ hyper v0.12.5 (*)
β”‚   β”œβ”€β”€ native-tls v0.1.4
β”‚   β”‚   └── openssl v0.9.14
β”‚   β”‚       β”œβ”€β”€ bitflags v0.9.1 (*)
β”‚   β”‚       β”œβ”€β”€ foreign-types v0.2.0
β”‚   β”‚       β”œβ”€β”€ lazy_static v0.2.8
β”‚   β”‚       β”œβ”€β”€ libc v0.2.42 (*)
β”‚   β”‚       └── openssl-sys v0.9.14
β”‚   β”‚           └── libc v0.2.42 (*)
β”‚   β”‚           [build-dependencies]
β”‚   β”‚           β”œβ”€β”€ gcc v0.3.51
β”‚   β”‚           └── pkg-config v0.3.9
β”‚   β”œβ”€β”€ tokio-io v0.1.7 (*)
β”‚   └── tokio-tls v0.1.4
β”‚       β”œβ”€β”€ futures v0.1.21 (*)
β”‚       β”œβ”€β”€ native-tls v0.1.4 (*)
β”‚       β”œβ”€β”€ tokio-core v0.1.17
β”‚       β”‚   β”œβ”€β”€ bytes v0.4.8 (*)
β”‚       β”‚   β”œβ”€β”€ futures v0.1.21 (*)
β”‚       β”‚   β”œβ”€β”€ iovec v0.1.2 (*)
β”‚       β”‚   β”œβ”€β”€ log v0.4.3 (*)
β”‚       β”‚   β”œβ”€β”€ mio v0.6.14 (*)
β”‚       β”‚   β”œβ”€β”€ scoped-tls v0.1.0
β”‚       β”‚   β”œβ”€β”€ tokio v0.1.7 (*)
β”‚       β”‚   β”œβ”€β”€ tokio-executor v0.1.2 (*)
β”‚       β”‚   β”œβ”€β”€ tokio-io v0.1.7 (*)
β”‚       β”‚   β”œβ”€β”€ tokio-reactor v0.1.2 (*)
β”‚       β”‚   └── tokio-timer v0.2.4 (*)
β”‚       └── tokio-io v0.1.7 (*)
β”œβ”€β”€ pretty_env_logger v0.2.3
β”‚   β”œβ”€β”€ ansi_term v0.11.0
β”‚   β”œβ”€β”€ env_logger v0.5.10
β”‚   β”‚   β”œβ”€β”€ atty v0.2.2 (*)
β”‚   β”‚   β”œβ”€β”€ humantime v1.1.1
β”‚   β”‚   β”‚   └── quick-error v1.2.2
β”‚   β”‚   β”œβ”€β”€ log v0.4.3 (*)
β”‚   β”‚   β”œβ”€β”€ regex v1.0.1
β”‚   β”‚   β”‚   β”œβ”€β”€ aho-corasick v0.6.5
β”‚   β”‚   β”‚   β”‚   └── memchr v2.0.1
β”‚   β”‚   β”‚   β”‚       └── libc v0.2.42 (*)
β”‚   β”‚   β”‚   β”œβ”€β”€ memchr v2.0.1 (*)
β”‚   β”‚   β”‚   β”œβ”€β”€ regex-syntax v0.6.1
β”‚   β”‚   β”‚   β”‚   └── ucd-util v0.1.1
β”‚   β”‚   β”‚   β”œβ”€β”€ thread_local v0.3.5
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ lazy_static v1.0.1 (*)
β”‚   β”‚   β”‚   β”‚   └── unreachable v1.0.0
β”‚   β”‚   β”‚   β”‚       └── void v1.0.2
β”‚   β”‚   β”‚   └── utf8-ranges v1.0.0
β”‚   β”‚   └── termcolor v0.3.6
β”‚   └── log v0.4.3 (*)
└── tokio-core v0.1.17 (*)

cargo tree has nice output. Cf. https://github.com/sfackler/cargo-tree

I think stack ls dependencies needs to go unchanged as other tools reuse the output for things like license checking, but a --tree flag or similar would be nice.

And so do Maven (mvn dependency:tree), Gradle (gradle dependencies), and several other package managers / dependency tools / build systems / whatever you call them...

NPM does as well:

$ npm ls | head
[email protected] /home/christopher/dev/proj
β”œβ”€β”€ [email protected]
β”œβ”€β”¬ [email protected]
β”‚ β”œβ”€β”¬ [email protected]
β”‚ β”‚ β”œβ”€β”¬ [email protected]
β”‚ β”‚ β”‚ β”œβ”€β”€ [email protected]
β”‚ β”‚ β”‚ β”œβ”€β”€ [email protected]
β”‚ β”‚ β”‚ β”œβ”€β”¬ [email protected]
β”‚ β”‚ β”‚ β”‚ └── [email protected]
β”‚ β”‚ β”‚ β”œβ”€β”¬ [email protected]

@sschuberth I'm sure you've seen stack ls dependencies --license? It's not SPDX format afaik, but may still be a useful interim solution.

$ stack ls dependencies --separator=- --license | head
Cabal-BSD3
HTTP-BSD3
HUnit-BSD3
HsOpenSSL-PublicDomain
JuicyPixels-BSD3
MonadRandom-OtherLicense
QuickCheck-BSD3
RSA-BSD3
SHA-BSD3
StateVar-BSD3

@borsboom Why does --license discard the version number?

The problem with NPM is that it can only show the tree for installed dependencies. But ideally I'd like to see the list / tree of dependencies before I even install them. Maven and Gradle can do that just fine.

Thanks @creichert, I've seen stack ls dependencies --license, but I've already implemented a solution that uses a mix of calling stack and parsing *.cabal files directly from Hackage.

I was exploring the newcomer friendly tag and came across this. It looks interesting and also seem like nobody is try to implement this feature. I am going to take a dig at implementing this, please give me a shout if somebody is already doing it.

@akshaymankar For machine readable output I suggest json (unless @sschuberth has a better suggestion). It might be simple enough to implement some aeson instances and then plumb them in.

SPDX is well defined.

For the URI, I'm not sure what to go with. I don't really understand the use case, so perhaps @sschuberth can explain.

JSON is indeed fine from my perspective. But if e.g. more common in the Haskell community, YAML could be another option.

URI / URL was referring to the direct download location for the source code to the package. At the example of the memory package version 0.14.11, the URL would be https://hackage.haskell.org/package/memory-0.14.11/memory-0.14.11.tar.gz. The use-case is to perform automated license checks by scanning the source code of the package.

The URL in the JSON doesn't really work, because a package could either be from hackage, a local package, an archive specified by URL or a git repository. So, I guess a URL to a will not always be possible.

I am not sure what the best possible format for this would be. @sschuberth, do you have any suggestions/preferences?

@dbaynard I also couldn't find a place where stack figures out URL from PakcageLocation object. Can you please point me to it, so I don't invent the logic again.

@akshaymankar what's the problem to use file:// for local packages and git:// for git?

And I think you'll need a custom function for that, I don't think there's already some code doing that.

I was also about to say a URL should work:

  • a package could either be from hackage -> Use a http(s) URL to hackage.haskell.org
  • a local package -> Use a file:// URL
  • an archive specified by URL -> Use a http(s) URL to a non-hackage place
  • a git repository -> Here's the problem. Git supports a variety of transports, and in particular not all Git repos can be actually accessed via the git:// protocol (as that protocol offers no authentication). So either we come up with a convention that says that http(s) URLs that refer to Git repos have to end in .git (which is the common case anyway), or we introduce some additional meta-data field to indicate which type of URL this is. If packages are never going to be hosted in some other VCS than Git, the former approach would probably work fine. But e.g. for Mercurial there is no convention that the http(s) URL to a Mercurial repo should end in .hg, and we'd need the second approach of additional meta-data.

@dbaynard I also couldn't find a place where stack figures out URL from PakcageLocation object. Can you please point me to it, so I don't invent the logic again.

And I think you'll need a custom function for that, I don't think there's already some code doing that.

I think @qrilka was answering the question you asked of me.

I think file:// is a good idea for local packages.
For git and hg repositories, even if we go with any URL specification, we still won't be able to show the commit in the URL. So, I'm leaning towards the second approach of additional metadata.

Maybe it could look something like this for git (and similar for hg):

{
  "name": "foo",
  "version": "1.0.0.0",
  "license": "MIT",
  "location": {
    "type": "git",
    "url": "<URL provided by user>",
    "commit": "<sha>"
  }
}

For hackage:

{
  "name": "foo",
  "version": "1.0.0.0",
  "license": "MIT",
  "location": {
    "type": "hackage",
    "url": "<computed hackage URL>"
  }
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

jwaldmann picture jwaldmann  Β·  4Comments

s5k6 picture s5k6  Β·  3Comments

Toxaris picture Toxaris  Β·  4Comments

igrep picture igrep  Β·  3Comments

bitemyapp picture bitemyapp  Β·  3Comments