Skip to content

Commit

Permalink
Initial commit, moving from haskell-ide-engine
Browse files Browse the repository at this point in the history
  • Loading branch information
mpickering committed Jun 14, 2019
0 parents commit f438ad7
Show file tree
Hide file tree
Showing 23 changed files with 1,745 additions and 0 deletions.
2 changes: 2 additions & 0 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
2018-12-18 v0.0.0
* First release
29 changes: 29 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
Copyright (c) 2009, IIJ Innovation Institute Inc.
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of the copyright holders nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
132 changes: 132 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# hie-bios

`hie-bios` is the way which `hie` sets up a GHC API session.

Its design is motivated by the guiding principle:

> It is the responsibility of the build tool to describe the environment
> which a package should be built in.
This means that it is possible
to easily support a wide range of tools including `cabal-install`, `stack`,
`rules_haskell`, `hadrian` and `obelisk` without major contortions.
`hie-bios` does not depend on the `Cabal` library nor does not
read any complicated build products and so on.

How does a tool specify a session? A session is fully specified by a set of
standard GHC flags. Most tools already produce this information if they support
a `repl` command. Launching a repl is achieved by calling `ghci` with the
right flags to specify the package database. `hie-bios` needs a way to get
these flags and then it can set up GHC API session correctly.

Futher it means that any failure to set up the API session is the responsibility
of the build tool. It is up to them to provide the correct information if they
want HIE to work correctly.

## Explicit Configuration

The user can place a `hie.dhall` file in the root of the workspace which
describes how to setup the environment. For example, to explicitly state
that you want to use `stack` then the configuration file would look like:

```
{ cradle = CradleConfig.Stack {=} }
```

If you use `cabal` then you probably need to specify which component you want
to use.

```
{ cradle = CradleConfig.Cabal { component = Some "lib:haskell-ide-engine" } }
```

Or you can explicitly state the program which should be used to collect
the options by supplying the path to the program. It is interpreted
relative to the current working directory if it is not an absolute path.

```
{ cradle = CradleConfig.Bios { prog = ".hie-bios" } }
```

The complete dhall configuration is described by the following type

```
< cradle :
< Cabal : { component : Optional Text }
| Stack : {}
| Bazel : {}
| Obelisk : {}
| Bios : { prog : Text}
| Default : {} > >
```

## Implicit Configuration

There are several built in modes which captures most common Haskell development
scenarios. If no `hie.dhall` configuration file is found then an implicit
configuration is searched for.

### Priority

The targets are searched for in following order.

1. A specific `hie-bios` file.
2. An `obelisk` project
3. A `rules_haskell` project
4. A `stack` project
4. A `cabal` project
5. The default cradle which has no specific options.

### `cabal-install`

The workspace root is the first folder containing a `cabal.project` file.

The arguments are collected by running `cabal v2-repl`.

If `cabal v2-repl` fails, then the user needs to configure the correct
target to use by writing a `hie.dhall` file.

### `rules_haskell`

The workspace root is the folder containing a `WORKSPACE` file.

The options are collected by querying `bazel`.

### `obelisk`

The workspace root is the folder containing a `.obelisk` directory.

The options are collected by running `ob ide-args`.

### `bios`

The most general form is the `bios` mode which allows a user to specify themselves
which flags to provide.

In this mode, an executable file called `.hie-bios` is placed in the root
of the workspace directory. The script takes one argument, the filepath
to the current file we want to load into the session. The script returns
the correct arguments in order to load that file successfully.

A good guiding specification for this file is that the following command
should work for any file in your project.

```
ghci $(./hie-bios /path/to/foo.hs) /path/to/foo.hs
```

