diff --git a/.gitignore b/.gitignore
index 62d89f7..e84f893 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,3 +23,7 @@ tags
 TAGS
 codex.tags
 .vim
+
+
+## test specific ignores
+test/testdata/**/stack.yaml
diff --git a/app/Main.hs b/app/Main.hs
index 361e09e..902ba1e 100644
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE MultiWayIf #-}
 
 module Main where
@@ -14,24 +15,38 @@ import Hie.Locate
 import Hie.Yaml
 import System.Directory
 import System.Directory.Internal
-import System.FilePath.Posix
+import System.FilePath
+import Hie.CabalHelper
+import Distribution.Helper (Ex (..))
 
 main :: IO ()
 main = do
   pwd <- getCurrentDirectory
-  files <- listDirectory pwd
-  let name =
-        if  | any (("dist-newstyle" ==) . takeFileName) files -> "cabal"
-            | any ((".stack-work" ==) . takeFileName) files -> "stack"
-            | any (("cabal.project" ==) . takeFileName) files -> "cabal"
-            | any (("stack.yaml" ==) . takeFileName) files -> "stack"
-            | otherwise -> "cabal"
-  cfs <- runMaybeT $ case name of
-    "cabal" -> cabalPkgs pwd
-    _ -> stackYamlPkgs pwd
-  when (null cfs) $ error $
-    "No .cabal files found under"
-      <> pwd
-      <> "\n You may need to run stack build."
-  pkgs <- catMaybes <$> mapM (nestedPkg pwd) (concat cfs)
-  putStr <$> hieYaml name $ fmtPkgs name pkgs
+  projM <- findCabalHelperEntryPoint $ pwd </> "Foo.hs"
+  case projM of
+    Nothing -> error "could not find project context"
+    Just proj@(Ex p) -> do
+      env <- initialiseEnvironment p
+      packages <- loadPackages env
+      pkgs <- toHieYaml env packages
+      let name = if | isCabalProject proj -> "cabal"
+                    | isStackProject proj -> "stack"
+                    | otherwise -> error "Neither stack nor cabal project"
+      putStr $ hieYaml name $ fmtPkgs name pkgs
+
+  -- files <- listDirectory pwd
+  -- let name =
+  --       if  | any (("dist-newstyle" ==) . takeFileName) files -> "cabal"
+  --           | any ((".stack-work" ==) . takeFileName) files -> "stack"
+  --           | any (("cabal.project" ==) . takeFileName) files -> "cabal"
+  --           | any (("stack.yaml" ==) . takeFileName) files -> "stack"
+  --           | otherwise -> "cabal"
+  -- cfs <- runMaybeT $ case name of
+  --   "cabal" -> cabalPkgs pwd
+  --   _ -> stackYamlPkgs pwd
+  -- when (null cfs) $ error $
+  --   "No .cabal files found under"
+  --     <> pwd
+  --     <> "\n You may need to run stack build."
+  -- pkgs <- catMaybes <$> mapM (nestedPkg pwd) (concat cfs)
+  -- putStr <$> hieYaml name $ fmtPkgs name pkgs
diff --git a/hie.yaml b/hie.yaml
index 683f137..86e1067 100644
--- a/hie.yaml
+++ b/hie.yaml
@@ -8,3 +8,9 @@ cradle:
 
     - path: "test"
       component: "implicit-hie:test:implicit-hie-test"
+
+    - path: test/utils
+      component: "implicit-hie:test:unit-tests"
+      
+    - path: test/unit
+      component: "implicit-hie:test:unit-tests"
diff --git a/implicit-hie.cabal b/implicit-hie.cabal
index 0c495fb..770f9f9 100644
--- a/implicit-hie.cabal
+++ b/implicit-hie.cabal
@@ -1,11 +1,4 @@
-cabal-version: 2.0
-
--- This file has been generated from package.yaml by hpack version 0.33.0.
---
--- see: https://github.com/sol/hpack
---
--- hash: 18f92037a7863d121ac45e847f7dc6177adf0ebc7951dfa1588340f86e64456b
-
+cabal-version: 2.4
 name:           implicit-hie
 version:        0.1.0.0
 description:    Auto generate a stack or cabal multi component hie.yaml file
@@ -16,41 +9,68 @@ bug-reports:    https://github.com/Avi-D-coder/implicit-hie/issues
 author:         Avi Dessauer
 maintainer:     avi.the.coder@gmail.com
 copyright:      2020
-license:        BSD3
+license:        BSD-3-Clause
 license-file:   LICENSE
 build-type:     Simple
 extra-source-files:
     README.md
     ChangeLog.md
+    test/testdata/cabal-helper/**/*.hs
+    test/testdata/cabal-helper/**/*.cabal
+    test/testdata/cabal-helper/**/*.project
 
 source-repository head
   type: git
   location: https://github.com/Avi-D-coder/implicit-hie
 
+flag cabalHelper
+  Description: Enable Cabal-Helper dependencies
+  Default:     True
+  Manual:      True
+
+common cabal-helper
+  if flag(cabalHelper)
+    cpp-options: -DCABAL_HELPER_SUPPORT
+
 library
+  import: cabal-helper
   exposed-modules:
       Hie.Cabal.Parser
       Hie.Locate
+      Hie.Logger
       Hie.Yaml
+
+  if flag(cabalHelper)
+    exposed-modules:
+      Hie.CabalHelper
+    build-depends:
+      cabal-helper >= 1.1 && <1.2
+
   other-modules:
       Paths_implicit_hie
   autogen-modules:
       Paths_implicit_hie
   hs-source-dirs:
       src
-  ghc-options: -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns -fno-warn-unused-imports -fno-warn-unused-binds -fno-warn-name-shadowing -fwarn-redundant-constraints
+  ghc-options: -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns -fwarn-unused-imports -fwarn-unused-binds -fwarn-name-shadowing -fwarn-redundant-constraints
   build-depends:
-      attoparsec >= 0.13
+      aeson
+    , attoparsec >= 0.13
     , base >=4.7 && <5
+    , containers
     , directory >= 1.3
     , filepath >= 1.4
     , filepattern >= 0.1
+    , hie-bios >=0.5 && <0.6
+    , hslogger
     , text >= 1.2
     , transformers >= 0.5
+    , process
     , yaml >= 0.5
   default-language: Haskell2010
 
 executable gen-hie
+  import: cabal-helper
   main-is: Main.hs
   other-modules:
       Paths_implicit_hie
@@ -62,16 +82,19 @@ executable gen-hie
   build-depends:
       attoparsec
     , base >=4.7 && <5
+    , cabal-helper
     , directory
     , filepath
     , filepattern
     , implicit-hie
+    , optparse-applicative
     , text
     , transformers
     , yaml
   default-language: Haskell2010
 
 test-suite implicit-hie-test
+  import: cabal-helper
   type: exitcode-stdio-1.0
   main-is: Spec.hs
   other-modules:
@@ -94,3 +117,27 @@ test-suite implicit-hie-test
     , transformers
     , yaml
   default-language: Haskell2010
+
+test-suite unit-tests
+  import: cabal-helper
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Paths_implicit_hie, TestUtils
+  if flag(cabalHelper)
+    other-modules:
+      CabalHelperSpec
+  hs-source-dirs:
+      test/unit, test/utils
+  ghc-options: -Wall -Wincomplete-record-updates -Wincomplete-uni-patterns -fno-warn-unused-imports -fno-warn-unused-binds -fno-warn-name-shadowing -fwarn-redundant-constraints -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      aeson
+    , base
+    , directory
+    , filepath
+    , hie-bios
+    , hspec
+    , implicit-hie
+    , text
+    , yaml
+  default-language: Haskell2010
diff --git a/src/Hie/Cabal/Parser.hs b/src/Hie/Cabal/Parser.hs
index 92633f0..4766294 100644
--- a/src/Hie/Cabal/Parser.hs
+++ b/src/Hie/Cabal/Parser.hs
@@ -20,7 +20,7 @@ type Indent = Int
 data Package = Package Name [Component]
   deriving (Show, Eq, Ord)
 
-data CompType = Lib | Exe | Test | Bench
+data CompType = Lib | Exe | Test | Bench | FLib
   deriving (Show, Eq, Ord)
 
 data Component
