cabal icon indicating copy to clipboard operation
cabal copied to clipboard

"cabal new-exec ghc" should work outside of .cabal projects

Open RyanGlScott opened this issue 6 years ago • 17 comments

Nearly half my time in Haskell development is spend running ghc on individual files, outside of .cabal projects, which might have some number of dependencies. Prevously, I could just old-install all of my dependencies, point ghc to the right file, and everything would Just Work™. Unfortunately, there isn't really a new-* equivalent of this. There's cabal new-exec ghc, but that assumes that you're in a .cabal project, which usually isn't the case for me.

Similarly to how you can run cabal new-repl -b dep1,...,depn outside of a .cabal project today, I'd like to request that cabal new-exec -b dep1,...,depn ghc -- Foo.hs work outside of .cabal projects. This would operate conceptually in a similar manner to stack exec ghc.

cc @phadej

RyanGlScott avatar Feb 18 '19 10:02 RyanGlScott

See https://github.com/haskell/cabal/issues/5544#issuecomment-442106712 for a similar feature request for cabal new-repl -b dep1,...,depn Foo.hs.

RyanGlScott avatar Feb 18 '19 10:02 RyanGlScott

Because we cannot know for sure what compiler user will use in a v2-exec shell, I'll propose that we'll setup

  • HC - points to ghc or ghcjs or ...
  • HCFLAGS needed flags (to find package-db)
  • HCPKG - ghc-pkg analogue
  • and maybe others

Then, however

cabal new-exec -w /some/local/ghc -b dep1,dep2,dep3 $HC $HCPKG -- Foo.hs

wont work as environment variables are expanded too early. Maybe we could use shell aliases (when shell supports such), with hc = $HC HCFLAGS, and hc-pkg=$HCPKG $HCFLAGS.

cabal new-exec -w /some/local/ghc -b dep1,dep2,dep3 hc -- Foo.hs

Would be nice, won't it?

I think we can start implementing this by adding support for environment variables and aliases in project setting, and then generalise. Maybe it's easy to do all a once.

Supporting -z (ignore cabal.project) flag is :+1:

phadej avatar Feb 18 '19 10:02 phadej

