diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -44,8 +44,10 @@
 `cabal://NAME-VERSION`, which will automatically download the file from
 Hackage. Alternatively, a direct `http://host/path/pkg.cabal` URL can be
 provided, as well as a `file:///local/path/pkg.cabal` URI that doesn't
-depend on network access. Run the utility with `--help` to see the
-complete list of supported command line flags.
+depend on network access. However, if the source hash is not already in `cabal2nix`'s cache or
+provided using the `--sha256` option, `cabal2nix` still needs to download the source code to
+compute the hash, which obviously still causes network traffic.
+Run the utility with `--help` to see the complete list of supported command line flags.
 
 To add a new package to Nix, checkout the Nixpkgs project from
 https://github.com/nixos/nixpkgs.git and run
@@ -58,6 +60,49 @@
 `pkgs/top-level/haskell-packages.nix`, for example:
 
     foo = callPackage ../development/libraries/haskell/foo {};
+
+`cabal2nix` can also build derivations for projects from other
+sources than hackage. You only need to provide an URI that points
+to a cabal project. The most common usecase for this is probably to
+generate a derivation for a project on the local file system:
+
+    aeson:$ cabal2nix ./.
+    # This file was auto-generated by cabal2nix. Please do NOT edit manually!
+
+    { cabal, attoparsec, blazeBuilder, deepseq, dlist, hashable, HUnit
+    , mtl, QuickCheck, scientific, syb, testFramework
+    , testFrameworkHunit, testFrameworkQuickcheck2, text, time
+    , unorderedContainers, vector
+    }
+
+    cabal.mkDerivation (self: {
+      pname = "aeson";
+      version = "0.7.0.2";
+      src = ./.;
+      buildDepends = [
+        attoparsec blazeBuilder deepseq dlist hashable mtl scientific syb
+        text time unorderedContainers vector
+      ];
+      testDepends = [
+        attoparsec HUnit QuickCheck testFramework testFrameworkHunit
+        testFrameworkQuickcheck2 text time unorderedContainers vector
+      ];
+      meta = {
+        homepage = "https://github.com/bos/aeson";
+        description = "Fast JSON parsing and encoding";
+        license = self.stdenv.lib.licenses.bsd3;
+        platforms = self.ghc.meta.platforms;
+      };
+    })
+
+This derivation will not fetch from hackage, but instead use the directory which
+contains the derivation as the source repository.
+
+`cabal2nix` currently supports the following respository types:
+
+* directory
+* source archive (zip, tar.gz, ...) from http or https URL or local file.
+* git, mercurial, svn or bazaar repository
 
 ### Hackage4nix
 
diff --git a/cabal2nix.cabal b/cabal2nix.cabal
--- a/cabal2nix.cabal
+++ b/cabal2nix.cabal
@@ -1,5 +1,5 @@
 Name:                   cabal2nix
-Version:                1.68
+Version:                1.69
 Copyright:              Peter Simons, Andres Loeh
 License:                BSD3
 License-File:           LICENSE
@@ -16,7 +16,7 @@
   The @cabal2nix@ utility converts Cabal files into Nix build instructions. The
   commandline syntax is:
   .
-  > Usage: cabal2nix [options] url-to-cabal-file
+  > Usage: cabal2nix [options] url-to-cabal-file-or-repo
   >   -h             --help                   show this help text
   >                  --hackage-db=FILEPATH    path to the local hackage db in tar format
   >                  --sha256=HASH            sha256 hash of source tarball
@@ -33,16 +33,22 @@
   >   cabal://pkgname                  download latest version of the specified package from Hackage
   >   http://host/path                 fetch the Cabal file via HTTP
   >   file:///local/path               load the Cabal file from the local disk
-  >   /local/path                      abbreviated version of file URI
+  >   /local/path.cabal                abbreviated version of file URI
+  >   <git/svn/bzr/hg URL>             download the source from the specified repository
   .
   The only required argument is the path to the cabal file. For example:
   .
-  > cabal2nix http://hackage.haskell.org/packages/archive/cabal2nix/1.68/cabal2nix.cabal
-  > cabal2nix cabal://cabal2nix-1.68
+  > cabal2nix http://hackage.haskell.org/packages/archive/cabal2nix/1.69/cabal2nix.cabal
+  > cabal2nix cabal://cabal2nix-1.69
   .
   If the @--sha256@ option has not been specified, cabal2nix calls
   @nix-prefetch-url@ to determine the hash automatically. This causes
   network traffic, obviously.
+  .
+  If the argument refers to a source repository instead of a cabal file,
+  cabal2nix will use that source repository to fetch from instead of hackage.
+  Currently, cabal2nix supports directories, archives (fetched via http or https) and
+  git, mercurial, svn or bazaar repositories.
 
 Source-Repository head
   Type:                 git
@@ -52,17 +58,18 @@
   main-is:              cabal2nix.hs
   hs-source-dirs:       src
   Build-Depends:        base >= 3 && < 5, regex-posix, pretty, Cabal >= 1.16,
-                        filepath, directory, process, HTTP, hackage-db
+                        filepath, directory, process, hackage-db, transformers
   Extensions:           PatternGuards, RecordWildCards, CPP
   Ghc-Options:          -Wall
   other-modules:        Cabal2Nix.CorePackages
                         Cabal2Nix.Flags
                         Cabal2Nix.Generate
