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

Don't mix stack with cabal #1557

Merged
merged 10 commits into from
Jan 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions .azure/linux-installhs-stack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,5 @@ jobs:
displayName: Run help of `install.hs`
- bash: |
source .azure/linux.bashrc
stack install.hs stack-install-cabal
displayName: Run stack-install-cabal target of `install.hs`
- bash: |
source .azure/linux.bashrc
stack install.hs build-latest
displayName: Run build-latest target of `install.hs`
stack install.hs latest
displayName: Run latest target of `install.hs`
8 changes: 2 additions & 6 deletions .azure/windows-installhs-stack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,5 @@ jobs:
displayName: Run help of `install.hs`
- bash: |
source .azure/windows.bashrc
stack install.hs stack-install-cabal
displayName: Run stack-install-cabal target of `install.hs`
- bash: |
source .azure/windows.bashrc
stack install.hs build-latest
displayName: Run build-latest target of `install.hs`
stack install.hs latest
displayName: Run latest target of `install.hs`
28 changes: 8 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,47 +226,35 @@ or using the existing alias script

Running the script with cabal on windows requires a cabal version greater or equal to `3.0.0.0`.

Unfortunately, it is still required to have `stack` installed so that the install-script can locate the `local-bin` directory (on Linux `~/.local/bin`) and copy the `hie` binaries to `hie-x.y.z`, which is required for the `hie-wrapper` to function as expected. There are plans to remove this requirement and let users build hie only with one build tool or another.

For brevity, only the `stack`-based commands are presented in the following sections.

##### Install cabal using stack

Although you can use hie for stack based projects (those which have a `stack.yaml` in the project base directory) without having cabal installed, you will need it for cabal based projects (with only a `<projectName>.cabal` file or a `cabal.project` one in the project base directory).

You can install an appropriate cabal version using stack by running:

```bash
stack ./install.hs stack-install-cabal
```

##### Install specific GHC Version

Install hie for the latest available and supported GHC version (and hoogle docs):

```bash
stack ./install.hs build
stack ./install.hs hie
```

Install hie for a specific GHC version (and hoogle docs):

```bash
stack ./install.hs hie-8.6.5
stack ./install.hs build-data
stack ./install.hs data
```

The Haskell IDE Engine can also be built with `cabal v2-build` instead of `stack build`.
This has the advantage that you can decide how the GHC versions have been installed.
To see what GHC versions are available, the command `stack install.hs cabal-ghcs` can be used.
To see what GHC versions are available, the command `cabal-hie-install ghcs` can be used.
It will list all GHC versions that are on the path and their respective installation directory.
If you think, this list is incomplete, you can try to modify the PATH variable, such that the executables can be found.
Note, that the targets `cabal-build` and `cabal-build-data` depend on the found GHC versions.
Note, that the targets `hie` and `data` depend on the found GHC versions.
They install Haskell IDE Engine only for the found GHC versions.

An example output is:

```bash
> stack install.hs cabal-ghcs
> cabal-hie-install ghcs
******************************************************************
Found the following GHC paths:
ghc-8.4.4: /opt/bin/ghc-8.4.4
Expand All @@ -278,11 +266,11 @@ ghc-8.6.2: /opt/bin/ghc-8.6.2
If your desired ghc has been found, you use it to install Haskell IDE Engine.

```bash
stack install.hs cabal-hie-8.4.4
stack install.hs cabal-build-data
cabal-hie-install hie-8.4.4
cabal-hie-install data
```

In general, targets that use `cabal` instead of `stack` are prefixed with `cabal-*` and are identical to their counterpart, except they do not install a GHC if it is missing but fail.
In general, executing targets with `cabal` instead of `stack` have the same behaviour, except they do not install a GHC if it is missing but fail.

##### Multiple versions of HIE (optional)

Expand Down
26 changes: 15 additions & 11 deletions docs/Build.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,17 @@ See the project's `README` for detailed information about installing `hie`.
The build script `install.hs` defines several targets using the `shake` build system. The targets are roughly:

* `hie-*`: builds and installs the `hie` binaries. Also renames the binaries to contain the correct version-number.
* `build-latest`: builds and installs `hie` for the latest available and supported `ghc` version.
* `build-data`: builds the hoogle-db required by `hie`
* `build`: builds and installs `hie` for the latest supported `ghc` version (like `build-latest`) and the hoogle-db (like `build-data`)
* `cabal-*`: execute the same task as the original target, but with `cabal` instead of `stack`
* `latest`: builds and installs `hie` for the latest available and supported `ghc` version.
* `data`: builds the hoogle-db required by `hie`
* `hie`: builds and installs `hie` for the latest supported `ghc` version (like `latest`) and the hoogle-db (like `data`)

Each `stack-*.yaml` contains references to packages in the submodules. Calling `stack` with one of those causes the build to fail if the submodules have not been initialized already. The file `shake.yaml` solves this issue invoking the `git` binary itself to update the submodules. Moreover, it specifies the correct version of `shake` and is used for installing all run-time dependencies such as `cabal` and `hoogle` if necessary.
Each `stack-*.yaml` contains references to packages in the submodules. Calling `stack` with one of those causes the build to fail if the submodules have not been initialized already. The file `shake.yaml` solves this issue invoking the `git` binary itself to update the submodules. Moreover, it specifies the correct version of `shake` and is used for installing all run-time dependencies such as `hoogle` if necessary.

### Run-time dependencies

`hie` depends on a correct environment in order to function properly:

* `cabal-install`: This dependency is required by `hie` to handle correctly projects that are not `stack` based. You can install an appropriate version using `stack` with the `stack-install-cabal` target.
* `cabal-install`: This dependency is required by `hie` to handle correctly projects that are not `stack` based. You can install it using one of the methods listed here: https://www.haskell.org/cabal/#install-upgrade
* The `hoogle` database: `hoogle generate` needs to be called with the most-recent `hoogle` version.

### Steps to build `hie`
Expand Down Expand Up @@ -68,12 +67,21 @@ EOF

Then `hie` can be compiled for a specific GHC version:

* For cabal prior to 3.0.0.0
```bash
export GHCP=<path-to-ghc-binary>
cabal v2-install exe:hie -w $GHCP \
--write-ghc-environment-files=never --symlink-bindir=$HOME/.local/bin \
--overwrite-policy=always --reinstall
```
* For cabal 3.0.0.0 or newer
```bash
export GHCP=<path-to-ghc-binary>
cabal v2-install exe:hie -w $GHCP \
--write-ghc-environment-files=never --installdir=$HOME/.local/bin \
--overwrite-policy=always --reinstall
```
* For windows you will need cabal 3.0.0.0 and add the argument `--install-method=copy`

The final step is to configure the `hie` client to use a custom `hie-wrapper` script that enables the runtime options for profiling. Such a script could look like this:

Expand All @@ -88,17 +96,13 @@ The final step is to configure the `hie` client to use a custom `hie-wrapper` sc

The `install.hs` script performs some checks to ensure that a correct installation is possible and provide meaningful error messages for known issues.

* `stack` needs to be up-to-date. Version `1.9.3` is required
* `stack` needs to be up-to-date. Version `2.1.1` is required
* `cabal` needs to be up-to-date. Version `3.0.0.0` is required for windows systems and `2.4.1.0` for other ones.
* `ghc-8.6.3` is broken on windows. Trying to install `hie-8.6.3` on windows is not possible.
* When the build fails, an error message, that suggests to remove `.stack-work` directory, is displayed.

### Tradeoffs

#### `stack` is a build dependency

Currently, `stack` is needed even if you run the script with `cabal` to get the path where install the binaries but there are plans to remove that dependency (see #1380).

#### run `install.hs` with `stack` installs a GHC before running

Before the code in `install.hs` can be executed, `stack` installs a `GHC`, depending on the `resolver` field in `shake.yaml`. This is necessary if `install.hs` should be completely functional right after a fresh `git clone` without further configuration.
Expand Down
2 changes: 2 additions & 0 deletions install/hie-install.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ library

if flag(run-from-stack)
cpp-options: -DRUN_FROM_STACK
else
build-depends: cabal-install-parsers

flag run-from-stack
description: Inform the application that it is run from stack
Expand Down
49 changes: 20 additions & 29 deletions install/src/Cabal.hs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{-# LANGUAGE CPP #-}

module Cabal where

import Development.Shake
Expand All @@ -15,16 +17,26 @@ import System.Directory ( findExecutable
import Version
import Print
import Env
import Stack
import Data.Functor.Identity
#if RUN_FROM_STACK
import Control.Exception ( throwIO )
#else
import Cabal.Config
#endif

getInstallDir :: IO FilePath
#if RUN_FROM_STACK
-- we should never hit this codepath
getInstallDir = throwIO $ userError "Stack and cabal should never be mixed"
#else
getInstallDir = runIdentity . cfgInstallDir <$> readConfig
#endif

execCabal :: CmdResult r => [String] -> Action r
execCabal = execCabalWithOriginalPath
execCabal = command [] "cabal"

execCabal_ :: [String] -> Action ()
execCabal_ = execCabalWithOriginalPath

execCabalWithOriginalPath :: CmdResult r => [String] -> Action r
execCabalWithOriginalPath = withoutStackCachedBinaries . (command [] "cabal")
execCabal_ = command [] "cabal"

cabalBuildData :: Action ()
cabalBuildData = do
Expand All @@ -39,19 +51,9 @@ getGhcPathOfOrThrowError versionNumber =
error (ghcVersionNotFoundFailMsg versionNumber)
Just p -> return p

cabalBuildHie :: VersionNumber -> Action ()
cabalBuildHie versionNumber = do
ghcPath <- getGhcPathOfOrThrowError versionNumber
execCabal_
[ "v2-build"
, "-w", ghcPath
, "--write-ghc-environment-files=never"
, "--max-backjumps=5000"
, "--disable-tests"]

cabalInstallHie :: VersionNumber -> Action ()
cabalInstallHie versionNumber = do
localBin <- getLocalBin
localBin <- liftIO $ getInstallDir
cabalVersion <- getCabalVersion
ghcPath <- getGhcPathOfOrThrowError versionNumber

Expand All @@ -65,6 +67,7 @@ cabalInstallHie versionNumber = do
, "-w", ghcPath
, "--write-ghc-environment-files=never"
, installDirOpt, localBin
, "--max-backjumps=5000"
, "exe:hie"
, "--overwrite-policy=always"
]
Expand All @@ -84,18 +87,6 @@ cabalInstallHie versionNumber = do
++ minorVerExe
++ " to " ++ localBin

installCabalWithStack :: Action ()
installCabalWithStack = do
-- try to find existing `cabal` executable with appropriate version
mbc <- withoutStackCachedBinaries (liftIO (findExecutable "cabal"))

case mbc of
Just c -> do
cabalVersion <- checkCabal
printLine $ "There is already a cabal executable in $PATH with the required minimum version: " ++ cabalVersion
-- install `cabal-install` if not already installed
Nothing -> execStackShake_ ["install", "cabal-install"]

checkCabal_ :: Action ()
checkCabal_ = checkCabal >> return ()

Expand Down
56 changes: 5 additions & 51 deletions install/src/Help.hs
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,7 @@ helpMessage versions@BuildableVersions {..} = do
[emptyTarget]
[ generalTargets
, defaultTargets
, stackTargets
, cabalTargets
, if isRunFromCabal then [cabalGhcsTarget] else []
, [macosIcuTarget]
]

Expand All @@ -89,52 +88,26 @@ helpMessage versions@BuildableVersions {..} = do
defaultTargets = [buildTarget, buildLatestTarget, buildDataTarget]
++ map hieTarget (getDefaultBuildSystemVersions versions)

stackTargets =
[ stackTarget buildTarget
, stackTarget buildLatestTarget
, stackTarget buildDataTarget
]
++ (if isRunFromStack then [stackTarget installCabalTarget] else [])
++ map (stackTarget . hieTarget) stackVersions

cabalTargets =
[ cabalGhcsTarget
, cabalTarget buildTarget
, cabalTarget buildLatestTarget
, cabalTarget buildDataTarget
]
++ map (cabalTarget . hieTarget) cabalVersions

-- | Empty target. Purpose is to introduce a newline between the targets
emptyTarget :: (String, String)
emptyTarget = ("", "")

templateTarget :: (String, String)
templateTarget = ("<target>", "")

targetWithBuildSystem :: String -> TargetDescription -> TargetDescription
targetWithBuildSystem system (target, description) =
(system ++ "-" ++ target, description ++ "; with " ++ system)

stackTarget :: TargetDescription -> TargetDescription
stackTarget = targetWithBuildSystem "stack"

cabalTarget :: TargetDescription -> TargetDescription
cabalTarget = targetWithBuildSystem "cabal"

hieTarget :: String -> TargetDescription
hieTarget version =
("hie-" ++ version, "Builds hie for GHC version " ++ version)

buildTarget :: TargetDescription
buildTarget = ("build", "Build hie with the latest available GHC and the data files")
buildTarget = ("hie", "Build hie with the latest available GHC and the data files")

buildLatestTarget :: TargetDescription
buildLatestTarget = ("build-latest", "Build hie with the latest available GHC")
buildLatestTarget = ("latest", "Build hie with the latest available GHC")

buildDataTarget :: TargetDescription
buildDataTarget =
("build-data", "Get the required data-files for `hie` (Hoogle DB)")
("data", "Get the required data-files for `hie` (Hoogle DB)")

-- special targets

Expand All @@ -146,25 +119,6 @@ helpTarget = ("help", "Show help message including all targets")

cabalGhcsTarget :: TargetDescription
cabalGhcsTarget =
( "cabal-ghcs"
( "ghcs"
, "Show all GHC versions that can be installed via `cabal-build`."
)

installCabalTarget :: TargetDescription
installCabalTarget =
( "install-cabal"
, "Install the cabal executable. It will install the required minimum version for hie (currently "
++ versionToString requiredCabalVersion
++ ") if it isn't already present in $PATH"
)

-- | Creates a message of the form "a, b, c and d", where a,b,c,d are GHC versions.
-- If there is no GHC in the list of `hieVersions`
allVersionMessage :: [String] -> String
allVersionMessage wordList = case wordList of
[] -> ""
[a] -> show a
(a : as) ->
let msg = intersperse ", " wordList
lastVersion = last msg
in concat $ init (init msg) ++ [" and ", lastVersion]
Loading