diff --git a/cabal2nix.cabal b/cabal2nix.cabal
--- a/cabal2nix.cabal
+++ b/cabal2nix.cabal
@@ -1,5 +1,5 @@
 Name:                   cabal2nix
-Version:                1.8
+Version:                1.9
 Copyright:              (c) 2011 Peter Simons and Andres Loeh
 License:                BSD3
 License-File:           LICENSE
@@ -30,8 +30,8 @@
   .
   The only required argument is the path to the cabal file. For example:
   .
-  > cabal2nix http://hackage.haskell.org/packages/archive/cabal2nix/1.8/cabal2nix.cabal
-  > cabal2nix cabal://cabal2nix-1.8
+  > cabal2nix http://hackage.haskell.org/packages/archive/cabal2nix/1.9/cabal2nix.cabal
+  > cabal2nix cabal://cabal2nix-1.9
   .
   If the @--sha256@ option has not been specified, cabal2nix calls
   @nix-prefetch-url@ to determine the hash automatically. This causes
@@ -46,6 +46,12 @@
   hs-source-dirs:       src
   Build-Depends:        base >= 3 && < 5, Cabal, process, HTTP, pretty, filepath, directory
   Ghc-Options:          -Wall
+  other-modules:        Cabal2Nix.Pretty
+                        Cabal2Nix.License
+                        Cabal2Nix.Name
+                        Cabal2Nix.CorePackages
+                        Cabal2Nix.Package
+                        Cabal2Nix.Hackage
 
 Executable hackage4nix
   main-is:              Hackage4Nix.hs
@@ -53,3 +59,9 @@
   Build-Depends:        base >= 3 && < 5, Cabal, pretty, filepath, directory, bytestring,
                         regex-posix, mtl, containers
   Ghc-Options:          -Wall