-                        Cabal2Nix.Hackage
+                        Cabal2Nix.Package
                         Cabal2Nix.License
                         Cabal2Nix.Name
                         Cabal2Nix.Normalize
                         Cabal2Nix.PostProcess
+                        Distribution.NixOS.Fetch
                         Distribution.NixOS.Derivation.Cabal
                         Distribution.NixOS.Derivation.License
                         Distribution.NixOS.Derivation.Meta
@@ -73,7 +80,7 @@
   main-is:              hackage4nix.hs
   hs-source-dirs:       src
   Build-Depends:        base >= 3 && < 5, regex-posix, pretty, Cabal >= 1.16,
-                        mtl, containers, directory, filepath, hackage-db
+                        mtl, containers, directory, filepath, hackage-db, transformers, process
   Extensions:           PatternGuards, RecordWildCards, CPP
   Ghc-Options:          -Wall
   other-modules:        Cabal2Nix.CorePackages
@@ -83,6 +90,7 @@
                         Cabal2Nix.Name
                         Cabal2Nix.Normalize
                         Cabal2Nix.PostProcess
+                        Distribution.NixOS.Fetch
                         Distribution.NixOS.Derivation.Cabal
                         Distribution.NixOS.Derivation.License
                         Distribution.NixOS.Derivation.Meta
diff --git a/src/Cabal2Nix/Generate.hs b/src/Cabal2Nix/Generate.hs
--- a/src/Cabal2Nix/Generate.hs
+++ b/src/Cabal2Nix/Generate.hs
@@ -1,23 +1,22 @@
 module Cabal2Nix.Generate ( cabal2nix ) where
 
+import Cabal2Nix.Flags
 import Cabal2Nix.License
-import Cabal2Nix.PostProcess
 import Cabal2Nix.Normalize
-import Cabal2Nix.Flags
+import Cabal2Nix.PostProcess
 import Data.Maybe
 import Distribution.Compiler
+import Distribution.NixOS.Derivation.Cabal
 import qualified Distribution.Package as Cabal
 import qualified Distribution.PackageDescription as Cabal
 import Distribution.PackageDescription.Configuration
 import Distribution.System
