#!/usr/bin/env stack {- stack script --resolver lts-13.12 --ghc-options -Wall --package bytestring,text,filepath,directory,optparse-applicative,typed-process -} {-# LANGUAGE CPP #-} -- = About -- -- Install multiple versions of haskell-code-indexer, each with the version of -- GHC it was built with appended to the executable name. -- -- = Original -- -- Modified from the BSD3 licensed script at: -- https://github.com/haskell/haskell-ide-engine/blob/ec5e34ca52d389b713df918f02ff63920aede4be/install.hs -- -- Thanks haskell-ide-engine folks! -- -- = Changes from the original -- -- + Switched from Shake to IO script -- + Added optparse-applicative -- + Switched to Stack only (PRs welcome to support other tools) module Main (main) where import Control.Monad import Data.ByteString (ByteString) import qualified Data.ByteString.Lazy as LBS import Data.Char (isSpace) import Data.Foldable import Data.List (dropWhileEnd) import qualified Data.Text as T import Data.Text.Encoding import System.FilePath ((<.>), ()) import Options.Applicative import System.Directory (copyFile, removeFile) import System.Process.Typed -- | Keep this in sync with the stack.yamls at the top level of the project. supportedGhcVersions :: [Version] supportedGhcVersions = map Version ["8.0.2", "8.2.2", "8.4.3", "8.4.4", "8.6.3", "8.6.4","8.6.5"] newtype Version = Version { unVersion :: String } deriving Eq -- * CLI args data Args = Args { argBuildVersions :: [Version] , argBuildServer :: Bool } cliArgs :: IO Args cliArgs = customExecParser (prefs showHelpOnError) argsParser argsParser :: ParserInfo Args argsParser = fmap defaultToAll $ info (helper <*> parser) (fullDesc <> progDesc desc) where parser :: Parser Args parser = Args <$> (some indexVersion <|> pure mempty) <*> switch ( long "server" <> help "Build haskell-code-server" ) indexVersion :: Parser Version indexVersion = argument (eitherReader checkVersion) ( metavar "INDEX_VERSION" <> help "haskell-code-indexer-X-Y-Z version to build" ) checkVersion :: String -> Either String Version checkVersion s = case find ((==) (Version s)) supportedGhcVersions of Nothing -> Left . unwords $ "Not a supported GHC version. Currently supported versions are:" : map unVersion supportedGhcVersions Just v -> Right v defaultToAll :: Args -> Args defaultToAll args = if argBuildVersions args == mempty && argBuildServer args == False then Args (reverse supportedGhcVersions) True -- reverse to build latest first else args desc :: String desc = "Install haskell-code-indexer executables with the GHC version they were" <> " compiled with appended to their name. Builds everything if you don't" <> " specify options. Note that if you already have an indexer executable" <> " without the GHC version appended in your Stack's local bin" <> " it will be deleted." -- * Build main :: IO () main = run =<< cliArgs run :: Args -> IO () run args = do putStrLn (startupNotice args) when (argBuildServer args) buildServer for_ (argBuildVersions args) buildVersion startupNotice :: Args -> String startupNotice args = unlines $ "Building:" : (if argBuildServer args then [" + haskell-code-explorer"] else mempty) <> map versionEntry (argBuildVersions args) where versionEntry :: Version -> String versionEntry v = " + haskell-code-indexer-" <> unVersion v buildServer :: IO () buildServer = void $ execStack ["build", "--copy-bins", "haskell-code-explorer:haskell-code-server"] buildVersion :: Version -> IO () buildVersion v = do execStackWithVersion_ v ["build", "--copy-bins", "haskell-code-explorer:haskell-code-indexer"] localBinDir <- getLocalBin let -- exe is "exe" on Windows and "" otherwise fromFile = localBinDir "haskell-code-indexer" <.> exe toFile = localBinDir "haskell-code-indexer-" ++ unVersion v <.> exe copyFile fromFile toFile removeFile fromFile exe :: String #if defined(mingw32_HOST_OS) exe = "exe" #else exe = "" #endif -- | E.g. @"/home/user/bin"@. getLocalBin :: IO FilePath getLocalBin = do stackLocalDir' <- decodeUtf8 <$> execStack ["path", "--stack-yaml=stack.yaml", "--local-bin"] pure $ trimEnd (T.unpack stackLocalDir') -- | Uses the stack.yaml for the given @Version@. execStackWithVersion_ :: Version -> [String] -> IO () execStackWithVersion_ v args = do let stackFile = "stack-" ++ unVersion v ++ ".yaml" void $ execStack (("--stack-yaml=" ++ stackFile) : args) execStack :: [String] -> IO ByteString execStack = fmap LBS.toStrict . readProcessStdout_ . proc "stack" trimEnd :: String -> String trimEnd = dropWhileEnd isSpace