Skip to content
This repository has been archived by the owner on Oct 7, 2020. It is now read-only.

Implement the HIE Bios #1126

Merged
merged 346 commits into from
Dec 20, 2019
Merged

Implement the HIE Bios #1126

merged 346 commits into from
Dec 20, 2019

Conversation

mpickering
Copy link
Collaborator

@mpickering mpickering commented Mar 10, 2019

This branch implements the HIE bios which is a simplified way of setting up a GHC session.

The library, hie-bios, is responsible for one thing and only one thing. That is to set up the correct GHC session.

The guiding principle of hie-bios is:

It is the responsibility of the build tool to describe the environment which a package should be built in.

This means that it is easy to implement support for any build tool. I already implemented support for stack/cabal/hadrian/rules_haskell/obelisk. These are the main build tools I think. More information can be read in the README

I need help in finishing this PR which is why I am putting it up here.

  • HaRe support is broken at the moment as it relies on ghc-mod. In theory it could still use ghc-mod I think but I didn't give it much thought yet.
  • There are still many references to functions from ghc-mod which are not ghc-mod specific. They need to be moved into haskell-ide-engine or upstreamed.
  • Multi-project support is not implemented. The whole premise of how this works seemed quite fragile to me. Is it really enough to set a different set of DynFlags when changing between sessions? This is not something the GHC API is really designed for, I would think you would have to make a new GHC session for each component or spawn another process. If the approach of replacing DynFlags really does work then we just need to create a map from arguments to DynFlags. This also deals with creating a new session if the project configuration changes quite naturally as the cache will miss and a new session created.
  • It all needs testing by someone who knows how HIE works now as I don't use it (yet). The whole reason for this PR was to get HIE working on GHC's codebase.

@mpickering
Copy link
Collaborator Author

The relevant ticket is #1053

Copy link
Collaborator

@DanielG DanielG left a comment

Choose a reason for hiding this comment

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

Basically the idea here is nothing new. Calling cabal repl --with-compiler=some-wrapper-thing was literally one of the first designs I tried in ghc-mod before cabal-helper. I abandoned it for legacy reasons: namely low latency single shot command support for the vim people among other thing.

@mpickering definitely convinced me that now that these legacy factors are gone going back to that approach as a low effort, wide spectrum, base implementation for pretty much all Haskell build systems in existence is a very good idea and exactly the reason we need fresh eyes on these problems. Mine are obviously old and broken for completely missing this.

I'd definitely like to steal that but I think we shouldn't just throw everything away on a whim.

Especially with show-build-info back on the right track thank's to @hvr's students (haskell/cabal#5954) cabal-helper can be made even more reliable to the point where it's not even a dirty hack at all anymore. Removing the cabal-helper-wrapper and $libexec stuff already made a huge dent I think.

According to @mpickering starting from scratch instead of integrating into cabal-helper is partially a decision motivated by GPL aversion. I'm not sure if it's just FUD related or an actual real world problem. I'd really like to find out since it would be much more productive to work together on this rather than in different silos.

One more point I'd like to make is that this patch doesn't change the horribleness of the old ghc-mod Cradle approach at all. @mpickering do you have a plan on how to provide a better user experience than just randomly guessing one cradle type on overlap? Because I do :)

Overall I'm still not really convinced forking an ancient version of ghc-mod and patching it to hell is a good idea instead of just integrating this into cabal-helper and working together on moving that forward.

hie-bios/.travis.yml Outdated Show resolved Hide resolved
hie-bios/LICENSE Outdated Show resolved Hide resolved
hie-bios/cabal.project Outdated Show resolved Hide resolved
hie-bios/hie-bios.cabal Outdated Show resolved Hide resolved
hie-plugin-api/Haskell/Ide/Engine/MonadFunctions.hs Outdated Show resolved Hide resolved
shell.nix Outdated Show resolved Hide resolved
hie-bios/wrappers/cabal Outdated Show resolved Hide resolved
hie-bios/hie-bios.cabal Outdated Show resolved Hide resolved
hie-bios/hie-bios.cabal Outdated Show resolved Hide resolved
@sheyll
Copy link

sheyll commented Mar 27, 2019

@mpickering @DanielG thanks to both of you

@lukel97
Copy link
Collaborator

lukel97 commented Apr 3, 2019

@DanielG @mpickering Is the plan for this PR to fully-remove/replace the ghc-mod plugin? I'm currently wondering for the sake of wether or not this gets rid of cabal-helper-wrapper: it would be nice to get this rid of it so we can unblock static builds (cc @fendor this is related to #1143 #1068 #529 )

@DanielG
Copy link
Collaborator

DanielG commented Apr 4, 2019

