packages feed

codex 0.5.1.2 → 0.5.2.0

raw patch · 8 files changed

+312/−64 lines, 8 filesdep −MissingHdep −eitherdep −monad-loopsdep ~Cabaldep ~basedep ~bytestringnew-uploaderPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies removed: MissingH, either, monad-loops

Dependency ranges changed: Cabal, base, bytestring, hackage-db, process

API changes (from Hackage documentation)

+ Codex.Internal: readStackVersion :: IO Version
+ Codex.Project: allComponentsInBuildOrder' :: LocalBuildInfo -> [ComponentLocalBuildInfo]
+ Codex.Project: findCabalFilePath :: FilePath -> IO (Maybe FilePath)
+ Codex.Project: findLocalPackages :: Int -> FilePath -> IO [WorkspaceProject]
+ Codex.Project: resolveLocalDependencies :: Builder -> FilePath -> [WorkspaceProject] -> IO ProjectDependencies
+ Codex.Project: type Hackage = HackageDB
+ Codex.Project: withinRange' :: Version -> VersionRange -> Bool
- Codex: data Verbosity :: *
+ Codex: data Verbosity
- Codex: type Action = EitherT String IO
+ Codex: type Action = ExceptT String IO
- Codex.Internal: stackListDependencies :: String -> IO [PackageIdentifier]
+ Codex.Internal: stackListDependencies :: String -> String -> IO [PackageIdentifier]
- Codex.Project: type ProjectDependencies = (PackageIdentifier, [PackageIdentifier], [WorkspaceProject])
+ Codex.Project: type ProjectDependencies = (Maybe PackageIdentifier, [PackageIdentifier], [WorkspaceProject])

Files

