From 69b4f07cf858354491a607dbf51ae7af529a6a0f Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Fri, 12 Jun 2020 17:35:13 +0100 Subject: [PATCH 01/51] WIP --- .github/workflows/build.yml | 78 +++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000000..70c20e07ee --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,78 @@ +name: Builds + +on: + push: + branch: "github-action-builds" +jobs: + release: + runs-on: ubuntu-latest + steps: + - name: Create Release + id: create_release + uses: actions/create-release@v1.1.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: ${{ github.ref }} + release_name: Test release + prerelease: true + - name: Output Release URL File + run: echo "${{ steps.create_release.outputs.upload_url }}" > release_url.txt + - name: Save Release URL File for publish + uses: actions/upload-artifact@v1 + with: + name: release_url + path: release_url.txt + + build: + runs-on: ${{ matrix.os }} + needs: [release] + + strategy: + fail-fast: false + matrix: + ghc: ['8.10.1', '8.8.3', '8.6.5'] # , '8.4.4'] + os: [ubuntu-latest, macOS-latest, windows-latest] + exclude: + - os: windows-latest + ghc: '8.8.3' # fails due to segfault + + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - name: Load Release URL File from release job + uses: actions/download-artifact@v1 + with: + name: release_url + - name: Get Release File Name & Upload URL + id: get_release_info + run: | + VALUE=`cat release_url/release_url.txt` + echo ::set-output name=upload_url::$VALUE + env: + TAG_REF_NAME: ${{ github.ref }} + REPOSITORY_NAME: ${{ github.repository }} + - uses: actions/setup-haskell@v1.1.1 + with: + ghc-version: ${{ matrix.ghc }} + cabal-version: '3.2' + - run: cabal update + + # - name: Cache Cabal + # uses: actions/cache@v1.2.0 + # with: + # path: ~/.cabal + # key: ${{ runner.OS }}-${{ matrix.ghc }}-cabal-0 + + - name: Build + run: cabal build exe:haskell-language-server + - name: Upload Release Asset + uses: actions/upload-release-asset@v1.0.2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.get_release_info.outputs.upload_url }} + asset_path: ./dist-newstyle/build/*/*/haskell-language-server-*/haskell-language-server/build/haskell-language-server/haskell-language-server + asset_name: haskell-language-server-${{ runner.OS }}-${{ matrix.ghc }} + asset_content_type: application/octet-stream From 0e302911eb528130318a4e00cca947e887fab794 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Fri, 12 Jun 2020 18:33:12 +0100 Subject: [PATCH 02/51] WIP 2 From 43c80768a5768277996fb60c580659641eaedd8b Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Fri, 12 Jun 2020 19:13:13 +0100 Subject: [PATCH 03/51] WIP 3 --- .github/workflows/build.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 70c20e07ee..ea43ee4fb8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,6 +47,7 @@ jobs: name: release_url - name: Get Release File Name & Upload URL id: get_release_info + shell: bash run: | VALUE=`cat release_url/release_url.txt` echo ::set-output name=upload_url::$VALUE @@ -64,15 +65,24 @@ jobs: # with: # path: ~/.cabal # key: ${{ runner.OS }}-${{ matrix.ghc }}-cabal-0 + - name: Build run: cabal build exe:haskell-language-server + + - name: Find binary + id: find_binary + shell: bash + run: | + HLS=`find dist-newstyle -name 'haskell-language-server' -type f` + echo ::set-output name=hls_binary::$HLS + - name: Upload Release Asset uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ steps.get_release_info.outputs.upload_url }} - asset_path: ./dist-newstyle/build/*/*/haskell-language-server-*/haskell-language-server/build/haskell-language-server/haskell-language-server + asset_path: ${{ steps.find_binary.outputs.hls_binary }} asset_name: haskell-language-server-${{ runner.OS }}-${{ matrix.ghc }} asset_content_type: application/octet-stream From 8ba974b017234d28cce3b7d65a7fdd9b6cd06f5d Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Fri, 12 Jun 2020 19:55:24 +0100 Subject: [PATCH 04/51] WIP 4 --- .github/workflows/build.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ea43ee4fb8..f69f2ab5a9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,8 +1,7 @@ name: Builds on: - push: - branch: "github-action-builds" + push: [tags] jobs: release: runs-on: ubuntu-latest From cde25d73a8a6abb6afdc6b9c3aa461538fbe76c8 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Fri, 12 Jun 2020 20:00:22 +0100 Subject: [PATCH 05/51] WIP 5 --- .github/workflows/build.yml | 44 +++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f69f2ab5a9..b961fb6dbb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,27 +1,28 @@ name: Builds on: - push: [tags] -jobs: release: - runs-on: ubuntu-latest - steps: - - name: Create Release - id: create_release - uses: actions/create-release@v1.1.0 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ github.ref }} - release_name: Test release - prerelease: true - - name: Output Release URL File - run: echo "${{ steps.create_release.outputs.upload_url }}" > release_url.txt - - name: Save Release URL File for publish - uses: actions/upload-artifact@v1 - with: - name: release_url - path: release_url.txt + types: [created] +jobs: + # release: + # runs-on: ubuntu-latest + # steps: + # - name: Create Release + # id: create_release + # uses: actions/create-release@v1.1.0 + # env: + # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # with: + # tag_name: ${{ github.ref }} + # release_name: Test release + # prerelease: true + # - name: Output Release URL File + # run: echo "${{ steps.create_release.outputs.upload_url }}" > release_url.txt + # - name: Save Release URL File for publish + # uses: actions/upload-artifact@v1 + # with: + # name: release_url + # path: release_url.txt build: runs-on: ${{ matrix.os }} @@ -81,7 +82,8 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - upload_url: ${{ steps.get_release_info.outputs.upload_url }} + # upload_url: ${{ steps.get_release_info.outputs.upload_url }} + upload_url: ${{ github.event.release.upload_url }} asset_path: ${{ steps.find_binary.outputs.hls_binary }} asset_name: haskell-language-server-${{ runner.OS }}-${{ matrix.ghc }} asset_content_type: application/octet-stream From c328c80a9c6a1f6d4a566a70eb9b69b336446f4f Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Fri, 12 Jun 2020 20:00:56 +0100 Subject: [PATCH 06/51] WIP 6 --- .github/workflows/build.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b961fb6dbb..5a32e3e5be 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: build: runs-on: ${{ matrix.os }} - needs: [release] + # needs: [release] strategy: fail-fast: false @@ -45,15 +45,15 @@ jobs: uses: actions/download-artifact@v1 with: name: release_url - - name: Get Release File Name & Upload URL - id: get_release_info - shell: bash - run: | - VALUE=`cat release_url/release_url.txt` - echo ::set-output name=upload_url::$VALUE - env: - TAG_REF_NAME: ${{ github.ref }} - REPOSITORY_NAME: ${{ github.repository }} + # - name: Get Release File Name & Upload URL + # id: get_release_info + # shell: bash + # run: | + # VALUE=`cat release_url/release_url.txt` + # echo ::set-output name=upload_url::$VALUE + # env: + # TAG_REF_NAME: ${{ github.ref }} + # REPOSITORY_NAME: ${{ github.repository }} - uses: actions/setup-haskell@v1.1.1 with: ghc-version: ${{ matrix.ghc }} From 2a5b400916aef64b132b5b86a586ad3d5c5ce311 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Fri, 12 Jun 2020 20:03:04 +0100 Subject: [PATCH 07/51] WIP 7 --- .github/workflows/build.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5a32e3e5be..d3935ba9e4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,10 +41,10 @@ jobs: - uses: actions/checkout@v2 with: submodules: true - - name: Load Release URL File from release job - uses: actions/download-artifact@v1 - with: - name: release_url + # - name: Load Release URL File from release job + # uses: actions/download-artifact@v1 + # with: + # name: release_url # - name: Get Release File Name & Upload URL # id: get_release_info # shell: bash From ffe3d4e91c0eb09e9c8c00de78d63685a911509d Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Fri, 12 Jun 2020 21:45:41 +0100 Subject: [PATCH 08/51] WIP 8 --- .github/workflows/build.yml | 35 +---------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d3935ba9e4..ba35fbda16 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,29 +4,9 @@ on: release: types: [created] jobs: - # release: - # runs-on: ubuntu-latest - # steps: - # - name: Create Release - # id: create_release - # uses: actions/create-release@v1.1.0 - # env: - # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # with: - # tag_name: ${{ github.ref }} - # release_name: Test release - # prerelease: true - # - name: Output Release URL File - # run: echo "${{ steps.create_release.outputs.upload_url }}" > release_url.txt - # - name: Save Release URL File for publish - # uses: actions/upload-artifact@v1 - # with: - # name: release_url - # path: release_url.txt build: runs-on: ${{ matrix.os }} - # needs: [release] strategy: fail-fast: false @@ -41,19 +21,6 @@ jobs: - uses: actions/checkout@v2 with: submodules: true - # - name: Load Release URL File from release job - # uses: actions/download-artifact@v1 - # with: - # name: release_url - # - name: Get Release File Name & Upload URL - # id: get_release_info - # shell: bash - # run: | - # VALUE=`cat release_url/release_url.txt` - # echo ::set-output name=upload_url::$VALUE - # env: - # TAG_REF_NAME: ${{ github.ref }} - # REPOSITORY_NAME: ${{ github.repository }} - uses: actions/setup-haskell@v1.1.1 with: ghc-version: ${{ matrix.ghc }} @@ -74,7 +41,7 @@ jobs: id: find_binary shell: bash run: | - HLS=`find dist-newstyle -name 'haskell-language-server' -type f` + HLS=$(find dist-newstyle \( -name 'haskell-language-server' -o -name 'haskell-language-server.exe' \) -type f) echo ::set-output name=hls_binary::$HLS - name: Upload Release Asset From 7c7c2071b73cb366983dfd1e7348d821c782e50e Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Sat, 13 Jun 2020 01:38:16 +0100 Subject: [PATCH 09/51] WIP 9 Use patched hie-bios to get libdir dynamically --- cabal.project | 5 +++ exe/Arguments.hs | 13 ++++++-- exe/Main.hs | 57 ++++++++++++++++++----------------- exe/Wrapper.hs | 2 +- haskell-language-server.cabal | 5 +-- src/Ide/Cradle.hs | 5 ++- 6 files changed, 54 insertions(+), 33 deletions(-) diff --git a/cabal.project b/cabal.project index 3d0f651488..c4c1823d62 100644 --- a/cabal.project +++ b/cabal.project @@ -13,6 +13,11 @@ source-repository-package location: https://github.com/peti/cabal-plan tag: 894b76c0b6bf8f7d2f881431df1f13959a8fce87 +source-repository-package + type: git + location: https://github.com/bubba/hie-bios.git + tag: 69d689a5a2f46d1ca5ceac2becda9d38613ff66a + tests: true documentation: true diff --git a/exe/Arguments.hs b/exe/Arguments.hs index 1dcf3e6da0..2ba8a470fc 100644 --- a/exe/Arguments.hs +++ b/exe/Arguments.hs @@ -14,13 +14,16 @@ module Arguments , getLibdir ) where +import Data.Char +import Data.List import Data.Maybe import Data.Version import Development.GitRev -import qualified GHC.Paths +-- import qualified GHC.Paths import Options.Applicative import Paths_haskell_language_server import System.Environment +import System.Process -- --------------------------------------------------------------------- @@ -84,7 +87,13 @@ arguments exeName = Arguments -- --------------------------------------------------------------------- -- Set the GHC libdir to the nix libdir if it's present. getLibdir :: IO FilePath -getLibdir = fromMaybe GHC.Paths.libdir <$> lookupEnv "NIX_GHC_LIBDIR" +-- getLibdir = fromMaybe GHC.Paths.libdir <$> lookupEnv "NIX_GHC_LIBDIR" +getLibdir = fromJust <$> queryLibDir + +queryLibDir :: IO (Maybe FilePath) +queryLibDir = Just . trim <$> readProcess ghcExe ["--print-libdir"] "" + where ghcExe = "ghc-" <> VERSION_ghc + trim = dropWhileEnd isSpace ghcideVersion :: IO String ghcideVersion = do diff --git a/exe/Main.hs b/exe/Main.hs index 6a2ec8c3e3..7be064c548 100644 --- a/exe/Main.hs +++ b/exe/Main.hs @@ -268,9 +268,9 @@ cradleToSessionOpts cradle file = do CradleNone -> return (Left []) pure opts -emptyHscEnv :: IORef NameCache -> IO HscEnv -emptyHscEnv nc = do - libdir <- getLibdir +emptyHscEnv :: Maybe FilePath -> IORef NameCache -> IO HscEnv +emptyHscEnv mld nc = do + libdir <- fromMaybe <$> getLibdir <*> pure mld env <- runGhc (Just libdir) getSession initDynLinker env pure $ setNameCache nc env @@ -319,7 +319,7 @@ loadSession dir = do -- which contains both. packageSetup <- return $ \(hieYaml, cfp, opts) -> do -- Parse DynFlags for the newly discovered component - hscEnv <- emptyHscEnv nc + hscEnv <- emptyHscEnv (ghcLibDir opts) nc (df, targets) <- evalGhcEnv hscEnv $ do setOptions opts (hsc_dflags hscEnv) dep_info <- getDependencyInfo (componentDependencies opts) @@ -355,7 +355,7 @@ loadSession dir = do -- It's important to keep the same NameCache though for reasons -- that I do not fully understand print ("Making new HscEnv" ++ (show inplace)) - hscEnv <- emptyHscEnv nc + hscEnv <- emptyHscEnv (ghcLibDir opts) nc newHscEnv <- -- Add the options for the current component to the HscEnv evalGhcEnv hscEnv $ do @@ -388,10 +388,13 @@ loadSession dir = do let hscEnv' = hscEnv { hsc_dflags = df , hsc_IC = (hsc_IC hscEnv) { ic_dflags = df } } - versionMismatch <- checkGhcVersion - henv <- case versionMismatch of - Just mismatch -> return mismatch - Nothing -> newHscEnvEq hscEnv' uids + -- versionMismatch <- checkGhcVersion + -- henv <- case versionMismatch of + -- Just mismatch -> return mismatch + -- Nothing -> newHscEnvEq hscEnv' uids + + henv <- newHscEnvEq hscEnv' uids + let res = (([], Just henv), di) print res @@ -437,7 +440,7 @@ loadSession dir = do Just opts -> do --putStrLn $ "Cached component of " <> show file pure ([], fst opts) - Nothing-> do + Nothing -> do finished_barrier <- newBarrier -- fork a new thread here which won't be killed by shake -- throwing an async exception @@ -554,7 +557,7 @@ setCacheDir prefix hscComponents comps dflags = do renderCradleError :: NormalizedFilePath -> CradleError -> FileDiagnostic -renderCradleError nfp (CradleError _ec t) = +renderCradleError nfp (CradleError _deps _ec t) = ideErrorText nfp (T.unlines (map T.pack t)) @@ -611,8 +614,8 @@ memoIO op = do return (Map.insert k res mp, res) Just res -> return (mp, res) -setOptions :: GhcMonad m =>ComponentOptions -> DynFlags -> m (DynFlags, [Target]) -setOptions (ComponentOptions theOpts compRoot _) dflags = do +setOptions :: GhcMonad m => ComponentOptions -> DynFlags -> m (DynFlags, [Target]) +setOptions (ComponentOptions theOpts compRoot _deps _mlibdir) dflags = do (dflags_, targets) <- addCmdOpts theOpts dflags let dflags' = makeDynFlagsAbsolute compRoot dflags_ let dflags'' = @@ -664,17 +667,17 @@ getCacheDir prefix opts = IO.getXdgDirectory IO.XdgCache (cacheDir prefix ++ cacheDir :: String cacheDir = "ghcide" -ghcVersionChecker :: IO VersionCheck -ghcVersionChecker = $$(makeGhcVersionChecker (pure <$> getLibdir)) - -checkGhcVersion :: IO (Maybe HscEnvEq) -checkGhcVersion = do - res <- ghcVersionChecker - case res of - Failure err -> do - putStrLn $ "Error while checking GHC version: " ++ show err - return Nothing - Mismatch {..} -> - return $ Just GhcVersionMismatch {..} - _ -> - return Nothing +-- ghcVersionChecker :: IO VersionCheck +-- ghcVersionChecker = $$(makeGhcVersionChecker (pure <$> getLibdir)) + +-- checkGhcVersion :: IO (Maybe HscEnvEq) +-- checkGhcVersion = do +-- res <- ghcVersionChecker +-- case res of +-- Failure err -> do +-- putStrLn $ "Error while checking GHC version: " ++ show err +-- return Nothing +-- Mismatch {..} -> +-- return $ Just GhcVersionMismatch {..} +-- _ -> +-- return Nothing diff --git a/exe/Wrapper.hs b/exe/Wrapper.hs index 395eae6972..3a679e00f9 100644 --- a/exe/Wrapper.hs +++ b/exe/Wrapper.hs @@ -65,7 +65,7 @@ main = do logm $ "args:" ++ show args -- Get the cabal directory from the cradle - cradle <- findLocalCradle (d "File.hs") + cradle <- findLocalCradle d let dir = cradleRootDir cradle logm $ "Cradle directory:" ++ dir setCurrentDirectory dir diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index d76451a0a5..450d9c40ed 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -144,6 +144,7 @@ executable haskell-language-server , directory , extra , filepath + , process -------------------------------------------------------------- -- The MIN_GHC_API_VERSION macro relies on MIN_VERSION pragmas -- which require depending on ghc. So the tests need to depend @@ -153,7 +154,7 @@ executable haskell-language-server , ghc -------------------------------------------------------------- , ghc-check >= 0.3.0.1 && < 0.4 - , ghc-paths + -- , ghc-paths , ghcide , gitrev , hashable @@ -198,7 +199,7 @@ executable haskell-language-server-wrapper , filepath , gitrev , ghc - , ghc-paths + -- , ghc-paths , hie-bios , haskell-language-server , optparse-applicative diff --git a/src/Ide/Cradle.hs b/src/Ide/Cradle.hs index 9211df2453..f3df72938a 100644 --- a/src/Ide/Cradle.hs +++ b/src/Ide/Cradle.hs @@ -54,7 +54,7 @@ findLocalCradle fp = do debugm $ "Found \"" ++ yaml ++ "\" for \"" ++ fp ++ "\"" crdl <- Bios.loadCradle yaml return $ fmap (const CabalNone) crdl - Nothing -> cabalHelperCradle fp + Nothing -> Bios.loadImplicitCradle fp -- cabalHelperCradle fp logm $ "Module \"" ++ fp ++ "\" is loaded by Cradle: " ++ show crdl return crdl @@ -469,6 +469,7 @@ cabalHelperCradle file = do { componentOptions = [file, fixImportDirs cwd "-i."] , componentRoot = cwd , componentDependencies = [] + , ghcLibDir = Nothing } } } @@ -554,10 +555,12 @@ cabalHelperAction proj env package root fp = do ComponentOptions { componentOptions = ghcOptions , componentRoot = root , componentDependencies = [] + , ghcLibDir = Nothing } Left err -> return $ CradleFail $ CradleError + [] (ExitFailure 2) err From bf59f2923abab16c39e7050d16be3157378c8185 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Sat, 13 Jun 2020 02:55:19 +0100 Subject: [PATCH 10/51] Try building the wrapper --- .github/workflows/build.yml | 40 ++++++++++++++++++++++++++++------- exe/Arguments.hs | 25 +++++++++++++--------- exe/Main.hs | 12 +++++------ exe/Wrapper.hs | 3 +++ haskell-language-server.cabal | 4 ++-- 5 files changed, 58 insertions(+), 26 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ba35fbda16..7efee2eee2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,12 +11,15 @@ jobs: strategy: fail-fast: false matrix: - ghc: ['8.10.1', '8.8.3', '8.6.5'] # , '8.4.4'] + ghc: ['8.10.1', '8.8.3', '8.6.5'] os: [ubuntu-latest, macOS-latest, windows-latest] exclude: - os: windows-latest ghc: '8.8.3' # fails due to segfault + env: + build_wrapper: ${{ matrix.ghc == '8.10.1' }} + steps: - uses: actions/checkout@v2 with: @@ -25,7 +28,6 @@ jobs: with: ghc-version: ${{ matrix.ghc }} cabal-version: '3.2' - - run: cabal update # - name: Cache Cabal # uses: actions/cache@v1.2.0 @@ -34,23 +36,45 @@ jobs: # key: ${{ runner.OS }}-${{ matrix.ghc }}-cabal-0 - - name: Build + - name: Build Server run: cabal build exe:haskell-language-server - - name: Find binary - id: find_binary + - name: Find Server Binary + id: find_server_binary shell: bash run: | HLS=$(find dist-newstyle \( -name 'haskell-language-server' -o -name 'haskell-language-server.exe' \) -type f) echo ::set-output name=hls_binary::$HLS - - name: Upload Release Asset + - name: Upload Server Binary uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - # upload_url: ${{ steps.get_release_info.outputs.upload_url }} upload_url: ${{ github.event.release.upload_url }} - asset_path: ${{ steps.find_binary.outputs.hls_binary }} + asset_path: ${{ steps.find_server_binary.outputs.hls_binary }} asset_name: haskell-language-server-${{ runner.OS }}-${{ matrix.ghc }} asset_content_type: application/octet-stream + + - name: Build Wrapper + if: ${{ env.build_wrapper }} + run: cabal build exe:haskell-language-server-wrapper + + - name: Find Wrapper Binary + if: ${{ env.build_wrapper }} + id: find_wrapper_binary + shell: bash + run: | + HLS_WRAPPER=$(find dist-newstyle \( -name 'haskell-language-server-wrapper' -o -name 'haskell-language-server-wrapper.exe' \) -type f) + echo ::set-output name=hls_wrapper_binary::$HLS_WRAPPER + + - name: Upload Wrapper + if: ${{ env.build_wrapper }} + uses: actions/upload-release-asset@v1.0.2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ github.event.release.upload_url }} + asset_path: ${{ steps.find_wrapper_binary.outputs.hls_wrapper_binary }} + asset_name: haskell-language-server-wrapper-${{ runner.OS }} + asset_content_type: application/octet-stream diff --git a/exe/Arguments.hs b/exe/Arguments.hs index 2ba8a470fc..14f1d684a5 100644 --- a/exe/Arguments.hs +++ b/exe/Arguments.hs @@ -11,7 +11,7 @@ module Arguments ( Arguments(..) , getArguments , ghcideVersion - , getLibdir + , getGhcLibDir ) where import Data.Char @@ -19,7 +19,8 @@ import Data.List import Data.Maybe import Data.Version import Development.GitRev --- import qualified GHC.Paths +import qualified GHC.Paths +import HIE.Bios.Types import Options.Applicative import Paths_haskell_language_server import System.Environment @@ -40,6 +41,7 @@ data Arguments = Arguments , argsDebugOn :: Bool , argsLogFile :: Maybe String , argsThreads :: Int + , argsProjectGhcVersion :: Bool } deriving Show getArguments :: String -> IO Arguments @@ -83,17 +85,20 @@ arguments exeName = Arguments <> value 0 <> showDefault ) + <*> switch (long "project-ghc-version" + <> help "Work out the project GHC version and print it") -- --------------------------------------------------------------------- -- Set the GHC libdir to the nix libdir if it's present. -getLibdir :: IO FilePath --- getLibdir = fromMaybe GHC.Paths.libdir <$> lookupEnv "NIX_GHC_LIBDIR" -getLibdir = fromJust <$> queryLibDir - -queryLibDir :: IO (Maybe FilePath) -queryLibDir = Just . trim <$> readProcess ghcExe ["--print-libdir"] "" - where ghcExe = "ghc-" <> VERSION_ghc - trim = dropWhileEnd isSpace +getGhcLibDir :: ComponentOptions -> IO FilePath +getGhcLibDir opts = do + nixLibDir <- lookupEnv "NIX_GHC_LIBDIR" + -- We want to avoid using ghc-paths, as it is not portable + -- in the static binary sense - it just bakes in the path to the + -- libraries at compile time! This is ok if the user built from + -- source, but if they downloaoded a binary then this will return + -- some path that doesn't exist on their computer. + return $ fromMaybe GHC.Paths.libdir (nixLibDir <|> ghcLibDir opts) ghcideVersion :: IO String ghcideVersion = do diff --git a/exe/Main.hs b/exe/Main.hs index 7be064c548..297ce75f2f 100644 --- a/exe/Main.hs +++ b/exe/Main.hs @@ -268,10 +268,9 @@ cradleToSessionOpts cradle file = do CradleNone -> return (Left []) pure opts -emptyHscEnv :: Maybe FilePath -> IORef NameCache -> IO HscEnv -emptyHscEnv mld nc = do - libdir <- fromMaybe <$> getLibdir <*> pure mld - env <- runGhc (Just libdir) getSession +emptyHscEnv :: FilePath -> IORef NameCache -> IO HscEnv +emptyHscEnv libDir nc = do + env <- runGhc (Just libDir) getSession initDynLinker env pure $ setNameCache nc env @@ -319,7 +318,8 @@ loadSession dir = do -- which contains both. packageSetup <- return $ \(hieYaml, cfp, opts) -> do -- Parse DynFlags for the newly discovered component - hscEnv <- emptyHscEnv (ghcLibDir opts) nc + libdir <- getGhcLibDir opts + hscEnv <- emptyHscEnv libdir nc (df, targets) <- evalGhcEnv hscEnv $ do setOptions opts (hsc_dflags hscEnv) dep_info <- getDependencyInfo (componentDependencies opts) @@ -355,7 +355,7 @@ loadSession dir = do -- It's important to keep the same NameCache though for reasons -- that I do not fully understand print ("Making new HscEnv" ++ (show inplace)) - hscEnv <- emptyHscEnv (ghcLibDir opts) nc + hscEnv <- emptyHscEnv libdir nc newHscEnv <- -- Add the options for the current component to the HscEnv evalGhcEnv hscEnv $ do diff --git a/exe/Wrapper.hs b/exe/Wrapper.hs index 3a679e00f9..5f2a54ee64 100644 --- a/exe/Wrapper.hs +++ b/exe/Wrapper.hs @@ -37,6 +37,9 @@ main = do -- then the language server will not work Arguments{..} <- getArguments "haskell-language-server-wrapper" + when argsProjectGhcVersion $ + getCurrentDirectory >>= loadImplicitCradle >>= getProjectGhcVersion >>= putStrLn >> exitSuccess + if argsVersion then ghcideVersion >>= putStrLn >> exitSuccess else hPutStrLn stderr {- see WARNING above -} =<< ghcideVersion diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 450d9c40ed..51f401c517 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -154,7 +154,7 @@ executable haskell-language-server , ghc -------------------------------------------------------------- , ghc-check >= 0.3.0.1 && < 0.4 - -- , ghc-paths + , ghc-paths , ghcide , gitrev , hashable @@ -199,7 +199,7 @@ executable haskell-language-server-wrapper , filepath , gitrev , ghc - -- , ghc-paths + , ghc-paths , hie-bios , haskell-language-server , optparse-applicative From 0ace84b55433acc16fa247acb7a6bda36e9b7bfb Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Sat, 13 Jun 2020 14:17:41 +0100 Subject: [PATCH 11/51] Try to fix build_wrapper env variable not being picked up --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7efee2eee2..e80fd7fd4c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -57,11 +57,11 @@ jobs: asset_content_type: application/octet-stream - name: Build Wrapper - if: ${{ env.build_wrapper }} + if: env.build_wrapper run: cabal build exe:haskell-language-server-wrapper - name: Find Wrapper Binary - if: ${{ env.build_wrapper }} + if: env.build_wrapper id: find_wrapper_binary shell: bash run: | @@ -69,7 +69,7 @@ jobs: echo ::set-output name=hls_wrapper_binary::$HLS_WRAPPER - name: Upload Wrapper - if: ${{ env.build_wrapper }} + if: env.build_wrapper uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 32c5937848282668884a0681ded5d4da6f9f6e98 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Sat, 13 Jun 2020 16:32:49 +0100 Subject: [PATCH 12/51] Try again --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e80fd7fd4c..546bf991ee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -57,11 +57,11 @@ jobs: asset_content_type: application/octet-stream - name: Build Wrapper - if: env.build_wrapper + if: env.build_wrapper == true run: cabal build exe:haskell-language-server-wrapper - name: Find Wrapper Binary - if: env.build_wrapper + if: env.build_wrapper == true id: find_wrapper_binary shell: bash run: | @@ -69,7 +69,7 @@ jobs: echo ::set-output name=hls_wrapper_binary::$HLS_WRAPPER - name: Upload Wrapper - if: env.build_wrapper + if: env.build_wrapper == true uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 1a66953e5deafafca7539ff7002d841423136792 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Sat, 13 Jun 2020 17:05:06 +0100 Subject: [PATCH 13/51] Give up on the env var idea --- .github/workflows/build.yml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 546bf991ee..7609fd7a74 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,9 +17,6 @@ jobs: - os: windows-latest ghc: '8.8.3' # fails due to segfault - env: - build_wrapper: ${{ matrix.ghc == '8.10.1' }} - steps: - uses: actions/checkout@v2 with: @@ -57,11 +54,11 @@ jobs: asset_content_type: application/octet-stream - name: Build Wrapper - if: env.build_wrapper == true + if: matrix.ghc == '8.10.1' run: cabal build exe:haskell-language-server-wrapper - name: Find Wrapper Binary - if: env.build_wrapper == true + if: matrix.ghc == '8.10.1' id: find_wrapper_binary shell: bash run: | @@ -69,7 +66,7 @@ jobs: echo ::set-output name=hls_wrapper_binary::$HLS_WRAPPER - name: Upload Wrapper - if: env.build_wrapper == true + if: matrix.ghc == '8.10.1' uses: actions/upload-release-asset@v1.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 5d3fed7b3eef8b6faf91ebcd666a91a4012e6068 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Sat, 13 Jun 2020 17:12:08 +0100 Subject: [PATCH 14/51] Try out static optimised builds? --- .github/workflows/build.yml | 4 ++-- cabal.project | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7609fd7a74..581d287781 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,7 +34,7 @@ jobs: - name: Build Server - run: cabal build exe:haskell-language-server + run: cabal build exe:haskell-language-server -O2 --ghc-options='-static' - name: Find Server Binary id: find_server_binary @@ -55,7 +55,7 @@ jobs: - name: Build Wrapper if: matrix.ghc == '8.10.1' - run: cabal build exe:haskell-language-server-wrapper + run: cabal build exe:haskell-language-server-wrapper -O2 --ghc-options='-static' - name: Find Wrapper Binary if: matrix.ghc == '8.10.1' diff --git a/cabal.project b/cabal.project index c4c1823d62..1a820d6ad3 100644 --- a/cabal.project +++ b/cabal.project @@ -16,7 +16,7 @@ source-repository-package source-repository-package type: git location: https://github.com/bubba/hie-bios.git - tag: 69d689a5a2f46d1ca5ceac2becda9d38613ff66a + tag: c9411178 tests: true documentation: true From 191272a62af7b934f628517b231b75461b3beebb Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Mon, 15 Jun 2020 16:14:01 +0100 Subject: [PATCH 15/51] Try squashing the working dir --- .github/workflows/build.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 581d287781..a495adb251 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,11 +1,17 @@ name: Builds on: + push: + branches: + - github-action-builds release: types: [created] jobs: build: + defaults: + run: + working-directory: h runs-on: ${{ matrix.os }} strategy: @@ -18,9 +24,12 @@ jobs: ghc: '8.8.3' # fails due to segfault steps: + - shell: bash + run: echo $GITHUB_WORKSPACE - uses: actions/checkout@v2 with: submodules: true + path: '../h' - uses: actions/setup-haskell@v1.1.1 with: ghc-version: ${{ matrix.ghc }} @@ -34,7 +43,7 @@ jobs: - name: Build Server - run: cabal build exe:haskell-language-server -O2 --ghc-options='-static' + run: cabal build exe:haskell-language-server -O2 - name: Find Server Binary id: find_server_binary @@ -55,7 +64,7 @@ jobs: - name: Build Wrapper if: matrix.ghc == '8.10.1' - run: cabal build exe:haskell-language-server-wrapper -O2 --ghc-options='-static' + run: cabal build exe:haskell-language-server-wrapper -O2 - name: Find Wrapper Binary if: matrix.ghc == '8.10.1' From a3b309c8e04fc58c91ed3656f46a4ba8f7fb9141 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Mon, 15 Jun 2020 16:14:53 +0100 Subject: [PATCH 16/51] Woops --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a495adb251..75d6fdba94 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,12 +24,12 @@ jobs: ghc: '8.8.3' # fails due to segfault steps: - - shell: bash - run: echo $GITHUB_WORKSPACE - uses: actions/checkout@v2 with: submodules: true path: '../h' + - shell: bash + run: echo $GITHUB_WORKSPACE - uses: actions/setup-haskell@v1.1.1 with: ghc-version: ${{ matrix.ghc }} From a626ffc20a8da60c63f6ccd8d75cec361d0bce16 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Mon, 15 Jun 2020 16:17:12 +0100 Subject: [PATCH 17/51] Try squashing the builddir --- .github/workflows/build.yml | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 75d6fdba94..f1e815c3e7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,9 +9,6 @@ on: jobs: build: - defaults: - run: - working-directory: h runs-on: ${{ matrix.os }} strategy: @@ -27,9 +24,6 @@ jobs: - uses: actions/checkout@v2 with: submodules: true - path: '../h' - - shell: bash - run: echo $GITHUB_WORKSPACE - uses: actions/setup-haskell@v1.1.1 with: ghc-version: ${{ matrix.ghc }} @@ -43,13 +37,13 @@ jobs: - name: Build Server - run: cabal build exe:haskell-language-server -O2 + run: cabal build exe:haskell-language-server -O2 --builddir=b - name: Find Server Binary id: find_server_binary shell: bash run: | - HLS=$(find dist-newstyle \( -name 'haskell-language-server' -o -name 'haskell-language-server.exe' \) -type f) + HLS=$(find b \( -name 'haskell-language-server' -o -name 'haskell-language-server.exe' \) -type f) echo ::set-output name=hls_binary::$HLS - name: Upload Server Binary @@ -64,14 +58,14 @@ jobs: - name: Build Wrapper if: matrix.ghc == '8.10.1' - run: cabal build exe:haskell-language-server-wrapper -O2 + run: cabal build exe:haskell-language-server-wrapper -O2 --builddir=b - name: Find Wrapper Binary if: matrix.ghc == '8.10.1' id: find_wrapper_binary shell: bash run: | - HLS_WRAPPER=$(find dist-newstyle \( -name 'haskell-language-server-wrapper' -o -name 'haskell-language-server-wrapper.exe' \) -type f) + HLS_WRAPPER=$(find b \( -name 'haskell-language-server-wrapper' -o -name 'haskell-language-server-wrapper.exe' \) -type f) echo ::set-output name=hls_wrapper_binary::$HLS_WRAPPER - name: Upload Wrapper From 4219d519730ee709dce64c20e40b0545a6fec0ad Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Mon, 15 Jun 2020 21:00:18 +0100 Subject: [PATCH 18/51] Try going into the parent directory --- .github/workflows/build.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f1e815c3e7..e7b2c817da 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,9 +1,6 @@ name: Builds on: - push: - branches: - - github-action-builds release: types: [created] jobs: @@ -37,13 +34,13 @@ jobs: - name: Build Server - run: cabal build exe:haskell-language-server -O2 --builddir=b + run: cabal build exe:haskell-language-server -O2 --builddir=../b --disable-documentation - name: Find Server Binary id: find_server_binary shell: bash run: | - HLS=$(find b \( -name 'haskell-language-server' -o -name 'haskell-language-server.exe' \) -type f) + HLS=$(find ../b \( -name 'haskell-language-server' -o -name 'haskell-language-server.exe' \) -type f) echo ::set-output name=hls_binary::$HLS - name: Upload Server Binary @@ -58,14 +55,14 @@ jobs: - name: Build Wrapper if: matrix.ghc == '8.10.1' - run: cabal build exe:haskell-language-server-wrapper -O2 --builddir=b + run: cabal build exe:haskell-language-server-wrapper -O2 --builddir=../b --disable-documentation -v - name: Find Wrapper Binary if: matrix.ghc == '8.10.1' id: find_wrapper_binary shell: bash run: | - HLS_WRAPPER=$(find b \( -name 'haskell-language-server-wrapper' -o -name 'haskell-language-server-wrapper.exe' \) -type f) + HLS_WRAPPER=$(find ../b \( -name 'haskell-language-server-wrapper' -o -name 'haskell-language-server-wrapper.exe' \) -type f) echo ::set-output name=hls_wrapper_binary::$HLS_WRAPPER - name: Upload Wrapper @@ -78,3 +75,4 @@ jobs: asset_path: ${{ steps.find_wrapper_binary.outputs.hls_wrapper_binary }} asset_name: haskell-language-server-wrapper-${{ runner.OS }} asset_content_type: application/octet-stream + From dc99a505fbc97428385aba3a8effd9e0e9c88e45 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Mon, 15 Jun 2020 21:51:51 +0100 Subject: [PATCH 19/51] Radical approach - don't use such a long name for the wrapper --- .github/workflows/build.yml | 8 +++--- cabal.project | 2 +- exe/Arguments.hs | 17 ------------ exe/Main.hs | 52 ++++++++++++++++++++++------------- exe/Wrapper.hs | 21 +++++++------- haskell-language-server.cabal | 22 ++++++++++----- src/Ide/Cradle.hs | 28 +++++++------------ 7 files changed, 73 insertions(+), 77 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e7b2c817da..830af811a9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,13 +34,13 @@ jobs: - name: Build Server - run: cabal build exe:haskell-language-server -O2 --builddir=../b --disable-documentation + run: cabal build exe:haskell-language-server -O2 --disable-documentation - name: Find Server Binary id: find_server_binary shell: bash run: | - HLS=$(find ../b \( -name 'haskell-language-server' -o -name 'haskell-language-server.exe' \) -type f) + HLS=$(find dist-newstyle \( -name 'haskell-language-server' -o -name 'haskell-language-server.exe' \) -type f) echo ::set-output name=hls_binary::$HLS - name: Upload Server Binary @@ -55,14 +55,14 @@ jobs: - name: Build Wrapper if: matrix.ghc == '8.10.1' - run: cabal build exe:haskell-language-server-wrapper -O2 --builddir=../b --disable-documentation -v + run: cabal build exe:hls-wrapper -O2 --disable-documentation - name: Find Wrapper Binary if: matrix.ghc == '8.10.1' id: find_wrapper_binary shell: bash run: | - HLS_WRAPPER=$(find ../b \( -name 'haskell-language-server-wrapper' -o -name 'haskell-language-server-wrapper.exe' \) -type f) + HLS_WRAPPER=$(find dist-newstyle \( -name 'hls-wrapper' -o -name 'hls-wrapper.exe' \) -type f) echo ::set-output name=hls_wrapper_binary::$HLS_WRAPPER - name: Upload Wrapper diff --git a/cabal.project b/cabal.project index 1a820d6ad3..8d8c5072a3 100644 --- a/cabal.project +++ b/cabal.project @@ -16,7 +16,7 @@ source-repository-package source-repository-package type: git location: https://github.com/bubba/hie-bios.git - tag: c9411178 + tag: 6df34cb7df3d380a2ef245d4fe54c18ce6874f48 tests: true documentation: true diff --git a/exe/Arguments.hs b/exe/Arguments.hs index 14f1d684a5..bb914c456d 100644 --- a/exe/Arguments.hs +++ b/exe/Arguments.hs @@ -11,20 +11,13 @@ module Arguments ( Arguments(..) , getArguments , ghcideVersion - , getGhcLibDir ) where -import Data.Char -import Data.List -import Data.Maybe import Data.Version import Development.GitRev -import qualified GHC.Paths -import HIE.Bios.Types import Options.Applicative import Paths_haskell_language_server import System.Environment -import System.Process -- --------------------------------------------------------------------- @@ -89,16 +82,6 @@ arguments exeName = Arguments <> help "Work out the project GHC version and print it") -- --------------------------------------------------------------------- --- Set the GHC libdir to the nix libdir if it's present. -getGhcLibDir :: ComponentOptions -> IO FilePath -getGhcLibDir opts = do - nixLibDir <- lookupEnv "NIX_GHC_LIBDIR" - -- We want to avoid using ghc-paths, as it is not portable - -- in the static binary sense - it just bakes in the path to the - -- libraries at compile time! This is ok if the user built from - -- source, but if they downloaoded a binary then this will return - -- some path that doesn't exist on their computer. - return $ fromMaybe GHC.Paths.libdir (nixLibDir <|> ghcLibDir opts) ghcideVersion :: IO String ghcideVersion = do diff --git a/exe/Main.hs b/exe/Main.hs index 297ce75f2f..ec3973c65c 100644 --- a/exe/Main.hs +++ b/exe/Main.hs @@ -1,6 +1,6 @@ -- Copyright (c) 2019 The DAML Authors. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE CPP #-} -- To get precise GHC version +{-# LANGUAGE CPP #-} -- To get precise GHC version and check if distributed binary {-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -Wno-dodgy-imports #-} -- GHC no longer exports def in GHC 8.6 and above {-# LANGUAGE DeriveGeneric #-} @@ -58,7 +58,7 @@ import GHC hiding (def) import GHC.Check ( VersionCheck(..), makeGhcVersionChecker ) -- import GhcMonad import HIE.Bios.Cradle -import HIE.Bios.Environment (addCmdOpts, makeDynFlagsAbsolute) +import HIE.Bios.Environment (addCmdOpts, makeDynFlagsAbsolute, getRuntimeGhcLibDir) import HIE.Bios.Types import HscTypes (HscEnv(..), ic_dflags) import qualified Language.Haskell.LSP.Core as LSP @@ -316,10 +316,9 @@ loadSession dir = do -- If the hieYaml file already has an HscEnv, the new component is -- combined with the components in the old HscEnv into a new HscEnv -- which contains both. - packageSetup <- return $ \(hieYaml, cfp, opts) -> do + packageSetup <- return $ \(hieYaml, cfp, opts, libDir) -> do -- Parse DynFlags for the newly discovered component - libdir <- getGhcLibDir opts - hscEnv <- emptyHscEnv libdir nc + hscEnv <- emptyHscEnv libDir nc (df, targets) <- evalGhcEnv hscEnv $ do setOptions opts (hsc_dflags hscEnv) dep_info <- getDependencyInfo (componentDependencies opts) @@ -355,7 +354,7 @@ loadSession dir = do -- It's important to keep the same NameCache though for reasons -- that I do not fully understand print ("Making new HscEnv" ++ (show inplace)) - hscEnv <- emptyHscEnv libdir nc + hscEnv <- emptyHscEnv libDir nc newHscEnv <- -- Add the options for the current component to the HscEnv evalGhcEnv hscEnv $ do @@ -372,8 +371,8 @@ loadSession dir = do pure (Map.insert hieYaml (newHscEnv, new_deps) m, (newHscEnv, head new_deps', tail new_deps')) - session <- return $ \(hieYaml, cfp, opts) -> do - (hscEnv, new, old_deps) <- packageSetup (hieYaml, cfp, opts) + session <- return $ \(hieYaml, cfp, opts, libDir) -> do + (hscEnv, new, old_deps) <- packageSetup (hieYaml, cfp, opts, libDir) -- TODO Handle the case where there is no hie.yaml -- Make a map from unit-id to DynFlags, this is used when trying to -- resolve imports. @@ -447,19 +446,34 @@ loadSession dir = do void $ forkIO $ do putStrLn $ "Consulting the cradle for " <> show file cradle <- maybe (loadImplicitCradle $ addTrailingPathSeparator dir) loadCradle hieYaml + + -- we don't want to use ghc-paths if this is a portable, distributed binary + mLibDir <- getRuntimeGhcLibDir cradle + #ifdef DIST_BINARY + True + #else + False + #endif + eopts <- cradleToSessionOpts cradle cfp print eopts - case eopts of - Right opts -> do - (cs, res) <- session (hieYaml, toNormalizedFilePath' cfp, opts) + + let ncfp = toNormalizedFilePath' cfp + cradleMishap diags = do + dep_info <- getDependencyInfo ([fp | Just fp <- [hieYaml]]) + let res = (diags, Nothing) + modifyVar_ fileToFlags $ \var -> do + pure $ Map.insertWith HM.union hieYaml (HM.singleton ncfp (res, dep_info)) var + signalBarrier finished_barrier ([(ncfp, (res, dep_info) )], res) + + case (eopts, mLibDir) of + (Right opts, Just libDir) -> do + (cs, res) <- session (hieYaml, toNormalizedFilePath' cfp, opts, libDir) signalBarrier finished_barrier (cs, fst res) - Left err -> do - dep_info <- getDependencyInfo ([fp | Just fp <- [hieYaml]]) - let ncfp = toNormalizedFilePath' cfp - let res = (map (renderCradleError ncfp) err, Nothing) - modifyVar_ fileToFlags $ \var -> do - pure $ Map.insertWith HM.union hieYaml (HM.singleton ncfp (res, dep_info)) var - signalBarrier finished_barrier ([(ncfp, (res, dep_info) )], res) + + (Left err, _) -> cradleMishap $ map (renderCradleError ncfp) err + (_, Nothing) -> cradleMishap [ideErrorText ncfp "Couldn't get the GHC library directory"] + waitBarrier finished_barrier dummyAs <- async $ return (error "Uninitialised") @@ -615,7 +629,7 @@ memoIO op = do Just res -> return (mp, res) setOptions :: GhcMonad m => ComponentOptions -> DynFlags -> m (DynFlags, [Target]) -setOptions (ComponentOptions theOpts compRoot _deps _mlibdir) dflags = do +setOptions (ComponentOptions theOpts compRoot _deps) dflags = do (dflags_, targets) <- addCmdOpts theOpts dflags let dflags' = makeDynFlagsAbsolute compRoot dflags_ let dflags'' = diff --git a/exe/Wrapper.hs b/exe/Wrapper.hs index 5f2a54ee64..3dad06ea95 100644 --- a/exe/Wrapper.hs +++ b/exe/Wrapper.hs @@ -24,7 +24,6 @@ import Ide.Version import System.Directory import System.Environment import System.Exit -import System.FilePath import System.IO import System.Info import System.Process @@ -36,9 +35,17 @@ main = do -- WARNING: If you write to stdout before runLanguageServer -- then the language server will not work Arguments{..} <- getArguments "haskell-language-server-wrapper" + + d <- getCurrentDirectory + + -- Get the cabal directory from the cradle + cradle <- findLocalCradle d + let dir = cradleRootDir cradle + setCurrentDirectory dir + + ghcVersion <- getProjectGhcVersion cradle - when argsProjectGhcVersion $ - getCurrentDirectory >>= loadImplicitCradle >>= getProjectGhcVersion >>= putStrLn >> exitSuccess + when argsProjectGhcVersion $ putStrLn ghcVersion >> exitSuccess if argsVersion then ghcideVersion >>= putStrLn >> exitSuccess else hPutStrLn stderr {- see WARNING above -} =<< ghcideVersion @@ -61,19 +68,11 @@ main = do progName <- getProgName logm $ "run entered for haskell-language-server-wrapper(" ++ progName ++ ") " ++ hlsVersion - d <- getCurrentDirectory logm $ "Current directory:" ++ d logm $ "Operating system:" ++ os args <- getArgs logm $ "args:" ++ show args - - -- Get the cabal directory from the cradle - cradle <- findLocalCradle d - let dir = cradleRootDir cradle logm $ "Cradle directory:" ++ dir - setCurrentDirectory dir - - ghcVersion <- getProjectGhcVersion cradle logm $ "Project GHC version:" ++ ghcVersion let diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 51f401c517..817298a17c 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -26,17 +26,25 @@ flag pedantic Default: False Manual: True +flag dist-binary + Description: Build haskell-language-server ready for distributing binaries + Default: False + Manual: True + source-repository head type: git location: https://github.com/haskell/haskell-language-server -common agpl +common cpp-flags if flag(agpl) cpp-options: -DAGPL + if flag(dist-binary) + cpp-options: + -DDIST_BINARY library - import: agpl + import: cpp-flags exposed-modules: Ide.Cradle Ide.Logger @@ -107,7 +115,7 @@ library default-language: Haskell2010 executable haskell-language-server - import: agpl + import: cpp-flags main-is: Main.hs hs-source-dirs: exe @@ -169,8 +177,8 @@ executable haskell-language-server , unordered-containers default-language: Haskell2010 -executable haskell-language-server-wrapper - import: agpl +executable hls-wrapper + import: cpp-flags main-is: Wrapper.hs hs-source-dirs: exe @@ -210,7 +218,7 @@ executable haskell-language-server-wrapper -- We removed it due to issues with stack when loading the project using a stack based hie.yaml -- See https://github.com/haskell/haskell-language-server/issues/114 common hls-test-utils - import: agpl + import: cpp-flags hs-source-dirs: test/utils other-modules: Test.Hls.Util build-depends: base @@ -238,7 +246,7 @@ common hls-test-utils default-language: Haskell2010 test-suite func-test - import: agpl, hls-test-utils + import: cpp-flags, hls-test-utils type: exitcode-stdio-1.0 default-language: Haskell2010 build-tool-depends: haskell-language-server:haskell-language-server diff --git a/src/Ide/Cradle.hs b/src/Ide/Cradle.hs index f3df72938a..912a43a578 100644 --- a/src/Ide/Cradle.hs +++ b/src/Ide/Cradle.hs @@ -54,7 +54,7 @@ findLocalCradle fp = do debugm $ "Found \"" ++ yaml ++ "\" for \"" ++ fp ++ "\"" crdl <- Bios.loadCradle yaml return $ fmap (const CabalNone) crdl - Nothing -> Bios.loadImplicitCradle fp -- cabalHelperCradle fp + Nothing -> Bios.loadImplicitCradle (fp "Foo.hs") -- cabalHelperCradle fp logm $ "Module \"" ++ fp ++ "\" is loaded by Cradle: " ++ show crdl return crdl @@ -100,7 +100,7 @@ data CabalHelper execProjectGhc :: Cradle CabalHelper -> [String] -> IO (Maybe String) execProjectGhc crdl args = do isStackInstalled <- isJust <$> findExecutable "stack" - -- isCabalInstalled <- isJust <$> findExecutable "cabal" + isCabalInstalled <- isJust <$> findExecutable "cabal" ghcOutput <- if isStackCradle crdl && isStackInstalled then do logm $ "Executing Stack GHC with args: " <> unwords args @@ -112,11 +112,11 @@ execProjectGhc crdl args = do -- This command must work though before the project is build. -- Therefore, fallback to "ghc" on the path. -- - -- else if isCabalCradle crdl && isCabalInstalled then do - -- let cmd = "cabal v2-exec -v0 ghc -- " ++ unwords args - -- catch (Just <$> tryCommand crdl cmd) $ \(_ ::IOException) -> do - -- errorm $ "Command `" ++ cmd ++ "` failed." - -- return Nothing + else if isCabalCradle crdl && isCabalInstalled then do + let cmd = "cabal v2-exec -v0 ghc -- " ++ unwords args + catch (Just <$> tryCommand crdl cmd) $ \(_ ::IOException) -> do + errorm $ "Command `" ++ cmd ++ "` failed." + return Nothing else do logm $ "Executing GHC on path with args: " <> unwords args execWithGhc @@ -153,15 +153,6 @@ tryCommand crdl cmd = do ExitSuccess -> return $ T.unpack . T.strip . head . T.lines $ T.pack sout --- | Get the directory of the libdir based on the project ghc. -getProjectGhcLibDir :: Cradle CabalHelper -> IO (Maybe FilePath) -getProjectGhcLibDir crdl = - execProjectGhc crdl ["--print-libdir"] >>= \case - Nothing -> do - errorm "Could not obtain the libdir." - return Nothing - mlibdir -> return mlibdir - -- --------------------------------------------------------------------- @@ -469,8 +460,8 @@ cabalHelperCradle file = do { componentOptions = [file, fixImportDirs cwd "-i."] , componentRoot = cwd , componentDependencies = [] - , ghcLibDir = Nothing } + , runGhcLibDir = pure Nothing } } Just (Ex proj) -> do @@ -498,6 +489,7 @@ cabalHelperCradle file = do , cradleOptsProg = CradleAction { actionName = Bios.Other (projectNoneType proj) , runCradle = \_ _ -> return CradleNone + , runGhcLibDir = pure Nothing } } Just realPackage -> do @@ -518,6 +510,7 @@ cabalHelperCradle file = do realPackage normalisedPackageLocation fp + , runGhcLibDir = pure Nothing } } @@ -555,7 +548,6 @@ cabalHelperAction proj env package root fp = do ComponentOptions { componentOptions = ghcOptions , componentRoot = root , componentDependencies = [] - , ghcLibDir = Nothing } Left err -> return $ CradleFail From 277f20ea652804f3f216dfe9dd26d37bbd4ef9e3 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Mon, 15 Jun 2020 23:15:47 +0100 Subject: [PATCH 20/51] Use dist-binary flag --- .github/workflows/build.yml | 4 ++-- exe/Main.hs | 16 ++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 830af811a9..dc01c8d296 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,7 +34,7 @@ jobs: - name: Build Server - run: cabal build exe:haskell-language-server -O2 --disable-documentation + run: cabal build exe:haskell-language-server -O2 --disable-documentation -fdist-binary - name: Find Server Binary id: find_server_binary @@ -55,7 +55,7 @@ jobs: - name: Build Wrapper if: matrix.ghc == '8.10.1' - run: cabal build exe:hls-wrapper -O2 --disable-documentation + run: cabal build exe:hls-wrapper -O2 --disable-documentation -fdist-binary - name: Find Wrapper Binary if: matrix.ghc == '8.10.1' diff --git a/exe/Main.hs b/exe/Main.hs index ec3973c65c..1bca8cb6a2 100644 --- a/exe/Main.hs +++ b/exe/Main.hs @@ -448,12 +448,7 @@ loadSession dir = do cradle <- maybe (loadImplicitCradle $ addTrailingPathSeparator dir) loadCradle hieYaml -- we don't want to use ghc-paths if this is a portable, distributed binary - mLibDir <- getRuntimeGhcLibDir cradle - #ifdef DIST_BINARY - True - #else - False - #endif + mLibDir <- getRuntimeGhcLibDir cradle isDistBinary eopts <- cradleToSessionOpts cradle cfp print eopts @@ -695,3 +690,12 @@ cacheDir = "ghcide" -- return $ Just GhcVersionMismatch {..} -- _ -> -- return Nothing +-- + + +isDistBinary :: Bool +#ifdef DIST_BINARY +isDistBinary = True +#else +isDistBinary = False +#endif From 4feadad608fb003840a91bb700374c53a1272dc6 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Tue, 16 Jun 2020 12:46:52 +0100 Subject: [PATCH 21/51] Debug why floskell fails to build on windows --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dc01c8d296..a8f77c2f65 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,6 +32,7 @@ jobs: # path: ~/.cabal # key: ${{ runner.OS }}-${{ matrix.ghc }}-cabal-0 + - run: cabal build floskell -v2 - name: Build Server run: cabal build exe:haskell-language-server -O2 --disable-documentation -fdist-binary From f4a7fb81a06ab6dbffdff76d1b042c1df57823f7 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Tue, 16 Jun 2020 15:25:10 +0100 Subject: [PATCH 22/51] haskell-language-server => hls on CI I hate that I have to do this --- .github/workflows/build.yml | 9 +++++++-- .gitignore | 3 +++ cabal.project | 2 +- haskell-language-server.cabal | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a8f77c2f65..81eb506281 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,17 +31,22 @@ jobs: # with: # path: ~/.cabal # key: ${{ runner.OS }}-${{ matrix.ghc }}-cabal-0 + + - name: Shorten binary names + # if: ${{ runner.OS == Windows }} + shell: bash + run: sed -i -e 's/executable haskell-language-server/executable hls/' haskell-language-server.cabal - run: cabal build floskell -v2 - name: Build Server - run: cabal build exe:haskell-language-server -O2 --disable-documentation -fdist-binary + run: cabal build exe:hls -O2 --disable-documentation -fdist-binary - name: Find Server Binary id: find_server_binary shell: bash run: | - HLS=$(find dist-newstyle \( -name 'haskell-language-server' -o -name 'haskell-language-server.exe' \) -type f) + HLS=$(find dist-newstyle \( -name 'hls' -o -name 'hls.exe' \) -type f) echo ::set-output name=hls_binary::$HLS - name: Upload Server Binary diff --git a/.gitignore b/.gitignore index f2807263aa..6c1421621e 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,6 @@ shake.yaml.lock # ignore hie.yaml's for testdata test/**/*.yaml + +# macOS folder metadata +.DS_Store diff --git a/cabal.project b/cabal.project index 8d8c5072a3..eaf26fdb16 100644 --- a/cabal.project +++ b/cabal.project @@ -16,7 +16,7 @@ source-repository-package source-repository-package type: git location: https://github.com/bubba/hie-bios.git - tag: 6df34cb7df3d380a2ef245d4fe54c18ce6874f48 + tag: 12f4f2f050a643ee360b197789250f0d96d003ff tests: true documentation: true diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 817298a17c..ff3a24e3f2 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -177,7 +177,7 @@ executable haskell-language-server , unordered-containers default-language: Haskell2010 -executable hls-wrapper +executable haskell-language-server-wrapper import: cpp-flags main-is: Wrapper.hs hs-source-dirs: From a91ae039d6f113a34fd976e5d2956a6ae21a672c Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Tue, 16 Jun 2020 17:32:03 +0100 Subject: [PATCH 23/51] Employ extreme path saving measures --- .github/workflows/build.yml | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 81eb506281..d608ce26c8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,6 +21,7 @@ jobs: - uses: actions/checkout@v2 with: submodules: true + path: h - uses: actions/setup-haskell@v1.1.1 with: ghc-version: ${{ matrix.ghc }} @@ -32,21 +33,21 @@ jobs: # path: ~/.cabal # key: ${{ runner.OS }}-${{ matrix.ghc }}-cabal-0 - - name: Shorten binary names - # if: ${{ runner.OS == Windows }} - shell: bash - run: sed -i -e 's/executable haskell-language-server/executable hls/' haskell-language-server.cabal + # - name: Shorten binary names + # # if: ${{ runner.OS == Windows }} + # shell: bash + # run: sed -i -e 's/executable haskell-language-server/executable hls/' haskell-language-server.cabal - run: cabal build floskell -v2 - name: Build Server - run: cabal build exe:hls -O2 --disable-documentation -fdist-binary + run: cabal build exe:haskell-language-server -O2 --disable-documentation -fdist-binary --builddir=d - name: Find Server Binary id: find_server_binary shell: bash run: | - HLS=$(find dist-newstyle \( -name 'hls' -o -name 'hls.exe' \) -type f) + HLS=$(find dist-newstyle \( -name 'haskell-language-server' -o -name 'haskell-language-server.exe' \) -type f) echo ::set-output name=hls_binary::$HLS - name: Upload Server Binary @@ -61,14 +62,14 @@ jobs: - name: Build Wrapper if: matrix.ghc == '8.10.1' - run: cabal build exe:hls-wrapper -O2 --disable-documentation -fdist-binary + run: cabal build exe:haskell-language-server-wrapper -O2 --disable-documentation -fdist-binary --builddir=d - name: Find Wrapper Binary if: matrix.ghc == '8.10.1' id: find_wrapper_binary shell: bash run: | - HLS_WRAPPER=$(find dist-newstyle \( -name 'hls-wrapper' -o -name 'hls-wrapper.exe' \) -type f) + HLS_WRAPPER=$(find dist-newstyle \( -name 'haskell-language-server-wrapper' -o -name 'haskell-language-server-wrapper.exe' \) -type f) echo ::set-output name=hls_wrapper_binary::$HLS_WRAPPER - name: Upload Wrapper From d53fb6e6f47c8961d105872aa3b04d00ad9d58be Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Tue, 16 Jun 2020 18:24:32 +0100 Subject: [PATCH 24/51] sed time :( --- .github/workflows/build.yml | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d608ce26c8..47338e3024 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -21,7 +21,6 @@ jobs: - uses: actions/checkout@v2 with: submodules: true - path: h - uses: actions/setup-haskell@v1.1.1 with: ghc-version: ${{ matrix.ghc }} @@ -33,21 +32,25 @@ jobs: # path: ~/.cabal # key: ${{ runner.OS }}-${{ matrix.ghc }}-cabal-0 - # - name: Shorten binary names - # # if: ${{ runner.OS == Windows }} - # shell: bash - # run: sed -i -e 's/executable haskell-language-server/executable hls/' haskell-language-server.cabal + - name: Shorten binary names + shell: bash + run: | + sed -i '' -e 's/haskell-language-server/hls/g' \ + -e 's/haskell_language_server/hls/g' \ + haskell-language-server.cabal + sed -i '' -e 's/Paths_haskell_language_server/Paths_hls/g' \ + src/**/*.hs exe/*.hs - run: cabal build floskell -v2 - name: Build Server - run: cabal build exe:haskell-language-server -O2 --disable-documentation -fdist-binary --builddir=d + run: cabal build exe:hls -O2 --disable-documentation -fdist-binary - name: Find Server Binary id: find_server_binary shell: bash run: | - HLS=$(find dist-newstyle \( -name 'haskell-language-server' -o -name 'haskell-language-server.exe' \) -type f) + HLS=$(find dist-newstyle \( -name 'hls' -o -name 'hls.exe' \) -type f) echo ::set-output name=hls_binary::$HLS - name: Upload Server Binary @@ -62,14 +65,14 @@ jobs: - name: Build Wrapper if: matrix.ghc == '8.10.1' - run: cabal build exe:haskell-language-server-wrapper -O2 --disable-documentation -fdist-binary --builddir=d + run: cabal build exe:hls-wrapper -O2 --disable-documentation -fdist-binary - name: Find Wrapper Binary if: matrix.ghc == '8.10.1' id: find_wrapper_binary shell: bash run: | - HLS_WRAPPER=$(find dist-newstyle \( -name 'haskell-language-server-wrapper' -o -name 'haskell-language-server-wrapper.exe' \) -type f) + HLS_WRAPPER=$(find dist-newstyle \( -name 'hls-wrapper' -o -name 'hls-wrapper.exe' \) -type f) echo ::set-output name=hls_wrapper_binary::$HLS_WRAPPER - name: Upload Wrapper From 7e0402bf8b9473a1e9916a66f99f75bcaa8e25e7 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Tue, 16 Jun 2020 20:58:55 +0100 Subject: [PATCH 25/51] Try making sed command portable --- .github/workflows/build.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 47338e3024..c051ece4e2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -35,11 +35,11 @@ jobs: - name: Shorten binary names shell: bash run: | - sed -i '' -e 's/haskell-language-server/hls/g' \ - -e 's/haskell_language_server/hls/g' \ - haskell-language-server.cabal - sed -i '' -e 's/Paths_haskell_language_server/Paths_hls/g' \ - src/**/*.hs exe/*.hs + sed -i.bak -e 's/haskell-language-server/hls/g' \ + -e 's/haskell_language_server/hls/g' \ + haskell-language-server.cabal + sed -i.bak -e 's/Paths_haskell_language_server/Paths_hls/g' \ + src/**/*.hs exe/*.hs - run: cabal build floskell -v2 From 505ee0aa0df6f0ed2aa0692498ecf8560dc19a42 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Wed, 17 Jun 2020 13:34:40 +0100 Subject: [PATCH 26/51] Compress artefacts --- .github/workflows/build.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c051ece4e2..3341bd134a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -51,7 +51,8 @@ jobs: shell: bash run: | HLS=$(find dist-newstyle \( -name 'hls' -o -name 'hls.exe' \) -type f) - echo ::set-output name=hls_binary::$HLS + gzip --best $HLS + echo ::set-output name=hls_binary::$HLS.gz - name: Upload Server Binary uses: actions/upload-release-asset@v1.0.2 @@ -60,8 +61,8 @@ jobs: with: upload_url: ${{ github.event.release.upload_url }} asset_path: ${{ steps.find_server_binary.outputs.hls_binary }} - asset_name: haskell-language-server-${{ runner.OS }}-${{ matrix.ghc }} - asset_content_type: application/octet-stream + asset_name: haskell-language-server-${{ runner.OS }}-${{ matrix.ghc }}.gz + asset_content_type: application/gzip - name: Build Wrapper if: matrix.ghc == '8.10.1' @@ -73,7 +74,8 @@ jobs: shell: bash run: | HLS_WRAPPER=$(find dist-newstyle \( -name 'hls-wrapper' -o -name 'hls-wrapper.exe' \) -type f) - echo ::set-output name=hls_wrapper_binary::$HLS_WRAPPER + gzip --best $HLS_WRAPPER + echo ::set-output name=hls_wrapper_binary::$HLS_WRAPPER.gz - name: Upload Wrapper if: matrix.ghc == '8.10.1' @@ -83,6 +85,6 @@ jobs: with: upload_url: ${{ github.event.release.upload_url }} asset_path: ${{ steps.find_wrapper_binary.outputs.hls_wrapper_binary }} - asset_name: haskell-language-server-wrapper-${{ runner.OS }} - asset_content_type: application/octet-stream + asset_name: haskell-language-server-wrapper-${{ runner.OS }}.gz + asset_content_type: application/gzip From 304f4c34068e774c852685cc38f77d6a0d645a85 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Wed, 17 Jun 2020 17:33:53 +0100 Subject: [PATCH 27/51] Tidy up wrapper logging --- exe/Main.hs | 1 + exe/Wrapper.hs | 78 ++++++++++++++++---------------------------------- 2 files changed, 26 insertions(+), 53 deletions(-) diff --git a/exe/Main.hs b/exe/Main.hs index 1bca8cb6a2..b88b0d1d60 100644 --- a/exe/Main.hs +++ b/exe/Main.hs @@ -184,6 +184,7 @@ main = do hPutStrLn stderr "Starting (haskell-language-server)LSP server..." hPutStrLn stderr $ " with arguments: " <> show args hPutStrLn stderr $ " with plugins: " <> show (Map.keys $ ipMap idePlugins') + hPutStrLn stderr $ " in directory: " <> dir hPutStrLn stderr "If you are seeing this in a terminal, you probably should have run ghcide WITHOUT the --lsp option!" runLanguageServer options (pluginHandler plugins) getInitialConfig getConfigFromNotification $ \getLspId event vfs caps -> do t <- t diff --git a/exe/Wrapper.hs b/exe/Wrapper.hs index 3dad06ea95..91be5666b1 100644 --- a/exe/Wrapper.hs +++ b/exe/Wrapper.hs @@ -1,32 +1,22 @@ -{-# LANGUAGE CPP #-} {-# LANGUAGE RecordWildCards #-} -- | This module is based on the hie-wrapper.sh script in -- https://github.com/alanz/vscode-hie-server module Main where -#if __GLASGOW_HASKELL__ < 804 -import Data.Semigroup -#endif - import Arguments --- import Control.Concurrent.Extra import Control.Monad.Extra -import Data.Foldable -import Data.List --- import Data.List.Extra --- import qualified Data.Text as T --- import qualified Data.Text.IO as T --- import Development.IDE.Types.Logger +import Data.Foldable +import Data.List import HIE.Bios -import Ide.Cradle (findLocalCradle) -import Ide.Logger (logm) -import Ide.Version -import System.Directory -import System.Environment +import HIE.Bios.Types +import Ide.Cradle (findLocalCradle) +import Ide.Version +import System.Directory +import System.Environment import System.Exit import System.IO -import System.Info -import System.Process +import System.Info +import System.Process -- --------------------------------------------------------------------- @@ -40,40 +30,25 @@ main = do -- Get the cabal directory from the cradle cradle <- findLocalCradle d - let dir = cradleRootDir cradle - setCurrentDirectory dir - - ghcVersion <- getProjectGhcVersion cradle - - when argsProjectGhcVersion $ putStrLn ghcVersion >> exitSuccess - - if argsVersion then ghcideVersion >>= putStrLn >> exitSuccess - else hPutStrLn stderr {- see WARNING above -} =<< ghcideVersion + setCurrentDirectory $ cradleRootDir cradle - -- lock to avoid overlapping output on stdout - -- lock <- newLock - -- let logger p = Logger $ \pri msg -> when (pri >= p) $ withLock lock $ - -- T.putStrLn $ T.pack ("[" ++ upper (show pri) ++ "] ") <> msg + when argsProjectGhcVersion $ getProjectGhcVersion cradle >>= putStrLn >> exitSuccess + when argsVersion $ ghcideVersion >>= putStrLn >> exitSuccess whenJust argsCwd setCurrentDirectory - -- let mLogFileName = optLogFile opts - - -- logLevel = if optDebugOn opts - -- then L.DEBUG - -- else L.INFO - - -- Core.setupLogger mLogFileName ["hie"] logLevel - progName <- getProgName - logm $ "run entered for haskell-language-server-wrapper(" ++ progName ++ ") " - ++ hlsVersion - logm $ "Current directory:" ++ d - logm $ "Operating system:" ++ os + hPutStrLn stderr $ "Run entered for haskell-language-server-wrapper(" ++ progName ++ ") " + ++ hlsVersion + hPutStrLn stderr $ "Current directory: " ++ d + hPutStrLn stderr $ "Operating system: " ++ os args <- getArgs - logm $ "args:" ++ show args - logm $ "Cradle directory:" ++ dir - logm $ "Project GHC version:" ++ ghcVersion + hPutStrLn stderr $ "Arguments: " ++ show args + hPutStrLn stderr $ "Cradle directory: " ++ cradleRootDir cradle + hPutStrLn stderr $ "Cradle type: " ++ show (actionName (cradleOptsProg cradle)) + hPutStrLn stderr $ "Consulting the cradle to get project GHC version..." + ghcVersion <- getProjectGhcVersion cradle + hPutStrLn stderr $ "Project GHC version: " ++ ghcVersion let hlsBin = "haskell-language-server-" ++ ghcVersion @@ -84,17 +59,14 @@ main = do candidates' = [hlsBin, backupHlsBin, "haskell-language-server"] candidates = map (++ exeExtension) candidates' - logm $ "haskell-language-server exe candidates :" ++ show candidates + hPutStrLn stderr $ "haskell-language-server exe candidates: " ++ show candidates mexes <- traverse findExecutable candidates case asum mexes of - Nothing -> logm $ "cannot find any haskell-language-server exe, looked for:" ++ intercalate ", " candidates + Nothing -> hPutStrLn stderr $ "Cannot find any haskell-language-server exe, looked for: " ++ intercalate ", " candidates Just e -> do - logm $ "found haskell-language-server exe at:" ++ e - logm $ "args:" ++ show args - logm "launching ....\n\n\n" + hPutStrLn stderr $ "Launching haskell-language-server exe at:" ++ e callProcess e args - logm "done" -- --------------------------------------------------------------------- From 0c48be84429d8b18c1b95226e0bf0854fbc2e8c2 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Thu, 18 Jun 2020 21:00:18 +0100 Subject: [PATCH 28/51] Use version checking logic in hie-bios --- cabal.project | 2 +- exe/Main.hs | 3 ++- exe/Wrapper.hs | 10 ++++++---- src/Ide/Cradle.hs | 5 ++--- src/Ide/Version.hs | 14 -------------- 5 files changed, 11 insertions(+), 23 deletions(-) diff --git a/cabal.project b/cabal.project index eaf26fdb16..863ecdd115 100644 --- a/cabal.project +++ b/cabal.project @@ -16,7 +16,7 @@ source-repository-package source-repository-package type: git location: https://github.com/bubba/hie-bios.git - tag: 12f4f2f050a643ee360b197789250f0d96d003ff + tag: 6e45b12563c1dd1ecde4bb9b8438e160729c083e tests: true documentation: true diff --git a/exe/Main.hs b/exe/Main.hs index b88b0d1d60..1252a697f7 100644 --- a/exe/Main.hs +++ b/exe/Main.hs @@ -392,7 +392,7 @@ loadSession dir = do -- henv <- case versionMismatch of -- Just mismatch -> return mismatch -- Nothing -> newHscEnvEq hscEnv' uids - + henv <- newHscEnvEq hscEnv' uids let res = (([], Just henv), di) @@ -677,6 +677,7 @@ getCacheDir prefix opts = IO.getXdgDirectory IO.XdgCache (cacheDir prefix ++ cacheDir :: String cacheDir = "ghcide" +-- TODO: pass cabal ghc version to this -- ghcVersionChecker :: IO VersionCheck -- ghcVersionChecker = $$(makeGhcVersionChecker (pure <$> getLibdir)) diff --git a/exe/Wrapper.hs b/exe/Wrapper.hs index 91be5666b1..fd0c26ae14 100644 --- a/exe/Wrapper.hs +++ b/exe/Wrapper.hs @@ -8,12 +8,14 @@ import Control.Monad.Extra import Data.Foldable import Data.List import HIE.Bios +import HIE.Bios.Environment import HIE.Bios.Types import Ide.Cradle (findLocalCradle) import Ide.Version import System.Directory import System.Environment import System.Exit +import System.FilePath import System.IO import System.Info import System.Process @@ -25,14 +27,14 @@ main = do -- WARNING: If you write to stdout before runLanguageServer -- then the language server will not work Arguments{..} <- getArguments "haskell-language-server-wrapper" - + d <- getCurrentDirectory -- Get the cabal directory from the cradle - cradle <- findLocalCradle d + cradle <- findLocalCradle (d "a") setCurrentDirectory $ cradleRootDir cradle - when argsProjectGhcVersion $ getProjectGhcVersion cradle >>= putStrLn >> exitSuccess + when argsProjectGhcVersion $ getRuntimeGhcVersion cradle >>= putStrLn >> exitSuccess when argsVersion $ ghcideVersion >>= putStrLn >> exitSuccess whenJust argsCwd setCurrentDirectory @@ -47,7 +49,7 @@ main = do hPutStrLn stderr $ "Cradle directory: " ++ cradleRootDir cradle hPutStrLn stderr $ "Cradle type: " ++ show (actionName (cradleOptsProg cradle)) hPutStrLn stderr $ "Consulting the cradle to get project GHC version..." - ghcVersion <- getProjectGhcVersion cradle + ghcVersion <- getRuntimeGhcVersion cradle hPutStrLn stderr $ "Project GHC version: " ++ ghcVersion let diff --git a/src/Ide/Cradle.hs b/src/Ide/Cradle.hs index 912a43a578..dc93198c55 100644 --- a/src/Ide/Cradle.hs +++ b/src/Ide/Cradle.hs @@ -33,7 +33,6 @@ import System.Exit import System.FilePath import System.Process (readCreateProcessWithExitCode, shell, CreateProcess(..)) - -- --------------------------------------------------------------------- -- | Find the cradle that the given File belongs to. @@ -54,7 +53,7 @@ findLocalCradle fp = do debugm $ "Found \"" ++ yaml ++ "\" for \"" ++ fp ++ "\"" crdl <- Bios.loadCradle yaml return $ fmap (const CabalNone) crdl - Nothing -> Bios.loadImplicitCradle (fp "Foo.hs") -- cabalHelperCradle fp + Nothing -> Bios.loadImplicitCradle fp -- cabalHelperCradle fp logm $ "Module \"" ++ fp ++ "\" is loaded by Cradle: " ++ show crdl return crdl @@ -489,7 +488,7 @@ cabalHelperCradle file = do , cradleOptsProg = CradleAction { actionName = Bios.Other (projectNoneType proj) , runCradle = \_ _ -> return CradleNone - , runGhcLibDir = pure Nothing + , runGhcLibDir = pure Nothing } } Just realPackage -> do diff --git a/src/Ide/Version.hs b/src/Ide/Version.hs index 2baccfd501..47042abb59 100644 --- a/src/Ide/Version.hs +++ b/src/Ide/Version.hs @@ -35,19 +35,5 @@ hlsVersion = hlsGhcDisplayVersion :: String hlsGhcDisplayVersion = compilerName ++ "-" ++ VERSION_ghc -getProjectGhcVersion :: Bios.Cradle Bios.CabalHelper -> IO String -getProjectGhcVersion crdl = - fmap - (fromMaybe "No System GHC Found.") - (execProjectGhc crdl ["--numeric-version"]) - - hlsGhcVersion :: String hlsGhcVersion = VERSION_ghc - --- --------------------------------------------------------------------- - -checkCabalInstall :: IO Bool -checkCabalInstall = isJust <$> findExecutable "cabal" - --- --------------------------------------------------------------------- From 65eb6c429af47093b5d25e771404e72c55eef20f Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Thu, 18 Jun 2020 21:40:32 +0100 Subject: [PATCH 29/51] Add documentation on the releases process --- docs/releases.md | 60 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 docs/releases.md diff --git a/docs/releases.md b/docs/releases.md new file mode 100644 index 0000000000..966aa9b0fe --- /dev/null +++ b/docs/releases.md @@ -0,0 +1,60 @@ +# Releases and distributable binaries + +Starting with 0.X.0.0 haskell-language-server provides pre-built binaries on +each [GitHub +release](https://github.com/haskell/haskell-language-server/releases). These +binaries are used by the [vscode-hie-server +extension](https://github.com/alanz/vscode-hie-server) to provide automatic +installation for users on VS Code, but they can also be installed manually +when added to the path. + +## Making a new release of haskell-language-server + +Go to the [GitHub releases +page](https://github.com/haskell/haskell-language-server/releases) for +haskell-language-server and start to create a new release. Choose or create a +tag, fill out the release notes etc., but before you create it +**make sure to check the pre-release checkbox**. This will prevent VS Code +*extension +users from attempting to install this version before the binaries are +created. + +Once the release is created the [GitHub Actions +workflow](https://github.com/haskell/haskell-language-server/actions) will be +kicked off and will start creating binaries. They will be gzipped and +uploaded to the release. + +It creates a `haskell-language-server-OS-GHC` binary for each platform +(Linux, macOS, Windows) and each GHC version that we currently support, as well +as a `haskell-language-server-wrapper-OS` binary for each platform. Note that +only one wrapper binary is created per platform, and it should be built with the +most recent GHC version. + +Once all these binaries are present + +## Distributable binaries +In order to compile a hls binary on one machine and have it run on another, you +need to make sure there are **no hardcoded paths or data-files**. + +### ghc libdir +One noteable thing which cannot be hardcoded is the **GHC libdir** – this is +a path to `/usr/local/lib/ghc` or something like that, which was previously +baked in at compile-time with ghc-paths. Note that with static binaries we +can no longer use this because the GHC libdir of the GitHub Actions machine +will most almost certainly not exist on the end user's machine. +Therefore, hie-bios provides `getGhcRuntimeLibDir` to obtain this path on the fly +by consulting the cradle. + +In order to facilitate better error messages, when we build the distributable +binaries on CI we pass Cabal the `-fdist-binary` flag, which prevents hie-bios +from falling back to the hardcoded ghc-paths libdir. + +## The GitHub Actions workflow +It just kicks off a matrix of jobs varying across GHC versions and OSs, building +the binaries with Cabal and extracting them from the dist-newstyle directory. +The binaries are built with -O2. + +One caveat is that we need to rename the binaries from +haskell-language-server/haskell-language-server-wrapper to hls/hls-wrapper due to +path length limitations on windows. But whenever we upload them to the release, +we make sure to upload them as their full name variant. From c1a1228497dbd4b400e8262e5fd59efe82764907 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Fri, 19 Jun 2020 01:07:36 +0100 Subject: [PATCH 30/51] Remove unused code --- src/Ide/Cradle.hs | 41 ----------------------------------------- src/Ide/Version.hs | 15 ++------------- 2 files changed, 2 insertions(+), 54 deletions(-) diff --git a/src/Ide/Cradle.hs b/src/Ide/Cradle.hs index dc93198c55..b8ca3106b9 100644 --- a/src/Ide/Cradle.hs +++ b/src/Ide/Cradle.hs @@ -89,47 +89,6 @@ data CabalHelper | CabalNone deriving (Show, Eq, Ord) --- | Execute @ghc@ that is based on the given cradle. --- Output must be a single line. If an error is raised, e.g. the command --- failed, a 'Nothing' is returned. --- The exact error is written to logs. --- --- E.g. for a stack cradle, we use @stack ghc@ and for a cabal cradle --- we are taking the @ghc@ that is on the path. -execProjectGhc :: Cradle CabalHelper -> [String] -> IO (Maybe String) -execProjectGhc crdl args = do - isStackInstalled <- isJust <$> findExecutable "stack" - isCabalInstalled <- isJust <$> findExecutable "cabal" - ghcOutput <- if isStackCradle crdl && isStackInstalled - then do - logm $ "Executing Stack GHC with args: " <> unwords args - catch (Just <$> tryCommand crdl stackCmd) $ \(_ :: IOException) -> do - errorm $ "Command `" ++ stackCmd ++"` failed." - execWithGhc - -- The command `cabal v2-exec -v0 ghc` only works if the project has been - -- built already. - -- This command must work though before the project is build. - -- Therefore, fallback to "ghc" on the path. - -- - else if isCabalCradle crdl && isCabalInstalled then do - let cmd = "cabal v2-exec -v0 ghc -- " ++ unwords args - catch (Just <$> tryCommand crdl cmd) $ \(_ ::IOException) -> do - errorm $ "Command `" ++ cmd ++ "` failed." - return Nothing - else do - logm $ "Executing GHC on path with args: " <> unwords args - execWithGhc - debugm $ "GHC Output: \"" ++ show ghcOutput ++ "\"" - return ghcOutput - where - stackCmd = "stack ghc -- " ++ unwords args - plainCmd = "ghc " ++ unwords args - - execWithGhc = - catch (Just <$> tryCommand crdl plainCmd) $ \(_ :: IOException) -> do - errorm $ "Command `" ++ plainCmd ++"` failed." - return Nothing - tryCommand :: Cradle CabalHelper -> String -> IO String tryCommand crdl cmd = do let p = (shell cmd) { cwd = Just (cradleRootDir crdl) } diff --git a/src/Ide/Version.hs b/src/Ide/Version.hs index 47042abb59..14aa761e03 100644 --- a/src/Ide/Version.hs +++ b/src/Ide/Version.hs @@ -5,16 +5,11 @@ -- and the current project's version module Ide.Version where -import Data.Maybe import Development.GitRev (gitCommitCount) import Distribution.System (buildArch) import Distribution.Text (display) import Options.Applicative.Simple (simpleVersion) -import Ide.Cradle (execProjectGhc) -import qualified HIE.Bios.Types as Bios -import qualified Ide.Cradle as Bios import qualified Paths_haskell_language_server as Meta -import System.Directory import System.Info hlsVersion :: String @@ -29,11 +24,5 @@ hlsVersion = , [" ", display buildArch] , [" ", hlsGhcDisplayVersion] ] - --- --------------------------------------------------------------------- - -hlsGhcDisplayVersion :: String -hlsGhcDisplayVersion = compilerName ++ "-" ++ VERSION_ghc - -hlsGhcVersion :: String -hlsGhcVersion = VERSION_ghc + where + hlsGhcDisplayVersion = compilerName ++ "-" ++ VERSION_ghc From d0aa2fa0734d59148e46a4c0e15a52b9a2dca6f8 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Tue, 23 Jun 2020 14:00:24 +0100 Subject: [PATCH 31/51] Append .exe to windows binaries --- .github/workflows/build.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3341bd134a..e7ab8f3194 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,9 +40,12 @@ jobs: haskell-language-server.cabal sed -i.bak -e 's/Paths_haskell_language_server/Paths_hls/g' \ src/**/*.hs exe/*.hs + + - name: Set exe extension if on Windows + if: matrix.os == 'windows-latest' + shell: bash + run: echo '::set-env name=EXE_EXT::.exe' - - run: cabal build floskell -v2 - - name: Build Server run: cabal build exe:hls -O2 --disable-documentation -fdist-binary @@ -61,7 +64,7 @@ jobs: with: upload_url: ${{ github.event.release.upload_url }} asset_path: ${{ steps.find_server_binary.outputs.hls_binary }} - asset_name: haskell-language-server-${{ runner.OS }}-${{ matrix.ghc }}.gz + asset_name: haskell-language-server-${{ runner.OS }}-${{ matrix.ghc }}${{env.EXE_EXT}}.gz asset_content_type: application/gzip - name: Build Wrapper @@ -85,6 +88,6 @@ jobs: with: upload_url: ${{ github.event.release.upload_url }} asset_path: ${{ steps.find_wrapper_binary.outputs.hls_wrapper_binary }} - asset_name: haskell-language-server-wrapper-${{ runner.OS }}.gz + asset_name: haskell-language-server-wrapper-${{ runner.OS }}${{env.EXE_EXT}}.gz asset_content_type: application/gzip From edf2cc514b914b4883d2409b73d61f62b5f61ef9 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Tue, 23 Jun 2020 14:53:57 +0100 Subject: [PATCH 32/51] Try out building remaining supported ghc configurations --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e7ab8f3194..a893e530f7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - ghc: ['8.10.1', '8.8.3', '8.6.5'] + ghc: ['8.10.1', '8.8.3', '8.8.2', '8.6.5', '8.6.4'] os: [ubuntu-latest, macOS-latest, windows-latest] exclude: - os: windows-latest From 322e6adb564720596c37b7e5366369efe43bb0e5 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Thu, 25 Jun 2020 21:22:05 +0100 Subject: [PATCH 33/51] Add wrapper tests and update hie-bios --- cabal.project | 2 +- exe/Main.hs | 2 +- haskell-language-server.cabal | 16 +++++++++ stack-8.10.1.yaml | 4 ++- stack-8.6.4.yaml | 3 +- stack-8.6.5.yaml | 3 +- stack-8.8.2.yaml | 3 +- stack-8.8.3.yaml | 3 +- stack.yaml | 6 ++-- test/utils/Test/Hls/Util.hs | 27 ++++++++++++++- test/wrapper/Main.hs | 33 +++++++++++++++++++ test/wrapper/testdata/cabal-cur-ver/Lib.hs | 1 + .../cabal-cur-ver/cabal-cur-ver.cabal | 7 ++++ .../testdata/cabal-cur-ver/cabal.project | 1 + test/wrapper/testdata/stack-8.10.1/Lib.hs | 1 + test/wrapper/testdata/stack-8.10.1/foo.cabal | 7 ++++ test/wrapper/testdata/stack-8.8.3/Lib.hs | 1 + test/wrapper/testdata/stack-8.8.3/foo.cabal | 7 ++++ 18 files changed, 117 insertions(+), 10 deletions(-) create mode 100644 test/wrapper/Main.hs create mode 100644 test/wrapper/testdata/cabal-cur-ver/Lib.hs create mode 100644 test/wrapper/testdata/cabal-cur-ver/cabal-cur-ver.cabal create mode 100644 test/wrapper/testdata/cabal-cur-ver/cabal.project create mode 100644 test/wrapper/testdata/stack-8.10.1/Lib.hs create mode 100644 test/wrapper/testdata/stack-8.10.1/foo.cabal create mode 100644 test/wrapper/testdata/stack-8.8.3/Lib.hs create mode 100644 test/wrapper/testdata/stack-8.8.3/foo.cabal diff --git a/cabal.project b/cabal.project index 863ecdd115..cfb1c6ec44 100644 --- a/cabal.project +++ b/cabal.project @@ -16,7 +16,7 @@ source-repository-package source-repository-package type: git location: https://github.com/bubba/hie-bios.git - tag: 6e45b12563c1dd1ecde4bb9b8438e160729c083e + tag: 64b02c974b753160f20e9d14fc8de3ed008f2bcc tests: true documentation: true diff --git a/exe/Main.hs b/exe/Main.hs index 1252a697f7..7fee60f9e3 100644 --- a/exe/Main.hs +++ b/exe/Main.hs @@ -55,7 +55,7 @@ import DynFlags (gopt_set, gopt_unset, updOptLevel) import DynFlags (PackageFlag(..), PackageArg(..)) import GHC hiding (def) -import GHC.Check ( VersionCheck(..), makeGhcVersionChecker ) +-- import GHC.Check ( VersionCheck(..), makeGhcVersionChecker ) -- import GhcMonad import HIE.Bios.Cradle import HIE.Bios.Environment (addCmdOpts, makeDynFlagsAbsolute, getRuntimeGhcLibDir) diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index ff3a24e3f2..5d81a2fb13 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -238,6 +238,7 @@ common hls-test-utils , stm , tasty-hunit , text + , transformers , unordered-containers , yaml ghc-options: -Wall -Wredundant-constraints @@ -292,3 +293,18 @@ test-suite func-test -threaded -rtsopts -with-rtsopts=-N if flag(pedantic) ghc-options: -Werror -Wredundant-constraints + +test-suite wrapper-test + import: cpp-flags, hls-test-utils + type: exitcode-stdio-1.0 + build-tool-depends: haskell-language-server:haskell-language-server-wrapper + default-language: Haskell2010 + build-depends: base == 4.* + , directory + , process + , tasty + , tasty-hunit + , tasty-ant-xml >= 1.1.6 + hs-source-dirs: test/wrapper + main-is: Main.hs + ghc-options: -Wall diff --git a/stack-8.10.1.yaml b/stack-8.10.1.yaml index 2cc5a3a9a0..15e5390365 100644 --- a/stack-8.10.1.yaml +++ b/stack-8.10.1.yaml @@ -23,6 +23,8 @@ extra-deps: commit: fb3859dca2e54d1bbb2c873e68ed225fa179fbef - semigroups-0.18.5 - temporary-1.2.1.1 +- github: bubba/hie-bios + commit: 64b02c974b753160f20e9d14fc8de3ed008f2bcc flags: haskell-language-server: @@ -37,4 +39,4 @@ flags: nix: packages: [ icu libcxx zlib ] -concurrent-tests: false \ No newline at end of file +concurrent-tests: false diff --git a/stack-8.6.4.yaml b/stack-8.6.4.yaml index 44aad13daa..ab04bd1848 100644 --- a/stack-8.6.4.yaml +++ b/stack-8.6.4.yaml @@ -29,7 +29,8 @@ extra-deps: - haskell-lsp-0.22.0.0 - haskell-lsp-types-0.22.0.0 - haskell-src-exts-1.21.1 -- hie-bios-0.5.0 +- github: bubba/hie-bios + commit: 64b02c974b753160f20e9d14fc8de3ed008f2bcc - hlint-2.2.8 - hoogle-5.0.17.11 - hsimport-0.11.0@rev:2 diff --git a/stack-8.6.5.yaml b/stack-8.6.5.yaml index 8112ee1950..09668ccec0 100644 --- a/stack-8.6.5.yaml +++ b/stack-8.6.5.yaml @@ -24,7 +24,8 @@ extra-deps: - haddock-library-1.8.0 - haskell-lsp-0.22.0.0 - haskell-lsp-types-0.22.0.0 -- hie-bios-0.5.0 +- github: bubba/hie-bios + commit: 64b02c974b753160f20e9d14fc8de3ed008f2bcc - indexed-profunctors-0.1 - lens-4.18 - lsp-test-0.10.3.0 diff --git a/stack-8.8.2.yaml b/stack-8.8.2.yaml index 390910a08e..acdcb89ea5 100644 --- a/stack-8.8.2.yaml +++ b/stack-8.8.2.yaml @@ -19,7 +19,8 @@ extra-deps: - haskell-lsp-0.22.0.0 - haskell-lsp-types-0.22.0.0 - haskell-src-exts-1.21.1 -- hie-bios-0.5.0 +- github: bubba/hie-bios + commit: 64b02c974b753160f20e9d14fc8de3ed008f2bcc - hlint-2.2.8 - hoogle-5.0.17.11 - hsimport-0.11.0 diff --git a/stack-8.8.3.yaml b/stack-8.8.3.yaml index 7ddd544644..8bcb561fb7 100644 --- a/stack-8.8.3.yaml +++ b/stack-8.8.3.yaml @@ -18,7 +18,8 @@ extra-deps: - haskell-lsp-0.22.0.0 - haskell-lsp-types-0.22.0.0 - haskell-src-exts-1.21.1 -- hie-bios-0.5.0 +- github: bubba/hie-bios + commit: 64b02c974b753160f20e9d14fc8de3ed008f2bcc - hlint-2.2.8 - hoogle-5.0.17.11 - hsimport-0.11.0 diff --git a/stack.yaml b/stack.yaml index 814a85fa0d..4b583daf2d 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,4 +1,4 @@ -resolver: lts-14.27 # Last 8.6.5 +resolver: lts-14.27 # Last 8.6.5 packages: - . @@ -24,7 +24,9 @@ extra-deps: - haddock-library-1.8.0 - haskell-lsp-0.22.0.0 - haskell-lsp-types-0.22.0.0 -- hie-bios-0.5.0 +# - hie-bios-0.5.0 +- github: bubba/hie-bios + commit: 64b02c974b753160f20e9d14fc8de3ed008f2bcc - indexed-profunctors-0.1 - lens-4.18 - lsp-test-0.10.3.0 diff --git a/test/utils/Test/Hls/Util.hs b/test/utils/Test/Hls/Util.hs index 65b024167e..e3dd99a18d 100644 --- a/test/utils/Test/Hls/Util.hs +++ b/test/utils/Test/Hls/Util.hs @@ -14,6 +14,7 @@ module Test.Hls.Util , noLogConfig , setupBuildToolFiles , withFileLogging + , findExe -- , makeRequest -- , runIGM -- , runIGM' @@ -25,8 +26,10 @@ module Test.Hls.Util ) where +import Control.Applicative -- import Control.Concurrent.STM import Control.Monad +import Control.Monad.Trans.Maybe import Data.Default import Data.List (intercalate) -- import Data.Typeable @@ -309,4 +312,26 @@ dummyLspFuncs = LspFuncs { clientCapabilities = def , getWorkspaceFolders = return Nothing , withProgress = \_ _ f -> f (const (return ())) , withIndefiniteProgress = \_ _ f -> f - } \ No newline at end of file + } + +findExeRecursive :: FilePath -> FilePath -> IO (Maybe FilePath) +findExeRecursive exe dir = do + me <- listToMaybe <$> findExecutablesInDirectories [dir] exe + case me of + Just e -> return (Just e) + Nothing -> do + subdirs <- (fmap (dir )) <$> listDirectory dir >>= filterM doesDirectoryExist + foldM (\acc subdir -> case acc of + Just y -> pure $ Just y + Nothing -> findExeRecursive exe subdir) + Nothing + subdirs + +-- | So we can find an executable with cabal run +-- since it doesnt put build tools on the path (only cabal test) +findExe :: String -> IO FilePath +findExe name = do + fp <- fmap fromJust $ runMaybeT $ + MaybeT (findExecutable name) <|> + MaybeT (findExeRecursive name "dist-newstyle") + makeAbsolute fp diff --git a/test/wrapper/Main.hs b/test/wrapper/Main.hs new file mode 100644 index 0000000000..c89a4d9c58 --- /dev/null +++ b/test/wrapper/Main.hs @@ -0,0 +1,33 @@ +import Data.List +import Data.Char +import Test.Hls.Util +import Test.Tasty +import Test.Tasty.HUnit +import System.Directory +import System.Process + +main :: IO () +main = defaultMain $ + testGroup "haskell-language-server-wrapper" [projectGhcVersionTests] + +projectGhcVersionTests :: TestTree +projectGhcVersionTests = testGroup "--project-ghc-version" + [ testCase "stack with ghc 8.10.1" $ + testDir "test/wrapper/testdata/stack-8.10.1" "8.10.1" + , testCase "stack with ghc 8.8.3" $ + testDir "test/wrapper/testdata/stack-8.8.3" "8.8.3" + , testCase "cabal with global ghc" $ do + ghcVer <- trim <$> readProcess "ghc" ["--numeric-version"] "" + testDir "test/wrapper/testdata/cabal-cur-ver" ghcVer + ] + +testDir :: FilePath -> String -> Assertion +testDir dir expectedVer = do + wrapper <- findExe "haskell-language-server-wrapper" + withCurrentDirectory dir $ do + actualVer <- trim <$> readProcess wrapper ["--project-ghc-version"] "" + actualVer @?= expectedVer + +trim :: String -> String +trim = dropWhileEnd isSpace + diff --git a/test/wrapper/testdata/cabal-cur-ver/Lib.hs b/test/wrapper/testdata/cabal-cur-ver/Lib.hs new file mode 100644 index 0000000000..76a9bdb5d4 --- /dev/null +++ b/test/wrapper/testdata/cabal-cur-ver/Lib.hs @@ -0,0 +1 @@ +main = pure () diff --git a/test/wrapper/testdata/cabal-cur-ver/cabal-cur-ver.cabal b/test/wrapper/testdata/cabal-cur-ver/cabal-cur-ver.cabal new file mode 100644 index 0000000000..a64735e4d4 --- /dev/null +++ b/test/wrapper/testdata/cabal-cur-ver/cabal-cur-ver.cabal @@ -0,0 +1,7 @@ +cabal-version: 2.4 +name: cabal-cur-ver +version: 0.1.0.0 +library + exposed-modules: Lib + build-depends: base + default-language: Haskell2010 diff --git a/test/wrapper/testdata/cabal-cur-ver/cabal.project b/test/wrapper/testdata/cabal-cur-ver/cabal.project new file mode 100644 index 0000000000..e6fdbadb43 --- /dev/null +++ b/test/wrapper/testdata/cabal-cur-ver/cabal.project @@ -0,0 +1 @@ +packages: . diff --git a/test/wrapper/testdata/stack-8.10.1/Lib.hs b/test/wrapper/testdata/stack-8.10.1/Lib.hs new file mode 100644 index 0000000000..76a9bdb5d4 --- /dev/null +++ b/test/wrapper/testdata/stack-8.10.1/Lib.hs @@ -0,0 +1 @@ +main = pure () diff --git a/test/wrapper/testdata/stack-8.10.1/foo.cabal b/test/wrapper/testdata/stack-8.10.1/foo.cabal new file mode 100644 index 0000000000..affc654cad --- /dev/null +++ b/test/wrapper/testdata/stack-8.10.1/foo.cabal @@ -0,0 +1,7 @@ +cabal-version: 2.4 +name: foo +version: 0.1.0.0 +library + exposed-modules: Lib + build-depends: base + default-language: Haskell2010 diff --git a/test/wrapper/testdata/stack-8.8.3/Lib.hs b/test/wrapper/testdata/stack-8.8.3/Lib.hs new file mode 100644 index 0000000000..76a9bdb5d4 --- /dev/null +++ b/test/wrapper/testdata/stack-8.8.3/Lib.hs @@ -0,0 +1 @@ +main = pure () diff --git a/test/wrapper/testdata/stack-8.8.3/foo.cabal b/test/wrapper/testdata/stack-8.8.3/foo.cabal new file mode 100644 index 0000000000..affc654cad --- /dev/null +++ b/test/wrapper/testdata/stack-8.8.3/foo.cabal @@ -0,0 +1,7 @@ +cabal-version: 2.4 +name: foo +version: 0.1.0.0 +library + exposed-modules: Lib + build-depends: base + default-language: Haskell2010 From 997e6b0ce2ecf5ba4db11c83a19db7d3d1174672 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Fri, 26 Jun 2020 13:25:38 +0100 Subject: [PATCH 34/51] Use index timestamp that exists on hackage Fixes warning --- cabal.project | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cabal.project b/cabal.project index fecdfa37eb..d698cf8d07 100644 --- a/cabal.project +++ b/cabal.project @@ -23,4 +23,4 @@ package ghcide write-ghc-environment-files: never -index-state: 2020-06-18T17:03:29Z +index-state: 2020-06-16T12:16:47Z From 0df116866a0265bffc596a1b2344d33a3513326b Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Mon, 29 Jun 2020 14:11:49 +0100 Subject: [PATCH 35/51] Update hie-bios --- cabal.project | 2 +- src/Ide/Cradle.hs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cabal.project b/cabal.project index d698cf8d07..4c7566c83e 100644 --- a/cabal.project +++ b/cabal.project @@ -11,7 +11,7 @@ source-repository-package source-repository-package type: git location: https://github.com/bubba/hie-bios.git - tag: 64b02c974b753160f20e9d14fc8de3ed008f2bcc + tag: a18740914beb76bacfd7f866cdb7228433a5b5cc tests: true documentation: true diff --git a/src/Ide/Cradle.hs b/src/Ide/Cradle.hs index b8ca3106b9..8b1b0a5950 100644 --- a/src/Ide/Cradle.hs +++ b/src/Ide/Cradle.hs @@ -419,7 +419,7 @@ cabalHelperCradle file = do , componentRoot = cwd , componentDependencies = [] } - , runGhcLibDir = pure Nothing + , runGhc = \_ -> pure Nothing } } Just (Ex proj) -> do @@ -447,7 +447,7 @@ cabalHelperCradle file = do , cradleOptsProg = CradleAction { actionName = Bios.Other (projectNoneType proj) , runCradle = \_ _ -> return CradleNone - , runGhcLibDir = pure Nothing + , runGhc = \_ -> pure Nothing } } Just realPackage -> do @@ -468,7 +468,7 @@ cabalHelperCradle file = do realPackage normalisedPackageLocation fp - , runGhcLibDir = pure Nothing + , runGhc = \_ -> pure Nothing } } From 74468f7254e546e9dee5d7f079349a95d123da8a Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Mon, 29 Jun 2020 20:04:37 +0100 Subject: [PATCH 36/51] Update hie-bios --- cabal.project | 2 +- exe/Main.hs | 2 +- src/Ide/Cradle.hs | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cabal.project b/cabal.project index 4c7566c83e..4cd6abc250 100644 --- a/cabal.project +++ b/cabal.project @@ -11,7 +11,7 @@ source-repository-package source-repository-package type: git location: https://github.com/bubba/hie-bios.git - tag: a18740914beb76bacfd7f866cdb7228433a5b5cc + tag: a013b2a372ab618bc885ba8f2c11eef3c9ab824b tests: true documentation: true diff --git a/exe/Main.hs b/exe/Main.hs index 461118eb4d..61396c4b56 100644 --- a/exe/Main.hs +++ b/exe/Main.hs @@ -446,7 +446,7 @@ loadSession dir = do -- also note that getting the runtime ghc lib dir can end up configuring -- and building dependencies through the build tool, so include it in this -- progress section. - libDir <- getRuntimeGhcLibDir cradle isDistBinary + libDir <- getRuntimeGhcLibDir cradle opts <- cradleToSessionOpts cradle cfp return (opts, libDir) diff --git a/src/Ide/Cradle.hs b/src/Ide/Cradle.hs index 8b1b0a5950..18a993fba5 100644 --- a/src/Ide/Cradle.hs +++ b/src/Ide/Cradle.hs @@ -419,7 +419,7 @@ cabalHelperCradle file = do , componentRoot = cwd , componentDependencies = [] } - , runGhc = \_ -> pure Nothing + , runGhcCmd = \_ -> pure Nothing } } Just (Ex proj) -> do @@ -447,7 +447,7 @@ cabalHelperCradle file = do , cradleOptsProg = CradleAction { actionName = Bios.Other (projectNoneType proj) , runCradle = \_ _ -> return CradleNone - , runGhc = \_ -> pure Nothing + , runGhcCmd = \_ -> pure Nothing } } Just realPackage -> do @@ -468,7 +468,7 @@ cabalHelperCradle file = do realPackage normalisedPackageLocation fp - , runGhc = \_ -> pure Nothing + , runGhcCmd = \_ -> pure Nothing } } From 06abcf4fb18c32b42b2c4b318068d32cbb5d6d41 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Tue, 30 Jun 2020 14:38:55 +0100 Subject: [PATCH 37/51] Try building windows jobs on -j1 --- .github/workflows/build.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a893e530f7..301f592458 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -41,13 +41,16 @@ jobs: sed -i.bak -e 's/Paths_haskell_language_server/Paths_hls/g' \ src/**/*.hs exe/*.hs - - name: Set exe extension if on Windows + - name: Set some window specific things if: matrix.os == 'windows-latest' shell: bash - run: echo '::set-env name=EXE_EXT::.exe' + run: | + echo '::set-env name=EXE_EXT::.exe' + echo '::set-env name=WIN_CABAL_ARGS::-j1' - name: Build Server - run: cabal build exe:hls -O2 --disable-documentation -fdist-binary + shell: bash + run: cabal build exe:hls -O2 --disable-documentation -fdist-binary $WIN_CABAL_ARGS - name: Find Server Binary id: find_server_binary From f84d4a7e65682f80c6137732c997ca3a47568abd Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Tue, 30 Jun 2020 18:52:00 +0100 Subject: [PATCH 38/51] Skip windows 8.8.2 --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 301f592458..14078f2d5c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,6 +16,8 @@ jobs: exclude: - os: windows-latest ghc: '8.8.3' # fails due to segfault + - os: windows-latest + ghc: '8.8.2' # fails due to error with Cabal steps: - uses: actions/checkout@v2 From dc3a61a40ba3ec3d44992a765dbb9a44cf2a54bc Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Wed, 1 Jul 2020 11:45:30 +0100 Subject: [PATCH 39/51] Update ghc-check to use hie-bios runtime ghc libdir --- .github/workflows/build.yml | 12 +++---- docs/releases.md | 4 --- exe/Main.hs | 62 ++++++++++++++++------------------- haskell-language-server.cabal | 8 ----- test/wrapper/Main.hs | 1 + 5 files changed, 35 insertions(+), 52 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 14078f2d5c..96b44f75ec 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -33,7 +33,7 @@ jobs: # with: # path: ~/.cabal # key: ${{ runner.OS }}-${{ matrix.ghc }}-cabal-0 - + - name: Shorten binary names shell: bash run: | @@ -42,18 +42,18 @@ jobs: haskell-language-server.cabal sed -i.bak -e 's/Paths_haskell_language_server/Paths_hls/g' \ src/**/*.hs exe/*.hs - + - name: Set some window specific things if: matrix.os == 'windows-latest' shell: bash run: | echo '::set-env name=EXE_EXT::.exe' echo '::set-env name=WIN_CABAL_ARGS::-j1' - + - name: Build Server shell: bash - run: cabal build exe:hls -O2 --disable-documentation -fdist-binary $WIN_CABAL_ARGS - + run: cabal build exe:hls -O2 --disable-documentation $WIN_CABAL_ARGS + - name: Find Server Binary id: find_server_binary shell: bash @@ -74,7 +74,7 @@ jobs: - name: Build Wrapper if: matrix.ghc == '8.10.1' - run: cabal build exe:hls-wrapper -O2 --disable-documentation -fdist-binary + run: cabal build exe:hls-wrapper -O2 --disable-documentation - name: Find Wrapper Binary if: matrix.ghc == '8.10.1' diff --git a/docs/releases.md b/docs/releases.md index 966aa9b0fe..bc68607acb 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -45,10 +45,6 @@ will most almost certainly not exist on the end user's machine. Therefore, hie-bios provides `getGhcRuntimeLibDir` to obtain this path on the fly by consulting the cradle. -In order to facilitate better error messages, when we build the distributable -binaries on CI we pass Cabal the `-fdist-binary` flag, which prevents hie-bios -from falling back to the hardcoded ghc-paths libdir. - ## The GitHub Actions workflow It just kicks off a matrix of jobs varying across GHC versions and OSs, building the binaries with Cabal and extracting them from the dist-newstyle directory. diff --git a/exe/Main.hs b/exe/Main.hs index 61396c4b56..05c28599f7 100644 --- a/exe/Main.hs +++ b/exe/Main.hs @@ -85,6 +85,7 @@ import qualified System.Directory.Extra as IO -- import System.Environment import System.Exit import System.FilePath +import System.Info import System.IO import qualified System.Log.Logger as L import System.Time.Extra @@ -321,18 +322,9 @@ loadSession dir = do res' <- traverse IO.makeAbsolute res return $ normalise <$> res' - libdir <- getGhcideLibdir - installationCheck <- ghcVersionChecker libdir - dummyAs <- async $ return (error "Uninitialised") runningCradle <- newVar dummyAs :: IO (Var (Async (IdeResult HscEnvEq,[FilePath]))) - - case installationCheck of - InstallationNotFound{..} -> - error $ "GHC installation not found in libdir: " <> libdir - InstallationMismatch{..} -> - return $ returnWithVersion $ \fp -> return (([renderPackageSetupException compileTime fp GhcVersionMismatch{..}], Nothing),[]) - InstallationChecked compileTime ghcLibCheck -> return $ do + return $ do ShakeExtras{logger, eventer, restartShakeSession, withIndefiniteProgress, ideNc, session=ideSession} <- getShakeExtras IdeOptions{optTesting = IdeTesting optTesting} <- getIdeOptions @@ -340,9 +332,9 @@ loadSession dir = do -- If the hieYaml file already has an HscEnv, the new component is -- combined with the components in the old HscEnv into a new HscEnv -- which contains the union. - let packageSetup :: (Maybe FilePath, NormalizedFilePath, ComponentOptions, FilePath) + let packageSetup :: (Maybe FilePath, NormalizedFilePath, ComponentOptions, FilePath, Ghc PackageCheckResult) -> IO (HscEnv, ComponentInfo, [ComponentInfo]) - packageSetup (hieYaml, cfp, opts, libDir) = do + packageSetup (hieYaml, cfp, opts, libDir, ghcLibCheck) = do -- Parse DynFlags for the newly discovered component hscEnv <- emptyHscEnv libDir ideNc (df, targets) <- evalGhcEnv hscEnv $ @@ -406,9 +398,9 @@ loadSession dir = do -- existing packages pure (Map.insert hieYaml (newHscEnv, new_deps) m, (newHscEnv, head new_deps', tail new_deps')) - let session :: (Maybe FilePath, NormalizedFilePath, ComponentOptions, FilePath) -> IO ([NormalizedFilePath],(IdeResult HscEnvEq,[FilePath])) - session (hieYaml, cfp, opts, libDir) = do - (hscEnv, new, old_deps) <- packageSetup (hieYaml, cfp, opts, libDir) + let session :: (Maybe FilePath, NormalizedFilePath, ComponentOptions, FilePath, Ghc PackageCheckResult) -> IO ([NormalizedFilePath],(IdeResult HscEnvEq,[FilePath])) + session (hieYaml, cfp, opts, libDir, ghcLibCheck) = do + (hscEnv, new, old_deps) <- packageSetup (hieYaml, cfp, opts, libDir, ghcLibCheck) -- Make a map from unit-id to DynFlags, this is used when trying to -- resolve imports. (especially PackageImports) let uids = map (\ci -> (componentUnitId ci, componentDynFlags ci)) (new : old_deps) @@ -442,8 +434,7 @@ loadSession dir = do -- cradle is let progMsg = "Setting up project " <> T.pack (takeBaseName (cradleRootDir cradle)) (eopts, mLibDir) <- withIndefiniteProgress progMsg LSP.NotCancellable $ do - -- we don't want to use ghc-paths if this is a portable, distributed binary - -- also note that getting the runtime ghc lib dir can end up configuring + -- Note that getting the runtime ghc lib dir can end up configuring -- and building dependencies through the build tool, so include it in this -- progress section. libDir <- getRuntimeGhcLibDir cradle @@ -460,13 +451,22 @@ loadSession dir = do logDebug logger $ T.pack ("Session loading result: " <> show eopts) case (eopts, mLibDir) of - -- The cradle gave us some options so get to work turning them - -- into and HscEnv. - (Right opts, Just libDir) -> do - session (hieYaml, toNormalizedFilePath' cfp, opts, libDir) - -- Failure case, either a cradle error or the none cradle - (Left err, _) -> cradleMishap $ map (renderCradleError ncfp) err - (_, Nothing) -> cradleMishap [ideErrorText ncfp "Couldn't get the GHC library directory"] + -- The cradle gave us some options so get to work turning them + -- into and HscEnv. + (Right opts, Just libDir) -> do + installationCheck <- ghcVersionChecker libDir + case installationCheck of + InstallationNotFound{..} -> + error $ "GHC installation not found in libdir: " <> libdir + InstallationMismatch{..} -> + return ([],(([renderPackageSetupException cfp GhcVersionMismatch{..}], Nothing),[])) + InstallationChecked _ ghcLibCheck -> + session (hieYaml, toNormalizedFilePath' cfp, opts, libDir, ghcLibCheck) + -- Failure case, either a cradle error or the none cradle + (Left err, _) -> + cradleMishap $ map (renderCradleError ncfp) err + (_, Nothing) -> + cradleMishap [ideErrorText ncfp "Couldn't get the GHC library directory"] -- This caches the mapping from hie.yaml + Mod.hs -> [String] -- Returns the Ghc session and the cradle dependencies @@ -496,7 +496,7 @@ loadSession dir = do getOptions file = do hieYaml <- cradleLoc file sessionOpts (hieYaml, file) `catch` \e -> - return ([],(([renderPackageSetupException compileTime file e], Nothing),[])) + return ([],(([renderPackageSetupException file e], Nothing),[])) returnWithVersion $ \file -> do (cs, opts) <- liftIO $ join $ mask_ $ modifyVar runningCradle $ \as -> do @@ -833,13 +833,7 @@ showPackageSetupException _ (PackageCheckFailed BasePackageAbiMismatch{..}) = un ,"\nThis is unsupported, ghcide must be compiled with the same GHC installation as the project." ] -renderPackageSetupException :: Version -> FilePath -> PackageSetupException -> (NormalizedFilePath, ShowDiagnostic, Diagnostic) -renderPackageSetupException compileTime fp e = - ideErrorWithSource (Just "cradle") (Just DsError) (toNormalizedFilePath' fp) (T.pack $ showPackageSetupException compileTime e) +renderPackageSetupException :: FilePath -> PackageSetupException -> (NormalizedFilePath, ShowDiagnostic, Diagnostic) +renderPackageSetupException fp e = + ideErrorWithSource (Just "cradle") (Just DsError) (toNormalizedFilePath' fp) (T.pack $ showPackageSetupException compilerVersion e) -isDistBinary :: Bool -#ifdef DIST_BINARY -isDistBinary = True -#else -isDistBinary = False -#endif diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 03622366b8..b38cdba602 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -26,11 +26,6 @@ flag pedantic Default: False Manual: True -flag dist-binary - Description: Build haskell-language-server ready for distributing binaries - Default: False - Manual: True - source-repository head type: git location: https://github.com/haskell/haskell-language-server @@ -39,9 +34,6 @@ common cpp-flags if flag(agpl) cpp-options: -DAGPL - if flag(dist-binary) - cpp-options: - -DDIST_BINARY library import: cpp-flags diff --git a/test/wrapper/Main.hs b/test/wrapper/Main.hs index c89a4d9c58..ea793e57fd 100644 --- a/test/wrapper/Main.hs +++ b/test/wrapper/Main.hs @@ -10,6 +10,7 @@ main :: IO () main = defaultMain $ testGroup "haskell-language-server-wrapper" [projectGhcVersionTests] +--TODO: WAIT ON HIE-BIOS STOP FILES projectGhcVersionTests :: TestTree projectGhcVersionTests = testGroup "--project-ghc-version" [ testCase "stack with ghc 8.10.1" $ From c76a69ef8c52c127bfd3e2b9ae3fbbcb0ba3c8c5 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Wed, 1 Jul 2020 11:57:40 +0100 Subject: [PATCH 40/51] Upload binaries as an artifact too --- .github/workflows/build.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 96b44f75ec..c358bda1e1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,6 +72,11 @@ jobs: asset_name: haskell-language-server-${{ runner.OS }}-${{ matrix.ghc }}${{env.EXE_EXT}}.gz asset_content_type: application/gzip + - uses: actions/upload-artifact@v2 + with: + name: haskell-language-server-${{ runner.OS }}-${{ matrix.ghc }}${{env.EXE_EXT}}.gz + path: ${{ steps.find_server_binary.outputs.hls_binary }} + - name: Build Wrapper if: matrix.ghc == '8.10.1' run: cabal build exe:hls-wrapper -O2 --disable-documentation @@ -96,3 +101,8 @@ jobs: asset_name: haskell-language-server-wrapper-${{ runner.OS }}${{env.EXE_EXT}}.gz asset_content_type: application/gzip + - uses: actions/upload-artifact@v2 + with: + name: haskell-language-server-wrapper-${{ runner.OS }}${{env.EXE_EXT}}.gz + path: ${{ steps.find_wrapper_binary.outputs.hls_wrapper_binary }} + From 18c408225b7bbf237f2707741a4ac6686c5078e5 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Tue, 7 Jul 2020 17:48:17 +0100 Subject: [PATCH 41/51] Try flicking on enable-executable-static I don't expect this to work, puffnfresh has already tried this and had to fork ghcup --- .github/workflows/build.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c358bda1e1..604064c042 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -50,9 +50,14 @@ jobs: echo '::set-env name=EXE_EXT::.exe' echo '::set-env name=WIN_CABAL_ARGS::-j1' + - name: Set some linux specific things + if: matrix.os == 'ubuntu-latest' + run: | + echo '::set-env name=LINUX_CABAL_ARGS::--enable-executable-static' + - name: Build Server shell: bash - run: cabal build exe:hls -O2 --disable-documentation $WIN_CABAL_ARGS + run: cabal build exe:hls -O2 --disable-documentation $WIN_CABAL_ARGS $LINUX_CABAL_ARGS - name: Find Server Binary id: find_server_binary @@ -79,7 +84,7 @@ jobs: - name: Build Wrapper if: matrix.ghc == '8.10.1' - run: cabal build exe:hls-wrapper -O2 --disable-documentation + run: cabal build exe:hls-wrapper -O2 --disable-documentation $WIN_CABAL_ARGS $LINUX_CABAL_ARGS - name: Find Wrapper Binary if: matrix.ghc == '8.10.1' From 90b286e7705c03206e41fa7df13906860440d86e Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Tue, 7 Jul 2020 18:31:57 +0100 Subject: [PATCH 42/51] Fix artifact upload --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 604064c042..c096034b13 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -107,6 +107,7 @@ jobs: asset_content_type: application/gzip - uses: actions/upload-artifact@v2 + if: matrix.ghc == '8.10.1' with: name: haskell-language-server-wrapper-${{ runner.OS }}${{env.EXE_EXT}}.gz path: ${{ steps.find_wrapper_binary.outputs.hls_wrapper_binary }} From a5c5608bec4c4b3acfd2990dc4e7011e36659eb5 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Wed, 15 Jul 2020 14:56:35 +0100 Subject: [PATCH 43/51] Update to latest ghcide and reuse loadSession --- .gitmodules | 3 +- cabal.project | 2 +- exe/Arguments.hs | 16 +- exe/Main.hs | 617 +--------------------------------- exe/Wrapper.hs | 18 +- ghcide | 2 +- haskell-language-server.cabal | 16 +- stack-8.10.1.yaml | 2 - 8 files changed, 27 insertions(+), 649 deletions(-) diff --git a/.gitmodules b/.gitmodules index c64555bbb2..839d96ebdc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,4 +13,5 @@ # url = https://github.com/digital-asset/ghcide.git # url = https://github.com/alanz/ghcide.git # url = https://github.com/wz1000/ghcide.git - url = https://github.com/fendor/ghcide.git + # url = https://github.com/fendor/ghcide.git + url = https://github.com/bubba/ghcide.git diff --git a/cabal.project b/cabal.project index 0246840a83..1fd1a8d0bf 100644 --- a/cabal.project +++ b/cabal.project @@ -12,4 +12,4 @@ package ghcide write-ghc-environment-files: never -index-state: 2020-07-13T21:29:04Z +index-state: 2020-07-13T21:03:06Z diff --git a/exe/Arguments.hs b/exe/Arguments.hs index 90edaba459..81e388d3de 100644 --- a/exe/Arguments.hs +++ b/exe/Arguments.hs @@ -10,17 +10,14 @@ module Arguments ( Arguments(..) , getArguments - , ghcideVersion - , getGhcideLibdir + , haskellLanguageServerVersion ) where -import Data.Maybe import Data.Version import Development.GitRev import Options.Applicative import Paths_haskell_language_server import System.Environment -import qualified GHC.Paths -- --------------------------------------------------------------------- @@ -86,19 +83,14 @@ arguments exeName = Arguments -- --------------------------------------------------------------------- -ghcideVersion :: IO String -ghcideVersion = do +haskellLanguageServerVersion :: IO String +haskellLanguageServerVersion = do path <- getExecutablePath let gitHashSection = case $(gitHash) of x | x == "UNKNOWN" -> "" x -> " (GIT hash: " <> x <> ")" - return $ "ghcide version: " <> showVersion version + return $ "haskell-language-server version: " <> showVersion version <> " (GHC: " <> VERSION_ghc <> ") (PATH: " <> path <> ")" <> gitHashSection --- --------------------------------------------------------------------- - --- Defined here -getGhcideLibdir :: IO FilePath -getGhcideLibdir = fromMaybe GHC.Paths.libdir <$> lookupEnv "NIX_GHC_LIBDIR" diff --git a/exe/Main.hs b/exe/Main.hs index 317f679f83..b8fda410df 100644 --- a/exe/Main.hs +++ b/exe/Main.hs @@ -16,33 +16,15 @@ module Main(main) where import Arguments -import Control.Concurrent.Async import Control.Concurrent.Extra --- import Control.Exception -import Control.Exception.Safe import Control.Monad.Extra -import Control.Monad.IO.Class -import qualified Crypto.Hash.SHA1 as H -import Data.Aeson (ToJSON(toJSON)) -import Data.Bifunctor (Bifunctor(second)) -import Data.ByteString.Base16 (encode) -import qualified Data.ByteString.Char8 as B import Data.Default -import Data.Either -import Data.Either.Extra -import Data.Foldable -import Data.Function -import qualified Data.HashMap.Strict as HM import qualified Data.HashSet as HashSet -import Data.IORef import Data.List.Extra import qualified Data.Map.Strict as Map import Data.Maybe import qualified Data.Text as T import qualified Data.Text.IO as T -import Data.Time.Clock (UTCTime) -import Data.Version --- import Development.GitRev import Development.IDE.Core.Debouncer import Development.IDE.Core.FileStore import Development.IDE.Core.OfInterest @@ -50,25 +32,15 @@ import Development.IDE.Core.RuleTypes import Development.IDE.Core.Rules import Development.IDE.Core.Service import Development.IDE.Core.Shake -import Development.IDE.GHC.Util import Development.IDE.LSP.LanguageServer import Development.IDE.LSP.Protocol import Development.IDE.Plugin +import Development.IDE.Session import Development.IDE.Types.Diagnostics import Development.IDE.Types.Location import Development.IDE.Types.Logger import Development.IDE.Types.Options -import Development.Shake (Action) -import DynFlags (gopt_set, gopt_unset, - updOptLevel) -import DynFlags (PackageFlag(..), PackageArg(..)) -import GHC hiding (def) -import GHC.Check --- import GhcMonad import HIE.Bios.Cradle -import HIE.Bios.Environment (addCmdOpts, makeDynFlagsAbsolute, getRuntimeGhcLibDir) -import HIE.Bios.Types -import HscTypes (HscEnv(..), ic_dflags) import qualified Language.Haskell.LSP.Core as LSP import Ide.Logger import Ide.Plugin @@ -76,16 +48,9 @@ import Ide.Plugin.Config import Ide.Types (IdePlugins, ipMap) import Language.Haskell.LSP.Messages import Language.Haskell.LSP.Types -import Linker (initDynLinker) -import Module -import NameCache -import Packages -import System.Directory import qualified System.Directory.Extra as IO --- import System.Environment import System.Exit import System.FilePath -import System.Info import System.IO import qualified System.Log.Logger as L import System.Time.Extra @@ -161,8 +126,9 @@ main = do -- then the language server will not work args@Arguments{..} <- getArguments "haskell-language-server" - if argsVersion then ghcideVersion >>= putStrLn >> exitSuccess - else hPutStrLn stderr {- see WARNING above -} =<< ghcideVersion + hlsVer <- haskellLanguageServerVersion + if argsVersion then putStrLn hlsVer + else hPutStrLn stderr hlsVer {- see WARNING above -} LSP.setupLogger argsLogFile ["hls", "hie-bios"] $ if argsDebugOn then L.DEBUG else L.INFO @@ -262,578 +228,3 @@ showEvent _ (EventFileDiagnostics _ []) = return () showEvent lock (EventFileDiagnostics (toNormalizedFilePath' -> file) diags) = withLock lock $ T.putStrLn $ showDiagnosticsColored $ map (file,ShowDiag,) diags showEvent lock e = withLock lock $ print e - - --- | Run the specific cradle on a specific FilePath via hie-bios. -cradleToSessionOpts :: Cradle a -> FilePath -> IO (Either [CradleError] ComponentOptions) -cradleToSessionOpts cradle file = do - let showLine s = putStrLn ("> " ++ s) - cradleRes <- runCradle (cradleOptsProg cradle) showLine file - case cradleRes of - CradleSuccess r -> pure (Right r) - CradleFail err -> return (Left [err]) - -- For the None cradle perhaps we still want to report an Info - -- message about the fact that the file is being ignored. - CradleNone -> return (Left []) - -emptyHscEnv :: FilePath -> IORef NameCache -> IO HscEnv -emptyHscEnv libDir nc = do - env <- runGhc (Just libDir) getSession - initDynLinker env - pure $ setNameCache nc env - --- | Convert a target to a list of potential absolute paths. --- A TargetModule can be anywhere listed by the supplied include --- directories --- A target file is a relative path but with a specific prefix so just need --- to canonicalise it. -targetToFile :: [FilePath] -> TargetId -> IO [NormalizedFilePath] -targetToFile is (TargetModule mod) = do - let fps = [i moduleNameSlashes mod -<.> ext | ext <- exts, i <- is ] - exts = ["hs", "hs-boot", "lhs"] - mapM (fmap toNormalizedFilePath' . canonicalizePath) fps -targetToFile _ (TargetFile f _) = do - f' <- canonicalizePath f - return [toNormalizedFilePath' f'] - -setNameCache :: IORef NameCache -> HscEnv -> HscEnv -setNameCache nc hsc = hsc { hsc_NC = nc } - --- | This is the key function which implements multi-component support. All --- components mapping to the same hie.yaml file are mapped to the same --- HscEnv which is updated as new components are discovered. -loadSession :: FilePath -> IO (Action IdeGhcSession) -loadSession dir = do - -- Mapping from hie.yaml file to HscEnv, one per hie.yaml file - hscEnvs <- newVar Map.empty :: IO (Var HieMap) - -- Mapping from a Filepath to HscEnv - fileToFlags <- newVar Map.empty :: IO (Var FlagsMap) - -- Version of the mappings above - version <- newVar 0 - let returnWithVersion fun = IdeGhcSession fun <$> liftIO (readVar version) - let invalidateShakeCache = do - modifyVar_ version (return . succ) - -- This caches the mapping from Mod.hs -> hie.yaml - cradleLoc <- liftIO $ memoIO $ \v -> do - res <- findCradle v - -- Sometimes we get C:, sometimes we get c:, and sometimes we get a relative path - -- try and normalise that - -- e.g. see https://github.com/digital-asset/ghcide/issues/126 - res' <- traverse IO.makeAbsolute res - return $ normalise <$> res' - - dummyAs <- async $ return (error "Uninitialised") - runningCradle <- newVar dummyAs :: IO (Var (Async (IdeResult HscEnvEq,[FilePath]))) - return $ do - ShakeExtras{logger, eventer, restartShakeSession, withIndefiniteProgress, ideNc, session=ideSession} <- getShakeExtras - IdeOptions{optTesting = IdeTesting optTesting} <- getIdeOptions - - -- Create a new HscEnv from a hieYaml root and a set of options - -- If the hieYaml file already has an HscEnv, the new component is - -- combined with the components in the old HscEnv into a new HscEnv - -- which contains the union. - let packageSetup :: (Maybe FilePath, NormalizedFilePath, ComponentOptions, FilePath, Ghc PackageCheckResult) - -> IO (HscEnv, ComponentInfo, [ComponentInfo]) - packageSetup (hieYaml, cfp, opts, libDir, ghcLibCheck) = do - -- Parse DynFlags for the newly discovered component - hscEnv <- emptyHscEnv libDir ideNc - (df, targets) <- evalGhcEnv hscEnv $ - setOptions opts (hsc_dflags hscEnv) - let deps = componentDependencies opts ++ maybeToList hieYaml - dep_info <- getDependencyInfo deps - -- Now lookup to see whether we are combining with an existing HscEnv - -- or making a new one. The lookup returns the HscEnv and a list of - -- information about other components loaded into the HscEnv - -- (unitId, DynFlag, Targets) - modifyVar hscEnvs $ \m -> do - -- Just deps if there's already an HscEnv - -- Nothing is it's the first time we are making an HscEnv - let oldDeps = Map.lookup hieYaml m - let -- Add the raw information about this component to the list - -- We will modify the unitId and DynFlags used for - -- compilation but these are the true source of - -- information. - new_deps = RawComponentInfo (thisInstalledUnitId df) df targets cfp opts dep_info - : maybe [] snd oldDeps - -- Get all the unit-ids for things in this component - inplace = map rawComponentUnitId new_deps - - new_deps' <- forM new_deps $ \RawComponentInfo{..} -> do - -- Remove all inplace dependencies from package flags for - -- components in this HscEnv - let (df2, uids) = removeInplacePackages inplace rawComponentDynFlags - let prefix = show rawComponentUnitId - -- See Note [Avoiding bad interface files] - processed_df <- setCacheDir logger prefix (sort $ map show uids) opts df2 - -- The final component information, mostly the same but the DynFlags don't - -- contain any packages which are also loaded - -- into the same component. - pure $ ComponentInfo rawComponentUnitId - processed_df - uids - rawComponentTargets - rawComponentFP - rawComponentCOptions - rawComponentDependencyInfo - -- Make a new HscEnv, we have to recompile everything from - -- scratch again (for now) - -- It's important to keep the same NameCache though for reasons - -- that I do not fully understand - logInfo logger (T.pack ("Making new HscEnv" ++ show inplace)) - hscEnv <- emptyHscEnv libDir ideNc - newHscEnv <- - -- Add the options for the current component to the HscEnv - evalGhcEnv hscEnv $ do - _ <- setSessionDynFlags df - checkSession logger ghcLibCheck - getSession - - -- Modify the map so the hieYaml now maps to the newly created - -- HscEnv - -- Returns - -- . the new HscEnv so it can be used to modify the - -- FilePath -> HscEnv map (fileToFlags) - -- . The information for the new component which caused this cache miss - -- . The modified information (without -inplace flags) for - -- existing packages - pure (Map.insert hieYaml (newHscEnv, new_deps) m, (newHscEnv, head new_deps', tail new_deps')) - - let session :: (Maybe FilePath, NormalizedFilePath, ComponentOptions, FilePath, Ghc PackageCheckResult) -> IO ([NormalizedFilePath],(IdeResult HscEnvEq,[FilePath])) - session (hieYaml, cfp, opts, libDir, ghcLibCheck) = do - (hscEnv, new, old_deps) <- packageSetup (hieYaml, cfp, opts, libDir, ghcLibCheck) - -- Make a map from unit-id to DynFlags, this is used when trying to - -- resolve imports. (especially PackageImports) - let uids = map (\ci -> (componentUnitId ci, componentDynFlags ci)) (new : old_deps) - - -- For each component, now make a new HscEnvEq which contains the - -- HscEnv for the hie.yaml file but the DynFlags for that component - - -- New HscEnv for the component in question, returns the new HscEnvEq and - -- a mapping from FilePath to the newly created HscEnvEq. - let new_cache = newComponentCache logger hscEnv uids - (cs, res) <- new_cache new - -- Modified cache targets for everything else in the hie.yaml file - -- which now uses the same EPS and so on - cached_targets <- concatMapM (fmap fst . new_cache) old_deps - modifyVar_ fileToFlags $ \var -> do - pure $ Map.insert hieYaml (HM.fromList (cs ++ cached_targets)) var - - -- Invalidate all the existing GhcSession build nodes by restarting the Shake session - invalidateShakeCache - -- restartShakeSession [kick] - - return (map fst cs, second Map.keys res) - - let consultCradle :: Maybe FilePath -> FilePath -> IO ([NormalizedFilePath], (IdeResult HscEnvEq, [FilePath])) - consultCradle hieYaml cfp = do - when optTesting $ eventer $ notifyCradleLoaded cfp - logInfo logger $ T.pack ("Consulting the cradle for " <> show cfp) - - cradle <- maybe (loadImplicitCradle $ addTrailingPathSeparator dir) loadCradle hieYaml - -- Display a user friendly progress message here: They probably don't know what a - -- cradle is - let progMsg = "Setting up project " <> T.pack (takeBaseName (cradleRootDir cradle)) - (eopts, mLibDir) <- withIndefiniteProgress progMsg LSP.NotCancellable $ do - -- Note that getting the runtime ghc lib dir can end up configuring - -- and building dependencies through the build tool, so include it in this - -- progress section. - libDir <- getRuntimeGhcLibDir cradle - opts <- cradleToSessionOpts cradle cfp - return (opts, libDir) - - let ncfp = toNormalizedFilePath' cfp - cradleMishap diags = do - dep_info <- getDependencyInfo (maybeToList hieYaml) - let res = (diags, Nothing) - modifyVar_ fileToFlags $ \var -> do - pure $ Map.insertWith HM.union hieYaml (HM.singleton ncfp (res, dep_info)) var - return ([ncfp],(res,[])) - - logDebug logger $ T.pack ("Session loading result: " <> show eopts) - case (eopts, mLibDir) of - -- The cradle gave us some options so get to work turning them - -- into and HscEnv. - (Right opts, Just libDir) -> do - installationCheck <- ghcVersionChecker libDir - case installationCheck of - InstallationNotFound{..} -> - error $ "GHC installation not found in libdir: " <> libdir - InstallationMismatch{..} -> - return ([],(([renderPackageSetupException cfp GhcVersionMismatch{..}], Nothing),[])) - InstallationChecked _ ghcLibCheck -> - session (hieYaml, toNormalizedFilePath' cfp, opts, libDir, ghcLibCheck) - -- Failure case, either a cradle error or the none cradle - (Left err, _) -> - cradleMishap $ map (renderCradleError ncfp) err - (_, Nothing) -> - cradleMishap [ideErrorText ncfp "Couldn't get the GHC library directory"] - - -- This caches the mapping from hie.yaml + Mod.hs -> [String] - -- Returns the Ghc session and the cradle dependencies - let sessionOpts :: (Maybe FilePath, FilePath) -> IO ([NormalizedFilePath], (IdeResult HscEnvEq, [FilePath])) - sessionOpts (hieYaml, file) = do - v <- fromMaybe HM.empty . Map.lookup hieYaml <$> readVar fileToFlags - cfp <- canonicalizePath file - case HM.lookup (toNormalizedFilePath' cfp) v of - Just (opts, old_di) -> do - deps_ok <- checkDependencyInfo old_di - if not deps_ok - then do - -- If the dependencies are out of date then clear both caches and start - -- again. - modifyVar_ fileToFlags (const (return Map.empty)) - -- Keep the same name cache - modifyVar_ hscEnvs (return . Map.adjust (\(h, _) -> (h, [])) hieYaml ) - consultCradle hieYaml cfp - else return ([], (opts, Map.keys old_di)) - Nothing -> consultCradle hieYaml cfp - - -- The main function which gets options for a file. We only want one of these running - -- at a time. Therefore the IORef contains the currently running cradle, if we try - -- to get some more options then we wait for the currently running action to finish - -- before attempting to do so. - let getOptions :: FilePath -> IO ([NormalizedFilePath],(IdeResult HscEnvEq, [FilePath])) - getOptions file = do - hieYaml <- cradleLoc file - sessionOpts (hieYaml, file) `catch` \e -> - return ([],(([renderPackageSetupException file e], Nothing),[])) - - returnWithVersion $ \file -> do - (cs, opts) <- liftIO $ join $ mask_ $ modifyVar runningCradle $ \as -> do - -- If the cradle is not finished, then wait for it to finish. - void $ wait as - as <- async $ getOptions file - return $ (fmap snd as, wait as) - unless (null cs) $ - void $ shakeEnqueueSession ideSession $ mkDelayedAction "InitialLoad" Info $ void $ do - cfps' <- liftIO $ filterM (IO.doesFileExist . fromNormalizedFilePath) cs - mmt <- uses GetModificationTime cfps' - let cs_exist = catMaybes (zipWith (<$) cfps' mmt) - uses GetModIface cs_exist - pure opts - --- | Create a mapping from FilePaths to HscEnvEqs -newComponentCache - :: Logger - -> HscEnv - -> [(InstalledUnitId, DynFlags)] - -> ComponentInfo - -> IO ([(NormalizedFilePath, (IdeResult HscEnvEq, DependencyInfo))], (IdeResult HscEnvEq, DependencyInfo)) -newComponentCache logger hsc_env uids ci = do - let df = componentDynFlags ci - let hscEnv' = hsc_env { hsc_dflags = df - , hsc_IC = (hsc_IC hsc_env) { ic_dflags = df } } - - henv <- newHscEnvEq hscEnv' uids - let res = (([], Just henv), componentDependencyInfo ci) - logDebug logger ("New Component Cache HscEnvEq: " <> T.pack (show res)) - - let is = importPaths df - ctargets <- concatMapM (targetToFile is . targetId) (componentTargets ci) - -- A special target for the file which caused this wonderful - -- component to be created. In case the cradle doesn't list all the targets for - -- the component, in which case things will be horribly broken anyway. - -- Otherwise, we will immediately attempt to reload this module which - -- causes an infinite loop and high CPU usage. - let special_target = (componentFP ci, res) - let xs = map (,res) ctargets - return (special_target:xs, res) - -{- Note [Avoiding bad interface files] -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Originally, we set the cache directory for the various components once -on the first occurrence of the component. -This works fine if these components have no references to each other, -but you have components that depend on each other, the interface files are -updated for each component. -After restarting the session and only opening the component that depended -on the other, suddenly the interface files of this component are stale. -However, from the point of view of `ghcide`, they do not look stale, -thus, not regenerated and the IDE shows weird errors such as: -``` -typecheckIface -Declaration for Rep_ClientRunFlags -Axiom branches Rep_ClientRunFlags: - Failed to load interface for ‘Distribution.Simple.Flag’ - Use -v to see a list of the files searched for. -``` -and -``` -expectJust checkFamInstConsistency -CallStack (from HasCallStack): - error, called at compiler\\utils\\Maybes.hs:55:27 in ghc:Maybes - expectJust, called at compiler\\typecheck\\FamInst.hs:461:30 in ghc:FamInst -``` - -To mitigate this, we set the cache directory for each component dependent -on the components of the current `HscEnv`, additionally to the component options -of the respective components. -Assume two components, c1, c2, where c2 depends on c1, and the options of the -respective components are co1, co2. -If we want to load component c2, followed by c1, we set the cache directory for -each component in this way: - - * Load component c2 - * (Cache Directory State) - - name of c2 + co2 - * Load component c1 - * (Cache Directory State) - - name of c2 + name of c1 + co2 - - name of c2 + name of c1 + co1 - -Overall, we created three cache directories. If we opened c1 first, then we -create a fourth cache directory. -This makes sure that interface files are always correctly updated. - -Since this causes a lot of recompilation, we only update the cache-directory, -if the dependencies of a component have really changed. -E.g. when you load two executables, they can not depend on each other. They -should be filtered out, such that we dont have to re-compile everything. --} - --- | Set the cache-directory based on the ComponentOptions and a list of --- internal packages. --- For the exact reason, see Note [Avoiding bad interface files]. -setCacheDir :: MonadIO m => Logger -> String -> [String] -> ComponentOptions -> DynFlags -> m DynFlags -setCacheDir logger prefix hscComponents comps dflags = do - cacheDir <- liftIO $ getCacheDir prefix (hscComponents ++ componentOptions comps) - liftIO $ logInfo logger $ "Using interface files cache dir: " <> T.pack cacheDir - pure $ dflags - & setHiDir cacheDir - & setHieDir cacheDir - - -renderCradleError :: NormalizedFilePath -> CradleError -> FileDiagnostic -renderCradleError nfp (CradleError _deps _ec t) = - ideErrorWithSource (Just "cradle") (Just DsError) nfp (T.unlines (map T.pack t)) - --- See Note [Multi Cradle Dependency Info] -type DependencyInfo = Map.Map FilePath (Maybe UTCTime) -type HieMap = Map.Map (Maybe FilePath) (HscEnv, [RawComponentInfo]) -type FlagsMap = Map.Map (Maybe FilePath) (HM.HashMap NormalizedFilePath (IdeResult HscEnvEq, DependencyInfo)) - --- This is pristine information about a component -data RawComponentInfo = RawComponentInfo - { rawComponentUnitId :: InstalledUnitId - -- | Unprocessed DynFlags. Contains inplace packages such as libraries. - -- We do not want to use them unprocessed. - , rawComponentDynFlags :: DynFlags - -- | All targets of this components. - , rawComponentTargets :: [Target] - -- | Filepath which caused the creation of this component - , rawComponentFP :: NormalizedFilePath - -- | Component Options used to load the component. - , rawComponentCOptions :: ComponentOptions - -- | Maps cradle dependencies, such as `stack.yaml`, or `.cabal` file - -- to last modification time. See Note [Multi Cradle Dependency Info]. - , rawComponentDependencyInfo :: DependencyInfo - } - --- This is processed information about the component, in particular the dynflags will be modified. -data ComponentInfo = ComponentInfo - { componentUnitId :: InstalledUnitId - -- | Processed DynFlags. Does not contain inplace packages such as local - -- libraries. Can be used to actually load this Component. - , componentDynFlags :: DynFlags - -- | Internal units, such as local libraries, that this component - -- is loaded with. These have been extracted from the original - -- ComponentOptions. - , componentInternalUnits :: [InstalledUnitId] - -- | All targets of this components. - , componentTargets :: [Target] - -- | Filepath which caused the creation of this component - , componentFP :: NormalizedFilePath - -- | Component Options used to load the component. - , componentCOptions :: ComponentOptions - -- | Maps cradle dependencies, such as `stack.yaml`, or `.cabal` file - -- to last modification time. See Note [Multi Cradle Dependency Info] - , componentDependencyInfo :: DependencyInfo - } - --- | Check if any dependency has been modified lately. -checkDependencyInfo :: DependencyInfo -> IO Bool -checkDependencyInfo old_di = do - di <- getDependencyInfo (Map.keys old_di) - return (di == old_di) - --- Note [Multi Cradle Dependency Info] --- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- Why do we implement our own file modification tracking here? --- The primary reason is that the custom caching logic is quite complicated and going into shake --- adds even more complexity and more indirection. I did try for about 5 hours to work out how to --- use shake rules rather than IO but eventually gave up. - --- | Computes a mapping from a filepath to its latest modification date. --- See Note [Multi Cradle Dependency Info] why we do this ourselves instead --- of letting shake take care of it. -getDependencyInfo :: [FilePath] -> IO DependencyInfo -getDependencyInfo fs = Map.fromList <$> mapM do_one fs - - where - tryIO :: IO a -> IO (Either IOException a) - tryIO = try - - do_one :: FilePath -> IO (FilePath, Maybe UTCTime) - do_one fp = (fp,) . eitherToMaybe <$> tryIO (getModificationTime fp) - --- | This function removes all the -package flags which refer to packages we --- are going to deal with ourselves. For example, if a executable depends --- on a library component, then this function will remove the library flag --- from the package flags for the executable --- --- There are several places in GHC (for example the call to hptInstances in --- tcRnImports) which assume that all modules in the HPT have the same unit --- ID. Therefore we create a fake one and give them all the same unit id. -removeInplacePackages :: [InstalledUnitId] -> DynFlags -> (DynFlags, [InstalledUnitId]) -removeInplacePackages us df = (df { packageFlags = ps - , thisInstalledUnitId = fake_uid }, uids) - where - (uids, ps) = partitionEithers (map go (packageFlags df)) - fake_uid = toInstalledUnitId (stringToUnitId "fake_uid") - go p@(ExposePackage _ (UnitIdArg u) _) = if toInstalledUnitId u `elem` us - then Left (toInstalledUnitId u) - else Right p - go p = Right p - --- | Memoize an IO function, with the characteristics: --- --- * If multiple people ask for a result simultaneously, make sure you only compute it once. --- --- * If there are exceptions, repeatedly reraise them. --- --- * If the caller is aborted (async exception) finish computing it anyway. -memoIO :: Ord a => (a -> IO b) -> IO (a -> IO b) -memoIO op = do - ref <- newVar Map.empty - return $ \k -> join $ mask_ $ modifyVar ref $ \mp -> - case Map.lookup k mp of - Nothing -> do - res <- onceFork $ op k - return (Map.insert k res mp, res) - Just res -> return (mp, res) - --- | Throws if package flags are unsatisfiable -setOptions :: GhcMonad m => ComponentOptions -> DynFlags -> m (DynFlags, [Target]) -setOptions (ComponentOptions theOpts compRoot _deps) dflags = do - (dflags', targets) <- addCmdOpts theOpts dflags - let dflags'' = - -- disabled, generated directly by ghcide instead - flip gopt_unset Opt_WriteInterface $ - -- disabled, generated directly by ghcide instead - -- also, it can confuse the interface stale check - dontWriteHieFiles $ - setIgnoreInterfacePragmas $ - setLinkerOptions $ - disableOptimisation $ - makeDynFlagsAbsolute compRoot dflags' - -- initPackages parses the -package flags and - -- sets up the visibility for each component. - -- Throws if a -package flag cannot be satisfied. - (final_df, _) <- liftIO $ wrapPackageSetupException $ initPackages dflags'' - return (final_df, targets) - - --- we don't want to generate object code so we compile to bytecode --- (HscInterpreted) which implies LinkInMemory --- HscInterpreted -setLinkerOptions :: DynFlags -> DynFlags -setLinkerOptions df = df { - ghcLink = LinkInMemory - , hscTarget = HscNothing - , ghcMode = CompManager - } - -setIgnoreInterfacePragmas :: DynFlags -> DynFlags -setIgnoreInterfacePragmas df = - gopt_set (gopt_set df Opt_IgnoreInterfacePragmas) Opt_IgnoreOptimChanges - -disableOptimisation :: DynFlags -> DynFlags -disableOptimisation df = updOptLevel 0 df - -setHiDir :: FilePath -> DynFlags -> DynFlags -setHiDir f d = - -- override user settings to avoid conflicts leading to recompilation - d { hiDir = Just f} - -getCacheDir :: String -> [String] -> IO FilePath -getCacheDir prefix opts = IO.getXdgDirectory IO.XdgCache (cacheDir prefix ++ "-" ++ opts_hash) - where - -- Create a unique folder per set of different GHC options, assuming that each different set of - -- GHC options will create incompatible interface files. - opts_hash = B.unpack $ encode $ H.finalize $ H.updates H.init (map B.pack opts) - --- | Sub directory for the cache path -cacheDir :: String -cacheDir = "ghcide" - -notifyCradleLoaded :: FilePath -> FromServerMessage -notifyCradleLoaded fp = - NotCustomServer $ - NotificationMessage "2.0" (CustomServerMethod cradleLoadedMethod) $ - toJSON fp - -cradleLoadedMethod :: T.Text -cradleLoadedMethod = "ghcide/cradle/loaded" - ----------------------------------------------------------------------------------------------------- - -ghcVersionChecker :: GhcVersionChecker -ghcVersionChecker = $$(makeGhcVersionChecker getGhcideLibdir) - --- | Throws a 'PackageSetupException' if the 'Session' cannot be used by ghcide -checkSession :: Logger -> Ghc PackageCheckResult -> Ghc () -checkSession logger ghcLibCheck = - ghcLibCheck >>= \res -> case guessCompatibility res of - ProbablyCompatible mbWarning -> - for_ mbWarning $ liftIO . logInfo logger . T.pack - NotCompatible err -> - liftIO $ throwIO $ PackageCheckFailed err - -data PackageSetupException - = PackageSetupException - { message :: !String - } - | GhcVersionMismatch - { compileTime :: !Version - , runTime :: !Version - } - | PackageCheckFailed !NotCompatibleReason - deriving (Eq, Show, Typeable) - -instance Exception PackageSetupException - --- | Wrap any exception as a 'PackageSetupException' -wrapPackageSetupException :: IO a -> IO a -wrapPackageSetupException = handleAny $ \case - e | Just (pkgE :: PackageSetupException) <- fromException e -> throwIO pkgE - e -> (throwIO . PackageSetupException . show) e - -showPackageSetupException :: Version -> PackageSetupException -> String -showPackageSetupException _ GhcVersionMismatch{..} = unwords - ["ghcide compiled against GHC" - ,showVersion compileTime - ,"but currently using" - ,showVersion runTime - ,"\nThis is unsupported, ghcide must be compiled with the same GHC version as the project." - ] -showPackageSetupException compileTime PackageSetupException{..} = unwords - [ "ghcide compiled by GHC", showVersion compileTime - , "failed to load packages:", message <> "." - , "\nPlease ensure that ghcide is compiled with the same GHC installation as the project."] -showPackageSetupException _ (PackageCheckFailed PackageVersionMismatch{..}) = unwords - ["ghcide compiled with package " - , packageName <> "-" <> showVersion compileTime - ,"but project uses package" - , packageName <> "-" <> showVersion runTime - ,"\nThis is unsupported, ghcide must be compiled with the same GHC installation as the project." - ] -showPackageSetupException _ (PackageCheckFailed BasePackageAbiMismatch{..}) = unwords - ["ghcide compiled with base-" <> showVersion compileTime <> "-" <> compileTimeAbi - ,"but project uses base-" <> showVersion compileTime <> "-" <> runTimeAbi - ,"\nThis is unsupported, ghcide must be compiled with the same GHC installation as the project." - ] - -renderPackageSetupException :: FilePath -> PackageSetupException -> (NormalizedFilePath, ShowDiagnostic, Diagnostic) -renderPackageSetupException fp e = - ideErrorWithSource (Just "cradle") (Just DsError) (toNormalizedFilePath' fp) (T.pack $ showPackageSetupException compilerVersion e) - diff --git a/exe/Wrapper.hs b/exe/Wrapper.hs index fd0c26ae14..af192e4f40 100644 --- a/exe/Wrapper.hs +++ b/exe/Wrapper.hs @@ -34,8 +34,8 @@ main = do cradle <- findLocalCradle (d "a") setCurrentDirectory $ cradleRootDir cradle - when argsProjectGhcVersion $ getRuntimeGhcVersion cradle >>= putStrLn >> exitSuccess - when argsVersion $ ghcideVersion >>= putStrLn >> exitSuccess + when argsProjectGhcVersion $ getRuntimeGhcVersion' cradle >>= putStrLn >> exitSuccess + when argsVersion $ haskellLanguageServerVersion >>= putStrLn >> exitSuccess whenJust argsCwd setCurrentDirectory @@ -48,8 +48,10 @@ main = do hPutStrLn stderr $ "Arguments: " ++ show args hPutStrLn stderr $ "Cradle directory: " ++ cradleRootDir cradle hPutStrLn stderr $ "Cradle type: " ++ show (actionName (cradleOptsProg cradle)) + + -- Get the ghc version -- this might fail! hPutStrLn stderr $ "Consulting the cradle to get project GHC version..." - ghcVersion <- getRuntimeGhcVersion cradle + ghcVersion <- getRuntimeGhcVersion' cradle hPutStrLn stderr $ "Project GHC version: " ++ ghcVersion let @@ -71,4 +73,12 @@ main = do hPutStrLn stderr $ "Launching haskell-language-server exe at:" ++ e callProcess e args --- --------------------------------------------------------------------- +-- | Version of 'getRuntimeGhcVersion' that dies if we can't get it +getRuntimeGhcVersion' :: Cradle a -> IO String +getRuntimeGhcVersion' cradle = do + ghcVersionRes <- getRuntimeGhcVersion cradle + case ghcVersionRes of + CradleSuccess ver -> do + return ver + CradleFail error -> die $ "Failed to get project GHC version:" ++ show error + CradleNone -> die "Failed get project GHC version, since we have a none cradle" diff --git a/ghcide b/ghcide index 8530b98087..a936cca495 160000 --- a/ghcide +++ b/ghcide @@ -1 +1 @@ -Subproject commit 8530b980871f9bc4f6fc2e688a4620e5fe1a0340 +Subproject commit a936cca495dad33be702bb936d3d2b2b2ecce5e9 diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 2e8fd5c7a2..46ffe9dc1b 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -133,34 +133,20 @@ executable haskell-language-server build-depends: base >=4.7 && <5 - , aeson - , async - , base16-bytestring , binary - , bytestring - , cryptohash-sha1 , containers , data-default - , deepseq , directory , extra , filepath , process - -------------------------------------------------------------- - -- The MIN_GHC_API_VERSION macro relies on MIN_VERSION pragmas - -- which require depending on ghc. So the tests need to depend - -- on ghc if they need to use MIN_GHC_API_VERSION. Maybe a - -- better solution can be found, but this is a quick solution - -- which works for now. , ghc -------------------------------------------------------------- - , ghc-check >= 0.5.0.1 && < 0.6 - , ghc-paths , ghcide , gitrev , hashable , haskell-lsp - , hie-bios >= 0.4 + , hie-bios , haskell-language-server , hslogger , optparse-applicative diff --git a/stack-8.10.1.yaml b/stack-8.10.1.yaml index acfdcfa0b7..73b0855aff 100644 --- a/stack-8.10.1.yaml +++ b/stack-8.10.1.yaml @@ -22,8 +22,6 @@ extra-deps: - stylish-haskell-0.11.0.0 - semigroups-0.18.5 - temporary-1.2.1.1 -- github: bubba/hie-bios - commit: 64b02c974b753160f20e9d14fc8de3ed008f2bcc - these-1.1 flags: From cf05aa95fceec0e7d5b9477c6abe49ae4c6bb421 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Thu, 16 Jul 2020 13:41:27 +0100 Subject: [PATCH 44/51] Check if the tool is installed in --project-ghc-version in the wrapper --- exe/Wrapper.hs | 22 ++++++++++++++++++++-- ghcide | 2 +- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/exe/Wrapper.hs b/exe/Wrapper.hs index af192e4f40..8b9b30f40c 100644 --- a/exe/Wrapper.hs +++ b/exe/Wrapper.hs @@ -73,12 +73,30 @@ main = do hPutStrLn stderr $ "Launching haskell-language-server exe at:" ++ e callProcess e args --- | Version of 'getRuntimeGhcVersion' that dies if we can't get it -getRuntimeGhcVersion' :: Cradle a -> IO String +-- | Version of 'getRuntimeGhcVersion' that dies if we can't get it, and also +-- checks to see if the tool is missing if it is one of +getRuntimeGhcVersion' :: Show a => Cradle a -> IO String getRuntimeGhcVersion' cradle = do + + -- See if the tool is installed + case actionName (cradleOptsProg cradle) of + Stack -> checkToolExists "stack" + Cabal -> checkToolExists "cabal" + Default -> checkToolExists "ghc" + Direct -> checkToolExists "ghc" + _ -> pure () + ghcVersionRes <- getRuntimeGhcVersion cradle case ghcVersionRes of CradleSuccess ver -> do return ver CradleFail error -> die $ "Failed to get project GHC version:" ++ show error CradleNone -> die "Failed get project GHC version, since we have a none cradle" + where + checkToolExists exe = do + exists <- findExecutable exe + case exists of + Just _ -> pure () + Nothing -> + die $ "Cradle requires " ++ exe ++ " but couldn't find it" ++ "\n" + ++ show cradle diff --git a/ghcide b/ghcide index a936cca495..7e895cfa53 160000 --- a/ghcide +++ b/ghcide @@ -1 +1 @@ -Subproject commit a936cca495dad33be702bb936d3d2b2b2ecce5e9 +Subproject commit 7e895cfa53260b41996df707baec496a8f2c75dc From 773ec8658ec192435419fa630d9509f0211dbc4b Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Thu, 16 Jul 2020 15:26:04 +0100 Subject: [PATCH 45/51] Fix wrapper tests by copying to temporary directory --- .gitignore | 2 +- haskell-language-server.cabal | 1 + test/utils/Test/Hls/Util.hs | 31 ++++++++++++++++++- test/wrapper/Main.hs | 11 +++---- test/wrapper/testdata/stack-8.10.1/stack.yaml | 1 + test/wrapper/testdata/stack-8.8.3/stack.yaml | 1 + 6 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 test/wrapper/testdata/stack-8.10.1/stack.yaml create mode 100644 test/wrapper/testdata/stack-8.8.3/stack.yaml diff --git a/.gitignore b/.gitignore index 395b29dc79..edae27b422 100644 --- a/.gitignore +++ b/.gitignore @@ -20,7 +20,7 @@ stack*.yaml.lock shake.yaml.lock # ignore hie.yaml's for testdata -test/**/*.yaml +test/testdata/**/hie.yaml # metadata files on macOS .DS_Store diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 46ffe9dc1b..dcd1aa8752 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -217,6 +217,7 @@ common hls-test-utils , lsp-test , stm , tasty-hunit + , temporary , text , transformers , unordered-containers diff --git a/test/utils/Test/Hls/Util.hs b/test/utils/Test/Hls/Util.hs index e3dd99a18d..c6b14a6ea1 100644 --- a/test/utils/Test/Hls/Util.hs +++ b/test/utils/Test/Hls/Util.hs @@ -15,6 +15,7 @@ module Test.Hls.Util , setupBuildToolFiles , withFileLogging , findExe + , withCurrentDirectoryInTmp -- , makeRequest -- , runIGM -- , runIGM' @@ -46,6 +47,7 @@ import System.Directory import System.Environment import System.FilePath import qualified System.Log.Logger as L +import System.IO.Temp -- import Test.Hspec import Test.Hspec.Runner import Test.Hspec.Core.Formatters @@ -332,6 +334,33 @@ findExeRecursive exe dir = do findExe :: String -> IO FilePath findExe name = do fp <- fmap fromJust $ runMaybeT $ - MaybeT (findExecutable name) <|> + MaybeT (findExecutable name) <|> MaybeT (findExeRecursive name "dist-newstyle") makeAbsolute fp + +-- | Like 'withCurrentDirectory', but will copy the directory over to the system +-- temporary directory first to avoid haskell-language-server's source tree from +-- interfering with the cradle +withCurrentDirectoryInTmp :: FilePath -> IO a -> IO a +withCurrentDirectoryInTmp dir f = + withTempCopy dir $ \newDir -> + withCurrentDirectory newDir f + +withTempCopy :: FilePath -> (FilePath -> IO a) -> IO a +withTempCopy srcDir f = do + withSystemTempDirectory "hls-test" $ \newDir -> do + copyDir srcDir newDir + f newDir + +copyDir :: FilePath -> FilePath -> IO () +copyDir src dst = do + cnts <- listDirectory src + forM_ cnts $ \file -> do + unless (file `elem` ignored) $ do + let srcFp = src file + dstFp = dst file + isDir <- doesDirectoryExist srcFp + if isDir + then createDirectory dstFp >> copyDir srcFp dstFp + else copyFile srcFp dstFp + where ignored = ["dist", "dist-newstyle", ".stack-work"] diff --git a/test/wrapper/Main.hs b/test/wrapper/Main.hs index ea793e57fd..6f2795a579 100644 --- a/test/wrapper/Main.hs +++ b/test/wrapper/Main.hs @@ -3,14 +3,14 @@ import Data.Char import Test.Hls.Util import Test.Tasty import Test.Tasty.HUnit -import System.Directory import System.Process main :: IO () -main = defaultMain $ - testGroup "haskell-language-server-wrapper" [projectGhcVersionTests] +main = do + flushStackEnvironment + defaultMain $ + testGroup "haskell-language-server-wrapper" [projectGhcVersionTests] ---TODO: WAIT ON HIE-BIOS STOP FILES projectGhcVersionTests :: TestTree projectGhcVersionTests = testGroup "--project-ghc-version" [ testCase "stack with ghc 8.10.1" $ @@ -25,10 +25,9 @@ projectGhcVersionTests = testGroup "--project-ghc-version" testDir :: FilePath -> String -> Assertion testDir dir expectedVer = do wrapper <- findExe "haskell-language-server-wrapper" - withCurrentDirectory dir $ do + withCurrentDirectoryInTmp dir $ do actualVer <- trim <$> readProcess wrapper ["--project-ghc-version"] "" actualVer @?= expectedVer trim :: String -> String trim = dropWhileEnd isSpace - diff --git a/test/wrapper/testdata/stack-8.10.1/stack.yaml b/test/wrapper/testdata/stack-8.10.1/stack.yaml new file mode 100644 index 0000000000..409e7fe489 --- /dev/null +++ b/test/wrapper/testdata/stack-8.10.1/stack.yaml @@ -0,0 +1 @@ +resolver: ghc-8.10.1 diff --git a/test/wrapper/testdata/stack-8.8.3/stack.yaml b/test/wrapper/testdata/stack-8.8.3/stack.yaml new file mode 100644 index 0000000000..fc8cd8cd8f --- /dev/null +++ b/test/wrapper/testdata/stack-8.8.3/stack.yaml @@ -0,0 +1 @@ +resolver: ghc-8.8.3 From bcfa908fd96acebad639c6d902d5c80461958cb4 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Thu, 16 Jul 2020 17:37:47 +0100 Subject: [PATCH 46/51] Try caching --- .github/workflows/build.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c096034b13..02315c2c30 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,11 +28,11 @@ jobs: ghc-version: ${{ matrix.ghc }} cabal-version: '3.2' - # - name: Cache Cabal - # uses: actions/cache@v1.2.0 - # with: - # path: ~/.cabal - # key: ${{ runner.OS }}-${{ matrix.ghc }}-cabal-0 + - name: Cache Cabal + uses: actions/cache@v1.2.0 + with: + path: ~/.cabal + key: ${{ runner.OS }}-${{ matrix.ghc }}-${{ hashFiles('**/*.cabal') }} - name: Shorten binary names shell: bash @@ -51,13 +51,16 @@ jobs: echo '::set-env name=WIN_CABAL_ARGS::-j1' - name: Set some linux specific things - if: matrix.os == 'ubuntu-latest' + if: matrix.os == 'ubuntu-latest' run: | echo '::set-env name=LINUX_CABAL_ARGS::--enable-executable-static' - name: Build Server shell: bash - run: cabal build exe:hls -O2 --disable-documentation $WIN_CABAL_ARGS $LINUX_CABAL_ARGS + # Try building it twice in case of flakey builds on Windows + run: | + cabal build exe:hls -O2 --disable-documentation $WIN_CABAL_ARGS $LINUX_CABAL_ARGS || \ + cabal build exe:hls -O2 --disable-documentation $WIN_CABAL_ARGS $LINUX_CABAL_ARGS - name: Find Server Binary id: find_server_binary From 53c3676ca3a7e97cdc7c60291b820912dede6413 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Thu, 16 Jul 2020 17:47:00 +0100 Subject: [PATCH 47/51] Tidy up and switch back to cabal helper implicit cradle --- .github/workflows/build.yml | 5 ++--- docs/releases.md | 16 ++++++++++++++++ exe/Main.hs | 3 +-- src/Ide/Cradle.hs | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 02315c2c30..a8b0f8ac08 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,7 +48,6 @@ jobs: shell: bash run: | echo '::set-env name=EXE_EXT::.exe' - echo '::set-env name=WIN_CABAL_ARGS::-j1' - name: Set some linux specific things if: matrix.os == 'ubuntu-latest' @@ -59,8 +58,8 @@ jobs: shell: bash # Try building it twice in case of flakey builds on Windows run: | - cabal build exe:hls -O2 --disable-documentation $WIN_CABAL_ARGS $LINUX_CABAL_ARGS || \ - cabal build exe:hls -O2 --disable-documentation $WIN_CABAL_ARGS $LINUX_CABAL_ARGS + cabal build exe:hls -O2 --disable-documentation $LINUX_CABAL_ARGS || \ + cabal build exe:hls -O2 --disable-documentation $LINUX_CABAL_ARGS - name: Find Server Binary id: find_server_binary diff --git a/docs/releases.md b/docs/releases.md index bc68607acb..f1dc6dc040 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -45,6 +45,22 @@ will most almost certainly not exist on the end user's machine. Therefore, hie-bios provides `getGhcRuntimeLibDir` to obtain this path on the fly by consulting the cradle. +### Static binaries +We use the word "distributable" here because technically only the Linux builds +are static. They are built by passing `--enable-executable-static` to cabal. +Static binaries don't really exist on macOS, and there are issues with +proprietary code being linked in on Windows. However, the `.dylib`s linked on +macOS are all already provided by the system: + +``` +$ objdump -macho --dylibs-used haskell-language-server +haskell-language-server: + /usr/lib/libncurses.5.4.dylib (compatibility version 5.4.0, current version 5.4.0) + /usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0) + /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1) + /usr/lib/libcharset.1.dylib (compatibility version 2.0.0, current version 2.0.0) +``` + ## The GitHub Actions workflow It just kicks off a matrix of jobs varying across GHC versions and OSs, building the binaries with Cabal and extracting them from the dist-newstyle directory. diff --git a/exe/Main.hs b/exe/Main.hs index b8fda410df..736b05ca19 100644 --- a/exe/Main.hs +++ b/exe/Main.hs @@ -1,6 +1,6 @@ -- Copyright (c) 2019 The DAML Authors. All rights reserved. -- SPDX-License-Identifier: Apache-2.0 -{-# LANGUAGE CPP #-} -- To get precise GHC version and check if distributed binary +{-# LANGUAGE CPP #-} -- To get precise GHC version {-# LANGUAGE TemplateHaskell #-} {-# OPTIONS_GHC -Wno-dodgy-imports #-} -- GHC no longer exports def in GHC 8.6 and above {-# LANGUAGE DeriveGeneric #-} @@ -72,7 +72,6 @@ import Ide.Plugin.StylishHaskell as StylishHaskell import Ide.Plugin.Brittany as Brittany #endif import Ide.Plugin.Pragmas as Pragmas --- import Data.Typeable (Typeable) -- --------------------------------------------------------------------- diff --git a/src/Ide/Cradle.hs b/src/Ide/Cradle.hs index feec15310a..5562f26def 100644 --- a/src/Ide/Cradle.hs +++ b/src/Ide/Cradle.hs @@ -54,7 +54,7 @@ findLocalCradle fp = do debugm $ "Found \"" ++ yaml ++ "\" for \"" ++ fp ++ "\"" crdl <- Bios.loadCradle yaml return $ fmap (const CabalNone) crdl - Nothing -> Bios.loadImplicitCradle fp -- cabalHelperCradle fp + Nothing -> cabalHelperCradle fp logm $ "Module \"" ++ fp ++ "\" is loaded by Cradle: " ++ show crdl return crdl From 54ce0ec0aecc0aa7e0831b5c0500f533b823fd8f Mon Sep 17 00:00:00 2001 From: amesgen Date: Thu, 16 Jul 2020 19:07:26 +0200 Subject: [PATCH 48/51] use split sections --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a8b0f8ac08..3f05374956 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,7 +52,7 @@ jobs: - name: Set some linux specific things if: matrix.os == 'ubuntu-latest' run: | - echo '::set-env name=LINUX_CABAL_ARGS::--enable-executable-static' + echo '::set-env name=LINUX_CABAL_ARGS::--enable-executable-static --ghc-options=-split-sections' - name: Build Server shell: bash From 7156e45a6b687d63e5b188315d2189bab200db31 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Fri, 17 Jul 2020 13:38:36 +0100 Subject: [PATCH 49/51] 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. --- exe/Wrapper.hs | 24 +- haskell-language-server.cabal | 2 - src/Ide/Cradle.hs | 862 ---------------------------------- stack-8.10.1.yaml | 3 - stack-8.6.4.yaml | 1 - stack-8.6.5.yaml | 1 - stack-8.8.2.yaml | 1 - stack-8.8.3.yaml | 1 - stack.yaml | 1 - 9 files changed, 23 insertions(+), 873 deletions(-) delete mode 100644 src/Ide/Cradle.hs diff --git a/exe/Wrapper.hs b/exe/Wrapper.hs index 8b9b30f40c..703ceedaf3 100644 --- a/exe/Wrapper.hs +++ b/exe/Wrapper.hs @@ -7,10 +7,10 @@ import Arguments import Control.Monad.Extra import Data.Foldable import Data.List +import Data.Void import HIE.Bios import HIE.Bios.Environment import HIE.Bios.Types -import Ide.Cradle (findLocalCradle) import Ide.Version import System.Directory import System.Environment @@ -100,3 +100,25 @@ getRuntimeGhcVersion' cradle = do Nothing -> die $ "Cradle requires " ++ exe ++ " but couldn't find it" ++ "\n" ++ show cradle + +-- | Find the cradle that the given File belongs to. +-- +-- First looks for a "hie.yaml" file in the directory of the file +-- or one of its parents. If this file is found, the cradle +-- is read from the config. If this config does not comply to the "hie.yaml" +-- specification, an error is raised. +-- +-- If no "hie.yaml" can be found, the implicit config is used. +-- The implicit config uses different heuristics to determine the type +-- of the project that may or may not be accurate. +findLocalCradle :: FilePath -> IO (Cradle Void) +findLocalCradle fp = do + cradleConf <- findCradle fp + crdl <- case cradleConf of + Just yaml -> do + hPutStrLn stderr $ "Found \"" ++ yaml ++ "\" for \"" ++ fp ++ "\"" + loadCradle yaml + Nothing -> loadImplicitCradle fp + hPutStrLn stderr $ "Module \"" ++ fp ++ "\" is loaded by Cradle: " ++ show crdl + return crdl + diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index dcd1aa8752..7acdfaff6c 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -38,7 +38,6 @@ common cpp-flags library import: cpp-flags exposed-modules: - Ide.Cradle Ide.Logger Ide.Plugin Ide.Plugin.Config @@ -62,7 +61,6 @@ library , aeson , binary , bytestring - , cabal-helper >= 1.1 , containers , data-default , deepseq diff --git a/src/Ide/Cradle.hs b/src/Ide/Cradle.hs deleted file mode 100644 index 5562f26def..0000000000 --- a/src/Ide/Cradle.hs +++ /dev/null @@ -1,862 +0,0 @@ -{-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NamedFieldPuns #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE GADTs #-} - -module Ide.Cradle where - -import Control.Exception -import Data.Foldable (toList) -import Data.Function ((&)) -import Data.List (isPrefixOf, sortOn, find) -import Data.List.NonEmpty (NonEmpty) -import qualified Data.List.NonEmpty as NonEmpty -import qualified Data.Map as Map -import Data.Maybe (listToMaybe, mapMaybe, isJust) -import Data.Ord (Down(..)) -import Data.String (IsString(..)) -import qualified Data.Text as T -import Distribution.Helper (Package, projectPackages, pUnits, - pSourceDir, ChComponentInfo(..), - unChModuleName, Ex(..), ProjLoc(..), - QueryEnv, mkQueryEnv, runQuery, - Unit, unitInfo, uiComponents, - ChEntrypoint(..), UnitInfo(..), - qePrograms, ghcProgram) -import Distribution.Helper.Discover (findProjects, getDefaultDistDir) -import Ide.Logger -import HIE.Bios as Bios -import qualified HIE.Bios.Cradle as Bios -import HIE.Bios.Types (CradleAction(..)) -import qualified HIE.Bios.Types as Bios -import System.Directory (getCurrentDirectory, canonicalizePath, findExecutable) -import System.Exit -import System.FilePath -import System.Process (readCreateProcessWithExitCode, shell, CreateProcess(..)) - --- --------------------------------------------------------------------- - --- | Find the cradle that the given File belongs to. --- --- First looks for a "hie.yaml" file in the directory of the file --- or one of its parents. If this file is found, the cradle --- is read from the config. If this config does not comply to the "hie.yaml" --- specification, an error is raised. --- --- If no "hie.yaml" can be found, the implicit config is used. --- The implicit config uses different heuristics to determine the type --- of the project that may or may not be accurate. -findLocalCradle :: FilePath -> IO (Cradle CabalHelper) -findLocalCradle fp = do - cradleConf <- Bios.findCradle fp - crdl <- case cradleConf of - Just yaml -> do - debugm $ "Found \"" ++ yaml ++ "\" for \"" ++ fp ++ "\"" - crdl <- Bios.loadCradle yaml - return $ fmap (const CabalNone) crdl - Nothing -> cabalHelperCradle fp - logm $ "Module \"" ++ fp ++ "\" is loaded by Cradle: " ++ show crdl - return crdl - --- | Check if the given cradle is a stack cradle. --- This might be used to determine the GHC version to use on the project. --- If it is a stack-cradle, we have to use @"stack path --compiler-exe"@ --- otherwise we may ask `ghc` directly what version it is. -isStackCradle :: Cradle CabalHelper -> Bool -isStackCradle crdl = Bios.isStackCradle crdl || cabalHelperStackCradle crdl - where - cabalHelperStackCradle = - (`elem` [Bios.Other Stack, Bios.Other StackNone]) - . Bios.actionName - . Bios.cradleOptsProg - - --- | Check if the given cradle is a cabal cradle. --- This might be used to determine the GHC version to use on the project. --- If it is a stack-cradle, we have to use @"stack path --compiler-exe"@ --- otherwise we may ask @ghc@ directly what version it is. -isCabalCradle :: Cradle CabalHelper -> Bool -isCabalCradle crdl = Bios.isCabalCradle crdl || cabalHelperCabalCradle crdl - where - cabalHelperCabalCradle = - (`elem` [Bios.Other CabalV2, Bios.Other CabalNone]) - . Bios.actionName - . Bios.cradleOptsProg - -data CabalHelper - = Stack - | StackNone - | CabalV2 - | CabalNone - deriving (Show, Eq, Ord) - -tryCommand :: Cradle CabalHelper -> String -> IO String -tryCommand crdl cmd = do - let p = (shell cmd) { cwd = Just (cradleRootDir crdl) } - (code, sout, serr) <- readCreateProcessWithExitCode p "" - case code of - ExitFailure e -> do - let errmsg = concat - [ "`" - , cmd - , "`: Exit failure: " - , show e - , ", stdout: " - , sout - , ", stderr: " - , serr - ] - errorm errmsg - throwIO $ userError errmsg - - ExitSuccess -> return $ T.unpack . T.strip . head . T.lines $ T.pack sout - - - -- --------------------------------------------------------------------- - - -{- | Finds a Cabal v2-project, Cabal v1-project or a Stack project -relative to the given FilePath. -Cabal v2-project and Stack have priority over Cabal v1-project. -This entails that if a Cabal v1-project can be identified, it is -first checked whether there are Stack projects or Cabal v2-projects -before it is concluded that this is the project root. -Cabal v2-projects and Stack projects are equally important. -Due to the lack of user-input we have to guess which project it -should rather be. -This guessing has no guarantees and may change at any time. - -=== Example: - -Assume the following project structure: - -@ - / - └── Foo/ - ├── Foo.cabal - ├── stack.yaml - ├── cabal.project - ├── src - │ └── Lib.hs - └── B/ - ├── B.cabal - └── src/ - └── Lib2.hs -@ - -Assume the call @findCabalHelperEntryPoint "\/Foo\/B\/src\/Lib2.hs"@. -We now want to know to which project "\/Foo\/B\/src\/Lib2.hs" belongs to -and what the projects root is. If we only do a naive search to find the -first occurrence of either "B.cabal", "stack.yaml", "cabal.project" -or "Foo.cabal", we might assume that the location of "B.cabal" marks -the project's root directory of which "\/Foo\/B\/src\/Lib2.hs" is part of. -However, there is also a "cabal.project" and "stack.yaml" in the parent -directory, which add the package @B@ as a package. -So, the compilation of the package @B@, and the file "src\/Lib2.hs" in it, -does not only depend on the definitions in "B.cabal", but also -on "stack.yaml" and "cabal.project". -The project root is therefore "\/Foo\/". -Only if there is no "stack.yaml" or "cabal.project" in any of the ancestor -directories, it is safe to assume that "B.cabal" marks the root of the project. - -Thus: - ->>> findCabalHelperEntryPoint "/Foo/B/src/Lib2.hs -Just (Ex (ProjLocStackYaml { plStackYaml = "/Foo/"})) - -or - ->>> findCabalHelperEntryPoint "/Foo/B/src/Lib2.hs" -Just (Ex (ProjLocV2File { plProjectDirV2 = "/Foo/"})) - -In the given example, it is not guaranteed which project type is found, -it is only guaranteed that it will not identify the project -as a cabal v1-project. Note that with cabal-helper version (1.0), -by default a *.cabal file is identified as a 'ProjLocV2Dir' project. -The same issue as before exists and we look for a 'ProjLocV2File' or -'ProjLocStackYaml' before deciding that 'ProjLocV2Dir' marks the project root. - -Note that this will not return any project types for which the corresponding -build tool is not on the PATH. This is "stack" and "cabal" for stack and cabal -(both v1 and v2) projects respectively. --} -findCabalHelperEntryPoint :: FilePath -> IO (Maybe (Ex ProjLoc)) -findCabalHelperEntryPoint fp = do - allProjs <- concat <$> mapM findProjects (ancestors (takeDirectory fp)) - - debugm $ "Cabal-Helper found these projects: " ++ show (map (\(Ex x) -> show x) allProjs) - - -- We only want to return projects that we have the build tools installed for - isStackInstalled <- isJust <$> findExecutable "stack" - isCabalInstalled <- isJust <$> findExecutable "cabal" - let supportedProjs = filter (\x -> supported x isStackInstalled isCabalInstalled) allProjs - debugm $ "These projects have the build tools installed: " ++ show (map (\(Ex x) -> show x) supportedProjs) - - case filter (\p -> isCabalV2FileProject p || isStackProject p) supportedProjs of - (x:_) -> return $ Just x - [] -> case filter isCabalProject supportedProjs of - (x:_) -> return $ Just x - [] -> return Nothing - where - supported :: Ex ProjLoc -> Bool -> Bool -> Bool - supported (Ex ProjLocStackYaml {}) stackInstalled _ = stackInstalled - supported (Ex ProjLocV2Dir {}) _ cabalInstalled = cabalInstalled - supported (Ex ProjLocV2File {}) _ cabalInstalled = cabalInstalled - supported (Ex ProjLocV1Dir {}) _ cabalInstalled = cabalInstalled - supported (Ex ProjLocV1CabalFile {}) _ cabalInstalled = cabalInstalled - -isStackProject :: Ex ProjLoc -> Bool -isStackProject (Ex ProjLocStackYaml {}) = True -isStackProject _ = False - -isCabalV2FileProject :: Ex ProjLoc -> Bool -isCabalV2FileProject (Ex ProjLocV2File {}) = True -isCabalV2FileProject _ = False - -isCabalProject :: Ex ProjLoc -> Bool -isCabalProject (Ex ProjLocV1CabalFile {}) = True -isCabalProject (Ex ProjLocV1Dir {}) = True -isCabalProject (Ex ProjLocV2File {}) = True -isCabalProject (Ex ProjLocV2Dir {}) = True -isCabalProject _ = False - -{- | Given a FilePath, find the cradle the FilePath belongs to. - -Finds the Cabal Package the FilePath is most likely a part of -and creates a cradle whose root directory is the directory -of the package the File belongs to. - -It is not required that the FilePath given actually exists. If it does not -exist or is not part of any of the packages in the project, a "None"-cradle is -produced. -See for what a "None"-cradle is. -The "None"-cradle can still be used to query for basic information, such as -the GHC version used to build the project. However, it can not be used to -load any of the files in the project. - -== General Approach - -Given a FilePath that we want to load, we need to create a cradle -that can compile and load the given FilePath. -In Cabal-Helper, there is no notion of a cradle, but a project -consists of multiple packages that contain multiple units. -Each unit may consist of multiple components. -A unit is the smallest part of code that Cabal (the library) can compile. -Examples are executables, libraries, tests or benchmarks are all units. -Each of this units has a name that is unique within a build-plan, -such as "exe:hie" which represents the executable of the Haskell IDE Engine. - -In principle, a unit is what hie-bios considers to be a cradle. -However, to find out to which unit a FilePath belongs, we have to initialise -the unit, e.g. configure its dependencies and so on. When discovering a cradle -we do not want to pay for this upfront, but rather when we actually want to -load a Module in the project. Therefore, we only identify the package the -FilePath is part of and decide which unit to load when 'runCradle' is executed. - -Thus, to find the options required to compile and load the given FilePath, -we have to do the following: - - 1. Identify the package that contains the FilePath (should be unique) - Happens in 'cabalHelperCradle' - 2. Find the unit that that contains the FilePath (May be non-unique) - Happens in 'cabalHelperAction' - 3. Find the component that exposes the FilePath (May be non-unique) - Happens in 'cabalHelperAction' - -=== Identify the package that contains the FilePath - -The function 'cabalHelperCradle' does the first step only. -It starts by querying Cabal-Helper to find the project's root. -See 'findCabalHelperEntryPoint' for details how this is done. -Once the root of the project is defined, we query Cabal-Helper for all packages -that are defined in the project and match by the packages source directory -which package the given FilePath is most likely to be a part of. -E.g. if the source directory of the package is the most concrete -prefix of the FilePath, the FilePath is in that package. -After the package is identified, we create a cradle where cradle's root -directory is set to the package's source directory. This is necessary, -because compiler options obtained from a component, are relative -to the source directory of the package the component is part of. - -=== Find the unit that that contains the FilePath - -In 'cabalHelperAction' we want to load a given FilePath, already knowing -which package the FilePath is part of. Now we obtain all Units that are part -of the package and match by the source directories (plural is intentional), -to which unit the given FilePath most likely belongs to. If no unit can be -obtained, e.g. for every unit, no source directory is a prefix of the FilePath, -we return an error code, since this is not allowed to happen. -If there are multiple matches, which is possible, we check whether any of the -components defined in the unit exposes or defines the given FilePath as a module. - -=== Find the component that exposes the FilePath - -A component defines the options that are necessary to compile a FilePath that -is in the component. It also defines which modules are in the component. -Therefore, we translate the given FilePath into a module name, relative to -the unit's source directory, and check if the module name is exposed by the -component. There is a special case, executables define a FilePath, for the -file that contains the 'main'-function, that is relative to the unit's source -directory. - -After the component has been identified, we can actually retrieve the options -required to load and compile the given file. - -== Examples - -=== Mono-Repo - -Assume the project structure: - -@ - / - └── Mono/ - ├── cabal.project - ├── stack.yaml - ├── A/ - │ ├── A.cabal - │ └── Lib.hs - └── B/ - ├── B.cabal - └── Exe.hs -@ - -Currently, Haskell IDE Engine needs to know on startup which GHC version is -needed to compile the project. This information is needed to show warnings to -the user if the GHC version on the project does not agree with the GHC version -that was used to compile Haskell IDE Engine. - -Therefore, the function 'findLocalCradle' is invoked with a dummy FilePath, -such as "\/Mono\/Lib.hs". Since there will be no package that contains this -dummy FilePath, the result will be a None-cradle. - -Either - ->>> findLocalCradle "/Mono/Lib.hs" -Cradle { cradleRootDir = "/Mono/", CradleAction { actionName = "Cabal-Helper-Stack-None", ..} } - -or - ->>> findLocalCradle "/Mono/Lib.hs" -Cradle { cradleRootDir = "/Mono/", CradleAction { actionName = "Cabal-Helper-Cabal-V2-None", ..} } - -The cradle result of this invocation is only used to obtain the GHC version, -which is safe, since it only checks if the cradle is a 'stack' project or -a 'cabal' project. - - -If we are trying to load the executable: - ->>> findLocalCradle "/Mono/B/Exe.hs" -Cradle { cradleRootDir = "/Mono/", CradleAction { actionName = "Cabal-Helper-Cabal-V2", ..} } - -we will detect correctly the compiler options, by first finding the appropriate -package, followed by traversing the units in the package and finding the -component that exposes the executable by FilePath. - -=== No explicit executable folder - -Assume the project structure: - -@ - / - └── Library/ - ├── cabal.project - ├── stack.yaml - ├── Library.cabal - └── src - ├── Lib.hs - └── Exe.hs -@ - -There are different dependencies for the library "Lib.hs" and the -executable "Exe.hs". If we are trying to load the executable "src\/Exe.hs" -we will correctly identify the executable unit, and correctly initialise -dependencies of "exe:Library". -It will be correct even if we load the unit "lib:Library" before -the "exe:Library" because the unit "lib:Library" does not expose -a module @"Exe"@. - -=== Sub package - -Assume the project structure: - -@ - / - └── Repo/ - ├── cabal.project - ├── stack.yaml - ├── Library.cabal - ├── src - | └── Lib.hs - └── SubRepo - ├── SubRepo.cabal - └── Lib2.hs -@ - -When we try to load "\/Repo\/SubRepo\/Lib2.hs", we need to identify root -of the project, which is "\/Repo\/" but set the root directory of the cradle -responsible to load "\/Repo\/SubRepo\/Lib2.hs" to "\/Repo\/SubRepo", since -the compiler options obtained from Cabal-Helper are relative to the package -source directory, which is "\/Repo\/SubRepo". - --} -cabalHelperCradle :: FilePath -> IO (Cradle CabalHelper) -cabalHelperCradle file = do - projM <- findCabalHelperEntryPoint file - case projM of - Nothing -> do - errorm $ "Could not find a Project for file: " ++ file - cwd <- getCurrentDirectory - return - Cradle { cradleRootDir = cwd - , cradleOptsProg = - CradleAction { actionName = Bios.Direct - , runCradle = \_ _ -> - return - $ CradleSuccess - ComponentOptions - { componentOptions = [file, fixImportDirs cwd "-i."] - , componentRoot = cwd - , componentDependencies = [] - } - , runGhcCmd = \args -> Bios.readProcessWithCwd cwd "ghc" args "" - } - } - Just (Ex proj) -> do - logm $ "Cabal-Helper decided to use: " ++ show proj - -- Find the root of the project based on project type. - let root = projectRootDir proj - -- Create a suffix for the cradle name. - -- Purpose is mainly for easier debugging. - let actionNameSuffix = projectType proj - debugm $ "Cabal-Helper dirs: " ++ show [root, file] - let dist_dir = getDefaultDistDir proj - env <- mkQueryEnv proj dist_dir - packages <- runQuery projectPackages env - -- Find the package the given file may belong to. - -- If it does not belong to any package, create a none-cradle. - -- We might want to find a cradle without actually loading anything. - -- Useful if we only want to determine a ghc version to use. - case packages `findPackageFor` file of - Nothing -> do - debugm $ "Could not find a package for the file: " ++ file - debugm - "This is perfectly fine if we only want to determine the GHC version." - return - Cradle { cradleRootDir = root - , cradleOptsProg = - CradleAction { actionName = Bios.Other (projectNoneType proj) - , runCradle = \_ _ -> return CradleNone - , runGhcCmd = \_ -> pure CradleNone - } - } - Just realPackage -> do - debugm $ "Cabal-Helper cradle package: " ++ show realPackage - -- Field `pSourceDir` often has the form `/./plugin` - -- but we only want `/plugin` - normalisedPackageLocation <- canonicalizePath $ pSourceDir realPackage - debugm - $ "Cabal-Helper normalisedPackageLocation: " - ++ normalisedPackageLocation - return - Cradle { cradleRootDir = normalisedPackageLocation - , cradleOptsProg = - CradleAction { actionName = Bios.Other actionNameSuffix - , runCradle = \_ fp -> cabalHelperAction - (Ex proj) - env - realPackage - normalisedPackageLocation - fp - , runGhcCmd = \args -> do - let programs = qePrograms env - Bios.readProcessWithCwd normalisedPackageLocation (ghcProgram programs) args "" - } - } - --- | Cradle Action to query for the ComponentOptions that are needed --- to load the given FilePath. --- This Function is not supposed to throw any exceptions and use --- 'CradleLoadResult' to indicate errors. -cabalHelperAction :: Ex ProjLoc -- ^ Project location, can be used - -- to present build-tool - -- agnostic error messages. - -> QueryEnv v -- ^ Query Env created by 'mkQueryEnv' - -- with the appropriate 'distdir' - -> Package v -- ^ Package this cradle is part for. - -> FilePath -- ^ Root directory of the cradle - -- this action belongs to. - -> FilePath -- ^ FilePath to load, expected to be an absolute path. - -> IO (CradleLoadResult ComponentOptions) -cabalHelperAction proj env package root fp = do - -- Get all unit infos the given FilePath may belong to - let units = pUnits package - -- make the FilePath to load relative to the root of the cradle. - let relativeFp = makeRelative root fp - debugm $ "Relative Module FilePath: " ++ relativeFp - getComponent proj env (toList units) relativeFp - >>= \case - Right comp -> do - let fs' = getFlags comp - let fs = map (fixImportDirs root) fs' - let targets = getTargets comp relativeFp - let ghcOptions = fs ++ targets - debugm $ "Flags for \"" ++ fp ++ "\": " ++ show ghcOptions - debugm $ "Component Infos: " ++ show comp - return - $ CradleSuccess - ComponentOptions { componentOptions = ghcOptions - , componentRoot = root - , componentDependencies = [] - } - Left err -> return - $ CradleFail - $ CradleError - [] - (ExitFailure 2) - err - --- | Fix occurrences of "-i." to "-i" --- Flags obtained from cabal-helper are relative to the package --- source directory. This is less resilient to using absolute paths, --- thus, we fix it here. -fixImportDirs :: FilePath -> String -> String -fixImportDirs base_dir arg = - if "-i" `isPrefixOf` arg - then let dir = drop 2 arg - -- the flag "-i" has special meaning. - in if not (null dir) && isRelative dir then ("-i" ++ base_dir dir) - else arg - else arg - - --- | Get the component the given FilePath most likely belongs to. --- Lazily ask units whether the given FilePath is part of one of their --- component's. --- If a Module belongs to multiple components, it is not specified which --- component will be loaded. --- The given FilePath must be relative to the Root of the project --- the given units belong to. -getComponent - :: forall pt. Ex ProjLoc -> QueryEnv pt -> [Unit pt] -> FilePath -> IO (Either [String] ChComponentInfo) -getComponent proj env unitCandidates fp = getComponent' [] [] unitCandidates >>= - \case - (tried, failed, Nothing) -> return (Left $ buildErrorMsg tried failed) - (_, _, Just comp) -> return (Right comp) - where - getComponent' :: [UnitInfo] -> [(Unit pt, IOException)] -> [Unit pt] -> IO ([UnitInfo], [(Unit pt, IOException)], Maybe ChComponentInfo) - getComponent' triedUnits failedUnits [] = return (triedUnits, failedUnits, Nothing) - getComponent' triedUnits failedUnits (unit : units) = - try (runQuery (unitInfo unit) env) >>= \case - Left (e :: IOException) -> do - warningm $ "Catching and swallowing an IOException: " ++ show e - warningm - $ "The Exception was thrown in the context of finding" - ++ " a component for \"" - ++ fp - ++ "\" in the unit: " - ++ show unit - getComponent' triedUnits ((unit, e):failedUnits) units - Right ui -> do - let components = Map.elems (uiComponents ui) - debugm $ "Unit Info: " ++ show ui - case find (fp `partOfComponent`) components of - Nothing -> getComponent' (ui:triedUnits) failedUnits units - comp -> return (triedUnits, failedUnits, comp) - - buildErrorMsg :: [UnitInfo] -> [(Unit pt, IOException)] -> [String] - buildErrorMsg triedUnits failedUnits = - concat - [ [ "Could not obtain flags for: \"" ++ fp ++ "\"." - , "" - ] - , concat - [ concat - [ [ "This module was not part of any component we are aware of." - , "" - ] - , concatMap ppShowUnitInfo triedUnits - , [ "" - , "" - ] - , if isStackProject proj - then stackSpecificInstructions - else cabalSpecificInstructions - ] - | not (null triedUnits) - ] - , concat - [ - [ "We could not build all components." - , "If one of these components exposes this Module, make sure they compile." - , "You can try to invoke the commands yourself." - , "The following commands failed:" - ] - ++ concatMap (ppShowIOException . snd) failedUnits - | not (null failedUnits) - ] - ] - - stackSpecificInstructions :: [String] - stackSpecificInstructions = - [ "To expose a module, refer to:" - , "https://docs.haskellstack.org/en/stable/GUIDE/" - , "If you are using `package.yaml` then you don't have to manually expose modules." - , "Maybe you didn't set the source directories for your project correctly." - ] - - cabalSpecificInstructions :: [String] - cabalSpecificInstructions = - [ "To expose a module, refer to:" - , "https://www.haskell.org/cabal/users-guide/developing-packages.html" - , "" - ] - - ppShowUnitInfo :: UnitInfo -> [String] - ppShowUnitInfo u = - u - & uiComponents - & Map.toList - & map - (\(name, info) -> - "Component: " ++ show name ++ " with source directory: " ++ show (ciSourceDirs info) - ) - - - ppShowIOException :: IOException -> [String] - ppShowIOException e = - [ "" - , show e - ] - --- | Check whether the given FilePath is part of the Component. --- A FilePath is part of the Component if and only if: --- --- * One Component's 'ciSourceDirs' is a prefix of the FilePath --- * The FilePath, after converted to a module name, --- is a in the Component's Targets, or the FilePath is --- the executable in the component. --- --- The latter is achieved by making the FilePath relative to the 'ciSourceDirs' --- and then replacing Path separators with ".". --- To check whether the given FilePath is the executable of the Component, --- we have to check whether the FilePath, including 'ciSourceDirs', --- is part of the targets in the Component. -partOfComponent :: - -- | FilePath relative to the package root. - FilePath -> - -- | Component to check whether the given FilePath is part of it. - ChComponentInfo -> - Bool -partOfComponent fp' comp = - inTargets (ciSourceDirs comp) fp' (getTargets comp fp') - where - -- Check if the FilePath is in an executable or setup's main-is field - inMainIs :: FilePath -> Bool - inMainIs fp - | ChExeEntrypoint mainIs _ <- ciEntrypoints comp = mainIs == fp - | ChSetupEntrypoint mainIs <- ciEntrypoints comp = mainIs == fp - | otherwise = False - - inTargets :: [FilePath] -> FilePath -> [String] -> Bool - inTargets sourceDirs fp targets = - let candidates = relativeTo fp sourceDirs - in any (existsInTargets targets fp) candidates - - existsInTargets :: [String] -> FilePath -> FilePath -> Bool - existsInTargets targets absFp relFp = or - [ any (`elem` targets) [getModuleName relFp, absFp] - , inMainIs relFp - ] - - getModuleName :: FilePath -> String - getModuleName fp = map - (\c -> if isPathSeparator c - then '.' - else c) - (dropExtension fp) - --- | Get the flags necessary to compile the given component. -getFlags :: ChComponentInfo -> [String] -getFlags = ciGhcOptions - --- | Get all Targets of a Component, since we want to load all components. --- FilePath is needed for the special case that the Component is an Exe. --- The Exe contains a Path to the Main which is relative to some entry --- in 'ciSourceDirs'. --- We monkey-patch this by supplying the FilePath we want to load, --- which is part of this component, and select the 'ciSourceDir' we actually want. --- See the Documentation of 'ciSourceDir' to why this contains multiple entries. -getTargets :: ChComponentInfo -> FilePath -> [String] -getTargets comp fp = case ciEntrypoints comp of - ChSetupEntrypoint {} -> [] - ChLibEntrypoint { chExposedModules, chOtherModules } - -> map unChModuleName (chExposedModules ++ chOtherModules) - ChExeEntrypoint { chMainIs, chOtherModules } - -> [sourceDir chMainIs | Just sourceDir <- [sourceDirs]] - ++ map unChModuleName chOtherModules - where - sourceDirs = find (`isFilePathPrefixOf` fp) (ciSourceDirs comp) - --- | For all packages in a project, find the project the given FilePath --- belongs to most likely. -findPackageFor :: NonEmpty (Package pt) -> FilePath -> Maybe (Package pt) -findPackageFor packages fp = packages - & NonEmpty.toList - & sortOn (Down . pSourceDir) - & filter (\p -> pSourceDir p `isFilePathPrefixOf` fp) - & listToMaybe - - -projectRootDir :: ProjLoc qt -> FilePath -projectRootDir ProjLocV1CabalFile { plProjectDirV1 } = plProjectDirV1 -projectRootDir ProjLocV1Dir { plProjectDirV1 } = plProjectDirV1 -projectRootDir ProjLocV2File { plProjectDirV2 } = plProjectDirV2 -projectRootDir ProjLocV2Dir { plProjectDirV2 } = plProjectDirV2 -projectRootDir ProjLocStackYaml { plStackYaml } = takeDirectory plStackYaml - -projectType :: ProjLoc qt -> CabalHelper -projectType ProjLocV1CabalFile {} = CabalV2 -projectType ProjLocV1Dir {} = CabalV2 -projectType ProjLocV2File {} = CabalV2 -projectType ProjLocV2Dir {} = CabalV2 -projectType ProjLocStackYaml {} = Stack - -projectNoneType :: ProjLoc qt -> CabalHelper -projectNoneType ProjLocV1CabalFile {} = CabalNone -projectNoneType ProjLocV1Dir {} = CabalNone -projectNoneType ProjLocV2File {} = CabalNone -projectNoneType ProjLocV2Dir {} = CabalNone -projectNoneType ProjLocStackYaml {} = StackNone - --- ---------------------------------------------------------------------------- --- --- Utility functions to manipulate FilePath's --- --- ---------------------------------------------------------------------------- - --- | Helper function to make sure that both FilePaths are normalised. --- Checks whether the first FilePath is a Prefix of the second FilePath. --- Intended usage: --- --- >>> isFilePathPrefixOf "./src/" "./src/File.hs" --- True --- --- >>> isFilePathPrefixOf "./src" "./src/File.hs" --- True --- --- >>> isFilePathPrefixOf "./src/././" "./src/File.hs" --- True --- --- >>> isFilePathPrefixOf "./src" "./src-dir/File.hs" --- False -isFilePathPrefixOf :: FilePath -> FilePath -> Bool -isFilePathPrefixOf dir fp = isJust $ stripFilePath dir fp - --- | Strip the given directory from the filepath if and only if --- the given directory is a prefix of the filepath. --- --- >>> stripFilePath "app" "app/File.hs" --- Just "File.hs" --- --- >>> stripFilePath "src" "app/File.hs" --- Nothing --- --- >>> stripFilePath "src" "src-dir/File.hs" --- Nothing --- --- >>> stripFilePath "." "src/File.hs" --- Just "src/File.hs" --- --- >>> stripFilePath "app/" "./app/Lib/File.hs" --- Just "Lib/File.hs" --- --- >>> stripFilePath "/app/" "./app/Lib/File.hs" --- Nothing -- Nothing since '/app/' is absolute --- --- >>> stripFilePath "/app" "/app/Lib/File.hs" --- Just "Lib/File.hs" -stripFilePath :: FilePath -> FilePath -> Maybe FilePath -stripFilePath "." fp - | isRelative fp = Just fp - | otherwise = Nothing -stripFilePath dir' fp' - | Just relativeFpParts <- splitDir `stripPrefix` splitFp = Just (joinPath relativeFpParts) - | otherwise = Nothing - where - dir = normalise dir' - fp = normalise fp' - splitFp = splitPath fp - splitDir = splitPath dir - stripPrefix (x:xs) (y:ys) - | x `equalFilePath` y = stripPrefix xs ys - | otherwise = Nothing - stripPrefix [] ys = Just ys - stripPrefix _ [] = Nothing - --- | Obtain all ancestors from a given directory. --- --- >>> ancestors "a/b/c/d/e" --- [ "a/b/c/d/e", "a/b/c/d", "a/b/c", "a/b", "a", "." ] --- --- >>> ancestors "/a/b/c/d/e" --- [ "/a/b/c/d/e", "/a/b/c/d", "/a/b/c", "/a/b", "/a", "/" ] --- --- >>> ancestors "/a/b.hs" --- [ "/a/b.hs", "/a", "/" ] --- --- >>> ancestors "a/b.hs" --- [ "a/b.hs", "a", "." ] --- --- >>> ancestors "a/b/" --- [ "a/b" ] -ancestors :: FilePath -> [FilePath] -ancestors dir - | subdir `equalFilePath` dir = [dir] - | otherwise = dir : ancestors subdir - where - subdir = takeDirectory dir - --- | Assuming a FilePath @"src\/Lib\/Lib.hs"@ and a list of directories --- such as @["src", "app"]@, returns the given FilePath --- with a matching directory stripped away. --- If there are multiple matches, e.g. multiple directories are a prefix --- of the given FilePath we return all matches. --- Returns an empty list if no prefix matches the given FilePath. --- --- >>> relativeTo "src/Lib/Lib.hs" ["src"] --- ["Lib/Lib.hs"] --- --- >>> relativeTo "src/Lib/Lib.hs" ["app"] --- [] --- --- >>> relativeTo "src/Lib/Lib.hs" ["src", "src/Lib"] --- ["Lib/Lib.hs", "Lib.hs"] -relativeTo :: FilePath -> [FilePath] -> [FilePath] -relativeTo file sourceDirs = - mapMaybe (`stripFilePath` file) sourceDirs - --- | Returns a user facing display name for the cradle type, --- e.g. "Stack project" or "GHC session" -cradleDisplay :: IsString a => Cradle CabalHelper -> a -cradleDisplay cradle = fromString result - where - result - | Bios.isStackCradle cradle - || name - `elem` [Bios.Other Stack, Bios.Other StackNone] - = "Stack project" - | Bios.isCabalCradle cradle - || name - `elem` [Bios.Other CabalV2, Bios.Other CabalNone] - = "Cabal project" - | Bios.isDirectCradle cradle - = "GHC session" - | Bios.isMultiCradle cradle - = "Multi Component project" - | otherwise - = "project" - name = Bios.actionName (Bios.cradleOptsProg cradle) - --- --------------------------------------------------------------------- diff --git a/stack-8.10.1.yaml b/stack-8.10.1.yaml index 73b0855aff..c28931ef18 100644 --- a/stack-8.10.1.yaml +++ b/stack-8.10.1.yaml @@ -6,10 +6,7 @@ packages: extra-deps: - Cabal-3.0.2.0 -# - cabal-helper-1.1.0.0 - hie-bios-0.6.1 -- github: DanielG/cabal-helper - commit: 79a5608778493bf32e74b54bbf1ea2729941e50f - cabal-plan-0.7.0.0 - clock-0.7.2 - floskell-0.10.3 diff --git a/stack-8.6.4.yaml b/stack-8.6.4.yaml index 2f673b24f8..56eb41a9ad 100644 --- a/stack-8.6.4.yaml +++ b/stack-8.6.4.yaml @@ -11,7 +11,6 @@ extra-deps: - bytestring-trie-0.2.5.0 - Cabal-3.0.2.0 - cabal-doctest-1.0.8 -- cabal-helper-1.1.0.0 - cabal-plan-0.5.0.0 - constrained-dynamic-0.1.0.0 - deque-0.4.3 diff --git a/stack-8.6.5.yaml b/stack-8.6.5.yaml index 2a6f9298e7..62db328d8c 100644 --- a/stack-8.6.5.yaml +++ b/stack-8.6.5.yaml @@ -10,7 +10,6 @@ extra-deps: - brittany-0.12.1.1@rev:2 - butcher-1.3.3.1 - Cabal-3.0.2.0 -- cabal-helper-1.1.0.0 - cabal-plan-0.6.2.0 - clock-0.7.2 - extra-1.7.3 diff --git a/stack-8.8.2.yaml b/stack-8.8.2.yaml index af8599e938..ad95d9ea5b 100644 --- a/stack-8.8.2.yaml +++ b/stack-8.8.2.yaml @@ -9,7 +9,6 @@ extra-deps: - brittany-0.12.1.1 - butcher-1.3.3.2 - bytestring-trie-0.2.5.0 -- cabal-helper-1.1.0.0 - clock-0.7.2 - constrained-dynamic-0.1.0.0 - extra-1.7.3 diff --git a/stack-8.8.3.yaml b/stack-8.8.3.yaml index 7f8fe524ba..8ab95de263 100644 --- a/stack-8.8.3.yaml +++ b/stack-8.8.3.yaml @@ -7,7 +7,6 @@ packages: extra-deps: - apply-refact-0.7.0.0 - bytestring-trie-0.2.5.0 -- cabal-helper-1.1.0.0 - cabal-plan-0.6.2.0 - clock-0.7.2 - constrained-dynamic-0.1.0.0 diff --git a/stack.yaml b/stack.yaml index 88eb9f5d82..1ff400f465 100644 --- a/stack.yaml +++ b/stack.yaml @@ -10,7 +10,6 @@ extra-deps: - brittany-0.12.1.1@rev:2 - butcher-1.3.3.1 - Cabal-3.0.2.0 -- cabal-helper-1.1.0.0 - cabal-plan-0.6.2.0 - clock-0.7.2 - extra-1.7.3 From 9238ced813330b8a035d7a3abb9022904c5552ed Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Fri, 17 Jul 2020 13:49:59 +0100 Subject: [PATCH 50/51] Undo agpl common stanza change --- haskell-language-server.cabal | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/haskell-language-server.cabal b/haskell-language-server.cabal index 7acdfaff6c..f4a1d351e9 100644 --- a/haskell-language-server.cabal +++ b/haskell-language-server.cabal @@ -30,13 +30,13 @@ source-repository head type: git location: https://github.com/haskell/haskell-language-server -common cpp-flags +common agpl if flag(agpl) cpp-options: -DAGPL library - import: cpp-flags + import: agpl exposed-modules: Ide.Logger Ide.Plugin @@ -106,7 +106,7 @@ library default-language: Haskell2010 executable haskell-language-server - import: cpp-flags + import: agpl main-is: Main.hs hs-source-dirs: exe @@ -156,7 +156,7 @@ executable haskell-language-server default-language: Haskell2010 executable haskell-language-server-wrapper - import: cpp-flags + import: agpl main-is: Wrapper.hs hs-source-dirs: exe @@ -196,7 +196,7 @@ executable haskell-language-server-wrapper -- We removed it due to issues with stack when loading the project using a stack based hie.yaml -- See https://github.com/haskell/haskell-language-server/issues/114 common hls-test-utils - import: cpp-flags + import: agpl hs-source-dirs: test/utils other-modules: Test.Hls.Util build-depends: base @@ -226,7 +226,7 @@ common hls-test-utils default-language: Haskell2010 test-suite func-test - import: cpp-flags, hls-test-utils + import: agpl, hls-test-utils type: exitcode-stdio-1.0 default-language: Haskell2010 build-tool-depends: haskell-language-server:haskell-language-server @@ -276,7 +276,7 @@ test-suite func-test ghc-options: -Werror -Wredundant-constraints test-suite wrapper-test - import: cpp-flags, hls-test-utils + import: agpl, hls-test-utils type: exitcode-stdio-1.0 build-tool-depends: haskell-language-server:haskell-language-server-wrapper default-language: Haskell2010 From ebe5ef11cb53c4c1914f45f8301bd3b12c81beb4 Mon Sep 17 00:00:00 2001 From: Luke Lau Date: Fri, 17 Jul 2020 13:55:08 +0100 Subject: [PATCH 51/51] Add release number --- docs/releases.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/releases.md b/docs/releases.md index f1dc6dc040..a8645c1950 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -1,6 +1,6 @@ # Releases and distributable binaries -Starting with 0.X.0.0 haskell-language-server provides pre-built binaries on +Starting with 0.3.0.0 haskell-language-server provides pre-built binaries on each [GitHub release](https://github.com/haskell/haskell-language-server/releases). These binaries are used by the [vscode-hie-server