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 getRuntimeGhcLibDir and getRuntimeGhcVersion, via runGhc #207

Merged
merged 5 commits into from
Jul 1, 2020

Conversation

lukel97
Copy link
Contributor

@lukel97 lukel97 commented Jun 14, 2020

This PR moves the logic in ghcide/hls of getting the runtime ghc library directory and version into hie-bios, and in doing so moves the responsibility to the cradle

@lukel97
Copy link
Contributor Author

lukel97 commented Jun 14, 2020

Closes #151

Copy link
Collaborator

@fendor fendor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the design!
Since this is a big breaking change that is very important to get right, we will wait for some feedback before merging it.

tests/BiosTests.hs Show resolved Hide resolved
src/HIE/Bios/Cradle.hs Outdated Show resolved Hide resolved
, getGhcPath =
-- We're being lazy here and just returning the ghc path for the
-- first non-none cradle. This shouldn't matter in practice: all
-- sub cradles should be using the same ghc version!
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be "must"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about that, the multi combinations could give us as first ghc a wrong one. F.e. if you use a direct cradle it can give us the ghc on path but stack or cabal the ghc configured for the project.
However it is probable that the direct cradle could work with the build tool one but not the other way around.
Would be not feasible return a list of unique ghc's and let the client code pick the first one or handle the list in other way? i can imagine you could choose the newer ghc, f.e.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ghc libdir should be the same ghc version for each sub-cradle though. This would mean it doesn't matter which one is used to load the ghc session
The only case where it would make a difference would be if two sub-cradles actually had different ghc versions, and then returned two different ghc libdirs of different ghc versions.
But then in that case ghcide/hls wouldn't be able to load them both anyway!

I guess we could return a list anyway for ghcide/hls and they could just called head. But I think it would be nice to handle as much of this stuff in hie-bios as possible

Copy link
Member

@jneira jneira Jun 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh i completly missed that comment, sorry
Actually i am not sure if return a list without knowing which cradle is the source of each ghc version is a good idea.
I dont have an alternative that it is simple, maybe return an error or warning if not all ghcs are the same version? 🤔

If you think that this could be merged as is, it will be fine for me too

-- hie-bios compiled on, so we avoid using it unless we have to!
getGhcLibDir :: Cradle a -> IO FilePath
getGhcLibDir cradle =
fmap (fromMaybe Paths.libdir) $ runMaybeT $ fromNix <|> fromCradle
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we know what should take precedence?
In my experience with nix, ghc is wrapped to give the correct libdir.
The order should be in my opinion: Cradle Ghc -> Nix env var -> Paths.libdir

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As ghc-paths will be wrong in the general case maybe we should remove it and returns IO (Maybe Filepath) to make clear we cant return it for all cases in a reliable way and force the client code handle that possibility (maybe using ghc-paths at the end!)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My aim here was to mirror what currently happens inside ghcide/hls, and I presume this nix env var might be used for the direct cradles where it's calling ghc without cabal or stack: https://github.com/digital-asset/ghcide/blob/9e984b56cfb62a83829975210ce3f14c668912f6/docs/Setup.md#issues-with-nix

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the client code is just going to end up calling ghc-paths then shouldn't we just do it here as well? We might want to rename getGhcLibDir to something more honest though like mostlyAccurateGuessGhcLibDir, since the only scenario where ghc-paths doesn't work will be when moving binaries across machines. On the other hand, if we are using static binaries then we would really want to error here instead of silently giving the wrong path. But then we would end up splitting the libdir logic even further back into the clients

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well i suppose client code could do a final sanity check executing the ghc returned by the cradle (--numeric-version ?) and catch the error. Not elegant but doable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jneira getGhcOnPathLibDir should now do a check of the ghc version beforehand now

@@ -32,29 +31,25 @@ import HIE.Bios.Flags
----------------------------------------------------------------