note (please ignore if I'm stating the obvious) that cabal v2-exec ghc -- ... is already magic; the string ghc gets substituted with whatever --with-compiler is pointing to, which may not even be named ghc.

In other words, cabal v2-exec bash -- -c "ghc ..." is semantically differnet from cabal v2-exec ghc -- ...; iow, it's a very leaky abstraction. In general I consider v2-exec an anti-feature and I try to avoid it whenever I can. So I'd rather suggest to not go down the path of v2-exec but instead try to work out a workflow based on ghc package environment files and v2-install --lib which is far more flexible than having to snoehorn stuff via v2-exec, and also avoids to re-run the solver for every single invocation (also note that v2-exec is the wrong place anyway, as it does not build any goals, hence it never would be able to install any deps or run the solver).

PS: as for -z, this reminds me we need to add support for -z to v2-install and v2-update -- but I don't rememeber if I ever filed a ticket for that

hvr avatar Feb 18 '19 11:02 hvr

@RyanGlScott

Similarly to how you can run cabal new-repl -b dep1,...,depn outside of a .cabal project today, I'd like to request that cabal new-exec -b dep1,...,depn ghc -- Foo.hs work outside of .cabal projects. This would operate conceptually in a similar manner to stack exec ghc.

What's wrong with specifying the dependencies inside Foo.hs and using cabal scripts?

{- cabal: 
build-depends: base, lens, lens-aeson, aeson, text
-}

{-# LANGUAGE QuasiQuotes, OverloadedStrings #-}

import Data.Aeson.Lens
import Control.Lens
import Data.Aeson
import qualified Data.Text as T
import Data.Aeson.QQ.Simple
import Data.Text.Lens

--- ... and so on

You can run these anywhere outside a cabal project as cabal Toy.hs (or cabal new-run --with-ghc ghc-head Toy.hs).

harpocrates avatar Feb 18 '19 13:02 harpocrates

cabal new-run isn't adequate for my purposes for a couple of reasons:

  1. ~~cabal new-run uses the bytecode interpreter, whereas I need compiled code.~~ EDIT: Never mind, this is incorrect.
  2. cabal new-run doesn't support scripts with multiple files.

RyanGlScott avatar Feb 18 '19 13:02 RyanGlScott

  1. cabal new-run doesn't produce an executable file that I can run.

RyanGlScott avatar Feb 18 '19 14:02 RyanGlScott

Possibly stupid question: why not indeed run ghc inside cabal v2-exec ghc but put an appropriately-wrapped GHC at the front of PATH so that ghc (or another user-provided command that invokes it) refers to the right GHC?

michaelpj avatar Feb 19 '19 13:02 michaelpj

@michaelpj because e.g. of ghcjs.

In fact I agree with @hvr, and tried to convince Ryan to use environment files based approach. Yet, until https://github.com/haskell/cabal/issues/5559 is fixed, that's a non-argument.

phadej avatar Feb 19 '19 13:02 phadej

@phadej can you expand on that?

michaelpj avatar Feb 19 '19 13:02 michaelpj

Ok. To fix my own comment: the expanding of environment variables is not a problem.

You can create as script.sh which reads environment and run

cabal new-exec -w /some/local/ghc -b dep1,dep2,dep3 sh script.sh

Then AFAICS the problem is to add -b / --build-depends to cabal v2-exec.

phadej avatar Mar 18 '20 15:03 phadej

related with the mega issue about a possible replacement of cabal install --lib and not project centric workflows: #6481

@RyanGlScott cabal script has been improved a lot in master, maybe they could help better in your workflow nowadays

jneira avatar Mar 25 '22 06:03 jneira

What in particular has changed about scripts in master?

RyanGlScott avatar Mar 25 '22 13:03 RyanGlScott

What in particular has changed about scripts in master?

  • You can now use cabal.project fields like compiler: ghc-x.y.z in the script metadata: https://github.com/haskell/cabal/pull/7997
  • The verbosity has been reduced: https://github.com/haskell/cabal/pull/7990
  • cabal list-bin give you the path to the executable generated by the script: https://github.com/haskell/cabal/pull/7925
  • the script is now cached and executions after the first build executes it immediately
  • you can run cabal build script.hs and cabal repl script.hs and the metadata (deps for example) will be honoured: https://github.com/haskell/cabal/pull/7851

All thanks to the amazing work of @bacchanalia

and there are workarounds (create a package, which you dont want i guess :-P ) over the multiple file thing see f.e.: https://github.com/haskell/haskell-language-server/blob/1.6.1.0-hackage/install.hs

jneira avatar Mar 25 '22 13:03 jneira

Ah, I was looking at the wrong place for cabal-install's changelog (i.e., changelog.d). That's indeed quite a lot of improvements, and it would likely address limitation (3) from https://github.com/haskell/cabal/issues/5895#issuecomment-464742273. I'm not sure if limitation (2) from https://github.com/haskell/cabal/issues/5895#issuecomment-464739179 it still relevant—I'd have to build a more recent cabal-install to tell for sure.

Ultimately, the thing I care about the most (call it limitation (0), if you will) is having a mechanism for quickly compiling one-off files with a small number of Hackage dependencies that is more lightweight than setting up an entire .cabal project. At the moment, the closest thing to that mechanism appears to be cabal scripts, which is certainly an improvement over the status quo when I filed this issue.

I do think things could be even more lightweight, however. 99% of the time, all I really want to do is run something like cabal exec ghc -b lens Main.hs && ./Main. It sounds like I could accomplish the same thing by making Main.hs a cabal script, but that requires that I:

  • Remember the funny #!/bin/env cabal header that is only used in cabal scripts,
  • Remember the funny {- cabal: ... -} comment syntax that, again, is only used in cabal scripts, and
  • Having done all that, figure out how to actually run the compiled script. IIUC, with a cabal script you'd have to first use cabal list-bin to look up the executable before I can run it directly. Alternatively, I could use cabal run, but in my experience this usually incurs some extra startup time each time you run the executable. Perhaps this has changed on master—I haven't checked.

Certainly not insurmountable obstacles by any means, but there are some extra steps and cognitive overhead there. For this reason, I usually reach for cabal repl -b lens followed by :load Main.hs for most one-off experiments these days, as clunky as it may be. If cabal exec (or a comparable command) has a -b flag, I'd switch in a heartbeat.

In any case, that's my two cents on the issue. If the consensus is that what I'm asking for is an anti-pattern and that I should use cabal scripts instead, I'd be happy to close this issue. Just let me know what the intended direction of travel is.

RyanGlScott avatar Mar 25 '22 14:03 RyanGlScott

I usually reach for cabal repl -b lens followed by :load Main.hs for most one-off experiments these days, as clunky as it may be. If cabal exec (or a comparable command) has a -b flag, I'd switch in a heartbeat.

to be fair to have an uniform interface is a good thing in its own

I could use cabal run, but in my experience this usually incurs some extra startup time each time you run the executable. Perhaps this has changed on master—I haven't checked.

just checked and it continues being 10x slower so find the executable would be needed to speed up the thing: $(cabal list-bin script.hs) --script-arg

Remember the funny #!/bin/env cabal header that is only used in cabal scripts, Remember the funny {- cabal: ... -} comment syntax that, again, is only used in cabal scripts, and

The first one is optional only needed for the ./script.hs direct call so you can skip it if you are not gonna use it In the second one you exchange add a line in the file for save the -b dep in each call. If you use two or more dependencies it could be more attractive. And if you need more flags for whatever reason it would be even more.

In any case, that's my two cents on the issue. If the consensus is that what I'm asking for is an anti-pattern and that I should use cabal scripts instead

I somewhat agree with some of the hvr and other maintainers arguments on the hackiness of cabal exec so not sure if investing in would worth. For your use case, focused in use ghc directly, i would try the cabal install --lib lens --package-env=./ && ghc(i) file.hs. I linked #6481 for that reason. Hopefully the new cabal env command would let us doing a simpler cabal env lens && ghc file.hs in a safer way, not using global ghc environment files.

jneira avatar Mar 26 '22 12:03 jneira

For your use case, focused in use ghc directly, i would try the cabal install --lib lens --package-env=./ && ghc(i) file.hs.

Admittedly, I've never been able to figure out how cabal install --lib is supposed to work, due in no small part to running into #5559 any time I try to use it. If I'm reading #6481 correctly, should I think of cabal install --lib's current behavior as broken and cabal env being the thing that cabal install --lib will eventually become over time?

RyanGlScott avatar Mar 26 '22 12:03 RyanGlScott

well afaik the --package-env=./ make the command safer as it creates the ghc env file in cwd, so ghc only will honour it in the same cwd In front of any problem you can remove such file manually and regenerate it. Not ideal, sure, and the new cabal env should make the ux better

jneira avatar Mar 26 '22 12:03 jneira