+  other-modules:        Cabal2Nix.Pretty
+                        Cabal2Nix.License
+                        Cabal2Nix.Name
+                        Cabal2Nix.CorePackages
+                        Cabal2Nix.Package
+                        Cabal2Nix.Hackage
diff --git a/src/Cabal2Nix/CorePackages.hs b/src/Cabal2Nix/CorePackages.hs
new file mode 100644
--- /dev/null
+++ b/src/Cabal2Nix/CorePackages.hs
@@ -0,0 +1,43 @@
+module Cabal2Nix.CorePackages ( corePackages, coreBuildTools ) where
+
+-- | List of packages shipped with ghc and therefore at the moment not in
+-- nixpkgs. This should probably be configurable at first. Later, it might
+-- be good to actually include them as dependencies, but set them to null
+-- if GHC provides them (as different GHC versions vary).
+--
+-- The commented packages have previously been non-core, so we don't filter
+-- them.
+corePackages :: [String]
+corePackages = [
+    "array",
+    "base",
+    "bin-package-db",
+    "bytestring",
+    "Cabal",
+    "containers",
+    "directory",
+    -- "extensible-exceptions",
+    "ffi",
+    "filepath",
+    "ghc",
+    "ghc-binary",
+    "ghc-prim",
+    "haskell2010", -- new as core, but doesn't work in older GHCs anyway
+    "haskell98",
+    "hpc",
+    "integer-gmp",
+    "old-locale",
+    "old-time",
+    "pretty",
+    "process",
+    "random",
+    "template-haskell",
+    -- "time",
+    "unix"
+  ]
+
+coreBuildTools :: [String]
+coreBuildTools = [
+    "ghc",
+    "hsc2hs"
+  ]
diff --git a/src/Cabal2Nix/Hackage.hs b/src/Cabal2Nix/Hackage.hs
new file mode 100644
--- /dev/null
+++ b/src/Cabal2Nix/Hackage.hs
@@ -0,0 +1,43 @@
+module Cabal2Nix.Hackage ( hackagePath, Ext(..), hashPackage ) where
+
+import Distribution.Package ( PackageIdentifier(..), PackageName(..) )
+import System.Process ( readProcess )
+import System.Directory ( doesFileExist, getHomeDirectory, createDirectoryIfMissing )
+import System.FilePath ( dropFileName, (</>), (<.>) )
+import Data.Version ( showVersion )
+import Control.Monad ( when )
+
+data Ext = TarGz | Cabal deriving Eq
+
+showExt :: Ext -> String
+showExt TarGz = ".tar.gz"
+showExt Cabal = ".cabal"
+
+hackagePath :: PackageIdentifier -> Ext -> String
+hackagePath (PackageIdentifier (PackageName name) version') ext =
+    "http://hackage.haskell.org/packages/archive/" ++
+    name ++ "/" ++ version ++ "/" ++ name ++
+    (if ext == TarGz then "-" ++ version else "") ++
+    showExt ext
+  where
+    version = showVersion version'
+
+hashPackage :: PackageIdentifier -> IO String
+hashPackage pkg = do
+    cachePath <- hashCachePath pkg
+    exists <- doesFileExist cachePath
+    hash' <- case exists of
+              True -> readFile cachePath
+              False -> readProcess "bash" ["-c", "exec nix-prefetch-url 2>/dev/tty " ++ hackagePath pkg TarGz] ""
+    let hash = reverse (dropWhile (=='\n') (reverse hash'))
+    when (not exists) $ do
+      createDirectoryIfMissing True (dropFileName cachePath)
+      writeFile cachePath hash
+    return hash
+
+hashCachePath :: PackageIdentifier -> IO FilePath
+hashCachePath (PackageIdentifier (PackageName name) version') = do
+    home <- getHomeDirectory
+    return $ home ++ "/.cache/cabal2nix" </> name ++ "-" ++ version <.> "sha256"
+  where
+    version = showVersion version'
diff --git a/src/Cabal2Nix/License.hs b/src/Cabal2Nix/License.hs
new file mode 100644
--- /dev/null
+++ b/src/Cabal2Nix/License.hs
@@ -0,0 +1,21 @@
+module Cabal2Nix.License
+  (module Cabal2Nix.License, module Distribution.License)
+  where
+
+import Distribution.License
+import Distribution.Version
+
+showLic :: License -> String
+showLic (GPL Nothing)                     = show "GPL"
+showLic (GPL (Just (Version [2] [])))     = "self.stdenv.lib.licenses.gpl2"
+showLic (GPL (Just (Version [3] [])))     = "self.stdenv.lib.licenses.gpl3"
+showLic (LGPL Nothing)                    = show "LGPL"
+showLic (LGPL (Just (Version [2,1] [])))  = "self.stdenv.lib.licenses.lgpl21"
+showLic (LGPL (Just (Version [3] [])))    = "self.stdenv.lib.licenses.lgpl3"
+showLic BSD3                              = "self.stdenv.lib.licenses.bsd3"
+showLic BSD4                              = "self.stdenv.lib.licenses.bsd4"
+showLic MIT                               = "self.stdenv.lib.licenses.mit"
+showLic PublicDomain                      = "self.stdenv.lib.licenses.publicDomain"
+showLic AllRightsReserved                 = show "unknown"
+showLic OtherLicense                      = show "unknown"
+showLic l                                 = error $ "unknown license: " ++ show l
diff --git a/src/Cabal2Nix/Name.hs b/src/Cabal2Nix/Name.hs
new file mode 100644
--- /dev/null
+++ b/src/Cabal2Nix/Name.hs
@@ -0,0 +1,39 @@
+module Cabal2Nix.Name ( toNixName, libNixName ) where
+
+import Data.Char
+
+toNixName :: String -> String
+toNixName []      = error "toNixName: empty string is not a valid argument"
+toNixName name    = f name
+  where
+    f []                            = []
+    f ('-':c:cs) | c `notElem` "-"  = toUpper c : f cs
+    f ('-':_)                       = error ("unexpected package name " ++ show name)
+    f (c:cs)                        = c : f cs
+
+-- | Map libraries to Nix packages.
+-- TODO: This should probably be configurable. We also need to consider the
+-- possibility of name clashes with Haskell libraries. I have included
+-- identity mappings to incicate that I have verified their correctness.
+libNixName :: String -> [String]
+libNixName "adns"      = return "adns"
+libNixName "cairo"     = return "cairo"
+libNixName "cairo-pdf" = return "cairo"
+libNixName "cairo-ps"  = return "cairo"
+libNixName "cairo-svg" = return "cairo"
+libNixName "crypto"    = return "openssl"
+libNixName "gnome-keyring" = return "gnome_keyring"
+libNixName "gnome-keyring-1" = return "gnome_keyring"
+libNixName "idn"       = return "idn"
+libNixName "libidn"    = return "idn"
+libNixName "libzip"    = return "libzip"
+libNixName "m"         = []  -- in stdenv
+libNixName "pcre"      = return "pcre"
+libNixName "pq"        = return "postgresql"
+libNixName "sndfile"   = return "libsndfile"
+libNixName "sqlite3"   = return "sqlite"
+libNixName "stdc++"    = []  -- in stdenv
+libNixName "xft"       = return "libXft"
+libNixName "X11"       = return "libX11"
+libNixName "z"         = return "zlib"
+libNixName x           = return x
diff --git a/src/Cabal2Nix/Package.hs b/src/Cabal2Nix/Package.hs
new file mode 100644
--- /dev/null
+++ b/src/Cabal2Nix/Package.hs
@@ -0,0 +1,138 @@
+module Cabal2Nix.Package
+  ( cabal2nix, showNixPkg
+  , PkgName, PkgVersion, PkgSHA256, PkgURL, PkgDescription, PkgLicense
+  , PkgIsLib, PkgIsExe, PkgDependencies, PkgBuildTools, PkgExtraLibs
+  , PkgPkgconfigDeps, PkgPlatforms, PkgMaintainers
+  )
+  where
+
+import Data.List
+import Data.Maybe
+import Distribution.Compiler
+import Distribution.Package
+import Distribution.PackageDescription
+import Distribution.PackageDescription.Configuration
+import Distribution.System
+import Distribution.Version
+import Text.PrettyPrint
+import Data.Version ( showVersion )
+
+import Cabal2Nix.License
+import Cabal2Nix.Name
+import Cabal2Nix.CorePackages
+import Cabal2Nix.Pretty
+
+type PkgName          = String
+type PkgVersion       = String
+type PkgSHA256        = String
+type PkgURL           = String
+type PkgDescription   = String
+type PkgLicense       = License
+type PkgIsLib         = Bool
+type PkgIsExe         = Bool
+type PkgDependencies  = [Dependency] -- [CondTree ConfVar [Dependency] ()]
+type PkgBuildTools    = [Dependency]
+type PkgExtraLibs     = [String]
+type PkgPkgconfigDeps = [String]
+type PkgPlatforms     = [String]
+type PkgMaintainers   = [String]
+
+data Pkg = Pkg PkgName
+               PkgVersion
+               PkgSHA256
+               PkgURL
+               PkgDescription
+               PkgLicense
+               PkgIsLib
+               PkgIsExe
+               PkgDependencies
+               PkgBuildTools
+               PkgExtraLibs
+               PkgPkgconfigDeps
+               PkgPlatforms
+               PkgMaintainers
+  deriving (Show)
+
+showNixPkg :: Pkg -> String
+showNixPkg (Pkg name ver sha256 url desc lic isLib isExe deps
+                tools libs pcs platforms maintainers) =
+    render doc
+  where
+    doc = funargs (map text ("cabal" : pkgInputs)) $$
+          vcat [
+            text "",
+            text "cabal.mkDerivation" <+> lparen <> text "self" <>
+              colon <+> lbrace,
+            nest 2 $ vcat [
+              attr "pname"   $ string name,
+              attr "version" $ string ver,
+              attr "sha256"  $ string sha256,
+              boolattr "isLibrary"    (not isLib || isExe) isLib,
+              boolattr "isExecutable" (not isLib || isExe) isExe,
+              listattr "buildDepends"     pkgDeps,
+              listattr "buildTools"       pkgBuildTools,
+              listattr "extraLibraries"   pkgLibs,
+              listattr "pkgconfigDepends" pkgPCs,
+              vcat [
+                text "meta" <+> equals <+> lbrace,
+                nest 2 $ vcat [
+                  onlyIf url  $ attr "homepage"    $ string url,
+                  onlyIf desc $ attr "description" $ string desc,
+                  attr "license" $ text (showLic lic),
+                  onlyIf platforms $
+                    sep [
+                      text "platforms" <+> equals,
+                      nest 2 ((fsep $ punctuate (text " ++") $ map text platforms)) <> semi
+                    ],
+                  listattr "maintainers" maintainers
+                ],
+                rbrace <> semi
+              ]
+            ],
+            rbrace <> rparen,
+            text ""
+          ]
+    pkgLibs       = nub $ sort $ concatMap libNixName libs
+    pkgPCs        = nub $ sort $ concatMap libNixName pcs
+    pkgDeps       = nub $ sort $ map toNixName $
+                    filter (`notElem` (name : corePackages)) $ map unDep deps
+    pkgBuildTools = nub $ sort $ map toNixName $
+                    filter (`notElem` coreBuildTools) $ map unDep tools
+    pkgInputs     = nub $ pkgLibs ++ pkgPCs ++ pkgBuildTools ++ pkgDeps
+
+
+cabal2nix :: GenericPackageDescription -> PkgSHA256 -> PkgPlatforms -> PkgMaintainers -> Pkg
+cabal2nix cabal sha256 platforms maintainers =
+    Pkg pkgname pkgver sha256 url desc lic
+      isLib isExe
+      (buildDepends tpkg)
+      tools
+      libs
+      pcs
+      [ if '.' `elem` p then p else "self.stdenv.lib.platforms." ++ p | p <- platforms ]
+      [ if '.' `elem` m then m else "self.stdenv.lib.maintainers." ++ m | m <- maintainers ]
+  where
+    pkg = packageDescription cabal
+    PackageName pkgname = pkgName (package pkg)
+    pkgver = showVersion (pkgVersion (package pkg))
+    lic = license pkg
+    url = homepage pkg
+    desc = synopsis pkg
+    -- globalDeps = buildDepends pkg
+    -- Potentially dangerous: determine a default flattening of the
+    -- package description. Better approach: export the conditional
+    -- structure and reflect it in the generated file.
+    Right (tpkg, _) = finalizePackageDescription [] (const True)
+                        (Platform I386 Linux) -- shouldn't be hardcoded
+                        (CompilerId GHC (Version [7,0,4] [])) -- dito
+                        [] cabal
+    isLib   = isJust (library tpkg)
+    isExe   = not (null (executables tpkg))
+    libDeps = map libBuildInfo $ maybeToList (library tpkg)
+    exeDeps = map    buildInfo $ executables tpkg
+    libs    =             concatMap extraLibs        (libDeps ++ exeDeps)
+    pcs     = map unDep $ concatMap pkgconfigDepends (libDeps ++ exeDeps)
+    tools   =             concatMap buildTools       (libDeps ++ exeDeps)
+
+unDep :: Dependency -> String
+unDep (Dependency (PackageName x) _) = x
diff --git a/src/Cabal2Nix/Pretty.hs b/src/Cabal2Nix/Pretty.hs
new file mode 100644
--- /dev/null
+++ b/src/Cabal2Nix/Pretty.hs
@@ -0,0 +1,42 @@
+module Cabal2Nix.Pretty where
+
+import Text.PrettyPrint
+
+-- Pretty-printing helpers for Nix expressions.
+
+attr :: String -> Doc -> Doc
+attr n v = text n <+> equals <+> v <> semi
+
+onlyIf :: [a] -> Doc -> Doc
+onlyIf p d = if not (null p) then d else empty
+
+boolattr :: String -> Bool -> Bool -> Doc
+boolattr n p v = if p then attr n (bool v) else empty
+
+listattr :: String -> [String] -> Doc
+listattr n vs = onlyIf vs $
+                sep [ text n <+> equals <+> lbrack,
+                      nest 2 $ fsep $ map text vs,
+                      rbrack <> semi
+                    ]
+
+bool :: Bool -> Doc
+bool True  = text "true"
+bool False = text "false"
+
+string :: String -> Doc
+string = doubleQuotes . text
+
+version :: [Int] -> Doc
+version = doubleQuotes . hcat . punctuate (text ".") . map int
+
+prepunctuate :: Doc -> [Doc] -> [Doc]
+prepunctuate _ []     = []
+prepunctuate p (d:ds) = d : map (p <>) ds
+
+funargs :: [Doc] -> Doc
+funargs xs = sep [
+               lbrace <+> (fcat $ prepunctuate (comma <> text " ") $
+                           map (nest 2) xs),
+               rbrace <> colon
+             ]