diff --git a/src/Hie/CabalHelper.hs b/src/Hie/CabalHelper.hs
new file mode 100644
index 0000000..763cb8d
--- /dev/null
+++ b/src/Hie/CabalHelper.hs
@@ -0,0 +1,864 @@
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Hie.CabalHelper
+  ( CabalHelper (..),
+    isStackCradle,
+    isCabalCradle,
+    cabalHelperCradle,
+    findCabalHelperEntryPoint,
+    isCabalProject,
+    isStackProject,
+    initialiseEnvironment,
+    loadAllUnits,
+    toHieYaml,
+    loadPackages,
+  )
+where
+
+import Control.Exception
+import Control.Monad (forM)
+import Data.Foldable (toList)
+import Data.Function ((&))
+import Data.List (find, intercalate, isPrefixOf, sortOn)
+import qualified Data.List.NonEmpty as NonEmpty
+import Data.List.NonEmpty (NonEmpty)
+import qualified Data.Map as Map
+import Data.Maybe (isJust, listToMaybe, mapMaybe)
+import Data.Ord (Down (..))
+import qualified Data.Text as T
+import Distribution.Helper
+  ( ChComponentInfo (..),
+    ChComponentName (..),
+    ChLibraryName(..),
+    ChEntrypoint (..),
+    Ex (..),
+    Package,
+    ProjLoc (..),
+    QueryEnv,
+    Unit,
+    UnitInfo (..),
+    mkQueryEnv,
+    pPackageName,
+    pSourceDir,
+    pUnits,
+    projectPackages,
+    runQuery,
+    uiComponents,
+    unChModuleName,
+    unitInfo,
+  )
+import Distribution.Helper.Discover (findProjects, getDefaultDistDir)
+import HIE.Bios as Bios
+import HIE.Bios.Types (CradleAction (..))
+import qualified HIE.Bios.Types as Bios
+import Hie.Logger
+import qualified Hie.Cabal.Parser as CP
+import System.Directory (canonicalizePath, findExecutable, getCurrentDirectory, makeRelativeToCurrentDirectory)
+import System.Exit
+import System.FilePath
+
+-- | Check if the given cradle is a stack cradle.
+-- This might be used to determine the GHC version to use on the project.
+-- If it is a stack-cradle, we have to use @"stack path --compiler-exe"@
+-- otherwise we may ask `ghc` directly what version it is.
+isStackCradle :: Cradle CabalHelper -> Bool
+isStackCradle =
+  (`elem` [Bios.Other Stack, Bios.Other StackNone])
+    . Bios.actionName
+    . Bios.cradleOptsProg
+
+-- | Check if the given cradle is a cabal cradle.
+-- This might be used to determine the GHC version to use on the project.
+-- If it is a stack-cradle, we have to use @"stack path --compiler-exe"@
+-- otherwise we may ask @ghc@ directly what version it is.
+isCabalCradle :: Cradle CabalHelper -> Bool
+isCabalCradle =
+  (`elem` [Bios.Other CabalV2, Bios.Other CabalNone])
+    . Bios.actionName
+    . Bios.cradleOptsProg
+
+data CabalHelper
+  = Stack
+  | StackNone
+  | CabalV2
+  | CabalNone
+  deriving (Show, Eq, Ord)
+
+-- | Finds a Cabal v2-project, Cabal v1-project or a Stack project
+-- relative to the given FilePath.
+-- Cabal v2-project and Stack have priority over Cabal v1-project.
+-- This entails that if a Cabal v1-project can be identified, it is
+-- first checked whether there are Stack projects or Cabal v2-projects
+-- before it is concluded that this is the project root.
+-- Cabal v2-projects and Stack projects are equally important.
+-- Due to the lack of user-input we have to guess which project it
+-- should rather be.
+-- This guessing has no guarantees and may change at any time.
+--
+-- === Example:
+--
+-- Assume the following project structure:
+--
+-- @
+--  /
+--  └── Foo/
+--      ├── Foo.cabal
+--      ├── stack.yaml
+--      ├── cabal.project
+--      ├── src
+--      │   └── Lib.hs
+--      └── B/
+--          ├── B.cabal
+--          └── src/
+--              └── Lib2.hs
+-- @
+--
+-- Assume the call @findCabalHelperEntryPoint "\/Foo\/B\/src\/Lib2.hs"@.
+-- We now want to know to which project "\/Foo\/B\/src\/Lib2.hs" belongs to
+-- and what the projects root is. If we only do a naive search to find the
+-- first occurrence of either "B.cabal", "stack.yaml", "cabal.project"
+-- or "Foo.cabal", we might assume that the location  of "B.cabal" marks
+-- the project's root directory of which "\/Foo\/B\/src\/Lib2.hs" is part of.
+-- However, there is also a "cabal.project" and "stack.yaml" in the parent
+-- directory, which add the package @B@ as a package.
+-- So, the compilation of the package @B@, and the file "src\/Lib2.hs" in it,
+-- does not only depend on the definitions in "B.cabal", but also
+-- on "stack.yaml" and "cabal.project".
+-- The project root is therefore "\/Foo\/".
+-- Only if there is no "stack.yaml" or "cabal.project" in any of the ancestor
+-- directories, it is safe to assume that "B.cabal" marks the root of the project.
+--
+-- Thus:
+--
+-- >>> findCabalHelperEntryPoint "/Foo/B/src/Lib2.hs
+-- Just (Ex (ProjLocStackYaml { plStackYaml = "/Foo/"}))
+--
+-- or
+--
+-- >>> findCabalHelperEntryPoint "/Foo/B/src/Lib2.hs"
+-- Just (Ex (ProjLocV2File { plProjectDirV2 = "/Foo/"}))
+--
+-- In the given example, it is not guaranteed which project type is found,
+-- it is only guaranteed that it will not identify the project
+-- as a cabal v1-project. Note that with cabal-helper version (1.0),
+-- by default a *.cabal file is identified as a 'ProjLocV2Dir' project.
+-- The same issue as before exists and we look for a 'ProjLocV2File' or
+-- 'ProjLocStackYaml' before deciding that 'ProjLocV2Dir' marks the project root.
+--
+-- Note that this will not return any project types for which the corresponding
+-- build tool is not on the PATH. This is "stack" and "cabal" for stack and cabal
+-- (both v1 and v2) projects respectively.
+findCabalHelperEntryPoint :: FilePath -> IO (Maybe (Ex ProjLoc))
+findCabalHelperEntryPoint fp = do
+  allProjs <- concat <$> mapM findProjects (ancestors (takeDirectory fp))
+  debugm $ "Cabal-Helper found these projects: " ++ show (map (\(Ex x) -> show x) allProjs)
+  -- We only want to return projects that we have the build tools installed for
+  isStackInstalled <- isJust <$> findExecutable "stack"
+  isCabalInstalled <- isJust <$> findExecutable "cabal"
+  let supportedProjs = filter (\x -> supported x isStackInstalled isCabalInstalled) allProjs
+  debugm $ "These projects have the build tools installed: " ++ show (map (\(Ex x) -> show x) supportedProjs)
+  case filter (\p -> isCabalV2FileProject p || isStackProject p) supportedProjs of
+    (x : _) -> return $ Just x
+    [] -> case filter isCabalProject supportedProjs of
+      (x : _) -> return $ Just x
+      [] -> return Nothing
+  where
+    supported :: Ex ProjLoc -> Bool -> Bool -> Bool
+    supported (Ex ProjLocStackYaml {}) stackInstalled _ = stackInstalled
+    supported (Ex ProjLocV2Dir {}) _ cabalInstalled = cabalInstalled
+    supported (Ex ProjLocV2File {}) _ cabalInstalled = cabalInstalled
+    supported (Ex ProjLocV1Dir {}) _ cabalInstalled = cabalInstalled
+    supported (Ex ProjLocV1CabalFile {}) _ cabalInstalled = cabalInstalled
+
+isStackProject :: Ex ProjLoc -> Bool
+isStackProject (Ex ProjLocStackYaml {}) = True
+isStackProject _ = False
+
+isCabalV2FileProject :: Ex ProjLoc -> Bool
+isCabalV2FileProject (Ex ProjLocV2File {}) = True
+isCabalV2FileProject _ = False
+
+isCabalProject :: Ex ProjLoc -> Bool
+isCabalProject (Ex ProjLocV1CabalFile {}) = True
+isCabalProject (Ex ProjLocV1Dir {}) = True
+isCabalProject (Ex ProjLocV2File {}) = True
+isCabalProject (Ex ProjLocV2Dir {}) = True
+isCabalProject _ = False
+
+-- | Given a FilePath, find the cradle the FilePath belongs to.
+--
+-- Finds the Cabal Package the FilePath is most likely a part of
+-- and creates a cradle whose root directory is the directory
+-- of the package the File belongs to.
+--
+-- It is not required that the FilePath given actually exists. If it does not
+-- exist or is not part of any of the packages in the project, a "None"-cradle is
+-- produced.
+-- See <https://github.com/mpickering/hie-bios> for what a "None"-cradle is.
+-- The "None"-cradle can still be used to query for basic information, such as
+-- the GHC version used to build the project. However, it can not be used to
+-- load any of the files in the project.
+--
+-- == General Approach
+--
+-- Given a FilePath that we want to load, we need to create a cradle
+-- that can compile and load the given FilePath.
+-- In Cabal-Helper, there is no notion of a cradle, but a project
+-- consists of multiple packages that contain multiple units.
+-- Each unit may consist of multiple components.
+-- A unit is the smallest part of code that Cabal (the library) can compile.
+-- Examples are executables, libraries, tests or benchmarks are all units.
+-- Each of this units has a name that is unique within a build-plan,
+-- such as "exe:hie" which represents the executable of the Haskell IDE Engine.
+--
+-- In principle, a unit is what hie-bios considers to be a cradle.
+-- However, to find out to which unit a FilePath belongs, we have to initialise
+-- the unit, e.g. configure its dependencies and so on. When discovering a cradle
+-- we do not want to pay for this upfront, but rather when we actually want to
+-- load a Module in the project. Therefore, we only identify the package the
+-- FilePath is part of and decide which unit to load when 'runCradle' is executed.
+--
+-- Thus, to find the options required to compile and load the given FilePath,
+-- we have to do the following:
+--
+--  1. Find the project type of the project.
+--     Happens in 'cabalHelperCradle'
+--  2. Identify the package that contains the FilePath (should be unique).
+--     Happens in 'cabalHelperAction'
+--  3. Find the unit that that contains the FilePath (May be non-unique).
+--     Happens in 'cabalHelperAction'
+--  4. Find the component that exposes the FilePath (May be non-unique).
+--     Happens in 'cabalHelperAction'
+--
+-- === Find the project type of the project.
+--
+-- The function 'cabalHelperCradle' does the first step only.
+-- It starts by querying Cabal-Helper to find the project's root.
+-- See 'findCabalHelperEntryPoint' for details how this is done.
+--
+-- === Identify the package that contains the FilePath
+--
+-- Once the root of the project is defined, we query Cabal-Helper for all packages
+-- that are defined in the project and match by the packages source directory
+-- which package the given FilePath is most likely to be a part of.
+-- E.g. if the source directory of the package is the most concrete
+-- prefix of the FilePath, the FilePath is in that package.
+-- After the package is identified, we create a cradle where cradle's root
+-- directory is set to the package's source directory. This is necessary,
+-- because compiler options obtained from a component, are relative
+-- to the source directory of the package the component is part of.
+--
+-- === Find the unit that that contains the FilePath
+--
+-- In 'cabalHelperAction' we want to load a given FilePath, already knowing
+-- which package the FilePath is part of. Now we obtain all Units that are part
+-- of the package and match by the source directories (plural is intentional),
+-- to which unit the given FilePath most likely belongs to. If no unit can be
+-- obtained, e.g. for every unit, no source directory is a prefix of the FilePath,
+-- we return an error code, since this is not allowed to happen.
+-- If there are multiple matches, which is possible, we check whether any of the
+-- components defined in the unit exposes or defines the given FilePath as a module.
+--
+-- === Find the component that exposes the FilePath
+--
+-- A component defines the options that are necessary to compile a FilePath that
+-- is in the component. It also defines which modules are in the component.
+-- Therefore, we translate the given FilePath into a module name, relative to
+-- the unit's source directory, and check if the module name is exposed by the
+-- component. There is a special case, executables define a FilePath, for the
+-- file that contains the 'main'-function, that is relative to the unit's source
+-- directory.
+--
+-- After the component has been identified, we can actually retrieve the options
+-- required to load and compile the given file.
+--
+-- == Examples
+--
+-- === Mono-Repo
+--
+-- Assume the project structure:
+--
+-- @
+--  /
+--  └── Mono/
+--      ├── cabal.project
+--      ├── stack.yaml
+--      ├── A/
+--      │   ├── A.cabal
+--      │   └── Lib.hs
+--      └── B/
+--          ├── B.cabal
+--          └── Exe.hs
+-- @
+--
+-- Currently, Haskell IDE Engine needs to know on startup which GHC version is
+-- needed to compile the project. This information is needed to show warnings to
+-- the user if the GHC version on the project does not agree with the GHC version
+-- that was used to compile Haskell IDE Engine.
+--
+-- Therefore, the function 'findLocalCradle' is invoked with a dummy FilePath,
+-- such as "\/Mono\/Lib.hs". Since there will be no package that contains this
+-- dummy FilePath, the result will be a None-cradle.
+--
+-- Either
+--
+-- >>> findLocalCradle "/Mono/Lib.hs"
+-- Cradle { cradleRootDir = "/Mono/", CradleAction { actionName = "Cabal-Helper-Stack-None", ..} }
+--
+-- or
+--
+-- >>> findLocalCradle "/Mono/Lib.hs"
+-- Cradle { cradleRootDir = "/Mono/", CradleAction { actionName = "Cabal-Helper-Cabal-V2-None", ..} }
+--
+-- The cradle result of this invocation is only used to obtain the GHC version,
+-- which is safe, since it only checks if the cradle is a 'stack' project or
+-- a 'cabal' project.
+--
+--
+-- If we are trying to load the executable:
+--
+-- >>> findLocalCradle "/Mono/B/Exe.hs"
+-- Cradle { cradleRootDir = "/Mono/", CradleAction { actionName = "Cabal-Helper-Cabal-V2", ..} }
+--
+-- we will detect correctly the compiler options, by first finding the appropriate
+-- package, followed by traversing the units in the package and finding the
+-- component that exposes the executable by FilePath.
+--
+-- === No explicit executable folder
+--
+-- Assume the project structure:
+--
+-- @
+--  /
+--  └── Library/
+--      ├── cabal.project
+--      ├── stack.yaml
+--      ├── Library.cabal
+--      └── src
+--          ├── Lib.hs
+--          └── Exe.hs
+-- @
+--
+-- There are different dependencies for the library "Lib.hs" and the
+-- executable "Exe.hs". If we are trying to load the executable "src\/Exe.hs"
+-- we will correctly identify the executable unit, and correctly initialise
+-- dependencies of "exe:Library".
+-- It will be correct even if we load the unit "lib:Library" before
+-- the "exe:Library" because the unit "lib:Library" does not expose
+-- a module @"Exe"@.
+--
+-- === Sub package
+--
+-- Assume the project structure:
+--
+-- @
+--  /
+--  └── Repo/
+--      ├── cabal.project
+--      ├── stack.yaml
+--      ├── Library.cabal
+--      ├── src
+--      |   └── Lib.hs
+--      └── SubRepo
+--          ├── SubRepo.cabal
+--          └── Lib2.hs
+-- @
+--
+-- When we try to load "\/Repo\/SubRepo\/Lib2.hs", we need to identify root
+-- of the project, which is "\/Repo\/" but set the root directory of the cradle
+-- responsible to load "\/Repo\/SubRepo\/Lib2.hs" to "\/Repo\/SubRepo", since
+-- the compiler options obtained from Cabal-Helper are relative to the package
+-- source directory, which is "\/Repo\/SubRepo".
+cabalHelperCradle :: FilePath -> IO (Cradle CabalHelper)
+cabalHelperCradle file = do
+  projM <- findCabalHelperEntryPoint file
+  case projM of
+    Nothing -> do
+      errorm $ "Could not find a Project for file: " ++ file
+      cwd <- getCurrentDirectory
+      return
+        Cradle
+          { cradleRootDir = cwd,
+            cradleOptsProg =
+              CradleAction
+                { actionName = Bios.Direct,
+                  runCradle = \_ _ ->
+                    return $
+                      CradleSuccess
+                        ComponentOptions
+                          { componentOptions = [file, fixImportDirs cwd "-i."],
+                            componentRoot = cwd,
+                            componentDependencies = []
+                          }
+                }
+          }
+    Just (Ex proj) -> do
+      logm $ "Cabal-Helper decided to use: " ++ show proj
+      -- Find the root of the project based on project type.
+      let root = projectRootDir proj
+      -- Create a suffix for the cradle name.
+      -- Purpose is mainly for easier debugging.
+      let actionNameSuffix = projectType proj
+      env <- initialiseEnvironment proj
+      return
+        Cradle
+          { cradleRootDir = root,
+            cradleOptsProg =
+              CradleAction
+                { actionName = Bios.Other actionNameSuffix,
+                  runCradle = \_ fp ->
+                    cabalHelperAction
+                      (Ex proj)
+                      env
+                      fp
+                }
+          }
+
+-- | Cradle Action to query for the ComponentOptions that are needed
+-- to load the given FilePath.
+-- This Function is not supposed to throw any exceptions and use
+-- 'CradleLoadResult' to indicate errors.
+cabalHelperAction ::
+  -- | Project location, can be used
+  -- to present build-tool
+  -- agnostic error messages.
+  Ex ProjLoc ->
+  -- | Query Env created by 'mkQueryEnv'
+  -- with the appropriate 'distdir'
+  QueryEnv v ->
+  -- | FilePath to load, expected to be an absolute path.
+  FilePath ->
+  IO (CradleLoadResult ComponentOptions)
+cabalHelperAction proj env fp = do
+  -- This builds all packages in the project.
+  packages <- runQuery projectPackages env
+  -- Find the package the given file may belong to.
+  -- If it does not belong to any package, fail the loading process
+  case packages `findPackageFor` fp of
+    Nothing -> do
+      debugm $ "Failed to find a package for: " ++ fp
+      return $ CradleFail $
+        CradleError
+          (ExitFailure 1)
+          [ "Failed to find a package for: " ++ fp,
+            "No Prefix matched.",
+            "Following packages were searched: "
+              ++ intercalate
+                "; "
+                ( map
+                    (\p -> pPackageName p ++ "(" ++ pSourceDir p ++ ")")
+                    $ NonEmpty.toList packages
+                )
+          ]
+    Just package -> do
+      debugm $ "Cabal-Helper cradle package: " ++ show package
+      -- Field `pSourceDir` often has the form `<cwd>/./plugin`
+      -- but we only want `<cwd>/plugin`
+      packageRoot <- canonicalizePath $ pSourceDir package
+      debugm $
+        "Cabal-Helper normalisedPackageLocation: "
+          ++ packageRoot
+      -- Get all unit infos the given FilePath may belong to
+      let units = pUnits package
+      -- make the FilePath to load relative to the root of the cradle.
+      let relativeFp = makeRelative packageRoot fp
+      debugm $ "Relative Module FilePath: " ++ relativeFp
+      getComponent proj env (toList units) relativeFp
+        >>= \case
+          Right comp -> do
+            let fs = getFlags comp
+            let targets = getTargets comp relativeFp
+            let ghcOptions = removeRTS (fs ++ targets)
+            debugm $ "Flags for \"" ++ fp ++ "\": " ++ show ghcOptions
+            debugm $ "Component Infos: " ++ show comp
+            return $
+              CradleSuccess
+                ComponentOptions
+                  { componentOptions = ghcOptions,
+                    componentRoot = packageRoot,
+                    componentDependencies = []
+                  }
+          Left err ->
+            return
+              $ CradleFail
+              $ CradleError
+                (ExitFailure 2)
+                err
+  where
+    removeRTS :: [String] -> [String]
+    removeRTS ("+RTS" : xs) =
+      case dropWhile (/= "-RTS") xs of
+        [] -> []
+        (_ : ys) -> removeRTS ys
+    removeRTS (y : ys) = y : removeRTS ys
+    removeRTS [] = []
+
+-- | Fix occurrences of "-i." to "-i<cradle-root-dir>"
+-- Flags obtained from cabal-helper are relative to the package
+-- source directory. This is less resilient to using absolute paths,
+-- thus, we fix it here.
+fixImportDirs :: FilePath -> String -> String
+fixImportDirs base_dir arg =
+  if "-i" `isPrefixOf` arg
+    then
+      let dir = drop 2 arg
+       in -- the flag "-i" has special meaning.
+          if not (null dir) && isRelative dir
+            then ("-i" ++ base_dir </> dir)
+            else arg
+    else arg
+
+-- | Get the component the given FilePath most likely belongs to.
+-- Lazily ask units whether the given FilePath is part of one of their
+-- component's.
+-- If a Module belongs to multiple components, it is not specified which
+-- component will be loaded.
+-- The given FilePath must be relative to the Root of the project
+-- the given units belong to.
+getComponent ::
+  forall pt. Ex ProjLoc -> QueryEnv pt -> [Unit pt] -> FilePath -> IO (Either [String] ChComponentInfo)
+getComponent proj env unitCandidates fp = getComponent' [] [] unitCandidates
+  >>= \case
+    (tried, failed, Nothing) -> return (Left $ buildErrorMsg tried failed)
+    (_, _, Just comp) -> return (Right comp)
+  where
+    getComponent' :: [UnitInfo] -> [(Unit pt, IOException)] -> [Unit pt] -> IO ([UnitInfo], [(Unit pt, IOException)], Maybe ChComponentInfo)
+    getComponent' triedUnits failedUnits [] = return (triedUnits, failedUnits, Nothing)
+    getComponent' triedUnits failedUnits (unit : units) =
+      try (runQuery (unitInfo unit) env) >>= \case
+        Left (e :: IOException) -> do
+          warningm $ "Catching and swallowing an IOException: " ++ show e
+          warningm $
+            "The Exception was thrown in the context of finding"
+              ++ " a component for \""
+              ++ fp
+              ++ "\" in the unit: "
+              ++ show unit
+          getComponent' triedUnits ((unit, e) : failedUnits) units
+        Right ui -> do
+          let components = Map.elems (uiComponents ui)
+          debugm $ "Unit Info: " ++ show ui
+          case find (fp `partOfComponent`) components of
+            Nothing -> getComponent' (ui : triedUnits) failedUnits units
+            comp -> return (triedUnits, failedUnits, comp)
+    buildErrorMsg :: [UnitInfo] -> [(Unit pt, IOException)] -> [String]
+    buildErrorMsg triedUnits failedUnits =
+      concat
+        [ [ "Could not obtain flags for: \"" ++ fp ++ "\".",
+            ""
+          ],
+          concat
+            [ concat
+                [ [ "This module was not part of any component we are aware of.",
+                    ""
+                  ],
+                  concatMap ppShowUnitInfo triedUnits,
+                  [ "",
+                    ""
+                  ],
+                  if isStackProject proj
+                    then stackSpecificInstructions
+                    else cabalSpecificInstructions
+                ]
+              | not (null triedUnits)
+            ],
+          concat
+            [ [ "We could not build all components.",
+                "If one of these components exposes this Module, make sure they compile.",
+                "You can try to invoke the commands yourself.",
+                "The following commands failed:"
+              ]
+                ++ concatMap (ppShowIOException . snd) failedUnits
+              | not (null failedUnits)
+            ]
+        ]
+    stackSpecificInstructions :: [String]
+    stackSpecificInstructions =
+      [ "To expose a module, refer to:",
+        "https://docs.haskellstack.org/en/stable/GUIDE/",
+        "If you are using `package.yaml` then you don't have to manually expose modules.",
+        "Maybe you didn't set the source directories for your project correctly."
+      ]
+    cabalSpecificInstructions :: [String]
+    cabalSpecificInstructions =
+      [ "To expose a module, refer to:",
+        "https://www.haskell.org/cabal/users-guide/developing-packages.html",
+        ""
+      ]
+    ppShowUnitInfo :: UnitInfo -> [String]
+    ppShowUnitInfo u =
+      u
+        & uiComponents
+        & Map.toList
+        & map
+          ( \(name, info) ->
+              "Component: " ++ show name ++ " with source directory: " ++ show (ciSourceDirs info)
+          )
+    ppShowIOException :: IOException -> [String]
+    ppShowIOException e =
+      [ "",
+        show e
+      ]
+
+-- ----------------------------------------------------------------------------
+
+loadPackages :: QueryEnv pt -> IO [Package pt]
+loadPackages = fmap toList . runQuery projectPackages
+
+-- | Eagerly load all given units.
+loadAllUnits :: QueryEnv pt -> [Unit pt] -> IO ([UnitInfo], [(Unit pt, IOException)])
+loadAllUnits env units =
+  loadAllUnits' env [] [] units
+
+loadAllUnits' :: QueryEnv pt -> [UnitInfo] -> [(Unit pt, IOException)] -> [Unit pt] -> IO ([UnitInfo], [(Unit pt, IOException)])
+loadAllUnits' _ triedUnits failedUnits [] = return (triedUnits, failedUnits)
+loadAllUnits' env triedUnits failedUnits (unit : units) =
+  try (runQuery (unitInfo unit) env) >>= \case
+    Left (e :: IOException) -> do
+      warningm $ "Catching and swallowing an IOException: " ++ show e
+      loadAllUnits' env triedUnits ((unit, e) : failedUnits) units
+    Right ui -> do
+      debugm $ "Unit Info: " ++ show ui
+      loadAllUnits' env (ui : triedUnits) failedUnits units
+
+initialiseEnvironment :: ProjLoc pt -> IO (QueryEnv pt)
+initialiseEnvironment proj = do
+  -- Create a suffix for the cradle name.
+  -- Purpose is mainly for easier debugging.
+  let dist_dir = getDefaultDistDir proj
+  mkQueryEnv proj dist_dir
+
+toHieYaml :: QueryEnv pt -> [Package pt] -> IO [CP.Package]
+toHieYaml env packages = forM packages $ \package -> do
+  let units = NonEmpty.toList $ pUnits package
+  (unitInfos, _failed) <- loadAllUnits env units
+  let yamlComponents :: [(FilePath, CP.CompType, CP.Name)]
+      yamlComponents = concatMap unitToComp unitInfos
+  finalisePackage (pPackageName package) (pSourceDir package) yamlComponents
+  where
+    finalisePackage :: String -> FilePath -> [(FilePath, CP.CompType, CP.Name)] -> IO CP.Package
+    finalisePackage pName pRoot yamlUnits = do
+      pRoot' <- makeRelativeToCurrentDirectory pRoot
+      let yamlUnits' = map (\(fp, compType, name) -> CP.Comp compType name (T.pack $ addTrailingPathSeparator $ normalise $ pRoot' </> fp)) yamlUnits
+      pure $ CP.Package (T.pack pName ) yamlUnits'
+
+    toCompType :: ChComponentInfo -> [(FilePath, CP.CompType, CP.Name)]
+    toCompType chComp =
+      let comps = case ciComponentName chComp of
+            ChLibName ChMainLibName -> [(CP.Lib, T.pack "")]
+            ChLibName (ChSubLibName name) -> [(CP.Lib, T.pack name)]
+            ChFLibName name -> [(CP.FLib, T.pack name)]
+            ChExeName name -> [(CP.Exe, T.pack name)]
+            ChTestName name -> [(CP.Test, T.pack name)]
+            ChBenchName name -> [(CP.Bench, T.pack name)]
+            ChSetupHsName -> [] -- TODO: this could be a none cradle
+       in (\path (compType, name) -> (path, compType, name)) <$> ciSourceDirs chComp <*> comps
+
+    unitToComp :: UnitInfo -> [(FilePath, CP.CompType, CP.Name)]
+    unitToComp info = concatMap toCompType (Map.elems $ uiComponents info)
+
+-- ----------------------------------------------------------------------------
+
+-- | Check whether the given FilePath is part of the Component.
+-- A FilePath is part of the Component if and only if:
+--
+--   * One Component's 'ciSourceDirs' is a prefix of the FilePath
+--   * The FilePath, after converted to a module name,
+--     is a in the Component's Targets, or the FilePath is
+--     the executable in the component.
+--
+-- The latter is achieved by making the FilePath relative to the 'ciSourceDirs'
+-- and then replacing Path separators with ".".
+-- To check whether the given FilePath is the executable of the Component,
+-- we have to check whether the FilePath, including 'ciSourceDirs',
+-- is part of the targets in the Component.
+partOfComponent ::
+  -- | FilePath relative to the package root.
+  FilePath ->
+  -- | Component to check whether the given FilePath is part of it.
+  ChComponentInfo ->
+  Bool
+partOfComponent fp' comp =
+  inTargets (ciSourceDirs comp) fp' (getTargets comp fp')
+  where
+    -- Check if the FilePath is in an executable or setup's main-is field
+    inMainIs :: FilePath -> Bool
+    inMainIs fp
+      | ChExeEntrypoint mainIs _ <- ciEntrypoints comp = mainIs == fp
+      | ChSetupEntrypoint mainIs <- ciEntrypoints comp = mainIs == fp
+      | otherwise = False
+    inTargets :: [FilePath] -> FilePath -> [String] -> Bool
+    inTargets sourceDirs fp targets =
+      let candidates = relativeTo fp sourceDirs
+       in any (existsInTargets targets fp) candidates
+    existsInTargets :: [String] -> FilePath -> FilePath -> Bool
+    existsInTargets targets absFp relFp =
+      or
+        [ any (`elem` targets) [getModuleName relFp, absFp],
+          inMainIs relFp
+        ]
+    getModuleName :: FilePath -> String
+    getModuleName fp =
+      map
+        ( \c ->
+            if isPathSeparator c
+              then '.'
+              else c
+        )
+        (dropExtension fp)
+
+-- | Get the flags necessary to compile the given component.
+getFlags :: ChComponentInfo -> [String]
+getFlags = ciGhcOptions
+
+-- | Get all Targets of a Component, since we want to load all components.
+-- FilePath is needed for the special case that the Component is an Exe.
+-- The Exe contains a Path to the Main which is relative to some entry
+-- in 'ciSourceDirs'.
+-- We monkey-patch this by supplying the FilePath we want to load,
+-- which is part of this component, and select the 'ciSourceDir' we actually want.
+-- See the Documentation of 'ciSourceDir' to why this contains multiple entries.
+getTargets :: ChComponentInfo -> FilePath -> [String]
+getTargets comp fp = case ciEntrypoints comp of
+  ChSetupEntrypoint {} -> []
+  ChLibEntrypoint {chExposedModules, chOtherModules} ->
+    map unChModuleName (chExposedModules ++ chOtherModules)
+  ChExeEntrypoint {chMainIs, chOtherModules} ->
+    [sourceDir </> chMainIs | Just sourceDir <- [sourceDirs]]
+      ++ map unChModuleName chOtherModules
+    where
+      sourceDirs = find (`isFilePathPrefixOf` fp) (ciSourceDirs comp)
+
+-- | For all packages in a project, find the project the given FilePath
+-- belongs to most likely.
+findPackageFor :: NonEmpty (Package pt) -> FilePath -> Maybe (Package pt)
+findPackageFor packages fp =
+  packages
+    & NonEmpty.toList
+    & sortOn (Down . pSourceDir)
+    & filter (\p -> pSourceDir p `isFilePathPrefixOf` fp)
+    & listToMaybe
+
+projectRootDir :: ProjLoc qt -> FilePath
+projectRootDir ProjLocV1CabalFile {plProjectDirV1} = plProjectDirV1
+projectRootDir ProjLocV1Dir {plProjectDirV1} = plProjectDirV1
+projectRootDir ProjLocV2File {plProjectDirV2} = plProjectDirV2
+projectRootDir ProjLocV2Dir {plProjectDirV2} = plProjectDirV2
+projectRootDir ProjLocStackYaml {plStackYaml} = takeDirectory plStackYaml
+
+projectType :: ProjLoc qt -> CabalHelper
+projectType ProjLocV1CabalFile {} = CabalV2
+projectType ProjLocV1Dir {} = CabalV2
+projectType ProjLocV2File {} = CabalV2
+projectType ProjLocV2Dir {} = CabalV2
+projectType ProjLocStackYaml {} = Stack
+
+-- ----------------------------------------------------------------------------
+--
+-- Utility functions to manipulate FilePath's
+--
+-- ----------------------------------------------------------------------------
+
+-- | Helper function to make sure that both FilePaths are normalised.
+-- Checks whether the first FilePath is a Prefix of the second FilePath.
+-- Intended usage:
+--
+-- >>> isFilePathPrefixOf "./src/" "./src/File.hs"
+-- True
+--
+-- >>> isFilePathPrefixOf "./src" "./src/File.hs"
+-- True
+--
+-- >>> isFilePathPrefixOf "./src/././" "./src/File.hs"
+-- True
+--
+-- >>> isFilePathPrefixOf "./src" "./src-dir/File.hs"
+-- False
+isFilePathPrefixOf :: FilePath -> FilePath -> Bool
+isFilePathPrefixOf dir fp = isJust $ stripFilePath dir fp
+
+-- | Strip the given directory from the filepath if and only if
+-- the given directory is a prefix of the filepath.
+--
+-- >>> stripFilePath "app" "app/File.hs"
+-- Just "File.hs"
+--
+-- >>> stripFilePath "src" "app/File.hs"
+-- Nothing
+--
+-- >>> stripFilePath "src" "src-dir/File.hs"
+-- Nothing
+--
+-- >>> stripFilePath "." "src/File.hs"
+-- Just "src/File.hs"
+--
+-- >>> stripFilePath "app/" "./app/Lib/File.hs"
+-- Just "Lib/File.hs"
+--
+-- >>> stripFilePath "/app/" "./app/Lib/File.hs"
+-- Nothing -- Nothing since '/app/' is absolute
+--
+-- >>> stripFilePath "/app" "/app/Lib/File.hs"
+-- Just "Lib/File.hs"
+stripFilePath :: FilePath -> FilePath -> Maybe FilePath
+stripFilePath "." fp
+  | isRelative fp = Just fp
+  | otherwise = Nothing
+stripFilePath dir' fp'
+  | Just relativeFpParts <- splitDir `stripPrefix` splitFp = Just (joinPath relativeFpParts)
+  | otherwise = Nothing
+  where
+    dir = normalise dir'
+    fp = normalise fp'
+    splitFp = splitPath fp
+    splitDir = splitPath dir
+    stripPrefix (x : xs) (y : ys)
+      | x `equalFilePath` y = stripPrefix xs ys
+      | otherwise = Nothing
+    stripPrefix [] ys = Just ys
+    stripPrefix _ [] = Nothing
+
+-- | Obtain all ancestors from a given directory.
+--
+-- >>> ancestors "a/b/c/d/e"
+-- [ "a/b/c/d/e", "a/b/c/d", "a/b/c", "a/b", "a", "." ]
+--
+-- >>> ancestors "/a/b/c/d/e"
+-- [ "/a/b/c/d/e", "/a/b/c/d", "/a/b/c", "/a/b", "/a", "/" ]
+--
+-- >>> ancestors "/a/b.hs"
+-- [ "/a/b.hs", "/a", "/" ]
+--
+-- >>> ancestors "a/b.hs"
+-- [ "a/b.hs", "a", "." ]
+--
+-- >>> ancestors "a/b/"
+-- [ "a/b" ]
+ancestors :: FilePath -> [FilePath]
+ancestors dir
+  | subdir `equalFilePath` dir = [dir]
+  | otherwise = dir : ancestors subdir
+  where
+    subdir = takeDirectory dir
+
+-- | Assuming a FilePath @"src\/Lib\/Lib.hs"@ and a list of directories
+-- such as @["src", "app"]@, returns the given FilePath
+-- with a matching directory stripped away.
+-- If there are multiple matches, e.g. multiple directories are a prefix
+-- of the given FilePath we return all matches.
+-- Returns an empty list if no prefix matches the given FilePath.
+--
+-- >>> relativeTo "src/Lib/Lib.hs" ["src"]
+-- ["Lib/Lib.hs"]
+--
+-- >>> relativeTo "src/Lib/Lib.hs" ["app"]
+-- []
+--
+-- >>> relativeTo "src/Lib/Lib.hs" ["src", "src/Lib"]
+-- ["Lib/Lib.hs", "Lib.hs"]
+relativeTo :: FilePath -> [FilePath] -> [FilePath]
+relativeTo file sourceDirs =
+  mapMaybe (`stripFilePath` file) sourceDirs
\ No newline at end of file
diff --git a/src/Hie/Logger.hs b/src/Hie/Logger.hs
new file mode 100644
index 0000000..1ccd970
--- /dev/null
+++ b/src/Hie/Logger.hs
@@ -0,0 +1,30 @@
+module Hie.Logger where
+
+import           Control.Monad.IO.Class
+import qualified Data.Text as T
+import           System.Log.Logger
+
+-- ---------------------------------------------------------------------
+
+setupLogger :: MonadIO m => Priority -> m ()
+setupLogger p = liftIO $ updateGlobalLogger loggerName
+                          (setLevel p)
+
+loggerName :: String
+loggerName = "gen-hie"
+
+-- ---------------------------------------------------------------------
+
+logm :: MonadIO m => String -> m ()
+logm s = liftIO $ infoM loggerName s
+
+debugm :: MonadIO m => String -> m ()
+debugm s = liftIO $ debugM loggerName s
+
+warningm :: MonadIO m => String -> m ()
+warningm s = liftIO $ warningM loggerName s
+
+errorm :: MonadIO m => String -> m ()
+errorm s = liftIO $ errorM loggerName s
+
+-- ---------------------------------------------------------------------
diff --git a/test/Spec.hs b/test/Spec.hs
index ed378ee..6323182 100644
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -49,7 +49,9 @@ spec = do
           "implicit-hie"
           [ Comp Lib "" "src",
             Comp Exe "gen-hie" "app/Main.hs",
-            Comp Test "implicit-hie-test" "test"
+            Comp Test "implicit-hie-test" "test",
+            Comp Test "unit-tests" "test/unit",
+            Comp Test "unit-tests" "test/utils"
           ]
   describe "Should Succeed"
     $ it