-- | Converting the 'Ghc' monad to the 'IO' monad. All exceptions are ignored and logged.
withGHC :: FilePath -- ^ A target file displayed in an error message.
withGHC :: Cradle c -- ^ The cradle to use for resolving the lib dir
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not entirely sure I like this API. The ghc session is not dependent on the cradle, only on the libdir.
To me, this usage sends a signal that the cradle is somehow used in the session and needs to be known before launching the session. (my opinion is debatable here)
(btw, neither ghcide/hls nor haskell-ide-engine uses it)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'm happy enough to change this to the libdir, and then the users of this API can just run the cradle themselves to extract it

Copy link
Contributor Author

@lukel97 lukel97 Jun 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Come to think of it, if we make the user pass in the libdir then there's no point really in having withGhcT, since all it did was handle the libdir part

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also advocate for just removing withGHC as well, since it's not used by ghcide/hls and is a carryover from ghc-mod

Copy link
Collaborator

@fendor fendor Jun 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I agree, let's remove it.

src/HIE/Bios/Ghc/Check.hs Outdated Show resolved Hide resolved
src/HIE/Bios/Ghc/Check.hs Outdated Show resolved Hide resolved
, getGhcPath =
-- We're being lazy here and just returning the ghc path for the
-- first non-none cradle. This shouldn't matter in practice: all
-- sub cradles should be using the same ghc version!
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about that, the multi combinations could give us as first ghc a wrong one. F.e. if you use a direct cradle it can give us the ghc on path but stack or cabal the ghc configured for the project.
However it is probable that the direct cradle could work with the build tool one but not the other way around.
Would be not feasible return a list of unique ghc's and let the client code pick the first one or handle the list in other way? i can imagine you could choose the newer ghc, f.e.