cabal-helper-wrapper is gone with cabal-helper-1.0 either way so this PR isn't strictly a prerequisite for that particular issue. FYI if someone feels inclined it would actually be downright trivial to remove it from the 0.8 branch too until the 1.0 release happens.


As far as I understand from discussion with @mpickering his intention is to more or less replace the ghc-mod plugin with this. However things aren't so easy, this approach he's chosen has significantly different tradeoffs than what we're used to from ghc-mod/cabal-helper land so just replacing outright is a bit drastic.

I've been discussing this with @mpickering and I think we've come to the understanding that both approaches are worthwhile and we'll will work towards having HIE allow both. It is unclear at this point how we handle the transition though.

Obviously @mpickering wants us to adopt his branch and then integrate the cabal-helper story into that but I'm erring on the side of caution and proposing to first bring ghc-mod(-core) in line with cabal-helper-1.0 first and then after we relieve the time pressure to release something that works with new-build take the time to re-design the interface between HIE and GHC taking into account his approach. Incidentally I'm planning on submitting a GSoC proposal for just such an endeavor.

IMO radically changing the software stack like this is too risky but ultimately I defer that decision to people more actively working on HIE than me.

Oh and I should mention that since my last comment I actually did a bit more investigating and unfortunately @mpickering's approach will simply not fit into cabal-helper for technical reasons. So unfortunately folding both efforts together isn't easily possible.

@mpickering
Copy link
Collaborator Author

From my perspective replacing the ghc-mod backend with hie-bios has the quite significant advantage of working in many more situations. It also makes future maintenance easier by passing the responsibility for setting up the environment off to consumers.

My idea here is that first a robust foundation needs to be created and then additional complicated
features can be added once the foundations are secure.

There are some things which are not working yet on the branch

  • Quite poor reload times (compared to ghcid)
  • Quite poor memory usage on large projects
  • Multi-session support is not tested well but I don't think this feature is necessary until everything else is robust.
  • Cradle finding logic is fragile. This can be resolved with configuration like .ghcid.

Oh and I should mention that since my last comment I actually did a bit more investigating and unfortunately @mpickering's approach will simply not fit into cabal-helper for technical reasons. So unfortunately folding both efforts together isn't easily possible.

Vague comments like this are not very helpful. Please can you explain precisely what the problem is?

@DanielG
Copy link
Collaborator

DanielG commented Apr 4, 2019

Please can you explain precisely what the problem is?

Like I mentioned in private cabal-helper expects to be able to enumerate the project essentially, so I need at the very least a way to get a list of all Cabal packages related to a given super-project. Your repl trick however simply relies on having the path to the current Haskell source file and let the build tool figure out which package/component it belongs too. So there is no channel to get that information.

Come to mention it I still don't really see how that is going to work properly with cabal or stack? Their repl commands don't support that at the moment (AFAIK). Can you explain how you plan to make that work?

@mpickering
Copy link
Collaborator Author

The stack repl command supports querying by a file path already. Cabal doesn't but which component to use can be configured a la ghcid. Once show-build-info is implemented the implicit search can use that to inform itself of the component.

@alanz
Copy link
Collaborator

alanz commented Apr 7, 2019

Please put git modules in the submodules directory, so it is clear what they are.

stack-8.6.4.yaml Show resolved Hide resolved
@lukel97
Copy link
Collaborator

lukel97 commented Apr 7, 2019

cabal-helper-wrapper is gone with cabal-helper-1.0 either way so this PR isn't strictly a prerequisite for that particular issue. FYI if someone feels inclined it would actually be downright trivial to remove it from the 0.8 branch too until the 1.0 release happens.

I might give this a shot on @alanz's fork, although I'm not sure if its been merged in with the latest upstream changes.

@fendor fendor mentioned this pull request Apr 21, 2019
2 tasks
# url = https://github.com/bubba/ghc-mod.git
url = https://github.com/alanz/ghc-mod.git

url = https://github.com/fendor/ghc-mod.git
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Need an issue which details what to do to remove this?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Good idea, we want to subsume ghc-project-types to make us independent of ghc-mod.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Moreover, if we take ghc-project-types and turn it into its own project, we are almost ready to go to hackage.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I did discuss that with @DanielG , he is happy for us to move it elsewhere, possibly as a sub-project inside haskell-ide-engine.

Copy link
Collaborator Author

@mpickering mpickering left a comment

Choose a reason for hiding this comment

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

I didn't look too closely at the changes in the tests.