diff --git a/test/stackHie.yaml b/test/stackHie.yaml
index f0ed5ca..8d03553 100644
--- a/test/stackHie.yaml
+++ b/test/stackHie.yaml
@@ -8,3 +8,9 @@ cradle:
 
     - path: "./test"
       component: "implicit-hie:test:implicit-hie-test"
+
+    - path: "./test/unit"
+      component: "implicit-hie:test:unit-tests"
+
+    - path: "./test/utils"
+      component: "implicit-hie:test:unit-tests"
diff --git a/test/testdata/cabal-helper/implicit-exe/Setup.hs b/test/testdata/cabal-helper/implicit-exe/Setup.hs
new file mode 100644
index 0000000..9a994af
--- /dev/null
+++ b/test/testdata/cabal-helper/implicit-exe/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/test/testdata/cabal-helper/implicit-exe/cabal.project b/test/testdata/cabal-helper/implicit-exe/cabal.project
new file mode 100644
index 0000000..bfe6289
--- /dev/null
+++ b/test/testdata/cabal-helper/implicit-exe/cabal.project
@@ -0,0 +1 @@
+packages: ./
\ No newline at end of file
diff --git a/test/testdata/cabal-helper/implicit-exe/implicit-exe.cabal b/test/testdata/cabal-helper/implicit-exe/implicit-exe.cabal
new file mode 100644
index 0000000..3aca1b4
--- /dev/null
+++ b/test/testdata/cabal-helper/implicit-exe/implicit-exe.cabal
@@ -0,0 +1,17 @@
+cabal-version:       >=1.10
+name:                implicit-exe
+version:             0.1.0.0
+license-file:        LICENSE
+build-type:          Simple
+
+library
+  exposed-modules:     Lib
+  hs-source-dirs:      src
+  build-depends:       base
+  default-language:    Haskell2010
+
+
+executable implicit-exe
+  main-is:             src/Exe.hs
+  build-depends:       base, implicit-exe
+  default-language:    Haskell2010
\ No newline at end of file
diff --git a/test/testdata/cabal-helper/implicit-exe/src/Exe.hs b/test/testdata/cabal-helper/implicit-exe/src/Exe.hs
new file mode 100644
index 0000000..ed41929
--- /dev/null
+++ b/test/testdata/cabal-helper/implicit-exe/src/Exe.hs
@@ -0,0 +1,4 @@
+
+import Lib (someFunc)
+
+main = someFunc
\ No newline at end of file
diff --git a/test/testdata/cabal-helper/implicit-exe/src/Lib.hs b/test/testdata/cabal-helper/implicit-exe/src/Lib.hs
new file mode 100644
index 0000000..f51af83
--- /dev/null
+++ b/test/testdata/cabal-helper/implicit-exe/src/Lib.hs
@@ -0,0 +1,4 @@
+module Lib (someFunc) where
+
+someFunc :: IO ()
+someFunc = putStrLn "someFunc"
diff --git a/test/testdata/cabal-helper/mono-repo/A/A.cabal b/test/testdata/cabal-helper/mono-repo/A/A.cabal
new file mode 100644
index 0000000..e70b43f
--- /dev/null
+++ b/test/testdata/cabal-helper/mono-repo/A/A.cabal
@@ -0,0 +1,15 @@
+cabal-version:       >=1.10
+name:                A
+version:             0.1.0.0
+build-type:          Simple
+
+library
+  exposed-modules:     MyLib
+  build-depends:       base
+  default-language:    Haskell2010
+
+executable A
+  main-is:             Main.hs
+  other-modules:       MyLib
+  build-depends:       base, A
+  default-language:    Haskell2010
diff --git a/test/testdata/cabal-helper/mono-repo/A/Main.hs b/test/testdata/cabal-helper/mono-repo/A/Main.hs
new file mode 100644
index 0000000..60d904e
--- /dev/null
+++ b/test/testdata/cabal-helper/mono-repo/A/Main.hs
@@ -0,0 +1,8 @@
+module Main where
+
+import qualified MyLib (someFunc)
+
+main :: IO ()
+main = do
+  putStrLn "Hello, Haskell!"
+  MyLib.someFunc
diff --git a/test/testdata/cabal-helper/mono-repo/A/MyLib.hs b/test/testdata/cabal-helper/mono-repo/A/MyLib.hs
new file mode 100644
index 0000000..e657c44
--- /dev/null
+++ b/test/testdata/cabal-helper/mono-repo/A/MyLib.hs
@@ -0,0 +1,4 @@
+module MyLib (someFunc) where
+
+someFunc :: IO ()
+someFunc = putStrLn "someFunc"
diff --git a/test/testdata/cabal-helper/mono-repo/A/Setup.hs b/test/testdata/cabal-helper/mono-repo/A/Setup.hs
new file mode 100644
index 0000000..9a994af
--- /dev/null
+++ b/test/testdata/cabal-helper/mono-repo/A/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/test/testdata/cabal-helper/mono-repo/B/B.cabal b/test/testdata/cabal-helper/mono-repo/B/B.cabal
new file mode 100644
index 0000000..4093e1d
--- /dev/null
+++ b/test/testdata/cabal-helper/mono-repo/B/B.cabal
@@ -0,0 +1,15 @@
+cabal-version:       >=1.10
+name:                B
+version:             0.1.0.0
+build-type:          Simple
+
+library
+  exposed-modules:     MyLib
+  build-depends:       base
+  default-language:    Haskell2010
+
+executable B
+  main-is:             Main.hs
+  other-modules:       MyLib
+  build-depends:       base, B
+  default-language:    Haskell2010
diff --git a/test/testdata/cabal-helper/mono-repo/B/Main.hs b/test/testdata/cabal-helper/mono-repo/B/Main.hs
new file mode 100644
index 0000000..60d904e
--- /dev/null
+++ b/test/testdata/cabal-helper/mono-repo/B/Main.hs
@@ -0,0 +1,8 @@
+module Main where
+
+import qualified MyLib (someFunc)
+
+main :: IO ()
+main = do
+  putStrLn "Hello, Haskell!"
+  MyLib.someFunc
diff --git a/test/testdata/cabal-helper/mono-repo/B/MyLib.hs b/test/testdata/cabal-helper/mono-repo/B/MyLib.hs
new file mode 100644
index 0000000..e657c44
--- /dev/null
+++ b/test/testdata/cabal-helper/mono-repo/B/MyLib.hs
@@ -0,0 +1,4 @@
+module MyLib (someFunc) where
+
+someFunc :: IO ()
+someFunc = putStrLn "someFunc"
diff --git a/test/testdata/cabal-helper/mono-repo/B/Setup.hs b/test/testdata/cabal-helper/mono-repo/B/Setup.hs
new file mode 100644
index 0000000..9a994af
--- /dev/null
+++ b/test/testdata/cabal-helper/mono-repo/B/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/test/testdata/cabal-helper/mono-repo/C/C.cabal b/test/testdata/cabal-helper/mono-repo/C/C.cabal
new file mode 100644
index 0000000..db5e380
--- /dev/null
+++ b/test/testdata/cabal-helper/mono-repo/C/C.cabal
@@ -0,0 +1,9 @@
+cabal-version:       >=1.10
+name:                C
+version:             0.1.0.0
+build-type:          Simple
+
+library
+  exposed-modules:     MyLib
+  build-depends:       base
+  default-language:    Haskell2010
diff --git a/test/testdata/cabal-helper/mono-repo/C/MyLib.hs b/test/testdata/cabal-helper/mono-repo/C/MyLib.hs
new file mode 100644
index 0000000..e657c44
--- /dev/null
+++ b/test/testdata/cabal-helper/mono-repo/C/MyLib.hs
@@ -0,0 +1,4 @@
+module MyLib (someFunc) where
+
+someFunc :: IO ()
+someFunc = putStrLn "someFunc"
diff --git a/test/testdata/cabal-helper/mono-repo/C/Setup.hs b/test/testdata/cabal-helper/mono-repo/C/Setup.hs
new file mode 100644
index 0000000..9a994af
--- /dev/null
+++ b/test/testdata/cabal-helper/mono-repo/C/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/test/testdata/cabal-helper/mono-repo/cabal.project b/test/testdata/cabal-helper/mono-repo/cabal.project
new file mode 100644
index 0000000..cf2eab3
--- /dev/null
+++ b/test/testdata/cabal-helper/mono-repo/cabal.project
@@ -0,0 +1,4 @@
+packages:
+  ./A/
+  ./B/
+  ./C/
\ No newline at end of file
diff --git a/test/testdata/cabal-helper/multi-source-dirs/Setup.hs b/test/testdata/cabal-helper/multi-source-dirs/Setup.hs
new file mode 100644
index 0000000..9a994af
--- /dev/null
+++ b/test/testdata/cabal-helper/multi-source-dirs/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/test/testdata/cabal-helper/multi-source-dirs/multi-source-dirs.cabal b/test/testdata/cabal-helper/multi-source-dirs/multi-source-dirs.cabal
new file mode 100644
index 0000000..5856868
--- /dev/null
+++ b/test/testdata/cabal-helper/multi-source-dirs/multi-source-dirs.cabal
@@ -0,0 +1,11 @@
+cabal-version:       >=1.10
+name:                multi-source-dirs
+version:             0.1.0.0
+license-file:        LICENSE
+build-type:          Simple
+
+library
+  exposed-modules:     Lib, BetterLib
+  hs-source-dirs:      src, src/input
+  build-depends:       base
+  default-language:    Haskell2010
diff --git a/test/testdata/cabal-helper/multi-source-dirs/src/BetterLib.hs b/test/testdata/cabal-helper/multi-source-dirs/src/BetterLib.hs
new file mode 100644
index 0000000..0784c76
--- /dev/null
+++ b/test/testdata/cabal-helper/multi-source-dirs/src/BetterLib.hs
@@ -0,0 +1,5 @@
+module BetterLib where
+
+
+foo = 3
+bar = "String"
\ No newline at end of file
diff --git a/test/testdata/cabal-helper/multi-source-dirs/src/input/Lib.hs b/test/testdata/cabal-helper/multi-source-dirs/src/input/Lib.hs
new file mode 100644
index 0000000..6c37234
--- /dev/null
+++ b/test/testdata/cabal-helper/multi-source-dirs/src/input/Lib.hs
@@ -0,0 +1,6 @@
+module Lib where
+
+foobar = 15
+
+fizbuzz :: Int -> String
+fizbuzz n = "Fizz"
\ No newline at end of file
diff --git a/test/testdata/cabal-helper/simple-cabal/MyLib.hs b/test/testdata/cabal-helper/simple-cabal/MyLib.hs
new file mode 100644
index 0000000..e657c44
--- /dev/null
+++ b/test/testdata/cabal-helper/simple-cabal/MyLib.hs
@@ -0,0 +1,4 @@
+module MyLib (someFunc) where
+
+someFunc :: IO ()
+someFunc = putStrLn "someFunc"
diff --git a/test/testdata/cabal-helper/simple-cabal/Setup.hs b/test/testdata/cabal-helper/simple-cabal/Setup.hs
new file mode 100644
index 0000000..9a994af
--- /dev/null
+++ b/test/testdata/cabal-helper/simple-cabal/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/test/testdata/cabal-helper/simple-cabal/simple-cabal-test.cabal b/test/testdata/cabal-helper/simple-cabal/simple-cabal-test.cabal
new file mode 100644
index 0000000..3c8be5d
--- /dev/null
+++ b/test/testdata/cabal-helper/simple-cabal/simple-cabal-test.cabal
@@ -0,0 +1,10 @@
+cabal-version:       >=1.10
+name:                simple-cabal-test
+version:             0.1.0.0
+license-file:        LICENSE
+build-type:          Simple
+
+library
+  exposed-modules:     MyLib
+  build-depends:       base
+  default-language:    Haskell2010
diff --git a/test/testdata/cabal-helper/simple-stack/MyLib.hs b/test/testdata/cabal-helper/simple-stack/MyLib.hs
new file mode 100644
index 0000000..e657c44
--- /dev/null
+++ b/test/testdata/cabal-helper/simple-stack/MyLib.hs
@@ -0,0 +1,4 @@
+module MyLib (someFunc) where
+
+someFunc :: IO ()
+someFunc = putStrLn "someFunc"
diff --git a/test/testdata/cabal-helper/simple-stack/Setup.hs b/test/testdata/cabal-helper/simple-stack/Setup.hs
new file mode 100644
index 0000000..9a994af
--- /dev/null
+++ b/test/testdata/cabal-helper/simple-stack/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/test/testdata/cabal-helper/simple-stack/simple-stack-test.cabal b/test/testdata/cabal-helper/simple-stack/simple-stack-test.cabal
new file mode 100644
index 0000000..264baeb
--- /dev/null
+++ b/test/testdata/cabal-helper/simple-stack/simple-stack-test.cabal
@@ -0,0 +1,10 @@
+cabal-version:       >=1.10
+name:                simple-stack-test
+version:             0.1.0.0
+license-file:        LICENSE
+build-type:          Simple
+
+library
+  exposed-modules:     MyLib
+  build-depends:       base
+  default-language:    Haskell2010
diff --git a/test/testdata/cabal-helper/sub-package/Setup.hs b/test/testdata/cabal-helper/sub-package/Setup.hs
new file mode 100644
index 0000000..9a994af
--- /dev/null
+++ b/test/testdata/cabal-helper/sub-package/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/test/testdata/cabal-helper/sub-package/app/Main.hs b/test/testdata/cabal-helper/sub-package/app/Main.hs
new file mode 100644
index 0000000..60d904e
--- /dev/null
+++ b/test/testdata/cabal-helper/sub-package/app/Main.hs
@@ -0,0 +1,8 @@
+module Main where
+
+import qualified MyLib (someFunc)
+
+main :: IO ()
+main = do
+  putStrLn "Hello, Haskell!"
+  MyLib.someFunc
diff --git a/test/testdata/cabal-helper/sub-package/plugins-api/PluginLib.hs b/test/testdata/cabal-helper/sub-package/plugins-api/PluginLib.hs
new file mode 100644
index 0000000..55a7098
--- /dev/null
+++ b/test/testdata/cabal-helper/sub-package/plugins-api/PluginLib.hs
@@ -0,0 +1,4 @@
+module PluginLib (someFunc) where
+
+someFunc :: IO ()
+someFunc = putStrLn "someFunc"
diff --git a/test/testdata/cabal-helper/sub-package/plugins-api/Setup.hs b/test/testdata/cabal-helper/sub-package/plugins-api/Setup.hs
new file mode 100644
index 0000000..9a994af
--- /dev/null
+++ b/test/testdata/cabal-helper/sub-package/plugins-api/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/test/testdata/cabal-helper/sub-package/plugins-api/plugins-api.cabal b/test/testdata/cabal-helper/sub-package/plugins-api/plugins-api.cabal
new file mode 100644
index 0000000..223fa73
--- /dev/null
+++ b/test/testdata/cabal-helper/sub-package/plugins-api/plugins-api.cabal
@@ -0,0 +1,10 @@
+cabal-version:       >=1.10
+name:                plugins-api
+version:             0.1.0.0
+license-file:        LICENSE
+build-type:          Simple
+
+library
+  exposed-modules:     PluginLib
+  build-depends:       base
+  default-language:    Haskell2010
diff --git a/test/testdata/cabal-helper/sub-package/src/MyLib.hs b/test/testdata/cabal-helper/sub-package/src/MyLib.hs
new file mode 100644
index 0000000..53ea5c6
--- /dev/null
+++ b/test/testdata/cabal-helper/sub-package/src/MyLib.hs
@@ -0,0 +1,6 @@
+module MyLib (someFunc) where
+
+import qualified PluginLib as L
+
+someFunc :: IO ()
+someFunc = L.someFunc
diff --git a/test/testdata/cabal-helper/sub-package/sub-package.cabal b/test/testdata/cabal-helper/sub-package/sub-package.cabal
new file mode 100644
index 0000000..ba36f1b
--- /dev/null
+++ b/test/testdata/cabal-helper/sub-package/sub-package.cabal
@@ -0,0 +1,17 @@
+cabal-version:       >=1.10
+name:                sub-package
+version:             0.1.0.0
+license-file:        LICENSE
+build-type:          Simple
+
+library
+  exposed-modules:     MyLib
+  build-depends:       base, plugins-api
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+
+executable sub-package
+  main-is:             Main.hs
+  build-depends:       base, sub-package
+  hs-source-dirs:      app
+  default-language:    Haskell2010
diff --git a/test/unit/CabalHelperSpec.hs b/test/unit/CabalHelperSpec.hs
new file mode 100644
index 0000000..f9c6d1f
--- /dev/null
+++ b/test/unit/CabalHelperSpec.hs
@@ -0,0 +1,236 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module CabalHelperSpec where
+
+import Data.Maybe (isJust)
+import HIE.Bios.Types (Cradle, CradleLoadResult (..), cradleOptsProg, runCradle)
+import Hie.CabalHelper
+import System.Directory (findExecutable, getCurrentDirectory, removeFile)
+import System.FilePath
+import Test.Hspec
+import TestUtils
+
+rootPath :: FilePath -> FilePath
+rootPath cwd = cwd </> "test" </> "testdata" </> "cabal-helper"
+
+implicitExePath :: FilePath -> FilePath
+implicitExePath cwd = rootPath cwd </> "implicit-exe"
+
+monoRepoPath :: FilePath -> FilePath
+monoRepoPath cwd = rootPath cwd </> "mono-repo"
+
+subPackagePath :: FilePath -> FilePath
+subPackagePath cwd = rootPath cwd </> "sub-package"
+
+simpleCabalPath :: FilePath -> FilePath
+simpleCabalPath cwd = rootPath cwd </> "simple-cabal"
+
+simpleStackPath :: FilePath -> FilePath
+simpleStackPath cwd = rootPath cwd </> "simple-stack"
+
+multiSourceDirsPath :: FilePath -> FilePath
+multiSourceDirsPath cwd = rootPath cwd </> "multi-source-dirs"
+
+spec :: Spec
+spec = beforeAll_ setupStackFiles $ do
+  describe "stack and cabal executables should be accesible" $ do
+    it "cabal is accesible" $ do
+      stack <- findExecutable "cabal"
+      stack `shouldSatisfy` isJust
+    it "stack is accesible" $ do
+      cabal <- findExecutable "stack"
+      cabal `shouldSatisfy` isJust
+  describe "cabal-helper spec" $ do
+    describe "find entry point" findCabalHelperEntryPointSpec
+    describe "cradle discovery and loading" cabalHelperCradleSpec
+
+cabalHelperCradleSpec :: Spec
+cabalHelperCradleSpec = do
+  cwd <- runIO getCurrentDirectory
+  describe "dummy filepath, finds none-cradle" $ do
+    it "implicit exe" $ do
+      crdl <- cabalHelperCradle (implicitExePath cwd </> "File.hs")
+      crdl `shouldSatisfy` isCabalCradle
+    it "mono repo" $ do
+      crdl <- cabalHelperCradle (monoRepoPath cwd </> "File.hs")
+      crdl `shouldSatisfy` isCabalCradle
+    it "stack repo" $ do
+      crdl <- cabalHelperCradle (simpleStackPath cwd </> "File.hs")
+      crdl `shouldSatisfy` isStackCradle
+    it "cabal repo" $
+      pendingWith "Can not work because of global `cabal.project`"
+    -- crdl <- cabalHelperCradle (simpleCabalPath cwd </> "File.hs")
+    -- crdl `shouldSatisfy` isCabalCradle
+    it "sub package" $ do
+      crdl <- cabalHelperCradle (subPackagePath cwd </> "File.hs")
+      crdl `shouldSatisfy` isStackCradle
+    it "multi-source-dirs" $ do
+      crdl <- cabalHelperCradle (multiSourceDirsPath cwd </> "File.hs")
+      crdl `shouldSatisfy` isStackCradle
+  describe "existing projects" $ do
+    it "implicit exe" $ do
+      let fp = implicitExePath cwd </> "src" </> "Exe.hs"
+      componentTest fp isCabalCradle
+    it "mono repo" $ do
+      let fp = monoRepoPath cwd </> "A" </> "Main.hs"
+      componentTest fp isCabalCradle
+    it "stack repo" $ do
+      let fp = simpleStackPath cwd </> "MyLib.hs"
+      componentTest fp isStackCradle
+    it "cabal repo" $
+      pendingWith "Can not work because of global `cabal.project`"
+    -- let fp = (simpleCabalPath cwd </> "MyLib.hs")
+    -- componentTest fp isStackCradle
+    it "sub package" $ do
+      let fp = subPackagePath cwd </> "plugins-api" </> "PluginLib.hs"
+      componentTest fp isStackCradle
+    it "multi-source-dirs, nested dir" $ do
+      let fp = multiSourceDirsPath cwd </> "src" </> "input" </> "Lib.hs"
+      componentTest fp isStackCradle
+    it "multi-source-dirs" $ do
+      let fp = multiSourceDirsPath cwd </> "src" </> "BetterLib.hs"
+      componentTest fp isStackCradle
+
+componentTest :: FilePath -> (Cradle CabalHelper -> Bool) -> Expectation
+componentTest fp testCradleType = do
+  crdl <- cabalHelperCradle fp
+  crdl `shouldSatisfy` testCradleType
+
+-- TODO: this works but CI crashes
+-- loadComponent crdl fp
+
+loadComponent :: Cradle CabalHelper -> FilePath -> Expectation
+loadComponent crdl fp = do
+  result <- runCradle (cradleOptsProg crdl) (\_ -> return ()) fp
+  case result of
+    CradleFail err -> expectationFailure $ "Loading should not have failed: " ++ show err
+    _ -> return ()
+  return ()
+
+findCabalHelperEntryPointSpec :: Spec
+findCabalHelperEntryPointSpec = do
+  cwd <- runIO getCurrentDirectory
+  describe "implicit exe" $ do
+    it "dummy filepath" $ do
+      let dummyFile = implicitExePath cwd </> "File.hs"
+      cabalTest dummyFile
+    it "source component" $ do
+      let libFile = implicitExePath cwd </> "src" </> "Lib.hs"
+      cabalTest libFile
+    it "executable component" $ do
+      let mainFile = implicitExePath cwd </> "src" </> "Exe.hs"
+      cabalTest mainFile
+  describe "mono repo" $ do
+    it "dummy filepath" $ do
+      let dummyFile = monoRepoPath cwd </> "File.hs"
+      cabalTest dummyFile
+    it "existing executable" $ do
+      let mainFile = monoRepoPath cwd </> "A" </> "Main.hs"
+      cabalTest mainFile
+  describe "sub package repo" $ do
+    it "dummy filepath" $ do
+      let dummyFile = subPackagePath cwd </> "File.hs"
+      stackTest dummyFile
+    it "existing executable" $ do
+      let mainFile = subPackagePath cwd </> "plugins-api" </> "PluginLib.hs"
+      stackTest mainFile
+  describe "stack repo" $ do
+    it "dummy filepath" $ do
+      let dummyFile = simpleStackPath cwd </> "File.hs"
+      stackTest dummyFile
+    it "real filepath" $ do
+      let dummyFile = simpleStackPath cwd </> "MyLib.hs"
+      stackTest dummyFile
+  describe "multi-source-dirs" $ do
+    it "dummy filepath" $ do
+      let dummyFile = multiSourceDirsPath cwd </> "File.hs"
+      stackTest dummyFile
+    it "real filepath" $ do
+      let dummyFile = multiSourceDirsPath cwd </> "src" </> "BetterLib.hs"
+      stackTest dummyFile
+    it "nested filpath" $ do
+      let dummyFile = multiSourceDirsPath cwd </> "src" </> "input" </> "Lib.hs"
+      stackTest dummyFile
+  describe "simple cabal repo"
+    $ it "Find project root with dummy filepath"
+    $ pendingWith "Change test-setup, we will always find `cabal.project` in root dir"
+
+-- -------------------------------------------------------------
+
+cabalTest :: FilePath -> IO ()
+cabalTest fp = do
+  entryPoint <- findCabalHelperEntryPoint fp
+  let Just proj = entryPoint
+      isCabal = isCabalProject proj
+  shouldBe isCabal True
+
+stackTest :: FilePath -> IO ()
+stackTest fp = do
+  entryPoint <- findCabalHelperEntryPoint fp
+  let Just proj = entryPoint
+      isStack = isStackProject proj
+  shouldBe isStack True
+
+-- -------------------------------------------------------------
+
+setupStackFiles :: IO ()
+setupStackFiles = do
+  resolver <- readResolver
+  cwd <- getCurrentDirectory
+  writeFile (implicitExePath cwd </> "stack.yaml") (standardStackYaml resolver)
+  writeFile (monoRepoPath cwd </> "stack.yaml") (monoRepoStackYaml resolver)
+  writeFile (subPackagePath cwd </> "stack.yaml") (subPackageStackYaml resolver)
+  writeFile (simpleStackPath cwd </> "stack.yaml") (standardStackYaml resolver)
+  writeFile
+    (multiSourceDirsPath cwd </> "stack.yaml")
+    (standardStackYaml resolver)
+
+cleanupStackFiles :: IO ()
+cleanupStackFiles = do
+  cwd <- getCurrentDirectory
+  removeFile (implicitExePath cwd </> "stack.yaml")
+  removeFile (monoRepoPath cwd </> "stack.yaml")
+  removeFile (subPackagePath cwd </> "stack.yaml")
+  removeFile (simpleStackPath cwd </> "stack.yaml")
+  removeFile (multiSourceDirsPath cwd </> "stack.yaml")
+
+-- -------------------------------------------------------------
+
+standardStackYaml :: String -> String
+standardStackYaml resolver =
+  unlines
+    [ "# WARNING: THIS FILE IS AUTOGENERATED IN test/utils/CabalHelperSpec. IT WILL BE OVERWRITTEN ON EVERY TEST RUN",
+      "resolver: " ++ resolver,
+      "packages:",
+      "- '.'",
+      "extra-deps: []",
+      "flags: {}",
+      "extra-package-dbs: []"
+    ]
+
+monoRepoStackYaml :: String -> String
+monoRepoStackYaml resolver =
+  unlines
+    [ "# WARNING: THIS FILE IS AUTOGENERATED IN test/utils/CabalHelperSpec. IT WILL BE OVERWRITTEN ON EVERY TEST RUN",
+      "resolver: " ++ resolver,
+      "packages:",
+      "- 'A'",
+      "- 'B'",
+      "- 'C'",
+      "extra-deps: []",
+      "flags: {}",
+      "extra-package-dbs: []"
+    ]
+
+subPackageStackYaml :: String -> String
+subPackageStackYaml resolver =
+  unlines
+    [ "# WARNING: THIS FILE IS AUTOGENERATED IN test/unit/CabalHelperSpec. IT WILL BE OVERWRITTEN ON EVERY TEST RUN",
+      "resolver: " ++ resolver,
+      "packages:",
+      "- '.'",
+      "- 'plugins-api'",
+      "extra-deps: []",
+      "flags: {}",
+      "extra-package-dbs: []"
+    ]
diff --git a/test/unit/Spec.hs b/test/unit/Spec.hs
new file mode 100644
index 0000000..5d7c833
--- /dev/null
+++ b/test/unit/Spec.hs
@@ -0,0 +1,18 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE CPP #-}
+
+#ifdef CABAL_HELPER_SUPPORT
+import qualified CabalHelperSpec
+#endif
+import Test.Hspec
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec =
+#ifdef CABAL_HELPER_SUPPORT
+    CabalHelperSpec.spec
+#else
+    pure ()
+#endif
diff --git a/test/utils/TestUtils.hs b/test/utils/TestUtils.hs
new file mode 100644
index 0000000..d6f546d
--- /dev/null
+++ b/test/utils/TestUtils.hs
@@ -0,0 +1,28 @@
+{-# LANGUAGE CPP, OverloadedStrings, NamedFieldPuns #-}
+module TestUtils where
+
+import Data.Aeson.Types (typeMismatch)
+import Data.Yaml
+import Data.Text
+import Data.Typeable
+
+-- |Choose a resolver based on the current compiler, otherwise HaRe/ghc-mod will
+-- not be able to load the files
+readResolver :: IO String
+readResolver = readResolverFrom stackYaml
+
+stackYaml :: FilePath
+stackYaml = "stack.yaml"
+
+newtype StackResolver = StackResolver String
+
+instance FromJSON StackResolver where
+  parseJSON (Object x) = StackResolver <$> x .: pack "resolver"
+  parseJSON invalid = typeMismatch "StackResolver" invalid
+
+readResolverFrom :: FilePath -> IO String
+readResolverFrom yamlPath = do
+  result <- decodeFileEither yamlPath
+  case result of
+    Left err -> error $ yamlPath ++ " parsing failed: " ++ show err
+    Right (StackResolver res) -> return res