hie-bios 0.6.3 → 0.7.0
raw patch · 27 files changed
+438/−94 lines, 27 filesdep ~aesondep ~base16-bytestringdep ~yamlPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: aeson, base16-bytestring, yaml
API changes (from Hackage documentation)
- HIE.Bios.Config: [component] :: CradleType a -> Maybe String
+ HIE.Bios.Config: [cabalType] :: CradleType a -> !CabalType
+ HIE.Bios.Config: [defaultCabal] :: CradleType a -> !CabalType
+ HIE.Bios.Config: [defaultStack] :: CradleType a -> !StackType
+ HIE.Bios.Config: [stackType] :: CradleType a -> !StackType
+ HIE.Bios.Config: [subCabalComponents] :: CradleType a -> [(FilePath, CabalType)]
+ HIE.Bios.Config: [subStackComponents] :: CradleType a -> [(FilePath, StackType)]
+ HIE.Bios.Config: cabalComponent :: CabalType -> Maybe String
+ HIE.Bios.Config: data CabalType
+ HIE.Bios.Config: data StackType
+ HIE.Bios.Config: instance GHC.Base.Monoid HIE.Bios.Config.CabalType
+ HIE.Bios.Config: instance GHC.Base.Monoid HIE.Bios.Config.StackType
+ HIE.Bios.Config: instance GHC.Base.Semigroup HIE.Bios.Config.CabalType
+ HIE.Bios.Config: instance GHC.Base.Semigroup HIE.Bios.Config.StackType
+ HIE.Bios.Config: instance GHC.Classes.Eq HIE.Bios.Config.CabalType
+ HIE.Bios.Config: instance GHC.Classes.Eq HIE.Bios.Config.StackType
+ HIE.Bios.Config: instance GHC.Show.Show HIE.Bios.Config.CabalType
+ HIE.Bios.Config: instance GHC.Show.Show HIE.Bios.Config.StackType
+ HIE.Bios.Config: pattern CabalType :: Maybe String -> CabalType
+ HIE.Bios.Config: pattern StackType :: Maybe String -> Maybe String -> StackType
+ HIE.Bios.Config: stackComponent :: StackType -> Maybe String
+ HIE.Bios.Config: stackYaml :: StackType -> Maybe String
- HIE.Bios.Config: Cabal :: Maybe String -> CradleType a
+ HIE.Bios.Config: Cabal :: !CabalType -> CradleType a
- HIE.Bios.Config: CabalMulti :: [(FilePath, String)] -> CradleType a
+ HIE.Bios.Config: CabalMulti :: !CabalType -> [(FilePath, CabalType)] -> CradleType a
- HIE.Bios.Config: Stack :: Maybe String -> CradleType a
+ HIE.Bios.Config: Stack :: !StackType -> CradleType a
- HIE.Bios.Config: StackMulti :: [(FilePath, String)] -> CradleType a
+ HIE.Bios.Config: StackMulti :: !StackType -> [(FilePath, StackType)] -> CradleType a
Files
- ChangeLog.md +11/−0
- README.md +57/−6
- hie-bios.cabal +19/−2
- src/HIE/Bios/Config.hs +128/−32
- src/HIE/Bios/Cradle.hs +39/−29
- tests/BiosTests.hs +21/−10
- tests/ParserTests.hs +36/−14
- tests/configs/keys-not-unique-fails.yaml +12/−0
- tests/configs/multi-stack-with-yaml.yaml +8/−0
- tests/configs/stack-with-both.yaml +4/−0
- tests/configs/stack-with-yaml.yaml +3/−0
- tests/projects/multi-stack-with-yaml/appA/Setup.hs +2/−0
- tests/projects/multi-stack-with-yaml/appA/appA.cabal +12/−0
- tests/projects/multi-stack-with-yaml/appA/src/Lib.hs +1/−0
- tests/projects/multi-stack-with-yaml/appB/Setup.hs +2/−0
- tests/projects/multi-stack-with-yaml/appB/appB.cabal +12/−0
- tests/projects/multi-stack-with-yaml/appB/src/Lib.hs +1/−0
- tests/projects/multi-stack-with-yaml/hie.yaml +16/−0
- tests/projects/multi-stack/multi-stack.cabal +0/−0
- tests/projects/simple-bios-shell/hie.yaml +4/−0
- tests/projects/simple-bios/hie-bios-deps.sh +1/−0
- tests/projects/simple-bios/hie.yaml +6/−1
- tests/projects/stack-with-yaml/Setup.hs +2/−0
- tests/projects/stack-with-yaml/app/Main.hs +4/−0
- tests/projects/stack-with-yaml/hie.yaml +9/−0
- tests/projects/stack-with-yaml/src/Lib.hs +5/−0
- tests/projects/stack-with-yaml/stack-with-yaml.cabal +23/−0
ChangeLog.md view
@@ -1,5 +1,16 @@ # ChangeLog hie-bios +## 2020-08-27 - 0.7.0++### New Features++* Allow specifying a stack.yaml for stack configurations [#230](https://github.com/mpickering/hie-bios/pull/230)+* Pass HIE_BIOS_ARG to the dependencies program [#235](https://github.com/mpickering/hie-bios/pull/235)++### API Changes++* Change Config CradleType+ ## 2020-08-08 - 0.6.3 ### API Addition
README.md view
@@ -73,6 +73,16 @@ This configuration suffices if your whole project can be loaded by the command `stack repl`. As a rule of thumb, this works if the project consists of only one executable, one library and one test-suite. +Some projects have multiple `stack-*.yaml` files for multiple versions of ghc/resolvers. In this case you+can specify an alternate relative file to use by using the `stackYaml` option. The path is relative to the+configuration file.++```yaml+cradle:+ stack:+ stackYaml: "./stack-8.8.3.yaml"+```+ If your project is more complicated, you need to specify which [components](https://docs.haskellstack.org/en/stable/build_command/#components) you want to load. A component is, roughly speaking, a library/executable/test-suite or benchmark in `stack`. You can view the components/targets of a stack project by executing the command: ``` sh $ stack ide targets@@ -126,6 +136,26 @@ This way we specified which component needs to be compiled given a source file for our whole project. +If you use both, multiple components and an alternate `stack.yaml` file, there is a way to specify defaults+for the path-specific configurations.++```yaml+cradle:+ stack:+ stackYaml: "stack-8.3.3.yaml"+ components:+ - path: "./src"+ component: "hie-bios:lib"+ - path: "./exe"+ component: "hie-bios:exe:hie-bios"+ - path: "./tests/BiosTests.hs"+ component: "hie-bios:test:hie-bios"+ - path: "./tests/ParserTests.hs"+ component: "hie-bios:test:parser-tests"+```++A word of warning: Due to current restrictions in the language server, as mentioned in [this bug report](https://github.com/haskell/haskell-language-server/issues/268#issuecomment-667640809) all referenced stack.yaml files must specify the same version of GHC, as only one version of ghcide is loaded at a time per workspace root. This restriction might be lifted in the future.+ #### Debugging a `stack` cradle If you find that `hie-bios` can't load a certain component or file, run `stack repl` and `stack repl <component name>` to see if `stack` succeeds in building your project. Chances are that there is a problem in your project and if you fix that, `hie-bios` will succeed to load it.@@ -267,6 +297,25 @@ * The filepath can be a file. * This is convenient if components are overlapping. +Similarly to `multi-stack` configurations, you can also specify multiple components using a `components` subkey.+While this is currently not used for anything, this syntax gives you a place to put defaults, directly under+the `cabal` entry.++```yaml+cradle:+ cabal:+ # Reserved for future default options+ components:+ - path: "./src"+ component: "lib:hie-bios"+ - path: "./exe"+ component: "exe:hie-bios"+ - path: "./tests/BiosTests.hs"+ component: "test:hie-bios"+ - path: "./tests/ParserTests.hs"+ component: "test:parser-tests"+```+ This way we specified which component needs to be compiled given a certain source file for our whole project. #### Debugging a `cabal` cradle@@ -523,13 +572,15 @@ ```yaml cradle: bios:- shell: build-tool flags $HIE_BIOS_ARG- dependency-shell: build-tool dependencies+ shell: build-tool flags $HIE_BIOS_ARG > $HIE_BIOS_OUTPUT+ dependency-shell: build-tool dependencies $HIE_BIOS_ARG > $HIE_BIOS_OUTPUT ``` -The dependency program or command is executed with no parameters and it is-expected to output on stdout on each line exactly one filepath relative-to the root of the cradle, not relative to the location of the program.+The dependency program or command is executed in a similar fashion to the bios+program, using the environment variables HIE_BIOS_ARG and HIE_BIOS_OUTPUT to+communicate respectively the path of the source file being loaded, and the path+where the set of dependency paths should be written, one per line,+and relative to the root of the cradle, not to the location of the program. Programs and shell commands for flags and dependencies can be mixed and matched. @@ -547,7 +598,7 @@ program: "program to run" dependency-program: "optional program to run" shell: build-tool flags $HIE_BIOS_ARG- dependency-shell: build-tool dependencies+ dependency-shell: build-tool dependencies $HIE_BIOS_ARG with-ghc: "optional path to ghc" direct: arguments: ["list","of","ghc","arguments"]
hie-bios.cabal view
@@ -1,6 +1,6 @@ Cabal-Version: 2.2 Name: hie-bios-Version: 0.6.3+Version: 0.7.0 Author: Matthew Pickering <matthewtpickering@gmail.com> Maintainer: Matthew Pickering <matthewtpickering@gmail.com> License: BSD-3-Clause@@ -31,12 +31,16 @@ tests/configs/direct.yaml tests/configs/multi.yaml tests/configs/multi-ch.yaml+ tests/configs/multi-stack-with-yaml.yaml+ tests/configs/keys-not-unique-fails.yaml tests/configs/nested-cabal-multi.yaml tests/configs/nested-stack-multi.yaml tests/configs/none.yaml tests/configs/obelisk.yaml tests/configs/stack-config.yaml tests/configs/stack-multi.yaml+ tests/configs/stack-with-both.yaml+ tests/configs/stack-with-yaml.yaml tests/projects/symlink-test/a/A.hs tests/projects/symlink-test/hie.yaml tests/projects/multi-direct/A.hs@@ -94,6 +98,7 @@ tests/projects/simple-bios/A.hs tests/projects/simple-bios/B.hs tests/projects/simple-bios/hie-bios.sh+ tests/projects/simple-bios/hie-bios-deps.sh tests/projects/simple-bios/hie.yaml tests/projects/simple-bios-ghc/A.hs tests/projects/simple-bios-ghc/B.hs@@ -145,7 +150,18 @@ tests/projects/implicit-stack-multi/other-package/Setup.hs tests/projects/implicit-stack-multi/other-package/other-package.cabal tests/projects/implicit-stack-multi/other-package/Main.hs-+ tests/projects/multi-stack-with-yaml/appA/Setup.hs+ tests/projects/multi-stack-with-yaml/appA/appA.cabal+ tests/projects/multi-stack-with-yaml/appA/src/Lib.hs+ tests/projects/multi-stack-with-yaml/appB/Setup.hs+ tests/projects/multi-stack-with-yaml/appB/appB.cabal+ tests/projects/multi-stack-with-yaml/appB/src/Lib.hs+ tests/projects/multi-stack-with-yaml/hie.yaml+ tests/projects/stack-with-yaml/Setup.hs+ tests/projects/stack-with-yaml/app/Main.hs+ tests/projects/stack-with-yaml/hie.yaml+ tests/projects/stack-with-yaml/stack-with-yaml.cabal+ tests/projects/stack-with-yaml/src/Lib.hs tested-with: GHC==8.10.1, GHC==8.8.3, GHC==8.6.5, GHC==8.4.4 @@ -216,6 +232,7 @@ base, filepath, hie-bios,+ hspec-expectations, tasty, tasty-hunit, text,
src/HIE/Bios/Config.hs view
@@ -2,21 +2,47 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE ViewPatterns #-}+{-# LANGUAGE LambdaCase #-} -- | Logic and datatypes for parsing @hie.yaml@ files. module HIE.Bios.Config( readConfig, Config(..), CradleConfig(..),+ CabalType,+ pattern CabalType,+ cabalComponent,+ StackType,+ pattern StackType,+ stackComponent,+ stackYaml, CradleType(..), Callable(..) ) where +import Control.Exception import qualified Data.Text as T import qualified Data.Vector as V import qualified Data.HashMap.Strict as Map+import Data.Maybe (mapMaybe)+import Data.Semigroup import Data.Foldable (foldrM)+import Data.Aeson (JSONPath) import Data.Yaml+import Data.Yaml.Internal (Warning(..)) +type MLast a = Maybe (Last a)++viewLast :: MLast a -> Maybe a+viewLast (Just l) = Just $ getLast l+viewLast Nothing = Nothing++pattern MLast :: Maybe a -> MLast a+pattern MLast m <- (viewLast -> m) where+ MLast (Just l) = Just $ Last l+ MLast Nothing = Nothing+ -- | Configuration that can be used to load a 'Cradle'. -- A configuration has roughly the following form: --@@ -43,11 +69,45 @@ data Callable = Program FilePath | Command String deriving (Show, Eq) +data CabalType+ = CabalType_ { _cabalComponent :: !(MLast String) }+ deriving (Eq)++instance Semigroup CabalType where+ CabalType_ cr <> CabalType_ cl = CabalType_ (cr <> cl)++instance Monoid CabalType where+ mempty = CabalType_ mempty++pattern CabalType :: Maybe String -> CabalType+pattern CabalType { cabalComponent } = CabalType_ (MLast cabalComponent)+{-# COMPLETE CabalType #-}++instance Show CabalType where+ show = show . Cabal++data StackType+ = StackType_ { _stackComponent :: !(MLast String) , _stackYaml :: !(MLast String) }+ deriving (Eq)++instance Semigroup StackType where+ StackType_ cr yr <> StackType_ cl yl = StackType_ (cr <> cl) (yr <> yl)++instance Monoid StackType where+ mempty = StackType_ mempty mempty++pattern StackType :: Maybe String -> Maybe String -> StackType+pattern StackType { stackComponent, stackYaml } = StackType_ (MLast stackComponent) (MLast stackYaml)+{-# COMPLETE StackType #-}++instance Show StackType where+ show = show . Stack+ data CradleType a- = Cabal { component :: Maybe String }- | CabalMulti [ (FilePath, String) ]- | Stack { component :: Maybe String }- | StackMulti [ (FilePath, String) ]+ = Cabal { cabalType :: !CabalType }+ | CabalMulti { defaultCabal :: !CabalType, subCabalComponents :: [ (FilePath, CabalType) ] }+ | Stack { stackType :: !StackType }+ | StackMulti { defaultStack :: !StackType, subStackComponents :: [ (FilePath, StackType) ] } -- Bazel and Obelisk used to be supported but bit-rotted and no users have complained. -- They can be added back if a user -- | Bazel@@ -73,10 +133,10 @@ 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 (Cabal comp) = "Cabal {component = " ++ show (cabalComponent comp) ++ "}"+ show (CabalMulti d a) = "CabalMulti {defaultCabal = " ++ show d ++ ", subCabalComponents = " ++ show a ++ "}"+ show (Stack comp) = "Stack {component = " ++ show (stackComponent comp) ++ ", stackYaml = " ++ show (stackYaml comp) ++ "}"+ show (StackMulti d a) = "StackMulti {defaultStack = " ++ show d ++ ", subStackComponents = " ++ show a ++ "}" show Bios { call, depsCall } = "Bios {call = " ++ show call ++ ", depsCall = " ++ show depsCall ++ "}" show (Direct args) = "Direct {arguments = " ++ show args ++ "}" show None = "None"@@ -96,38 +156,61 @@ | 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)+parseSingleOrMultiple+ :: Monoid x+ => (x -> CradleType a)+ -> (x -> [(FilePath, x)] -> CradleType a)+ -> (Map.HashMap T.Text Value -> Parser x) -> 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+parseSingleOrMultiple single multiple parse = doParse where+ 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)+ = (T.unpack prefix,) <$> parse (Map.delete "path" v) | 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."+ = fail "Expected an object with a path key"+ parseArray = foldrM (\v cs -> (: cs) <$> parseOne v) []+ doParse (Object v)+ | Just (Array x) <- Map.lookup "components" v+ = do+ d <- parse (Map.delete "components" v)+ xs <- parseArray x+ return $ multiple d xs+ | Just _ <- Map.lookup "components" v+ = fail "Expected components to be an array of subcomponents"+ | Nothing <- Map.lookup "components" v+ = single <$> parse v+ doParse (Array x)+ = do+ xs <- parseArray x+ return $ multiple mempty xs+ doParse Null = single <$> parse Map.empty+ doParse _ = fail "Configuration is expected to be an object or an array of objects." parseStack :: Value -> Parser (CradleType a)-parseStack = parseStackOrCabal Stack StackMulti+parseStack = parseSingleOrMultiple Stack StackMulti $+ \case x | Map.size x == 2+ , Just (String component) <- Map.lookup "component" x+ , Just (String syaml) <- Map.lookup "stackYaml" x+ -> return $ StackType (Just $ T.unpack component) (Just $ T.unpack syaml)+ | Map.size x == 1, Just (String component) <- Map.lookup "component" x+ -> return $ StackType (Just $ T.unpack component) Nothing+ | Map.size x == 1, Just (String syaml) <- Map.lookup "stackYaml" x+ -> return $ StackType Nothing (Just $ T.unpack syaml)+ | Map.null x+ -> return $ StackType Nothing Nothing+ | otherwise+ -> fail "Not a valid Stack configuration, following keys are allowed: component, stackYaml" parseCabal :: Value -> Parser (CradleType a)-parseCabal = parseStackOrCabal Cabal CabalMulti+parseCabal = parseSingleOrMultiple Cabal CabalMulti $+ \case x | Map.size x == 1, Just (String component) <- Map.lookup "component" x+ -> return $ CabalType (Just $ T.unpack component)+ | Map.null x+ -> return $ CabalType Nothing+ | otherwise+ -> fail "Not a valid Cabal configuration, following keys are allowed: component" parseBios :: Value -> Parser (CradleType a) parseBios (Object x) =@@ -206,4 +289,17 @@ -- 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+readConfig fp = do+ result <- decodeFileWithWarnings fp+ either throwIO failOnAnyDuplicate result+ where+ failOnAnyDuplicate :: ([Warning], Config a) -> IO (Config a)+ failOnAnyDuplicate (warnings, config) = do+ _ <- case mapMaybe failOnDuplicate warnings of+ dups@(_:_) -> throwIO $ InvalidYaml $ Just $ YamlException+ $ "Duplicate keys are not allowed, found: " ++ show dups+ _ -> return ()+ return config+ -- future proofing in case more warnings are added+ failOnDuplicate :: Warning -> Maybe JSONPath+ failOnDuplicate (DuplicateKey a) = Just a
src/HIE/Bios/Cradle.hs view
@@ -55,6 +55,7 @@ import qualified Data.Conduit as C import qualified Data.Conduit.Text as C import qualified Data.Text as T+import qualified Data.HashMap.Strict as Map import Data.Maybe (fromMaybe, maybeToList) import GHC.Fingerprint (fingerprintString) @@ -93,17 +94,17 @@ getCradle :: (b -> Cradle a) -> (CradleConfig b, FilePath) -> Cradle a getCradle buildCustomCradle (cc, wdir) = addCradleDeps cradleDeps $ case cradleType cc of- Cabal mc -> cabalCradle wdir mc- CabalMulti ms ->+ Cabal CabalType{ cabalComponent = mc } -> cabalCradle wdir mc+ CabalMulti dc ms -> getCradle buildCustomCradle $ (CradleConfig cradleDeps- (Multi [(p, CradleConfig [] (Cabal (Just c))) | (p, c) <- ms])+ (Multi [(p, CradleConfig [] (Cabal $ dc <> c)) | (p, c) <- ms]) , wdir)- Stack mc -> stackCradle wdir mc- StackMulti ms ->+ Stack StackType{ stackComponent = mc, stackYaml = syaml} -> stackCradle wdir mc (fromMaybe "stack.yaml" syaml)+ StackMulti ds ms -> getCradle buildCustomCradle $ (CradleConfig cradleDeps- (Multi [(p, CradleConfig [] (Stack (Just c))) | (p, c) <- ms])+ (Multi [(p, CradleConfig [] (Stack $ ds <> c)) | (p, c) <- ms]) , wdir) -- Bazel -> rulesHaskellCradle wdir -- Obelisk -> obeliskCradle wdir@@ -143,8 +144,8 @@ (Bios (Program $ wdir </> ".hie-bios") Nothing Nothing, wdir)) <$> biosWorkDir fp -- <|> (Obelisk,) <$> obeliskWorkDir fp -- <|> (Bazel,) <$> rulesHaskellWorkDir fp- <|> (stackExecutable >> (Stack Nothing,) <$> stackWorkDir fp)- <|> ((Cabal Nothing,) <$> cabalWorkDir fp)+ <|> (stackExecutable >> (Stack $ StackType Nothing Nothing,) <$> stackWorkDir fp)+ <|> ((Cabal $ CabalType Nothing,) <$> cabalWorkDir fp) yamlConfig :: FilePath -> MaybeT IO FilePath@@ -349,14 +350,14 @@ biosWorkDir :: FilePath -> MaybeT IO FilePath biosWorkDir = findFileUpwards (".hie-bios" ==) -biosDepsAction :: LoggingFunction -> FilePath -> Maybe Callable -> IO [FilePath]-biosDepsAction l wdir (Just biosDepsCall) = do- biosDeps' <- callableToProcess biosDepsCall Nothing+biosDepsAction :: LoggingFunction -> FilePath -> Maybe Callable -> FilePath -> IO [FilePath]+biosDepsAction l wdir (Just biosDepsCall) fp = do+ biosDeps' <- callableToProcess biosDepsCall (Just fp) (ex, sout, serr, args) <- readProcessWithOutputFile l wdir biosDeps' case ex of ExitFailure _ -> error $ show (ex, sout, serr) ExitSuccess -> return args-biosDepsAction _ _ Nothing = return []+biosDepsAction _ _ Nothing _ = return [] biosAction :: FilePath -> Callable@@ -367,7 +368,7 @@ biosAction wdir bios bios_deps l fp = do bios' <- callableToProcess bios (Just fp) (ex, _stdo, std, res) <- readProcessWithOutputFile l wdir bios'- deps <- biosDepsAction l wdir bios_deps+ deps <- biosDepsAction l wdir bios_deps fp -- 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.@@ -553,16 +554,16 @@ ------------------------------------------------------------------------ -- | Stack Cradle -- Works for by invoking `stack repl` with a wrapper script-stackCradle :: FilePath -> Maybe String -> Cradle a-stackCradle wdir mc =+stackCradle :: FilePath -> Maybe String -> FilePath -> Cradle a+stackCradle wdir mc syaml = Cradle { cradleRootDir = wdir , cradleOptsProg = CradleAction { actionName = Types.Stack- , runCradle = stackAction wdir mc+ , runCradle = stackAction wdir mc syaml , runGhcCmd = \args -> readProcessWithCwd- wdir "stack" (["exec", "--silent", "ghc", "--"] <> args) ""+ wdir "stack" (["--stack-yaml", syaml, "exec", "--silent", "ghc", "--"] <> args) "" } } @@ -575,32 +576,32 @@ -- a '.cabal' file. -- -- Found dependencies are relative to 'rootDir'.-stackCradleDependencies :: FilePath -> FilePath -> IO [FilePath]-stackCradleDependencies wdir componentDir = do+stackCradleDependencies :: FilePath -> FilePath -> FilePath -> IO [FilePath]+stackCradleDependencies wdir componentDir syaml = do let relFp = makeRelative wdir componentDir cabalFiles' <- findCabalFiles componentDir let cabalFiles = map (relFp </>) cabalFiles'- return $ map normalise $ cabalFiles ++ [relFp </> "package.yaml", "stack.yaml"]+ return $ map normalise $ cabalFiles ++ [relFp </> "package.yaml", syaml] -stackAction :: FilePath -> Maybe String -> LoggingFunction -> FilePath -> IO (CradleLoadResult ComponentOptions)-stackAction work_dir mc l _fp = do- let ghcProcArgs = ("stack", ["exec", "ghc", "--"])+stackAction :: FilePath -> Maybe String -> FilePath -> LoggingFunction -> FilePath -> IO (CradleLoadResult ComponentOptions)+stackAction work_dir mc syaml l _fp = do+ let ghcProcArgs = ("stack", ["--stack-yaml", syaml, "exec", "ghc", "--"]) -- Same wrapper works as with cabal withCabalWrapperTool ghcProcArgs work_dir $ \wrapper_fp -> do (ex1, _stdo, stde, args) <- readProcessWithOutputFile l work_dir $- proc "stack" $ ["repl", "--no-nix-pure", "--with-ghc", wrapper_fp]+ proc "stack" $ ["--stack-yaml", syaml, "repl", "--no-nix-pure", "--with-ghc", wrapper_fp] ++ [ comp | Just comp <- [mc] ] (ex2, pkg_args, stdr, _) <- readProcessWithOutputFile l work_dir $- proc "stack" ["path", "--ghc-package-path"]+ proc "stack" ["--stack-yaml", syaml, "path", "--ghc-package-path"] let split_pkgs = concatMap splitSearchPath pkg_args pkg_ghc_args = concatMap (\p -> ["-package-db", p] ) split_pkgs case processCabalWrapperArgs args of Nothing -> do -- Best effort. Assume the working directory is the -- the root of the component, so we are right in trivial cases at least.- deps <- stackCradleDependencies work_dir work_dir+ deps <- stackCradleDependencies work_dir work_dir syaml pure $ CradleFail (CradleError deps ex1 $ [ "Failed to parse result of calling stack" ]@@ -609,7 +610,7 @@ ) Just (componentDir, ghc_args) -> do- deps <- stackCradleDependencies work_dir componentDir+ deps <- stackCradleDependencies work_dir componentDir syaml pure $ makeCradleResult ( combineExitCodes [ex1, ex2] , stde ++ stdr, componentDir@@ -736,6 +737,14 @@ getFiles = filter p <$> getDirectoryContents dir doesPredFileExist file = doesFileExist $ dir </> file +-- Some environments (e.g. stack exec) include GHC_PACKAGE_PATH.+-- Cabal v2 *will* complain, even though or precisely because it ignores them+-- Unset them from the environment to sidestep this+getCleanEnvironment :: IO [(String, String)]+getCleanEnvironment = do+ e <- getEnvironment+ return $ Map.toList $ Map.delete "GHC_PACKAGE_PATH" $ Map.fromList e+ -- | Call a given process. -- * A special file is created for the process to write to, the process can discover the name of -- the file by reading the @HIE_BIOS_OUTPUT@ environment variable. The contents of this file is@@ -749,7 +758,7 @@ -> CreateProcess -- ^ Parameters for the process to be executed. -> IO (ExitCode, [String], [String], [String]) readProcessWithOutputFile l work_dir cp = do- old_env <- getEnvironment+ old_env <- getCleanEnvironment withHieBiosOutput old_env $ \output_file -> do -- Pipe stdout directly into the logger@@ -797,7 +806,8 @@ -- | Wrapper around 'readCreateProcess' that sets the working directory readProcessWithCwd :: FilePath -> FilePath -> [String] -> String -> IO (CradleLoadResult String) readProcessWithCwd dir cmd args stdi = do- let createProc = (proc cmd args) { cwd = Just dir }+ cleanEnv <- getCleanEnvironment+ let createProc = (proc cmd args) { cwd = Just dir, env = Just cleanEnv } mResult <- optional $ readCreateProcessWithExitCode createProc stdi case mResult of Just (ExitSuccess, stdo, _) -> pure $ CradleSuccess stdo
tests/BiosTests.hs view
@@ -106,6 +106,7 @@ length cradleErrorStderr `shouldBe` 1 head cradleErrorStderr `shouldStartWith` "Couldn't execute myGhc") , testCaseSteps "simple-bios-shell" $ testDirectory isBiosCradle "./tests/projects/simple-bios-shell" "B.hs"+ , testCaseSteps "simple-bios-shell-deps" $ testLoadCradleDependencies isBiosCradle "./tests/projects/simple-bios-shell" "B.hs" (assertEqual "dependencies" ["hie.yaml"]) , testCaseSteps "simple-cabal" $ testDirectory isCabalCradle "./tests/projects/simple-cabal" "B.hs" , testCaseSteps "simple-direct" $ testDirectory isDirectCradle "./tests/projects/simple-direct" "B.hs" , testCaseSteps "nested-cabal" $ testLoadCradleDependencies isCabalCradle "./tests/projects/nested-cabal" "sub-comp/Lib.hs"@@ -144,6 +145,12 @@ , testCaseSteps "nested-stack2" $ testLoadCradleDependencies isStackCradle "./tests/projects/nested-stack" "MyLib.hs" (\deps -> deps `shouldMatchList` ["nested-stack.cabal", "package.yaml", "stack.yaml"] )+ , testCaseSteps "stack-with-yaml" {- tests if both components can be loaded -}+ $ testDirectory isStackCradle "./tests/projects/stack-with-yaml" "app/Main.hs"+ >> testDirectory isStackCradle "./tests/projects/stack-with-yaml" "src/Lib.hs"+ , testCaseSteps "multi-stack-with-yaml" {- tests if both components can be loaded -}+ $ testDirectory isStackCradle "./tests/projects/multi-stack-with-yaml" "appA/src/Lib.hs"+ >> testDirectory isStackCradle "./tests/projects/multi-stack-with-yaml" "appB/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@@ -175,6 +182,7 @@ | not isWindows = [ testCaseSteps "simple-bios" $ testDirectory isBiosCradle "./tests/projects/simple-bios" "B.hs" , testCaseSteps "simple-bios-ghc" $ testDirectory isBiosCradle "./tests/projects/simple-bios-ghc" "B.hs"+ , testCaseSteps "simple-bios-deps" $ testLoadCradleDependencies isBiosCradle "./tests/projects/simple-bios" "B.hs" (assertEqual "dependencies" ["hie-bios.sh", "hie.yaml"]) ] | otherwise = []@@ -338,18 +346,21 @@ writeStackYamlFiles :: IO () writeStackYamlFiles =- forM_ stackProjects $ \(proj, pkgs) ->- writeFile (proj </> "stack.yaml") (stackYaml stackYamlResolver pkgs)+ forM_ stackProjects $ \(proj, syaml, pkgs) ->+ writeFile (proj </> syaml) (stackYaml stackYamlResolver pkgs) -stackProjects :: [(FilePath, [FilePath])]+stackProjects :: [(FilePath, FilePath, [FilePath])] stackProjects =- [ ("tests" </> "projects" </> "multi-stack", ["."])- , ("tests" </> "projects" </> "failing-stack", ["."])- , ("tests" </> "projects" </> "simple-stack", ["."])- , ("tests" </> "projects" </> "nested-stack", [".", "./sub-comp"])- , ("tests" </> "projects" </> "space stack", ["."])- , ("tests" </> "projects" </> "implicit-stack", ["."])- , ("tests" </> "projects" </> "implicit-stack-multi", ["."])+ [ ("tests" </> "projects" </> "multi-stack", "stack.yaml", ["."])+ , ("tests" </> "projects" </> "failing-stack", "stack.yaml", ["."])+ , ("tests" </> "projects" </> "simple-stack", "stack.yaml", ["."])+ , ("tests" </> "projects" </> "nested-stack", "stack.yaml", [".", "./sub-comp"])+ , ("tests" </> "projects" </> "space stack", "stack.yaml", ["."])+ , ("tests" </> "projects" </> "implicit-stack", "stack.yaml", ["."])+ , ("tests" </> "projects" </> "implicit-stack-multi", "stack.yaml", ["."])+ , ("tests" </> "projects" </> "implicit-stack-multi", "stack.yaml", ["."])+ , ("tests" </> "projects" </> "multi-stack-with-yaml", "stack-alt.yaml", ["appA", "appB"])+ , ("tests" </> "projects" </> "stack-with-yaml", "stack-alt.yaml", ["."]) ] stackYaml :: String -> [FilePath] -> String
tests/ParserTests.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE OverloadedStrings #-} module Main where +import Test.Hspec.Expectations import Test.Tasty import Test.Tasty.HUnit import HIE.Bios.Config@@ -10,6 +11,7 @@ import qualified Data.Text as T import System.FilePath import Control.Applicative ( (<|>) )+import Control.Exception configDir :: FilePath configDir = "tests/configs"@@ -17,36 +19,48 @@ 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 "cabal-1.yaml" (noDeps (Cabal $ CabalType (Just "lib:hie-bios")))+ assertParser "stack-config.yaml" (noDeps (Stack $ StackType Nothing Nothing)) --assertParser "bazel.yaml" (noDeps Bazel) assertParser "bios-1.yaml" (noDeps (Bios (Program "program") Nothing Nothing)) assertParser "bios-2.yaml" (noDeps (Bios (Program "program") (Just (Program "dep-program")) Nothing)) assertParser "bios-3.yaml" (noDeps (Bios (Command "shellcommand") Nothing Nothing)) assertParser "bios-4.yaml" (noDeps (Bios (Command "shellcommand") (Just (Command "dep-shellcommand")) Nothing)) assertParser "bios-5.yaml" (noDeps (Bios (Command "shellcommand") (Just (Program "dep-program")) Nothing))- assertParser "dependencies.yaml" (Config (CradleConfig ["depFile"] (Cabal (Just "lib:hie-bios"))))+ assertParser "dependencies.yaml" (Config (CradleConfig ["depFile"] (Cabal $ CabalType (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 "multi.yaml" (noDeps (Multi [("./src", CradleConfig [] (Cabal $ CabalType (Just "lib:hie-bios")))+ ,("./test", CradleConfig [] (Cabal $ CabalType (Just "test")) ) ])) - assertParser "cabal-multi.yaml" (noDeps (CabalMulti [("./src", "lib:hie-bios")- ,("./", "lib:hie-bios")]))+ assertParser "cabal-multi.yaml" (noDeps (CabalMulti (CabalType Nothing)+ [("./src", CabalType $ Just "lib:hie-bios")+ ,("./", CabalType $ Just "lib:hie-bios")])) - assertParser "stack-multi.yaml" (noDeps (StackMulti [("./src", "lib:hie-bios")- ,("./", "lib:hie-bios")]))+ assertParser "stack-multi.yaml" (noDeps (StackMulti (StackType Nothing Nothing)+ [("./src", StackType (Just "lib:hie-bios") Nothing)+ ,("./", StackType (Just"lib:hie-bios") Nothing)])) assertParser "nested-cabal-multi.yaml" (noDeps (Multi [("./test/testdata", CradleConfig [] None) ,("./", CradleConfig [] (- CabalMulti [("./src", "lib:hie-bios")- ,("./tests", "parser-tests")]))]))+ CabalMulti (CabalType Nothing)+ [("./src", CabalType $ Just "lib:hie-bios")+ ,("./tests", CabalType $ Just "parser-tests")]))])) assertParser "nested-stack-multi.yaml" (noDeps (Multi [("./test/testdata", CradleConfig [] None) ,("./", CradleConfig [] (- StackMulti [("./src", "lib:hie-bios")- ,("./tests", "parser-tests")]))]))+ StackMulti (StackType Nothing Nothing)+ [("./src", StackType (Just "lib:hie-bios") Nothing)+ ,("./tests", StackType (Just "parser-tests") Nothing)]))]))+ assertParser "stack-with-yaml.yaml"+ (noDeps (Stack $ StackType Nothing (Just "stack-8.8.3.yaml")))+ assertParser "stack-with-both.yaml"+ (noDeps (Stack $ StackType (Just "hie-bios:hie") (Just "stack-8.8.3.yaml")))+ assertParser "multi-stack-with-yaml.yaml"+ (noDeps (StackMulti (StackType Nothing (Just "stack-8.8.3.yaml"))+ [("./src", StackType (Just "lib:hie-bios") Nothing)+ ,("./vendor", StackType (Just "parser-tests") Nothing)])) assertCustomParser "ch-cabal.yaml" (noDeps (Other CabalHelperCabal $ simpleCabalHelperYaml "cabal"))@@ -56,9 +70,10 @@ (noDeps (Multi [ ("./src", CradleConfig [] (Other CabalHelperStack $ simpleCabalHelperYaml "stack")) , ("./input", CradleConfig [] (Other CabalHelperCabal $ simpleCabalHelperYaml "cabal"))- , ("./test", CradleConfig [] (Cabal (Just "test")))+ , ("./test", CradleConfig [] (Cabal $ CabalType (Just "test"))) , (".", CradleConfig [] None) ]))+ assertParserFails "keys-not-unique-fails.yaml" invalidYamlException assertParser :: FilePath -> Config Void -> Assertion assertParser fp cc = do@@ -66,6 +81,13 @@ (conf == cc) @? (unlines [("Parser Failed: " ++ fp) , "Expected: " ++ show cc , "Actual: " ++ show conf ])++invalidYamlException :: Selector ParseException+invalidYamlException (InvalidYaml (Just _)) = True+invalidYamlException _ = False++assertParserFails :: Exception e => FilePath -> Selector e -> Assertion+assertParserFails fp es = (readConfig (configDir </> fp) :: IO (Config Void)) `shouldThrow` es assertCustomParser :: FilePath -> Config CabalHelper -> Assertion assertCustomParser fp cc = do
+ tests/configs/keys-not-unique-fails.yaml view
@@ -0,0 +1,12 @@+cradle:+ cabal:+ components:+ - path: "./src"+ component: "lib:hie-bios"+ - path: "./"+ component: "lib:hie-bios"+ components:+ - path: "./src"+ component: "exe:hie-bios"+ - path: "./"+ component: "exe:hie-bios"
+ tests/configs/multi-stack-with-yaml.yaml view
@@ -0,0 +1,8 @@+cradle:+ stack:+ stackYaml: "stack-8.8.3.yaml"+ components:+ - path: "./src"+ component: "lib:hie-bios"+ - path: "./vendor"+ component: "parser-tests"
+ tests/configs/stack-with-both.yaml view
@@ -0,0 +1,4 @@+cradle:+ stack:+ stackYaml: "stack-8.8.3.yaml"+ component: "hie-bios:hie"
+ tests/configs/stack-with-yaml.yaml view
@@ -0,0 +1,3 @@+cradle:+ stack:+ stackYaml: "stack-8.8.3.yaml"
+ tests/projects/multi-stack-with-yaml/appA/Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ tests/projects/multi-stack-with-yaml/appA/appA.cabal view
@@ -0,0 +1,12 @@+cabal-version: >=2.0+name: appA+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
+ tests/projects/multi-stack-with-yaml/appA/src/Lib.hs view
@@ -0,0 +1,1 @@+module Lib where
+ tests/projects/multi-stack-with-yaml/appB/Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ tests/projects/multi-stack-with-yaml/appB/appB.cabal view
@@ -0,0 +1,12 @@+cabal-version: >=2.0+name: appB+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
+ tests/projects/multi-stack-with-yaml/appB/src/Lib.hs view
@@ -0,0 +1,1 @@+module Lib where
+ tests/projects/multi-stack-with-yaml/hie.yaml view
@@ -0,0 +1,16 @@+cradle:+ multi:+ - path: "appA"+ config:+ cradle:+ stack:+ - path: appA/src+ component: appA:lib+ stackYaml: stack-alt.yaml+ - path: "appB"+ config:+ cradle:+ stack:+ - path: appB/src+ component: appB:lib+ stackYaml: stack-alt.yaml
tests/projects/multi-stack/multi-stack.cabal view
tests/projects/simple-bios-shell/hie.yaml view
@@ -8,3 +8,7 @@ ECHO "-Wall" >> %HIE_BIOS_OUTPUT% ECHO "A" >> %HIE_BIOS_OUTPUT% ECHO "B" >> %HIE_BIOS_OUTPUT%+ dependency-shell: |+ echo -n # > nul && ECHO hie.yaml>> %HIE_BIOS_OUTPUT%+ :; echo "hie.yaml" >> $HIE_BIOS_OUTPUT+ :; exit 0
+ tests/projects/simple-bios/hie-bios-deps.sh view
@@ -0,0 +1,1 @@+echo "hie-bios.sh" >> $HIE_BIOS_OUTPUT
tests/projects/simple-bios/hie.yaml view
@@ -1,2 +1,7 @@ cradle:- bios: {program: ./hie-bios.sh }+ bios:+ program: ./hie-bios.sh+ dependency-program: ./hie-bios-deps.sh++dependencies:+ - hie.yaml
+ tests/projects/stack-with-yaml/Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ tests/projects/stack-with-yaml/app/Main.hs view
@@ -0,0 +1,4 @@++import System.Directory (getCurrentDirectory)++main = return ()
+ tests/projects/stack-with-yaml/hie.yaml view
@@ -0,0 +1,9 @@+cradle:+ stack:+ stackYaml: "stack-alt.yaml"+ components:+ - path: ./src+ component: "stack-with-yaml:lib"++ - path: ./app+ component: "stack-with-yaml:exe:stack-with-yaml"
+ tests/projects/stack-with-yaml/src/Lib.hs view
@@ -0,0 +1,5 @@+module Lib where++import System.FilePath ((</>))++foo = "test" </> "me"
+ tests/projects/stack-with-yaml/stack-with-yaml.cabal view
@@ -0,0 +1,23 @@+cabal-version: >=2.0+name: stack-with-yaml+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 stack-with-yaml+ 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