This is useful if you are designing a new build system or the other modes
fail to setup the correct session for some reason. For example, this is
how hadrian (GHC's build system) is integrated into HIE.


## Relationship with `ghcid`

The design of `hie-bios` is inspired by `ghcid`. Like `ghcid`, it does not depend
on any of the tools it supports. The success of `ghcid` is that it works reliably
in many situations. This is because of the fact that it delegates complicated
decisions about a build to the build tool.

`ghcid` could be implemented using `hie-bios` using the `ghci $(./hie-bios Main.hs) Main.hs`
idiom described earlier.

2 changes: 2 additions & 0 deletions Setup.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain
1 change: 1 addition & 0 deletions cabal.project
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
packages: .
82 changes: 82 additions & 0 deletions exe/biosc.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{-# LANGUAGE DeriveDataTypeable #-}

module Main where

import Config (cProjectVersion)

import Control.Exception (Exception, Handler(..), ErrorCall(..))
import qualified Control.Exception as E
import Data.Typeable (Typeable)
import Data.Version (showVersion)
import System.Directory (getCurrentDirectory)
import System.Environment (getArgs)
import System.Exit (exitFailure)
import System.IO (hPutStr, hPutStrLn, stdout, stderr, hSetEncoding, utf8)

import HIE.Bios
import HIE.Bios.Types
import HIE.Bios.Check
import HIE.Bios.Debug
import Paths_hie_bios

----------------------------------------------------------------

progVersion :: String
progVersion = "biosc version " ++ showVersion version ++ " compiled by GHC " ++ cProjectVersion ++ "\n"

ghcOptHelp :: String
ghcOptHelp = " [-g GHC_opt1 -g GHC_opt2 ...] "

usage :: String
usage = progVersion
++ "Usage:\n"
++ "\t biosc check" ++ ghcOptHelp ++ "<HaskellFiles...>\n"
++ "\t biosc version\n"
++ "\t biosc help\n"

----------------------------------------------------------------

data HhpcError = SafeList
| TooManyArguments String
| NoSuchCommand String
| CmdArg [String]
| FileNotExist String deriving (Show, Typeable)

instance Exception HhpcError

----------------------------------------------------------------

main :: IO ()
main = flip E.catches handlers $ do
hSetEncoding stdout utf8
args <- getArgs
cradle <- getCurrentDirectory >>= findCradle
let cmdArg0 = args !. 0
remainingArgs = tail args
opt = defaultOptions
res <- case cmdArg0 of
"check" -> checkSyntax opt cradle remainingArgs
"expand" -> expandTemplate opt cradle remainingArgs
"debug" -> debugInfo opt cradle
"root" -> rootInfo opt cradle
"version" -> return progVersion
cmd -> E.throw (NoSuchCommand cmd)
putStr res
where
handlers = [Handler (handleThenExit handler1), Handler (handleThenExit handler2)]
handleThenExit handler e = handler e >> exitFailure
handler1 :: ErrorCall -> IO ()
handler1 = print -- for debug
handler2 :: HhpcError -> IO ()
handler2 SafeList = return ()
handler2 (TooManyArguments cmd) = do
hPutStrLn stderr $ "\"" ++ cmd ++ "\": Too many arguments"
handler2 (NoSuchCommand cmd) = do
hPutStrLn stderr $ "\"" ++ cmd ++ "\" not supported"
handler2 (CmdArg errs) = do
mapM_ (hPutStr stderr) errs
handler2 (FileNotExist file) = do
hPutStrLn stderr $ "\"" ++ file ++ "\" not found"
xs !. idx
| length xs <= idx = E.throw SafeList
| otherwise = xs !! idx
68 changes: 68 additions & 0 deletions hie-bios.cabal
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
Name: hie-bios
Version: 0.0.0
Author: Kazu Yamamoto <[email protected]> and Matthew Pickering <[email protected]>
Maintainer: Matthew Pickering <[email protected]>
License: BSD3
License-File: LICENSE
Homepage: https://github.com/mpickering/hie-bios
Synopsis: Set up a GHC API session
Description:

Category: Development
Cabal-Version: >= 1.10
Build-Type: Simple
Extra-Source-Files: ChangeLog
wrappers/bazel
wrappers/cabal

Library
Default-Language: Haskell2010
GHC-Options: -Wall
HS-Source-Dirs: src
Exposed-Modules: HIE.Bios
HIE.Bios.Check
HIE.Bios.Cradle
HIE.Bios.Debug
HIE.Bios.GHCApi
HIE.Bios.Gap
HIE.Bios.Doc
HIE.Bios.Load
HIE.Bios.Logger
HIE.Bios.Types
HIE.Bios.Things
HIE.Bios.Config
Build-Depends: base >= 4.9 && < 5
, containers
, deepseq
, directory
, filepath
, ghc
, process
, transformers
, file-embed
, temporary
, unix-compat
, cryptohash-sha1
, bytestring
, base16-bytestring
, dhall <= 1.20.1
, text
, lens-family-core
if impl(ghc < 8.2)
Build-Depends: ghc-boot

Executable biosc
Default-Language: Haskell2010
Main-Is: biosc.hs
Other-Modules: Paths_hie_bios
GHC-Options: -Wall
HS-Source-Dirs: exe
Build-Depends: base >= 4.9 && < 5
, directory
, filepath
, ghc
, hie-bios

Source-Repository head
Type: git
Location: git://github.com/mpickering/hie-bios.git
20 changes: 20 additions & 0 deletions src/HIE/Bios.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
-- | The HIE Bios

module HIE.Bios (
-- * Initialise a session
Cradle(..)
, findCradle
, defaultCradle
, initializeFlagsWithCradle
, initializeFlagsWithCradleWithMessage
-- * Load a module into a session
, loadFile
, loadFileWithMessage
-- * Eliminate a session to IO
, withGhcT
) where

import HIE.Bios.Cradle
import HIE.Bios.Types
import HIE.Bios.GHCApi
import HIE.Bios.Load
Loading

0 comments on commit f438ad7

Please sign in to comment.