README.md Outdated Show resolved Hide resolved
README.md Outdated Show resolved Hide resolved
README.md Show resolved Hide resolved
cradle:
cabal:
component: "lib:haskell-ide-engine"
```
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Use the lightweight-cabal syntax here I think.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Here it should show how to define multi-cradles. Mutli-Cabal version is a bit above

Left err -> error (show err)
Right cr -> return $ GM.cradleRootDir cr
-- Get the cabal directory from the cradle
cradle <- findLocalCradle (d </> "File.hs")
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Unsure this will work well with multi-cradle projects.

Copy link
Collaborator

Choose a reason for hiding this comment

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

It wont, we have to tackle it in hie-bios

Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Collaborator

Choose a reason for hiding this comment

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

So, this will work in a best effort manner, imo.

in makeRequest $ IReq tn reqId reqCallback x
go acc [] _ _ _ callback = callback acc
go acc (x : xs) d tn reqId callback =
let reqCallback result = go (acc ++ [result]) xs d tn reqId callback
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't think that changed on this PR? Is that a general point?

src/Haskell/Ide/Engine/Scheduler.hs Outdated Show resolved Hide resolved
errorHandler lid J.InternalError msg
errorHandler (Just lid) J.InternalError msg

liftIO $ traceEventIO $ "STOP " ++ show tn ++ "ide:" ++ d
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We should make a new module for these trace events to abstract over this pattern.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We could move a fitting function, traceEvent :: MonadIO m => String -> m a -> m a into PluginUtils? Or into a module hie-plugin-api/Haskell/Ide/Engine/TraceEvent.hs?

test/testdata/badProjects/cabal/.hie-bios Outdated Show resolved Hide resolved
@@ -0,0 +1 @@
cabal-helper-helper . $1
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, we need to remove them.

app/MainHie.hs Outdated Show resolved Hide resolved
app/MainHie.hs Outdated Show resolved Hide resolved
when (optGhcModVomit opts) $
logm "Enabling --vomit for ghc-mod. Output will be on stderr"
when (optBiosVerbose opts) $
logm "Enabling verbose mode for hie-bios. This option currently doesn't do anything."
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would be in favour of removing these manual command line arguments in favour for LSP's InitialiseParams.trace

@@ -1,5 +1,5 @@
name: haskell-ide-engine
version: 0.14.0.0
version: 1.0.0.0
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would leave the version bump till after the merge. I think it is a definitely a worthwhile change but there's probably still some ironing out to do once it lands in master

hie-plugin-api/Haskell/Ide/Engine/ArtifactMap.hs Outdated Show resolved Hide resolved
hie-plugin-api/Haskell/Ide/Engine/Ghc.hs Outdated Show resolved Hide resolved
-- If the virtual file associated to the given uri does not exist, Nothing
-- is returned.
persistVirtualFile' :: Core.LspFuncs Config -> Uri -> IO (Maybe FilePath)
persistVirtualFile' lf uri = Core.persistVirtualFileFunc lf (toNormalizedUri uri)
Copy link
Collaborator

Choose a reason for hiding this comment

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

These smell as if they should be in haskell-lsp

hie-plugin-api/Haskell/Ide/Engine/PluginsIdeMonads.hs Outdated Show resolved Hide resolved
@@ -116,6 +117,11 @@ updateDocumentRequest
:: (MonadIO m, MonadReader REnv m) => Uri -> Int -> PluginRequest R -> m ()
updateDocumentRequest = Scheduler.updateDocumentRequest

updateDocument :: (MonadIO m, MonadReader REnv m) => Uri -> Int -> m ()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can this be documented to clarify what exactly its doing again? @mpickering

@@ -46,7 +48,7 @@ spec = describe "code actions" $ do
contents <- getDocumentEdit doc
liftIO $ contents `shouldBe` "main = undefined\nfoo x = x\n"

noDiagnostics
-- noDiagnostics
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why have these been commented out?

Copy link
Collaborator

Choose a reason for hiding this comment

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

No clue. @alanz ? We can try to enable them again.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I found the tests were failing on my local machine with them in. I have no strong feeling, so long as CI is happy.

Copy link
Collaborator

Choose a reason for hiding this comment

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

lemme try reenabling them

@@ -118,7 +125,7 @@ newScheduler plugins biosOpts = do
}

-- | A handler for any errors that the dispatcher may encounter.
type ErrorHandler = J.LspId -> J.ErrorCode -> T.Text -> IO ()
type ErrorHandler = Maybe J.LspId -> J.ErrorCode -> T.Text -> IO ()
Copy link
Collaborator

Choose a reason for hiding this comment

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

This was an ad-hoc change that somebody should think about again. Intention was that in LspStdio.hs we can differentiate between errors caused by requests and stuff such as runActionWithContext

src/Haskell/Ide/Engine/Transport/LspStdio.hs Show resolved Hide resolved
test/functional/FunctionalLiquidSpec.hs Show resolved Hide resolved
@fendor fendor merged commit 8582a96 into haskell:master Dec 20, 2019
@lukel97
Copy link
Collaborator

lukel97 commented Dec 20, 2019

Congrats to everyone thats worked on this, this is a huge milestone 🥳

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

Successfully merging this pull request may close these issues.