-import Distribution.Version
-import Distribution.NixOS.Derivation.Cabal
 
 cabal2nix :: Cabal.GenericPackageDescription -> Derivation
 cabal2nix cabal = normalize $ postProcess MkDerivation
   { pname          = let Cabal.PackageName x = Cabal.pkgName pkg in x
   , version        = Cabal.pkgVersion pkg
-  , sha256         = "cabal2nix left the sha256 field undefined"
+  , src            = error "cabal2nix left the src field undefined"
   , isLibrary      = isJust (Cabal.library tpkg)
   , isExecutable   = not (null (Cabal.executables tpkg))
   , extraFunctionArgs = []
diff --git a/src/Cabal2Nix/Hackage.hs b/src/Cabal2Nix/Hackage.hs
deleted file mode 100644
--- a/src/Cabal2Nix/Hackage.hs
+++ /dev/null
@@ -1,84 +0,0 @@
-module Cabal2Nix.Hackage ( hashPackage, readCabalFile ) where
-
-import Control.Monad ( unless )
-import Data.List ( isPrefixOf )
-import Data.Maybe ( fromJust )
-import Data.Version ( showVersion, versionBranch )
-import Distribution.Package ( PackageIdentifier(..), PackageName(..) )
-import Distribution.Text
-import Network.HTTP ( getRequest, rspBody )
-import Network.Browser ( browse, request, setCheckForProxy, setDebugLog, setOutHandler )
-import System.Directory ( doesFileExist, getHomeDirectory, createDirectoryIfMissing )
-import System.FilePath ( dropFileName, (</>), (<.>) )
-import System.Process ( readProcess )
-import Control.Exception (handle, SomeException(..))
-
-import qualified Distribution.Hackage.DB as DB
-
-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' <- if exists
-               then readFile cachePath
-               else getHackageHash
-    let hash = reverse (dropWhile (=='\n') (reverse hash'))
-    unless exists $ do
-      createDirectoryIfMissing True (dropFileName cachePath)
-      writeFile cachePath hash
-    return hash
-  where getHackageHash = do
-            let command = "exec nix-prefetch-url 2>/dev/tty " ++ hackagePath pkg TarGz
-            handle handlePrefetchError (readProcess "bash" ["-c", command] "")
-        handlePrefetchError (SomeException _) =
-           error $ "\nError: Cannot compute hash. (Not a hackage project?)\n" ++
-                  "Specify hash explicitly via --sha256 and add appropriate \"src\" attribute " ++
-                  "to resulting nix expression."
-
-hashCachePath :: PackageIdentifier -> IO FilePath
-hashCachePath (PackageIdentifier (PackageName name) version') = do
-    home <- getHomeDirectory
-    return $ home ++ "/.cache/cabal2nix" </> name ++ "-" ++ version <.> "sha256"
-  where
-    version = showVersion version'
-
-readCabalFile :: Maybe FilePath -> FilePath -> IO String
-readCabalFile hackageDbPath path
-  | "cabal://" `isPrefixOf` path = do let pid p = fromJust $ simpleParse p
-                                          packageName = drop 8 path
-                                      case versionBranch $ pkgVersion $ pid packageName of
-                                        [] -> do
-                                          packageDescription <- DB.lookup packageName `fmap` maybe DB.readHackage DB.readHackage' hackageDbPath
-                                          case packageDescription of
-                                            Just d -> do
-                                              let version = showVersion $ last $ DB.keys d
-                                              readCabalFile Nothing $ hackagePath (pid $ packageName ++ "-" ++ version) Cabal
-                                            Nothing -> error "No such package"
-                                        _  -> readCabalFile Nothing $ hackagePath (pid packageName) Cabal
-  | "http://"  `isPrefixOf` path = fetchUrl path
-  | "file://"  `isPrefixOf` path = readCabalFile Nothing (drop 7 path)
-  | otherwise                    = readFile path
-
-fetchUrl :: String -> IO String
-fetchUrl url = do
-  (_,rsp) <- browse $ do
-     setCheckForProxy True
-     setDebugLog Nothing
-     setOutHandler (\_ -> return ())
-     request (getRequest url)
-  return (rspBody rsp)
diff --git a/src/Cabal2Nix/Name.hs b/src/Cabal2Nix/Name.hs
--- a/src/Cabal2Nix/Name.hs
+++ b/src/Cabal2Nix/Name.hs
@@ -48,6 +48,7 @@
 libNixName "gstreamer-plugins-base-0.10"        = return "gstreamer-plugins-base"
 libNixName "gthread-2.0"                        = return "glib"
 libNixName "gtk+-2.0"                           = return "gtk"
+libNixName "gtk+-3.0"                           = return "gtk3"
 libNixName "gtkglext-1.0"                       = return "gtkglext"
 libNixName "gtksourceview-2.0"                  = return "gtksourceview"
 libNixName "icudata"                            = return "icu"
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,144 @@
+module Cabal2Nix.Package where
+
+import Control.Applicative
+import Control.Monad
+import Control.Monad.IO.Class
+import Control.Monad.Trans.Maybe
+import Data.List ( isSuffixOf, isPrefixOf )
+import Data.Maybe ( listToMaybe )
+import Distribution.NixOS.Derivation.Cabal
+import Distribution.NixOS.Fetch
+import Distribution.Text ( simpleParse )
+import System.Directory ( doesDirectoryExist, doesFileExist, createDirectoryIfMissing, getHomeDirectory, getDirectoryContents )
+import System.Exit ( exitFailure )
+import System.FilePath ( (</>), (<.>) )
+import System.IO ( hPutStrLn, stderr, hPutStr )
+
+import qualified Distribution.Hackage.DB as DB
+import qualified Distribution.Package as Cabal
+import qualified Distribution.PackageDescription as Cabal
+import qualified Distribution.PackageDescription.Parse as Cabal
+import qualified Distribution.ParseUtils as ParseUtils
+
+import qualified Control.Exception as Exception
+
+data Package = Package
+  { source :: DerivationSource
+  , cabal :: Cabal.GenericPackageDescription
+  }
+
+getPackage :: Maybe String -> Source -> IO Package
+getPackage optHackageDB source = do
+  (derivSource, pkgDesc) <- fetchOrFromDB optHackageDB source
+  flip Package pkgDesc <$> maybe (sourceFromHackage (sourceHash source) $ showPackageIdentifier pkgDesc) return derivSource
+
+
+fetchOrFromDB :: Maybe String -> Source -> IO (Maybe DerivationSource, Cabal.GenericPackageDescription)
+fetchOrFromDB optHackageDB src
+  | "cabal://" `isPrefixOf` sourceUrl src = fmap ((,) Nothing) . fromDB optHackageDB . drop (length "cabal://") $ sourceUrl src
+  | otherwise                             = do
+    r <- fetch cabalFromPath src
+    case r of
+      Nothing ->
+        hPutStrLn stderr "*** failed to fetch source. Does the URL exist?" >> exitFailure
+      Just (derivSource, (externalSource, pkgDesc)) ->
+        return (derivSource <$ guard externalSource, pkgDesc)
+
+fromDB :: Maybe String -> String -> IO Cabal.GenericPackageDescription
+fromDB optHackageDB pkg = do
+  pkgDesc <- (lookupVersion <=< DB.lookup name) <$> maybe DB.readHackage DB.readHackage' optHackageDB
+  case pkgDesc of
+    Just r -> return r
+    Nothing -> hPutStrLn stderr "*** no such package in the cabal database (did you run cabal update?). " >> exitFailure
+ where
+  Just pkgId = simpleParse pkg
+  Cabal.PackageName name = Cabal.pkgName pkgId
+  version = Cabal.pkgVersion pkgId
+
+  lookupVersion :: DB.Map DB.Version Cabal.GenericPackageDescription -> Maybe Cabal.GenericPackageDescription
+  lookupVersion
+    | null (versionBranch version) = fmap snd . listToMaybe . reverse . DB.toAscList
+    | otherwise                    = DB.lookup version
+
+
+readFileMay :: String -> IO (Maybe String)
+readFileMay file = do
+  e <- doesFileExist file
+  if e
+    then Just <$> readFile file
+    else return Nothing
+
+hashCachePath :: String -> IO String
+hashCachePath pid = do
+  home <- getHomeDirectory
+  let cacheDir = home </> ".cache/cabal2nix"
+  createDirectoryIfMissing True cacheDir
+  return $ cacheDir </> pid <.> "sha256"
+
+sourceFromHackage :: Maybe String -> String -> IO DerivationSource
+sourceFromHackage optHash pkgId = do
+  cacheFile <- hashCachePath pkgId
+  let cachedHash = MaybeT $ maybe (readFileMay cacheFile) (return . Just) optHash
+      url = "mirror://hackage/" ++ pkgId ++ ".tar.gz"
+
+  -- Use the cached hash (either from cache file or given on cmdline via sha256 opt)
+  -- if available, otherwise download from hackage to compute hash.
+  maybeHash <- runMaybeT $ cachedHash <|> derivHash . fst <$> fetchWith (False, "url") (Source url "" Nothing)
+  case maybeHash of
+    Just hash ->
+      -- We need to force the hash here. If we didn't do this, then when reading the
+      -- hash from the cache file, the cache file will still be open for reading
+      -- (because lazy io) when writeFile opens the file again for writing. By forcing
+      -- the hash here, we ensure that the file is closed before opening it again.
+      seq (length hash) $
+      DerivationSource "url" url "" hash <$ writeFile cacheFile hash
+    Nothing -> do
+      hPutStr stderr $ unlines
+        [ "*** cannot compute hash. (Not a hackage project?)"
+        , " If your project is not on hackage, please supply the path to the root directory of"
+        , " the project, not to the cabal file."
+        , ""
+        , " If your project is on hackage but you still want to specify the hash manually, you"
+        , " can use the --sha256 option."
+        ]
+      exitFailure
+
+showPackageIdentifier :: Cabal.GenericPackageDescription -> String
+showPackageIdentifier pkgDesc = name ++ "-" ++ showVersion version where
+  pkgId = Cabal.package . Cabal.packageDescription $ pkgDesc
+  Cabal.PackageName name = Cabal.packageName pkgId
+  version = Cabal.packageVersion pkgId
+
+cabalFromPath :: FilePath -> MaybeT IO (Bool, Cabal.GenericPackageDescription)
+cabalFromPath path = do
+  d <- liftIO $ doesDirectoryExist path
+  (,) d <$> if d
+    then cabalFromDirectory  path
+    else cabalFromFile False path
+
+cabalFromDirectory :: FilePath -> MaybeT IO Cabal.GenericPackageDescription
+cabalFromDirectory dir = do
+  cabals <- liftIO $ getDirectoryContents dir >>= filterM doesFileExist . map (dir </>) . filter (".cabal" `isSuffixOf`)
+  case cabals of
+    [cabalFile] -> cabalFromFile True cabalFile
+    _       -> liftIO $ hPutStrLn stderr "*** found zero or more than one cabal file. Exiting." >> exitFailure
+
+handleIO :: (Exception.IOException -> IO a) -> IO a -> IO a
+handleIO = Exception.handle
+
+cabalFromFile :: Bool -> FilePath -> MaybeT IO Cabal.GenericPackageDescription
+cabalFromFile failHard file =
+  -- readFile throws an error if it's used on binary files which contain sequences
+  -- that do not represent valid characters. To catch that exception, we need to
+  -- wrap the whole block in `catchIO`, because of lazy IO. The `case` will force
+  -- the reading of the file, so we will always catch the expression here.
+  MaybeT $ handleIO (const $ return Nothing) $ do
+    content <- readFile file
+    case Cabal.parsePackageDescription content of
+      Cabal.ParseFailed e | failHard -> do
+        let (line, err) = ParseUtils.locatedErrorMsg e
+            msg = maybe "" ((++ ": ") . show) line ++ err
+        putStrLn $ "*** error parsing cabal file: " ++ msg
+        exitFailure
+      Cabal.ParseFailed _  -> return Nothing
+      Cabal.ParseOk     _ a -> return (Just a)
diff --git a/src/Cabal2Nix/PostProcess.hs b/src/Cabal2Nix/PostProcess.hs
--- a/src/Cabal2Nix/PostProcess.hs
+++ b/src/Cabal2Nix/PostProcess.hs
@@ -15,7 +15,9 @@
   | pname == "alex" && version >= Version [3,1] []
                                 = deriv { buildTools = "perl":"happy":buildTools }
   | pname == "bindings-GLFW"    = deriv { extraLibs = "libXext":"libXfixes":extraLibs }
+  | pname == "bits-extras"      = deriv { configureFlags = "--ghc-option=-lgcc_s":configureFlags, extraLibs = filter (/= "gcc_s") extraLibs }
   | pname == "cabal2nix"        = deriv { doCheck = True, phaseOverrides = cabal2nixDoCheckHook }
+  | pname == "cabal-bounds"      = deriv { buildTools = "cabalInstall":buildTools }
   | pname == "cabal-install" && version >= Version [0,14] []
                                 = deriv { phaseOverrides = cabalInstallPostInstall }
   | pname == "cairo"            = deriv { extraLibs = "pkgconfig":"libc":"cairo":"zlib":extraLibs }
@@ -43,6 +45,7 @@
   | pname == "gtksourceview2"   = deriv { extraLibs = "pkgconfig":"libc":extraLibs }
   | pname == "haddock" && version < Version [2,14] []
                                 = deriv { buildTools = "alex":"happy":buildTools }
+  | pname == "haddock"          = deriv { phaseOverrides = haddockPreCheck }
   | pname == "happy"            = deriv { buildTools = "perl":buildTools }
   | pname == "haskeline"        = deriv { buildDepends = "utf8String":buildDepends }
   | pname == "haskell-src"      = deriv { buildTools = "happy":buildTools }
@@ -95,7 +98,6 @@
                                 = deriv { doCheck = True, phaseOverrides = sybDoCheck }
   | pname == "tar"              = deriv { runHaddock = True, phaseOverrides = tarNoHaddock }
   | pname == "terminfo"         = deriv { extraLibs = "ncurses":extraLibs }
-  | pname == "text-icu"         = deriv { doCheck = True, phaseOverrides = textIcuDoCheckHook }
   | pname == "threadscope"      = deriv { configureFlags = "--ghc-options=-rtsopts":configureFlags }
   | pname == "thyme"            = deriv { buildTools = "cpphs":buildTools }
   | pname == "transformers" && version >= Version [0,4,1] []
@@ -257,9 +259,6 @@
 cookieDoCheckHook :: String
 cookieDoCheckHook = "doCheck = self.stdenv.lib.versionOlder \"7.8\" self.ghc.version;"
 
-textIcuDoCheckHook :: String
-textIcuDoCheckHook = "doCheck = !self.stdenv.isDarwin;"
-
 eitherNoHaddock :: String
 eitherNoHaddock = "noHaddock = self.stdenv.lib.versionOlder self.ghc.version \"7.6\";"
 
@@ -302,3 +301,6 @@
 sybDoCheck, splitDoCheck :: String
 sybDoCheck = "doCheck = self.stdenv.lib.versionOlder self.ghc.version \"7.9\";"
 splitDoCheck = sybDoCheck
+
+haddockPreCheck :: String
+haddockPreCheck = "preCheck = \"unset GHC_PACKAGE_PATH\";"
diff --git a/src/Distribution/NixOS/Derivation/Cabal.hs b/src/Distribution/NixOS/Derivation/Cabal.hs
--- a/src/Distribution/NixOS/Derivation/Cabal.hs
+++ b/src/Distribution/NixOS/Derivation/Cabal.hs
@@ -1,4 +1,4 @@
-{-# LANGUAGE PatternGuards, CPP #-}
+{-# LANGUAGE PatternGuards, RecordWildCards, CPP #-}
 {- |
    Module      :  Distribution.NixOS.Derivation.Cabal
    License     :  BSD3
@@ -14,16 +14,18 @@
 module Distribution.NixOS.Derivation.Cabal
   ( Derivation(..)
   , parseDerivation
+  , DerivationSource(..)
   , module Distribution.NixOS.Derivation.Meta
   , module Data.Version
   )
   where
 
 import Distribution.NixOS.Derivation.Meta
+import Distribution.NixOS.Fetch
 import Distribution.NixOS.PrettyPrinting
 import Distribution.NixOS.Regex hiding ( empty )
-import Distribution.Text
 import Distribution.Package
+import Distribution.Text
 #ifdef __HADDOCK__
 import Distribution.PackageDescription ( PackageDescription )
 #endif
@@ -39,11 +41,10 @@
 --
 -- Note that the "Text" instance definition provides pretty-printing,
 -- but no parsing as of now!
-
 data Derivation = MkDerivation
   { pname               :: String
   , version             :: Version
-  , sha256              :: String
+  , src                 :: DerivationSource
   , isLibrary           :: Bool
   , isExecutable        :: Bool
   , extraFunctionArgs   :: [String]
@@ -82,7 +83,7 @@
   , nest 2 $ vcat
     [ attr "pname"   $ string (pname deriv)
     , attr "version" $ doubleQuotes (disp (version deriv))
-    , attr "sha256"  $ string (sha256 deriv)
+    , sourceAttr (src deriv)
     , boolattr "isLibrary" (not (isLibrary deriv) || isExecutable deriv) (isLibrary deriv)
     , boolattr "isExecutable" (not (isLibrary deriv) || isExecutable deriv) (isExecutable deriv)
     , listattr "buildDepends" empty (buildDepends deriv)
@@ -106,20 +107,36 @@
   where
     inputs = nub $ sortBy (compare `on` map toLower) $ filter (/="cabal") $ filter (not . isPrefixOf "self.") $
               buildDepends deriv ++ testDepends deriv ++ buildTools deriv ++ extraLibs deriv ++ pkgConfDeps deriv ++ extraFunctionArgs deriv
+           ++ ["fetch" ++ derivKind (src deriv) | derivKind (src deriv) /= "" && not isHackagePackage]
     renderedFlags =  [ text "-f" <> (if enable then empty else char '-') <> text f | (FlagName f, enable) <- cabalFlags deriv ]
                   ++ map text (configureFlags deriv)
+    isHackagePackage = "mirror://hackage/" `isPrefixOf` derivUrl (src deriv)
+    sourceAttr (DerivationSource{..})
+      | isHackagePackage = attr "sha256" $ string derivHash
+      | derivKind /= "" = vcat
+         [ text "src" <+> equals <+> text ("fetch" ++ derivKind) <+> lbrace
+         , nest 2 $ vcat
+           [ attr "url" $ string derivUrl
+           , attr "sha256" $ string derivHash
+           , if derivRevision /= "" then attr "rev" (string derivRevision) else empty
+           ]
+         , rbrace <> semi
+         ]
+      | otherwise = attr "src" $ text derivUrl
 
 -- | A very incomplete parser that extracts 'pname', 'version',
 -- 'sha256', 'platforms', 'hydraPlatforms', 'maintainers', 'doCheck',
 -- 'jailbreak', and 'runHaddock' from the given Nix expression.
-
 parseDerivation :: String -> Maybe Derivation
 parseDerivation buf
   | buf =~ "cabal.mkDerivation"
   , [name]    <- buf `regsubmatch` "pname *= *\"([^\"]+)\""
   , [vers']   <- buf `regsubmatch` "version *= *\"([^\"]+)\""
   , Just vers <- simpleParse vers'
-  , [sha]     <- buf `regsubmatch` "sha256 *= *\"([^\"]+)\""
+  , sha       <- buf `regsubmatch` "sha256 *= *\"([^\"]+)\""
+  , sr        <- buf `regsubmatch` "src *= *([^;]+);"
+  , url       <- buf `regsubmatch` "url *= *\"?([^;\"]+)\"? *;"
+  , rev       <- buf `regsubmatch` "rev *= *\"([^\"]+)\""
   , hplats    <- buf `regsubmatch` "hydraPlatforms *= *([^;]+);"
   , plats     <- buf `regsubmatch` "platforms *= *([^;]+);"
   , maint     <- buf `regsubmatch` "maintainers *= *(with [^;]+;)? \\[([^\"]+)]"
@@ -131,7 +148,12 @@
               = Just MkDerivation
                   { pname          = name
                   , version        = vers
-                  , sha256         = sha
+                  , src            = case (sr,url) of
+                                       ([]   , _      ) -> DerivationSource "url" ("mirror://hackage/" ++ name ++ "-" ++ vers' ++ ".tar.gz") "" $ head sha
+                                       (sr':_, []     ) -> DerivationSource "" sr' "" ""
+                                       (sr':_, url':_ ) -> DerivationSource (drop (length "fetch") . head . words $ sr')
+                                                                           url' (head $ rev ++ [""]) $ head sha
+
                   , isLibrary      = False
                   , isExecutable   = False
                   , extraFunctionArgs = []
diff --git a/src/Distribution/NixOS/Fetch.hs b/src/Distribution/NixOS/Fetch.hs
new file mode 100644
--- /dev/null
+++ b/src/Distribution/NixOS/Fetch.hs
@@ -0,0 +1,133 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+module Distribution.NixOS.Fetch
+  ( Source(..)
+  , DerivationSource(..), fromDerivationSource
+  , fetch
+  , fetchWith
+  ) where
+
+import Control.Applicative
+import Control.Monad
+import Control.Monad.IO.Class
+import Control.Monad.Trans.Maybe
+import Data.Maybe
+import System.Directory
+import System.Environment
+import System.Exit
+import System.IO
+import System.Process
+
+-- | A source is a location from which we can fetch, such as a HTTP URL, a GIT URL, ....
+data Source = Source
+  { sourceUrl       :: String       -- ^ URL to fetch from.
+  , sourceRevision  :: String       -- ^ Revision to use. For protocols where this doesn't make sense (such as HTTP), this
+                                   --   should be the empty string.
+  , sourceHash      :: Maybe String -- ^ The expected hash of the source, if available.
+  } deriving (Show, Eq, Ord)
+
+-- | A source for a derivation. It always needs a hash and also has a protocol attached to it (url, git, svn, ...).
+-- A @DerivationSource@ also always has it's revision fully resolved (not relative revisions like @master@, @HEAD@, etc).
+data DerivationSource = DerivationSource
+  { derivKind     :: String -- ^ The kind of the source. The name of the build-support fetch derivation should be fetch<kind>.
+  , derivUrl      :: String -- ^ URL to fetch from.
+  , derivRevision :: String -- ^ Revision to use. Leave empty if the fetcher doesn't support revisions.
+  , derivHash     :: String -- ^ The hash of the source.
+  } deriving (Show, Eq, Ord)
+
+fromDerivationSource :: DerivationSource -> Source
+fromDerivationSource DerivationSource{..} = Source derivUrl derivRevision $ Just derivHash
+
+-- | Fetch a source, trying any of the various nix-prefetch-* scripts.
+fetch :: forall a. (String -> MaybeT IO a)      -- ^ This function is passed the output path name as an argument.
+                                                -- It should return 'Nothing' if the file doesn't match the expected format.
+                                                -- This is required, because we cannot always check if a download succeeded otherwise.
+      -> Source                                 -- ^ The source to fetch from.
+      -> IO (Maybe (DerivationSource, a))       -- ^ The derivation source and the result of the processing function. Returns Nothing if the download failed.
+fetch f = runMaybeT . fetchers where
+  fetchers :: Source -> MaybeT IO (DerivationSource, a)
+  fetchers source = msum . (fetchLocal source :) $ map (\fetcher -> fetchWith fetcher source >>= process)
+    [ (False, "zip")
+    , (False, "url")
+    , (True, "git")
+    , (True, "hg")
+    , (True, "svn")
+    , (True, "bzr")
+    ]
+
+  fetchLocal :: Source -> MaybeT IO (DerivationSource, a)
+  fetchLocal src = do
+    let path = stripPrefix "file://" $ sourceUrl src
+    existsFile <- liftIO $ doesFileExist path
+    existsDir  <- liftIO $ doesDirectoryExist path
+    guard $ existsDir || existsFile
+    let path' | '/' `elem` path = path
+              | otherwise       = "./" ++ path
+    process (DerivationSource "" path' "" "", path') <|> localArchive path'
+
+  localArchive :: FilePath -> MaybeT IO (DerivationSource, a)
+  localArchive path = do
+    absolutePath <- liftIO $ canonicalizePath path
+    unpacked <- snd <$> fetchWith (False, "zip") (Source ("file://" ++ absolutePath) "" Nothing)
+    process (DerivationSource "" absolutePath "" "", unpacked)
+
+  process :: (DerivationSource, FilePath) -> MaybeT IO (DerivationSource, a)
+  process (derivSource, file) = (,) derivSource <$> f file
+
+-- | Like 'fetch', but allows to specify which script to use.
+fetchWith :: (Bool, String) -> Source -> MaybeT IO (DerivationSource, FilePath)
+fetchWith (supportsRev, kind) source = do
+  unless ((sourceRevision source /= "") || isNothing (sourceHash source) || not supportsRev) $
+    liftIO (hPutStrLn stderr "** need a revision for VCS when the hash is given. skipping.") >> mzero
+
+  MaybeT $ liftIO $ do
+    envs <- getEnvironment
+    (Nothing, Just stdoutH, _, processH) <- createProcess (proc script args)
+      { env = Just $ ("PRINT_PATH", "1") : envs
+      , std_in = Inherit
+      , std_err = Inherit
+      , std_out = CreatePipe
+      }
+
+    exitCode <- waitForProcess processH
+    case exitCode of
+      ExitSuccess   -> fmap Just . processOutputSwitch . lines =<< hGetContents stdoutH
+      ExitFailure _ -> return Nothing
+
+ where
+
+   script :: String
+   script = "nix-prefetch-" ++ kind
+
+   args :: [String]
+   args = sourceUrl source : [ sourceRevision source | supportsRev ] ++ maybeToList (sourceHash source)
+
+   processOutputWithRev :: [String] -> IO (DerivationSource, FilePath)
+   processOutputWithRev [rev,hash,path] = return (DerivationSource kind (sourceUrl source) (extractRevision rev) hash, path)
+   processOutputWithRev out = unexpectedOutput out
+
+   extractRevision :: String -> String
+   extractRevision = unwords . drop 3 . words -- drop the "<vcs> revision is" prefix
+
+   processOutput :: [String] -> IO (DerivationSource, FilePath)
+   processOutput [hash,path] = return (DerivationSource kind (sourceUrl source) (sourceRevision source) hash, path)
+   processOutput out = unexpectedOutput out
+
+   unexpectedOutput :: [String] -> IO a
+   unexpectedOutput out = do
+     hPutStrLn stderr $ "*** unexpected output from " ++ "nix-prefetch-" ++ kind ++ " script: "
+     hPutStr   stderr $ unlines out
+     exitFailure
+
+   processOutputSwitch :: [String] -> IO (DerivationSource, FilePath)
+   processOutputSwitch
+     | supportsRev && isNothing (sourceHash source) = processOutputWithRev
+     | otherwise                                   = processOutput
+
+stripPrefix :: Eq a => [a] -> [a] -> [a]
+stripPrefix prefix as
+  | prefix' == prefix = stripped
+  | otherwise = as
+ where
+  (prefix', stripped) = splitAt (length prefix) as
diff --git a/src/cabal2nix.hs b/src/cabal2nix.hs
--- a/src/cabal2nix.hs
+++ b/src/cabal2nix.hs
@@ -1,14 +1,13 @@
 module Main ( main ) where
 
-import Cabal2Nix.Hackage ( hashPackage, readCabalFile )
 import Cabal2Nix.Generate ( cabal2nix )
 import Cabal2Nix.Normalize ( normalize )
+import Cabal2Nix.Package
 import Distribution.NixOS.Derivation.Cabal
+import Distribution.NixOS.Fetch
 
 import Control.Exception ( bracket )
 import Control.Monad ( when )
-import Distribution.PackageDescription ( package, packageDescription )
-import Distribution.PackageDescription.Parse ( parsePackageDescription, ParseResult(..) )
 import Distribution.Text ( disp )
 import System.Console.GetOpt ( OptDescr(..), ArgDescr(..), ArgOrder(..), usageInfo, getOpt )
 import System.Environment ( getArgs )
@@ -24,6 +23,7 @@
   , optHaddock :: Bool
   , optDoCheck :: Bool
   , optJailbreak :: Bool
+  , optRevision :: String
   , optHyperlinkSource :: Bool
   , optHackageDb :: Maybe FilePath
   }
@@ -39,6 +39,7 @@
   , optHaddock = True
   , optDoCheck = True
   , optJailbreak = False
+  , optRevision = ""
   , optHyperlinkSource = True
   , optHackageDb = Nothing
   }
@@ -53,19 +54,25 @@
   , Option ""  ["jailbreak"]  (NoArg (\o -> o { optJailbreak = True }))                                  "don't honor version restrictions on build inputs"
   , Option ""  ["no-haddock"] (NoArg (\o -> o { optHaddock = False }))                                   "don't run Haddock when building this package"
   , Option ""  ["no-check"]   (NoArg (\o -> o { optDoCheck = False }))                                   "don't run regression test suites of this package"
+  , Option ""  ["rev"]        (ReqArg (\x o -> o { optRevision = x }) "REVISION")                        "revision, only used when fetching from VCS"
   , Option ""  ["no-hyperlink-source"] (NoArg (\o -> o { optHyperlinkSource = False }))                  "don't add pretty-printed source code to the documentation"
   ]
 
 usage :: String
-usage = usageInfo "Usage: cabal2nix [options] url-to-cabal-file" options ++ unlines
+usage = usageInfo "Usage: cabal2nix [options] URI" options ++ unlines
         [ ""
         , "Recognized URI schemes:"
         , ""
-        , "  cabal://pkgname-pkgversion       download the specified package from Hackage"
-        , "  cabal://pkgname                  download latest version of the specified package from Hackage"
-        , "  http://host/path                 fetch the Cabal file via HTTP"
-        , "  file:///local/path               load the Cabal file from the local disk"
-        , "  /local/path                      abbreviated version of file URI"
+        , "  cabal://pkgname-pkgversion     download the specified package from Hackage"
+        , "  cabal://pkgname                download latest version of the specified package from Hackage"
+        , "  file:///local/path             load the Cabal file from the local disk"
+        , "  /local/path                    abbreviated version of file URI"
+        , "  <git/svn/bzr/hg URL>           download the source from the specified repository"
+        , ""
+        , "If the URI refers to a cabal file, information for building the package will be retrieved from that file, but "
+        , "hackage will be used as a source for the derivation."
+        , "Otherwise, the supplied URI will be used to as the source for the derivation and the information is taken"
+        , "from the cabal file at the root of the downloaded source."
         ]
 
 cmdlineError :: String -> IO a
@@ -82,19 +89,9 @@
   when (optPrintHelp cfg) (putStr usage >> exitSuccess)
   when (length args /= 1) (cmdlineError "*** exactly one url-to-cabal-file must be specified\n")
 
-  cabal' <- fmap parsePackageDescription (readCabalFile (optHackageDb cfg) (head args))
-  cabal <- case cabal' of
-             ParseOk _ a -> return a
-             ParseFailed err -> do
-               hPutStrLn stderr ("*** cannot parse cabal file: " ++ show err)
-               exitFailure
-
-  let packageId = package (packageDescription cabal)
-  sha <- case optSha256 cfg of
-              Just hash -> return hash
-              Nothing -> hashPackage packageId
+  pkg <- getPackage (optHackageDb cfg) $ Source (head args) (optRevision cfg) (optSha256 cfg)
 
-  let deriv  = (cabal2nix cabal) { sha256 = sha, runHaddock = optHaddock cfg, jailbreak = optJailbreak cfg }
+  let deriv  = (cabal2nix $ cabal pkg) { src = source pkg, runHaddock = optHaddock cfg, jailbreak = optJailbreak cfg }
       deriv' = deriv { metaSection = (metaSection deriv)
                                      { maintainers = optMaintainer cfg
                                      , platforms   = optPlatform cfg
diff --git a/src/hackage4nix.hs b/src/hackage4nix.hs
--- a/src/hackage4nix.hs
+++ b/src/hackage4nix.hs
@@ -1,7 +1,7 @@
 module Main ( main ) where
 
-import Cabal2Nix.Normalize
 import Cabal2Nix.Generate
+import Cabal2Nix.Normalize
 import Control.Exception ( bracket )
 import Control.Monad.RWS
 import Data.List
@@ -111,6 +111,7 @@
       nix' <- io (readFile file) >>= parseNixFile file
       flip (maybe (return ())) nix' $ \nix ->
         modify (Set.insert nix)
+
   -- Re-generate all Haskell packages in-place (unless
   -- 'regenerateDerivation' decided that this particular package
   -- shouldn't be touched.
@@ -124,10 +125,11 @@
         maints = maintainers (metaSection deriv)
         plats  = platforms (metaSection deriv)
         hplats = if isLatest deriv then hydraPlatforms (metaSection deriv) else ["self.stdenv.lib.platforms.none"]
+
     when regenerate $ do
       msgDebug ("re-generate " ++ path)
       pkg <- getCabalPackage (pname deriv) (version deriv)
-      let deriv'  = (cabal2nix pkg) { sha256 = sha256 deriv
+      let deriv'  = (cabal2nix pkg) { src = src deriv
                                     , runHaddock = runHaddock deriv
                                     , doCheck = doCheck deriv
                                     , jailbreak = jailbreak deriv
@@ -143,6 +145,7 @@
                                            }
                            }
       io $ writeFile path (show (disp (normalize deriv'')))
+
   -- Discover available updates and print the appropriate cabal2nix
   -- command line to the console for the user to execute at his/her
   -- discretion.
diff --git a/test/doc-test.hs b/test/doc-test.hs
--- a/test/doc-test.hs
+++ b/test/doc-test.hs
@@ -20,13 +20,14 @@
              , "src/Cabal2Nix/CorePackages.hs"
              , "src/Cabal2Nix/Flags.hs"
              , "src/Cabal2Nix/Generate.hs"
-             , "src/Cabal2Nix/Hackage.hs"
+             , "src/Cabal2Nix/Package.hs"
              , "src/Cabal2Nix/Normalize.hs"
              , "src/Cabal2Nix/Name.hs"
              , "src/Cabal2Nix/PostProcess.hs"
              , "src/Distribution/NixOS/Derivation/License.hs"
              , "src/Distribution/NixOS/Derivation/Cabal.hs"
              , "src/Distribution/NixOS/Derivation/Meta.hs"
+             , "src/Distribution/NixOS/Fetch.hs"
              , "src/Distribution/NixOS/PrettyPrinting.hs"
              , "src/Distribution/NixOS/Regex.hs"
              ]