src/HIE/Bios/Cradle.hs Outdated Show resolved Hide resolved
@@ -326,6 +340,7 @@ biosCradle wdir biosCall biosDepsCall =
, cradleOptsProg = CradleAction
{ actionName = Types.Bios
, runCradle = biosAction wdir biosCall biosDepsCall
, getGhcPath = findExecutable "ghc"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Afaik the dependencies of the bios cradle is a responsability of the bios program (see biosDeps): should not be the ghc used by that bios program returned by itself? Or at least being able to take in account it and fallback to the ghc on path.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way for a bios program to return the path to the GHC it intends to use? From my understanding it just returns newline separated arguments in stdout

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may still want define the libdir, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if there will be a good way to have the bios program pass the library directory back, seeing as programs are only really aware of filepaths being passed to them, either through the shell arguments or by the HIE_BIOS_ARG environment variable. Also I can't really imagine how the bios programs are going to return the library directory in the first place.
With that said, the current way haskell-language-server works out the project version is by calling ghc on the path anyway, and this should fall back to the hardcoded ghc-paths should that fail. So I don't think there would be much of a difference.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, an option could be add another optional config option like the existing one for dependencies:

cradle:
  bios:
    program: ./flags.sh
    dependency-program: ./dependency.sh
    ghc-path-program: ./ghc-path.sh

But i am not aware of a bios program that needs it so i would merge this without it and add it on concrete user request afterwards

@jneira
Copy link
Member

jneira commented Jun 14, 2020

@bubba thanks a lot for this, not only it is necessary for the demanded precompiled ide binaries but i think it is a great feature for hie-bios standalone.
As @fendor noted we have to be specially careful, sorry if i have been too fuzzy 😅

@lukel97 lukel97 force-pushed the ghc-path-libdir branch 5 times, most recently from 6df34cb to 12f4f2f Compare June 16, 2020 12:39
src/HIE/Bios/Cradle.hs Outdated Show resolved Hide resolved
@lukel97 lukel97 changed the title Add getGhcPath and getGhcLibDir Add getRuntimeGhcLibDir and getRuntimeGhcVersion Jun 21, 2020
@jneira jneira self-requested a review June 23, 2020 22:22
Copy link
Member

@jneira jneira left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've tested in windows with some cabal and stack projects and it has worked in all cases.
Really a great job.

EDIT: Well, that was difficult but the result for ghc and its bios cradle is strange:

GHC library directory: Just "D:\\bin\\stack\\x86_64-windows\\ghc-8.6.5\\lib"

(ghc on path is 8.8.3 and hie-bios was installed with 8.10.1)

EDIT2: I've jut run it again in ghc and i've ot another result so surely i did something wrong

PS D:\dev\ws\haskell\ghc> hie-bios debug .\ghc\Main.hs
Root directory:        D:\dev\ws\haskell\ghc
Component directory:   D:\dev\ws\haskell\ghc
GHC options:           "-v "
GHC library directory: Nothing
Config Location:       D:\dev\ws\haskell\ghc\hie.yaml
Cradle:                Cradle {cradleRootDir = "D:\\dev\\ws\\haskell\\ghc", cradleOptsProg = CradleAction: Bios}
Dependencies:

@lukel97
Copy link
Contributor Author

lukel97 commented Jun 25, 2020

For posterity, the issue with hie-bios failing on the ghc source tree itself was due to the sanity check that the ghc versions matched when fetching the library directory for GHC on the path. We just removed this in the end, since we now have a separate function for checking the project version.

And upon further reflection, I think hie-bios shouldn't try to act smart here – the runtime ghc version for a bios or plain/default cradle should always just consult ghc on the PATH. Not ghc-8.x.y with whatever version hie-bios was compiled with.
This will only get confusing, and the version of what hie-bios was compiled with really shouldn't determine the version of ghc selected since we will be using this logic in haskell-langugage-server-wrapper, which is compiled with an arbitrary ghc version!

@jneira
Copy link
Member

jneira commented Jun 25, 2020

Not ghc-8.x.y with whatever version hie-bios was compiled with.

Yeah, moreover now it is named getRuntimeGhcLibDir, i would remove the fromGhcPaths alternative

@lukel97 lukel97 changed the title Add getRuntimeGhcLibDir and getRuntimeGhcVersion Add getRuntimeGhcLibDir and getRuntimeGhcVersion, via runGhc Jun 25, 2020
@lukel97 lukel97 force-pushed the ghc-path-libdir branch 2 times, most recently from 29d2d3a to 126bfd8 Compare June 26, 2020 12:39
This is part of the work towards getting static binaries properly
working in ghcide and haskell-language-server. To do this we need to be
able to fetch the ghc library directory on the fly, as it will change
with each ghc installation.

getRuntimeGhcLibDir is what client code should use to obtain the libdir,
and searches three places, falling back in this order:
1. the NIX_GHC_LIBDIR environment variable
2. ghc --print-libdir for whatever ghc the cradle is using
3. the libdir baked into ghc-paths

We want to avoid using ghc-paths if possible since it bakes the path
into the binary, which means it isn't portable in the static binary
sense. So if True is passed for the second argument, it will avoid
falling back on ghc-paths, which can be useful when distributing static
binaries etc.

From the library directory we can also get the path to the ghc binary
used, which we can then use to get the cradle ghc version via
getRuntimeGhcVersion
@lukel97 lukel97 force-pushed the ghc-path-libdir branch 6 times, most recently from 45577a8 to 482d8af Compare June 26, 2020 20:26
@lukel97
Copy link
Contributor Author

lukel97 commented Jun 26, 2020

Was trying to figure out why appveyor was failing with stack and ghc 8.10.1 so I set up GitHub CI to see if would make a difference but it didn't. I think there's something funny going on with that combination anyway so I've just commented out those tests again. I've left in the GitHub CI anyway, should be handy

Copy link
Collaborator

@fendor fendor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good!

Needs a follow-up PR so that bios cradle can also specify its libdir somehow.

tests/BiosTests.hs Outdated Show resolved Hide resolved
tests/BiosTests.hs Outdated Show resolved Hide resolved
lukel97 added 3 commits June 28, 2020 16:06
Now we can just call `cabal exec ghc -- --numeric-version` directly
without the need for guessing where the ghc binary lies.
Since the functions are now "getRuntimeGhc*", it doesn't make sense to
try and use anything compiletime related
This avoids clashes with runGhc exported from GHC
@fendor fendor merged commit c786a73 into haskell:master Jul 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants