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

Environment files get written to $TMP when using cabal scripts #6999

Open
georgefst opened this issue Jul 30, 2020 · 13 comments
Open

Environment files get written to $TMP when using cabal scripts #6999

georgefst opened this issue Jul 30, 2020 · 13 comments

Comments

@georgefst
Copy link

Running cabal run --write-ghc-environment-files=always on a cabal script doesn't actually write a GHC environment file.

It would be great if it did, since it would mean that after the first run, straightforward calls to ghc and runghc would just work, and haskell-language-server, for one, would be able to load the file without requiring any setup.

Is there any fundamental reason why this shouldn't just work?

I've actually ended up writing a hacky little script to parse the build-depends field and pass all the package names to cabal install --package-env . --lib, but it would be nice to have a proper solution.

@fgaz
Copy link
Member

fgaz commented Jul 30, 2020

Does adding --package-env=/absolute/path/to/env work? It might be possible that cabal is writing the env file in the temporary "fake package" directory

@georgefst
Copy link
Author

--package-env doesn't seem to be a valid flag to any command other than install.

But you're right that the environment file does get written to disk, albeit temporarily. I've now scripted a slightly less hacky solution based on recursively watching /tmp and copying the file out.

Perhaps the solution to #6977 will solve this as a side-effect...

@fgaz
Copy link
Member

fgaz commented Jul 30, 2020

Ok so this is an actual bug (I don't think anyone wants cabal to put the env file in /tmp by default)

@fgaz fgaz added the type: bug label Jul 30, 2020
@fgaz fgaz changed the title Cabal scripts and environment files Environment files get written to /tmp when using cabal scripts Jul 30, 2020
@fgaz fgaz changed the title Environment files get written to /tmp when using cabal scripts Environment files get written to $TMP when using cabal scripts Jul 30, 2020
@bacchanalia
Copy link
Collaborator

this issue may have been resolved by #7851, since script builds are now cached on disk

@jneira
Copy link
Member

jneira commented Feb 15, 2022

hmm maybe the title should be changed but iiuc the issue goals were:

  1. being able to load the script with a plain ghci/ghc
  2. to make easier hls load the script

Afaik #7851 does not resolve 1 but it does for 2, as a simple cabal cradle would be able to load it with cabal repl script.hs
@georgefst what do you think about closing?

@georgefst
Copy link
Author

I'll need to build Cabal HEAD and investigate in the coming days, to see what the ergonomics are now like.

@georgefst
Copy link
Author

I don't think this can really be closed, as the issue still stands.

But the improvements to scripts do make it a lesser issue. Indeed, for my use case, the desire to generate environment files entirely disappears, since the benefits of calling ghc/ghci directly no longer apply:

  • We can get a REPL with the right environment using cabal repl
  • build and run take only a fraction of a second when no work needs to be done

So thanks @bacchanalia!

@georgefst
Copy link
Author

Incidentally, I've found that the existence of a cabal.project file (say, a totally empty one) is enough to avoid this behaviour. Naively, I would've thought that scripts ignore cabal.project files by default, but this is evidently not the case. Note that when a cabal.project file is picked up, dist-newstyle also ends up in the current directory, rather than /tmp.

@bacchanalia
Copy link
Collaborator

I'm glad to hear my improvements help you!

I did some testing, and it looks now like the only thing written to dist-newtype when a cabal.project file is present is dist-newstyle/cache/config. The reason we read the cabal.project file is to determine whether a given target passed to cabal run is a regular target or a script. In theory in unambiguous cases (the script path contains characters that are not valid in a regular target) we could bypass reading the project file, but we don't do so currently.

The environment file gets written to the cache dir now even with cabal.project file present, so in some sense the script improvements made things worse in that the work around no longer works.

This looks to me like another issue that will only be resolved when #6977 is.

@georgefst
Copy link
Author

The environment file gets written to the cache dir now even with cabal.project file present, so in some sense the script improvements made things worse in that the work around no longer works.

Additionally, as far as I can tell based on recursive file watching, cabal-3.8.1.0 run Script.hs --write-ghc-environment-files=always no longer results in an environment file being written anywhere in /tmp (or anywhere else I can think of i.e. the current directory), which unfortunately breaks my other workaround.

@jneira
Copy link
Member

jneira commented Jan 4, 2023

Hi, nowadays cabal run build products are placed in $CABALDIR/script-builds/$hash, in my tests the env file is also there:

PS D:\dev\ws\haskell\cabal-test\scripts> cabal run Say.hs --write-ghc-environment-files=always -v
this build was affected by the following (project) config files:
- D:\dev\ws\haskell\cabal-test\cabal.project
creating
D:\cabal\script-builds\a5e79fcbc4de03127e08e94c3b4ab5518e22c4dddb37fdabfaddf3ac6701fa2b
...........
PS D:\dev\ws\haskell\cabal-test\scripts> ls D:\cabal\script-builds\a5e79fcbc4de03127e08e94c3b4ab5518e22c4dddb37fdabfaddf3ac6701fa2b


    Directorio: D:\cabal\script-builds\a5e79fcbc4de03127e08e94c3b4ab5518e22c4dddb37fdabfaddf3ac6701fa2b


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        11/12/2021     17:24                dist-newstyle
-a----        04/01/2023     21:55            628 .ghc.environment.x86_64-mingw32-8.10.7
-a----        04/01/2023     15:40            289 fake-package.cabal
-a----        04/01/2023     21:55             43 scriptlocation

georgefst added a commit to georgefst/hs-scripts that referenced this issue Jan 5, 2023
Thanks to useful pointer from haskell/cabal#6999 (comment).

File watching actually doesn't work, as the same hash is reused for the same script (based on location?) even after edits. This is much cleaner anyway.
@georgefst
Copy link
Author

Thanks @jneira! That gets me back to having a working workaround:

#!/usr/bin/env cabal
{-# LANGUAGE GHC2021 #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoMonomorphismRestriction #-}
{-# OPTIONS_GHC -Wall #-}
{-# OPTIONS_GHC -threaded #-}

{- cabal:
build-depends:
    base,
    bytestring,
    filepath-bytestring,
    rawfilepath,
    unix,
-}

{- | This will find a GHC environment file created by a cabal script, and copy it in to the current directory.
It is the workaround mentioned [here](https://github.com/haskell/cabal/issues/6999).
-}
module Main (main) where

import Data.ByteString.Char8 (
    isInfixOf,
    isPrefixOf,
    lines,
    putStrLn,
    unlines,
 )
import Data.ByteString.RawFilePath (readFile, writeFile)
import Data.Foldable (find, for_)
import RawFilePath (listDirectory, proc, readProcessWithExitCode)
import System.Exit (exitFailure)
import System.FilePath.ByteString (takeFileName, (</>))
import System.Posix.ByteString (getArgs)
import Prelude hiding (lines, putStrLn, readFile, unlines, writeFile)

main :: IO ()
main = do
    scriptFile <-
        getArgs >>= \case
            [scriptFile] -> pure scriptFile
            _ -> putStrLn "Provide a cabal script file" >> exitFailure
    (_exitCode, out, _err) <-
        readProcessWithExitCode $
            proc "cabal" ["build", "-v", scriptFile, "--write-ghc-environment-files=always"]
    p <- case find ("script-builds" `isInfixOf`) $ lines out of -- TODO this may be a brittle heuristic
        Just p -> pure p
        Nothing -> putStrLn "Failed to parse cabal output" >> exitFailure
    envFiles <- filter (".ghc.environment." `isPrefixOf`) <$> listDirectory p
    if null envFiles
        then putStrLn "No environment files found" >> exitFailure
        else putStrLn "Found environment files:"
    for_ envFiles \envFile -> do
        putStrLn envFile
        contents <- readFile $ p </> envFile
        writeFile (takeFileName envFile)
            . unlines
            . filter (not . ("package-db dist-newstyle" `isPrefixOf`))
            $ lines contents

@georgefst
Copy link
Author

But the improvements to scripts do make it a lesser issue. Indeed, for my use case, the desire to generate environment files entirely disappears, since the benefits of calling ghc/ghci directly no longer apply:

Actually, this isn't quite true. Environment files are still the easiest way to get Cabal scripts to load in HLS: haskell/haskell-language-server#111.

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

No branches or pull requests

4 participants