diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,14 +1,8 @@
 # ats-pkg
 
-This is a build system for ATS written in Haskell and configured with Dhall.
-
-## Example
-
-To build a binary package from source, run
+[![Build Status](https://travis-ci.org/vmchale/atspkg.svg?branch=master)](https://travis-ci.org/vmchale/atspkg)
 
-```bash
- $ atspkg remote https://github.com/vmchale/polyglot/archive/0.3.34.tar.gz
-```
+This is a build system for ATS written in Haskell and configured with Dhall.
 
 ## Installation
 
@@ -24,4 +18,59 @@
 
 ```bash
  $ cabal new-install ats-pkg --symlink-bindir ~/.local/bin
+```
+
+## Examples
+
+`atspkg` is configured with
+[Dhall](https://hackage.haskell.org/package/dhall/docs/Dhall-Tutorial.html). You
+may wish to read the Dhall tutorial first, but you do not need to fully
+understand everything to get started.
+
+### Building a Binary Package
+
+The minimal configuration for a package with a binary target is as follows:
+
+```dhall
+let pkg = https://raw.githubusercontent.com/vmchale/atspkg/master/pkgs/default.dhall
+
+in pkg //
+  { bin =
+    [
+      { src = "src/program.dats"
+      , target = "target/program"
+      , gc = True
+      }
+    ]
+  }
+```
+
+You need only specify the source file and the target; `atspkg` will parse your
+ATS source files and track them.
+
+### Depending on a Library
+
+Library specifications are also written in Dhall. Let's look at an example:
+
+```dhall
+let dep =
+  { libName = "atscntrb-concurrency-0.1.0"
+  , dir = ".atspkg/contrib"
+  , url = "https://github.com/vmchale/ats-concurrency/archive/0.1.0.tar.gz"
+  , libVersion = [0,1,0]
+  }
+
+in dep
+```
+
+### Building a Haskell Library
+
+You can see an example [here](https://github.com/vmchale/fast-arithmetic). You
+can
+
+```
+let pkg = https://raw.githubusercontent.com/vmchale/atspkg/master/pkgs/default.dhall
+
+in pkg //
+  { atsSource = [ "ats-src/{{ project }}.dats" ] }
 ```
diff --git a/ats-pkg.cabal b/ats-pkg.cabal
--- a/ats-pkg.cabal
+++ b/ats-pkg.cabal
@@ -1,5 +1,5 @@
 name:                ats-pkg
-version:             1.3.0.0
+version:             1.4.0.0
 synopsis:            Package manager for ATS
 description:         A collection of scripts to make building ATS projects easy.
 homepage:            https://github.com/vmchale/ats-pkg#readme
@@ -27,14 +27,17 @@
 
 library
   hs-source-dirs:      src
-  exposed-modules:     Language.ATS.Package.Compiler
-                     , Language.ATS.Package.Exec
+  exposed-modules:     Language.ATS.Package.Exec
+                     , Language.ATS.Package
   other-modules:       Paths_ats_pkg
                      , Language.ATS.Package.Error
                      , Language.ATS.Package.Type
                      , Language.ATS.Package.Dependency
+                     , Language.ATS.Package.Compiler
+                     , Language.ATS.Package.Build
   build-depends:       base >= 4.7 && < 5
                      , http-client
+                     , containers
                      , filemanip
                      , bytestring
                      , shake
@@ -49,12 +52,13 @@
                      , lens
                      , dhall
                      , shake-ats
-                     , shake-ext
+                     , shake-ext >= 1.4.0.2
                      , composition-prelude >= 1.1.0.2
                      , optparse-applicative
                      , temporary
-                     , lzma
                      , ansi-wl-pprint
+                     -- , lzma
+                     -- , bzlib
   default-language:    Haskell2010
   if flag(development)
     ghc-options: -Werror
diff --git a/src/Language/ATS/Package.hs b/src/Language/ATS/Package.hs
new file mode 100644
--- /dev/null
+++ b/src/Language/ATS/Package.hs
@@ -0,0 +1,10 @@
+module Language.ATS.Package ( packageCompiler
+                            -- * Types
+                            , Version (..)
+                            , Pkg (..)
+                            , Bin (..)
+                            , Constraint (..)
+                            ) where
+
+import           Language.ATS.Package.Compiler
+import           Language.ATS.Package.Type
diff --git a/src/Language/ATS/Package/Build.hs b/src/Language/ATS/Package/Build.hs
new file mode 100644
--- /dev/null
+++ b/src/Language/ATS/Package/Build.hs
@@ -0,0 +1,109 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Language.ATS.Package.Build ( mkPkg ) where
+
+import           Control.Composition
+import           Control.Concurrent.ParallelIO.Global
+import           Control.Monad.IO.Class               (MonadIO)
+import           Data.List                            (nub)
+import           Data.Maybe                           (fromMaybe)
+import           Data.Semigroup                       (Semigroup (..))
+import qualified Data.Text.Lazy                       as TL
+import           Development.Shake
+import           Development.Shake.ATS
+import           Development.Shake.Check
+import           Development.Shake.FilePath
+import           Development.Shake.Man
+import           Dhall                                hiding (bool)
+import           Language.ATS.Package.Dependency
+import           Language.ATS.Package.Type
+import           System.Directory                     (getCurrentDirectory)
+
+mkClean :: Rules ()
+mkClean =
+    "clean" ~> do
+        removeFilesAfter "." ["//*.1","//*.c", "tags"]
+        removeFilesAfter "target" ["//*"]
+        removeFilesAfter ".atspkg" ["//*"]
+        removeFilesAfter "ats-deps" ["//*"]
+
+mkInstall :: Rules ()
+mkInstall =
+    "install" ~> do
+        config <- getConfig
+        bins <- fmap (TL.unpack . target) . bin <$> getConfig
+        need bins
+        home <- fromMaybe "" <$> getEnv "HOME"
+        let binDest = ((home <> "/.local/bin/") <>) . takeBaseName <$> bins
+        void $ zipWithM copyFile' bins binDest
+        pa <- pandoc
+        case man config of
+            Just mt -> if not pa then pure () else do
+                let mt' = manTarget mt
+                    manDest = (home <> "/.local/share/man/man1/") <> mt'
+                need [mt']
+                copyFile' mt' manDest
+            Nothing -> pure ()
+
+mkManpage :: Rules ()
+mkManpage = do
+    c <- getConfig
+    b <- pandoc
+    case man c of
+        Just _ -> bool (pure ()) manpages b
+        _      -> pure ()
+
+getConfig :: MonadIO m => m Pkg
+getConfig = liftIO $ do
+    d <- getCurrentDirectory
+    input auto (TL.pack d <> "/atspkg.dhall")
+
+manTarget :: Text -> FilePath
+manTarget m = TL.unpack m -<.> "1"
+
+mkTest :: Rules ()
+mkTest =
+    "test" ~> do
+        config <- getConfig
+        let tests = fmap (TL.unpack . target) . test $ config
+        need tests
+        mapM_ cmd_ tests
+
+options :: ShakeOptions
+options = shakeOptions { shakeFiles = ".atspkg"
+                       , shakeThreads = 4
+                       , shakeProgress = progressSimple
+                       }
+
+-- TODO verbosity & coloring?
+mkPkg :: [String] -> IO ()
+mkPkg rs = shake options $
+    want rs >>
+    mkTest >>
+    mkClean >>
+    mkManpage >>
+    mkInstall >>
+    (pkgToAction rs =<< getConfig)
+
+-- TODO infer dependencies on gc/atomic gc based on boolean flag.
+pkgToAction :: [String] -> Pkg -> Rules ()
+pkgToAction rs (Pkg bs ts mt v v' ds cds cc cf as cdir) = do
+    unless (rs == ["clean"]) $
+        liftIO $ fetchDeps False ds cds >> stopGlobalPool
+    action (need ["atspkg.dhall"])
+    mapM_ g (bs ++ ts)
+    let bins = TL.unpack . target <$> bs
+    pa <- pandoc
+    when (null rs) $ do
+        cDeps
+        case mt of
+            (Just m) -> want (bool bins (manTarget m : bins) pa)
+            Nothing  -> want bins
+
+    where g (Bin s t ls gc') = atsBin (TL.unpack cc) (TL.unpack <$> cf) v v' gc' (TL.unpack <$> ls) (TL.unpack s) (TL.unpack t)
+          cDeps = unless (null as) $ do
+            let cedar = TL.unpack cdir
+                atsSourceDirs = nub (takeDirectory . TL.unpack <$> as)
+                targets = fmap (((cedar <> "/") <>) . (-<.> "c") . takeBaseName . TL.unpack) as
+            want targets
+            mapM_ (cgen v v') atsSourceDirs
diff --git a/src/Language/ATS/Package/Compiler.hs b/src/Language/ATS/Package/Compiler.hs
--- a/src/Language/ATS/Package/Compiler.hs
+++ b/src/Language/ATS/Package/Compiler.hs
@@ -1,38 +1,37 @@
 {-# LANGUAGE OverloadedStrings #-}
 
+-- | This module contains scripts to fetch the compiler.
 module Language.ATS.Package.Compiler
     ( packageCompiler
-    , nuke
+    , cleanAll
     , fetchCompiler
     , setupCompiler
     , Version (..)
     ) where
 
-import qualified Codec.Archive.Tar       as Tar
-import           Codec.Compression.GZip  (compress, decompress)
-import           Control.Monad           (void, when)
-import qualified Data.ByteString.Lazy    as BS
-import           Data.List               (intercalate)
-import           Network.HTTP.Client     hiding (decompress)
-import           Network.HTTP.Client.TLS (tlsManagerSettings)
+import qualified Codec.Archive.Tar         as Tar
+import           Codec.Compression.GZip    (compress, decompress)
+import           Control.Monad             (void, when)
+import qualified Data.ByteString.Lazy      as BS
+import           Language.ATS.Package.Type
+import           Network.HTTP.Client       hiding (decompress)
+import           Network.HTTP.Client.TLS   (tlsManagerSettings)
 import           System.Directory
-import           System.Environment      (getEnv)
-import           System.FilePath.Find    (find)
+import           System.Environment        (getEnv)
+import           System.FilePath.Find      (find)
 import           System.Posix.Files
 import           System.Process
 
-nuke :: Version -> IO ()
-nuke v = do
-    putStrLn "Cleaning everything..."
-    b <- doesDirectoryExist =<< compilerDir v
-    when b
-        (removeDirectoryRecursive =<< compilerDir v)
-
-newtype Version = Version [Integer]
-    deriving (Eq)
+-- request to https://github.com/vmchale/atspkg/releases/latest/ for latest
+-- version, then we can upgrade
 
-instance Show Version where
-    show (Version is) = intercalate "." (show <$> is)
+cleanAll :: IO ()
+cleanAll = do
+    d <- (++ "/.atspkg") <$> getEnv "HOME"
+    b <- doesDirectoryExist d
+    when b $ do
+        putStrLn "Cleaning everything..."
+        removeDirectoryRecursive d
 
 -- TODO depend on version
 compilerDir :: Version -> IO FilePath
diff --git a/src/Language/ATS/Package/Dependency.hs b/src/Language/ATS/Package/Dependency.hs
--- a/src/Language/ATS/Package/Dependency.hs
+++ b/src/Language/ATS/Package/Dependency.hs
@@ -1,7 +1,4 @@
-{-# LANGUAGE DeriveAnyClass    #-}
-{-# LANGUAGE DeriveGeneric     #-}
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE TemplateHaskell   #-}
 
 module Language.ATS.Package.Dependency ( -- * Functions
                                          fetchDeps
@@ -11,7 +8,6 @@
 
 import qualified Codec.Archive.Tar                    as Tar
 import qualified Codec.Compression.GZip               as Gzip
-import qualified Codec.Compression.Lzma               as Lzma
 import           Control.Concurrent.ParallelIO.Global
 import           Control.Lens
 import           Control.Monad
@@ -21,6 +17,7 @@
 import qualified Data.Text.Lazy                       as TL
 import           Dhall
 import           Language.ATS.Package.Error
+import           Language.ATS.Package.Type
 import           Network.HTTP.Client
 import           Network.HTTP.Client.TLS              (tlsManagerSettings)
 import           System.Directory
@@ -28,15 +25,6 @@
 import           System.Posix.Files
 import           System.Process
 
--- | Type for a dependency
-data Dependency = Dependency { libName :: Text -- ^ Library name, e.g.
-                             , dir     :: Text -- ^ Directory we should unpack to
-                             , url     :: Text -- ^ Url pointing to tarball
-                             }
-    deriving (Eq, Show, Generic, Interpret)
-
-makeLensesFor [("dir", "dirLens")] ''Dependency
-
 fetchDeps :: Bool -- ^ Set to 'False' if unsure.
           -> [Dependency] -- ^ ATS dependencies
           -> [Dependency] -- ^ C Dependencies
@@ -45,10 +33,10 @@
     unless (null deps && null cdeps) $ do
         putStrLn "Checking ATS dependencies..."
         d <- (<> "lib/") <$> pkgHome
-        let libs = fmap (buildHelper b) deps
+        let libs' = fmap (buildHelper b) deps
             unpacked = fmap (over dirLens (TL.pack d <>)) cdeps
             clibs = fmap (buildHelper b) unpacked
-        parallel_ (libs ++ clibs)
+        parallel_ (libs' ++ clibs)
         mapM_ setup unpacked
 
 pkgHome :: IO FilePath
@@ -81,7 +69,7 @@
     void $ readCreateProcess ((proc "make" ["install"]) { cwd = Just p, std_err = CreatePipe }) ""
 
 setup :: Dependency -> IO ()
-setup (Dependency lib' dirName' _) = do
+setup (Dependency lib' dirName' _ _) = do
     lib'' <- (<> TL.unpack lib') <$> pkgHome
     b <- doesFileExist lib''
     unless b $ do
@@ -91,12 +79,11 @@
 getCompressor :: Text -> IO (ByteString -> ByteString)
 getCompressor s
     | ".tar.gz" `TL.isSuffixOf` s || ".tgz" `TL.isSuffixOf` s = pure Gzip.decompress
-    | ".tar.xz" `TL.isSuffixOf` s = pure Lzma.decompress
     | ".tar" `TL.isSuffixOf` s = pure id
     | otherwise = unrecognized (TL.unpack s)
 
 buildHelper :: Bool -> Dependency -> IO ()
-buildHelper b (Dependency lib' dirName' url'') = do
+buildHelper b (Dependency lib' dirName' url'' _) = do
 
     let (lib, dirName, url') = (lib', dirName', url'') & each %~ TL.unpack
 
diff --git a/src/Language/ATS/Package/Exec.hs b/src/Language/ATS/Package/Exec.hs
--- a/src/Language/ATS/Package/Exec.hs
+++ b/src/Language/ATS/Package/Exec.hs
@@ -13,6 +13,7 @@
 import           Data.Version                    hiding (Version (..))
 import           Development.Shake.FilePath
 import           Dhall                           hiding (bool)
+import           Language.ATS.Package.Build
 import           Language.ATS.Package.Compiler
 import           Language.ATS.Package.Dependency
 import           Language.ATS.Package.Type       hiding (test, version)
@@ -48,11 +49,6 @@
     <> command "nuke" (info (pure Nuke) (progDesc "Uninstall all globally installed libraries"))
     )
 
-cleanAll :: IO ()
-cleanAll = do
-    d <- getEnv "HOME"
-    removeDirectoryRecursive $ d ++ "/.atspkg"
-
 build :: Parser Command
 build = Build <$> many
     (argument str
@@ -74,7 +70,7 @@
 fetchPkg :: String -> IO ()
 fetchPkg pkg = withSystemTempDirectory "atspkg" $ \p -> do
     let (lib, dirName, url') = ("atspkg", p, pkg) & each %~ TL.pack
-    fetchDeps True [Dependency lib dirName url'] []
+    fetchDeps True [Dependency lib dirName url' undefined] []
     ps <- fmap ((p ++ "/") ++) <$> listDirectory p
     pkgDir <- fromMaybe p <$> findFile (p:ps) "atspkg.dhall"
     let a = withCurrentDirectory (takeDirectory pkgDir) (mkPkg ["install"])
@@ -83,7 +79,6 @@
 exec :: IO ()
 exec = execParser wrapper >>= run
 
--- https://github.com/vmchale/polyglot/archive/0.3.27.tar.gz
 run :: Command -> IO ()
 run Nuke = cleanAll
 run (Fetch u) = fetchPkg u
@@ -96,7 +91,7 @@
           g _          = undefined
 
 want :: FilePath -> IO Version
-want p = Version . compiler <$> input auto (TL.pack p)
+want p = compiler <$> input auto (TL.pack p)
 
 buildAll :: FilePath -> IO ()
 buildAll p = on (>>) (=<< want p) fetchCompiler setupCompiler
diff --git a/src/Language/ATS/Package/Type.hs b/src/Language/ATS/Package/Type.hs
--- a/src/Language/ATS/Package/Type.hs
+++ b/src/Language/ATS/Package/Type.hs
@@ -1,129 +1,65 @@
-{-# OPTIONS_GHC -fno-warn-unused-top-binds #-}
+{-# OPTIONS_GHC -fno-warn-unused-top-binds -fno-warn-orphans #-}
 
-{-# LANGUAGE DeriveAnyClass    #-}
-{-# LANGUAGE DeriveGeneric     #-}
-{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE DeriveAnyClass             #-}
+{-# LANGUAGE DeriveGeneric              #-}
+{-# LANGUAGE DerivingStrategies         #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE OverloadedStrings          #-}
+{-# LANGUAGE ScopedTypeVariables        #-}
+{-# LANGUAGE StandaloneDeriving         #-}
+{-# LANGUAGE TemplateHaskell            #-}
 
-module Language.ATS.Package.Type ( Pkg (..)
-                                 , mkPkg
+module Language.ATS.Package.Type ( -- * Types
+                                   Pkg (..)
+                                 , Dependency (..)
+                                 , Bin (..)
+                                 , Version (..)
+                                 , Constraint (..)
+                                 -- * Lenses
+                                 , dirLens
                                  ) where
 
-import           Control.Composition
-import           Control.Concurrent.ParallelIO.Global
-import           Control.Monad.IO.Class               (MonadIO)
-import           Data.Maybe                           (fromMaybe, isJust)
-import           Data.Semigroup                       (Semigroup (..))
-import qualified Data.Text.Lazy                       as TL
-import           Development.Shake
+import           Control.Lens
 import           Development.Shake.ATS
-import           Development.Shake.FilePath
-import           Development.Shake.Man
-import           Dhall                                hiding (bool)
-import           Language.ATS.Package.Dependency
-import           System.Directory                     (findExecutable, getCurrentDirectory)
-
--- TODO custom c compiler
-
-options :: ShakeOptions
-options = shakeOptions { shakeFiles = ".atspkg"
-                       , shakeThreads = 4
-                       , shakeProgress = progressSimple
-                       }
-
--- TODO verbosity & coloring?
-mkPkg :: [String] -> IO ()
-mkPkg rs = shake options $
-    want rs >>
-    mkTest >>
-    mkClean >>
-    mkManpage >>
-    mkInstall >>
-    (pkgToAction rs =<< getConfig)
-
-pandoc :: (MonadIO m) => m Bool
-pandoc = isJust <$> liftIO (findExecutable "pandoc")
-
-mkManpage :: Rules ()
-mkManpage = do
-    c <- getConfig
-    b <- pandoc
-    case man c of
-        Just _ -> bool (pure ()) manpages b
-        _      -> pure ()
-
-getConfig :: MonadIO m => m Pkg
-getConfig = liftIO $ do
-    d <- getCurrentDirectory
-    input auto (TL.pack d <> "/atspkg.dhall")
-
-manTarget :: Text -> FilePath
-manTarget m = TL.unpack m -<.> "1"
+import           Dhall
 
-mkInstall :: Rules ()
-mkInstall =
-    "install" ~> do
-        config <- getConfig
-        bins <- fmap (TL.unpack . target) . bin <$> getConfig
-        need bins
-        home <- fromMaybe "" <$> getEnv "HOME"
-        let binDest = ((home <> "/.local/bin/") <>) . takeBaseName <$> bins
-        void $ zipWithM copyFile' bins binDest
-        pa <- pandoc
-        case man config of
-            Just mt -> if not pa then pure () else do
-                let mt' = manTarget mt
-                    manDest = (home <> "/.local/share/man/man1/") <> mt'
-                need [mt']
-                copyFile' mt' manDest
-            Nothing -> pure ()
+-- TODO constraints?
 
-mkClean :: Rules ()
-mkClean =
-    "clean" ~> do
-    removeFilesAfter "." ["//*.1","//*.c", "tags"]
-    removeFilesAfter "target" ["//*"]
-    removeFilesAfter ".atspkg" ["//*"]
+data Constraint = Constraint { pkgName :: Text
+                             , lower   :: Maybe Version
+                             , upper   :: Maybe Version
+                             }
+                deriving (Eq, Show, Generic, Interpret)
 
-mkTest :: Rules ()
-mkTest =
-    "test" ~> do
-        config <- getConfig
-        let tests = fmap (TL.unpack . target) . test $ config
-        need tests
-        mapM_ cmd_ tests
+deriving newtype instance Interpret Version
 
--- TODO infer dependencies on gc/atomic gc based on boolean flag.
-pkgToAction :: [String] -> Pkg -> Rules ()
-pkgToAction rs (Pkg bs ts mt v v' ds cds cc cf) = do
-    unless (rs == ["clean"]) $
-        liftIO $ fetchDeps False ds cds >> stopGlobalPool
-    action (need ["atspkg.dhall"])
-    mapM_ g (bs ++ ts)
-    let bins = TL.unpack . target <$> bs
-    pa <- pandoc
-    when (null rs) $
-        case mt of
-            (Just m) -> want (bool bins (manTarget m : bins) pa)
-            Nothing  -> want bins
+-- | Type for a dependency
+data Dependency = Dependency { libName    :: Text -- ^ Library name, e.g.
+                             , dir        :: Text -- ^ Directory we should unpack to
+                             , url        :: Text -- ^ Url pointing to tarball
+                             , libVersion :: Version
+                             }
+                deriving (Eq, Show, Generic, Interpret)
 
-    where g (Bin s t ls gc') = atsBin (TL.unpack cc) (TL.unpack <$> cf) (Version v) (Version v') gc' (TL.unpack <$> ls) (TL.unpack s) (TL.unpack t)
+makeLensesFor [("dir", "dirLens")] ''Dependency
 
 data Bin = Bin { src    :: Text -- ^ Source file (should end with @.dats@)
                , target :: Text -- ^ Binary to be built
                , libs   :: [Text] -- ^ Libraries to link against (e.g. @[ "pthread" ]@)
-               , gc     :: Bool } -- ^ Whether to use the garbage collector
+               , gc     :: Bool -- ^ Whether to use the garbage collector
+               }
          deriving (Show, Eq, Generic, Interpret)
 
--- data RemotePkg = RemotePkg Pkg Text
-
 data Pkg = Pkg { bin          :: [Bin] -- ^ List of binaries to be built
                , test         :: [Bin] -- ^ List of test suites
                , man          :: Maybe Text -- ^ Optional (markdown) manpages to be converted using @pandoc@.
-               , version      :: [Integer] -- ^ Library version
-               , compiler     :: [Integer] -- ^ Compiler version
+               , version      :: Version -- ^ Library version
+               , compiler     :: Version -- ^ Compiler version
                , dependencies :: [Dependency] -- ^ List of dependencies
                , clib         :: [Dependency] -- ^ List of C dependencies
                , ccompiler    :: Text -- ^ The C compiler we should use
                , cflags       :: [Text] -- ^ List of flags to pass to the C compiler
+               , atsSource    :: [Text] -- ^ Directory containing ATS source to be compile to C.
+               , cDir         :: Text -- ^ Directory for generated C.
                }
          deriving (Show, Eq, Generic, Interpret)
diff --git a/stack.yaml b/stack.yaml
--- a/stack.yaml
+++ b/stack.yaml
@@ -1,10 +1,10 @@
 ---
-resolver: lts-10.3
+resolver: lts-10.4
 packages:
   - '.'
 extra-deps:
-  - shake-ext-1.4.0.1
-  - shake-ats-0.2.0.0
+  - shake-ext-1.4.0.3
+  - shake-ats-0.2.0.4
   - composition-prelude-1.1.0.2
   - language-ats-0.1.1.5
   - cli-setup-0.1.0.3
