Skip to content

Commit 0c99ce0

Browse files
lukel97amesgen
andauthored
Distributable binaries (#165)
Update ghcide to obtain the GHC lib dir at runtime, rather than at compile time with ghc-paths. This means that the binaries can be moved about since the lib dir is obtained on the fly Share the exe/main.hs logic between ghcide and hls: the session setup logic which previously took up most of exe/main.hs now resides inside the ghcide library, and is used by both ghcide and hls's executables Add a --project-ghc-version option to the wrapper which spits out the project's ghc version to stdout. This is useful for the vscode extension which can then use it to download the corresponding version of binary that the wrapper would have otherwise attempted to launch Make the wrapper check to see if the correct tool is installed beforehand. For example, if it detects a stack project but stack isn't on the path, it will report an error then and there, rather than having hls/ghcide confusingly fail later on. The vscode extension uses this new error message as well to provide a pop up message linking the user to a website to install the missing tool Remove cabal-helper from the wrapper, so that the implicit cradle logic is the same between ghcide/hls/hls-wrapper And of course, add a GitHub action workflow that runs whenever a release is created on GitHub that builds static binaries on Linux, and distributable enough binaries on macOS and windows. This is documented a bit more in docs/releases.md * WIP * WIP 2 * WIP 3 * WIP 4 * WIP 5 * WIP 6 * WIP 7 * WIP 8 * WIP 9 Use patched hie-bios to get libdir dynamically * Try building the wrapper * Try to fix build_wrapper env variable not being picked up * Try again * Give up on the env var idea * Try out static optimised builds? * Try squashing the working dir * Woops * Try squashing the builddir * Try going into the parent directory * Radical approach - don't use such a long name for the wrapper * Use dist-binary flag * Debug why floskell fails to build on windows * haskell-language-server => hls on CI I hate that I have to do this * Employ extreme path saving measures * sed time :( * Try making sed command portable * Compress artefacts * Tidy up wrapper logging * Use version checking logic in hie-bios * Add documentation on the releases process * Remove unused code * Append .exe to windows binaries * Try out building remaining supported ghc configurations * Add wrapper tests and update hie-bios * Use index timestamp that exists on hackage Fixes warning * Update hie-bios * Update hie-bios * Try building windows jobs on -j1 * Skip windows 8.8.2 * Update ghc-check to use hie-bios runtime ghc libdir * Upload binaries as an artifact too * Try flicking on enable-executable-static I don't expect this to work, puffnfresh has already tried this and had to fork ghcup * Fix artifact upload * Update to latest ghcide and reuse loadSession * Check if the tool is installed in --project-ghc-version in the wrapper * Fix wrapper tests by copying to temporary directory * Try caching * Tidy up and switch back to cabal helper implicit cradle * use split sections * Remove cabal-helper and replace it with hie-bios implicit logic The cabal-helper cradle was only used by the wrapper for detecting the project GHC version in the absence of an explicit hie.yaml file, whilst ghcide itself used the hie-bios implicit cradle logic. This brings the two in sync so the wrapper should behave more predictably now. * Undo agpl common stanza change * Add release number Co-authored-by: amesgen <[email protected]>
1 parent acaba05 commit 0c99ce0

28 files changed

+426
-1644
lines changed

.github/workflows/build.yml

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
name: Builds
2+
3+
on:
4+
release:
5+
types: [created]
6+
jobs:
7+
8+
build:
9+
runs-on: ${{ matrix.os }}
10+
11+
strategy:
12+
fail-fast: false
13+
matrix:
14+
ghc: ['8.10.1', '8.8.3', '8.8.2', '8.6.5', '8.6.4']
15+
os: [ubuntu-latest, macOS-latest, windows-latest]
16+
exclude:
17+
- os: windows-latest
18+
ghc: '8.8.3' # fails due to segfault
19+
- os: windows-latest
20+
ghc: '8.8.2' # fails due to error with Cabal
21+
22+
steps:
23+
- uses: actions/checkout@v2
24+
with:
25+
submodules: true
26+
- uses: actions/[email protected]
27+
with:
28+
ghc-version: ${{ matrix.ghc }}
29+
cabal-version: '3.2'
30+
31+
- name: Cache Cabal
32+
uses: actions/[email protected]
33+
with:
34+
path: ~/.cabal
35+
key: ${{ runner.OS }}-${{ matrix.ghc }}-${{ hashFiles('**/*.cabal') }}
36+
37+
- name: Shorten binary names
38+
shell: bash
39+
run: |
40+
sed -i.bak -e 's/haskell-language-server/hls/g' \
41+
-e 's/haskell_language_server/hls/g' \
42+
haskell-language-server.cabal
43+
sed -i.bak -e 's/Paths_haskell_language_server/Paths_hls/g' \
44+
src/**/*.hs exe/*.hs
45+
46+
- name: Set some window specific things
47+
if: matrix.os == 'windows-latest'
48+
shell: bash
49+
run: |
50+
echo '::set-env name=EXE_EXT::.exe'
51+
52+
- name: Set some linux specific things
53+
if: matrix.os == 'ubuntu-latest'
54+
run: |
55+
echo '::set-env name=LINUX_CABAL_ARGS::--enable-executable-static --ghc-options=-split-sections'
56+
57+
- name: Build Server
58+
shell: bash
59+
# Try building it twice in case of flakey builds on Windows
60+
run: |
61+
cabal build exe:hls -O2 --disable-documentation $LINUX_CABAL_ARGS || \
62+
cabal build exe:hls -O2 --disable-documentation $LINUX_CABAL_ARGS
63+
64+
- name: Find Server Binary
65+
id: find_server_binary
66+
shell: bash
67+
run: |
68+
HLS=$(find dist-newstyle \( -name 'hls' -o -name 'hls.exe' \) -type f)
69+
gzip --best $HLS
70+
echo ::set-output name=hls_binary::$HLS.gz
71+
72+
- name: Upload Server Binary
73+
uses: actions/[email protected]
74+
env:
75+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
76+
with:
77+
upload_url: ${{ github.event.release.upload_url }}
78+
asset_path: ${{ steps.find_server_binary.outputs.hls_binary }}
79+
asset_name: haskell-language-server-${{ runner.OS }}-${{ matrix.ghc }}${{env.EXE_EXT}}.gz
80+
asset_content_type: application/gzip
81+
82+
- uses: actions/upload-artifact@v2
83+
with:
84+
name: haskell-language-server-${{ runner.OS }}-${{ matrix.ghc }}${{env.EXE_EXT}}.gz
85+
path: ${{ steps.find_server_binary.outputs.hls_binary }}
86+
87+
- name: Build Wrapper
88+
if: matrix.ghc == '8.10.1'
89+
run: cabal build exe:hls-wrapper -O2 --disable-documentation $WIN_CABAL_ARGS $LINUX_CABAL_ARGS
90+
91+
- name: Find Wrapper Binary
92+
if: matrix.ghc == '8.10.1'
93+
id: find_wrapper_binary
94+
shell: bash
95+
run: |
96+
HLS_WRAPPER=$(find dist-newstyle \( -name 'hls-wrapper' -o -name 'hls-wrapper.exe' \) -type f)
97+
gzip --best $HLS_WRAPPER
98+
echo ::set-output name=hls_wrapper_binary::$HLS_WRAPPER.gz
99+
100+
- name: Upload Wrapper
101+
if: matrix.ghc == '8.10.1'
102+
uses: actions/[email protected]
103+
env:
104+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
105+
with:
106+
upload_url: ${{ github.event.release.upload_url }}
107+
asset_path: ${{ steps.find_wrapper_binary.outputs.hls_wrapper_binary }}
108+
asset_name: haskell-language-server-wrapper-${{ runner.OS }}${{env.EXE_EXT}}.gz
109+
asset_content_type: application/gzip
110+
111+
- uses: actions/upload-artifact@v2
112+
if: matrix.ghc == '8.10.1'
113+
with:
114+
name: haskell-language-server-wrapper-${{ runner.OS }}${{env.EXE_EXT}}.gz
115+
path: ${{ steps.find_wrapper_binary.outputs.hls_wrapper_binary }}
116+

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,8 @@ shake.yaml.lock
1919
stack*.yaml.lock
2020
shake.yaml.lock
2121

22+
# ignore hie.yaml's for testdata
23+
test/testdata/**/hie.yaml
24+
2225
# metadata files on macOS
2326
.DS_Store

.gitmodules

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@
1313
# url = https://github.com/digital-asset/ghcide.git
1414
# url = https://github.com/alanz/ghcide.git
1515
# url = https://github.com/wz1000/ghcide.git
16-
url = https://github.com/fendor/ghcide.git
16+
# url = https://github.com/fendor/ghcide.git
17+
url = https://github.com/bubba/ghcide.git

docs/releases.md

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Releases and distributable binaries
2+
3+
Starting with 0.3.0.0 haskell-language-server provides pre-built binaries on
4+
each [GitHub
5+
release](https://github.com/haskell/haskell-language-server/releases). These
6+
binaries are used by the [vscode-hie-server
7+
extension](https://github.com/alanz/vscode-hie-server) to provide automatic
8+
installation for users on VS Code, but they can also be installed manually
9+
when added to the path.
10+
11+
## Making a new release of haskell-language-server
12+
13+
Go to the [GitHub releases
14+
page](https://github.com/haskell/haskell-language-server/releases) for
15+
haskell-language-server and start to create a new release. Choose or create a
16+
tag, fill out the release notes etc., but before you create it
17+
**make sure to check the pre-release checkbox**. This will prevent VS Code
18+
*extension
19+
users from attempting to install this version before the binaries are
20+
created.
21+
22+
Once the release is created the [GitHub Actions
23+
workflow](https://github.com/haskell/haskell-language-server/actions) will be
24+
kicked off and will start creating binaries. They will be gzipped and
25+
uploaded to the release.
26+
27+
It creates a `haskell-language-server-OS-GHC` binary for each platform
28+
(Linux, macOS, Windows) and each GHC version that we currently support, as well
29+
as a `haskell-language-server-wrapper-OS` binary for each platform. Note that
30+
only one wrapper binary is created per platform, and it should be built with the
31+
most recent GHC version.
32+
33+
Once all these binaries are present
34+
35+
## Distributable binaries
36+
In order to compile a hls binary on one machine and have it run on another, you
37+
need to make sure there are **no hardcoded paths or data-files**.
38+
39+
### ghc libdir
40+
One noteable thing which cannot be hardcoded is the **GHC libdir** – this is
41+
a path to `/usr/local/lib/ghc` or something like that, which was previously
42+
baked in at compile-time with ghc-paths. Note that with static binaries we
43+
can no longer use this because the GHC libdir of the GitHub Actions machine
44+
will most almost certainly not exist on the end user's machine.
45+
Therefore, hie-bios provides `getGhcRuntimeLibDir` to obtain this path on the fly
46+
by consulting the cradle.
47+
48+
### Static binaries
49+
We use the word "distributable" here because technically only the Linux builds
50+
are static. They are built by passing `--enable-executable-static` to cabal.
51+
Static binaries don't really exist on macOS, and there are issues with
52+
proprietary code being linked in on Windows. However, the `.dylib`s linked on
53+
macOS are all already provided by the system:
54+
55+
```
56+
$ objdump -macho --dylibs-used haskell-language-server
57+
haskell-language-server:
58+
/usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0)
59+
/usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)
60+
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1)
61+
/usr/lib/libcharset.1.dylib (compatibility version 2.0.0, current version 2.0.0)
62+
```
63+
64+
## The GitHub Actions workflow
65+
It just kicks off a matrix of jobs varying across GHC versions and OSs, building
66+
the binaries with Cabal and extracting them from the dist-newstyle directory.
67+
The binaries are built with -O2.
68+
69+
One caveat is that we need to rename the binaries from
70+
haskell-language-server/haskell-language-server-wrapper to hls/hls-wrapper due to
71+
path length limitations on windows. But whenever we upload them to the release,
72+
we make sure to upload them as their full name variant.

exe/Arguments.hs

+7-11
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,11 @@
1010
module Arguments
1111
( Arguments(..)
1212
, getArguments
13-
, ghcideVersion
14-
, getLibdir
13+
, haskellLanguageServerVersion
1514
) where
1615

17-
import Data.Maybe
1816
import Data.Version
1917
import Development.GitRev
20-
import qualified GHC.Paths
2118
import Options.Applicative
2219
import Paths_haskell_language_server
2320
import System.Environment
@@ -37,6 +34,7 @@ data Arguments = Arguments
3734
, argsDebugOn :: Bool
3835
, argsLogFile :: Maybe String
3936
, argsThreads :: Int
37+
, argsProjectGhcVersion :: Bool
4038
} deriving Show
4139

4240
getArguments :: String -> IO Arguments
@@ -80,21 +78,19 @@ arguments exeName = Arguments
8078
<> value 0
8179
<> showDefault
8280
)
81+
<*> switch (long "project-ghc-version"
82+
<> help "Work out the project GHC version and print it")
8383

8484
-- ---------------------------------------------------------------------
85-
-- Set the GHC libdir to the nix libdir if it's present.
86-
getLibdir :: IO FilePath
87-
getLibdir = fromMaybe GHC.Paths.libdir <$> lookupEnv "NIX_GHC_LIBDIR"
8885

89-
ghcideVersion :: IO String
90-
ghcideVersion = do
86+
haskellLanguageServerVersion :: IO String
87+
haskellLanguageServerVersion = do
9188
path <- getExecutablePath
9289
let gitHashSection = case $(gitHash) of
9390
x | x == "UNKNOWN" -> ""
9491
x -> " (GIT hash: " <> x <> ")"
95-
return $ "ghcide version: " <> showVersion version
92+
return $ "haskell-language-server version: " <> showVersion version
9693
<> " (GHC: " <> VERSION_ghc
9794
<> ") (PATH: " <> path <> ")"
9895
<> gitHashSection
9996

100-
-- ---------------------------------------------------------------------

0 commit comments

Comments
 (0)