diff --git a/ChangeLog b/ChangeLog
deleted file mode 100644
--- a/ChangeLog
+++ /dev/null
@@ -1,93 +0,0 @@
-2020-06-26 - 0.5.1
-
-	* Fix printing of current directory in wrapper script [#206](https://github.com/mpickering/hie-bios/pull/206)
-	* Export Cradle utilizes [#189](https://github.com/mpickering/hie-bios/pull/189)
-
-2020-05-08 - 0.5.0
-
-	* Add cabal.project.local to cabal cradle dependencies [#184](https://github.com/mpickering/hie-bios/pull/184)
-	* Remove unused environment variables to simplify code. [#182](https://github.com/mpickering/hie-bios/pull/182)
-	* Clean up hie-bios wrapper scripts after they are used. [#179](https://github.com/mpickering/hie-bios/pull/179)
-	* Avoid error in windows due to temp file being locked. [#175](https://github.com/mpickering/hie-bios/pull/175)
-	* Get building with ghc-8.10. [#173](https://github.com/mpickering/hie-bios/pull/173)
-	* Add getCompilerOptionsWithLogger convenience function.
-	* Add componentRoot to ComponentOptions. [#166](https://github.com/mpickering/hie-bios/pull/166)
-		Options may be relative to the componentRoot.
-	* Add makeDynFlagsAbsolute to fix mangling of ghc options starting with "-i". [#166](https://github.com/mpickering/hie-bios/pull/166)
-		Breaks backwards-compatibility, because ComponentOptions now may contain
-		filepaths relative to the component root directory.
-		This function needs to be invoked on the parsed 'DynFlags' to normalise the filepaths.
-	* Fix Ghci Script parses space in Filepath as Module (#162)
-	* Correct path to .hie-bios example in readme (#159)
-	* Relax upper bound for 'extra' (#161)
-
-2020-01-29 - 0.4.0
-
-	* Return CompilerOptions in initialization (#130)
-	* Implement hook into config parser (#131)
-	* Enable GHC 8.8.1 windows ci (#128)
-	* Catch permission errors in cradle discovery (#127)
-	* Add explicit cradle predicates and multi cradle depend on its cradles (#119)
-	* Fix outdated direct cradle in README (#124)
-	* Pass filepath to cabal v2-repl when getting flags (#123)
-	* CPP for GHC 8.10 compatibility (#134)
-	* Derive Ord for ComponentOptions (#133)
-	* Lower the required version of the GHC dependency (#138)
-	* Add tests for implicit cradles (#135)
-	* Add Functor instance for Cradle and ActionName (#140)
-	* Remove Show instance from public API (#146)
-	* Add Show instance for CradleLoadResult (#145)
-	* Typo in debug message (#144)
-	* Add lower bound for aeson and clean-up API (#142)
-
-2019-12-19 - 0.3.2
-
-	* Compile windows wrapper script in a a more appropiate directory. (#109)
-	* Fix situation in wrapper script when environmental variable wasn't set. (#109)
-
-2019-12-18 - 0.3.1
-
-	* Fix bug in the windows wrapper script (#108)
-
-
-2019-12-15 - 0.3.0
-
-	* Add multi cradle, cabal multi cradle and none cradle
-	* Remove obelisk, bazel and default cradle types
-	* bios program now expects arguments to be separated by newlines rather than
-		spaces. (#80)
-	* Only try to use stack cradle if `stack` is executable.
-	* Filter out `-w -v0` from cabal output when using cabal cradle.
-	* Initialise plugins when loading a module.
-	* Interface file cache persists between loads -- this greatly speeds up
-	reloading a project if the options don't change.
-	* Reuse wrapper executable on windows if one already exists.
-	* Make stack cradle work more like the cabal cradle
-		- Syntax for specifying a specific component
-		- Targets are read from the ghci script file
-	* Cradles now use a temporary file to communicate arguments to hie-bios.
-	bios cradles should consult the HIE_BIOS_OUTPUT envvar for the filepath to
-	write the arguments seperated by newlines.
-
-2019-09-19 - 0.2.1
-
-	* Make stack cradle use the same wrappers as cabal cradle. Fixes some issues
-		on windows.
-
-2019-09-18 - 0.2.0
-
-	* Compat with 8.2 and 8.8
-	* Add support for explicitly specifying dependencies for a cradle
-	* Separate arguments by null bytes, so arguments can contain spaces
-	(cabal/stack wrapper)
-	* Add --help to CLI
-	* Fix the directories that certain processes run in
-
-2019-09-07 - 0.1.1
-
-	* Compat with GHC 8.4
-	* Fix long paths issue on windows
-	* Handle projects with .o files
-
-2019-09-06 - 0.1.0
-	* First release
diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,117 @@
+# ChangeLog hie-bios
+
+## 2020-07-12 - 0.6.0
+
+### New Features:
+
+* Add getRuntimeGhcLibDir and getRuntimeGhcVersion functions through a new runGhcCmd API [#207](https://github.com/mpickering/hie-bios/pull/207) [#224](https://github.com/mpickering/hie-bios/pull/224)
+
+### Bug Fixes:
+
+* Improve the README [#225](https://github.com/mpickering/hie-bios/pull/225)
+* Detect implicit cabal cradle in the absence of cabal.project [#221](https://github.com/mpickering/hie-bios/pull/221)
+* Dont resolve symlinks in cradle discovery [#219](https://github.com/mpickering/hie-bios/pull/219)
+* Make Cradle dependencies for stack and cabal more reasonable [#209](https://github.com/mpickering/hie-bios/pull/209)
+* This ships with a known bug: `stack` lists cradle dependencies from
+	sub-directories incorrectly.
+* Fix absolute mains [#205](https://github.com/mpickering/hie-bios/pull/205)
+* Improve filtering of rts arguments from stack and cabal cradles [#197](https://github.com/mpickering/hie-bios/pull/197)
+* Make package db paths absolute [#193](https://github.com/mpickering/hie-bios/pull/193)
+* Add shell and dependency-shell attributes to bios cradle type [#188](https://github.com/mpickering/hie-bios/pull/188)
+* Store dependencies in CradleError [#186](https://github.com/mpickering/hie-bios/pull/186)
+* Add cabal.project.local to cabal cradle dependencies [#184](https://github.com/mpickering/hie-bios/pull/184)
+* Remove outdated reference to $HIE_BIOS_GHC[_ARGS]
+
+## 2020-06-26 - 0.5.1
+
+* Fix printing of current directory in wrapper script [#206](https://github.com/mpickering/hie-bios/pull/206)
+* Export Cradle utilizes [#189](https://github.com/mpickering/hie-bios/pull/189)
+
+## 2020-05-08 - 0.5.0
+
+* Add cabal.project.local to cabal cradle dependencies [#184](https://github.com/mpickering/hie-bios/pull/184)
+* Remove unused environment variables to simplify code. [#182](https://github.com/mpickering/hie-bios/pull/182)
+* Clean up hie-bios wrapper scripts after they are used. [#179](https://github.com/mpickering/hie-bios/pull/179)
+* Avoid error in windows due to temp file being locked. [#175](https://github.com/mpickering/hie-bios/pull/175)
+* Get building with ghc-8.10. [#173](https://github.com/mpickering/hie-bios/pull/173)
+* Add getCompilerOptionsWithLogger convenience function.
+* Add componentRoot to ComponentOptions. [#166](https://github.com/mpickering/hie-bios/pull/166)
+Options may be relative to the componentRoot.
+* Add makeDynFlagsAbsolute to fix mangling of ghc options starting with "-i". [#166](https://github.com/mpickering/hie-bios/pull/166)
+Breaks backwards-compatibility, because ComponentOptions now may contain
+filepaths relative to the component root directory.
+This function needs to be invoked on the parsed 'DynFlags' to normalise the filepaths.
+* Fix Ghci Script parses space in Filepath as Module (#162)
+* Correct path to .hie-bios example in readme (#159)
+* Relax upper bound for 'extra' (#161)
+
+## 2020-01-29 - 0.4.0
+
+* Return CompilerOptions in initialization (#130)
+* Implement hook into config parser (#131)
+* Enable GHC 8.8.1 windows ci (#128)
+* Catch permission errors in cradle discovery (#127)
+* Add explicit cradle predicates and multi cradle depend on its cradles (#119)
+* Fix outdated direct cradle in README (#124)
+* Pass filepath to cabal v2-repl when getting flags (#123)
+* CPP for GHC 8.10 compatibility (#134)
+* Derive Ord for ComponentOptions (#133)
+* Lower the required version of the GHC dependency (#138)
+* Add tests for implicit cradles (#135)
+* Add Functor instance for Cradle and ActionName (#140)
+* Remove Show instance from public API (#146)
+* Add Show instance for CradleLoadResult (#145)
+* Typo in debug message (#144)
+* Add lower bound for aeson and clean-up API (#142)
+
+## 2019-12-19 - 0.3.2
+
+* Compile windows wrapper script in a a more appropiate directory. (#109)
+* Fix situation in wrapper script when environmental variable wasn't set. (#109)
+
+## 2019-12-18 - 0.3.1
+
+* Fix bug in the windows wrapper script (#108)
+
+## 2019-12-15 - 0.3.0
+
+* Add multi cradle, cabal multi cradle and none cradle
+* Remove obelisk, bazel and default cradle types
+* bios program now expects arguments to be separated by newlines rather than
+spaces. (#80)
+* Only try to use stack cradle if `stack` is executable.
+* Filter out `-w -v0` from cabal output when using cabal cradle.
+* Initialise plugins when loading a module.
+* Interface file cache persists between loads -- this greatly speeds up
+reloading a project if the options don't change.
+* Reuse wrapper executable on windows if one already exists.
+* Make stack cradle work more like the cabal cradle
+- Syntax for specifying a specific component
+- Targets are read from the ghci script file
+* Cradles now use a temporary file to communicate arguments to hie-bios.
+bios cradles should consult the HIE_BIOS_OUTPUT envvar for the filepath to
+write the arguments seperated by newlines.
+
+## 2019-09-19 - 0.2.1
+
+* Make stack cradle use the same wrappers as cabal cradle. Fixes some issues
+on windows.
+
+## 2019-09-18 - 0.2.0
+
+* Compat with 8.2 and 8.8
+* Add support for explicitly specifying dependencies for a cradle
+* Separate arguments by null bytes, so arguments can contain spaces
+(cabal/stack wrapper)
+* Add --help to CLI
+* Fix the directories that certain processes run in
+
+## 2019-09-07 - 0.1.1
+
+* Compat with GHC 8.4
+* Fix long paths issue on windows
+* Handle projects with .o files
+
+## 2019-09-06 - 0.1.0
+
+* First release
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,12 +1,40 @@
 # hie-bios
 
+- [hie-bios](#hie-bios)
+  - [Explicit Configuration](#explicit-configuration)
+    - [Stack](#stack)
+      - [Debugging a `stack` cradle](#debugging-a-stack-cradle)
+      - [Ignoring directories](#ignoring-directories)
+      - [Internal Libraries](#internal-libraries)
+    - [Cabal](#cabal)
+      - [Debugging a `cabal` cradle](#debugging-a-cabal-cradle)
+      - [Ignoring directories](#ignoring-directories-1)
+    - [Bios](#bios)
+      - [Debugging a `bios` cradle](#debugging-a-bios-cradle)
+    - [Direct](#direct)
+      - [Debugging a `direct` cradle](#debugging-a-direct-cradle)
+    - [None](#none)
+  - [Multi-Cradle](#multi-cradle)
+  - [Cradle Dependencies](#cradle-dependencies)
+  - [Configuration specification](#configuration-specification)
+  - [Testing your configuration](#testing-your-configuration)
+  - [Implicit Configuration](#implicit-configuration)
+    - [Priority](#priority)
+    - [`cabal-install`](#cabal-install)
+    - [`stack`](#stack-1)
+    - [`bios`](#bios-1)
+  - [Supporting Bazel and Obelisk](#supporting-bazel-and-obelisk)
+
 `hie-bios` is the way to specify how
-[`hie`](https://github.com/haskell/haskell-ide-engine) and
-[`ghcide`](https://github.com/digital-asset/ghcide) sets up a GHC API session.
+[`haskell-language-server`](https://github.com/haskell/haskell-language-server)
+and [`ghcide`](https://github.com/digital-asset/ghcide) set up a GHC API session.
 
 Given a Haskell project that is managed by Stack, Cabal, or other package tools,
-`hie` needs to know the full set of flags to pass to GHC in order to build the
-project. `hie-bios` satisfies this need.
+`haskell-language-server` needs to know the full set of flags to pass to GHC in
+order to build the project. These flags might contain some compilation options
+like `-O2`, but a lot of the time they are package dependencies such as
+`-package-id directory-1.3.6.0`, which also need to be built beforehand.
+`hie-bios` satisfies both these needs.
 
 Its design is motivated by the guiding principle:
 
@@ -16,65 +44,347 @@
 Using this principle, it is possible
 to easily support a wide range of tools including `cabal-install`, `stack`,
 `rules_haskell`, `hadrian` and `obelisk` without major contortions.
-`hie-bios` does not depend on the `Cabal` library nor does not
+`hie-bios` does not depend on the `Cabal` library nor does it
 read any complicated build products and so on.
 
 How does a tool specify a session? A session is fully specified by a set of
 standard GHC flags. Most tools already produce this information if they support
 a `repl` command. Launching a repl is achieved by calling `ghci` with the
 right flags to specify the package database. `hie-bios` needs a way to get
-these flags and then it can set up GHC API session correctly.
+these flags and then it can set up a GHC API session correctly.
 
-Futher it means that any failure to set up the API session is the responsibility
+Further it means that any failure to set up the API session is the responsibility
 of the build tool. It is up to them to provide the correct information if they
 want the tool to work correctly.
 
 ## Explicit Configuration
 
 The user can place a `hie.yaml` file in the root of the workspace which
-describes how to setup the environment. For example, to explicitly state
-that you want to use `stack` then the configuration file would look like:
+describes how to set up the environment. There are several supported ways to setup the environment.
 
+### Stack
+
+To explicitly state that you want to use `stack`, the basic configuration `hie.yaml` would look like:
+
 ```yaml
-cradle: {stack: {component: "haskell-ide-engine:lib" }}
+cradle:
+  stack:
 ```
 
-While the component is optional, this is recommended to make sure the correct
-component is loaded.
+This configuration suffices if your whole project can be loaded by the command `stack repl`. As a rule of thumb, this works if the project consists of only one executable, one library and one test-suite.
 
-To use `cabal`, the explicit configuration looks similar.
-Note that `cabal` and `stack` have different way of specifying their
+If your project is more complicated, you need to specify which [components](https://docs.haskellstack.org/en/stable/build_command/#components) you want to load. A component is, roughly speaking, a library/executable/test-suite or benchmark in `stack`. You can view the components/targets of a stack project by executing the command:
+``` sh
+$ stack ide targets
+```
+
+Since we have two test-suites, one executable and a library, for `hie-bios`, this would output the following:
+
+``` sh
+$ stack ide targets
+hie-bios:lib
+hie-bios:exe:hie-bios
+hie-bios:test:bios-tests
+hie-bios:test:parser-tests
+```
+
+For an explanation of the target syntax, we refer to the documentation of the [target syntax](https://docs.haskellstack.org/en/stable/build_command/#target-syntax).
+
+To tell `hie-bios` which component it should load, the following `hie.yaml` can be used:
+
+```yaml
+cradle:
+  stack:
+    component: "<component name>"
+```
+
+where `<component name>` is the name of the component/target you want to load.
+While the component is optional, this is recommended to make sure the correct component is loaded.
+
+Why is this not enough? Usually, you have multiple components with different dependencies. Your library won't depend on `tasty` or `hspec`, but your test-suite might. With this setup, you would only be able to load files from the given component.
+
+Since you rarely only want to load a single component in a `stack` project, we have special syntax to be able to conveniently specify which directory belongs to which component. It is basically a [multi-cradle](#multi-cradle).
+
+```yaml
+cradle:
+  stack:
+    - path: "./src"
+      component: "hie-bios:lib"
+    - path: "./exe"
+      component: "hie-bios:exe:hie-bios"
+    - path: "./tests/BiosTests.hs"
+      component: "hie-bios:test:hie-bios"
+    - path: "./tests/ParserTests.hs"
+      component: "hie-bios:test:parser-tests"
+```
+
+Here you can see two important features:
+  * We provide a mapping from a filepath to component.
+    * That way, we specify that a file such as `./src/HIE/Bios.hs` belongs to the component `hie-bios:lib`.
+  * The filepath can be a file.
+    * This is convenient if components are overlapping.
+
+This way we specified which component needs to be compiled given a source file for our whole project.
+
+#### Debugging a `stack` cradle
+
+If you find that `hie-bios` can't load a certain component or file, run `stack repl` and `stack repl <component name>` to see if `stack` succeeds in building your project. Chances are that there is a problem in your project and if you fix that, `hie-bios` will succeed to load it.
+
+Also, see notes for [testing your configuration](#testing-your-configuration).
+
+Otherwise, please open an [issue](https://github.com/mpickering/hie-bios/issues/new).
+
+#### Ignoring directories
+
+You can combine the [multi-cradle](#multi-cradle) with a [none-cradle](#none-cradle) to ignore all source files in a certain directory. The syntax is a bit verbose:
+
+```yaml
+cradle:
+  multi:
+    - path: "./tests/projects"
+      config:
+        cradle:
+          none:
+    - path: "./"
+      config:
+        cradle:
+          stack:
+            - path: "./src"
+              component: "hie-bios:lib"
+            - path: "./exe"
+              component: "hie-bios:exe:hie-bios"
+            - path: "./tests/BiosTests.hs"
+              component: "hie-bios:test:hie-bios"
+            - path: "./tests/ParserTests.hs"
+              component: "hie-bios:test:parser-tests"
+```
+
+This way, we specify that we do not want to load any files in our test project directories.
+
+#### Internal Libraries
+
+Internal libraries are not well supported in `stack`. Since the syntax `stack repl <internal library name>` [doesn't work](https://github.com/commercialhaskell/stack/issues/4564), `hie-bios` will generally not work with internal libraries using `stack`.
+
+### Cabal
+
+To use `cabal`, the basic explicit configuration looks similar to `stack`'s configuration.
+
+```yaml
+cradle:
+  cabal:
+```
+
+The implication of this configuration is a bit different, though. Given a source file to load, we will use `cabal repl <filename>` to find the component of the given filepath.
+
+This configuration should work in (almost) every standard project setup, since `cabal` finds the component associated to a given source file.
+However, due to an unfortunate [bug](https://github.com/haskell/cabal/issues/6622), this fails on some files with `cabal` versions older than `3.4`.
+So, to make your project loadable by older `cabal` versions, you can specify a component to load.
+A [component](https://cabal.readthedocs.io/en/stable/nix-local-build.html?highlight=component#cabal-v2-build) is roughly speaking a library, executable, test-suite or benchmark in `cabal`.
+The `hie.yaml` file looks like this:
+
+```yaml
+cradle:
+  cabal:
+    component: <component name>
+```
+
+This tells `hie-bios` that whichever source file it tries to load, this source file should be handled as if it belongs to `<component name>`.
+
+As an example, to load the library of `hie-bios`, the following `hie.yaml` can be used:
+
+```yaml
+cradle:
+  cabal:
+    component: "lib:hie-bios"
+```
+
+The component syntax `"lib:hie-bios"` refers to the library of the package `hie-bios`. For a complete reference of the component syntax, we refer to the [documentation](https://cabal.readthedocs.io/en/stable/nix-local-build.html?highlight=component#cabal-v2-build).
+
+Note that `cabal` and `stack` have different ways of specifying their
 components.
 
+
+If we only specify a single component, then we can only load source files from this component. This is unsatisfactory as we want to be able to navigate our project freely and work on multiple components (test-suite, library, executable, etc...) in parallel.
+
+In a project such as `hie-bios`, we have more than one component, in particular we have four:
+
+ * An executable
+ * A library
+ * Two test-suites
+
+The component syntax can easily be extracted from the `hie-bios.cabal` file. Relevant sections are:
+
+```cabal
+...
+Name:                   hie-bios
+...
+
+Library
+  ...
+  HS-Source-Dirs:       src
+
+Executable hie-bios
+  ...
+  Main-Is:              Main.hs
+  HS-Source-Dirs:       exe
+
+test-suite parser-tests
+  ...
+  hs-source-dirs: tests/
+  main-is: ParserTests.hs
+
+test-suite bios-tests
+  ...
+  hs-source-dirs: tests/
+  main-is: BiosTests.hs
+```
+
+Using the documentation of cabal, we extract the four component names of the `hie-bios` project:
+
+* `lib:hie-bios`
+* `exe:hie-bios`
+* `test:bios-tests`
+* `test:parser-tests`
+
+Since you rarely only want to load a single component in a `cabal` project, we have special syntax to be able to conveniently specify which directory belongs to which component. It is basically a [multi-cradle](#multi-cradle).
+
 ```yaml
-cradle: {cabal: {component: "lib:haskell-ide-engine"}}
+cradle:
+  cabal:
+    - path: "./src"
+      component: "lib:hie-bios"
+    - path: "./exe"
+      component: "exe:hie-bios"
+    - path: "./tests/BiosTests.hs"
+      component: "test:hie-bios"
+    - path: "./tests/ParserTests.hs"
+      component: "test:parser-tests"
 ```
 
-Or you can explicitly state the program which should be used to collect
-the options by supplying the path to the program. It is interpreted
-relative to the current working directory if it is not an absolute path.
-The bios program should consult the `HIE_BIOS_OUTPUT` env var and write a list of
-options to this file separated by newlines. Once the program finishes running `hie-bios`
-reads this file and uses the arguments to set up the GHC session. This is how GHC's
-build system is able to support `hie-bios`.
+Here you can see two important features:
+  * We provide a mapping from filepath to component.
+    * That way, we specify that a file such as `./src/HIE/Bios.hs` belongs to the component `lib:hie-bios`.
+  * The filepath can be a file.
+    * This is convenient if components are overlapping.
 
+This way we specified which component needs to be compiled given a certain source file for our whole project.
+
+#### Debugging a `cabal` cradle
+
+If you find that `hie-bios` can't load a certain component or file, you may run `cabal repl <filename>` and `cabal repl <component name>` to see if `cabal` succeeds in building the components. Chances are that there is a problem and if you fix that, `hie-bios` will succeed to load the project.
+
+Also, see notes for [testing your configuration](#testing-your-configuration).
+
+Otherwise, please open an [issue](https://github.com/mpickering/hie-bios/issues/new).
+
+#### Ignoring directories
+
+You can combine the [multi-cradle](#multi-cradle) with a [none-cradle](#none-cradle) to ignore all source files in a certain directory. The syntax is a bit verbose:
+
 ```yaml
-cradle: {bios: {program: ".hie-bios"}}
+cradle:
+  multi:
+    - path: "./tests/projects"
+      config:
+        cradle:
+          none:
+    - path: "./"
+      config:
+        cradle:
+          cabal:
+            - path: "./src"
+              component: "lib:hie-bios"
+            - path: "./exe"
+              component: "exe:hie-bios"
+            - path: "./tests/BiosTests.hs"
+              component: "test:hie-bios"
+            - path: "./tests/ParserTests.hs"
+              component: "test:parser-tests"
 ```
 
+This way, we specify that we do not want to load any files in our test project directories.
+
+### Bios
+
+Alternatively you can explicitly state a `program` or shell command which should
+be used to collect the options. This is the most general approach and can be extended to handle arbitrary build systems.
+
+The path of the `program` attribute is interpreted relative to the current
+working directory if it isn't absolute. A program is passed the file to
+return options for as its first argument, and a shell command will have it
+available in the `HIE_BIOS_ARG` environment variable.
+
+There are two important environment variables:
+
+* `HIE_BIOS_OUTPUT`: describes the filepath the options should be written to. If this file does not exist, the `program` should create it.
+* `HIE_BIOS_ARG`: the source file that we want to load. Options returned by the `program` should be able to compile the given source file.
+  * This environment variable is *only* available if a shell program is given.
+
+The program flow is roughly as follows:
+The process must consult the `HIE_BIOS_OUTPUT` environment variable and write a
+list of options to this file, separated by newlines. Once the process finishes
+running, `hie-bios` reads this file and uses the arguments to set up the GHC
+session. This is how GHC's build system is able to support `hie-bios`.
+Note, the `program` is intended to produce the build flags to compile the *whole* component the given source file belongs to. This entails that the `program` lists all of the component's module- and file targets.
+
+A good guiding specification for this file is that the following commands
+should work for any file in your project.
+
+``` sh
+$ export HIE_BIOS_OUTPUT=./options.txt # this is usually some temporary file
+$ ./<program> /path/to/foo.hs
+$ ghci $(cat $HIE_BIOS_OUTPUT | tr '\n' ' ')
+```
+
+where `HIE_BIOS_OUTPUT` is some chosen output file and `HIE_BIOS_ARG` contains the file parameter.
+
+The `hie.yaml` configuration looks like this:
+
+```yaml
+cradle:
+  bios:
+    program: "<program>"
+```
+
+Alternatively, you may specify shell code directly.
+This is helpful, if your `program` executable consists of only a single call to another executable.
+
+```yaml
+cradle:
+  bios:
+    shell: "<build-tool flags $HIE_BIOS_ARG>"
+```
+
+#### Debugging a `bios` cradle
+
+The most common error in creating `bios` cradle is to not list all targets of the component. Please make sure, that you always list all targets of the component, associated with the filepath you want to load.
+
+Also, see notes for [testing your configuration](#testing-your-configuration).
+
+### Direct
+
 The `direct` cradle allows you to specify exactly the GHC options that should be used to load
 a project. This is good for debugging but not a very good approach in general as the set of options
 will quickly get out of sync with a cabal file.
 
 ```yaml
-cradle: {direct: { arguments: [arg1, arg2]} }
+cradle:
+  direct:
+    arguments: [arg1, arg2]
 ```
 
+#### Debugging a `direct` cradle
+
+The arguments of a `direct` cradle will be passed almost directly to `ghc`. If the command `ghc <cradle arguments>` succeeds, then `hie-bios` can load the project.
+
+### None
+
 The `none` cradle says that the IDE shouldn't even try to load the project. It
-is most useful when combined with the multi-cradle which is specified in the next section.
+is most useful when combined with the [multi-cradle](#multi-cradle) which is specified in the next section.
 
 ```yaml
-cradle: {none: }
+cradle:
+  none:
 ```
 
 ## Multi-Cradle
@@ -84,17 +394,23 @@
 
 The multi-cradle is a list of relative paths and cradle configurations.
 The path is relative to the configuration file and specifies the scope of
-the cradle. For example, this configuration specificies that files in the
+the cradle. For example, this configuration specifies that files in the
 `src` subdirectory should be handled with the `lib:hie-bios` component and
-files in the `test` directory using the `test` component.
+files in the `test` directory using the `test:bios-tests` component.
 
 ```yaml
 cradle:
   multi:
     - path: "./src"
-      config: { cradle: {cabal: {component: "lib:hie-bios"}} }
+      config:
+        cradle:
+          cabal:
+            component: "lib:hie-bios"
     - path: "./test"
-      config: { cradle: {cabal: {component: "test"}} }
+      config:
+        cradle:
+          cabal:
+            component: "test:bios-tests"
 ```
 
 If a file matches multiple prefixes, the most specific one is chosen.
@@ -116,7 +432,7 @@
     - path: "./src"
       config: { cradle: {cabal: {component: "lib:hie-bios"}} }
     - path: "./test"
-      config: { cradle: {cabal: {component: "test"}} }
+      config: { cradle: {cabal: {component: "test:bios-tests"}} }
     - path: "./test/test-files"
       config: { cradle: { none: } }
 ```
@@ -141,7 +457,7 @@
       component: "hie-bios:test:bios-tests"
 ```
 
-Remember you can combine this shorthand with more complicated configuration
+Remember you can combine this shorthand with more complicated configurations
 as well.
 
 ```yaml
@@ -155,7 +471,7 @@
                             , { path: "./tests", component: "parser-tests" } ] } }
 ```
 
-### Cradle Dependencies
+## Cradle Dependencies
 
 Sometimes it is necessary to reload a component, for example when a package
 dependency is added to the project. Each type of cradle defines a list of
@@ -168,8 +484,8 @@
 However, if there are files that are not covered by
 the cradle dependency resolution, you can add these files explicitly to
 `hie.yaml`.
-These files are not required to actually exist, since it can be useful
-to know when these files are created, e.g. if there was no `cabal.project`
+The file dependencies are not required to actually exist, since it can be useful
+to know when they are created, e.g. if there was no `cabal.project`
 in the project before and now there is, it might change how a file in the
 project is compiled.
 
@@ -187,8 +503,8 @@
   - default.nix
 ```
 
-For the `Bios` cradle type, there is an optional field to specify a program
-to obtain cradle dependencies from:
+For the `Bios` cradle type, there is an optional field for specifying a program
+or shell command to obtain cradle dependencies from:
 
 ```yaml
 cradle:
@@ -196,11 +512,19 @@
     program: ./flags.sh
     dependency-program: ./dependency.sh
 ```
+```yaml
+cradle:
+  bios:
+    shell: build-tool flags $HIE_BIOS_ARG
+    dependency-shell: build-tool dependencies
+```
 
-The program `./dependency.sh` is executed with no paramaters and it is
+The dependency program or command is executed with no parameters and it is
 expected to output on stdout on each line exactly one filepath relative
 to the root of the cradle, not relative to the location of the program.
 
+Programs and shell commands for flags and dependencies can be mixed and matched.
+
 ## Configuration specification
 
 The complete configuration is a subset of
@@ -214,6 +538,8 @@
   bios:
     program: "program to run"
     dependency-program: "optional program to run"
+    shell: build-tool flags $HIE_BIOS_ARG
+    dependency-shell: build-tool dependencies
   direct:
     arguments: ["list","of","ghc","arguments"]
   none:
@@ -226,32 +552,38 @@
 
 ## Testing your configuration
 
-The provided `hie-bios` executable is provided to test your configuration.
+The given `hie-bios` executable is provided to test your configuration.
 
 The `flags` command will print out the options that `hie-bios` thinks you will need to load a file.
 
-```
-hie-bios flags exe/Main.hs
+``` sh
+$ hie-bios flags exe/Main.hs
 ```
 
 The `check` command will try to use these flags to load the module into the GHC API.
 
+``` sh
+$ hie-bios check exe/Main.hs
 ```
-hie-bios check exe/Main.hs
+
+The `debug` command prints verbose information about the cradle, such as where the `hie.yaml` file was found, which file is loaded and the options that will eventually be used for loading a session.
+
+``` sh
+$ hie-bios debug exe/Main.hs
 ```
 
 ## Implicit Configuration
 
-There are several built in modes which captures most common Haskell development
+There are several built in modes which capture the most common Haskell development
 scenarios. If no `hie.yaml` configuration file is found then an implicit
 configuration is searched for. It is strongly recommended to just explicitly
 configure your project.
 
 ### Priority
 
-The targets are searched for in following order.
+The targets are searched for in the following order.
 
-1. A specific `hie-bios` file.
+1. A specific `.hie-bios` file.
 2. A `stack` project
 3. A `cabal` project
 4. The direct cradle which has no specific options.
@@ -260,32 +592,36 @@
 
 The workspace root is the first folder containing a `cabal.project` file.
 
-The arguments are collected by running `cabal v2-repl`.
+The arguments are collected by running `cabal v2-repl <filename>`.
 
-If `cabal v2-repl` fails, then the user needs to configure the correct
+If `cabal v2-repl <filename>` fails, then the user needs to configure the correct
 target to use by writing a `hie.yaml` file.
 
 ### `stack`
 
 The workspace root is the first folder containing a `stack.yaml` file.
 
-The arguments are collected by executing `stack repl`.
+The arguments are collected by executing `stack repl`. If this fails, the user needs to configure the correct target to use by writing a `hie.yaml` file.
 
 ### `bios`
 
-The most general form is the `bios` mode which allows a user to specify themselves
-which flags to provide.
+The most general form is the `bios` mode which allows users to specify
+which flags to provide themselves.
 
-In this mode, an executable file called `.hie-bios` is placed in the root
-of the workspace directory. The script takes one argument, the filepath
-to the current file we want to load into the session. The script returns
-a list of GHC arguments separated by newlines which will setup the correct session.
+The program will receive the file to return options for as its first argument.
 
-A good guiding specification for this file is that the following command
-should work for any file in your project.
+The program flow is roughly as follows:
+The process must consult the `HIE_BIOS_OUTPUT` environment variable and write a
+list of options to the file pointed to by `HIE_BIOS_OUTPUT`, separated by newlines. Once the process finishes running, `hie-bios` reads this file and uses the arguments to set up the GHC
+session. This is how GHC's build system is able to support `hie-bios`.
+Note, the `program` is intended to produce the build flags to compile the *whole* component the given source file belongs to. This entails that the `program` lists all of the component's module- and file targets.
 
-```
-ghci $(./.hie-bios /path/to/foo.hs | tr '\n' ' ') /path/to/foo.hs
+A good guiding specification for this file is that the following commands
+should work for any file in your project.
+``` sh
+$ export HIE_BIOS_OUTPUT=./options.txt # this is usually some temporary file
+$ ./.hie-bios /path/to/foo.hs
+$ ghci $(cat $HIE_BIOS_OUTPUT | tr '\n' ' ')
 ```
 
 This is useful if you are designing a new build system or the other modes
@@ -295,5 +631,4 @@
 ## Supporting Bazel and Obelisk
 
 In previous versions of `hie-bios` there was also support for projects using `rules_haskell` and `obelisk`.
-This was removed in the 0.3 release as they were unused and broken. There is no conceptual barrier to adding
-back support but it requires a user of these two approaches to maintain them.
+This was removed in the 0.3 release as they were unused and broken. There is no conceptual barrier to adding back support but it requires a user of these two approaches to maintain them.
diff --git a/exe/Main.hs b/exe/Main.hs
--- a/exe/Main.hs
+++ b/exe/Main.hs
@@ -80,7 +80,7 @@
         res <- forM remainingArgs $ \fp -> do
                 res <- getCompilerOptions fp cradle
                 case res of
-                    CradleFail (CradleError _ex err) ->
+                    CradleFail (CradleError _deps _ex err) ->
                       return $ "Failed to show flags for \""
                                                 ++ fp
                                                 ++ "\": " ++ show err
diff --git a/hie-bios.cabal b/hie-bios.cabal
--- a/hie-bios.cabal
+++ b/hie-bios.cabal
@@ -1,6 +1,6 @@
 Cabal-Version:          2.2
 Name:                   hie-bios
-Version:                0.5.1
+Version:                0.6.0
 Author:                 Matthew Pickering <matthewtpickering@gmail.com>
 Maintainer:             Matthew Pickering <matthewtpickering@gmail.com>
 License:                BSD-3-Clause
@@ -12,13 +12,16 @@
 Category:               Development
 Build-Type:             Simple
 -- No glob syntax until GHC 8.6 because of stack
-Extra-Source-Files:     ChangeLog
+Extra-Source-Files:     ChangeLog.md
                         README.md
                         wrappers/cabal
                         wrappers/cabal.hs
                         tests/configs/bazel.yaml
                         tests/configs/bios-1.yaml
                         tests/configs/bios-2.yaml
+                        tests/configs/bios-3.yaml
+                        tests/configs/bios-4.yaml
+                        tests/configs/bios-5.yaml
                         tests/configs/cabal-1.yaml
                         tests/configs/cabal-multi.yaml
                         tests/configs/ch-cabal.yaml
@@ -34,6 +37,8 @@
                         tests/configs/obelisk.yaml
                         tests/configs/stack-config.yaml
                         tests/configs/stack-multi.yaml
+                        tests/projects/symlink-test/a/A.hs
+                        tests/projects/symlink-test/hie.yaml
                         tests/projects/multi-direct/A.hs
                         tests/projects/multi-direct/B.hs
                         tests/projects/multi-direct/hie.yaml
@@ -43,16 +48,53 @@
                         tests/projects/multi-cabal/hie.yaml
                         tests/projects/multi-cabal/multi-cabal.cabal
                         tests/projects/multi-cabal/src/Lib.hs
+                        tests/projects/monorepo-cabal/cabal.project
+                        tests/projects/monorepo-cabal/hie.yaml
+                        tests/projects/monorepo-cabal/A/Main.hs
+                        tests/projects/monorepo-cabal/A/A.cabal
+                        tests/projects/monorepo-cabal/A/Setup.hs
+                        tests/projects/monorepo-cabal/B/MyLib.hs
+                        tests/projects/monorepo-cabal/B/B.cabal
+                        tests/projects/monorepo-cabal/B/Setup.hs
                         tests/projects/multi-stack/Setup.hs
                         tests/projects/multi-stack/app/Main.hs
                         tests/projects/multi-stack/cabal.project
                         tests/projects/multi-stack/hie.yaml
                         tests/projects/multi-stack/multi-stack.cabal
                         tests/projects/multi-stack/src/Lib.hs
+                        tests/projects/failing-bios/A.hs
+                        tests/projects/failing-bios/B.hs
+                        tests/projects/failing-bios/hie.yaml
+                        tests/projects/failing-cabal/failing-cabal.cabal
+                        tests/projects/failing-cabal/hie.yaml
+                        tests/projects/failing-cabal/MyLib.hs
+                        tests/projects/failing-cabal/Setup.hs
+                        tests/projects/failing-stack/failing-stack.cabal
+                        tests/projects/failing-stack/hie.yaml
+                        tests/projects/failing-stack/src/Lib.hs
+                        tests/projects/failing-stack/Setup.hs
+                        tests/projects/nested-cabal/nested-cabal.cabal
+                        tests/projects/nested-cabal/cabal.project
+                        tests/projects/nested-cabal/hie.yaml
+                        tests/projects/nested-cabal/MyLib.hs
+                        tests/projects/nested-cabal/Setup.hs
+                        tests/projects/nested-cabal/sub-comp/sub-comp.cabal
+                        tests/projects/nested-cabal/sub-comp/Lib.hs
+                        tests/projects/nested-cabal/sub-comp/Setup.hs
+                        tests/projects/nested-stack/nested-stack.cabal
+                        tests/projects/nested-stack/hie.yaml
+                        tests/projects/nested-stack/MyLib.hs
+                        tests/projects/nested-stack/Setup.hs
+                        tests/projects/nested-stack/sub-comp/sub-comp.cabal
+                        tests/projects/nested-stack/sub-comp/Lib.hs
+                        tests/projects/nested-stack/sub-comp/Setup.hs
                         tests/projects/simple-bios/A.hs
                         tests/projects/simple-bios/B.hs
                         tests/projects/simple-bios/hie-bios.sh
                         tests/projects/simple-bios/hie.yaml
+                        tests/projects/simple-bios-shell/A.hs
+                        tests/projects/simple-bios-shell/B.hs
+                        tests/projects/simple-bios-shell/hie.yaml
                         tests/projects/simple-cabal/A.hs
                         tests/projects/simple-cabal/B.hs
                         tests/projects/simple-cabal/CHANGELOG.md
@@ -79,6 +121,14 @@
                         tests/projects/implicit-cabal/cabal.project
                         tests/projects/implicit-cabal/implicit-cabal.cabal
                         tests/projects/implicit-cabal/Main.hs
+                        tests/projects/implicit-cabal-no-project/implicit-cabal-no-project.cabal
+                        tests/projects/implicit-cabal-no-project/Main.hs
+                        tests/projects/implicit-cabal-deep-project/README
+                        tests/projects/implicit-cabal-deep-project/implicit-cabal-deep-project.cabal
+                        tests/projects/implicit-cabal-deep-project/Main.hs
+                        tests/projects/implicit-cabal-deep-project/cabal.project
+                        tests/projects/implicit-cabal-deep-project/foo/foo.cabal
+                        tests/projects/implicit-cabal-deep-project/foo/Main.hs
                         tests/projects/implicit-stack/Setup.hs
                         tests/projects/implicit-stack/implicit-stack.cabal
                         tests/projects/implicit-stack/Main.hs
@@ -177,9 +227,12 @@
       extra,
       tasty,
       tasty-hunit,
+      tasty-expected-failure,
+      hspec-expectations,
       hie-bios,
       filepath,
       directory,
+      temporary,
       ghc
 
   hs-source-dirs: tests/
diff --git a/src/HIE/Bios/Config.hs b/src/HIE/Bios/Config.hs
--- a/src/HIE/Bios/Config.hs
+++ b/src/HIE/Bios/Config.hs
@@ -7,7 +7,8 @@
     readConfig,
     Config(..),
     CradleConfig(..),
-    CradleType(..)
+    CradleType(..),
+    Callable(..)
     ) where
 
 import qualified Data.Text as T
@@ -39,6 +40,9 @@
         }
         deriving (Show, Eq, Functor)
 
+data Callable = Program FilePath | Command String
+    deriving (Show, Eq)
+
 data CradleType a
     = Cabal { component :: Maybe String }
     | CabalMulti [ (FilePath, String) ]
@@ -49,13 +53,12 @@
 --    | Bazel
 --    | Obelisk
     | Bios
-        { prog :: FilePath
-        -- ^ Path to program that retrieves options to compile a file
-        , depsProg :: Maybe FilePath
-        -- ^ Optional Path to program to obtain cradle dependencies.
+        { call :: Callable
+        -- ^ Path to program or shell command that retrieves options to compile a file
+        , depsCall :: Maybe Callable
+        -- ^ Optional path to program or shell command to obtain cradle dependencies.
         -- Each cradle dependency is to be expected to be on a separate line
-        -- and relative to the root dir of the cradle, not relative
-        -- to the location of this program.
+        -- and relative to the root dir of the cradle.
         }
     | Direct { arguments :: [String] }
     | None
@@ -72,7 +75,7 @@
     show (CabalMulti a) = "CabalMulti " ++ show a
     show (Stack comp) = "Stack {component = " ++ show comp ++ "}"
     show (StackMulti a) = "StackMulti " ++ show a
-    show Bios { prog, depsProg } = "Bios {prog = " ++ show prog ++ ", depsProg = " ++ show depsProg ++ "}"
+    show Bios { call, depsCall } = "Bios {call = " ++ show call ++ ", depsCall = " ++ show depsCall ++ "}"
     show (Direct args) = "Direct {arguments = " ++ show args ++ "}"
     show None = "None"
     show (Multi a) = "Multi " ++ show a
@@ -127,16 +130,28 @@
 parseBios :: Value -> Parser (CradleType a)
 parseBios (Object x)
     | 2 == Map.size x
-    , Just (String biosProgram) <- Map.lookup "program" x
-    , Just (String biosDepsProgram) <- Map.lookup "dependency-program" x
-    = return $ Bios (T.unpack biosProgram) (Just (T.unpack biosDepsProgram))
+    , Just biosCallable <- exclusive (stringTypeFromMap Program "program") (stringTypeFromMap Command "shell")
+    , Just biosDepsCallable <- exclusive (stringTypeFromMap Program "dependency-program") (stringTypeFromMap Command "dependency-shell")
+    = return $ Bios biosCallable (Just biosDepsCallable)
 
     | 1 == Map.size x
-    , Just (String biosProgram) <- Map.lookup "program" x
-    = return $ Bios (T.unpack biosProgram) Nothing
+    , Just biosCallable <- exclusive (stringTypeFromMap Program "program") (stringTypeFromMap Command "shell")
+    = return $ Bios biosCallable Nothing
 
     | otherwise
-    = fail "Not a valid Bios Configuration type, following keys are allowed: program, dependency-program"
+    = fail "Not a valid Bios Configuration type, following keys are allowed: program or shell, dependency-program or dependency-shell"
+
+    where
+        exclusive :: Maybe a -> Maybe a -> Maybe a
+        exclusive (Just _) (Just _) = Nothing
+        exclusive l Nothing = l
+        exclusive Nothing r = r
+        stringTypeFromMap :: (String -> t) -> T.Text -> Maybe t
+        stringTypeFromMap constructor name = constructor <$> (intoString =<< Map.lookup name x)
+        intoString :: Value -> Maybe String
+        intoString (String s) = Just (T.unpack s)
+        intoString _ = Nothing
+
 parseBios _ = fail "Bios Configuration is expected to be an object."
 
 parseDirect :: Value -> Parser (CradleType a)
diff --git a/src/HIE/Bios/Cradle.hs b/src/HIE/Bios/Cradle.hs
--- a/src/HIE/Bios/Cradle.hs
+++ b/src/HIE/Bios/Cradle.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TupleSections #-}
 {-# LANGUAGE BangPatterns #-}
@@ -36,7 +37,7 @@
 import System.Info.Extra
 import Control.Monad.IO.Class
 import System.Environment
-import Control.Applicative ((<|>))
+import Control.Applicative ((<|>), optional)
 import System.IO.Temp
 import System.IO.Error (isPermissionError)
 import Data.List
@@ -52,8 +53,9 @@
 import qualified Data.Conduit as C
 import qualified Data.Conduit.Text as C
 import qualified Data.Text as T
-import           Data.Maybe (fromMaybe)
+import           Data.Maybe (fromMaybe, maybeToList)
 import           GHC.Fingerprint (fingerprintString)
+
 ----------------------------------------------------------------
 
 -- | Given root\/foo\/bar.hs, return root\/hie.yaml, or wherever the yaml file was found.
@@ -118,9 +120,17 @@
     addActionDeps :: CradleAction a -> CradleAction a
     addActionDeps ca =
       ca { runCradle = \l fp ->
-            (fmap (\(ComponentOptions os' dir ds) -> ComponentOptions os' dir (ds `union` deps)))
-              <$> runCradle ca l fp }
+            runCradle ca l fp
+              >>= \case
+                CradleSuccess (ComponentOptions os' dir ds) ->
+                  pure $ CradleSuccess (ComponentOptions os' dir (ds `union` deps))
+                CradleFail err ->
+                  pure $ CradleFail
+                    (err { cradleErrorDependencies = cradleErrorDependencies err `union` deps })
+                CradleNone -> pure CradleNone
+         }
 
+
 implicitConfig :: FilePath -> MaybeT IO (CradleConfig a, FilePath)
 implicitConfig fp = do
   (crdType, wdir) <- implicitConfig' fp
@@ -128,7 +138,7 @@
 
 implicitConfig' :: FilePath -> MaybeT IO (CradleType a, FilePath)
 implicitConfig' fp = (\wdir ->
-         (Bios (wdir </> ".hie-bios") Nothing, wdir)) <$> biosWorkDir fp
+         (Bios (Program $ wdir </> ".hie-bios") Nothing, wdir)) <$> biosWorkDir fp
   --   <|> (Obelisk,) <$> obeliskWorkDir fp
   --   <|> (Bazel,) <$> rulesHaskellWorkDir fp
      <|> (stackExecutable >> (Stack Nothing,) <$> stackWorkDir fp)
@@ -203,12 +213,14 @@
     { cradleRootDir = cur_dir
     , cradleOptsProg = CradleAction
         { actionName = Types.Default
-        , runCradle = \_ _ -> return (CradleSuccess (ComponentOptions [] cur_dir []))
+        , runCradle = \_ _ ->
+            return (CradleSuccess (ComponentOptions [] cur_dir []))
+        , runGhcCmd = runGhcCmdOnPath cur_dir
         }
     }
 
 ---------------------------------------------------------------
--- The none cradle tells us not to even attempt to load a certain directory
+-- | The none cradle tells us not to even attempt to load a certain directory
 
 noneCradle :: FilePath -> Cradle a
 noneCradle cur_dir =
@@ -217,11 +229,12 @@
     , cradleOptsProg = CradleAction
         { actionName = Types.None
         , runCradle = \_ _ -> return CradleNone
+        , runGhcCmd = \_   -> return CradleNone
         }
     }
 
 ---------------------------------------------------------------
--- The multi cradle selects a cradle based on the filepath
+-- | The multi cradle selects a cradle based on the filepath
 
 multiCradle :: (b -> Cradle a) -> FilePath -> [(FilePath, CradleConfig b)] -> Cradle a
 multiCradle buildCustomCradle cur_dir cs =
@@ -229,7 +242,15 @@
     { cradleRootDir  = cur_dir
     , cradleOptsProg = CradleAction
         { actionName = multiActionName
-        , runCradle  = \l fp -> canonicalizePath fp >>= multiAction buildCustomCradle cur_dir cs l
+        , runCradle  = \l fp -> makeAbsolute fp >>= multiAction buildCustomCradle cur_dir cs l
+        , runGhcCmd = \args ->
+            -- We're being lazy here and just returning the ghc path for the
+            -- first non-none cradle. This shouldn't matter in practice: all
+            -- sub cradles should be using the same ghc version!
+            case filter (not . isNoneCradleConfig) $ map snd cs of
+              [] -> return CradleNone
+              (cfg:_) -> flip runGhcCmd args $ cradleOptsProg $
+                getCradle buildCustomCradle (cfg, cur_dir)
         }
     }
   where
@@ -280,10 +301,10 @@
     canonicalizeCradles :: IO [(FilePath, CradleConfig b)]
     canonicalizeCradles =
       sortOn (Down . fst)
-        <$> mapM (\(p, c) -> (,c) <$> (canonicalizePath (cur_dir </> p))) cs
+        <$> mapM (\(p, c) -> (,c) <$> (makeAbsolute (cur_dir </> p))) cs
 
     selectCradle [] =
-      return (CradleFail (CradleError ExitSuccess err_msg))
+      return (CradleFail (CradleError [] ExitSuccess err_msg))
     selectCradle ((p, c): css) =
         if p `isPrefixOf` cur_fp
           then runCradle
@@ -296,12 +317,14 @@
 -------------------------------------------------------------------------
 
 directCradle :: FilePath -> [String] -> Cradle a
-directCradle wdir args  =
+directCradle wdir args =
   Cradle
     { cradleRootDir = wdir
     , cradleOptsProg = CradleAction
         { actionName = Types.Direct
-        , runCradle = \_ _ -> return (CradleSuccess (ComponentOptions args wdir []))
+        , runCradle = \_ _ ->
+            return (CradleSuccess (ComponentOptions args wdir []))
+        , runGhcCmd = runGhcCmdOnPath wdir
         }
     }
 
@@ -310,37 +333,38 @@
 
 -- | Find a cradle by finding an executable `hie-bios` file which will
 -- be executed to find the correct GHC options to use.
-biosCradle :: FilePath -> FilePath -> Maybe FilePath -> Cradle a
-biosCradle wdir biosProg biosDepsProg =
+biosCradle :: FilePath -> Callable -> Maybe Callable -> Cradle a
+biosCradle wdir biosCall biosDepsCall =
   Cradle
     { cradleRootDir    = wdir
     , cradleOptsProg   = CradleAction
         { actionName = Types.Bios
-        , runCradle = biosAction wdir biosProg biosDepsProg
+        , runCradle = biosAction wdir biosCall biosDepsCall
+        , runGhcCmd = runGhcCmdOnPath wdir
         }
     }
 
 biosWorkDir :: FilePath -> MaybeT IO FilePath
 biosWorkDir = findFileUpwards (".hie-bios" ==)
 
-biosDepsAction :: LoggingFunction -> FilePath -> Maybe FilePath -> IO [FilePath]
-biosDepsAction l wdir (Just biosDepsProg) = do
-  biosDeps' <- canonicalizePath biosDepsProg
-  (ex, sout, serr, args) <- readProcessWithOutputFile l wdir biosDeps' []
+biosDepsAction :: LoggingFunction -> FilePath -> Maybe Callable -> IO [FilePath]
+biosDepsAction l wdir (Just biosDepsCall) = do
+  biosDeps' <- callableToProcess biosDepsCall Nothing
+  (ex, sout, serr, args) <- readProcessWithOutputFile l wdir biosDeps'
   case ex of
     ExitFailure _ ->  error $ show (ex, sout, serr)
     ExitSuccess -> return args
 biosDepsAction _ _ Nothing = return []
 
 biosAction :: FilePath
-           -> FilePath
-           -> Maybe FilePath
+           -> Callable
+           -> Maybe Callable
            -> LoggingFunction
            -> FilePath
            -> IO (CradleLoadResult ComponentOptions)
 biosAction wdir bios bios_deps l fp = do
-  bios' <- canonicalizePath bios
-  (ex, _stdo, std, res) <- readProcessWithOutputFile l wdir bios' [fp]
+  bios' <- callableToProcess bios (Just fp)
+  (ex, _stdo, std, res) <- readProcessWithOutputFile l wdir bios'
   deps <- biosDepsAction l wdir bios_deps
         -- Output from the program should be written to the output file and
         -- delimited by newlines.
@@ -348,10 +372,19 @@
         -- Removes all duplicates.
   return $ makeCradleResult (ex, std, wdir, res) deps
 
+callableToProcess :: Callable -> Maybe String -> IO CreateProcess
+callableToProcess (Command shellCommand) file = do
+  old_env <- getEnvironment
+  return $ (shell shellCommand) { env = (: old_env) <$> (,) hieBiosArg <$> file }
+    where
+      hieBiosArg = "HIE_BIOS_ARG"
+callableToProcess (Program path) file = do
+  canon_path <- canonicalizePath path
+  return $ proc canon_path (maybeToList file)
+
 ------------------------------------------------------------------------
--- Cabal Cradle
--- Works for new-build by invoking `v2-repl` does not support components
--- yet.
+-- |Cabal Cradle
+-- Works for new-build by invoking `v2-repl`.
 cabalCradle :: FilePath -> Maybe String -> Cradle a
 cabalCradle wdir mc =
   Cradle
@@ -359,14 +392,38 @@
     , cradleOptsProg   = CradleAction
         { actionName = Types.Cabal
         , runCradle = cabalAction wdir mc
+        , runGhcCmd = \args -> do
+            -- Workaround for a cabal-install bug on 3.0.0.0:
+            -- ./dist-newstyle/tmp/environment.-24811: createDirectory: does not exist (No such file or directory)
+            -- (It's ok to pass 'dist-newstyle' here, as it can only be changed
+            -- with the --builddir flag and not cabal.project, which we aren't
+            -- using in our call to v2-exec)
+            createDirectoryIfMissing True (wdir </> "dist-newstyle" </> "tmp")
+            -- Need to pass -v0 otherwise we get "resolving dependencies..."
+            readProcessWithCwd
+              wdir "cabal" (["v2-exec", "ghc", "-v0", "--"] ++ args) ""
         }
     }
 
-cabalCradleDependencies :: FilePath -> IO [FilePath]
-cabalCradleDependencies rootDir = do
-    cabalFiles <- findCabalFiles rootDir
-    return $ cabalFiles ++ ["cabal.project", "cabal.project.local"]
+-- | @'cabalCradleDependencies' rootDir componentDir@.
+-- Compute the dependencies of the cabal cradle based
+-- on the cradle root and the component directory.
+--
+-- Directory 'componentDir' is a sub-directory where we look for
+-- package specific cradle dependencies, such as a '.cabal' file.
+--
+-- Found dependencies are relative to 'rootDir'.
+cabalCradleDependencies :: FilePath -> FilePath -> IO [FilePath]
+cabalCradleDependencies rootDir componentDir = do
+    let relFp = makeRelative rootDir componentDir
+    cabalFiles' <- findCabalFiles componentDir
+    let cabalFiles = map (relFp </>) cabalFiles'
+    return $ map normalise $ cabalFiles ++ ["cabal.project", "cabal.project.local"]
 
+-- |Find .cabal files in the given directory.
+--
+-- Might return multiple results, as we can not know in advance
+-- which one is important to the user.
 findCabalFiles :: FilePath -> IO [FilePath]
 findCabalFiles wdir = do
   dirContent <- listDirectory wdir
@@ -390,7 +447,7 @@
 -- arguments to the executable.
 type GhcProc = (FilePath, [String])
 
--- generate a fake GHC that can be passed to cabal
+-- | Generate a fake GHC that can be passed to cabal
 -- when run with --interactive, it will print out its
 -- command-line arguments and exit
 withCabalWrapperTool :: GhcProc -> FilePath -> (FilePath -> IO a) -> IO a
@@ -428,15 +485,20 @@
   withCabalWrapperTool ("ghc", []) work_dir $ \wrapper_fp -> do
     let cab_args = ["v2-repl", "--with-compiler", wrapper_fp, fromMaybe (fixTargetPath fp) mc]
     (ex, output, stde, args) <-
-      readProcessWithOutputFile l work_dir "cabal" cab_args
-    deps <- cabalCradleDependencies work_dir
+      readProcessWithOutputFile l work_dir (proc "cabal" cab_args)
     case processCabalWrapperArgs args of
-        Nothing -> pure $ CradleFail (CradleError ex
+        Nothing -> do
+          -- Best effort. Assume the working directory is the
+          -- the root of the component, so we are right in trivial cases at least.
+          deps <- cabalCradleDependencies work_dir work_dir
+          pure $ CradleFail (CradleError deps ex
                     ["Failed to parse result of calling cabal"
                      , unlines output
                      , unlines stde
                      , unlines args])
-        Just (componentDir, final_args) -> pure $ makeCradleResult (ex, stde, componentDir, final_args) deps
+        Just (componentDir, final_args) -> do
+          deps <- cabalCradleDependencies work_dir componentDir
+          pure $ makeCradleResult (ex, stde, componentDir, final_args) deps
   where
     -- Need to make relative on Windows, due to a Cabal bug with how it
     -- parses file targets with a C: drive in it
@@ -447,28 +509,48 @@
 removeInteractive :: [String] -> [String]
 removeInteractive = filter (/= "--interactive")
 
--- Strip out any ["+RTS", ..., "-RTS"] sequences in the command string list.
+-- | Strip out any ["+RTS", ..., "-RTS"] sequences in the command string list.
+data InRTS = OutsideRTS | InsideRTS
+
+-- | Strip out any ["+RTS", ..., "-RTS"] sequences in the command string list.
+--
+-- >>> removeRTS ["option1", "+RTS -H32m -RTS", "option2"]
+-- ["option1", "option2"]
+--
+-- >>> removeRTS ["option1", "+RTS", "-H32m", "-RTS", "option2"]
+-- ["option1", "option2"]
+--
+-- >>> removeRTS ["option1", "+RTS -H32m"]
+-- ["option1"]
+--
+-- >>> removeRTS ["option1", "+RTS -H32m", "-RTS", "option2"]
+-- ["option1", "option2"]
+--
+-- >>> removeRTS ["option1", "+RTS -H32m", "-H32m -RTS", "option2"]
+-- ["option1", "option2"]
 removeRTS :: [String] -> [String]
-removeRTS ("+RTS" : xs)  =
-  case dropWhile (/= "-RTS") xs of
-    [] -> []
-    (_ : ys) -> removeRTS ys
-removeRTS (y:ys)         = y : removeRTS ys
-removeRTS []             = []
+removeRTS = go OutsideRTS
+  where
+    go :: InRTS -> [String] -> [String]
+    go _ [] = []
+    go OutsideRTS (y:ys)
+      | "+RTS" `isPrefixOf` y = go (if "-RTS" `isSuffixOf` y then OutsideRTS else InsideRTS) ys
+      | otherwise = y : go OutsideRTS ys
+    go InsideRTS (y:ys) = go (if "-RTS" `isSuffixOf` y then OutsideRTS else InsideRTS) ys
 
+
 removeVerbosityOpts :: [String] -> [String]
 removeVerbosityOpts = filter ((&&) <$> (/= "-v0") <*> (/= "-w"))
 
 
 cabalWorkDir :: FilePath -> MaybeT IO FilePath
-cabalWorkDir = findFileUpwards isCabal
-  where
-    isCabal name = name == "cabal.project"
+cabalWorkDir wdir =
+      findFileUpwards (== "cabal.project") wdir
+  <|> findFileUpwards (\fp -> takeExtension fp == ".cabal") wdir
 
 ------------------------------------------------------------------------
--- Stack Cradle
+-- | Stack Cradle
 -- Works for by invoking `stack repl` with a wrapper script
-
 stackCradle :: FilePath -> Maybe String -> Cradle a
 stackCradle wdir mc =
   Cradle
@@ -476,13 +558,27 @@
     , cradleOptsProg   = CradleAction
         { actionName = Types.Stack
         , runCradle = stackAction wdir mc
+        , runGhcCmd = \args ->
+            readProcessWithCwd
+              wdir "stack" (["exec", "--silent", "ghc", "--"] <> args) ""
         }
     }
 
-stackCradleDependencies :: FilePath-> IO [FilePath]
-stackCradleDependencies wdir = do
-  cabalFiles <- findCabalFiles wdir
-  return $ cabalFiles ++ ["package.yaml", "stack.yaml"]
+-- | @'stackCradleDependencies' rootDir componentDir@.
+-- Compute the dependencies of the stack cradle based
+-- on the cradle root and the component directory.
+--
+-- Directory 'componentDir' is a sub-directory where we look for
+-- package specific cradle dependencies, such as 'package.yaml' and
+-- a '.cabal' file.
+--
+-- Found dependencies are relative to 'rootDir'.
+stackCradleDependencies :: FilePath -> FilePath -> IO [FilePath]
+stackCradleDependencies wdir componentDir = do
+  let relFp = makeRelative wdir componentDir
+  cabalFiles' <- findCabalFiles componentDir
+  let cabalFiles = map (relFp </>) cabalFiles'
+  return $ map normalise $ cabalFiles ++ [relFp </> "package.yaml", "stack.yaml"]
 
 stackAction :: FilePath -> Maybe String -> LoggingFunction -> FilePath -> IO (CradleLoadResult ComponentOptions)
 stackAction work_dir mc l _fp = do
@@ -490,22 +586,34 @@
   -- Same wrapper works as with cabal
   withCabalWrapperTool ghcProcArgs work_dir $ \wrapper_fp -> do
     (ex1, _stdo, stde, args) <-
-      readProcessWithOutputFile l work_dir
-              "stack" $ ["repl", "--no-nix-pure", "--with-ghc", wrapper_fp]
-                        ++ [ comp | Just comp <- [mc] ]
+      readProcessWithOutputFile l work_dir $
+        proc "stack" $ ["repl", "--no-nix-pure", "--with-ghc", wrapper_fp]
+                       ++ [ comp | Just comp <- [mc] ]
     (ex2, pkg_args, stdr, _) <-
-      readProcessWithOutputFile l work_dir "stack" ["path", "--ghc-package-path"]
+      readProcessWithOutputFile l work_dir $
+        proc "stack" ["path", "--ghc-package-path"]
     let split_pkgs = concatMap splitSearchPath pkg_args
         pkg_ghc_args = concatMap (\p -> ["-package-db", p] ) split_pkgs
-    deps <- stackCradleDependencies work_dir
-    return $ case processCabalWrapperArgs args of
-        Nothing -> CradleFail (CradleError ex1 $
-                    ("Failed to parse result of calling stack":
-                      stde)
-                     ++ args)
+    case processCabalWrapperArgs args of
+        Nothing -> do
+          -- Best effort. Assume the working directory is the
+          -- the root of the component, so we are right in trivial cases at least.
+          deps <- stackCradleDependencies work_dir work_dir
+          pure $ CradleFail
+                    (CradleError deps ex1 $
+                      [ "Failed to parse result of calling stack" ]
+                      ++ stde
+                      ++ args
+                    )
 
-        Just (componentDir, ghc_args) ->
-          makeCradleResult (combineExitCodes [ex1, ex2], stde ++ stdr, componentDir, ghc_args ++ pkg_ghc_args) deps
+        Just (componentDir, ghc_args) -> do
+          deps <- stackCradleDependencies work_dir componentDir
+          pure $ makeCradleResult
+                    ( combineExitCodes [ex1, ex2]
+                    , stde ++ stdr, componentDir
+                    , ghc_args ++ pkg_ghc_args
+                    )
+                    deps
 
 combineExitCodes :: [ExitCode] -> ExitCode
 combineExitCodes = foldr go ExitSuccess
@@ -626,30 +734,27 @@
     getFiles = filter p <$> getDirectoryContents dir
     doesPredFileExist file = doesFileExist $ dir </> file
 
--- | Call a process with the given arguments.
+-- | Call a given process.
 -- * A special file is created for the process to write to, the process can discover the name of
 -- the file by reading the @HIE_BIOS_OUTPUT@ environment variable. The contents of this file is
 -- returned by the function.
 -- * The logging function is called every time the process emits anything to stdout or stderr.
 -- it can be used to report progress of the process to a user.
 -- * The process is executed in the given directory.
--- * The path to the GHC version to use is supplied in the environment variable @HIE_BIOS_GHC@.
---   Additionally, arguments to ghc are supplied via @HIE_BIOS_GHC_ARGS@
 readProcessWithOutputFile
   :: LoggingFunction -- ^ Output of the process is streamed into this function.
   -> FilePath -- ^ Working directory. Process is executed in this directory.
-  -> FilePath -- ^ Process to call.
-  -> [String] -- ^ Arguments to the process.
+  -> CreateProcess -- ^ Parameters for the process to be executed.
   -> IO (ExitCode, [String], [String], [String])
-readProcessWithOutputFile l work_dir fp args = do
+readProcessWithOutputFile l work_dir cp = do
   old_env <- getEnvironment
 
   withHieBiosOutput old_env $ \output_file -> do
     -- Pipe stdout directly into the logger
-    let process = (readProcessInDirectory work_dir fp args)
-                      { env = Just
+    let process = cp { env = Just
                               $ (hieBiosOutput, output_file)
-                              : old_env
+                              : (fromMaybe old_env (env cp)),
+                       cwd = Just work_dir
                       }
 
     -- Windows line endings are not converted so you have to filter out `'r` characters
@@ -664,8 +769,8 @@
 
     where
       withHieBiosOutput :: [(String,String)] -> (FilePath -> IO a) -> IO a
-      withHieBiosOutput env action = do
-        let mbHieBiosOut = lookup hieBiosOutput env
+      withHieBiosOutput env' action = do
+        let mbHieBiosOut = lookup hieBiosOutput env'
         case mbHieBiosOut of
           Just file@(_:_) -> action file
           _ -> withSystemTempFile "hie-bios" $
@@ -673,13 +778,28 @@
 
       hieBiosOutput = "HIE_BIOS_OUTPUT"
 
-readProcessInDirectory :: FilePath -> FilePath -> [String] -> CreateProcess
-readProcessInDirectory wdir p args = (proc p args) { cwd = Just wdir }
-
 makeCradleResult :: (ExitCode, [String], FilePath, [String]) -> [FilePath] -> CradleLoadResult ComponentOptions
 makeCradleResult (ex, err, componentDir, gopts) deps =
   case ex of
-    ExitFailure _ -> CradleFail (CradleError ex err)
+    ExitFailure _ -> CradleFail (CradleError deps ex err)
     _ ->
         let compOpts = ComponentOptions gopts componentDir deps
         in CradleSuccess compOpts
+
+-- | Calls @ghc --print-libdir@, with just whatever's on the PATH.
+runGhcCmdOnPath :: FilePath -> [String] -> IO (CradleLoadResult String)
+runGhcCmdOnPath wdir args = readProcessWithCwd wdir "ghc" args ""
+  -- case mResult of
+  --   Nothing
+
+-- | Wrapper around 'readCreateProcess' that sets the working directory
+readProcessWithCwd :: FilePath -> FilePath -> [String] -> String -> IO (CradleLoadResult String)
+readProcessWithCwd dir cmd args stdi = do
+  let createProc = (proc cmd args) { cwd = Just dir }
+  mResult <- optional $ readCreateProcessWithExitCode createProc stdi
+  case mResult of
+    Just (ExitSuccess, stdo, _) -> pure $ CradleSuccess stdo
+    Just (exitCode, stdo, stde) -> pure $ CradleFail $
+      CradleError [] exitCode ["Error when calling " <> cmd <> " " <> unwords args, stdo, stde]
+    Nothing -> pure $ CradleFail $
+      CradleError [] ExitSuccess ["Couldn't execute " <> cmd <> " " <> unwords args]
diff --git a/src/HIE/Bios/Environment.hs b/src/HIE/Bios/Environment.hs
--- a/src/HIE/Bios/Environment.hs
+++ b/src/HIE/Bios/Environment.hs
@@ -1,16 +1,16 @@
 {-# LANGUAGE RecordWildCards, CPP #-}
-module HIE.Bios.Environment (initSession, getSystemLibDir, makeDynFlagsAbsolute, getCacheDir, addCmdOpts) where
+module HIE.Bios.Environment (initSession, getRuntimeGhcLibDir, getRuntimeGhcVersion, makeDynFlagsAbsolute, makeTargetsAbsolute, getCacheDir, addCmdOpts) where
 
 import CoreMonad (liftIO)
-import GHC (DynFlags(..), GhcLink(..), HscTarget(..), GhcMonad)
+import GHC (GhcMonad)
 import qualified GHC as G
 import qualified DriverPhases as G
 import qualified Util as G
 import DynFlags
 
+import Control.Applicative
 import Control.Monad (void)
 
-import System.Process (readProcess)
 import System.Directory
 import System.FilePath
 import System.Environment (lookupEnv)
@@ -20,8 +20,7 @@
 import Data.ByteString.Base16
 import Data.List
 import Data.Char (isSpace)
-import Control.Applicative ((<|>))
-import Text.ParserCombinators.ReadP
+import Text.ParserCombinators.ReadP hiding (optional)
 import HIE.Bios.Types
 import HIE.Bios.Ghc.Gap
 
@@ -47,22 +46,50 @@
         $ setVerbosity 0                       -- Set verbosity to zero just in case the user specified `-vx` in the options.
         $ setLinkerOptions df''                 -- Set `-fno-code` to avoid generating object files, unless we have to.
         )
+
+    let targets' = makeTargetsAbsolute componentRoot targets
     -- Unset the default log action to avoid output going to stdout.
     unsetLogAction
-    return targets
+    return targets'
 
 ----------------------------------------------------------------
 
--- | Obtain the directory for system libraries.
-getSystemLibDir :: IO (Maybe FilePath)
-getSystemLibDir = do
-    res <- readProcess "ghc" ["--print-libdir"] []
-    return $ case res of
-        ""   -> Nothing
-        dirn -> Just (init dirn)
+makeTargetsAbsolute :: FilePath -> [G.Target] -> [G.Target]
+makeTargetsAbsolute wdir = map (\target -> target {G.targetId = makeTargetIdAbsolute wdir (G.targetId target)})
 
+makeTargetIdAbsolute :: FilePath -> G.TargetId -> G.TargetId
+makeTargetIdAbsolute wdir (G.TargetFile fp phase) = G.TargetFile (prependIfRelative wdir fp) phase
+makeTargetIdAbsolute _ tid = tid
+
 ----------------------------------------------------------------
 
+-- | @getRuntimeGhcLibDir cradle@ will give you the ghc libDir:
+-- __do not__ use 'runGhcCmd' directly.
+-- This will also perform additional lookups and fallbacks to try and get a
+-- reliable library directory.
+-- It tries this specific order of paths:
+--
+-- 1. the @NIX_GHC_LIBDIR@ if it is set
+-- 2. calling 'runCradleGhc' on the provided cradle
+getRuntimeGhcLibDir :: Cradle a
+                    -> IO (CradleLoadResult FilePath)
+getRuntimeGhcLibDir cradle = do
+  maybeNixLibDir <- lookupEnv "NIX_GHC_LIBDIR"
+  case maybeNixLibDir of 
+    Just ld -> pure (CradleSuccess ld)
+    Nothing -> fmap (fmap trim) $
+      runGhcCmd (cradleOptsProg cradle) ["--print-libdir"]
+
+-- | Gets the version of ghc used when compiling the cradle. It is based off of
+-- 'getRuntimeGhcLibDir'. If it can't work out the verison reliably, it will
+-- return a 'CradleError'
+getRuntimeGhcVersion :: Cradle a
+                     -> IO (CradleLoadResult String)
+getRuntimeGhcVersion cradle =
+  fmap (fmap trim) $ runGhcCmd (cradleOptsProg cradle) ["--numeric-version"]
+
+----------------------------------------------------------------
+
 -- | What to call the cache directory in the cache folder.
 cacheDir :: String
 cacheDir = "hie-bios"
@@ -171,15 +198,23 @@
 -- This makes the 'DynFlags' independent of the current working directory.
 makeDynFlagsAbsolute :: FilePath -> DynFlags -> DynFlags
 makeDynFlagsAbsolute work_dir df =
-  mapOverIncludePaths (prependIfNotAbsolute work_dir)
+  mapOverIncludePaths (prependIfRelative work_dir)
   $ df
-    { importPaths = map (prependIfNotAbsolute work_dir) (importPaths df)
+    { importPaths = map (prependIfRelative work_dir) (importPaths df)
+    , packageDBFlags =
+        let makePackageDbAbsolute (PackageDB pkgConfRef) = PackageDB
+              $ case pkgConfRef of
+                PkgConfFile fp -> PkgConfFile (prependIfRelative work_dir fp)
+                conf -> conf
+            makePackageDbAbsolute db = db
+        in map makePackageDbAbsolute (packageDBFlags df)
     }
-  where
-    prependIfNotAbsolute wdir f
-      | isAbsolute f = f
-      | otherwise = wdir </> f
 
+prependIfRelative :: FilePath -> FilePath -> FilePath
+prependIfRelative wdir f
+  | isAbsolute f = f
+  | otherwise = wdir </> f
+
 -- partition_args, along with some of the other code in this file,
 -- was copied from ghc/Main.hs
 -- -----------------------------------------------------------------------------
@@ -277,3 +312,7 @@
 
 anyToken :: ReadP Char
 anyToken = satisfy $ const True
+
+-- Used for clipping the trailing newlines on GHC output
+trim :: String -> String
+trim = dropWhileEnd isSpace
diff --git a/src/HIE/Bios/Ghc/Api.hs b/src/HIE/Bios/Ghc/Api.hs
--- a/src/HIE/Bios/Ghc/Api.hs
+++ b/src/HIE/Bios/Ghc/Api.hs
@@ -4,57 +4,21 @@
     initializeFlagsWithCradle
   , initializeFlagsWithCradleWithMessage
   , G.SuccessFlag(..)
-  -- * Utility functions for running the GHC monad and implementing internal utilities
-  , withGHC
-  , withGHC'
-  , withGhcT
-  , getSystemLibDir
   , withDynFlags
   ) where
 
 import CoreMonad (liftIO)
-import Exception (ghandle, SomeException(..), ExceptionMonad(..))
-import GHC (Ghc, LoadHowMuch(..), GhcMonad, GhcT)
+import GHC (LoadHowMuch(..), GhcMonad)
 import DynFlags
 
 import qualified GHC as G
-import qualified MonadUtils as G
 import qualified HscMain as G
 import qualified GhcMake as G
 
 import Control.Monad (void)
-import System.Exit (exitSuccess)
 import HIE.Bios.Types
-import qualified HIE.Bios.Internal.Log as Log
 import HIE.Bios.Environment
 import HIE.Bios.Flags
-
-----------------------------------------------------------------
-
--- | Converting the 'Ghc' monad to the 'IO' monad. All exceptions are ignored and logged.
-withGHC :: FilePath  -- ^ A target file displayed in an error message.
-        -> Ghc a -- ^ 'Ghc' actions created by the Ghc utilities.
-        -> IO a
-withGHC file body = ghandle ignore $ withGHC' body
-  where
-    ignore :: SomeException -> IO a
-    ignore e = do
-        Log.logm $ file ++ ":0:0:Error:"
-        Log.logm (show e)
-        exitSuccess
-
--- | Run a Ghc monad computation with an automatically discovered libdir.
--- It calculates the lib dir by calling ghc with the `--print-libdir` flag.
-withGHC' :: Ghc a -> IO a
-withGHC' body = do
-    -- TODO: Why is this not using ghc-paths?
-    mlibdir <- getSystemLibDir
-    G.runGhc mlibdir body
-
-withGhcT :: (Exception.ExceptionMonad m, G.MonadIO m, Monad m) => GhcT m a -> m a
-withGhcT body = do
-  mlibdir <- G.liftIO $ getSystemLibDir
-  G.runGhcT mlibdir body
 
 ----------------------------------------------------------------
 
diff --git a/src/HIE/Bios/Ghc/Check.hs b/src/HIE/Bios/Ghc/Check.hs
--- a/src/HIE/Bios/Ghc/Check.hs
+++ b/src/HIE/Bios/Ghc/Check.hs
@@ -6,6 +6,7 @@
 import GHC (DynFlags(..), GhcMonad)
 import Exception
 
+import HIE.Bios.Environment
 import HIE.Bios.Ghc.Api
 import HIE.Bios.Ghc.Logger
 import qualified HIE.Bios.Internal.Log as Log
@@ -16,8 +17,8 @@
 import System.IO.Unsafe (unsafePerformIO)
 import qualified HIE.Bios.Ghc.Gap as Gap
 
+import qualified DynFlags as G
 import qualified GHC as G
-import HIE.Bios.Environment
 
 ----------------------------------------------------------------
 
@@ -28,23 +29,19 @@
             -> [FilePath]  -- ^ The target files.
             -> IO String
 checkSyntax _      []    = return ""
-checkSyntax cradle files = withGhcT $ do
-    Log.debugm $ "Cradle: " ++ show cradle
-    res <- initializeFlagsWithCradle (head files) cradle
-    case res of
-      CradleSuccess (ini, _) -> do
-        _sf <- ini
-        either id id <$> check files
-      CradleFail ce -> liftIO $ throwIO ce
-      CradleNone -> return "No cradle"
-
-
+checkSyntax cradle files = do
+    libDirRes <- getRuntimeGhcLibDir cradle
+    handleRes libDirRes $ \libDir ->
+      G.runGhcT (Just libDir) $ do
+        Log.debugm $ "Cradle: " ++ show cradle
+        res <- initializeFlagsWithCradle (head files) cradle
+        handleRes res $ \(ini, _) -> do
+          _sf <- ini
+          either id id <$> check files
   where
-    {-
-    sessionName = case files of
-      [file] -> file
-      _      -> "MultipleFiles"
-      -}
+    handleRes (CradleSuccess x) f = f x
+    handleRes (CradleFail ce) _f = liftIO $ throwIO ce 
+    handleRes CradleNone _f = return "No cradle"
 
 ----------------------------------------------------------------
 
@@ -53,7 +50,9 @@
 check :: (GhcMonad m)
       => [FilePath]  -- ^ The target files.
       -> m (Either String String)
-check fileNames = withLogger setAllWarningFlags $ setTargetFiles (map dup fileNames)
+check fileNames = do
+  libDir <- G.topDir <$> G.getDynFlags
+  withLogger (setAllWarningFlags libDir) $ setTargetFiles (map dup fileNames)
 
 dup :: a -> (a, a)
 dup x = (x, x)
@@ -61,14 +60,13 @@
 ----------------------------------------------------------------
 
 -- | Set 'DynFlags' equivalent to "-Wall".
-setAllWarningFlags :: DynFlags -> DynFlags
-setAllWarningFlags df = df { warningFlags = allWarningFlags }
+setAllWarningFlags :: FilePath -> DynFlags -> DynFlags
+setAllWarningFlags libDir df = df { warningFlags = allWarningFlags libDir }
 
 {-# NOINLINE allWarningFlags #-}
-allWarningFlags :: Gap.WarnFlags
-allWarningFlags = unsafePerformIO $ do
-    mlibdir <- getSystemLibDir
-    G.runGhcT mlibdir $ do
+allWarningFlags :: FilePath -> Gap.WarnFlags
+allWarningFlags libDir = unsafePerformIO $
+    G.runGhcT (Just libDir) $ do
         df <- G.getSessionDynFlags
         (df', _) <- addCmdOpts ["-Wall"] df
         return $ G.warningFlags df'
diff --git a/src/HIE/Bios/Ghc/Logger.hs b/src/HIE/Bios/Ghc/Logger.hs
--- a/src/HIE/Bios/Ghc/Logger.hs
+++ b/src/HIE/Bios/Ghc/Logger.hs
@@ -10,7 +10,7 @@
 import ErrUtils
 import Exception (ghandle)
 import FastString (unpackFS)
-import GHC (DynFlags(..), SrcSpan(..), Severity(SevError), GhcMonad)
+import GHC (DynFlags(..), SrcSpan(..), GhcMonad)
 import qualified GHC as G
 import HscTypes (SourceError, srcErrorMessages)
 import Outputable (PprStyle, SDoc)
diff --git a/src/HIE/Bios/Internal/Debug.hs b/src/HIE/Bios/Internal/Debug.hs
--- a/src/HIE/Bios/Internal/Debug.hs
+++ b/src/HIE/Bios/Internal/Debug.hs
@@ -1,15 +1,13 @@
 {-# LANGUAGE LambdaCase #-}
 module HIE.Bios.Internal.Debug (debugInfo, rootInfo, configInfo, cradleInfo) where
 
-import Control.Monad.IO.Class (liftIO)
 import Control.Monad
 import Data.Void
 
 import qualified Data.Char as Char
-import Data.Maybe (fromMaybe)
 
-import HIE.Bios.Ghc.Api
 import HIE.Bios.Cradle
+import HIE.Bios.Environment
 import HIE.Bios.Types
 import HIE.Bios.Flags
 
@@ -34,20 +32,23 @@
     canonFp <- canonicalizePath fp
     conf <- findConfig canonFp
     crdl <- findCradle' canonFp
+    ghcLibDir <- getRuntimeGhcLibDir cradle
+    ghcVer <- getRuntimeGhcVersion cradle
     case res of
       CradleSuccess (ComponentOptions gopts croot deps) -> do
-        mglibdir <- liftIO getSystemLibDir
         return [
-            "Root directory:      " ++ rootDir
-          , "Component directory: " ++ croot
-          , "GHC options:         " ++ unwords (map quoteIfNeeded gopts)
-          , "System libraries:    " ++ fromMaybe "" mglibdir
-          , "Config Location:     " ++ conf
-          , "Cradle:              " ++ crdl
-          , "Dependencies:        " ++ unwords deps
+            "Root directory:        " ++ rootDir
+          , "Component directory:   " ++ croot
+          , "GHC options:           " ++ unwords (map quoteIfNeeded gopts)
+          , "GHC library directory: " ++ show ghcLibDir
+          , "GHC version:           " ++ show ghcVer
+          , "Config Location:       " ++ conf
+          , "Cradle:                " ++ crdl
+          , "Dependencies:          " ++ unwords deps
           ]
-      CradleFail (CradleError ext stderr) ->
+      CradleFail (CradleError deps ext stderr) ->
         return ["Cradle failed to load"
+               , "Deps: " ++ show deps
                , "Exit Code: " ++ show ext
                , "Stderr: " ++ unlines stderr]
       CradleNone ->
diff --git a/src/HIE/Bios/Types.hs b/src/HIE/Bios/Types.hs
--- a/src/HIE/Bios/Types.hs
+++ b/src/HIE/Bios/Types.hs
@@ -53,10 +53,14 @@
   deriving (Show, Eq, Ord, Functor)
 
 data CradleAction a = CradleAction {
-                      actionName :: ActionName a
+                        actionName    :: ActionName a
                       -- ^ Name of the action.
-                      , runCradle :: LoggingFunction -> FilePath -> IO (CradleLoadResult ComponentOptions)
+                      , runCradle     :: LoggingFunction -> FilePath -> IO (CradleLoadResult ComponentOptions)
                       -- ^ Options to compile the given file with.
+                      , runGhcCmd :: [String] -> IO (CradleLoadResult String)
+                      -- ^ Executes the @ghc@ binary that is usually used to
+                      -- build the cradle. E.g. for a cabal cradle this should be
+                      -- equivalent to @cabal exec ghc -- args@
                       }
   deriving (Functor)
 
@@ -70,10 +74,20 @@
   = CradleSuccess r -- ^ The cradle succeeded and returned these options.
   | CradleFail CradleError -- ^ We tried to load the cradle and it failed.
   | CradleNone -- ^ No attempt was made to load the cradle.
-  deriving (Functor, Show)
+  deriving (Functor, Show, Eq)
 
 
-data CradleError = CradleError ExitCode [String] deriving (Show)
+data CradleError = CradleError
+  { cradleErrorDependencies :: [FilePath]
+  -- ^ Dependencies of the cradle that failed to load.
+  -- Can be watched for changes to attempt a reload of the cradle.
+  , cradleErrorExitCode :: ExitCode
+  -- ^ ExitCode of the cradle loading mechanism.
+  , cradleErrorStderr :: [String]
+  -- ^ Standard error output that can be shown to users to explain
+  -- the loading error.
+  }
+  deriving (Show, Eq)
 
 instance Exception CradleError where
 ----------------------------------------------------------------
@@ -81,8 +95,8 @@
 -- | Option information for GHC
 data ComponentOptions = ComponentOptions {
     componentOptions  :: [String]  -- ^ Command line options.
-  , componentRoot :: FilePath 
-  -- ^ Root directory of the component. All 'componentOptions' are either 
+  , componentRoot :: FilePath
+  -- ^ Root directory of the component. All 'componentOptions' are either
   -- absolute, or relative to this directory.
   , componentDependencies :: [FilePath]
   -- ^ Dependencies of a cradle that might change the cradle.
diff --git a/tests/BiosTests.hs b/tests/BiosTests.hs
--- a/tests/BiosTests.hs
+++ b/tests/BiosTests.hs
@@ -1,20 +1,31 @@
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE RecordWildCards #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE CPP #-}
 module Main where
 
 import Test.Tasty
 import Test.Tasty.HUnit
+import Test.Hspec.Expectations
+#if __GLASGOW_HASKELL__ < 810
+import Test.Tasty.ExpectedFailure
+#endif
+import qualified GHC as G
 import HIE.Bios
 import HIE.Bios.Ghc.Api
 import HIE.Bios.Ghc.Load
 import HIE.Bios.Cradle
+import HIE.Bios.Environment
 import HIE.Bios.Types
 import Control.Monad.IO.Class
-import Control.Monad ( unless, forM_, when )
+import Control.Monad ( forM_, unless )
 import Data.Void
 import System.Directory
-import System.FilePath ( makeRelative, (</>) )
+import System.FilePath (addTrailingPathSeparator,  makeRelative, (</>) )
 import System.Info.Extra ( isWindows )
+import System.IO.Temp
+import System.Exit (ExitCode(ExitFailure))
+import Control.Monad.Extra (unlessM)
 
 main :: IO ()
 main = do
@@ -36,124 +47,295 @@
                   (Just "./tests/projects/simple-cabal/hie.yaml")
                 )
         ]
+      , testGroup "Symlink"
+        [ testCaseSteps "Can load base module" $ \step -> do
+            withTempCopy "./tests/projects/symlink-test" $ \tmpdir -> do
+              crdl <- initialiseCradle isMultiCradle (addTrailingPathSeparator tmpdir) step
+              step "Load module A"
+              withCurrentDirectory (cradleRootDir crdl) $ do
+                runCradle (cradleOptsProg crdl) (const (pure ())) "./a/A.hs"
+                >>= \case
+                  CradleSuccess r ->
+                    componentOptions r `shouldMatchList` ["a"]
+                  _ -> expectationFailure "Cradle could not be loaded"
+
+        , testCaseSteps "Can load symlinked module" $ \step -> do
+            withTempCopy "./tests/projects/symlink-test" $ \tmpdir -> do
+              crdl <- initialiseCradle isMultiCradle (addTrailingPathSeparator tmpdir) step
+              step "Attemp to load symlinked module A"
+              withCurrentDirectory (cradleRootDir crdl) $ do
+                createDirectoryLink "./a" "./b"
+                unlessM (doesFileExist "./b/A.hs")
+                  $ expectationFailure "Test invariant broken, this file must exist."
+
+                runCradle (cradleOptsProg crdl) (const (pure ())) "./b/A.hs"
+                >>= \case
+                  CradleSuccess r ->
+                    componentOptions r `shouldMatchList` ["b"]
+                  _ -> expectationFailure "Cradle could not be loaded"
+
+        , testCaseSteps "Can not load symlinked module that is ignored" $ \step -> do
+            withTempCopy "./tests/projects/symlink-test" $ \tmpdir -> do
+              crdl <- initialiseCradle isMultiCradle (addTrailingPathSeparator tmpdir) step
+              step "Attemp to load symlinked module A"
+              withCurrentDirectory (cradleRootDir crdl) $ do
+                createDirectoryLink "./a" "./c"
+                unlessM (doesFileExist "./c/A.hs")
+                  $ expectationFailure "Test invariant broken, this file must exist."
+
+                runCradle (cradleOptsProg crdl) (const (pure ())) "./c/A.hs"
+                  >>= \case
+                    CradleNone -> pure ()
+                    _ -> expectationFailure "Cradle loaded symlink"
+        ]
       , testGroup "Loading tests"
         $ linuxExlusiveTestCases
         ++
-           [ testCaseSteps "simple-cabal" $ testDirectory isCabalCradle "./tests/projects/simple-cabal/B.hs"
-           , testCaseSteps "simple-direct" $ testDirectory isDirectCradle "./tests/projects/simple-direct/B.hs"
+           [ testCaseSteps "failing-cabal" $ testDirectoryFail isCabalCradle "./tests/projects/failing-cabal" "MyLib.hs"
+              (\CradleError {..} -> do
+                  cradleErrorExitCode `shouldBe` ExitFailure 1
+                  cradleErrorDependencies `shouldMatchList` ["failing-cabal.cabal", "cabal.project", "cabal.project.local"])
+           , testCaseSteps "failing-bios" $ testDirectoryFail isBiosCradle "./tests/projects/failing-bios" "B.hs"
+              (\CradleError {..} -> do
+                  cradleErrorExitCode `shouldBe` ExitFailure 1
+                  cradleErrorDependencies `shouldMatchList` ["hie.yaml"])
+           , testCaseSteps "simple-bios-shell" $ testDirectory isBiosCradle "./tests/projects/simple-bios-shell" "B.hs"
+           , testCaseSteps "simple-cabal" $ testDirectory isCabalCradle "./tests/projects/simple-cabal" "B.hs"
+           , testCaseSteps "simple-direct" $ testDirectory isDirectCradle "./tests/projects/simple-direct" "B.hs"
+           , testCaseSteps "nested-cabal" $ testLoadCradleDependencies isCabalCradle "./tests/projects/nested-cabal" "sub-comp/Lib.hs"
+              (\deps -> deps `shouldMatchList` ["sub-comp" </> "sub-comp.cabal", "cabal.project", "cabal.project.local"]
+              )
+           , testCaseSteps "nested-cabal2" $ testLoadCradleDependencies isCabalCradle "./tests/projects/nested-cabal" "MyLib.hs"
+              (\deps -> deps `shouldMatchList` ["nested-cabal.cabal", "cabal.project", "cabal.project.local"]
+              )
            , testCaseSteps "multi-direct" {- tests if both components can be loaded -}
-                         $  testDirectory isMultiCradle "./tests/projects/multi-direct/A.hs"
-                         >> testDirectory isMultiCradle "./tests/projects/multi-direct/B.hs"
+                         $  testDirectory isMultiCradle "./tests/projects/multi-direct" "A.hs"
+                         >> testDirectory isMultiCradle "./tests/projects/multi-direct" "B.hs"
            , testCaseSteps "multi-cabal" {- tests if both components can be loaded -}
-                         $  testDirectory isCabalCradle "./tests/projects/multi-cabal/app/Main.hs"
-                         >> testDirectory isCabalCradle "./tests/projects/multi-cabal/src/Lib.hs"
+                         $  testDirectory isCabalCradle "./tests/projects/multi-cabal" "app/Main.hs"
+                         >> testDirectory isCabalCradle "./tests/projects/multi-cabal" "src/Lib.hs"
+           , testCaseSteps "monorepo-cabal" {- issue https://github.com/mpickering/hie-bios/issues/200 -}
+                         $  testDirectory isCabalCradle "./tests/projects/monorepo-cabal" "A/Main.hs"
+                         >> testDirectory isCabalCradle "./tests/projects/monorepo-cabal" "B/MyLib.hs"
            ]
--- TODO: Remove once there's a stackage snapshot for ghc 8.10
+-- TODO: Remove once stack and ghc-8.10.1 play well
+-- https://github.com/bubba/hie-bios/runs/811271872?check_suite_focus=true
 #if __GLASGOW_HASKELL__ < 810
-       ++ [ testCaseSteps "simple-stack" $ testDirectory isStackCradle "./tests/projects/simple-stack/B.hs"
+       ++ [ expectFailBecause "stack repl does not fail on an invalid cabal file" $
+              testCaseSteps "failing-stack" $ testDirectoryFail isStackCradle "./tests/projects/failing-stack" "src/Lib.hs"
+                (\CradleError {..} -> do
+                    cradleErrorExitCode `shouldBe` ExitFailure 1
+                    cradleErrorDependencies `shouldMatchList` ["failing-stack.cabal", "stack.yaml", "package.yaml"])
+          , testCaseSteps "simple-stack" $ testDirectory isStackCradle "./tests/projects/simple-stack" "B.hs"
           , testCaseSteps "multi-stack" {- tests if both components can be loaded -}
-                        $  testDirectory isStackCradle "./tests/projects/multi-stack/app/Main.hs"
-                        >> testDirectory isStackCradle "./tests/projects/multi-stack/src/Lib.hs"
+                        $  testDirectory isStackCradle "./tests/projects/multi-stack" "app/Main.hs"
+                        >> testDirectory isStackCradle "./tests/projects/multi-stack" "src/Lib.hs"
+          , expectFailBecause "stack repl set the component directory to the root directory" $
+              testCaseSteps "nested-stack" $ testLoadCradleDependencies isStackCradle "./tests/projects/nested-stack" "sub-comp/Lib.hs"
+                (\deps -> deps `shouldMatchList`
+                  ["sub-comp" </> "sub-comp.cabal", "sub-comp" </> "package.yaml", "stack.yaml"]
+                )
+          , testCaseSteps "nested-stack2" $ testLoadCradleDependencies isStackCradle "./tests/projects/nested-stack" "MyLib.hs"
+              (\deps -> deps `shouldMatchList` ["nested-stack.cabal", "package.yaml", "stack.yaml"]
+              )
           ,
           -- Test for special characters in the path for parsing of the ghci-scripts.
           -- Issue https://github.com/mpickering/hie-bios/issues/162
           testCaseSteps "space stack"
-                        $  testDirectory isStackCradle "./tests/projects/space stack/A.hs"
-                        >> testDirectory isStackCradle "./tests/projects/space stack/B.hs"
+                        $  testDirectory isStackCradle "./tests/projects/space stack" "A.hs"
+                        >> testDirectory isStackCradle "./tests/projects/space stack" "B.hs"
           ]
 #endif
       , testGroup "Implicit cradle tests" $
-        [ testCaseSteps "implicit-cabal" $ testImplicitCradle "./tests/projects/implicit-cabal/Main.hs" Cabal
--- TODO: Remove once there's a stackage snapshot for ghc 8.10
+        [ testCaseSteps "implicit-cabal" $
+            testImplicitCradle "./tests/projects/implicit-cabal" "Main.hs" Cabal
+        , testCaseSteps "implicit-cabal-no-project" $
+            testImplicitCradle "./tests/projects/implicit-cabal-no-project" "Main.hs" Cabal
+        , testCaseSteps "implicit-cabal-deep-project" $
+            testImplicitCradle "./tests/projects/implicit-cabal-deep-project" "foo/Main.hs" Cabal
+-- TODO: same here
 #if __GLASGOW_HASKELL__ < 810
-        , testCaseSteps "implicit-stack" $ testImplicitCradle "./tests/projects/implicit-stack/Main.hs" Stack
+        , testCaseSteps "implicit-stack" $
+            testImplicitCradle "./tests/projects/implicit-stack" "Main.hs" Stack
         , testCaseSteps "implicit-stack-multi"
-            $ testImplicitCradle "./tests/projects/implicit-stack-multi/Main.hs" Stack
-            >> testImplicitCradle "./tests/projects/implicit-stack-multi/other-package/Main.hs" Stack
+            $ testImplicitCradle "./tests/projects/implicit-stack-multi" "Main.hs" Stack
+            >> testImplicitCradle "./tests/projects/implicit-stack-multi" "other-package/Main.hs" Stack
 #endif
         ]
       ]
 
 linuxExlusiveTestCases :: [TestTree]
-linuxExlusiveTestCases = [ testCaseSteps "simple-bios" $ testDirectory isBiosCradle "./tests/projects/simple-bios/B.hs" | not isWindows ]
+linuxExlusiveTestCases =
+  [ testCaseSteps "simple-bios" $ testDirectory isBiosCradle "./tests/projects/simple-bios" "B.hs" | not isWindows ]
 
-testDirectory :: (Cradle Void -> Bool) -> FilePath -> (String -> IO ()) -> IO ()
-testDirectory cradlePred fp step = do
-  a_fp <- canonicalizePath fp
+testDirectory :: (Cradle Void -> Bool) -> FilePath -> FilePath -> (String -> IO ()) -> IO ()
+testDirectory cradlePred rootDir file step =
+  -- We need to copy over the directory to somewhere outside the source tree
+  -- when we test, since the cabal.project/stack.yaml/hie.yaml file in the root
+  -- of this repository interferes with the test cradles!
+  withTempCopy rootDir $ \rootDir' -> do
+    fp <- canonicalizePath (rootDir' </> file)
+    crd <- initialiseCradle cradlePred fp step
+    step "Get runtime GHC library directory"
+    testGetGhcLibDir crd
+    step "Get runtime GHC version"
+    testGetGhcVersion crd
+    step "Initialise Flags"
+    testLoadFile crd fp step
+
+-- | Here we are testing that the cradle's method of obtaining the ghcLibDir
+-- always works.
+testGetGhcLibDir :: Cradle a -> IO ()
+testGetGhcLibDir crd = do
+  libDirRes <- getRuntimeGhcLibDir crd
+  libDirRes `shouldSatisfy` isSuccess
+  where isSuccess (CradleSuccess _) = True
+        isSuccess _ = False
+
+-- | Here we are testing that the cradle's method of getting the runtime ghc
+-- version is correct - which while testing, should be the version that we have
+-- built the tests with. This will fail if you compiled the tests with a ghc
+-- that doesn't equal the ghc on your path though :(
+testGetGhcVersion :: Cradle a -> IO ()
+testGetGhcVersion crd =
+  getRuntimeGhcVersion crd `shouldReturn` CradleSuccess VERSION_ghc
+
+testDirectoryFail :: (Cradle Void -> Bool) -> FilePath -> FilePath -> (CradleError -> Expectation) -> (String -> IO ()) -> IO ()
+testDirectoryFail cradlePred rootDir file cradleFailPred step = do
+  withTempCopy rootDir $ \rootDir' -> do
+    fp <- canonicalizePath (rootDir' </> file)
+    crd <- initialiseCradle cradlePred fp step
+    step "Initialise Flags"
+    testLoadFileCradleFail crd fp cradleFailPred step
+
+initialiseCradle :: (Cradle Void -> Bool) -> FilePath -> (String -> IO ()) -> IO (Cradle Void)
+initialiseCradle cradlePred a_fp step = do
   step $ "Finding Cradle for: " ++ a_fp
   mcfg <- findCradle a_fp
   step $ "Loading Cradle: " ++ show mcfg
   crd <- case mcfg of
           Just cfg -> loadCradle cfg
           Nothing -> loadImplicitCradle a_fp
-  when (not $ cradlePred crd) $ error $ "Cradle is incorrect: " ++ show (actionName $ cradleOptsProg crd)
-  step "Initialise Flags"
-  testLoadFile crd a_fp step
+  crd `shouldSatisfy` cradlePred
+  pure crd
 
 testLoadFile :: Cradle a -> FilePath -> (String -> IO ()) -> IO ()
-testLoadFile crd fp step = do
-  a_fp <- canonicalizePath fp
-  withCurrentDirectory (cradleRootDir crd) $
-    withGHC' $ do
-      let relFp = makeRelative (cradleRootDir crd) a_fp
-      res <- initializeFlagsWithCradleWithMessage (Just (\_ n _ _ -> step (show n))) relFp crd
-      case res of
-        CradleSuccess (ini, _) -> do
+testLoadFile crd a_fp step = do
+  libDirRes <- getRuntimeGhcLibDir crd
+  handleCradleResult libDirRes $ \libDir ->
+    withCurrentDirectory (cradleRootDir crd) $
+      G.runGhc (Just libDir) $ do
+        let relFp = makeRelative (cradleRootDir crd) a_fp
+        res <- initializeFlagsWithCradleWithMessage (Just (\_ n _ _ -> step (show n))) relFp crd
+        handleCradleResult res $ \(ini, _) -> do
           liftIO (step "Initial module load")
           sf <- ini
           case sf of
             -- Test resetting the targets
             Succeeded -> setTargetFilesWithMessage (Just (\_ n _ _ -> step (show n))) [(a_fp, a_fp)]
-            Failed -> error "Module loading failed"
-        CradleNone -> error "None"
-        CradleFail (CradleError _ex stde) -> error (unlines stde)
+            Failed -> liftIO $ expectationFailure "Module loading failed"
 
+testLoadFileCradleFail :: Cradle a -> FilePath -> (CradleError -> Expectation) -> (String -> IO ()) -> IO ()
+testLoadFileCradleFail crd a_fp cradleErrorExpectation step = do
+  -- don't spin up a ghc session, just run the opts program manually since
+  -- we're not guaranteed to be able to get the ghc libdir if the cradle is
+  -- failing
+  withCurrentDirectory (cradleRootDir crd) $ do
+    let relFp = makeRelative (cradleRootDir crd) a_fp
+    res <- runCradle (cradleOptsProg crd) (step . show) relFp
+    case res of
+      CradleSuccess _ -> liftIO $ expectationFailure "Cradle loaded successfully"
+      CradleNone -> liftIO $ expectationFailure "Unexpected none-Cradle"
+      CradleFail crdlFail -> liftIO $ cradleErrorExpectation crdlFail
+
+testLoadCradleDependencies :: (Cradle Void -> Bool) -> FilePath -> FilePath -> ([FilePath] -> Expectation) -> (String -> IO ()) -> IO ()
+testLoadCradleDependencies cradlePred rootDir file dependencyPred step =
+  withTempCopy rootDir $ \rootDir' -> do
+    a_fp <- canonicalizePath (rootDir' </> file)
+    crd <- initialiseCradle cradlePred a_fp step
+    libDirRes <- getRuntimeGhcLibDir crd
+    handleCradleResult libDirRes $ \libDir -> do
+      step "Initialise Flags"
+      withCurrentDirectory (cradleRootDir crd) $
+        G.runGhc (Just libDir) $ do
+          let relFp = makeRelative (cradleRootDir crd) a_fp
+          res <- initializeFlagsWithCradleWithMessage (Just (\_ n _ _ -> step (show n))) relFp crd
+          handleCradleResult res $ \(_, options) ->
+            liftIO $ dependencyPred (componentDependencies options)
+
+handleCradleResult :: MonadIO m => CradleLoadResult a -> (a -> m ()) -> m ()
+handleCradleResult (CradleSuccess x) f = f x
+handleCradleResult CradleNone _ = liftIO $ expectationFailure "Unexpected none-Cradle"
+handleCradleResult (CradleFail (CradleError _deps _ex stde)) _ =
+  liftIO $ expectationFailure ("Unexpected cradle fail" ++ unlines stde)
+
 findCradleForModule :: FilePath -> Maybe FilePath -> (String -> IO ()) -> IO ()
 findCradleForModule fp expected' step = do
   expected <- maybe (return Nothing) (fmap Just . canonicalizePath) expected'
   a_fp <- canonicalizePath fp
   step "Finding cradle"
-  mcfg <- findCradle a_fp
-  unless (mcfg == expected)
-    $  error
-    $  "Expected cradle: "
-    ++ show expected
-    ++ ", Actual: "
-    ++ show mcfg
+  findCradle a_fp `shouldReturn` expected
 
-testImplicitCradle :: FilePath -> ActionName Void -> (String -> IO ()) -> IO ()
-testImplicitCradle fp' expectedActionName step = do
-  fp <- canonicalizePath fp'
-  step "Inferring implicit cradle"
-  crd <- loadImplicitCradle fp :: IO (Cradle Void)
-  unless (actionName (cradleOptsProg crd) == expectedActionName)
-    $ error $ "Expected cradle: " <> show expectedActionName
-    <> "\n, Actual: " <> show (actionName (cradleOptsProg crd))
-  step "Initialize flags"
-  testLoadFile crd fp step
+testImplicitCradle :: FilePath -> FilePath -> ActionName Void -> (String -> IO ()) -> IO ()
+testImplicitCradle rootDir file expectedActionName step =
+  withTempCopy rootDir $ \rootDir' -> do
+    fp <- makeAbsolute (rootDir' </> file)
+    step "Inferring implicit cradle"
+    crd <- loadImplicitCradle fp :: IO (Cradle Void)
 
+    actionName (cradleOptsProg crd) `shouldBe` expectedActionName
+
+    expectedCradleRootDir <- makeAbsolute rootDir'
+    cradleRootDir crd `shouldBe` expectedCradleRootDir
+
+    step "Initialize flags"
+    testLoadFile crd fp step
+
+withTempCopy :: FilePath -> (FilePath -> IO a) -> IO a
+withTempCopy srcDir f =
+  withSystemTempDirectory "hie-bios-test" $ \newDir -> do
+    copyDir srcDir newDir
+    f newDir
+
+copyDir :: FilePath -> FilePath -> IO ()
+copyDir src dst = do
+  contents <- listDirectory src
+  forM_ contents $ \file -> do
+    unless (file `elem` ignored) $ do
+      let srcFp = src </> file
+          dstFp = dst </> file
+      isDir <- doesDirectoryExist srcFp
+      if isDir
+        then createDirectory dstFp >> copyDir srcFp dstFp
+        else copyFile srcFp dstFp
+  where ignored = ["dist", "dist-newstyle", ".stack-work"]
+
+
 writeStackYamlFiles :: IO ()
-writeStackYamlFiles = do
-  let yamlFile = stackYaml stackYamlResolver
-  forM_ stackProjects $ \proj ->
-    writeFile (proj </> "stack.yaml") yamlFile
+writeStackYamlFiles =
+  forM_ stackProjects $ \(proj, pkgs) ->
+    writeFile (proj </> "stack.yaml") (stackYaml stackYamlResolver pkgs)
 
-stackProjects :: [FilePath]
+stackProjects :: [(FilePath, [FilePath])]
 stackProjects =
-  [ "tests" </> "projects" </> "multi-stack"
-  , "tests" </> "projects" </> "simple-stack"
-  , "tests" </> "projects" </> "space stack"
-  , "tests" </> "projects" </> "implicit-stack"
-  , "tests" </> "projects" </> "implicit-stack-multi"
+  [ ("tests" </> "projects" </> "multi-stack", ["."])
+  , ("tests" </> "projects" </> "failing-stack", ["."])
+  , ("tests" </> "projects" </> "simple-stack", ["."])
+  , ("tests" </> "projects" </> "nested-stack", [".", "./sub-comp"])
+  , ("tests" </> "projects" </> "space stack", ["."])
+  , ("tests" </> "projects" </> "implicit-stack", ["."])
+  , ("tests" </> "projects" </> "implicit-stack-multi", ["."])
   ]
 
-stackYaml :: String -> String
-stackYaml resolver = unlines ["resolver: " ++ resolver, "packages:", "- ."]
+stackYaml :: String -> [FilePath] -> String
+stackYaml resolver pkgs = unlines
+  $ ["resolver: " ++ resolver, "packages:"]
+  ++ map ("- " ++) pkgs
 
 stackYamlResolver :: String
 stackYamlResolver =
 #if (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(8,10,1,0)))
-  "TODO"
+  "nightly-2020-06-25"
 #elif (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(8,8,1,0)))
   "lts-15.10"
 #elif (defined(MIN_VERSION_GLASGOW_HASKELL) && (MIN_VERSION_GLASGOW_HASKELL(8,6,5,0)))
diff --git a/tests/ParserTests.hs b/tests/ParserTests.hs
--- a/tests/ParserTests.hs
+++ b/tests/ParserTests.hs
@@ -20,8 +20,11 @@
     assertParser "cabal-1.yaml" (noDeps (Cabal (Just "lib:hie-bios")))
     assertParser "stack-config.yaml" (noDeps (Stack Nothing))
     --assertParser "bazel.yaml" (noDeps Bazel)
-    assertParser "bios-1.yaml" (noDeps (Bios "program" Nothing))
-    assertParser "bios-2.yaml" (noDeps (Bios "program" (Just "dep-program")))
+    assertParser "bios-1.yaml" (noDeps (Bios (Program "program") Nothing))
+    assertParser "bios-2.yaml" (noDeps (Bios (Program "program") (Just (Program "dep-program"))))
+    assertParser "bios-3.yaml" (noDeps (Bios (Command "shellcommand") Nothing))
+    assertParser "bios-4.yaml" (noDeps (Bios (Command "shellcommand") (Just (Command "dep-shellcommand"))))
+    assertParser "bios-5.yaml" (noDeps (Bios (Command "shellcommand") (Just (Program "dep-program"))))
     assertParser "dependencies.yaml" (Config (CradleConfig ["depFile"] (Cabal (Just "lib:hie-bios"))))
     assertParser "direct.yaml" (noDeps (Direct ["list", "of", "arguments"]))
     assertParser "none.yaml" (noDeps None)
diff --git a/tests/configs/bios-3.yaml b/tests/configs/bios-3.yaml
new file mode 100644
--- /dev/null
+++ b/tests/configs/bios-3.yaml
@@ -0,0 +1,3 @@
+cradle:
+  bios:
+    shell: "shellcommand"
diff --git a/tests/configs/bios-4.yaml b/tests/configs/bios-4.yaml
new file mode 100644
--- /dev/null
+++ b/tests/configs/bios-4.yaml
@@ -0,0 +1,4 @@
+cradle:
+  bios:
+    shell: "shellcommand"
+    dependency-shell: "dep-shellcommand"
diff --git a/tests/configs/bios-5.yaml b/tests/configs/bios-5.yaml
new file mode 100644
--- /dev/null
+++ b/tests/configs/bios-5.yaml
@@ -0,0 +1,4 @@
+cradle:
+  bios:
+    shell: "shellcommand"
+    dependency-program: "dep-program"
diff --git a/tests/projects/failing-bios/A.hs b/tests/projects/failing-bios/A.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/failing-bios/A.hs
@@ -0,0 +1,1 @@
+module A where
diff --git a/tests/projects/failing-bios/B.hs b/tests/projects/failing-bios/B.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/failing-bios/B.hs
@@ -0,0 +1,3 @@
+module B where
+
+import A
diff --git a/tests/projects/failing-bios/hie.yaml b/tests/projects/failing-bios/hie.yaml
new file mode 100644
--- /dev/null
+++ b/tests/projects/failing-bios/hie.yaml
@@ -0,0 +1,5 @@
+cradle:
+  bios:
+    shell: "exit 1"
+dependencies:
+- hie.yaml
diff --git a/tests/projects/failing-cabal/MyLib.hs b/tests/projects/failing-cabal/MyLib.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/failing-cabal/MyLib.hs
@@ -0,0 +1,4 @@
+module MyLib (someFunc) where
+
+someFunc :: IO ()
+someFunc = putStrLn "someFunc"
diff --git a/tests/projects/failing-cabal/Setup.hs b/tests/projects/failing-cabal/Setup.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/failing-cabal/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/tests/projects/failing-cabal/failing-cabal.cabal b/tests/projects/failing-cabal/failing-cabal.cabal
new file mode 100644
--- /dev/null
+++ b/tests/projects/failing-cabal/failing-cabal.cabal
@@ -0,0 +1,13 @@
+cabal-version:       >=1.10
+name:                failing-cabal
+version:             0.1.0.0
+license-file:        LICENSE
+author:              fendor
+build-type:          Simple
+
+library
+  exposed-modules:     MyLib
+  build-depends:       base >=4,
+                       containers < 1 && > 1
+                       --         ^^^^^^^^^^ <<< Invalid constraint
+  default-language:    Haskell2010
diff --git a/tests/projects/failing-cabal/hie.yaml b/tests/projects/failing-cabal/hie.yaml
new file mode 100644
--- /dev/null
+++ b/tests/projects/failing-cabal/hie.yaml
@@ -0,0 +1,2 @@
+cradle:
+  cabal:
diff --git a/tests/projects/failing-stack/Setup.hs b/tests/projects/failing-stack/Setup.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/failing-stack/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/tests/projects/failing-stack/failing-stack.cabal b/tests/projects/failing-stack/failing-stack.cabal
new file mode 100644
--- /dev/null
+++ b/tests/projects/failing-stack/failing-stack.cabal
@@ -0,0 +1,16 @@
+cabal-version: 1.12
+name:           failing-stack
+version:        0.1.0.0
+description:    None
+build-type:     Simple
+
+library
+  exposed-modules:
+      Lib
+  hs-source-dirs:
+      src
+  build-depends:
+      base >=4.7 && <5,
+      containes < 1 && > 1
+      --         ^^^^^^^^^^ <<< Invalid constraint
+  default-language: Haskell2010
diff --git a/tests/projects/failing-stack/hie.yaml b/tests/projects/failing-stack/hie.yaml
new file mode 100644
--- /dev/null
+++ b/tests/projects/failing-stack/hie.yaml
@@ -0,0 +1,2 @@
+cradle:
+  stack:
diff --git a/tests/projects/failing-stack/src/Lib.hs b/tests/projects/failing-stack/src/Lib.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/failing-stack/src/Lib.hs
@@ -0,0 +1,6 @@
+module Lib
+    ( someFunc
+    ) where
+
+someFunc :: IO ()
+someFunc = putStrLn "someFunc"
diff --git a/tests/projects/implicit-cabal-deep-project/Main.hs b/tests/projects/implicit-cabal-deep-project/Main.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/implicit-cabal-deep-project/Main.hs
@@ -0,0 +1,1 @@
+main = pure ()
diff --git a/tests/projects/implicit-cabal-deep-project/README b/tests/projects/implicit-cabal-deep-project/README
new file mode 100644
--- /dev/null
+++ b/tests/projects/implicit-cabal-deep-project/README
@@ -0,0 +1,3 @@
+Here we are testing that if we have a nested cabal package but a cabal.project
+a directory above it, hie-bios will implicitly pick up the cabal.project and
+NOT the nested cabal package
diff --git a/tests/projects/implicit-cabal-deep-project/cabal.project b/tests/projects/implicit-cabal-deep-project/cabal.project
new file mode 100644
--- /dev/null
+++ b/tests/projects/implicit-cabal-deep-project/cabal.project
@@ -0,0 +1,2 @@
+packages: .
+          foo
diff --git a/tests/projects/implicit-cabal-deep-project/foo/Main.hs b/tests/projects/implicit-cabal-deep-project/foo/Main.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/implicit-cabal-deep-project/foo/Main.hs
@@ -0,0 +1,1 @@
+main = pure ()
diff --git a/tests/projects/implicit-cabal-deep-project/foo/foo.cabal b/tests/projects/implicit-cabal-deep-project/foo/foo.cabal
new file mode 100644
--- /dev/null
+++ b/tests/projects/implicit-cabal-deep-project/foo/foo.cabal
@@ -0,0 +1,9 @@
+cabal-version:       >=1.10
+name:                foo
+version:             0.1.0.0
+build-type:          Simple
+
+executable foo
+  main-is:             Main.hs
+  build-depends:       base >=4
+  default-language:    Haskell2010
diff --git a/tests/projects/implicit-cabal-deep-project/implicit-cabal-deep-project.cabal b/tests/projects/implicit-cabal-deep-project/implicit-cabal-deep-project.cabal
new file mode 100644
--- /dev/null
+++ b/tests/projects/implicit-cabal-deep-project/implicit-cabal-deep-project.cabal
@@ -0,0 +1,8 @@
+cabal-version:       >=1.10
+name:                implicit-cabal-deep-project
+version:             0.1.0.0
+build-type:          Simple
+executable foo
+  main-is:             Main.hs
+  build-depends:       base >=4
+  default-language:    Haskell2010
diff --git a/tests/projects/implicit-cabal-no-project/Main.hs b/tests/projects/implicit-cabal-no-project/Main.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/implicit-cabal-no-project/Main.hs
@@ -0,0 +1,1 @@
+main = pure ()
diff --git a/tests/projects/implicit-cabal-no-project/implicit-cabal-no-project.cabal b/tests/projects/implicit-cabal-no-project/implicit-cabal-no-project.cabal
new file mode 100644
--- /dev/null
+++ b/tests/projects/implicit-cabal-no-project/implicit-cabal-no-project.cabal
@@ -0,0 +1,8 @@
+cabal-version:       >=1.10
+name:                implicit-cabal-no-project
+version:             0.1.0.0
+build-type:          Simple
+executable foo
+  main-is:             Main.hs
+  build-depends:       base >=4
+  default-language:    Haskell2010
diff --git a/tests/projects/monorepo-cabal/A/A.cabal b/tests/projects/monorepo-cabal/A/A.cabal
new file mode 100644
--- /dev/null
+++ b/tests/projects/monorepo-cabal/A/A.cabal
@@ -0,0 +1,8 @@
+cabal-version:       >=1.10
+name:                A
+version:             0.1.0.0
+build-type:          Simple
+executable A
+  main-is:             Main.hs
+  build-depends:       base >=4.8, B
+  default-language:    Haskell2010
diff --git a/tests/projects/monorepo-cabal/A/Main.hs b/tests/projects/monorepo-cabal/A/Main.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/monorepo-cabal/A/Main.hs
@@ -0,0 +1,4 @@
+module Main where
+
+main :: IO ()
+main = putStrLn "Hello, Haskell!"
diff --git a/tests/projects/monorepo-cabal/A/Setup.hs b/tests/projects/monorepo-cabal/A/Setup.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/monorepo-cabal/A/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/tests/projects/monorepo-cabal/B/B.cabal b/tests/projects/monorepo-cabal/B/B.cabal
new file mode 100644
--- /dev/null
+++ b/tests/projects/monorepo-cabal/B/B.cabal
@@ -0,0 +1,10 @@
+cabal-version:       >=1.10
+
+name:                B
+version:             0.1.0.0
+build-type:          Simple
+
+library
+  exposed-modules:     MyLib
+  build-depends:       base >=4.8
+  default-language:    Haskell2010
diff --git a/tests/projects/monorepo-cabal/B/MyLib.hs b/tests/projects/monorepo-cabal/B/MyLib.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/monorepo-cabal/B/MyLib.hs
@@ -0,0 +1,4 @@
+module MyLib (someFunc) where
+
+someFunc :: IO ()
+someFunc = putStrLn "someFunc"
diff --git a/tests/projects/monorepo-cabal/B/Setup.hs b/tests/projects/monorepo-cabal/B/Setup.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/monorepo-cabal/B/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/tests/projects/monorepo-cabal/cabal.project b/tests/projects/monorepo-cabal/cabal.project
new file mode 100644
--- /dev/null
+++ b/tests/projects/monorepo-cabal/cabal.project
@@ -0,0 +1,1 @@
+packages: ./A ./B
diff --git a/tests/projects/monorepo-cabal/hie.yaml b/tests/projects/monorepo-cabal/hie.yaml
new file mode 100644
--- /dev/null
+++ b/tests/projects/monorepo-cabal/hie.yaml
@@ -0,0 +1,6 @@
+cradle:
+  cabal:
+    - path: ./A
+      component: "exe:A"
+    - path: ./B
+      component: "lib:B"
diff --git a/tests/projects/nested-cabal/MyLib.hs b/tests/projects/nested-cabal/MyLib.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/nested-cabal/MyLib.hs
@@ -0,0 +1,4 @@
+module MyLib (someFunc) where
+
+someFunc :: IO ()
+someFunc = putStrLn "someFunc"
diff --git a/tests/projects/nested-cabal/Setup.hs b/tests/projects/nested-cabal/Setup.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/nested-cabal/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/tests/projects/nested-cabal/cabal.project b/tests/projects/nested-cabal/cabal.project
new file mode 100644
--- /dev/null
+++ b/tests/projects/nested-cabal/cabal.project
@@ -0,0 +1,2 @@
+packages: ./sub-comp
+          ./
diff --git a/tests/projects/nested-cabal/hie.yaml b/tests/projects/nested-cabal/hie.yaml
new file mode 100644
--- /dev/null
+++ b/tests/projects/nested-cabal/hie.yaml
@@ -0,0 +1,6 @@
+cradle:
+  cabal:
+    - path: ./sub-comp
+      component: "lib:sub-comp"
+    - path: ./
+      component: "lib:nested-cabal"
diff --git a/tests/projects/nested-cabal/nested-cabal.cabal b/tests/projects/nested-cabal/nested-cabal.cabal
new file mode 100644
--- /dev/null
+++ b/tests/projects/nested-cabal/nested-cabal.cabal
@@ -0,0 +1,9 @@
+cabal-version:       >=1.10
+name:                nested-cabal
+version:             0.1.0.0
+build-type:          Simple
+
+library
+  exposed-modules:     MyLib
+  build-depends:       base < 5
+  default-language:    Haskell2010
diff --git a/tests/projects/nested-cabal/sub-comp/Lib.hs b/tests/projects/nested-cabal/sub-comp/Lib.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/nested-cabal/sub-comp/Lib.hs
@@ -0,0 +1,4 @@
+module Lib (someFunc) where
+
+someFunc :: IO ()
+someFunc = putStrLn "someFunc"
diff --git a/tests/projects/nested-cabal/sub-comp/Setup.hs b/tests/projects/nested-cabal/sub-comp/Setup.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/nested-cabal/sub-comp/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/tests/projects/nested-cabal/sub-comp/sub-comp.cabal b/tests/projects/nested-cabal/sub-comp/sub-comp.cabal
new file mode 100644
--- /dev/null
+++ b/tests/projects/nested-cabal/sub-comp/sub-comp.cabal
@@ -0,0 +1,10 @@
+cabal-version:       >=1.10
+name:                sub-comp
+version:             0.1.0.0
+build-type:          Simple
+
+library
+  exposed-modules:     Lib
+  build-depends:       base < 5
+  -- hs-source-dirs:
+  default-language:    Haskell2010
diff --git a/tests/projects/nested-stack/MyLib.hs b/tests/projects/nested-stack/MyLib.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/nested-stack/MyLib.hs
@@ -0,0 +1,4 @@
+module MyLib (someFunc) where
+
+someFunc :: IO ()
+someFunc = putStrLn "someFunc"
diff --git a/tests/projects/nested-stack/Setup.hs b/tests/projects/nested-stack/Setup.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/nested-stack/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/tests/projects/nested-stack/hie.yaml b/tests/projects/nested-stack/hie.yaml
new file mode 100644
--- /dev/null
+++ b/tests/projects/nested-stack/hie.yaml
@@ -0,0 +1,6 @@
+cradle:
+  stack:
+    - path: ./sub-comp
+      component: "sub-comp:lib"
+    - path: ./
+      component: "nested-stack:lib"
diff --git a/tests/projects/nested-stack/nested-stack.cabal b/tests/projects/nested-stack/nested-stack.cabal
new file mode 100644
--- /dev/null
+++ b/tests/projects/nested-stack/nested-stack.cabal
@@ -0,0 +1,9 @@
+cabal-version:       >=1.10
+name:                nested-stack
+version:             0.1.0.0
+build-type:          Simple
+
+library
+  exposed-modules:     MyLib
+  build-depends:       base < 5
+  default-language:    Haskell2010
diff --git a/tests/projects/nested-stack/sub-comp/Lib.hs b/tests/projects/nested-stack/sub-comp/Lib.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/nested-stack/sub-comp/Lib.hs
@@ -0,0 +1,4 @@
+module Lib (someFunc) where
+
+someFunc :: IO ()
+someFunc = putStrLn "someFunc"
diff --git a/tests/projects/nested-stack/sub-comp/Setup.hs b/tests/projects/nested-stack/sub-comp/Setup.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/nested-stack/sub-comp/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/tests/projects/nested-stack/sub-comp/sub-comp.cabal b/tests/projects/nested-stack/sub-comp/sub-comp.cabal
new file mode 100644
--- /dev/null
+++ b/tests/projects/nested-stack/sub-comp/sub-comp.cabal
@@ -0,0 +1,10 @@
+cabal-version:       >=1.10
+name:                sub-comp
+version:             0.1.0.0
+build-type:          Simple
+
+library
+  exposed-modules:     Lib
+  build-depends:       base < 5
+  -- hs-source-dirs:
+  default-language:    Haskell2010
diff --git a/tests/projects/simple-bios-shell/A.hs b/tests/projects/simple-bios-shell/A.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/simple-bios-shell/A.hs
@@ -0,0 +1,1 @@
+module A where
diff --git a/tests/projects/simple-bios-shell/B.hs b/tests/projects/simple-bios-shell/B.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/simple-bios-shell/B.hs
@@ -0,0 +1,3 @@
+module B where
+
+import A
diff --git a/tests/projects/simple-bios-shell/hie.yaml b/tests/projects/simple-bios-shell/hie.yaml
new file mode 100644
--- /dev/null
+++ b/tests/projects/simple-bios-shell/hie.yaml
@@ -0,0 +1,10 @@
+cradle:
+  bios:
+    shell: |
+      :; echo "-Wall" >> $HIE_BIOS_OUTPUT
+      :; echo "A" >> $HIE_BIOS_OUTPUT
+      :; echo "B" >> $HIE_BIOS_OUTPUT
+      :; exit 0
+      ECHO "-Wall" >> %HIE_BIOS_OUTPUT%
+      ECHO "A" >> %HIE_BIOS_OUTPUT%
+      ECHO "B" >> %HIE_BIOS_OUTPUT%
diff --git a/tests/projects/symlink-test/a/A.hs b/tests/projects/symlink-test/a/A.hs
new file mode 100644
--- /dev/null
+++ b/tests/projects/symlink-test/a/A.hs
@@ -0,0 +1,4 @@
+module A where
+
+a :: Int
+a = 5
diff --git a/tests/projects/symlink-test/hie.yaml b/tests/projects/symlink-test/hie.yaml
new file mode 100644
--- /dev/null
+++ b/tests/projects/symlink-test/hie.yaml
@@ -0,0 +1,16 @@
+cradle:
+  multi:
+    - path: ./a
+      config:
+        cradle:
+          direct:
+            arguments: ["a"]
+    - path: ./b
+      config:
+        cradle:
+          direct:
+            arguments: ["b"]
+    - path: ./c
+      config:
+        cradle:
+          none:
