hie-bios 0.4.0 → 0.5.0
raw patch · 93 files changed
+1469/−1327 lines, 93 filesdep ~ghcsetup-changedPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: ghc
API changes (from Hackage documentation)
+ HIE.Bios: [componentRoot] :: ComponentOptions -> FilePath
+ HIE.Bios.Environment: makeDynFlagsAbsolute :: FilePath -> DynFlags -> DynFlags
+ HIE.Bios.Flags: getCompilerOptionsWithLogger :: LoggingFunction -> FilePath -> Cradle a -> IO (CradleLoadResult ComponentOptions)
+ HIE.Bios.Flags: type LoggingFunction = String -> IO ()
+ HIE.Bios.Ghc.Gap: mapOverIncludePaths :: (FilePath -> FilePath) -> DynFlags -> DynFlags
+ HIE.Bios.Types: [componentRoot] :: ComponentOptions -> FilePath
- HIE.Bios: ComponentOptions :: [String] -> [FilePath] -> ComponentOptions
+ HIE.Bios: ComponentOptions :: [String] -> FilePath -> [FilePath] -> ComponentOptions
- HIE.Bios.Types: ComponentOptions :: [String] -> [FilePath] -> ComponentOptions
+ HIE.Bios.Types: ComponentOptions :: [String] -> FilePath -> [FilePath] -> ComponentOptions
Files
- ChangeLog +18/−0
- LICENSE +29/−29
- README.md +1/−1
- Setup.hs +2/−2
- exe/Main.hs +1/−0
- hie-bios.cabal +9/−4
- src/HIE/Bios.hs +32/−32
- src/HIE/Bios/Config.hs +188/−188
- src/HIE/Bios/Cradle.hs +91/−95
- src/HIE/Bios/Environment.hs +59/−9
- src/HIE/Bios/Flags.hs +25/−17
- src/HIE/Bios/Ghc/Api.hs +108/−108
- src/HIE/Bios/Ghc/Doc.hs +25/−25
- src/HIE/Bios/Ghc/Gap.hs +20/−1
- src/HIE/Bios/Ghc/Load.hs +171/−171
- src/HIE/Bios/Ghc/Logger.hs +118/−118
- src/HIE/Bios/Internal/Debug.hs +2/−1
- src/HIE/Bios/Internal/Log.hs +16/−16
- src/HIE/Bios/Types.hs +3/−0
- src/HIE/Bios/Wrappers.hs +12/−12
- tests/BiosTests.hs +22/−6
- tests/ParserTests.hs +105/−105
- tests/configs/bazel.yaml +2/−2
- tests/configs/bios-1.yaml +3/−3
- tests/configs/bios-2.yaml +4/−4
- tests/configs/cabal-1.yaml +2/−2
- tests/configs/cabal-multi.yaml +6/−6
- tests/configs/ch-cabal.yaml +3/−3
- tests/configs/ch-stack.yaml +3/−3
- tests/configs/default.yaml +2/−2
- tests/configs/dependencies.yaml +5/−5
- tests/configs/direct.yaml +3/−3
- tests/configs/multi-ch.yaml +10/−10
- tests/configs/multi.yaml +6/−6
- tests/configs/nested-cabal-multi.yaml +8/−8
- tests/configs/nested-stack-multi.yaml +8/−8
- tests/configs/none.yaml +2/−2
- tests/configs/obelisk.yaml +2/−2
- tests/configs/stack-config.yaml +2/−2
- tests/configs/stack-multi.yaml +6/−6
- tests/projects/implicit-cabal/Main.hs +4/−4
- tests/projects/implicit-cabal/Setup.hs +2/−2
- tests/projects/implicit-cabal/cabal.project +1/−1
- tests/projects/implicit-cabal/implicit-cabal.cabal +25/−25
- tests/projects/implicit-stack-multi/Main.hs +4/−4
- tests/projects/implicit-stack-multi/Setup.hs +2/−2
- tests/projects/implicit-stack-multi/implicit-stack-multi.cabal +25/−25
- tests/projects/implicit-stack-multi/other-package/Main.hs +4/−4
- tests/projects/implicit-stack-multi/other-package/Setup.hs +2/−2
- tests/projects/implicit-stack-multi/other-package/other-package.cabal +26/−26
- tests/projects/implicit-stack/Main.hs +4/−4
- tests/projects/implicit-stack/Setup.hs +2/−2
- tests/projects/implicit-stack/implicit-stack.cabal +25/−25
- tests/projects/multi-cabal/Setup.hs +2/−2
- tests/projects/multi-cabal/app/Main.hs +3/−3
- tests/projects/multi-cabal/cabal.project +1/−1
- tests/projects/multi-cabal/hie.yaml +6/−6
- tests/projects/multi-cabal/multi-cabal.cabal +23/−23
- tests/projects/multi-cabal/src/Lib.hs +4/−4
- tests/projects/multi-direct/B.hs +3/−3
- tests/projects/multi-direct/hie.yaml +12/−12
- tests/projects/multi-stack/Setup.hs +2/−2
- tests/projects/multi-stack/app/Main.hs +3/−3
- tests/projects/multi-stack/cabal.project +1/−1
- tests/projects/multi-stack/hie.yaml +6/−6
- tests/projects/multi-stack/multi-stack.cabal +22/−22
- tests/projects/multi-stack/src/Lib.hs +4/−4
- tests/projects/simple-bios/B.hs +3/−3
- tests/projects/simple-bios/hie-bios.sh +3/−3
- tests/projects/simple-bios/hie.yaml +2/−2
- tests/projects/simple-cabal/A.hs +1/−1
- tests/projects/simple-cabal/B.hs +3/−3
- tests/projects/simple-cabal/CHANGELOG.md +5/−5
- tests/projects/simple-cabal/Setup.hs +2/−2
- tests/projects/simple-cabal/cabal.project +1/−1
- tests/projects/simple-cabal/hie.yaml +2/−2
- tests/projects/simple-cabal/simple-cabal.cabal +23/−23
- tests/projects/simple-direct/B.hs +3/−3
- tests/projects/simple-direct/hie.yaml +2/−2
- tests/projects/simple-stack/A.hs +1/−1
- tests/projects/simple-stack/B.hs +3/−3
- tests/projects/simple-stack/CHANGELOG.md +5/−5
- tests/projects/simple-stack/Setup.hs +2/−2
- tests/projects/simple-stack/cabal.project +1/−1
- tests/projects/simple-stack/hie.yaml +3/−3
- tests/projects/simple-stack/simple-stack.cabal +23/−23
- tests/projects/space stack/A.hs +4/−0
- tests/projects/space stack/B.hs +3/−0
- tests/projects/space stack/Setup.hs +2/−0
- tests/projects/space stack/hie.yaml +3/−0
- tests/projects/space stack/stackproj.cabal +15/−0
- wrappers/cabal +1/−1
- wrappers/cabal.hs +1/−3
ChangeLog view
@@ -1,3 +1,21 @@+2020-05-08 - 0.5.0++ * Add cabal.project.local to cabal cradle dependencies [#184](https://github.com/mpickering/hie-bios/pull/184)+ * Remove unused environment variables to simplify code. [#182](https://github.com/mpickering/hie-bios/pull/182)+ * Clean up hie-bios wrapper scripts after they are used. [#179](https://github.com/mpickering/hie-bios/pull/179)+ * Avoid error in windows due to temp file being locked. [#175](https://github.com/mpickering/hie-bios/pull/175)+ * Get building with ghc-8.10. [#173](https://github.com/mpickering/hie-bios/pull/173)+ * Add getCompilerOptionsWithLogger convenience function.+ * Add componentRoot to ComponentOptions. [#166](https://github.com/mpickering/hie-bios/pull/166)+ Options may be relative to the componentRoot.+ * Add makeDynFlagsAbsolute to fix mangling of ghc options starting with "-i". [#166](https://github.com/mpickering/hie-bios/pull/166)+ Breaks backwards-compatibility, because ComponentOptions now may contain+ filepaths relative to the component root directory.+ This function needs to be invoked on the parsed 'DynFlags' to normalise the filepaths.+ * Fix Ghci Script parses space in Filepath as Module (#162)+ * Correct path to .hie-bios example in readme (#159)+ * Relax upper bound for 'extra' (#161)+ 2020-01-29 - 0.4.0 * Return CompilerOptions in initialization (#130)
LICENSE view
@@ -1,29 +1,29 @@-Copyright (c) 2009, IIJ Innovation Institute Inc.-All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions-are met:-- * Redistributions of source code must retain the above copyright- notice, this list of conditions and the following disclaimer.- * Redistributions in binary form must reproduce the above copyright- notice, this list of conditions and the following disclaimer in- the documentation and/or other materials provided with the- distribution.- * Neither the name of the copyright holders nor the names of its- contributors may be used to endorse or promote products derived- from this software without specific prior written permission.--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS-FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE-COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT-LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN-ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE-POSSIBILITY OF SUCH DAMAGE.+Copyright (c) 2009, IIJ Innovation Institute Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE.
README.md view
@@ -285,7 +285,7 @@ should work for any file in your project. ```-ghci $(./hie-bios /path/to/foo.hs | tr '\n' ' ') /path/to/foo.hs+ghci $(./.hie-bios /path/to/foo.hs | tr '\n' ' ') /path/to/foo.hs ``` This is useful if you are designing a new build system or the other modes
Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple +main = defaultMain
exe/Main.hs view
@@ -86,6 +86,7 @@ ++ "\": " ++ show err CradleSuccess opts -> return $ unlines ["Options: " ++ show (componentOptions opts)+ ,"ComponentDir: " ++ (componentRoot opts) ,"Dependencies: " ++ show (componentDependencies opts) ] CradleNone -> return "No flags: this component should not be loaded" return (unlines res)
hie-bios.cabal view
@@ -1,6 +1,6 @@ Cabal-Version: 2.2 Name: hie-bios-Version: 0.4.0+Version: 0.5.0 Author: Matthew Pickering <matthewtpickering@gmail.com> Maintainer: Matthew Pickering <matthewtpickering@gmail.com> License: BSD-3-Clause@@ -70,6 +70,11 @@ tests/projects/simple-stack/cabal.project tests/projects/simple-stack/hie.yaml tests/projects/simple-stack/simple-stack.cabal+ "tests/projects/space stack/A.hs"+ "tests/projects/space stack/B.hs"+ "tests/projects/space stack/Setup.hs"+ "tests/projects/space stack/hie.yaml"+ "tests/projects/space stack/stackproj.cabal" tests/projects/implicit-cabal/Setup.hs tests/projects/implicit-cabal/cabal.project tests/projects/implicit-cabal/implicit-cabal.cabal@@ -85,7 +90,7 @@ tests/projects/implicit-stack-multi/other-package/Main.hs -tested-with: GHC==8.8.1, GHC==8.6.5, GHC==8.4.4+tested-with: GHC==8.10.1, GHC==8.8.3, GHC==8.6.5, GHC==8.4.4 Library Default-Language: Haskell2010@@ -119,9 +124,9 @@ directory >= 1.3.0 && < 1.4, filepath >= 1.4.1 && < 1.5, time >= 1.8.0 && < 1.10,- extra >= 1.6.14 && < 1.7,+ extra >= 1.6.14 && < 1.8, process >= 1.6.1 && < 1.7,- ghc >= 8.4.1 && < 8.9,+ ghc >= 8.4.1 && < 8.11, transformers >= 0.5.2 && < 0.6, temporary >= 1.2 && < 1.4, text >= 1.2.3 && < 1.3,
src/HIE/Bios.hs view
@@ -1,33 +1,33 @@-{- | The HIE Bios--Provides an abstraction over the GHC Api to initialise a GHC session and-loading modules in a project.--Defines the `hie.yaml` file specification. This is used to explicitly configure-how a project should be built by GHC.---}-module HIE.Bios (- -- * Find and load a Cradle- Cradle(..)- , CradleLoadResult(..)- , CradleError(..)- , findCradle- , loadCradle- , loadImplicitCradle- , defaultCradle- -- * Compiler Options- , ComponentOptions(..)- , getCompilerOptions- -- * Initialising a GHC session from a Cradle- , initSession- -- * Loading targets into a GHC session- , loadFile-- ) where--import HIE.Bios.Cradle-import HIE.Bios.Types-import HIE.Bios.Flags-import HIE.Bios.Environment+{- | The HIE Bios + +Provides an abstraction over the GHC Api to initialise a GHC session and +loading modules in a project. + +Defines the `hie.yaml` file specification. This is used to explicitly configure +how a project should be built by GHC. + +-} +module HIE.Bios ( + -- * Find and load a Cradle + Cradle(..) + , CradleLoadResult(..) + , CradleError(..) + , findCradle + , loadCradle + , loadImplicitCradle + , defaultCradle + -- * Compiler Options + , ComponentOptions(..) + , getCompilerOptions + -- * Initialising a GHC session from a Cradle + , initSession + -- * Loading targets into a GHC session + , loadFile + + ) where + +import HIE.Bios.Cradle +import HIE.Bios.Types +import HIE.Bios.Flags +import HIE.Bios.Environment import HIE.Bios.Ghc.Load
src/HIE/Bios/Config.hs view
@@ -1,189 +1,189 @@-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE DeriveFunctor #-}--- | Logic and datatypes for parsing @hie.yaml@ files.-module HIE.Bios.Config(- readConfig,- Config(..),- CradleConfig(..),- CradleType(..)- ) where--import qualified Data.Text as T-import qualified Data.Vector as V-import qualified Data.HashMap.Strict as Map-import Data.Foldable (foldrM)-import Data.Yaml---- | Configuration that can be used to load a 'Cradle'.--- A configuration has roughly the following form:------ @--- cradle:--- cabal:--- component: "lib:hie-bios"--- @-newtype Config a = Config { cradle :: CradleConfig a }- deriving (Show, Eq, Functor)--data CradleConfig a =- CradleConfig- { cradleDependencies :: [FilePath]- -- ^ Dependencies of a cradle.- -- Dependencies are expected to be relative to the root directory.- -- The given files are not required to exist.- , cradleType :: CradleType a- -- ^ Type of the cradle to use. Actions to obtain- -- compiler flags from are dependant on this field.- }- deriving (Show, Eq, Functor)--data CradleType a- = Cabal { component :: Maybe String }- | CabalMulti [ (FilePath, String) ]- | Stack { component :: Maybe String }- | StackMulti [ (FilePath, String) ]--- Bazel and Obelisk used to be supported but bit-rotted and no users have complained.--- They can be added back if a user--- | Bazel--- | Obelisk- | Bios- { prog :: FilePath- -- ^ Path to program that retrieves options to compile a file- , depsProg :: Maybe FilePath- -- ^ Optional Path to program to obtain cradle dependencies.- -- Each cradle dependency is to be expected to be on a separate line- -- and relative to the root dir of the cradle, not relative- -- to the location of this program.- }- | Direct { arguments :: [String] }- | None- | Multi [ (FilePath, CradleConfig a) ]- | Other { otherConfig :: a, originalYamlValue :: Value }- deriving (Eq, Functor)--instance FromJSON a => FromJSON (CradleType a) where- parseJSON (Object o) = parseCradleType o- parseJSON _ = fail "Not a known cradle type. Possible are: cabal, stack, bios, direct, default, none, multi"--instance Show (CradleType a) where- show (Cabal comp) = "Cabal {component = " ++ show comp ++ "}"- show (CabalMulti a) = "CabalMulti " ++ show a- show (Stack comp) = "Stack {component = " ++ show comp ++ "}"- show (StackMulti a) = "StackMulti " ++ show a- show Bios { prog, depsProg } = "Bios {prog = " ++ show prog ++ ", depsProg = " ++ show depsProg ++ "}"- show (Direct args) = "Direct {arguments = " ++ show args ++ "}"- show None = "None"- show (Multi a) = "Multi " ++ show a- show (Other _ val) = "Other {originalYamlValue = " ++ show val ++ "}"--parseCradleType :: FromJSON a => Object -> Parser (CradleType a)-parseCradleType o- | Just val <- Map.lookup "cabal" o = parseCabal val- | Just val <- Map.lookup "stack" o = parseStack val--- | Just _val <- Map.lookup "bazel" o = return Bazel--- | Just _val <- Map.lookup "obelisk" o = return Obelisk- | Just val <- Map.lookup "bios" o = parseBios val- | Just val <- Map.lookup "direct" o = parseDirect val- | Just _val <- Map.lookup "none" o = return None- | Just val <- Map.lookup "multi" o = parseMulti val- | Just val <- Map.lookup "other" o = Other <$> parseJSON val <*> pure val-parseCradleType o = fail $ "Unknown cradle type: " ++ show o--parseStackOrCabal- :: (Maybe String -> CradleType a)- -> ([(FilePath, String)] -> CradleType a)- -> Value- -> Parser (CradleType a)-parseStackOrCabal singleConstructor _ (Object x)- | Map.size x == 1, Just (String stackComponent) <- Map.lookup "component" x- = return $ singleConstructor $ Just $ T.unpack stackComponent- | Map.null x- = return $ singleConstructor Nothing- | otherwise- = fail "Not a valid Configuration type, following keys are allowed: component"-parseStackOrCabal _ multiConstructor (Array x) = do- let parseOne e- | Object v <- e- , Just (String prefix) <- Map.lookup "path" v- , Just (String comp) <- Map.lookup "component" v- , Map.size v == 2- = return (T.unpack prefix, T.unpack comp)- | otherwise- = fail "Expected an object with path and component keys"-- xs <- foldrM (\v cs -> (: cs) <$> parseOne v) [] x- return $ multiConstructor xs-parseStackOrCabal singleConstructor _ Null = return $ singleConstructor Nothing-parseStackOrCabal _ _ _ = fail "Configuration is expected to be an object."--parseStack :: Value -> Parser (CradleType a)-parseStack = parseStackOrCabal Stack StackMulti--parseCabal :: Value -> Parser (CradleType a)-parseCabal = parseStackOrCabal Cabal CabalMulti--parseBios :: Value -> Parser (CradleType a)-parseBios (Object x)- | 2 == Map.size x- , Just (String biosProgram) <- Map.lookup "program" x- , Just (String biosDepsProgram) <- Map.lookup "dependency-program" x- = return $ Bios (T.unpack biosProgram) (Just (T.unpack biosDepsProgram))-- | 1 == Map.size x- , Just (String biosProgram) <- Map.lookup "program" x- = return $ Bios (T.unpack biosProgram) Nothing-- | otherwise- = fail "Not a valid Bios Configuration type, following keys are allowed: program, dependency-program"-parseBios _ = fail "Bios Configuration is expected to be an object."--parseDirect :: Value -> Parser (CradleType a)-parseDirect (Object x)- | Map.size x == 1- , Just (Array v) <- Map.lookup "arguments" x- = return $ Direct [T.unpack s | String s <- V.toList v]-- | otherwise- = fail "Not a valid Direct Configuration type, following keys are allowed: arguments"-parseDirect _ = fail "Direct Configuration is expected to be an object."--parseMulti :: FromJSON a => Value -> Parser (CradleType a)-parseMulti (Array x)- = Multi <$> mapM parsePath (V.toList x)-parseMulti _ = fail "Multi Configuration is expected to be an array."--parsePath :: FromJSON a => Value -> Parser (FilePath, CradleConfig a)-parsePath (Object v)- | Just (String path) <- Map.lookup "path" v- , Just c <- Map.lookup "config" v- = (T.unpack path,) <$> parseJSON c-parsePath o = fail ("Multi component is expected to be an object." ++ show o)--instance FromJSON a => FromJSON (CradleConfig a) where- parseJSON (Object val) = do- crd <- val .: "cradle"- crdDeps <- case Map.size val of- 1 -> return []- 2 -> val .: "dependencies"- _ -> fail "Unknown key, following keys are allowed: cradle, dependencies"-- return $ CradleConfig { cradleType = crd- , cradleDependencies = crdDeps- }-- parseJSON _ = fail "Expected a cradle: key containing the preferences, possible values: cradle, dependencies"---instance FromJSON a => FromJSON (Config a) where- parseJSON o = Config <$> parseJSON o----- | Decode given file to a 'Config a' value.--- Type variable 'a' can be used to extend the 'hie.yaml' file format--- to extend configuration in the user-library.--- If the contents of the file is not a valid 'Config a',--- an 'Control.Exception.IOException' is thrown.-readConfig :: FromJSON a => FilePath -> IO (Config a)+{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} +{-# LANGUAGE DeriveFunctor #-} +-- | Logic and datatypes for parsing @hie.yaml@ files. +module HIE.Bios.Config( + readConfig, + Config(..), + CradleConfig(..), + CradleType(..) + ) where + +import qualified Data.Text as T +import qualified Data.Vector as V +import qualified Data.HashMap.Strict as Map +import Data.Foldable (foldrM) +import Data.Yaml + +-- | Configuration that can be used to load a 'Cradle'. +-- A configuration has roughly the following form: +-- +-- @ +-- cradle: +-- cabal: +-- component: "lib:hie-bios" +-- @ +newtype Config a = Config { cradle :: CradleConfig a } + deriving (Show, Eq, Functor) + +data CradleConfig a = + CradleConfig + { cradleDependencies :: [FilePath] + -- ^ Dependencies of a cradle. + -- Dependencies are expected to be relative to the root directory. + -- The given files are not required to exist. + , cradleType :: CradleType a + -- ^ Type of the cradle to use. Actions to obtain + -- compiler flags from are dependant on this field. + } + deriving (Show, Eq, Functor) + +data CradleType a + = Cabal { component :: Maybe String } + | CabalMulti [ (FilePath, String) ] + | Stack { component :: Maybe String } + | StackMulti [ (FilePath, String) ] +-- Bazel and Obelisk used to be supported but bit-rotted and no users have complained. +-- They can be added back if a user +-- | Bazel +-- | Obelisk + | Bios + { prog :: FilePath + -- ^ Path to program that retrieves options to compile a file + , depsProg :: Maybe FilePath + -- ^ Optional Path to program to obtain cradle dependencies. + -- Each cradle dependency is to be expected to be on a separate line + -- and relative to the root dir of the cradle, not relative + -- to the location of this program. + } + | Direct { arguments :: [String] } + | None + | Multi [ (FilePath, CradleConfig a) ] + | Other { otherConfig :: a, originalYamlValue :: Value } + deriving (Eq, Functor) + +instance FromJSON a => FromJSON (CradleType a) where + parseJSON (Object o) = parseCradleType o + parseJSON _ = fail "Not a known cradle type. Possible are: cabal, stack, bios, direct, default, none, multi" + +instance Show (CradleType a) where + show (Cabal comp) = "Cabal {component = " ++ show comp ++ "}" + show (CabalMulti a) = "CabalMulti " ++ show a + show (Stack comp) = "Stack {component = " ++ show comp ++ "}" + show (StackMulti a) = "StackMulti " ++ show a + show Bios { prog, depsProg } = "Bios {prog = " ++ show prog ++ ", depsProg = " ++ show depsProg ++ "}" + show (Direct args) = "Direct {arguments = " ++ show args ++ "}" + show None = "None" + show (Multi a) = "Multi " ++ show a + show (Other _ val) = "Other {originalYamlValue = " ++ show val ++ "}" + +parseCradleType :: FromJSON a => Object -> Parser (CradleType a) +parseCradleType o + | Just val <- Map.lookup "cabal" o = parseCabal val + | Just val <- Map.lookup "stack" o = parseStack val +-- | Just _val <- Map.lookup "bazel" o = return Bazel +-- | Just _val <- Map.lookup "obelisk" o = return Obelisk + | Just val <- Map.lookup "bios" o = parseBios val + | Just val <- Map.lookup "direct" o = parseDirect val + | Just _val <- Map.lookup "none" o = return None + | Just val <- Map.lookup "multi" o = parseMulti val + | Just val <- Map.lookup "other" o = Other <$> parseJSON val <*> pure val +parseCradleType o = fail $ "Unknown cradle type: " ++ show o + +parseStackOrCabal + :: (Maybe String -> CradleType a) + -> ([(FilePath, String)] -> CradleType a) + -> Value + -> Parser (CradleType a) +parseStackOrCabal singleConstructor _ (Object x) + | Map.size x == 1, Just (String stackComponent) <- Map.lookup "component" x + = return $ singleConstructor $ Just $ T.unpack stackComponent + | Map.null x + = return $ singleConstructor Nothing + | otherwise + = fail "Not a valid Configuration type, following keys are allowed: component" +parseStackOrCabal _ multiConstructor (Array x) = do + let parseOne e + | Object v <- e + , Just (String prefix) <- Map.lookup "path" v + , Just (String comp) <- Map.lookup "component" v + , Map.size v == 2 + = return (T.unpack prefix, T.unpack comp) + | otherwise + = fail "Expected an object with path and component keys" + + xs <- foldrM (\v cs -> (: cs) <$> parseOne v) [] x + return $ multiConstructor xs +parseStackOrCabal singleConstructor _ Null = return $ singleConstructor Nothing +parseStackOrCabal _ _ _ = fail "Configuration is expected to be an object." + +parseStack :: Value -> Parser (CradleType a) +parseStack = parseStackOrCabal Stack StackMulti + +parseCabal :: Value -> Parser (CradleType a) +parseCabal = parseStackOrCabal Cabal CabalMulti + +parseBios :: Value -> Parser (CradleType a) +parseBios (Object x) + | 2 == Map.size x + , Just (String biosProgram) <- Map.lookup "program" x + , Just (String biosDepsProgram) <- Map.lookup "dependency-program" x + = return $ Bios (T.unpack biosProgram) (Just (T.unpack biosDepsProgram)) + + | 1 == Map.size x + , Just (String biosProgram) <- Map.lookup "program" x + = return $ Bios (T.unpack biosProgram) Nothing + + | otherwise + = fail "Not a valid Bios Configuration type, following keys are allowed: program, dependency-program" +parseBios _ = fail "Bios Configuration is expected to be an object." + +parseDirect :: Value -> Parser (CradleType a) +parseDirect (Object x) + | Map.size x == 1 + , Just (Array v) <- Map.lookup "arguments" x + = return $ Direct [T.unpack s | String s <- V.toList v] + + | otherwise + = fail "Not a valid Direct Configuration type, following keys are allowed: arguments" +parseDirect _ = fail "Direct Configuration is expected to be an object." + +parseMulti :: FromJSON a => Value -> Parser (CradleType a) +parseMulti (Array x) + = Multi <$> mapM parsePath (V.toList x) +parseMulti _ = fail "Multi Configuration is expected to be an array." + +parsePath :: FromJSON a => Value -> Parser (FilePath, CradleConfig a) +parsePath (Object v) + | Just (String path) <- Map.lookup "path" v + , Just c <- Map.lookup "config" v + = (T.unpack path,) <$> parseJSON c +parsePath o = fail ("Multi component is expected to be an object." ++ show o) + +instance FromJSON a => FromJSON (CradleConfig a) where + parseJSON (Object val) = do + crd <- val .: "cradle" + crdDeps <- case Map.size val of + 1 -> return [] + 2 -> val .: "dependencies" + _ -> fail "Unknown key, following keys are allowed: cradle, dependencies" + + return $ CradleConfig { cradleType = crd + , cradleDependencies = crdDeps + } + + parseJSON _ = fail "Expected a cradle: key containing the preferences, possible values: cradle, dependencies" + + +instance FromJSON a => FromJSON (Config a) where + parseJSON o = Config <$> parseJSON o + + +-- | Decode given file to a 'Config a' value. +-- Type variable 'a' can be used to extend the 'hie.yaml' file format +-- to extend configuration in the user-library. +-- If the contents of the file is not a valid 'Config a', +-- an 'Control.Exception.IOException' is thrown. +readConfig :: FromJSON a => FilePath -> IO (Config a) readConfig = decodeFileThrow
src/HIE/Bios/Cradle.hs view
@@ -115,7 +115,7 @@ addActionDeps :: CradleAction a -> CradleAction a addActionDeps ca = ca { runCradle = \l fp ->- (fmap (\(ComponentOptions os' ds) -> ComponentOptions os' (ds `union` deps)))+ (fmap (\(ComponentOptions os' dir ds) -> ComponentOptions os' dir (ds `union` deps))) <$> runCradle ca l fp } implicitConfig :: FilePath -> MaybeT IO (CradleConfig a, FilePath)@@ -200,7 +200,7 @@ { cradleRootDir = cur_dir , cradleOptsProg = CradleAction { actionName = Types.Default- , runCradle = \_ _ -> return (CradleSuccess (ComponentOptions [] []))+ , runCradle = \_ _ -> return (CradleSuccess (ComponentOptions [] cur_dir [])) } } @@ -298,7 +298,7 @@ { cradleRootDir = wdir , cradleOptsProg = CradleAction { actionName = Types.Direct- , runCradle = \_ _ -> return (CradleSuccess (ComponentOptions args []))+ , runCradle = \_ _ -> return (CradleSuccess (ComponentOptions args wdir [])) } } @@ -323,7 +323,7 @@ biosDepsAction :: LoggingFunction -> FilePath -> Maybe FilePath -> IO [FilePath] biosDepsAction l wdir (Just biosDepsProg) = do biosDeps' <- canonicalizePath biosDepsProg- (ex, sout, serr, args) <- readProcessWithOutputFile l Nothing wdir biosDeps' []+ (ex, sout, serr, args) <- readProcessWithOutputFile l wdir biosDeps' [] case ex of ExitFailure _ -> error $ show (ex, sout, serr) ExitSuccess -> return args@@ -337,13 +337,13 @@ -> IO (CradleLoadResult ComponentOptions) biosAction wdir bios bios_deps l fp = do bios' <- canonicalizePath bios- (ex, _stdo, std, res) <- readProcessWithOutputFile l Nothing wdir bios' [fp]+ (ex, _stdo, std, res) <- readProcessWithOutputFile l wdir bios' [fp] deps <- biosDepsAction l wdir bios_deps -- Output from the program should be written to the output file and -- delimited by newlines. -- Execute the bios action and add dependencies of the cradle. -- Removes all duplicates.- return $ makeCradleResult (ex, std, res) deps+ return $ makeCradleResult (ex, std, wdir, res) deps ------------------------------------------------------------------------ -- Cabal Cradle@@ -362,7 +362,7 @@ cabalCradleDependencies :: FilePath -> IO [FilePath] cabalCradleDependencies rootDir = do cabalFiles <- findCabalFiles rootDir- return $ cabalFiles ++ ["cabal.project"]+ return $ cabalFiles ++ ["cabal.project", "cabal.project.local"] findCabalFiles :: FilePath -> IO [FilePath] findCabalFiles wdir = do@@ -370,7 +370,7 @@ return $ filter ((== ".cabal") . takeExtension) dirContent -processCabalWrapperArgs :: [String] -> Maybe [String]+processCabalWrapperArgs :: [String] -> Maybe (FilePath, [String]) processCabalWrapperArgs args = case args of (dir: ghc_args) ->@@ -378,9 +378,8 @@ removeVerbosityOpts $ removeRTS $ removeInteractive- $ map (fixImportDirs dir) $ ghc_args- in Just final_args+ in Just (dir, final_args) _ -> Nothing -- | GHC process information.@@ -391,45 +390,50 @@ -- generate a fake GHC that can be passed to cabal -- when run with --interactive, it will print out its -- command-line arguments and exit-getCabalWrapperTool :: GhcProc -> FilePath -> IO FilePath-getCabalWrapperTool (ghcPath, ghcArgs) wdir = do- wrapper_fp <-- if isWindows- then do- cacheDir <- getCacheDir ""- let srcHash = show (fingerprintString cabalWrapperHs)- let wrapper_name = "wrapper-" ++ srcHash- let wrapper_fp = cacheDir </> wrapper_name <.> "exe"- exists <- doesFileExist wrapper_fp- unless exists $ withSystemTempDirectory "hie-bios" $ \ tmpDir -> do- createDirectoryIfMissing True cacheDir- let wrapper_hs = cacheDir </> wrapper_name <.> "hs"- writeFile wrapper_hs cabalWrapperHs- let ghc = (proc ghcPath $- ghcArgs ++ ["-rtsopts=ignore", "-outputdir", tmpDir, "-o", wrapper_fp, wrapper_hs])- { cwd = Just wdir }- readCreateProcess ghc "" >>= putStr- return wrapper_fp- else writeSystemTempFile "bios-wrapper" cabalWrapper+withCabalWrapperTool :: GhcProc -> FilePath -> (FilePath -> IO a) -> IO a+withCabalWrapperTool (ghcPath, ghcArgs) wdir k = do+ if isWindows+ then do+ cacheDir <- getCacheDir ""+ let srcHash = show (fingerprintString cabalWrapperHs)+ let wrapper_name = "wrapper-" ++ srcHash+ let wrapper_fp = cacheDir </> wrapper_name <.> "exe"+ exists <- doesFileExist wrapper_fp+ unless exists $ withSystemTempDirectory "hie-bios" $ \ tmpDir -> do+ createDirectoryIfMissing True cacheDir+ let wrapper_hs = cacheDir </> wrapper_name <.> "hs"+ writeFile wrapper_hs cabalWrapperHs+ let ghc = (proc ghcPath $+ ghcArgs ++ ["-rtsopts=ignore", "-outputdir", tmpDir, "-o", wrapper_fp, wrapper_hs])+ { cwd = Just wdir }+ readCreateProcess ghc "" >>= putStr+ setMode wrapper_fp+ k wrapper_fp+ else withSystemTempFile "bios-wrapper"+ (\loc h -> do+ hPutStr h cabalWrapper+ hClose h+ setMode loc+ k loc) - setFileMode wrapper_fp accessModes- _check <- readFile wrapper_fp- return wrapper_fp+ where+ setMode wrapper_fp = setFileMode wrapper_fp accessModes + cabalAction :: FilePath -> Maybe String -> LoggingFunction -> FilePath -> IO (CradleLoadResult ComponentOptions) cabalAction work_dir mc l fp = do- wrapper_fp <- getCabalWrapperTool ("ghc", []) work_dir- let cab_args = ["v2-repl", "--with-compiler", wrapper_fp, fromMaybe (fixTargetPath fp) mc]- (ex, output, stde, args) <-- readProcessWithOutputFile l Nothing work_dir "cabal" cab_args- deps <- cabalCradleDependencies work_dir- case processCabalWrapperArgs args of- Nothing -> pure $ CradleFail (CradleError ex- ["Failed to parse result of calling cabal"- , unlines output- , unlines stde- , unlines args])- Just final_args -> pure $ makeCradleResult (ex, stde, final_args) deps+ withCabalWrapperTool ("ghc", []) work_dir $ \wrapper_fp -> do+ let cab_args = ["v2-repl", "--with-compiler", wrapper_fp, fromMaybe (fixTargetPath fp) mc]+ (ex, output, stde, args) <-+ readProcessWithOutputFile l work_dir "cabal" cab_args+ deps <- cabalCradleDependencies work_dir+ case processCabalWrapperArgs args of+ Nothing -> pure $ CradleFail (CradleError ex+ ["Failed to parse result of calling cabal"+ , unlines output+ , unlines stde+ , unlines args])+ Just (componentDir, final_args) -> pure $ makeCradleResult (ex, stde, componentDir, final_args) deps where -- Need to make relative on Windows, due to a Cabal bug with how it -- parses file targets with a C: drive in it@@ -452,15 +456,7 @@ removeVerbosityOpts :: [String] -> [String] removeVerbosityOpts = filter ((&&) <$> (/= "-v0") <*> (/= "-w")) -fixImportDirs :: FilePath -> String -> String-fixImportDirs base_dir arg =- if "-i" `isPrefixOf` arg- then let dir = drop 2 arg- in if not (null dir) && isRelative dir then "-i" ++ base_dir </> dir- else arg- else arg - cabalWorkDir :: FilePath -> MaybeT IO FilePath cabalWorkDir = findFileUpwards isCabal where@@ -489,25 +485,24 @@ stackAction work_dir mc l _fp = do let ghcProcArgs = ("stack", ["exec", "ghc", "--"]) -- Same wrapper works as with cabal- wrapper_fp <- getCabalWrapperTool ghcProcArgs work_dir-- (ex1, _stdo, stde, args) <-- readProcessWithOutputFile l Nothing work_dir- "stack" $ ["repl", "--no-nix-pure", "--with-ghc", wrapper_fp]- ++ [ comp | Just comp <- [mc] ]- (ex2, pkg_args, stdr, _) <-- readProcessWithOutputFile l Nothing work_dir "stack" ["path", "--ghc-package-path"]- let split_pkgs = concatMap splitSearchPath pkg_args- pkg_ghc_args = concatMap (\p -> ["-package-db", p] ) split_pkgs- deps <- stackCradleDependencies work_dir- return $ case processCabalWrapperArgs args of- Nothing -> CradleFail (CradleError ex1 $- ("Failed to parse result of calling stack":- stde)- ++ args)+ withCabalWrapperTool ghcProcArgs work_dir $ \wrapper_fp -> do+ (ex1, _stdo, stde, args) <-+ readProcessWithOutputFile l work_dir+ "stack" $ ["repl", "--no-nix-pure", "--with-ghc", wrapper_fp]+ ++ [ comp | Just comp <- [mc] ]+ (ex2, pkg_args, stdr, _) <-+ readProcessWithOutputFile l work_dir "stack" ["path", "--ghc-package-path"]+ let split_pkgs = concatMap splitSearchPath pkg_args+ pkg_ghc_args = concatMap (\p -> ["-package-db", p] ) split_pkgs+ deps <- stackCradleDependencies work_dir+ return $ case processCabalWrapperArgs args of+ Nothing -> CradleFail (CradleError ex1 $+ ("Failed to parse result of calling stack":+ stde)+ ++ args) - Just ghc_args ->- makeCradleResult (combineExitCodes [ex1, ex2], stde ++ stdr, ghc_args ++ pkg_ghc_args) deps+ Just (componentDir, ghc_args) ->+ makeCradleResult (combineExitCodes [ex1, ex2], stde ++ stdr, componentDir, ghc_args ++ pkg_ghc_args) deps combineExitCodes :: [ExitCode] -> ExitCode combineExitCodes = foldr go ExitSuccess@@ -639,48 +634,49 @@ -- Additionally, arguments to ghc are supplied via @HIE_BIOS_GHC_ARGS@ readProcessWithOutputFile :: LoggingFunction -- ^ Output of the process is streamed into this function.- -> Maybe GhcProc -- ^ Optional FilePath to GHC and arguments that should- -- be passed to ghc.- -- In the process to call, filepath and arguments -> FilePath -- ^ Working directory. Process is executed in this directory. -> FilePath -- ^ Process to call. -> [String] -- ^ Arguments to the process. -> IO (ExitCode, [String], [String], [String])-readProcessWithOutputFile l ghcProc work_dir fp args =- withSystemTempFile "bios-output" $ \output_file h -> do- hSetBuffering h LineBuffering- old_env <- getEnvironment- let (ghcPath, ghcArgs) = case ghcProc of- Just (p, a) -> (p, unwords a)- Nothing ->- ( fromMaybe "ghc" (lookup hieBiosGhc old_env)- , fromMaybe "" (lookup hieBiosGhcArgs old_env)- )+readProcessWithOutputFile l work_dir fp args = do+ old_env <- getEnvironment++ withHieBiosOutput old_env $ \output_file -> do -- Pipe stdout directly into the logger let process = (readProcessInDirectory work_dir fp args) { env = Just- $ (hieBiosGhc, ghcPath)- : (hieBiosGhcArgs, ghcArgs)- : ("HIE_BIOS_OUTPUT", output_file)+ $ (hieBiosOutput, output_file) : old_env }- -- Windows line endings are not converted so you have to filter out `'r` characters- loggingConduit = (C.decodeUtf8 C..| C.lines C..| C.filterE (/= '\r') C..| C.map T.unpack C..| C.iterM l C..| C.sinkList)++ -- Windows line endings are not converted so you have to filter out `'r` characters+ let loggingConduit = (C.decodeUtf8 C..| C.lines C..| C.filterE (/= '\r') C..| C.map T.unpack C..| C.iterM l C..| C.sinkList) (ex, stdo, stde) <- sourceProcessWithStreams process mempty loggingConduit loggingConduit- !res <- force <$> hGetContents h+ res <- withFile output_file ReadMode $ \handle -> do+ hSetBuffering handle LineBuffering+ !res <- force <$> hGetContents handle+ return res+ return (ex, stdo, stde, lines (filter (/= '\r') res)) where- hieBiosGhc = "HIE_BIOS_GHC"- hieBiosGhcArgs = "HIE_BIOS_GHC_ARGS"+ withHieBiosOutput :: [(String,String)] -> (FilePath -> IO a) -> IO a+ withHieBiosOutput env action = do+ let mbHieBiosOut = lookup hieBiosOutput env+ case mbHieBiosOut of+ Just file@(_:_) -> action file+ _ -> withSystemTempFile "hie-bios" $+ \ file h -> hClose h >> action file + hieBiosOutput = "HIE_BIOS_OUTPUT"+ readProcessInDirectory :: FilePath -> FilePath -> [String] -> CreateProcess readProcessInDirectory wdir p args = (proc p args) { cwd = Just wdir } -makeCradleResult :: (ExitCode, [String], [String]) -> [FilePath] -> CradleLoadResult ComponentOptions-makeCradleResult (ex, err, gopts) deps =+makeCradleResult :: (ExitCode, [String], FilePath, [String]) -> [FilePath] -> CradleLoadResult ComponentOptions+makeCradleResult (ex, err, componentDir, gopts) deps = case ex of ExitFailure _ -> CradleFail (CradleError ex err) _ ->- let compOpts = ComponentOptions gopts deps+ let compOpts = ComponentOptions gopts componentDir deps in CradleSuccess compOpts
src/HIE/Bios/Environment.hs view
@@ -1,5 +1,5 @@ {-# LANGUAGE RecordWildCards, CPP #-}-module HIE.Bios.Environment (initSession, getSystemLibDir, getCacheDir, addCmdOpts) where+module HIE.Bios.Environment (initSession, getSystemLibDir, makeDynFlagsAbsolute, getCacheDir, addCmdOpts) where import CoreMonad (liftIO) import GHC (DynFlags(..), GhcLink(..), HscTarget(..), GhcMonad)@@ -19,7 +19,9 @@ import qualified Data.ByteString.Char8 as B import Data.ByteString.Base16 import Data.List-+import Data.Char (isSpace)+import Control.Applicative ((<|>))+import Text.ParserCombinators.ReadP import HIE.Bios.Types import HIE.Bios.Ghc.Gap @@ -37,12 +39,13 @@ cache_dir <- liftIO $ getCacheDir opts_hash -- Add the user specified options to a fresh GHC session. (df', targets) <- addCmdOpts componentOptions df+ let df'' = makeDynFlagsAbsolute componentRoot df' void $ G.setSessionDynFlags (disableOptimisation -- Compile with -O0 as we are not going to produce object files. $ setIgnoreInterfacePragmas -- Ignore any non-essential information in interface files such as unfoldings changing. $ writeInterfaceFiles (Just cache_dir) -- Write interface files to the cache $ setVerbosity 0 -- Set verbosity to zero just in case the user specified `-vx` in the options.- $ setLinkerOptions df' -- Set `-fno-code` to avoid generating object files, unless we have to.+ $ setLinkerOptions df'' -- Set `-fno-code` to avoid generating object files, unless we have to. ) -- Unset the default log action to avoid output going to stdout. unsetLogAction@@ -78,7 +81,7 @@ -} -- | Prepends the cache directory used by the library to the supplied file path.--- It tries to use the path under the environment variable `$HIE_BIOS_CACHE_DIR` +-- It tries to use the path under the environment variable `$HIE_BIOS_CACHE_DIR` -- and falls back to the standard `$XDG_CACHE_HOME/hie-bios` if the former is not set getCacheDir :: FilePath -> IO FilePath getCacheDir fp = do@@ -121,7 +124,6 @@ => [String] -> DynFlags -> m (DynFlags, [G.Target]) addCmdOpts cmdOpts df1 = do (df2, leftovers', _warns) <- G.parseDynamicFlags df1 (map G.noLoc cmdOpts)- -- parse targets from ghci-scripts. Only extract targets that have been ":add"'ed. additionalTargets <- concat <$> mapM (liftIO . getTargetsFromGhciScript) (ghciScripts df2) @@ -165,6 +167,19 @@ liftIO $ hPutStrLn stderr "cannot set package flags with :seti; use :set" -} +-- | Make filepaths in the given 'DynFlags' absolute.+-- This makes the 'DynFlags' independent of the current working directory.+makeDynFlagsAbsolute :: FilePath -> DynFlags -> DynFlags+makeDynFlagsAbsolute work_dir df =+ mapOverIncludePaths (prependIfNotAbsolute work_dir)+ $ df+ { importPaths = map (prependIfNotAbsolute work_dir) (importPaths df)+ }+ where+ prependIfNotAbsolute wdir f+ | isAbsolute f = f+ | otherwise = wdir </> f+ -- partition_args, along with some of the other code in this file, -- was copied from ghc/Main.hs -- -----------------------------------------------------------------------------@@ -223,7 +238,42 @@ getTargetsFromGhciScript :: FilePath -> IO [String] getTargetsFromGhciScript script = do contents <- lines <$> readFile script- return- $ concatMap (tail {- first element is ":add" which we want to remove -}- . words)- $ filter (":add" `isPrefixOf`) contents+ let parseGhciLine = concatMap fst . filter (null . snd) . readP_to_S parser+ return $ concatMap parseGhciLine contents++-- |This parser aims to parse targets and double-quoted filepaths that are separated by spaces+-- and prefixed with the literal ":add"+--+-- >>> filter (null . snd) $ readP_to_S parser ":add Lib Lib2"+-- [(["Lib","Lib2"],"")]+--+-- >>> filter (null . snd) $ readP_to_S parser ":add Lib Lib2 \"Test Example.hs\""+-- [(["Lib","Lib2","Test Example.hs"],"")]+--+-- >>> filter (null . snd) $ readP_to_S parser ":add Lib Lib2 \"Test Exa\\\"mple.hs\""+-- [(["Lib","Lib2","Test Exa\"mple.hs"],"")]+parser :: ReadP [String]+parser = do+ _ <- string ":add" <* space1+ scriptword `sepBy` space1++space1 :: ReadP [Char]+space1 = many1 (char ' ')++scriptword :: ReadP String+scriptword = quoted <++ value++-- | A balanced double-quoted string+quoted :: ReadP String+quoted = do+ _ <- char '"'+ manyTill (escaped '"' <|> anyToken) $ char '"'++escaped :: Char -> ReadP Char+escaped c = c <$ string ("\\" <> [c])++value :: ReadP String+value = many1 (satisfy (not . isSpace))++anyToken :: ReadP Char+anyToken = satisfy $ const True
src/HIE/Bios/Flags.hs view
@@ -1,17 +1,25 @@-module HIE.Bios.Flags (getCompilerOptions) where--import HIE.Bios.Types-import HIE.Bios.Internal.Log----- | Initialize the 'DynFlags' relating to the compilation of a single--- file or GHC session according to the provided 'Cradle'.-getCompilerOptions ::- FilePath -- The file we are loading it because of- -> Cradle a- -> IO (CradleLoadResult ComponentOptions)-getCompilerOptions fp cradle =- runCradle (cradleOptsProg cradle) logm fp-------------------------------------------------------------------+module HIE.Bios.Flags (getCompilerOptions, getCompilerOptionsWithLogger, LoggingFunction) where + +import HIE.Bios.Types +import HIE.Bios.Internal.Log + + +-- | Initialize the 'DynFlags' relating to the compilation of a single +-- file or GHC session according to the provided 'Cradle'. +getCompilerOptions :: + FilePath -- The file we are loading it because of + -> Cradle a + -> IO (CradleLoadResult ComponentOptions) +getCompilerOptions = + getCompilerOptionsWithLogger logm + +getCompilerOptionsWithLogger :: + LoggingFunction + -> FilePath + -> Cradle a + -> IO (CradleLoadResult ComponentOptions) +getCompilerOptionsWithLogger l fp cradle = + runCradle (cradleOptsProg cradle) l fp + + +----------------------------------------------------------------
src/HIE/Bios/Ghc/Api.hs view
@@ -1,108 +1,108 @@-{-# LANGUAGE ScopedTypeVariables, CPP #-}--- | These functions are for conveniently implementing the simple CLI-module HIE.Bios.Ghc.Api (- initializeFlagsWithCradle- , initializeFlagsWithCradleWithMessage- , G.SuccessFlag(..)- -- * Utility functions for running the GHC monad and implementing internal utilities- , withGHC- , withGHC'- , withGhcT- , getSystemLibDir- , withDynFlags- ) where--import CoreMonad (liftIO)-import Exception (ghandle, SomeException(..), ExceptionMonad(..))-import GHC (Ghc, LoadHowMuch(..), GhcMonad, GhcT)-import DynFlags--import qualified GHC as G-import qualified MonadUtils as G-import qualified HscMain as G-import qualified GhcMake as G--import Control.Monad (void)-import System.Exit (exitSuccess)-import HIE.Bios.Types-import qualified HIE.Bios.Internal.Log as Log-import HIE.Bios.Environment-import HIE.Bios.Flags---------------------------------------------------------------------- | Converting the 'Ghc' monad to the 'IO' monad. All exceptions are ignored and logged.-withGHC :: FilePath -- ^ A target file displayed in an error message.- -> Ghc a -- ^ 'Ghc' actions created by the Ghc utilities.- -> IO a-withGHC file body = ghandle ignore $ withGHC' body- where- ignore :: SomeException -> IO a- ignore e = do- Log.logm $ file ++ ":0:0:Error:"- Log.logm (show e)- exitSuccess---- | Run a Ghc monad computation with an automatically discovered libdir.--- It calculates the lib dir by calling ghc with the `--print-libdir` flag.-withGHC' :: Ghc a -> IO a-withGHC' body = do- -- TODO: Why is this not using ghc-paths?- mlibdir <- getSystemLibDir- G.runGhc mlibdir body--withGhcT :: (Exception.ExceptionMonad m, G.MonadIO m, Monad m) => GhcT m a -> m a-withGhcT body = do- mlibdir <- G.liftIO $ getSystemLibDir- G.runGhcT mlibdir body---------------------------------------------------------------------- | Initialize a GHC session by loading a given file into a given cradle.-initializeFlagsWithCradle ::- GhcMonad m- => FilePath -- ^ The file we are loading the 'Cradle' because of- -> Cradle a -- ^ The cradle we want to load- -> m (CradleLoadResult (m G.SuccessFlag, ComponentOptions))-initializeFlagsWithCradle = initializeFlagsWithCradleWithMessage (Just G.batchMsg)---- | The same as 'initializeFlagsWithCradle' but with an additional argument to control--- how the loading progress messages are displayed to the user. In @haskell-ide-engine@--- the module loading progress is displayed in the UI by using a progress notification.-initializeFlagsWithCradleWithMessage ::- GhcMonad m- => Maybe G.Messager- -> FilePath -- ^ The file we are loading the 'Cradle' because of- -> Cradle a -- ^ The cradle we want to load- -> m (CradleLoadResult (m G.SuccessFlag, ComponentOptions)) -- ^ Whether we actually loaded the cradle or not.-initializeFlagsWithCradleWithMessage msg fp cradle =- fmap (initSessionWithMessage msg) <$> liftIO (getCompilerOptions fp cradle)---- | Actually perform the initialisation of the session. Initialising the session corresponds to--- parsing the command line flags, setting the targets for the session and then attempting to load--- all the targets.-initSessionWithMessage :: (GhcMonad m)- => Maybe G.Messager- -> ComponentOptions- -> (m G.SuccessFlag, ComponentOptions)-initSessionWithMessage msg compOpts = (do- targets <- initSession compOpts- G.setTargets targets- -- Get the module graph using the function `getModuleGraph`- mod_graph <- G.depanal [] True- G.load' LoadAllTargets msg mod_graph, compOpts)--------------------------------------------------------------------withDynFlags ::- (GhcMonad m)- => (DynFlags -> DynFlags) -> m a -> m a-withDynFlags setFlag body = G.gbracket setup teardown (\_ -> body)- where- setup = do- dflag <- G.getSessionDynFlags- void $ G.setSessionDynFlags (setFlag dflag)- return dflag- teardown = void . G.setSessionDynFlags------------------------------------------------------------------+{-# LANGUAGE ScopedTypeVariables, CPP #-} +-- | These functions are for conveniently implementing the simple CLI +module HIE.Bios.Ghc.Api ( + initializeFlagsWithCradle + , initializeFlagsWithCradleWithMessage + , G.SuccessFlag(..) + -- * Utility functions for running the GHC monad and implementing internal utilities + , withGHC + , withGHC' + , withGhcT + , getSystemLibDir + , withDynFlags + ) where + +import CoreMonad (liftIO) +import Exception (ghandle, SomeException(..), ExceptionMonad(..)) +import GHC (Ghc, LoadHowMuch(..), GhcMonad, GhcT) +import DynFlags + +import qualified GHC as G +import qualified MonadUtils as G +import qualified HscMain as G +import qualified GhcMake as G + +import Control.Monad (void) +import System.Exit (exitSuccess) +import HIE.Bios.Types +import qualified HIE.Bios.Internal.Log as Log +import HIE.Bios.Environment +import HIE.Bios.Flags + +---------------------------------------------------------------- + +-- | Converting the 'Ghc' monad to the 'IO' monad. All exceptions are ignored and logged. +withGHC :: FilePath -- ^ A target file displayed in an error message. + -> Ghc a -- ^ 'Ghc' actions created by the Ghc utilities. + -> IO a +withGHC file body = ghandle ignore $ withGHC' body + where + ignore :: SomeException -> IO a + ignore e = do + Log.logm $ file ++ ":0:0:Error:" + Log.logm (show e) + exitSuccess + +-- | Run a Ghc monad computation with an automatically discovered libdir. +-- It calculates the lib dir by calling ghc with the `--print-libdir` flag. +withGHC' :: Ghc a -> IO a +withGHC' body = do + -- TODO: Why is this not using ghc-paths? + mlibdir <- getSystemLibDir + G.runGhc mlibdir body + +withGhcT :: (Exception.ExceptionMonad m, G.MonadIO m, Monad m) => GhcT m a -> m a +withGhcT body = do + mlibdir <- G.liftIO $ getSystemLibDir + G.runGhcT mlibdir body + +---------------------------------------------------------------- + +-- | Initialize a GHC session by loading a given file into a given cradle. +initializeFlagsWithCradle :: + GhcMonad m + => FilePath -- ^ The file we are loading the 'Cradle' because of + -> Cradle a -- ^ The cradle we want to load + -> m (CradleLoadResult (m G.SuccessFlag, ComponentOptions)) +initializeFlagsWithCradle = initializeFlagsWithCradleWithMessage (Just G.batchMsg) + +-- | The same as 'initializeFlagsWithCradle' but with an additional argument to control +-- how the loading progress messages are displayed to the user. In @haskell-ide-engine@ +-- the module loading progress is displayed in the UI by using a progress notification. +initializeFlagsWithCradleWithMessage :: + GhcMonad m + => Maybe G.Messager + -> FilePath -- ^ The file we are loading the 'Cradle' because of + -> Cradle a -- ^ The cradle we want to load + -> m (CradleLoadResult (m G.SuccessFlag, ComponentOptions)) -- ^ Whether we actually loaded the cradle or not. +initializeFlagsWithCradleWithMessage msg fp cradle = + fmap (initSessionWithMessage msg) <$> liftIO (getCompilerOptions fp cradle) + +-- | Actually perform the initialisation of the session. Initialising the session corresponds to +-- parsing the command line flags, setting the targets for the session and then attempting to load +-- all the targets. +initSessionWithMessage :: (GhcMonad m) + => Maybe G.Messager + -> ComponentOptions + -> (m G.SuccessFlag, ComponentOptions) +initSessionWithMessage msg compOpts = (do + targets <- initSession compOpts + G.setTargets targets + -- Get the module graph using the function `getModuleGraph` + mod_graph <- G.depanal [] True + G.load' LoadAllTargets msg mod_graph, compOpts) + +---------------------------------------------------------------- + +withDynFlags :: + (GhcMonad m) + => (DynFlags -> DynFlags) -> m a -> m a +withDynFlags setFlag body = G.gbracket setup teardown (\_ -> body) + where + setup = do + dflag <- G.getSessionDynFlags + void $ G.setSessionDynFlags (setFlag dflag) + return dflag + teardown = void . G.setSessionDynFlags + +----------------------------------------------------------------
src/HIE/Bios/Ghc/Doc.hs view
@@ -1,25 +1,25 @@--- | Pretty printer utilities-module HIE.Bios.Ghc.Doc where--import GHC (DynFlags, getPrintUnqual, pprCols, GhcMonad)-import Outputable (PprStyle, SDoc, withPprStyleDoc, neverQualify)-import Pretty (Mode(..), Doc, Style(..), renderStyle, style)--import HIE.Bios.Ghc.Gap (makeUserStyle)--showPage :: DynFlags -> PprStyle -> SDoc -> String-showPage dflag stl = showDocWith dflag PageMode . withPprStyleDoc dflag stl--showOneLine :: DynFlags -> PprStyle -> SDoc -> String-showOneLine dflag stl = showDocWith dflag OneLineMode . withPprStyleDoc dflag stl--getStyle :: (GhcMonad m) => DynFlags -> m PprStyle-getStyle dflags = makeUserStyle dflags <$> getPrintUnqual--styleUnqualified :: DynFlags -> PprStyle-styleUnqualified dflags = makeUserStyle dflags neverQualify--showDocWith :: DynFlags -> Mode -> Doc -> String-showDocWith dflags md = renderStyle mstyle- where- mstyle = style { mode = md, lineLength = pprCols dflags }+-- | Pretty printer utilities +module HIE.Bios.Ghc.Doc where + +import GHC (DynFlags, getPrintUnqual, pprCols, GhcMonad) +import Outputable (PprStyle, SDoc, withPprStyleDoc, neverQualify) +import Pretty (Mode(..), Doc, Style(..), renderStyle, style) + +import HIE.Bios.Ghc.Gap (makeUserStyle) + +showPage :: DynFlags -> PprStyle -> SDoc -> String +showPage dflag stl = showDocWith dflag PageMode . withPprStyleDoc dflag stl + +showOneLine :: DynFlags -> PprStyle -> SDoc -> String +showOneLine dflag stl = showDocWith dflag OneLineMode . withPprStyleDoc dflag stl + +getStyle :: (GhcMonad m) => DynFlags -> m PprStyle +getStyle dflags = makeUserStyle dflags <$> getPrintUnqual + +styleUnqualified :: DynFlags -> PprStyle +styleUnqualified dflags = makeUserStyle dflags neverQualify + +showDocWith :: DynFlags -> Mode -> Doc -> String +showDocWith dflags md = renderStyle mstyle + where + mstyle = style { mode = md, lineLength = pprCols dflags }
src/HIE/Bios/Ghc/Gap.hs view
@@ -8,6 +8,7 @@ , getTyThing , fixInfo , getModSummaries+ , mapOverIncludePaths , LExpression , LBinding , LPattern@@ -20,7 +21,7 @@ , unsetLogAction ) where -import DynFlags (DynFlags)+import DynFlags (DynFlags, includePaths) import GHC(LHsBind, LHsExpr, LPat, Type, ModSummary, ModuleGraph, HscEnv, setLogAction, GhcMonad) import Outputable (PrintUnqualified, PprStyle, Depth(AllTheWay), mkUserStyle) @@ -42,6 +43,10 @@ import GHC (mgModSummaries, mapMG) #endif +#if __GLASGOW_HASKELL__ >= 806+import DynFlags (IncludeSpecs(..))+#endif+ #if __GLASGOW_HASKELL__ >= 810 import GHC.Hs.Extension (GhcTc) import GHC.Hs.Expr (MatchGroup, MatchGroupTc(..), mg_ext)@@ -89,6 +94,20 @@ fixInfo :: (a, b, c, d, e) -> (a, b, c, d) fixInfo (t,f,cs,fs,_) = (t,f,cs,fs) #endif++----------------------------------------------------------------++mapOverIncludePaths :: (FilePath -> FilePath) -> DynFlags -> DynFlags+mapOverIncludePaths f df = df+ { includePaths = +#if __GLASGOW_HASKELL__ > 804+ IncludeSpecs+ (map f $ includePathsQuote (includePaths df))+ (map f $ includePathsGlobal (includePaths df))+#else+ map f (includePaths df)+#endif+ } ----------------------------------------------------------------
src/HIE/Bios/Ghc/Load.hs view
@@ -1,171 +1,171 @@-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE CPP #-}--- | Convenience functions for loading a file into a GHC API session-module HIE.Bios.Ghc.Load ( loadFileWithMessage, loadFile, setTargetFiles, setTargetFilesWithMessage) where--import GHC-import qualified GHC as G-import qualified GhcMake as G-import qualified HscMain as G-import HscTypes-import Control.Monad.IO.Class--import Data.IORef--import Hooks-import TcRnTypes (FrontendResult(..))-import Control.Monad (forM, void)-import GhcMonad-import HscMain-import Data.List--import Data.Time.Clock-import qualified HIE.Bios.Ghc.Gap as Gap-import qualified HIE.Bios.Internal.Log as Log---- | Load a target into the GHC session.------ The target is represented as a tuple. The tuple consists of the--- original filename and another file that contains the actual--- source code to compile.------ The optional messager can be used to log diagnostics, warnings or errors--- that occurred during loading the target.------ If the loading succeeds, the typechecked module is returned--- together with all the typechecked modules that had to be loaded--- in order to typecheck the given target.-loadFileWithMessage :: GhcMonad m- => Maybe G.Messager -- ^ Optional messager hook- -- to log messages produced by GHC.- -> (FilePath, FilePath) -- ^ Target file to load.- -> m (Maybe TypecheckedModule, [TypecheckedModule])- -- ^ Typechecked module and modules that had to- -- be loaded for the target.-loadFileWithMessage msg file = do- -- STEP 1: Load the file into the session, using collectASTs to also retrieve- -- typechecked and parsed modules.- (_, tcs) <- collectASTs $ (setTargetFilesWithMessage msg [file])- Log.debugm $ "loaded " ++ fst file ++ " - " ++ snd file- let get_fp = ml_hs_file . ms_location . pm_mod_summary . tm_parsed_module- Log.debugm $ "Typechecked modules for: " ++ (unlines $ map (show . get_fp) tcs)- -- Find the specific module in the list of returned typechecked modules if it exists.- let findMod [] = Nothing- findMod (x:xs) = case get_fp x of- Just fp -> if fp `isSuffixOf` (snd file) then Just x else findMod xs- Nothing -> findMod xs- return (findMod tcs, tcs)---- | Load a target into the GHC session with the default messager--- which outputs updates in the same format as normal GHC.------ The target is represented as a tuple. The tuple consists of the--- original filename and another file that contains the actual--- source code to compile.------ If the message should configured, use 'loadFileWithMessage'.------ If the loading succeeds, the typechecked module is returned--- together with all the typechecked modules that had to be loaded--- in order to typecheck the given target.-loadFile :: (GhcMonad m)- => (FilePath, FilePath) -- ^ Target file to load.- -> m (Maybe TypecheckedModule, [TypecheckedModule])- -- ^ Typechecked module and modules that had to- -- be loaded for the target.-loadFile = loadFileWithMessage (Just G.batchMsg)----- | Set the files as targets and load them. This will reset GHC's targets so only the modules you--- set as targets and its dependencies will be loaded or reloaded.--- Produced diagnostics will be printed similar to the normal output of GHC.--- To configure this, use 'setTargetFilesWithMessage'.-setTargetFiles :: GhcMonad m => [(FilePath, FilePath)] -> m ()-setTargetFiles = setTargetFilesWithMessage (Just G.batchMsg)--msTargetIs :: ModSummary -> Target -> Bool-msTargetIs ms t = case targetId t of- TargetModule m -> moduleName (ms_mod ms) == m- TargetFile f _ -> ml_hs_file (ms_location ms) == Just f---- | We bump the times for any ModSummary's that are Targets, to--- fool the recompilation checker so that we can get the typechecked modules-updateTime :: MonadIO m => [Target] -> ModuleGraph -> m ModuleGraph-updateTime ts graph = liftIO $ do- cur_time <- getCurrentTime- let go ms- | any (msTargetIs ms) ts = ms {ms_hs_date = cur_time}- | otherwise = ms- pure $ Gap.mapMG go graph---- | Set the files as targets and load them. This will reset GHC's targets so only the modules you--- set as targets and its dependencies will be loaded or reloaded.-setTargetFilesWithMessage :: (GhcMonad m) => Maybe G.Messager -> [(FilePath, FilePath)] -> m ()-setTargetFilesWithMessage msg files = do- targets <- forM files guessTargetMapped- Log.debugm $ "setTargets: " ++ show files- G.setTargets targets- mod_graph <- updateTime targets =<< depanal [] False- Log.debugm $ "modGraph: " ++ show (map ms_location $ Gap.mgModSummaries mod_graph)- void $ G.load' LoadAllTargets msg mod_graph---- | Add a hook to record the contents of any 'TypecheckedModule's which are produced--- during compilation.-collectASTs :: (GhcMonad m) => m a -> m (a, [TypecheckedModule])-collectASTs action = do- dflags0 <- getSessionDynFlags- ref1 <- liftIO $ newIORef []- let dflags1 = dflags0 { hooks = (hooks dflags0)- { hscFrontendHook = Just (astHook ref1) }- }- -- Modify session is much faster than `setSessionDynFlags`.- modifySession $ \h -> h{ hsc_dflags = dflags1 }- res <- action- tcs <- liftIO $ readIORef ref1- -- Unset the hook so that we don't retain the reference ot the IORef so it can be gced.- -- This stops the typechecked modules being retained in some cases.- liftIO $ writeIORef ref1 []- dflags_old <- getSessionDynFlags- let dflags2 = dflags1 { hooks = (hooks dflags_old)- { hscFrontendHook = Nothing }- }- modifySession $ \h -> h{ hsc_dflags = dflags2 }-- return (res, tcs)---- | This hook overwrites the default frontend action of GHC.-astHook :: IORef [TypecheckedModule] -> ModSummary -> Hsc FrontendResult-astHook tc_ref ms = ghcInHsc $ do- p <- G.parseModule =<< initializePluginsGhc ms- tcm <- G.typecheckModule p- let tcg_env = fst (tm_internals_ tcm)- liftIO $ modifyIORef tc_ref (tcm :)- return $ FrontendTypecheck tcg_env--initializePluginsGhc :: ModSummary -> Ghc ModSummary-initializePluginsGhc ms = do- hsc_env <- getSession- df <- liftIO $ Gap.initializePlugins hsc_env (ms_hspp_opts ms)- Log.debugm ("init-plugins(loaded):" ++ show (Gap.numLoadedPlugins df))- Log.debugm ("init-plugins(specified):" ++ show (length $ pluginModNames df))- return (ms { ms_hspp_opts = df })---ghcInHsc :: Ghc a -> Hsc a-ghcInHsc gm = do- hsc_session <- getHscEnv- session <- liftIO $ newIORef hsc_session- liftIO $ reflectGhc gm (Session session)---- | A variant of 'guessTarget' which after guessing the target for a filepath, overwrites the--- target file to be a temporary file.-guessTargetMapped :: (GhcMonad m) => (FilePath, FilePath) -> m Target-guessTargetMapped (orig_file_name, mapped_file_name) = do- t <- G.guessTarget orig_file_name Nothing- return (setTargetFilename mapped_file_name t)--setTargetFilename :: FilePath -> Target -> Target-setTargetFilename fn t =- t { targetId = case targetId t of- TargetFile _ p -> TargetFile fn p- tid -> tid }+{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE CPP #-} +-- | Convenience functions for loading a file into a GHC API session +module HIE.Bios.Ghc.Load ( loadFileWithMessage, loadFile, setTargetFiles, setTargetFilesWithMessage) where + +import GHC +import qualified GHC as G +import qualified GhcMake as G +import qualified HscMain as G +import HscTypes +import Control.Monad.IO.Class + +import Data.IORef + +import Hooks +import TcRnTypes (FrontendResult(..)) +import Control.Monad (forM, void) +import GhcMonad +import HscMain +import Data.List + +import Data.Time.Clock +import qualified HIE.Bios.Ghc.Gap as Gap +import qualified HIE.Bios.Internal.Log as Log + +-- | Load a target into the GHC session. +-- +-- The target is represented as a tuple. The tuple consists of the +-- original filename and another file that contains the actual +-- source code to compile. +-- +-- The optional messager can be used to log diagnostics, warnings or errors +-- that occurred during loading the target. +-- +-- If the loading succeeds, the typechecked module is returned +-- together with all the typechecked modules that had to be loaded +-- in order to typecheck the given target. +loadFileWithMessage :: GhcMonad m + => Maybe G.Messager -- ^ Optional messager hook + -- to log messages produced by GHC. + -> (FilePath, FilePath) -- ^ Target file to load. + -> m (Maybe TypecheckedModule, [TypecheckedModule]) + -- ^ Typechecked module and modules that had to + -- be loaded for the target. +loadFileWithMessage msg file = do + -- STEP 1: Load the file into the session, using collectASTs to also retrieve + -- typechecked and parsed modules. + (_, tcs) <- collectASTs $ (setTargetFilesWithMessage msg [file]) + Log.debugm $ "loaded " ++ fst file ++ " - " ++ snd file + let get_fp = ml_hs_file . ms_location . pm_mod_summary . tm_parsed_module + Log.debugm $ "Typechecked modules for: " ++ (unlines $ map (show . get_fp) tcs) + -- Find the specific module in the list of returned typechecked modules if it exists. + let findMod [] = Nothing + findMod (x:xs) = case get_fp x of + Just fp -> if fp `isSuffixOf` (snd file) then Just x else findMod xs + Nothing -> findMod xs + return (findMod tcs, tcs) + +-- | Load a target into the GHC session with the default messager +-- which outputs updates in the same format as normal GHC. +-- +-- The target is represented as a tuple. The tuple consists of the +-- original filename and another file that contains the actual +-- source code to compile. +-- +-- If the message should configured, use 'loadFileWithMessage'. +-- +-- If the loading succeeds, the typechecked module is returned +-- together with all the typechecked modules that had to be loaded +-- in order to typecheck the given target. +loadFile :: (GhcMonad m) + => (FilePath, FilePath) -- ^ Target file to load. + -> m (Maybe TypecheckedModule, [TypecheckedModule]) + -- ^ Typechecked module and modules that had to + -- be loaded for the target. +loadFile = loadFileWithMessage (Just G.batchMsg) + + +-- | Set the files as targets and load them. This will reset GHC's targets so only the modules you +-- set as targets and its dependencies will be loaded or reloaded. +-- Produced diagnostics will be printed similar to the normal output of GHC. +-- To configure this, use 'setTargetFilesWithMessage'. +setTargetFiles :: GhcMonad m => [(FilePath, FilePath)] -> m () +setTargetFiles = setTargetFilesWithMessage (Just G.batchMsg) + +msTargetIs :: ModSummary -> Target -> Bool +msTargetIs ms t = case targetId t of + TargetModule m -> moduleName (ms_mod ms) == m + TargetFile f _ -> ml_hs_file (ms_location ms) == Just f + +-- | We bump the times for any ModSummary's that are Targets, to +-- fool the recompilation checker so that we can get the typechecked modules +updateTime :: MonadIO m => [Target] -> ModuleGraph -> m ModuleGraph +updateTime ts graph = liftIO $ do + cur_time <- getCurrentTime + let go ms + | any (msTargetIs ms) ts = ms {ms_hs_date = cur_time} + | otherwise = ms + pure $ Gap.mapMG go graph + +-- | Set the files as targets and load them. This will reset GHC's targets so only the modules you +-- set as targets and its dependencies will be loaded or reloaded. +setTargetFilesWithMessage :: (GhcMonad m) => Maybe G.Messager -> [(FilePath, FilePath)] -> m () +setTargetFilesWithMessage msg files = do + targets <- forM files guessTargetMapped + Log.debugm $ "setTargets: " ++ show files + G.setTargets targets + mod_graph <- updateTime targets =<< depanal [] False + Log.debugm $ "modGraph: " ++ show (map ms_location $ Gap.mgModSummaries mod_graph) + void $ G.load' LoadAllTargets msg mod_graph + +-- | Add a hook to record the contents of any 'TypecheckedModule's which are produced +-- during compilation. +collectASTs :: (GhcMonad m) => m a -> m (a, [TypecheckedModule]) +collectASTs action = do + dflags0 <- getSessionDynFlags + ref1 <- liftIO $ newIORef [] + let dflags1 = dflags0 { hooks = (hooks dflags0) + { hscFrontendHook = Just (astHook ref1) } + } + -- Modify session is much faster than `setSessionDynFlags`. + modifySession $ \h -> h{ hsc_dflags = dflags1 } + res <- action + tcs <- liftIO $ readIORef ref1 + -- Unset the hook so that we don't retain the reference ot the IORef so it can be gced. + -- This stops the typechecked modules being retained in some cases. + liftIO $ writeIORef ref1 [] + dflags_old <- getSessionDynFlags + let dflags2 = dflags1 { hooks = (hooks dflags_old) + { hscFrontendHook = Nothing } + } + modifySession $ \h -> h{ hsc_dflags = dflags2 } + + return (res, tcs) + +-- | This hook overwrites the default frontend action of GHC. +astHook :: IORef [TypecheckedModule] -> ModSummary -> Hsc FrontendResult +astHook tc_ref ms = ghcInHsc $ do + p <- G.parseModule =<< initializePluginsGhc ms + tcm <- G.typecheckModule p + let tcg_env = fst (tm_internals_ tcm) + liftIO $ modifyIORef tc_ref (tcm :) + return $ FrontendTypecheck tcg_env + +initializePluginsGhc :: ModSummary -> Ghc ModSummary +initializePluginsGhc ms = do + hsc_env <- getSession + df <- liftIO $ Gap.initializePlugins hsc_env (ms_hspp_opts ms) + Log.debugm ("init-plugins(loaded):" ++ show (Gap.numLoadedPlugins df)) + Log.debugm ("init-plugins(specified):" ++ show (length $ pluginModNames df)) + return (ms { ms_hspp_opts = df }) + + +ghcInHsc :: Ghc a -> Hsc a +ghcInHsc gm = do + hsc_session <- getHscEnv + session <- liftIO $ newIORef hsc_session + liftIO $ reflectGhc gm (Session session) + +-- | A variant of 'guessTarget' which after guessing the target for a filepath, overwrites the +-- target file to be a temporary file. +guessTargetMapped :: (GhcMonad m) => (FilePath, FilePath) -> m Target +guessTargetMapped (orig_file_name, mapped_file_name) = do + t <- G.guessTarget orig_file_name Nothing + return (setTargetFilename mapped_file_name t) + +setTargetFilename :: FilePath -> Target -> Target +setTargetFilename fn t = + t { targetId = case targetId t of + TargetFile _ p -> TargetFile fn p + tid -> tid }
src/HIE/Bios/Ghc/Logger.hs view
@@ -1,118 +1,118 @@-{-# LANGUAGE BangPatterns #-}--module HIE.Bios.Ghc.Logger (- withLogger- ) where--import Bag (Bag, bagToList)-import CoreMonad (liftIO)-import DynFlags (LogAction, dopt, DumpFlag(Opt_D_dump_splices))-import ErrUtils-import Exception (ghandle)-import FastString (unpackFS)-import GHC (DynFlags(..), SrcSpan(..), Severity(SevError), GhcMonad)-import qualified GHC as G-import HscTypes (SourceError, srcErrorMessages)-import Outputable (PprStyle, SDoc)--import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef)-import Data.Maybe (fromMaybe)-import System.FilePath (normalise)--import HIE.Bios.Ghc.Doc (showPage, getStyle)-import HIE.Bios.Ghc.Api (withDynFlags)--------------------------------------------------------------------type Builder = [String] -> [String]--newtype LogRef = LogRef (IORef Builder)--newLogRef :: IO LogRef-newLogRef = LogRef <$> newIORef id--readAndClearLogRef :: LogRef -> IO String-readAndClearLogRef (LogRef ref) = do- b <- readIORef ref- writeIORef ref id- return $! unlines (b [])--appendLogRef :: DynFlags -> LogRef -> LogAction-appendLogRef df (LogRef ref) _ _ sev src style msg = do- let !l = ppMsg src sev df style msg- modifyIORef ref (\b -> b . (l:))---------------------------------------------------------------------- | Set the session flag (e.g. "-Wall" or "-w:") then--- executes a body. Log messages are returned as 'String'.--- Right is success and Left is failure.-withLogger ::- (GhcMonad m)- => (DynFlags -> DynFlags) -> m () -> m (Either String String)-withLogger setDF body = ghandle sourceError $ do- logref <- liftIO newLogRef- withDynFlags (setLogger logref . setDF) $ do- body- liftIO $ Right <$> readAndClearLogRef logref- where- setLogger logref df = df { log_action = appendLogRef df logref }---------------------------------------------------------------------- | Converting 'SourceError' to 'String'.-sourceError ::- (GhcMonad m)- => SourceError -> m (Either String String)-sourceError err = do- dflag <- G.getSessionDynFlags- style <- getStyle dflag- let ret = unlines . errBagToStrList dflag style . srcErrorMessages $ err- return (Left ret)--errBagToStrList :: DynFlags -> PprStyle -> Bag ErrMsg -> [String]-errBagToStrList dflag style = map (ppErrMsg dflag style) . reverse . bagToList--------------------------------------------------------------------ppErrMsg :: DynFlags -> PprStyle -> ErrMsg -> String-ppErrMsg dflag style err = ppMsg spn SevError dflag style msg -- ++ ext- where- spn = errMsgSpan err- msg = pprLocErrMsg err- -- fixme--- ext = showPage dflag style (pprLocErrMsg $ errMsgReason err)--ppMsg :: SrcSpan -> Severity-> DynFlags -> PprStyle -> SDoc -> String-ppMsg spn sev dflag style msg = prefix ++ cts- where- cts = showPage dflag style msg- defaultPrefix- | isDumpSplices dflag = ""- | otherwise = checkErrorPrefix- prefix = fromMaybe defaultPrefix $ do- (line,col,_,_) <- getSrcSpan spn- file <- normalise <$> getSrcFile spn- let severityCaption = showSeverityCaption sev- return $ file ++ ":" ++ show line ++ ":" ++ show col ++ ":" ++ severityCaption--checkErrorPrefix :: String-checkErrorPrefix = "Dummy:0:0:Error:"--showSeverityCaption :: Severity -> String-showSeverityCaption SevWarning = "Warning: "-showSeverityCaption _ = ""--getSrcFile :: SrcSpan -> Maybe String-getSrcFile (G.RealSrcSpan spn) = Just . unpackFS . G.srcSpanFile $ spn-getSrcFile _ = Nothing--isDumpSplices :: DynFlags -> Bool-isDumpSplices dflag = dopt Opt_D_dump_splices dflag--getSrcSpan :: SrcSpan -> Maybe (Int,Int,Int,Int)-getSrcSpan (RealSrcSpan spn) = Just ( G.srcSpanStartLine spn- , G.srcSpanStartCol spn- , G.srcSpanEndLine spn- , G.srcSpanEndCol spn)-getSrcSpan _ = Nothing+{-# LANGUAGE BangPatterns #-} + +module HIE.Bios.Ghc.Logger ( + withLogger + ) where + +import Bag (Bag, bagToList) +import CoreMonad (liftIO) +import DynFlags (LogAction, dopt, DumpFlag(Opt_D_dump_splices)) +import ErrUtils +import Exception (ghandle) +import FastString (unpackFS) +import GHC (DynFlags(..), SrcSpan(..), Severity(SevError), GhcMonad) +import qualified GHC as G +import HscTypes (SourceError, srcErrorMessages) +import Outputable (PprStyle, SDoc) + +import Data.IORef (IORef, newIORef, readIORef, writeIORef, modifyIORef) +import Data.Maybe (fromMaybe) +import System.FilePath (normalise) + +import HIE.Bios.Ghc.Doc (showPage, getStyle) +import HIE.Bios.Ghc.Api (withDynFlags) + +---------------------------------------------------------------- + +type Builder = [String] -> [String] + +newtype LogRef = LogRef (IORef Builder) + +newLogRef :: IO LogRef +newLogRef = LogRef <$> newIORef id + +readAndClearLogRef :: LogRef -> IO String +readAndClearLogRef (LogRef ref) = do + b <- readIORef ref + writeIORef ref id + return $! unlines (b []) + +appendLogRef :: DynFlags -> LogRef -> LogAction +appendLogRef df (LogRef ref) _ _ sev src style msg = do + let !l = ppMsg src sev df style msg + modifyIORef ref (\b -> b . (l:)) + +---------------------------------------------------------------- + +-- | Set the session flag (e.g. "-Wall" or "-w:") then +-- executes a body. Log messages are returned as 'String'. +-- Right is success and Left is failure. +withLogger :: + (GhcMonad m) + => (DynFlags -> DynFlags) -> m () -> m (Either String String) +withLogger setDF body = ghandle sourceError $ do + logref <- liftIO newLogRef + withDynFlags (setLogger logref . setDF) $ do + body + liftIO $ Right <$> readAndClearLogRef logref + where + setLogger logref df = df { log_action = appendLogRef df logref } + +---------------------------------------------------------------- + +-- | Converting 'SourceError' to 'String'. +sourceError :: + (GhcMonad m) + => SourceError -> m (Either String String) +sourceError err = do + dflag <- G.getSessionDynFlags + style <- getStyle dflag + let ret = unlines . errBagToStrList dflag style . srcErrorMessages $ err + return (Left ret) + +errBagToStrList :: DynFlags -> PprStyle -> Bag ErrMsg -> [String] +errBagToStrList dflag style = map (ppErrMsg dflag style) . reverse . bagToList + +---------------------------------------------------------------- + +ppErrMsg :: DynFlags -> PprStyle -> ErrMsg -> String +ppErrMsg dflag style err = ppMsg spn SevError dflag style msg -- ++ ext + where + spn = errMsgSpan err + msg = pprLocErrMsg err + -- fixme +-- ext = showPage dflag style (pprLocErrMsg $ errMsgReason err) + +ppMsg :: SrcSpan -> Severity-> DynFlags -> PprStyle -> SDoc -> String +ppMsg spn sev dflag style msg = prefix ++ cts + where + cts = showPage dflag style msg + defaultPrefix + | isDumpSplices dflag = "" + | otherwise = checkErrorPrefix + prefix = fromMaybe defaultPrefix $ do + (line,col,_,_) <- getSrcSpan spn + file <- normalise <$> getSrcFile spn + let severityCaption = showSeverityCaption sev + return $ file ++ ":" ++ show line ++ ":" ++ show col ++ ":" ++ severityCaption + +checkErrorPrefix :: String +checkErrorPrefix = "Dummy:0:0:Error:" + +showSeverityCaption :: Severity -> String +showSeverityCaption SevWarning = "Warning: " +showSeverityCaption _ = "" + +getSrcFile :: SrcSpan -> Maybe String +getSrcFile (G.RealSrcSpan spn) = Just . unpackFS . G.srcSpanFile $ spn +getSrcFile _ = Nothing + +isDumpSplices :: DynFlags -> Bool +isDumpSplices dflag = dopt Opt_D_dump_splices dflag + +getSrcSpan :: SrcSpan -> Maybe (Int,Int,Int,Int) +getSrcSpan (RealSrcSpan spn) = Just ( G.srcSpanStartLine spn + , G.srcSpanStartCol spn + , G.srcSpanEndLine spn + , G.srcSpanEndCol spn) +getSrcSpan _ = Nothing
src/HIE/Bios/Internal/Debug.hs view
@@ -35,10 +35,11 @@ conf <- findConfig canonFp crdl <- findCradle' canonFp case res of- CradleSuccess (ComponentOptions gopts deps) -> do+ CradleSuccess (ComponentOptions gopts croot deps) -> do mglibdir <- liftIO getSystemLibDir return [ "Root directory: " ++ rootDir+ , "Component directory: " ++ croot , "GHC options: " ++ unwords (map quoteIfNeeded gopts) , "System libraries: " ++ fromMaybe "" mglibdir , "Config Location: " ++ conf
src/HIE/Bios/Internal/Log.hs view
@@ -1,16 +1,16 @@-module HIE.Bios.Internal.Log where--import Control.Monad.IO.Class-import System.Log.Logger--logm :: MonadIO m => String -> m ()-logm s = liftIO $ infoM "hie-bios" s--debugm :: MonadIO m => String -> m ()-debugm s = liftIO $ debugM "hie-bios" s--warningm :: MonadIO m => String -> m ()-warningm s = liftIO $ warningM "hie-bios" s--errorm :: MonadIO m => String -> m ()-errorm s = liftIO $ errorM "hie-bios" s+module HIE.Bios.Internal.Log where + +import Control.Monad.IO.Class +import System.Log.Logger + +logm :: MonadIO m => String -> m () +logm s = liftIO $ infoM "hie-bios" s + +debugm :: MonadIO m => String -> m () +debugm s = liftIO $ debugM "hie-bios" s + +warningm :: MonadIO m => String -> m () +warningm s = liftIO $ warningM "hie-bios" s + +errorm :: MonadIO m => String -> m () +errorm s = liftIO $ errorM "hie-bios" s
src/HIE/Bios/Types.hs view
@@ -81,6 +81,9 @@ -- | Option information for GHC data ComponentOptions = ComponentOptions { componentOptions :: [String] -- ^ Command line options.+ , componentRoot :: FilePath + -- ^ Root directory of the component. All 'componentOptions' are either + -- absolute, or relative to this directory. , componentDependencies :: [FilePath] -- ^ Dependencies of a cradle that might change the cradle. -- Contains both files specified in hie.yaml as well as
src/HIE/Bios/Wrappers.hs view
@@ -1,12 +1,12 @@-{-# LANGUAGE TemplateHaskell #-}-{-# LANGUAGE QuasiQuotes #-}-module HIE.Bios.Wrappers (cabalWrapper, cabalWrapperHs) where--import Data.FileEmbed--cabalWrapper :: String-cabalWrapper = $(embedStringFile "wrappers/cabal")--cabalWrapperHs :: String-cabalWrapperHs = $(embedStringFile "wrappers/cabal.hs")-+{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE QuasiQuotes #-} +module HIE.Bios.Wrappers (cabalWrapper, cabalWrapperHs) where + +import Data.FileEmbed + +cabalWrapper :: String +cabalWrapper = $(embedStringFile "wrappers/cabal") + +cabalWrapperHs :: String +cabalWrapperHs = $(embedStringFile "wrappers/cabal.hs") +
tests/BiosTests.hs view
@@ -40,7 +40,6 @@ $ linuxExlusiveTestCases ++ [ testCaseSteps "simple-cabal" $ testDirectory isCabalCradle "./tests/projects/simple-cabal/B.hs"- , testCaseSteps "simple-stack" $ testDirectory isStackCradle "./tests/projects/simple-stack/B.hs" , testCaseSteps "simple-direct" $ testDirectory isDirectCradle "./tests/projects/simple-direct/B.hs" , testCaseSteps "multi-direct" {- tests if both components can be loaded -} $ testDirectory isMultiCradle "./tests/projects/multi-direct/A.hs"@@ -48,16 +47,30 @@ , testCaseSteps "multi-cabal" {- tests if both components can be loaded -} $ testDirectory isCabalCradle "./tests/projects/multi-cabal/app/Main.hs" >> testDirectory isCabalCradle "./tests/projects/multi-cabal/src/Lib.hs"- , testCaseSteps "multi-stack" {- tests if both components can be loaded -}- $ testDirectory isStackCradle "./tests/projects/multi-stack/app/Main.hs"- >> testDirectory isStackCradle "./tests/projects/multi-stack/src/Lib.hs" ]+-- TODO: Remove once there's a stackage snapshot for ghc 8.10+#if __GLASGOW_HASKELL__ < 810+ ++ [ testCaseSteps "simple-stack" $ testDirectory isStackCradle "./tests/projects/simple-stack/B.hs"+ , testCaseSteps "multi-stack" {- tests if both components can be loaded -}+ $ testDirectory isStackCradle "./tests/projects/multi-stack/app/Main.hs"+ >> testDirectory isStackCradle "./tests/projects/multi-stack/src/Lib.hs"+ ,+ -- Test for special characters in the path for parsing of the ghci-scripts.+ -- Issue https://github.com/mpickering/hie-bios/issues/162+ testCaseSteps "space stack"+ $ testDirectory isStackCradle "./tests/projects/space stack/A.hs"+ >> testDirectory isStackCradle "./tests/projects/space stack/B.hs"+ ]+#endif , testGroup "Implicit cradle tests" $ [ testCaseSteps "implicit-cabal" $ testImplicitCradle "./tests/projects/implicit-cabal/Main.hs" Cabal+-- TODO: Remove once there's a stackage snapshot for ghc 8.10+#if __GLASGOW_HASKELL__ < 810 , testCaseSteps "implicit-stack" $ testImplicitCradle "./tests/projects/implicit-stack/Main.hs" Stack , testCaseSteps "implicit-stack-multi" $ testImplicitCradle "./tests/projects/implicit-stack-multi/Main.hs" Stack >> testImplicitCradle "./tests/projects/implicit-stack-multi/other-package/Main.hs" Stack+#endif ] ] @@ -129,6 +142,7 @@ stackProjects = [ "tests" </> "projects" </> "multi-stack" , "tests" </> "projects" </> "simple-stack"+ , "tests" </> "projects" </> "space stack" , "tests" </> "projects" </> "implicit-stack" , "tests" </> "projects" </> "implicit-stack-multi" ]@@ -138,8 +152,10 @@ stackYamlResolver :: String stackYamlResolver =-#if (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(8,8,1,0)))- "nightly-2019-12-13"+#if (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(8,10,1,0)))+ "TODO"+#elif (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(8,8,1,0)))+ "lts-15.10" #elif (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(8,6,5,0))) "lts-14.17" #elif (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(8,6,4,0)))
tests/ParserTests.hs view
@@ -1,105 +1,105 @@-{-# LANGUAGE OverloadedStrings #-}-module Main where--import Test.Tasty-import Test.Tasty.HUnit-import HIE.Bios.Config-import qualified Data.HashMap.Strict as Map-import Data.Void-import Data.Yaml-import qualified Data.Text as T-import System.FilePath-import Control.Applicative ( (<|>) )--configDir :: FilePath-configDir = "tests/configs"--main :: IO ()-main = defaultMain $- testCase "Parser Tests" $ do- assertParser "cabal-1.yaml" (noDeps (Cabal (Just "lib:hie-bios")))- assertParser "stack-config.yaml" (noDeps (Stack Nothing))- --assertParser "bazel.yaml" (noDeps Bazel)- assertParser "bios-1.yaml" (noDeps (Bios "program" Nothing))- assertParser "bios-2.yaml" (noDeps (Bios "program" (Just "dep-program")))- assertParser "dependencies.yaml" (Config (CradleConfig ["depFile"] (Cabal (Just "lib:hie-bios"))))- assertParser "direct.yaml" (noDeps (Direct ["list", "of", "arguments"]))- assertParser "none.yaml" (noDeps None)- --assertParser "obelisk.yaml" (noDeps Obelisk)- assertParser "multi.yaml" (noDeps (Multi [("./src", CradleConfig [] (Cabal (Just "lib:hie-bios")))- , ("./test", CradleConfig [] (Cabal (Just "test")) ) ]))-- assertParser "cabal-multi.yaml" (noDeps (CabalMulti [("./src", "lib:hie-bios")- ,("./", "lib:hie-bios")]))-- assertParser "stack-multi.yaml" (noDeps (StackMulti [("./src", "lib:hie-bios")- ,("./", "lib:hie-bios")]))-- assertParser "nested-cabal-multi.yaml" (noDeps (Multi [("./test/testdata", CradleConfig [] None)- ,("./", CradleConfig [] (- CabalMulti [("./src", "lib:hie-bios")- ,("./tests", "parser-tests")]))]))-- assertParser "nested-stack-multi.yaml" (noDeps (Multi [("./test/testdata", CradleConfig [] None)- ,("./", CradleConfig [] (- StackMulti [("./src", "lib:hie-bios")- ,("./tests", "parser-tests")]))]))-- assertCustomParser "ch-cabal.yaml"- (noDeps (Other CabalHelperCabal $ simpleCabalHelperYaml "cabal"))- assertCustomParser "ch-stack.yaml"- (noDeps (Other CabalHelperStack $ simpleCabalHelperYaml "stack"))- assertCustomParser "multi-ch.yaml"- (noDeps (Multi- [ ("./src", CradleConfig [] (Other CabalHelperStack $ simpleCabalHelperYaml "stack"))- , ("./input", CradleConfig [] (Other CabalHelperCabal $ simpleCabalHelperYaml "cabal"))- , ("./test", CradleConfig [] (Cabal (Just "test")))- , (".", CradleConfig [] None)- ]))--assertParser :: FilePath -> Config Void -> Assertion-assertParser fp cc = do- conf <- readConfig (configDir </> fp)- (conf == cc) @? (unlines [("Parser Failed: " ++ fp)- , "Expected: " ++ show cc- , "Actual: " ++ show conf ])--assertCustomParser :: FilePath -> Config CabalHelper -> Assertion-assertCustomParser fp cc = do- conf <- readConfig (configDir </> fp)- (conf == cc) @? (unlines [("Parser Failed: " ++ fp)- , "Expected: " ++ show cc- , "Actual: " ++ show conf ])--noDeps :: CradleType a -> Config a-noDeps c = Config (CradleConfig [] c)---- --------------------------------------------------------------------data CabalHelper- = CabalHelperCabal- | CabalHelperStack- deriving (Show, Eq)--instance FromJSON CabalHelper where- parseJSON (Object o)- | Just obj <- Map.lookup "cabal-helper" o = chCabal obj <|> chStack obj- where- chCabal (Object val)- | Just _val <- Map.lookup "cabal" val = return CabalHelperCabal- chCabal _ = fail "CH: not a cabal cradle."-- chStack (Object val)- | Just _val <- Map.lookup "stack" val = return CabalHelperStack- chStack _ = fail "CH: not a stack cradle."-- parseJSON _ = fail "Not a valid cabal-helper specification"--simpleCabalHelperYaml :: T.Text -> Value-simpleCabalHelperYaml tool =- object- [ ( "cabal-helper", object- [ (tool, Null)- ]- )- ]+{-# LANGUAGE OverloadedStrings #-} +module Main where + +import Test.Tasty +import Test.Tasty.HUnit +import HIE.Bios.Config +import qualified Data.HashMap.Strict as Map +import Data.Void +import Data.Yaml +import qualified Data.Text as T +import System.FilePath +import Control.Applicative ( (<|>) ) + +configDir :: FilePath +configDir = "tests/configs" + +main :: IO () +main = defaultMain $ + testCase "Parser Tests" $ do + assertParser "cabal-1.yaml" (noDeps (Cabal (Just "lib:hie-bios"))) + assertParser "stack-config.yaml" (noDeps (Stack Nothing)) + --assertParser "bazel.yaml" (noDeps Bazel) + assertParser "bios-1.yaml" (noDeps (Bios "program" Nothing)) + assertParser "bios-2.yaml" (noDeps (Bios "program" (Just "dep-program"))) + assertParser "dependencies.yaml" (Config (CradleConfig ["depFile"] (Cabal (Just "lib:hie-bios")))) + assertParser "direct.yaml" (noDeps (Direct ["list", "of", "arguments"])) + assertParser "none.yaml" (noDeps None) + --assertParser "obelisk.yaml" (noDeps Obelisk) + assertParser "multi.yaml" (noDeps (Multi [("./src", CradleConfig [] (Cabal (Just "lib:hie-bios"))) + , ("./test", CradleConfig [] (Cabal (Just "test")) ) ])) + + assertParser "cabal-multi.yaml" (noDeps (CabalMulti [("./src", "lib:hie-bios") + ,("./", "lib:hie-bios")])) + + assertParser "stack-multi.yaml" (noDeps (StackMulti [("./src", "lib:hie-bios") + ,("./", "lib:hie-bios")])) + + assertParser "nested-cabal-multi.yaml" (noDeps (Multi [("./test/testdata", CradleConfig [] None) + ,("./", CradleConfig [] ( + CabalMulti [("./src", "lib:hie-bios") + ,("./tests", "parser-tests")]))])) + + assertParser "nested-stack-multi.yaml" (noDeps (Multi [("./test/testdata", CradleConfig [] None) + ,("./", CradleConfig [] ( + StackMulti [("./src", "lib:hie-bios") + ,("./tests", "parser-tests")]))])) + + assertCustomParser "ch-cabal.yaml" + (noDeps (Other CabalHelperCabal $ simpleCabalHelperYaml "cabal")) + assertCustomParser "ch-stack.yaml" + (noDeps (Other CabalHelperStack $ simpleCabalHelperYaml "stack")) + assertCustomParser "multi-ch.yaml" + (noDeps (Multi + [ ("./src", CradleConfig [] (Other CabalHelperStack $ simpleCabalHelperYaml "stack")) + , ("./input", CradleConfig [] (Other CabalHelperCabal $ simpleCabalHelperYaml "cabal")) + , ("./test", CradleConfig [] (Cabal (Just "test"))) + , (".", CradleConfig [] None) + ])) + +assertParser :: FilePath -> Config Void -> Assertion +assertParser fp cc = do + conf <- readConfig (configDir </> fp) + (conf == cc) @? (unlines [("Parser Failed: " ++ fp) + , "Expected: " ++ show cc + , "Actual: " ++ show conf ]) + +assertCustomParser :: FilePath -> Config CabalHelper -> Assertion +assertCustomParser fp cc = do + conf <- readConfig (configDir </> fp) + (conf == cc) @? (unlines [("Parser Failed: " ++ fp) + , "Expected: " ++ show cc + , "Actual: " ++ show conf ]) + +noDeps :: CradleType a -> Config a +noDeps c = Config (CradleConfig [] c) + +-- ------------------------------------------------------------------ + +data CabalHelper + = CabalHelperCabal + | CabalHelperStack + deriving (Show, Eq) + +instance FromJSON CabalHelper where + parseJSON (Object o) + | Just obj <- Map.lookup "cabal-helper" o = chCabal obj <|> chStack obj + where + chCabal (Object val) + | Just _val <- Map.lookup "cabal" val = return CabalHelperCabal + chCabal _ = fail "CH: not a cabal cradle." + + chStack (Object val) + | Just _val <- Map.lookup "stack" val = return CabalHelperStack + chStack _ = fail "CH: not a stack cradle." + + parseJSON _ = fail "Not a valid cabal-helper specification" + +simpleCabalHelperYaml :: T.Text -> Value +simpleCabalHelperYaml tool = + object + [ ( "cabal-helper", object + [ (tool, Null) + ] + ) + ]
tests/configs/bazel.yaml view
@@ -1,2 +1,2 @@-cradle:- { bazel: }+cradle: + { bazel: }
tests/configs/bios-1.yaml view
@@ -1,3 +1,3 @@-cradle:- bios:- program: "program"+cradle: + bios: + program: "program"
tests/configs/bios-2.yaml view
@@ -1,4 +1,4 @@-cradle:- bios:- program: "program"- dependency-program: "dep-program"+cradle: + bios: + program: "program" + dependency-program: "dep-program"
tests/configs/cabal-1.yaml view
@@ -1,2 +1,2 @@-cradle:- { cabal: {component: "lib:hie-bios"} }+cradle: + { cabal: {component: "lib:hie-bios"} }
tests/configs/cabal-multi.yaml view
@@ -1,6 +1,6 @@-cradle:- cabal:- - path: "./src"- component: "lib:hie-bios"- - path: "./"- component: "lib:hie-bios"+cradle: + cabal: + - path: "./src" + component: "lib:hie-bios" + - path: "./" + component: "lib:hie-bios"
tests/configs/ch-cabal.yaml view
@@ -1,4 +1,4 @@-cradle:- other:- cabal-helper:+cradle: + other: + cabal-helper: cabal:
tests/configs/ch-stack.yaml view
@@ -1,4 +1,4 @@-cradle:- other:- cabal-helper:+cradle: + other: + cabal-helper: stack:
tests/configs/default.yaml view
@@ -1,2 +1,2 @@-cradle:- default:+cradle: + default:
tests/configs/dependencies.yaml view
@@ -1,5 +1,5 @@-cradle:- { cabal: {component: "lib:hie-bios"} }--dependencies:- - "depFile"+cradle: + { cabal: {component: "lib:hie-bios"} } + +dependencies: + - "depFile"
tests/configs/direct.yaml view
@@ -1,3 +1,3 @@-cradle:- direct:- arguments: ["list", "of", "arguments"]+cradle: + direct: + arguments: ["list", "of", "arguments"]
tests/configs/multi-ch.yaml view
@@ -1,10 +1,10 @@-cradle:- multi:- - path: "./src"- config: { cradle: {other: {cabal-helper: {stack: } } } }- - path: "./input"- config: { cradle: {other: {cabal-helper: {cabal: } } } }- - path: "./test"- config: { cradle: {cabal: {component: "test"}} }- - path: "."- config: { cradle: { none: } }+cradle: + multi: + - path: "./src" + config: { cradle: {other: {cabal-helper: {stack: } } } } + - path: "./input" + config: { cradle: {other: {cabal-helper: {cabal: } } } } + - path: "./test" + config: { cradle: {cabal: {component: "test"}} } + - path: "." + config: { cradle: { none: } }
tests/configs/multi.yaml view
@@ -1,6 +1,6 @@-cradle:- multi:- - path: "./src"- config: { cradle: {cabal: {component: "lib:hie-bios"}} }- - path: "./test"- config: { cradle: {cabal: {component: "test"}} }+cradle: + multi: + - path: "./src" + config: { cradle: {cabal: {component: "lib:hie-bios"}} } + - path: "./test" + config: { cradle: {cabal: {component: "test"}} }
tests/configs/nested-cabal-multi.yaml view
@@ -1,8 +1,8 @@-cradle:- multi:- - path: "./test/testdata"- config: { cradle: { none: } }- - path: "./"- config: { cradle: { cabal:- [ { path: "./src", component: "lib:hie-bios" }- , { path: "./tests", component: "parser-tests" } ] } }+cradle: + multi: + - path: "./test/testdata" + config: { cradle: { none: } } + - path: "./" + config: { cradle: { cabal: + [ { path: "./src", component: "lib:hie-bios" } + , { path: "./tests", component: "parser-tests" } ] } }
tests/configs/nested-stack-multi.yaml view
@@ -1,8 +1,8 @@-cradle:- multi:- - path: "./test/testdata"- config: { cradle: { none: } }- - path: "./"- config: { cradle: { stack:- [ { path: "./src", component: "lib:hie-bios" }- , { path: "./tests", component: "parser-tests" } ] } }+cradle: + multi: + - path: "./test/testdata" + config: { cradle: { none: } } + - path: "./" + config: { cradle: { stack: + [ { path: "./src", component: "lib:hie-bios" } + , { path: "./tests", component: "parser-tests" } ] } }
tests/configs/none.yaml view
@@ -1,2 +1,2 @@-cradle:- none:+cradle: + none:
tests/configs/obelisk.yaml view
@@ -1,2 +1,2 @@-cradle:- { obelisk: }+cradle: + { obelisk: }
tests/configs/stack-config.yaml view
@@ -1,2 +1,2 @@-cradle:- { stack: {}}+cradle: + { stack: {}}
tests/configs/stack-multi.yaml view
@@ -1,6 +1,6 @@-cradle:- stack:- - path: "./src"- component: "lib:hie-bios"- - path: "./"- component: "lib:hie-bios"+cradle: + stack: + - path: "./src" + component: "lib:hie-bios" + - path: "./" + component: "lib:hie-bios"
tests/projects/implicit-cabal/Main.hs view
@@ -1,4 +1,4 @@-module Main where--main :: IO ()-main = putStrLn "Hello, Haskell!"+module Main where + +main :: IO () +main = putStrLn "Hello, Haskell!"
tests/projects/implicit-cabal/Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple +main = defaultMain
tests/projects/implicit-cabal/cabal.project view
@@ -1,1 +1,1 @@-packages: .+packages: .
tests/projects/implicit-cabal/implicit-cabal.cabal view
@@ -1,25 +1,25 @@-cabal-version: >=1.10--- Initial package description 'implicit-cabal.cabal' generated by 'cabal--- init'. For further documentation, see--- http://haskell.org/cabal/users-guide/--name: implicit-cabal-version: 0.1.0.0--- synopsis:--- description:--- bug-reports:--- license:-license-file: LICENSE-author: Luke Lau-maintainer: luke_lau@icloud.com--- copyright:--- category:-build-type: Simple--executable implicit-cabal- main-is: Main.hs- -- other-modules:- -- other-extensions:- build-depends: base == 4.*- -- hs-source-dirs:- default-language: Haskell2010+cabal-version: >=1.10 +-- Initial package description 'implicit-cabal.cabal' generated by 'cabal +-- init'. For further documentation, see +-- http://haskell.org/cabal/users-guide/ + +name: implicit-cabal +version: 0.1.0.0 +-- synopsis: +-- description: +-- bug-reports: +-- license: +license-file: LICENSE +author: Luke Lau +maintainer: luke_lau@icloud.com +-- copyright: +-- category: +build-type: Simple + +executable implicit-cabal + main-is: Main.hs + -- other-modules: + -- other-extensions: + build-depends: base == 4.* + -- hs-source-dirs: + default-language: Haskell2010
tests/projects/implicit-stack-multi/Main.hs view
@@ -1,4 +1,4 @@-module Main where--main :: IO ()-main = putStrLn "Hello, Haskell!"+module Main where + +main :: IO () +main = putStrLn "Hello, Haskell!"
tests/projects/implicit-stack-multi/Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple +main = defaultMain
tests/projects/implicit-stack-multi/implicit-stack-multi.cabal view
@@ -1,25 +1,25 @@-cabal-version: >=1.10--- Initial package description 'implicit-stack-multi.cabal' generated by--- 'cabal init'. For further documentation, see--- http://haskell.org/cabal/users-guide/--name: implicit-stack-multi-version: 0.1.0.0--- synopsis:--- description:--- bug-reports:--- license:-license-file: LICENSE-author: Luke Lau-maintainer: luke_lau@icloud.com--- copyright:--- category:-build-type: Simple--executable implicit-stack-multi- main-is: Main.hs- -- other-modules:- -- other-extensions:- build-depends: base == 4.*- -- hs-source-dirs:- default-language: Haskell2010+cabal-version: >=1.10 +-- Initial package description 'implicit-stack-multi.cabal' generated by +-- 'cabal init'. For further documentation, see +-- http://haskell.org/cabal/users-guide/ + +name: implicit-stack-multi +version: 0.1.0.0 +-- synopsis: +-- description: +-- bug-reports: +-- license: +license-file: LICENSE +author: Luke Lau +maintainer: luke_lau@icloud.com +-- copyright: +-- category: +build-type: Simple + +executable implicit-stack-multi + main-is: Main.hs + -- other-modules: + -- other-extensions: + build-depends: base == 4.* + -- hs-source-dirs: + default-language: Haskell2010
tests/projects/implicit-stack-multi/other-package/Main.hs view
@@ -1,4 +1,4 @@-module Main where--main :: IO ()-main = putStrLn "Hello, Haskell!"+module Main where + +main :: IO () +main = putStrLn "Hello, Haskell!"
tests/projects/implicit-stack-multi/other-package/Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple +main = defaultMain
tests/projects/implicit-stack-multi/other-package/other-package.cabal view
@@ -1,26 +1,26 @@-cabal-version: >=1.10--- Initial package description 'other-package.cabal' generated by 'cabal--- init'. For further documentation, see--- http://haskell.org/cabal/users-guide/--name: other-package-version: 0.1.0.0--- synopsis:--- description:--- bug-reports:--- license:-license-file: LICENSE-author: Luke Lau-maintainer: luke_lau@icloud.com--- copyright:--- category:-build-type: Simple-extra-source-files: CHANGELOG.md--executable other-package- main-is: Main.hs- -- other-modules:- -- other-extensions:- build-depends: base == 4.*- -- hs-source-dirs:- default-language: Haskell2010+cabal-version: >=1.10 +-- Initial package description 'other-package.cabal' generated by 'cabal +-- init'. For further documentation, see +-- http://haskell.org/cabal/users-guide/ + +name: other-package +version: 0.1.0.0 +-- synopsis: +-- description: +-- bug-reports: +-- license: +license-file: LICENSE +author: Luke Lau +maintainer: luke_lau@icloud.com +-- copyright: +-- category: +build-type: Simple +extra-source-files: CHANGELOG.md + +executable other-package + main-is: Main.hs + -- other-modules: + -- other-extensions: + build-depends: base == 4.* + -- hs-source-dirs: + default-language: Haskell2010
tests/projects/implicit-stack/Main.hs view
@@ -1,4 +1,4 @@-module Main where--main :: IO ()-main = putStrLn "Hello, Haskell!"+module Main where + +main :: IO () +main = putStrLn "Hello, Haskell!"
tests/projects/implicit-stack/Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple +main = defaultMain
tests/projects/implicit-stack/implicit-stack.cabal view
@@ -1,25 +1,25 @@-cabal-version: >=1.10--- Initial package description 'implicit-stack.cabal' generated by 'cabal--- init'. For further documentation, see--- http://haskell.org/cabal/users-guide/--name: implicit-stack-version: 0.1.0.0--- synopsis:--- description:--- bug-reports:--- license:-license-file: LICENSE-author: Luke Lau-maintainer: luke_lau@icloud.com--- copyright:--- category:-build-type: Simple--executable implicit-stack- main-is: Main.hs- -- other-modules:- -- other-extensions:- build-depends: base == 4.*- -- hs-source-dirs:- default-language: Haskell2010+cabal-version: >=1.10 +-- Initial package description 'implicit-stack.cabal' generated by 'cabal +-- init'. For further documentation, see +-- http://haskell.org/cabal/users-guide/ + +name: implicit-stack +version: 0.1.0.0 +-- synopsis: +-- description: +-- bug-reports: +-- license: +license-file: LICENSE +author: Luke Lau +maintainer: luke_lau@icloud.com +-- copyright: +-- category: +build-type: Simple + +executable implicit-stack + main-is: Main.hs + -- other-modules: + -- other-extensions: + build-depends: base == 4.* + -- hs-source-dirs: + default-language: Haskell2010
tests/projects/multi-cabal/Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple +main = defaultMain
tests/projects/multi-cabal/app/Main.hs view
@@ -1,4 +1,4 @@--import System.Directory (getCurrentDirectory)-+ +import System.Directory (getCurrentDirectory) + main = return ()
tests/projects/multi-cabal/cabal.project view
@@ -1,1 +1,1 @@-packages: ./+packages: ./
tests/projects/multi-cabal/hie.yaml view
@@ -1,7 +1,7 @@-cradle:- cabal:- - path: ./src- component: "lib:multi-cabal"-- - path: ./app+cradle: + cabal: + - path: ./src + component: "lib:multi-cabal" + + - path: ./app component: "exe:multi-cabal"
tests/projects/multi-cabal/multi-cabal.cabal view
@@ -1,23 +1,23 @@-cabal-version: >=2.0-name: multi-cabal-version: 0.1.0.0-build-type: Simple--library- exposed-modules: Lib- -- other-modules:- -- other-extensions:- build-depends: base >=4.10 && < 5, filepath- hs-source-dirs: src- default-language: Haskell2010- ghc-options: +RTS -A1m -N -RTS -Wall----executable multi-cabal- main-is: app/Main.hs- -- other-modules:- -- other-extensions:- build-depends: base >=4.10 && < 5, directory- -- hs-source-dirs:- default-language: Haskell2010+cabal-version: >=2.0 +name: multi-cabal +version: 0.1.0.0 +build-type: Simple + +library + exposed-modules: Lib + -- other-modules: + -- other-extensions: + build-depends: base >=4.10 && < 5, filepath + hs-source-dirs: src + default-language: Haskell2010 + ghc-options: +RTS -A1m -N -RTS -Wall + + + +executable multi-cabal + main-is: app/Main.hs + -- other-modules: + -- other-extensions: + build-depends: base >=4.10 && < 5, directory + -- hs-source-dirs: + default-language: Haskell2010
tests/projects/multi-cabal/src/Lib.hs view
@@ -1,5 +1,5 @@-module Lib where--import System.FilePath ((</>))-+module Lib where + +import System.FilePath ((</>)) + foo = "test" </> "me"
tests/projects/multi-direct/B.hs view
@@ -1,3 +1,3 @@-module B where--import A+module B where + +import A
tests/projects/multi-direct/hie.yaml view
@@ -1,12 +1,12 @@-cradle:- multi:- - path: "A.hs"- config:- cradle:- direct:- arguments: ["-Walaaal", "A", "B"]- - path: "B.hs"- config:- cradle:- direct:- arguments: ["-Walaaal", "A", "B"]+cradle: + multi: + - path: "A.hs" + config: + cradle: + direct: + arguments: ["-Walaaal", "A", "B"] + - path: "B.hs" + config: + cradle: + direct: + arguments: ["-Walaaal", "A", "B"]
tests/projects/multi-stack/Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple +main = defaultMain
tests/projects/multi-stack/app/Main.hs view
@@ -1,4 +1,4 @@--import System.Directory (getCurrentDirectory)-+ +import System.Directory (getCurrentDirectory) + main = return ()
tests/projects/multi-stack/cabal.project view
@@ -1,1 +1,1 @@-packages: ./+packages: ./
tests/projects/multi-stack/hie.yaml view
@@ -1,7 +1,7 @@-cradle:- stack:- - path: ./src- component: "multi-stack:lib"-- - path: ./app+cradle: + stack: + - path: ./src + component: "multi-stack:lib" + + - path: ./app component: "multi-stack:exe:multi-stack"
tests/projects/multi-stack/multi-stack.cabal view
@@ -1,23 +1,23 @@-cabal-version: >=2.0-name: multi-stack-version: 0.1.0.0-build-type: Simple--library- exposed-modules: Lib- -- other-modules:- -- other-extensions:- build-depends: base >=4.10 && < 5, filepath- hs-source-dirs: src- default-language: Haskell2010----executable multi-stack- main-is: app/Main.hs- -- other-modules:- -- other-extensions:- build-depends: base >=4.10 && < 5, directory- -- hs-source-dirs:- default-language: Haskell2010+cabal-version: >=2.0 +name: multi-stack +version: 0.1.0.0 +build-type: Simple + +library + exposed-modules: Lib + -- other-modules: + -- other-extensions: + build-depends: base >=4.10 && < 5, filepath + hs-source-dirs: src + default-language: Haskell2010 + + + +executable multi-stack + main-is: app/Main.hs + -- other-modules: + -- other-extensions: + build-depends: base >=4.10 && < 5, directory + -- hs-source-dirs: + default-language: Haskell2010 ghc-options: +RTS -A1m -N -RTS -Wall
tests/projects/multi-stack/src/Lib.hs view
@@ -1,5 +1,5 @@-module Lib where--import System.FilePath ((</>))-+module Lib where + +import System.FilePath ((</>)) + foo = "test" </> "me"
tests/projects/simple-bios/B.hs view
@@ -1,3 +1,3 @@-module B where--import A+module B where + +import A
tests/projects/simple-bios/hie-bios.sh view
@@ -1,3 +1,3 @@-echo "-Wall" >> $HIE_BIOS_OUTPUT-echo "A" >> $HIE_BIOS_OUTPUT-echo "B" >> $HIE_BIOS_OUTPUT+echo "-Wall" >> $HIE_BIOS_OUTPUT +echo "A" >> $HIE_BIOS_OUTPUT +echo "B" >> $HIE_BIOS_OUTPUT
tests/projects/simple-bios/hie.yaml view
@@ -1,2 +1,2 @@-cradle:- bios: {program: ./hie-bios.sh }+cradle: + bios: {program: ./hie-bios.sh }
tests/projects/simple-cabal/A.hs view
@@ -1,1 +1,1 @@-module A where+module A where
tests/projects/simple-cabal/B.hs view
@@ -1,3 +1,3 @@-module B where--import A+module B where + +import A
tests/projects/simple-cabal/CHANGELOG.md view
@@ -1,5 +1,5 @@-# Revision history for simple-cabal--## 0.1.0.0 -- YYYY-mm-dd--* First version. Released on an unsuspecting world.+# Revision history for simple-cabal + +## 0.1.0.0 -- YYYY-mm-dd + +* First version. Released on an unsuspecting world.
tests/projects/simple-cabal/Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple +main = defaultMain
tests/projects/simple-cabal/cabal.project view
@@ -1,1 +1,1 @@-packages: .+packages: .
tests/projects/simple-cabal/hie.yaml view
@@ -1,2 +1,2 @@-cradle:- cabal: {component: "lib:simple-cabal"}+cradle: + cabal: {component: "lib:simple-cabal"}
tests/projects/simple-cabal/simple-cabal.cabal view
@@ -1,23 +1,23 @@-cabal-version: >=2.0-name: simple-cabal-version: 0.1.0.0--- synopsis:--- description:--- bug-reports:--- license:-license-file: LICENSE-author: Matthew Pickering-maintainer: matthewtpickering@gmail.com--- copyright:--- category:-build-type: Simple-extra-source-files: CHANGELOG.md--library- exposed-modules: A B- -- other-modules:- -- other-extensions:- build-depends: base >=4.10 && < 5- ghc-options: +RTS -A1m -N -RTS -Wall- -- hs-source-dirs:- default-language: Haskell2010+cabal-version: >=2.0 +name: simple-cabal +version: 0.1.0.0 +-- synopsis: +-- description: +-- bug-reports: +-- license: +license-file: LICENSE +author: Matthew Pickering +maintainer: matthewtpickering@gmail.com +-- copyright: +-- category: +build-type: Simple +extra-source-files: CHANGELOG.md + +library + exposed-modules: A B + -- other-modules: + -- other-extensions: + build-depends: base >=4.10 && < 5 + ghc-options: +RTS -A1m -N -RTS -Wall + -- hs-source-dirs: + default-language: Haskell2010
tests/projects/simple-direct/B.hs view
@@ -1,3 +1,3 @@-module B where--import A+module B where + +import A
tests/projects/simple-direct/hie.yaml view
@@ -1,2 +1,2 @@-cradle:- direct: {arguments: ["-Walaaal", "A", "B"] }+cradle: + direct: {arguments: ["-Walaaal", "A", "B"] }
tests/projects/simple-stack/A.hs view
@@ -1,1 +1,1 @@-module A where+module A where
tests/projects/simple-stack/B.hs view
@@ -1,3 +1,3 @@-module B where--import A+module B where + +import A
tests/projects/simple-stack/CHANGELOG.md view
@@ -1,5 +1,5 @@-# Revision history for simple-cabal--## 0.1.0.0 -- YYYY-mm-dd--* First version. Released on an unsuspecting world.+# Revision history for simple-cabal + +## 0.1.0.0 -- YYYY-mm-dd + +* First version. Released on an unsuspecting world.
tests/projects/simple-stack/Setup.hs view
@@ -1,2 +1,2 @@-import Distribution.Simple-main = defaultMain+import Distribution.Simple +main = defaultMain
tests/projects/simple-stack/cabal.project view
@@ -1,1 +1,1 @@-packages: .+packages: .
tests/projects/simple-stack/hie.yaml view
@@ -1,3 +1,3 @@-cradle:- stack:- component: "simple-stack:lib"+cradle: + stack: + component: "simple-stack:lib"
tests/projects/simple-stack/simple-stack.cabal view
@@ -1,23 +1,23 @@-cabal-version: >=2.0-name: simple-stack-version: 0.1.0.0--- synopsis:--- description:--- bug-reports:--- license:-license-file: LICENSE-author: Matthew Pickering-maintainer: matthewtpickering@gmail.com--- copyright:--- category:-build-type: Simple-extra-source-files: CHANGELOG.md--library- exposed-modules: A B- -- other-modules:- -- other-extensions:- build-depends: base >=4.10 && < 5- ghc-options: -Wall +RTS -A1m -N -RTS- -- hs-source-dirs:- default-language: Haskell2010+cabal-version: >=2.0 +name: simple-stack +version: 0.1.0.0 +-- synopsis: +-- description: +-- bug-reports: +-- license: +license-file: LICENSE +author: Matthew Pickering +maintainer: matthewtpickering@gmail.com +-- copyright: +-- category: +build-type: Simple +extra-source-files: CHANGELOG.md + +library + exposed-modules: A B + -- other-modules: + -- other-extensions: + build-depends: base >=4.10 && < 5 + ghc-options: -Wall +RTS -A1m -N -RTS + -- hs-source-dirs: + default-language: Haskell2010
+ tests/projects/space stack/A.hs view
@@ -0,0 +1,4 @@+module A where++main :: IO ()+main = return ()
+ tests/projects/space stack/B.hs view
@@ -0,0 +1,3 @@+module B where++import A
+ tests/projects/space stack/Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ tests/projects/space stack/hie.yaml view
@@ -0,0 +1,3 @@+cradle:+ stack:+ component: "stackproj:exe:exe"
+ tests/projects/space stack/stackproj.cabal view
@@ -0,0 +1,15 @@+cabal-version: >=2.0+name: stackproj+version: 0.1.0.0+author: Matthew Pickering+maintainer: matthewtpickering@gmail.com+build-type: Simple++executable exe+ other-modules: B+ main-is: A.hs+ -- other-extensions:+ build-depends: base >=4.10 && < 5+ ghc-options: -Wall +RTS -A1m -N -RTS+ -- hs-source-dirs:+ default-language: Haskell2010
wrappers/cabal view
@@ -10,5 +10,5 @@ out "$arg" done else- "${HIE_BIOS_GHC}" ${HIE_BIOS_GHC_ARGS} "$@"+ "ghc" "$@" fi
wrappers/cabal.hs view
@@ -9,8 +9,6 @@ main = do args <- getArgs output_file <- getEnv "HIE_BIOS_OUTPUT"- ghcPath <- getEnv "HIE_BIOS_GHC"- ghcPathArgs <- fmap (maybe [] words) (lookupEnv "HIE_BIOS_GHC_ARGS") case args of "--interactive":_ -> do h <- openFile output_file AppendMode@@ -18,6 +16,6 @@ mapM_ (hPutStrLn h) args hClose h _ -> do- ph <- spawnProcess ghcPath (ghcPathArgs ++ args)+ ph <- spawnProcess "ghc" (args) code <- waitForProcess ph exitWith code