+ CHANGELOG.md view
@@ -0,0 +1,18 @@+# CHANGELOG++<!-- +When editing this file, please include a link to the PR and/or issue for+the change.  +-->++## 0.5.2.0++This CHANGELOG entry is incomplete, as it is reconstructed from the Git history+since the previous release.++- Fix `codex update` inside of cabal sandbox [#74](https://github.com/aloiscochard/codex/pull/74)+- Look for project packages in sub-directories [#76](https://github.com/aloiscochard/codex/pull/76)+- Improved error message if Hackage path not found [#79](https://github.com/aloiscochard/codex/pull/79)+- Use `stack ls dependencies` to avoid deprecation notice [#81](https://github.com/aloiscochard/codex/pull/81)+- Only use Stack when `.stack-work` is present [#87](https://github.com/aloiscochard/codex/pull/87)+- Support Cabal >= 2.2 [#88](https://github.com/aloiscochard/codex/pull/88)
+ README.md view
@@ -0,0 +1,58 @@+# Codex++[![View on hackage](https://img.shields.io/hackage/v/codex.svg)](http://hackage.haskell.org/package/codex)+[![Build Status](https://travis-ci.org/aloiscochard/codex.svg)](https://travis-ci.org/aloiscochard/codex)+[![Join the chat at https://gitter.im/aloiscochard/codex](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/aloiscochard/codex?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)++This tool download and cache the source code of packages in your local hackage,+it can then use this local cache to generate a `tags` file aggregating the sources of all the dependencies of a given cabal/stack project.++You basically do `codex update` in your project directory and you'll get a file (`codex.tags` by default, or `TAGS` when using+emacs format) that you can use in your favorite text editor.++*By default the generated tags file will include tags of the current project as well, this functionality can be disabled in your `~/.codex` file.*++## Install++Codex is published on [Hackage](http://hackage.haskell.org/package/codex) (and also mirrored on [Stackage](https://www.stackage.org/package/codex) and [NixOS](http://hydra.nixos.org/job/nixpkgs/trunk/haskellPackages.codex.x86_64-linux)).++    cabal install codex++Note: You might have to install binary dependencies manually as cabal does not support them yet!++## Usage++By default `hasktags` will be used, and need to be in the `PATH`, the tagger command can be fully customized in `~/.codex`.++*The configuration file is automatically generated with default values when running the tool for the first time.*++    codex [update] [cache clean] [set tagger [hasktags|ctags]] [set format [vim|emacs|sublime]]++* **update**: Synchronize the tags file in the current project directory (use --force to discard tags file hash)+* **cache clean**: Remove all `tags` file from the local hackage cache+* **set tagger [hasktags|ctags]**: Update the `~/.codex` configuration file for the given tagger+* **set format [vim|emacs|sublime]**: Update the `~/.codex` configuration file for the given format++*Note: codex will browse the parent directory for cabal projects and use them as dependency over hackage when possible.*++## VIM++Put this in your `.vimrc`:++    set tags=tags;/,codex.tags;/+    +**IMPORTANT**: You must use a version >= 7.4 (or you'll get `E431: Format error in tags file "codex.tags"`)++## FAQ++- I get `commitBuffer: invalid argument (invalid character)` when trying to use `codex` under Windows, is this a compatibility problem?++  It's an general issue with codec (http://jaspervdj.be/hakyll/tutorials/faq.html#hgetcontents-invalid-argument-or-commitbuffer-invalid-argument), which can be solved by running the following commands:++      $ /c/Windows/System32/chcp.com 65001+      $ codex cache clean+      $ rm codex.tags+      $ codex update+      +  (ref: https://github.com/aloiscochard/codex/issues/36)+
codex.cabal view
@@ -1,5 +1,5 @@ name:                codex-version:             0.5.1.2+version:             0.5.2.0 synopsis:            A ctags file generator for cabal project dependencies. description:   This tool download and cache the source code of packages in your local hackage,@@ -19,6 +19,8 @@ category:            Development build-type:          Simple cabal-version:       >=1.10+extra-source-files:  README.md+                     CHANGELOG.md  library   default-language:  Haskell2010@@ -33,18 +35,17 @@       ascii-progress      >= 0.3     , base                >= 4.6.0.1    && < 5     , bytestring          >= 0.10.0.2   && < 0.11-    , Cabal               >= 1.18       && < 1.25+    , Cabal               >= 1.18       && < 2.5     , cryptohash          >= 0.11       && < 0.12     , containers          >= 0.5.0.0    && < 0.6     , directory           >= 1.2.0.1    && < 1.4-    , either              >= 4.3.0.1    && < 4.5     , filepath            >= 1.3.0.1    && < 1.5-    , hackage-db          >= 1.22       && < 2+    , hackage-db          >= 1.22       && < 3     , http-client         >= 0.4        && < 0.6     , lens                >= 4.6        && < 5     , machines            >= 0.2        && < 0.7     , machines-directory  >= 0.0.0.2    && < 0.3-    , process             >= 1.2.3      && < 1.5+    , process             >= 1.2.3      && < 1.7     , tar                 >= 0.4.0.1    && < 0.6     , text                >= 1.1.1.3    && < 1.3     , transformers        >= 0.3.0.0    && < 0.6@@ -68,13 +69,9 @@       ascii-progress     , base     , Cabal-    , bytestring     , directory-    , either     , filepath     , hackage-db-    , MissingH            >= 1.2.1.0    && < 1.5-    , monad-loops         >= 0.4.2      && < 0.5     , network             >= 2.6        && < 2.7     , process     , transformers
codex/Main.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE CPP #-} +module Main (main) where+ #if !MIN_VERSION_base(4,8,0) import Control.Applicative ((<$>)) import Data.Traversable (traverse)@@ -9,10 +11,10 @@ import Control.Exception (try, SomeException) import Control.Monad import Control.Monad.IO.Class (liftIO)-import Control.Monad.Trans.Either hiding (left, right)+import Control.Monad.Trans.Except import Data.Either import Data.List-import Data.String.Utils+import qualified Distribution.Hackage.DB as DB import Distribution.Text import Network.Socket (withSocketsDo) import Network.Wreq.Session (withSession)@@ -55,7 +57,7 @@     safe = (try :: IO a -> IO (Either SomeException a))     listDirectory fp = do       xs <- getDirectoryContents fp-      return . fmap (fp </>) $ filter (not . startswith ".") xs+      return . fmap (fp </>) $ filter (not . isPrefixOf ".") xs     removeTagFiles = traverse (safe . removeFile) . fmap (</> "tags")     traverseDirectories = fmap rights . traverse (safe . listDirectory)     builderOp (Stack _) = traverseDirectories . concat@@ -73,26 +75,39 @@  update :: Bool -> Codex -> Builder -> IO () update force cx bldr = displayConsoleRegions $ do-  (project, dependencies, workspaceProjects') <- resolveCurrentProjectDependencies bldr $ hackagePath cx </> "00-index.tar"+#if MIN_VERSION_hackage_db(2,0,0)+  (mpid, dependencies, workspaceProjects') <- case bldr of+       Cabal -> do+         tb <- DB.hackageTarball+         resolveCurrentProjectDependencies bldr tb+       Stack _ -> resolveCurrentProjectDependencies bldr $ hackagePath cx+#else+  (mpid, dependencies, workspaceProjects') <-+    resolveCurrentProjectDependencies bldr (hackagePath cx)+#endif   projectHash <- computeCurrentProjectHash cx-   shouldUpdate <-     if null workspaceProjects' then-      either (const True) id <$> (runEitherT $ isUpdateRequired cx dependencies projectHash)+      either (const True) id <$> (runExceptT $ isUpdateRequired cx dependencies projectHash)     else return True    if force || shouldUpdate then do-    let workspaceProjects = if not $ currentProjectIncluded cx then workspaceProjects'-        else (WorkspaceProject project ".") : workspaceProjects'+    let workspaceProjects = if currentProjectIncluded cx+          then workspaceProjects'+          else filter (("." /=) . workspaceProjectPath) workspaceProjects'     fileExist <- doesFileExist tagsFile     when fileExist $ removeFile tagsFile-    putStrLn $ concat ["Updating ", display project]+    putStrLn ("Updating: " ++ displayPackages mpid workspaceProjects)     results <- withSession $ \s -> do       tick' <- newProgressBar' "Loading tags" (length dependencies)-      traverse (retrying 3 . runEitherT . getTags tick' s) dependencies+      traverse (retrying 3 . runExceptT . getTags tick' s) dependencies     _       <- traverse print . concat $ lefts results-    res     <- runEitherT $ assembly bldr cx dependencies projectHash workspaceProjects tagsFile-    either print (const $ return ()) res+    res     <- runExceptT $ assembly bldr cx dependencies projectHash workspaceProjects tagsFile+    case res of+      Left e -> do+        print e+        exitFailure+      Right _ -> pure ()   else     putStrLn "Nothing to update."   where@@ -102,12 +117,17 @@       Source Tagged   -> tick' >> return ()       Source Untagged -> tags bldr cx i >> tick' >> getTags tick' s i       Archive         -> extract hp i >> tick' >> getTags tick' s i-      Remote          -> liftIO $ eitherT ignore return $ fetch s hp i >> tick' >> getTags tick' s i+      Remote          -> liftIO $ either ignore return <=< runExceptT $ fetch s hp i >> tick' >> getTags tick' s i         where           ignore msg = do             putStrLn $ concat ["codex: *warning* unable to fetch an archive for ", display i]             putStrLn msg             return ()+    displayPackages mpid workspaceProjects =+      case mpid of+        Just p -> display p+        Nothing ->+          unwords (fmap (display . workspaceProjectIdentifier) workspaceProjects)  help :: IO () help = putStrLn $@@ -147,7 +167,8 @@       TaggerNotFound  -> fail' $ "codex: tagger not found."       Ready           -> do         stackFileExists <- doesFileExist $ "." </> "stack.yaml"-        if stackFileExists then do+        stackWorkExists <- doesDirectoryExist $ "." </> ".stack-work"+        if stackFileExists && stackWorkExists then do             (ec, _, _) <- readCreateProcessWithExitCode (shell "which stack") ""             case ec of                 ExitSuccess -> do
codex/Main/Config.hs view
@@ -1,5 +1,7 @@+{-# LANGUAGE CPP #-} module Main.Config where +import Control.Exception (catch) import Data.Yaml import System.Directory import System.FilePath@@ -12,6 +14,10 @@ import qualified Main.Config.Codex3 as C3 import qualified Distribution.Hackage.DB as DB +#if MIN_VERSION_hackage_db(2,0,0)+import qualified Distribution.Hackage.DB.Errors as Errors+#endif+ data ConfigState = Ready | TaggerNotFound  getConfigPath :: IO FilePath@@ -31,7 +37,18 @@ loadConfig :: IO Codex loadConfig = decodeConfig >>= maybe defaultConfig return where   defaultConfig = do+#if MIN_VERSION_hackage_db(2,0,0)+    hp <- DB.hackageTarball+      `catch` \Errors.NoHackageTarballFound -> do+        error $ unlines+          [ "couldn't find a Hackage tarball. This can happen if you use `stack` exclusively,"+          , "or just haven't run `cabal update` yet. To fix it, try running:"+          , ""+          , "    cabal update"+          ]+#else     hp <- DB.hackagePath+#endif     let cx = Codex True (dropFileName hp) defaultStackOpts (taggerCmd Hasktags) True True defaultTagsFileName     encodeConfig cx     return cx
src/Codex.hs view
@@ -11,7 +11,7 @@ import Control.Lens.Review (bimap) import Control.Monad import Control.Monad.IO.Class-import Control.Monad.Trans.Either+import Control.Monad.Trans.Except import Data.Machine import Data.Maybe import Data.List ((\\))@@ -62,7 +62,7 @@ data Status = Source Tagging | Archive | Remote   deriving (Eq, Show) -type Action = EitherT String IO+type Action = ExceptT String IO  data Tagger = Ctags | Hasktags | HasktagsEmacs | HasktagsExtended   deriving (Eq, Show, Read)@@ -84,7 +84,7 @@ tryIO :: IO a -> Action a tryIO io = do   res <- liftIO $ (try :: IO a -> IO (Either SomeException a)) io-  either (left . show) return res+  either (throwE . show) return res  codexHash :: Codex -> String codexHash cfg = md5hash $ show cfg@@ -129,7 +129,7 @@   bs <- tryIO $ do     createDirectoryIfMissing True (packagePath root i)     openLazyURI s url-  either left write bs where+  either throwE write bs where       write bs = fmap (const archivePath) $ tryIO $ BS.writeFile archivePath bs       archivePath = packageArchive root i       url = packageUrl i
src/Codex/Internal.hs view
@@ -10,11 +10,13 @@ import Data.Char (isSpace) import Data.Yaml import Data.Maybe (mapMaybe)+import Data.Version (versionBranch, Version, parseVersion) import Distribution.Package import Distribution.Text import GHC.Generics import System.FilePath import System.Process (shell, readCreateProcess)+import Text.ParserCombinators.ReadP (readP_to_S)  import qualified Data.List as L @@ -80,13 +82,36 @@   s <- readCreateProcess (shell cmd) ""   return $ init s -stackListDependencies :: String -> IO [PackageIdentifier]-stackListDependencies opts = do-    let cmd = concat ["stack ", opts,  " list-dependencies"]-    s <- readCreateProcess (shell cmd) ""-    return $ mapMaybe parsePackageIdentifier $ lines s+stackListDependencies :: String -> String -> IO [PackageIdentifier]+stackListDependencies opts pname = do+  version <- readStackVersion+  let+    cmd =+      case versionBranch version of+        a : b : _+          | a <= 1+          , b <= 7+          -> concat ["stack ", opts, " list-dependencies", pname]+        _+          -> concat ["stack ", opts, " ls dependencies ", pname]+  s <- readCreateProcess (shell cmd) ""+  return $ mapMaybe parsePackageIdentifier $ lines s   where     parsePackageIdentifier line =-        let line' = map (\c -> if c == ' ' then '-' else c)-                        line-        in  simpleParse line'+      let line' = map (\c -> if c == ' ' then '-' else c)+                      line+       in  simpleParse line'++readStackVersion :: IO Version+readStackVersion = do+  s <- readCreateProcess (shell "stack --version") ""+  let+    versionText =+      takeWhile (/= ',') (drop (length "Version ") s)+    parsed =+      readP_to_S parseVersion versionText+  case parsed of+    (v, _) : _ ->+      pure v+    _ ->+      error $ "Failed to parse stack version. Output: " ++ s
src/Codex/Project.hs view
@@ -6,15 +6,26 @@ import Data.Traversable (traverse) #endif +import Control.Applicative ((<|>)) import Control.Exception (try, SomeException)-import Control.Monad+import Control.Monad (filterM)+import Data.Bool (bool) import Data.Function+import Data.List (delete, isPrefixOf, union) import Data.Maybe import Distribution.InstalledPackageInfo+#if MIN_VERSION_hackage_db(2,0,0)+import Distribution.Hackage.DB (HackageDB, cabalFile, readTarball)+#else import Distribution.Hackage.DB (Hackage, readHackage')+#endif import Distribution.Package import Distribution.PackageDescription+#if MIN_VERSION_Cabal(2,2,0)+import Distribution.PackageDescription.Parsec+#else import Distribution.PackageDescription.Parse+#endif import Distribution.Sandbox.Utils (findSandbox) import Distribution.Simple.Configure import Distribution.Simple.LocalBuildInfo@@ -22,20 +33,29 @@ import Distribution.Verbosity import Distribution.Version import System.Directory+import System.Environment (lookupEnv) import System.FilePath+import Text.Read (readMaybe)  import qualified Data.List as List import qualified Data.Map as Map+#if !MIN_VERSION_hackage_db(2,0,0)+import qualified Data.Version as Base+#endif  import Codex.Internal (Builder(..), stackListDependencies) +#if MIN_VERSION_hackage_db(2,0,0)+type Hackage = HackageDB+#endif+ newtype Workspace = Workspace [WorkspaceProject]   deriving (Eq, Show)  data WorkspaceProject = WorkspaceProject { workspaceProjectIdentifier :: PackageIdentifier, workspaceProjectPath :: FilePath }   deriving (Eq, Show) -type ProjectDependencies = (PackageIdentifier, [PackageIdentifier], [WorkspaceProject])+type ProjectDependencies = (Maybe PackageIdentifier, [PackageIdentifier], [WorkspaceProject])  identifier :: GenericPackageDescription -> PackageIdentifier identifier = package . packageDescription@@ -50,15 +70,61 @@  findPackageDescription :: FilePath -> IO (Maybe GenericPackageDescription) findPackageDescription root = do-  contents  <- getDirectoryContents root-  files     <- filterM (doesFileExist . (</>) root) contents-  traverse (readPackageDescription silent) $ fmap (\x -> root </> x) $ List.find (List.isSuffixOf ".cabal") files+  mpath <- findCabalFilePath root+  traverse (+#if MIN_VERSION_Cabal(2,2,0)+    readGenericPackageDescription+#else+    readPackageDescription+#endif+    silent) mpath +-- | Find a regular file ending with ".cabal" within a directory.+findCabalFilePath :: FilePath -> IO (Maybe FilePath)+findCabalFilePath path = do+  paths <- getDirectoryContents path+  case List.find ((&&) <$> dotCabal <*> visible) paths of+    Just p -> do+      let p' = path </> p+      bool Nothing (Just p') <$> doesFileExist p'+    Nothing -> pure Nothing+  where+    dotCabal = (".cabal" ==) . takeExtension+    visible  = not . List.isPrefixOf "."+ resolveCurrentProjectDependencies :: Builder -> FilePath -> IO ProjectDependencies resolveCurrentProjectDependencies bldr hackagePath = do-  ws <- getWorkspace ".."-  resolveProjectDependencies bldr ws hackagePath "."+  mps <- localPackages+  case mps of+    Just ps -> resolveLocalDependencies bldr hackagePath ps+    Nothing -> do+      disableImplicitWorkspace <- isJust <$> lookupEnv "CODEX_DISABLE_WORKSPACE"+      ws <- if disableImplicitWorkspace+        then pure (Workspace [])+        else getWorkspace ".."+      resolveProjectDependencies bldr ws hackagePath "."+  where+    localPackages = do+      mpath <-+        case bldr of+          Cabal -> bool Nothing (Just ".") <$> doesFileExist "cabal.project"+          Stack _ -> pure (Just ".")+      case mpath of+        Nothing   -> pure Nothing+        Just path -> Just <$> findLocalPackages 2 path +-- | Resolve the dependencies of each local project package.+resolveLocalDependencies :: Builder -> FilePath -> [WorkspaceProject] -> IO ProjectDependencies+resolveLocalDependencies bldr hackagePath wps = do+  pids <- foldr mergeDependencies mempty <$> traverse resolve wps+  pure (Nothing, pids, wps)+  where+    resolve p@WorkspaceProject{workspaceProjectPath = packagePath} =+      let ws' = Workspace (delete p wps)+      in resolveProjectDependencies bldr ws' hackagePath packagePath+    mergeDependencies (_, pids, _) pids' =+      pids `union` pids'+ -- TODO Optimize resolveProjectDependencies :: Builder -> Workspace -> FilePath -> FilePath -> IO ProjectDependencies resolveProjectDependencies bldr ws hackagePath root = do@@ -67,8 +133,9 @@   ys <- resolveSandboxDependencies root   let zs   = resolveWorkspaceDependencies ws pd   let wsds = List.filter (shouldOverride xs) $ List.nubBy (on (==) prjId) $ concat [ys, zs]-  let pjds = List.filter (\x -> List.notElem (pkgName x) $ fmap prjId wsds) xs-  return (identifier pd, pjds, wsds) where+  let pjds = List.filter (\x -> (((unPackageName . pkgName) x) /= "rts") && (List.notElem (pkgName x) $ fmap prjId wsds)) xs+  return (Just (identifier pd), pjds, wsds)+  where     shouldOverride xs (WorkspaceProject x _) =       maybe True (\y -> pkgVersion x >= pkgVersion y) $ List.find (\y -> pkgName x == pkgName y) xs     prjId = pkgName . workspaceProjectIdentifier@@ -79,22 +146,53 @@     Cabal -> do       lbi <- withCabal       let ipkgs = installedPkgs lbi-          clbis = snd <$> allComponentsInBuildOrder lbi+          clbis = allComponentsInBuildOrder' lbi           pkgs  = componentPackageDeps =<< clbis           ys = (maybeToList . lookupInstalledPackageId ipkgs) =<< fmap fst pkgs           xs = fmap sourcePackageId $ ys       return xs where         withCabal = getPersistBuildConfig $ root </> "dist"-    Stack cmd -> let self = package (packageDescription pd)-             in  filter (/=self) <$> stackListDependencies cmd+    Stack cmd ->+      filter (/= pid) <$> stackListDependencies cmd pname+  where+    pid = pd & packageDescription & package+    pname = pid & pkgName & unPackageName +allComponentsInBuildOrder' :: LocalBuildInfo -> [ComponentLocalBuildInfo]+allComponentsInBuildOrder' =+#if MIN_VERSION_Cabal(2,0,0)+  allComponentsInBuildOrder+#else+  fmap snd . allComponentsInBuildOrder+#endif+ resolveHackageDependencies :: Hackage -> GenericPackageDescription -> [GenericPackageDescription] resolveHackageDependencies db pd = maybeToList . resolveDependency db =<< allDependencies pd where-  resolveDependency _ (Dependency (PackageName name) versionRange) = do-    pdsByVersion <- Map.lookup name db-    latest <- List.find (\x -> withinRange x versionRange) $ List.reverse $ List.sort $ Map.keys pdsByVersion-    Map.lookup latest pdsByVersion+  resolveDependency _ (Dependency name versionRange) = do+    pdsByVersion <- lookupName name+    latest <- List.find (\x -> withinRange' x versionRange) $ List.reverse $ List.sort $ Map.keys pdsByVersion+    lookupVersion latest pdsByVersion+#if MIN_VERSION_hackage_db(2,0,0)+  lookupName name = Map.lookup name db+  lookupVersion latest pdsByVersion = cabalFile <$> Map.lookup latest pdsByVersion+#else+  lookupName name = Map.lookup (unPackageName name) db+  lookupVersion latest pdsByVersion = Map.lookup latest pdsByVersion+#endif +#if MIN_VERSION_hackage_db(2,0,0)+withinRange' :: Version -> VersionRange -> Bool+withinRange' = withinRange+#else+withinRange' :: Base.Version -> VersionRange -> Bool+withinRange' =+#if MIN_VERSION_Cabal(2,0,0)+  withinRange . mkVersion'+#else+  withinRange+#endif+#endif+ resolvePackageDependencies :: Builder -> FilePath -> FilePath -> GenericPackageDescription -> IO [PackageIdentifier] resolvePackageDependencies bldr hackagePath root pd = do   xs <- either fallback return =<< resolveInstalledDependencies bldr root pd@@ -104,7 +202,13 @@       putStrLn "codex: *warning* falling back on dependency resolution using hackage"       resolveWithHackage     resolveWithHackage = do-      db <- readHackage' hackagePath+#if MIN_VERSION_hackage_db(2,0,0)+      db <- readTarball Nothing (hackagePath </> "00-index.tar")+        <|> readTarball Nothing (hackagePath </> "01-index.tar")+#else+      db <- readHackage' (hackagePath </> "00-index.tar")+        <|> readHackage' (hackagePath </> "01-index.tar")+#endif       return $ identifier <$> resolveHackageDependencies db pd  resolveSandboxDependencies :: FilePath -> IO [WorkspaceProject]@@ -123,7 +227,7 @@         projects :: String -> [FilePath]         projects x = sources x >>= (\x' -> fst <$> snd x')         sources :: String -> [(String, [(FilePath, Int)])]-        sources x = read x+        sources x = fromMaybe [] (readMaybe x)  resolveWorkspaceDependencies :: Workspace -> GenericPackageDescription -> [WorkspaceProject] resolveWorkspaceDependencies (Workspace ws) pd = maybeToList . resolveDependency =<< allDependencies pd where@@ -136,14 +240,22 @@   return $ fmap (\x -> WorkspaceProject (identifier x) path) pd  getWorkspace :: FilePath -> IO Workspace-getWorkspace _root = do-  root <- canonicalizePath _root-  xs <- listDirectory root-  ys <- traverse find xs-  return . Workspace $ ys >>= maybeToList where-    find path = do-      isDirectory <- doesDirectoryExist path-      if isDirectory then readWorkspaceProject path else return Nothing-    listDirectory fp = do-      xs <- getDirectoryContents fp-      return . fmap (fp </>) $ filter (not . List.isPrefixOf ".") xs+getWorkspace root =+  Workspace <$> findLocalPackages 1 root++-- | Recursively find local packages in @root@, up to @depth@ layers deep. The+-- @root@ directory has a depth of 0.+findLocalPackages :: Int -> FilePath -> IO [WorkspaceProject]+findLocalPackages depth root =+  catMaybes <$> go depth root+  where+    go n path+      | n < 0 = pure []+      | otherwise =+          (:) <$> readWorkspaceProject path+              <*> fmap mconcat (traverse (go (n - 1)) =<< listDirectories path)+    listDirectories path = do+      paths <- getDirectoryContents =<< canonicalizePath path+      filterM doesDirectoryExist ((path </>) <$> filter visible paths)+    visible path =+      (not . isPrefixOf ".") path && path `notElem` ["dist", "dist-new"]