diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -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)
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -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)
+
diff --git a/codex.cabal b/codex.cabal
--- a/codex.cabal
+++ b/codex.cabal
@@ -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
diff --git a/codex/Main.hs b/codex/Main.hs
--- a/codex/Main.hs
+++ b/codex/Main.hs
@@ -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
diff --git a/codex/Main/Config.hs b/codex/Main/Config.hs
--- a/codex/Main/Config.hs
+++ b/codex/Main/Config.hs
@@ -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
diff --git a/src/Codex.hs b/src/Codex.hs
--- a/src/Codex.hs
+++ b/src/Codex.hs
@@ -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
diff --git a/src/Codex/Internal.hs b/src/Codex/Internal.hs
--- a/src/Codex/Internal.hs
+++ b/src/Codex/Internal.hs
@@ -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
diff --git a/src/Codex/Project.hs b/src/Codex/Project.hs
--- a/src/Codex/Project.hs
+++ b/src/Codex/Project.hs
@@ -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"]
