diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,14 @@
+## Changes in 0.30.0
+  - Warn on duplicate fields (see #283)
+  - Always render `cabal-version` as `x.y` instead of `>= x.y` so that `cabal
+    check` does not complain (see #322)
+  - Extend `build-tools` so that it subsumes Cabal's `build-tools` and
+    `build-tool-depends` (see #254)
+  - Add support for `system-build-tools`
+  - Keep declaration order for literal files in c-sources (and other fields
+    that accept glob patterns).  This is crucial as a workaround for
+    https://ghc.haskell.org/trac/ghc/ticket/13786 (see #324)
+
 ## Changes in 0.29.7
   - Expose more stuff from `Hpack.Yaml` so that it can be used by third parties
 
diff --git a/hpack.cabal b/hpack.cabal
--- a/hpack.cabal
+++ b/hpack.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 8d1eb0679326dc7c31be647812822700dc8277a27737239f5a704e45f367abdf
+-- hash: a1136b4a83bc50abf6e1fac6ef221b3df0c837b248aa357568449996e9d20078
 
 name:           hpack
-version:        0.29.7
+version:        0.30.0
 synopsis:       A modern format for Haskell packages
 description:    See README at <https://github.com/sol/hpack#readme>
 category:       Development
@@ -50,7 +50,7 @@
     , transformers
     , unordered-containers
     , vector
-    , yaml
+    , yaml >=0.10.0
   exposed-modules:
       Hpack
       Hpack.Config
@@ -68,6 +68,7 @@
       Hpack.Options
       Hpack.Render.Dsl
       Hpack.Render.Hints
+      Hpack.Syntax.BuildTools
       Hpack.Syntax.Defaults
       Hpack.Syntax.Dependency
       Hpack.Syntax.DependencyVersion
@@ -105,7 +106,7 @@
     , transformers
     , unordered-containers
     , vector
-    , yaml
+    , yaml >=0.10.0
   other-modules:
       Paths_hpack
   default-language: Haskell2010
@@ -147,7 +148,7 @@
     , transformers
     , unordered-containers
     , vector
-    , yaml >=0.8.28
+    , yaml >=0.10.0
   other-modules:
       Data.Aeson.Config.FromValueSpec
       Data.Aeson.Config.TypesSpec
@@ -163,6 +164,7 @@
       Hpack.Render.DslSpec
       Hpack.Render.HintsSpec
       Hpack.RenderSpec
+      Hpack.Syntax.BuildToolsSpec
       Hpack.Syntax.DefaultsSpec
       Hpack.Syntax.DependencySpec
       Hpack.Syntax.GitSpec
@@ -183,6 +185,7 @@
       Hpack.Render
       Hpack.Render.Dsl
       Hpack.Render.Hints
+      Hpack.Syntax.BuildTools
       Hpack.Syntax.Defaults
       Hpack.Syntax.Dependency
       Hpack.Syntax.DependencyVersion
diff --git a/src/Data/Aeson/Config/Parser.hs b/src/Data/Aeson/Config/Parser.hs
--- a/src/Data/Aeson/Config/Parser.hs
+++ b/src/Data/Aeson/Config/Parser.hs
@@ -24,6 +24,9 @@
 , Array
 
 , liftParser
+
+, fromAesonPath
+, formatPath
 ) where
 
 import           Control.Monad
diff --git a/src/Hpack.hs b/src/Hpack.hs
--- a/src/Hpack.hs
+++ b/src/Hpack.hs
@@ -118,7 +118,7 @@
 setProgramName name options@Options{..} =
   options {optionsDecodeOptions = optionsDecodeOptions {decodeOptionsProgramName = name}}
 
-setDecode :: (FilePath -> IO (Either String Value)) -> Options -> Options
+setDecode :: (FilePath -> IO (Either String ([String], Value))) -> Options -> Options
 setDecode decode options@Options{..} =
   options {optionsDecodeOptions = optionsDecodeOptions {decodeOptionsDecode = decode}}
 
diff --git a/src/Hpack/Config.hs b/src/Hpack/Config.hs
--- a/src/Hpack/Config.hs
+++ b/src/Hpack/Config.hs
@@ -42,6 +42,8 @@
 , SourceDependency(..)
 , GitRef
 , GitUrl
+, BuildTool(..)
+, SystemBuildTools(..)
 , GhcOption
 , Verbatim(..)
 , VerbatimValue(..)
@@ -69,12 +71,15 @@
 , LibrarySection(..)
 , fromLibrarySectionInConditional
 , formatOrList
+
+, toBuildTool
 #endif
 ) where
 
 import           Control.Applicative
-import           Control.Arrow ((>>>))
+import           Control.Arrow ((>>>), (&&&))
 import           Control.Monad
+import           Data.Either
 import           Data.Bifunctor
 import           Data.Bitraversable
 import           Data.Map.Lazy (Map)
@@ -110,6 +115,7 @@
 import qualified Hpack.Yaml as Yaml
 import           Hpack.Syntax.DependencyVersion
 import           Hpack.Syntax.Dependency
+import           Hpack.Syntax.BuildTools
 import           Hpack.License
 import           Hpack.CabalFile (parseVersion)
 
@@ -175,7 +181,7 @@
     deps xs = [(name, version) | (name, version) <- (Map.toList . unDependencies . sectionDependencies) xs]
 
 section :: a -> Section a
-section a = Section a [] mempty [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] Nothing [] mempty []
+section a = Section a [] mempty [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] Nothing [] mempty mempty []
 
 packageConfig :: FilePath
 packageConfig = "package.yaml"
@@ -277,7 +283,8 @@
 , commonOptionsLdOptions :: Maybe (List LdOption)
 , commonOptionsBuildable :: Maybe Bool
 , commonOptionsWhen :: Maybe (List (ConditionalSection cSources cxxSources jsSources a))
-, commonOptionsBuildTools :: Maybe Dependencies
+, commonOptionsBuildTools :: Maybe BuildTools
+, commonOptionsSystemBuildTools :: Maybe SystemBuildTools
 , commonOptionsVerbatim :: Maybe (List Verbatim)
 } deriving (Functor, Generic)
 
@@ -310,6 +317,7 @@
   , commonOptionsBuildable = Nothing
   , commonOptionsWhen = Nothing
   , commonOptionsBuildTools = Nothing
+  , commonOptionsSystemBuildTools = Nothing
   , commonOptionsVerbatim = Nothing
   }
   mappend = (<>)
@@ -339,7 +347,8 @@
   , commonOptionsLdOptions = commonOptionsLdOptions a <> commonOptionsLdOptions b
   , commonOptionsBuildable = commonOptionsBuildable b <|> commonOptionsBuildable a
   , commonOptionsWhen = commonOptionsWhen a <> commonOptionsWhen b
-  , commonOptionsBuildTools = commonOptionsBuildTools b <> commonOptionsBuildTools a
+  , commonOptionsBuildTools = commonOptionsBuildTools a <> commonOptionsBuildTools b
+  , commonOptionsSystemBuildTools = commonOptionsSystemBuildTools b <> commonOptionsSystemBuildTools a
   , commonOptionsVerbatim = commonOptionsVerbatim a <> commonOptionsVerbatim b
   }
 
@@ -561,13 +570,16 @@
 type Errors = ExceptT String
 
 decodeYaml :: FromValue a => ProgramName -> FilePath -> Warnings (Errors IO) a
-decodeYaml programName file = lift (ExceptT $ Yaml.decodeYaml file) >>= decodeValue programName file
+decodeYaml programName file = do
+  (warnings, a) <- lift (ExceptT $ Yaml.decodeYaml file)
+  tell warnings
+  decodeValue programName file a
 
 data DecodeOptions = DecodeOptions {
   decodeOptionsProgramName :: ProgramName
 , decodeOptionsTarget :: FilePath
 , decodeOptionsUserDataDir :: Maybe FilePath
-, decodeOptionsDecode :: FilePath -> IO (Either String Value)
+, decodeOptionsDecode :: FilePath -> IO (Either String ([String], Value))
 }
 
 newtype ProgramName = ProgramName String
@@ -588,7 +600,8 @@
 
 readPackageConfig :: DecodeOptions -> IO (Either String DecodeResult)
 readPackageConfig (DecodeOptions programName file mUserDataDir readValue) = runExceptT $ fmap addCabalFile . runWriterT $ do
-  value <- lift . ExceptT $ readValue file
+  (warnings, value) <- lift . ExceptT $ readValue file
+  tell warnings
   config <- decodeValue programName file value
   dir <- liftIO $ takeDirectory <$> canonicalizePath file
   userDataDir <- liftIO $ maybe (getAppUserDataDirectory "hpack") return mUserDataDir
@@ -652,11 +665,9 @@
             Nothing -> Nothing
 
     inferredCabalVersion :: String
-    inferredCabalVersion
-      | version >= makeVersion [2,1] = showVersion version
-      | otherwise = (">= " ++) . showVersion $ version
+    inferredCabalVersion = showVersion version
 
-    version = fromMaybe (makeVersion [1,10]) $ maximum [
+    version = fromMaybe (makeVersion [1,12]) $ maximum [
         packageCabalVersion
       , packageLibrary >>= libraryCabalVersion
       , internalLibsCabalVersion packageInternalLibraries
@@ -705,16 +716,64 @@
     executableHasGeneratedModules = any (not . null . executableGeneratedModules)
 
     sectionCabalVersion :: Section a -> Maybe Version
-    sectionCabalVersion sect = maximum [
+    sectionCabalVersion sect = maximum $ [
         makeVersion [2,2] <$ guard (sectionSatisfies (not . null . sectionCxxSources) sect)
       , makeVersion [2,2] <$ guard (sectionSatisfies (not . null . sectionCxxOptions) sect)
-      ]
+      ] ++ map versionFromSystemBuildTool systemBuildTools
+      where
+        versionFromSystemBuildTool name
+          | name `elem` known_1_10 = Nothing
+          | name `elem` known_1_14 = Just (makeVersion [1,14])
+          | name `elem` known_1_22 = Just (makeVersion [1,22])
+          | otherwise = Just (makeVersion [2,0])
 
+        known_1_10 = [
+            "ghc"
+          , "ghc-pkg"
+          , "hugs"
+          , "ffihugs"
+          , "nhc98"
+          , "hmake"
+          , "jhc"
+          , "lhc"
+          , "lhc-pkg"
+          , "uhc"
+          , "gcc"
+          , "ranlib"
+          , "ar"
+          , "strip"
+          , "ld"
+          , "tar"
+          , "pkg-config"
+          ] \\ [
+          -- Support for these build tools has been removed from Cabal at some point
+            "hugs"
+          , "ffihugs"
+          , "nhc98"
+          , "ranlib"
+          , "lhc"
+          , "lhc-pkg"
+          ]
+        known_1_14 = [
+            "hpc"
+          ]
+        known_1_22 = [
+            "ghcjs"
+          , "ghcjs-pkg"
+          -- , "haskell-suite" // not a real build tool
+          -- , "haskell-suite-pkg" // not a real build tool
+          ]
+
+        systemBuildTools :: [String]
+        systemBuildTools = Map.keys $ unSystemBuildTools $ sectionAll sectionSystemBuildTools sect
+
     sectionSatisfies :: (Section a -> Bool) -> Section a -> Bool
     sectionSatisfies p sect = or [
         p sect
       , any (any (sectionSatisfies p)) (sectionConditionals sect)
       ]
+    sectionAll :: (Semigroup b, Monoid b) => (Section a -> b) -> Section a -> b
+    sectionAll f sect = f sect <> foldMap (foldMap $ sectionAll f) (sectionConditionals sect)
 
 decodeValue :: FromValue a => ProgramName -> FilePath -> Value -> Warnings (Errors IO) a
 decodeValue (ProgramName programName) file value = do
@@ -798,6 +857,9 @@
 , executableGeneratedModules :: [String]
 } deriving (Eq, Show)
 
+data BuildTool = BuildTool String String | LocalBuildTool String
+  deriving (Show, Eq, Ord)
+
 data Section a = Section {
   sectionData :: a
 , sectionSourceDirs :: [FilePath]
@@ -823,7 +885,8 @@
 , sectionLdOptions :: [LdOption]
 , sectionBuildable :: Maybe Bool
 , sectionConditionals :: [Conditional (Section a)]
-, sectionBuildTools :: Dependencies
+, sectionBuildTools :: Map BuildTool DependencyVersion
+, sectionSystemBuildTools :: SystemBuildTools
 , sectionVerbatim :: [Verbatim]
 } deriving (Eq, Show, Functor, Foldable, Traversable)
 
@@ -962,20 +1025,23 @@
 
 toPackage_ :: MonadIO m => FilePath -> Product GlobalOptions (PackageConfig CSources CxxSources JsSources) -> Warnings m (Package, String)
 toPackage_ dir (Product g PackageConfig{..}) = do
+  executableMap <- toExecutableMap packageName_ packageConfigExecutables packageConfigExecutable
   let
     globalVerbatim = commonOptionsVerbatim g
     globalOptions = g {commonOptionsVerbatim = Nothing}
 
-    toSect :: Monoid a => WithCommonOptions CSources CxxSources JsSources a -> Section a
-    toSect = toSection . first ((mempty <$ globalOptions) <>)
+    executableNames = maybe [] Map.keys executableMap
 
-    toLib = toLibrary dir packageName_ . toSect
-    toExecutables = liftIO . maybe mempty (traverse $ toExecutable dir packageName_ . toSect)
+    toSect :: (Monad m, Monoid a) => WithCommonOptions CSources CxxSources JsSources a -> Warnings m (Section a)
+    toSect = toSection packageName_ executableNames . first ((mempty <$ globalOptions) <>)
 
-  mLibrary <- liftIO $ traverse toLib packageConfigLibrary
-  internalLibraries <- liftIO $ maybe mempty (traverse toLib) packageConfigInternalLibraries
+    toLib = toSect >=> liftIO . toLibrary dir packageName_
+    toExecutables = maybe (return mempty) (traverse $ toSect >=> liftIO . toExecutable dir packageName_)
 
-  executables <- toExecutableMap packageName_ packageConfigExecutables packageConfigExecutable >>= toExecutables
+  mLibrary <- traverse toLib packageConfigLibrary
+  internalLibraries <- maybe (return mempty) (traverse toLib) packageConfigInternalLibraries
+
+  executables <- toExecutables executableMap
   tests <- toExecutables packageConfigTests
   benchmarks <- toExecutables packageConfigBenchmarks
 
@@ -1237,10 +1303,14 @@
       , sectionConditionals = map (fmap flatten) sectionConditionals
       }
 
-toSection :: WithCommonOptions CSources CxxSources JsSources a -> Section a
-toSection = go
+toSection :: Monad m => String -> [String] -> WithCommonOptions CSources CxxSources JsSources a -> Warnings m (Section a)
+toSection packageName_ executableNames = go
   where
-    go (Product CommonOptions{..} a) = Section {
+    go (Product CommonOptions{..} a) = do
+      (systemBuildTools, buildTools) <- maybe (return mempty) toBuildTools commonOptionsBuildTools
+
+      conditionals <- mapM toConditional (fromMaybeList commonOptionsWhen)
+      return Section {
         sectionData = a
       , sectionSourceDirs = fromMaybeList commonOptionsSourceDirs
       , sectionDefaultExtensions = fromMaybeList commonOptionsDefaultExtensions
@@ -1264,17 +1334,67 @@
       , sectionBuildable = commonOptionsBuildable
       , sectionDependencies = fromMaybe mempty commonOptionsDependencies
       , sectionPkgConfigDependencies = fromMaybeList commonOptionsPkgConfigDependencies
-      , sectionConditionals = map toConditional (fromMaybeList commonOptionsWhen)
-      , sectionBuildTools = fromMaybe mempty commonOptionsBuildTools
+      , sectionConditionals = conditionals
+      , sectionBuildTools = buildTools
+      , sectionSystemBuildTools = systemBuildTools <> fromMaybe mempty commonOptionsSystemBuildTools
       , sectionVerbatim = fromMaybeList commonOptionsVerbatim
       }
+    toBuildTools :: Monad m => BuildTools -> Warnings m (SystemBuildTools, Map BuildTool DependencyVersion)
+    toBuildTools = fmap (mkSystemBuildTools &&& mkBuildTools) . mapM toBuildTool_ . unBuildTools
+      where
+        mkSystemBuildTools = SystemBuildTools . Map.fromList . lefts
+        mkBuildTools = Map.fromList . rights
+        toBuildTool_ (t, v) = bimap addVersion addVersion <$> toBuildTool packageName_ executableNames t
+          where
+            addVersion = flip (,) v
 
-    toConditional :: ConditionalSection CSources CxxSources JsSources a -> Conditional (Section a)
+    toConditional :: Monad m => ConditionalSection CSources CxxSources JsSources a -> Warnings m (Conditional (Section a))
     toConditional x = case x of
-      ThenElseConditional (Product (ThenElse then_ else_) c) -> conditional c (go then_) (Just $ go else_)
-      FlatConditional (Product sect c) -> conditional c (go sect) Nothing
+      ThenElseConditional (Product (ThenElse then_ else_) c) -> conditional c <$> (go then_) <*> (Just <$> go else_)
+      FlatConditional (Product sect c) -> conditional c <$> (go sect) <*> pure Nothing
       where
         conditional (Condition (Cond c)) = Conditional c
+
+toBuildTool :: Monad m => String -> [String] -> ParseBuildTool -> Warnings m (Either String BuildTool)
+toBuildTool packageName_ executableNames = \ case
+  QualifiedBuildTool pkg executable
+    | pkg == packageName_ && executable `elem` executableNames -> localBuildTool executable
+    | otherwise -> buildTool pkg executable
+  UnqualifiedBuildTool executable
+    | executable `elem` executableNames -> localBuildTool executable
+    | Just pkg <- lookup executable legacyTools -> legacyBuildTool pkg executable
+    | executable `elem` legacySystemTools -> legacySystemBuildTool executable
+    | otherwise -> buildTool executable executable
+  where
+    buildTool pkg = return . Right . BuildTool pkg
+    systemBuildTool = return . Left
+    localBuildTool = return . Right . LocalBuildTool
+    legacyBuildTool pkg executable = warnLegacyTool pkg executable >> buildTool pkg executable
+    legacySystemBuildTool executable = warnLegacySystemTool executable >> systemBuildTool executable
+    legacyTools = [
+        ("gtk2hsTypeGen", "gtk2hs-buildtools")
+      , ("gtk2hsHookGenerator", "gtk2hs-buildtools")
+      , ("gtk2hsC2hs", "gtk2hs-buildtools")
+      , ("cabal", "cabal-install")
+      , ("grgen", "cgen")
+      , ("cgen-hs", "cgen")
+      ]
+    legacySystemTools = [
+        "ghc"
+      , "git"
+      , "llvm-config"
+      , "gfortran"
+      , "gcc"
+      , "couchdb"
+      , "mcc"
+      , "nix-store"
+      , "nix-instantiate"
+      , "nix-hash"
+      , "nix-env"
+      , "nix-build"
+      ]
+    warnLegacyTool pkg name = tell ["Usage of the unqualified build-tool name " ++ show name ++ " is deprecated! Please use the qualified name \"" ++ pkg ++ ":" ++ name ++ "\" instead!"]
+    warnLegacySystemTool name = tell ["Listing " ++ show name ++ " under build-tools is deperecated! Please list system executables under system-build-tools instead!"]
 
 pathsModuleFromPackageName :: String -> String
 pathsModuleFromPackageName name = "Paths_" ++ map f name
diff --git a/src/Hpack/Render.hs b/src/Hpack/Render.hs
--- a/src/Hpack/Render.hs
+++ b/src/Hpack/Render.hs
@@ -241,8 +241,8 @@
   , renderLdOptions sectionLdOptions
   , renderDependencies "build-depends" sectionDependencies
   , Field "pkgconfig-depends" (CommaSeparatedList sectionPkgConfigDependencies)
-  , renderDependencies "build-tools" sectionBuildTools
   ]
+  ++ renderBuildTools sectionBuildTools sectionSystemBuildTools
   ++ maybe [] (return . renderBuildable) sectionBuildable
   ++ map (renderConditional renderSectionData) sectionConditionals
   ++ extraFieldsEnd
@@ -313,12 +313,43 @@
 renderDependencies name = Field name . CommaSeparatedList . map renderDependency . Map.toList . unDependencies
 
 renderDependency :: (String, DependencyVersion) -> String
-renderDependency (name, version) = name ++ v
+renderDependency (name, version) = name ++ renderVersion version
+
+renderVersion :: DependencyVersion -> String
+renderVersion version = case version of
+  AnyVersion -> ""
+  VersionRange x -> " " ++ x
+  SourceDependency _ -> ""
+
+renderBuildTools :: Map BuildTool DependencyVersion -> SystemBuildTools -> [Element]
+renderBuildTools (map renderBuildTool . Map.toList -> xs) systemBuildTools = [
+    Field "build-tools" (CommaSeparatedList $ [x | BuildTools x <- xs] ++ renderSystemBuildTools systemBuildTools)
+  , Field "build-tool-depends" (CommaSeparatedList [x | BuildToolDepends x <- xs])
+  ]
+
+data RenderBuildTool = BuildTools String | BuildToolDepends String
+
+renderBuildTool :: (BuildTool,  DependencyVersion) -> RenderBuildTool
+renderBuildTool (buildTool, renderVersion -> version) = case buildTool of
+  LocalBuildTool executable -> BuildTools (executable ++ version)
+  BuildTool pkg executable
+    | pkg == executable && executable `elem` knownBuildTools -> BuildTools (executable ++ version)
+    | otherwise -> BuildToolDepends (pkg ++ ":" ++ executable ++ version)
   where
-    v = case version of
-      AnyVersion -> ""
-      VersionRange x -> " " ++ x
-      SourceDependency _ -> ""
+    knownBuildTools :: [String]
+    knownBuildTools = [
+        "alex"
+      , "c2hs"
+      , "cpphs"
+      , "greencard"
+      , "haddock"
+      , "happy"
+      , "hsc2hs"
+      , "hscolour"
+      ]
+
+renderSystemBuildTools :: SystemBuildTools -> [String]
+renderSystemBuildTools = map renderDependency . Map.toList . unSystemBuildTools
 
 renderGhcOptions :: [GhcOption] -> Element
 renderGhcOptions = Field "ghc-options" . WordList
diff --git a/src/Hpack/Syntax/BuildTools.hs b/src/Hpack/Syntax/BuildTools.hs
new file mode 100644
--- /dev/null
+++ b/src/Hpack/Syntax/BuildTools.hs
@@ -0,0 +1,94 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE ViewPatterns #-}
+module Hpack.Syntax.BuildTools (
+  BuildTools(..)
+, ParseBuildTool(..)
+, SystemBuildTools(..)
+) where
+
+import           Data.Text (Text)
+import qualified Data.Text as T
+import           Data.Semigroup (Semigroup(..))
+import           Data.Bifunctor
+import           Control.Applicative
+import qualified Distribution.Package as D
+import           Data.Map.Lazy (Map)
+import qualified Data.Map.Lazy as Map
+
+import qualified Distribution.Types.ExeDependency as D
+import qualified Distribution.Types.UnqualComponentName as D
+import qualified Distribution.Types.LegacyExeDependency as D
+
+import           Data.Aeson.Config.FromValue
+
+import           Hpack.Syntax.DependencyVersion
+import           Hpack.Syntax.Dependency (parseDependency)
+
+newtype BuildTools = BuildTools {
+  unBuildTools :: [(ParseBuildTool, DependencyVersion)]
+} deriving (Show, Eq, Semigroup, Monoid)
+
+data ParseBuildTool = QualifiedBuildTool String String | UnqualifiedBuildTool String
+  deriving (Show, Eq)
+
+instance FromValue BuildTools where
+  fromValue v = case v of
+    String s -> BuildTools . return <$> buildToolFromString s
+    Array xs -> BuildTools <$> parseArray buildToolFromValue xs
+    Object _ -> BuildTools . map (first nameToBuildTool) . Map.toList <$> fromValue v
+    _ -> typeMismatch "Array, Object, or String" v
+
+nameToBuildTool :: String -> ParseBuildTool
+nameToBuildTool name = case break (== ':') name of
+  (executable, "") -> UnqualifiedBuildTool executable
+  (package, executable) -> QualifiedBuildTool package (drop 1 executable)
+
+buildToolFromValue :: Value -> Parser (ParseBuildTool, DependencyVersion)
+buildToolFromValue v = case v of
+  String s -> buildToolFromString s
+  Object o -> sourceDependency o
+  _ -> typeMismatch "Object or String" v
+  where
+    sourceDependency o = (,) <$> (nameToBuildTool <$> name) <*> (SourceDependency <$> fromValue v)
+      where
+        name :: Parser String
+        name = o .: "name"
+
+buildToolFromString :: Text -> Parser (ParseBuildTool, DependencyVersion)
+buildToolFromString s = parseQualifiedBuildTool s <|> parseUnqualifiedBuildTool s
+
+parseQualifiedBuildTool :: Monad m => Text -> m (ParseBuildTool, DependencyVersion)
+parseQualifiedBuildTool = fmap f . cabalParse "build tool" . T.unpack
+  where
+    f :: D.ExeDependency -> (ParseBuildTool, DependencyVersion)
+    f (D.ExeDependency package executable version) = (
+        QualifiedBuildTool (D.unPackageName package) (D.unUnqualComponentName executable)
+      , dependencyVersionFromCabal version
+      )
+
+parseUnqualifiedBuildTool :: Monad m => Text -> m (ParseBuildTool, DependencyVersion)
+parseUnqualifiedBuildTool = fmap (first UnqualifiedBuildTool) . parseDependency "build tool"
+
+newtype SystemBuildTools = SystemBuildTools {
+  unSystemBuildTools :: Map String DependencyVersion
+} deriving (Show, Eq, Semigroup, Monoid)
+
+instance FromValue SystemBuildTools where
+  fromValue v = case v of
+    String s -> fromList . return <$> parseSystemBuildTool s
+    Array xs -> fromList <$> parseArray (withText parseSystemBuildTool) xs
+    Object _ -> SystemBuildTools <$> fromValue v
+    _ -> typeMismatch "Array, Object, or String" v
+    where
+      fromList :: [(String, DependencyVersion)] -> SystemBuildTools
+      fromList = SystemBuildTools . Map.fromList
+
+parseSystemBuildTool :: Monad m => Text -> m (String, DependencyVersion)
+parseSystemBuildTool = fmap fromCabal . parseCabalBuildTool . T.unpack
+  where
+    fromCabal :: D.LegacyExeDependency -> (String, DependencyVersion)
+    fromCabal (D.LegacyExeDependency name version) = (name, dependencyVersionFromCabal version)
+
+    parseCabalBuildTool :: Monad m => String -> m D.LegacyExeDependency
+    parseCabalBuildTool = cabalParse "system build tool"
diff --git a/src/Hpack/Syntax/Dependency.hs b/src/Hpack/Syntax/Dependency.hs
--- a/src/Hpack/Syntax/Dependency.hs
+++ b/src/Hpack/Syntax/Dependency.hs
@@ -3,6 +3,7 @@
 {-# LANGUAGE TypeFamilies #-}
 module Hpack.Syntax.Dependency (
   Dependencies(..)
+, parseDependency
 ) where
 
 import           Data.Text (Text)
diff --git a/src/Hpack/Util.hs b/src/Hpack/Util.hs
--- a/src/Hpack/Util.hs
+++ b/src/Hpack/Util.hs
@@ -113,11 +113,11 @@
   files <- globDir compiledPatterns dir >>= mapM removeDirectories
   let
     results :: [GlobResult]
-    results = map (uncurry $ uncurry GlobResult) $ zip (zip patterns compiledPatterns) files
+    results = map (uncurry $ uncurry GlobResult) $ zip (zip patterns compiledPatterns) (map sort files)
   return (combineResults results)
   where
     combineResults :: [GlobResult] -> ([String], [FilePath])
-    combineResults = bimap concat (nub . sort . concat) . unzip . map fromResult
+    combineResults = bimap concat (nub . concat) . unzip . map fromResult
 
     fromResult :: GlobResult -> ([String], [FilePath])
     fromResult (GlobResult pattern compiledPattern files) = case files of
diff --git a/src/Hpack/Yaml.hs b/src/Hpack/Yaml.hs
--- a/src/Hpack/Yaml.hs
+++ b/src/Hpack/Yaml.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE LambdaCase #-}
 module Hpack.Yaml (
 -- | /__NOTE:__/ This module is exposed to allow integration of Hpack into
 -- other tools.  It is not meant for general use by end users.  The following
@@ -16,14 +17,21 @@
 , module Data.Aeson.Config.FromValue
 ) where
 
-import           Data.Yaml hiding (decodeFile, decodeFileEither)
+import           Data.Bifunctor
+import           Data.Yaml hiding (decodeFile, decodeFileWithWarnings)
 import           Data.Yaml.Include
+import           Data.Yaml.Internal (Warning(..))
 import           Data.Aeson.Config.FromValue
+import           Data.Aeson.Config.Parser (fromAesonPath, formatPath)
 
-decodeYaml :: FilePath -> IO (Either String Value)
+formatWarning :: FilePath -> Warning -> String
+formatWarning file = \ case
+  DuplicateKey path -> file ++ ": Duplicate field " ++ formatPath (fromAesonPath path)
+
+decodeYaml :: FilePath -> IO (Either String ([String], Value))
 decodeYaml file = do
-  result <- decodeFileEither file
-  return $ either (Left . errToString) Right result
+  result <- decodeFileWithWarnings file
+  return $ either (Left . errToString) (Right . first (map $ formatWarning file)) result
   where
     errToString err = file ++ case err of
       AesonException e -> ": " ++ e
diff --git a/test/EndToEndSpec.hs b/test/EndToEndSpec.hs
--- a/test/EndToEndSpec.hs
+++ b/test/EndToEndSpec.hs
@@ -39,6 +39,15 @@
       other-modules:
           Paths_foo
       |]
+
+    it "warns on duplicate fields" $ do
+      [i|
+      name: foo
+      name: foo
+      |] `shouldWarn` [
+          "package.yaml: Duplicate field $.name"
+        ]
+
     describe "spec-version" $ do
       it "accepts spec-version" $ do
         [i|
@@ -448,7 +457,7 @@
         extra-doc-files:
             CHANGES.markdown
             README.markdown
-        |]) {packageCabalVersion = ">= 1.18"}
+        |]) {packageCabalVersion = "1.18"}
 
       it "accepts glob patterns" $ do
         touch "CHANGES.markdown"
@@ -460,7 +469,7 @@
         extra-doc-files:
             CHANGES.markdown
             README.markdown
-        |]) {packageCabalVersion = ">= 1.18"}
+        |]) {packageCabalVersion = "1.18"}
 
       it "warns if a glob pattern does not match anything" $ do
         [i|
@@ -469,6 +478,124 @@
           - "*.markdown"
         |] `shouldWarn` ["Specified pattern \"*.markdown\" for extra-doc-files does not match any files"]
 
+    describe "build-tools" $ do
+      it "adds known build tools to build-tools" $ do
+        [i|
+        executable:
+          build-tools:
+            alex == 0.1.0
+        |] `shouldRenderTo` executable_ "foo" [i|
+        build-tools:
+            alex ==0.1.0
+        |]
+
+      it "adds other build tools to build-tool-depends" $ do
+        [i|
+        executable:
+          build-tools:
+            hspec-discover: 0.1.0
+        |] `shouldRenderTo` (executable_ "foo" [i|
+        build-tool-depends:
+            hspec-discover:hspec-discover ==0.1.0
+        |]) {
+          -- NOTE: We do not set this to 2.0 on purpose, so that the .cabal
+          -- file is compatible with a wider range of Cabal versions!
+          packageCabalVersion = "1.12"
+        }
+
+      context "when the name of a build tool matches an executable from the same package" $ do
+        it "adds it to build-tools" $ do
+          [i|
+          executables:
+            bar:
+              build-tools:
+                - bar
+          |] `shouldRenderTo` executable_ "bar" [i|
+          build-tools:
+              bar
+          |]
+
+        it "gives per-section unqualified names precedence over global qualified names" $ do
+          [i|
+          build-tools:
+            - foo:bar == 0.1.0
+          executables:
+            bar:
+              build-tools:
+                - bar == 0.2.0
+          |] `shouldRenderTo` executable_ "bar" [i|
+          build-tools:
+              bar ==0.2.0
+          |]
+
+        it "gives per-section qualified names precedence over global unqualified names" $ do
+          [i|
+          build-tools:
+            - bar == 0.1.0
+          executables:
+            bar:
+              build-tools:
+                - foo:bar == 0.2.0
+          |] `shouldRenderTo` executable_ "bar" [i|
+          build-tools:
+              bar ==0.2.0
+          |]
+
+      context "when the name of a build tool matches a legacy system build tool" $ do
+        it "adds it to build-tools" $ do
+          [i|
+          executable:
+            build-tools:
+              ghc >= 7.10
+          |] `shouldRenderTo` (executable_ "foo" [i|
+          build-tools:
+              ghc >=7.10
+          |]) { packageWarnings = ["Listing \"ghc\" under build-tools is deperecated! Please list system executables under system-build-tools instead!"] }
+
+    describe "system-build-tools" $ do
+      it "adds system build tools to build-tools" $ do
+        [i|
+        executable:
+          system-build-tools:
+            ghc >= 7.10
+        |] `shouldRenderTo` executable_ "foo" [i|
+        build-tools:
+            ghc >=7.10
+        |]
+
+      context "with hpc" $ do
+        it "infers cabal-version 1.14" $ do
+          [i|
+          executable:
+            system-build-tools:
+              hpc
+          |] `shouldRenderTo` (executable_ "foo" [i|
+          build-tools:
+              hpc
+          |]) {packageCabalVersion = "1.14"}
+
+      context "with ghcjs" $ do
+        it "infers cabal-version 1.22" $ do
+          [i|
+          executable:
+            system-build-tools:
+              ghcjs
+          |] `shouldRenderTo` (executable_ "foo" [i|
+          build-tools:
+              ghcjs
+          |]) {packageCabalVersion = "1.22"}
+
+      context "with an unknown system build tool" $ do
+        it "infers cabal-version 2.0" $ do
+          [i|
+          executable:
+            system-build-tools:
+              g++ >= 5.4.0
+          |] `shouldRenderTo` (executable_ "foo" [i|
+          build-tools:
+              g++ >=5.4.0
+          |]) {packageCabalVersion = "2.0"}
+
     describe "dependencies" $ do
       it "accepts single dependency" $ do
         [i|
@@ -615,8 +742,8 @@
             - cxxbits/*.cc
         |] `shouldRenderTo` (executable_ "foo" [i|
         cxx-sources:
-            cxxbits/bar.cc
             foo.cc
+            cxxbits/bar.cc
         |]) {packageCabalVersion = "2.2"}
 
     describe "extra-lib-dirs" $ do
@@ -671,72 +798,39 @@
             bar
         |]
 
-    describe "c-sources" $ before_ (touch "cbits/foo.c" >> touch "cbits/bar.c") $ do
-      context "with internal-libraries" $ do
-        it "warns when a glob pattern does not match any files" $ do
-          [i|
-          name: foo
-          internal-libraries:
-            bar:
-              c-sources: foo/*.c
-          |] `shouldWarn` pure "Specified pattern \"foo/*.c\" for c-sources does not match any files"
-
-      context "with library" $ do
-        it "accepts global c-sources" $ do
-          [i|
-          c-sources: cbits/*.c
-          library: {}
-          |] `shouldRenderTo` library_ [i|
-          c-sources:
-              cbits/bar.c
-              cbits/foo.c
-          |]
-
-        it "accepts c-sources" $ do
-          [i|
-          library:
-            c-sources: cbits/*.c
-          |] `shouldRenderTo` library_ [i|
+    describe "c-sources" $ before_ (touch "cbits/foo.c" >> touch "cbits/bar.c" >> touch "cbits/baz.c") $ do
+      it "keeps declaration order" $ do
+        -- IMPORTANT: This is crucial as a workaround for https://ghc.haskell.org/trac/ghc/ticket/13786
+        [i|
+        library:
           c-sources:
-              cbits/bar.c
-              cbits/foo.c
-          |]
-
-        it "accepts c-sources in conditional" $ do
-          [i|
-          library:
-            when:
-              condition: os(windows)
-              c-sources: cbits/*.c
-          |] `shouldRenderTo` library_ [i|
-          if os(windows)
-            c-sources:
-                cbits/bar.c
-                cbits/foo.c
-          |]
+            - cbits/foo.c
+            - cbits/bar.c
+            - cbits/baz.c
+        |] `shouldRenderTo` library_ [i|
+        c-sources:
+            cbits/foo.c
+            cbits/bar.c
+            cbits/baz.c
+        |]
 
-      context "with executables" $ do
-        it "accepts global c-sources" $ do
-          [i|
+      it "accepts glob patterns" $ do
+        [i|
+        library:
           c-sources: cbits/*.c
-          executables:
-            foo: {}
-          |] `shouldRenderTo` executable_ "foo" [i|
-          c-sources:
-              cbits/bar.c
-              cbits/foo.c
-          |]
+        |] `shouldRenderTo` library_ [i|
+        c-sources:
+            cbits/bar.c
+            cbits/baz.c
+            cbits/foo.c
+        |]
 
-        it "accepts c-sources" $ do
-          [i|
-          executables:
-            foo:
-              c-sources: cbits/*.c
-          |] `shouldRenderTo` executable_ "foo" [i|
-          c-sources:
-              cbits/bar.c
-              cbits/foo.c
-          |]
+      it "warns when a glob pattern does not match any files" $ do
+        [i|
+        name: foo
+        library:
+          c-sources: foo/*.c
+        |] `shouldWarn` pure "Specified pattern \"foo/*.c\" for c-sources does not match any files"
 
     describe "custom-setup" $ do
       it "warns on unknown fields" $ do
@@ -779,7 +873,7 @@
         |] `shouldRenderTo` (library_ [i|
           reexported-modules:
               Baz
-        |]) {packageCabalVersion = ">= 1.22"}
+        |]) {packageCabalVersion = "1.22"}
 
       it "accepts signatures" $ do
         [i|
@@ -788,7 +882,7 @@
         |] `shouldRenderTo` (library_ [i|
           signatures:
               Foo
-        |]) {packageCabalVersion = ">= 2.0"}
+        |]) {packageCabalVersion = "2.0"}
 
       context "when package.yaml contains duplicate modules" $ do
         it "generates a cabal file with duplicate modules" $ do
@@ -957,7 +1051,7 @@
             autogen-modules:
                 Foo
                 Bar
-            |]) {packageCabalVersion = ">= 2.0"}
+            |]) {packageCabalVersion = "2.0"}
 
           it "does not infer any mentioned generated modules" $ do
             touch "src/Exposed.hs"
@@ -978,7 +1072,7 @@
             autogen-modules:
                 Exposed
                 Other
-            |]) {packageCabalVersion = ">= 2.0"}
+            |]) {packageCabalVersion = "2.0"}
 
           it "does not infer any generated modules mentioned inside conditionals" $ do
             touch "src/Exposed.hs"
@@ -1003,7 +1097,7 @@
               autogen-modules:
                   Other
                   Exposed
-            |]) {packageCabalVersion = ">= 2.0"}
+            |]) {packageCabalVersion = "2.0"}
 
     describe "internal-libraries" $ do
       it "accepts internal-libraries" $ do
@@ -1108,7 +1202,7 @@
                 Foo
             autogen-modules:
                 Foo
-          |]) {packageCabalVersion = ">= 2.0"}
+          |]) {packageCabalVersion = "2.0"}
 
         context "with conditional" $ do
           it "doesn't infer any modules mentioned in that conditional" $ do
@@ -1320,8 +1414,8 @@
         it "overrides header fields" $ do
           [i|
           verbatim:
-            cabal-version: ">= 2.0"
-          |] `shouldRenderTo` (package "") {packageCabalVersion = ">= 2.0"}
+            cabal-version: foo
+          |] `shouldRenderTo` (package "") {packageCabalVersion = "foo"}
 
         it "overrides other fields" $ do
           touch "foo"
@@ -1382,7 +1476,7 @@
   deriving Eq
 
 instance Show RenderResult where
-  show (RenderResult warnings output) = unlines (map ("WARNING in " ++) warnings) ++ output
+  show (RenderResult warnings output) = unlines (map ("WARNING: " ++) warnings) ++ output
 
 shouldRenderTo :: HasCallStack => String -> Package -> Expectation
 shouldRenderTo input p = do
@@ -1391,7 +1485,7 @@
   createDirectory currentDirectory
   withCurrentDirectory currentDirectory $ do
     (warnings, output) <- run ".." (".." </> packageConfig) expected
-    RenderResult warnings (dropEmptyLines output) `shouldBe` RenderResult [] expected
+    RenderResult warnings (dropEmptyLines output) `shouldBe` RenderResult (packageWarnings p) expected
   where
     expected = dropEmptyLines (renderPackage p)
     dropEmptyLines = unlines . filter (not . null) . lines
@@ -1408,7 +1502,7 @@
   run_ "" packageConfig "" `shouldReturn` Left expected
 
 customSetup :: String -> Package
-customSetup a = (package content) {packageCabalVersion = ">= 1.24", packageBuildType = "Custom"}
+customSetup a = (package content) {packageCabalVersion = "1.24", packageBuildType = "Custom"}
   where
     content = [i|
 custom-setup
@@ -1436,7 +1530,7 @@
 |]
 
 internalLibrary :: String -> String -> Package
-internalLibrary name e = (package content) {packageCabalVersion = ">= 2.0"}
+internalLibrary name e = (package content) {packageCabalVersion = "2.0"}
   where
     content = [i|
 library #{name}
@@ -1465,7 +1559,7 @@
 |]
 
 package :: String -> Package
-package = Package "foo" "0.0.0" "Simple" ">= 1.10"
+package c = Package "foo" "0.0.0" "Simple" "1.12" c []
 
 data Package = Package {
   packageName :: String
@@ -1473,6 +1567,7 @@
 , packageBuildType :: String
 , packageCabalVersion :: String
 , packageContent :: String
+, packageWarnings :: [String]
 }
 
 renderPackage :: Package -> String
diff --git a/test/Hpack/ConfigSpec.hs b/test/Hpack/ConfigSpec.hs
--- a/test/Hpack/ConfigSpec.hs
+++ b/test/Hpack/ConfigSpec.hs
@@ -23,8 +23,10 @@
 import           System.Directory (createDirectory)
 import           Data.Either
 import qualified Data.Map.Lazy as Map
+import           Control.Monad.Trans.Writer (runWriter)
 
 import           Hpack.Syntax.Dependency
+import           Hpack.Syntax.BuildTools
 import           Hpack.Config hiding (package)
 import qualified Hpack.Config as Config
 
@@ -158,6 +160,38 @@
         touch (dir </> "Setup.hs")
         getModules dir  "./." `shouldReturn` ["Foo"]
 
+  describe "toBuildTool" $ do
+    let toBuildTool_ = runWriter . toBuildTool "my-package" ["foo"]
+    context "with an UnqualifiedBuildTool" $ do
+      context "when name does not match a local executable" $ do
+        it "returns a BuildTool" $ do
+          toBuildTool_ (UnqualifiedBuildTool "bar") `shouldBe` (Right (BuildTool "bar" "bar"), [])
+
+      context "when name matches a local executable" $ do
+        it "returns a LocalBuildTool" $ do
+          toBuildTool_ (UnqualifiedBuildTool "foo") `shouldBe` (Right (LocalBuildTool "foo"), [])
+
+      context "when name matches a legacy executable" $ do
+        it "warns" $ do
+          toBuildTool_ (UnqualifiedBuildTool "gtk2hsTypeGen") `shouldBe` (Right (BuildTool "gtk2hs-buildtools" "gtk2hsTypeGen"), ["Usage of the unqualified build-tool name \"gtk2hsTypeGen\" is deprecated! Please use the qualified name \"gtk2hs-buildtools:gtk2hsTypeGen\" instead!"])
+
+      context "when name matches a legacy system build tool" $ do
+        it "warns" $ do
+          toBuildTool_ (UnqualifiedBuildTool "ghc") `shouldBe` (Left "ghc", ["Listing \"ghc\" under build-tools is deperecated! Please list system executables under system-build-tools instead!"])
+
+    context "with a QualifiedBuildTool" $ do
+      context "when only package matches the current package" $ do
+        it "returns a BuildTool" $ do
+          toBuildTool_ (QualifiedBuildTool "my-package" "bar") `shouldBe` (Right (BuildTool "my-package" "bar"), [])
+
+      context "when only executable matches a local executable" $ do
+        it "returns a BuildTool" $ do
+          toBuildTool_ (QualifiedBuildTool "other-package" "foo") `shouldBe` (Right (BuildTool "other-package" "foo"), [])
+
+      context "when both package matches the current package and executable matches a local executable" $ do
+        it "returns a LocalBuildTool" $ do
+          toBuildTool_ (QualifiedBuildTool "my-package" "foo") `shouldBe` (Right (LocalBuildTool "foo"), [])
+
   describe "readPackageConfig" $ do
     it "warns on missing name" $ do
       withPackageWarnings_ [i|
@@ -401,15 +435,6 @@
           |]
           (packageLibrary >>> (`shouldBe` Just (section library) {sectionSourceDirs = ["foo", "bar"]}))
 
-      it "accepts build-tools" $ do
-        withPackageConfig_ [i|
-          library:
-            build-tools:
-              - alex
-              - happy
-          |]
-          (packageLibrary >>> (`shouldBe` Just (section library) {sectionBuildTools = deps ["alex", "happy"]}))
-
       it "accepts default-extensions" $ do
         withPackageConfig_ [i|
           library:
@@ -437,15 +462,6 @@
           |]
           (packageLibrary >>> (`shouldBe` Just (section library) {sectionSourceDirs = ["foo", "bar"]}))
 
-      it "accepts global build-tools" $ do
-        withPackageConfig_ [i|
-          build-tools:
-            - alex
-            - happy
-          library: {}
-          |]
-          (packageLibrary >>> (`shouldBe` Just (section library) {sectionBuildTools = deps ["alex", "happy"]}))
-
       it "allows to specify exposed" $ do
         withPackageConfig_ [i|
           library:
@@ -502,17 +518,6 @@
           |]
           (packageExecutables >>> (`shouldBe` Map.fromList [("foo", (section (executable "Main.hs") {executableOtherModules = ["Paths_foo"]}) {sectionSourceDirs = ["foo", "bar"]})]))
 
-      it "accepts build-tools" $ do
-        withPackageConfig_ [i|
-          executables:
-            foo:
-              main: Main.hs
-              build-tools:
-                - alex
-                - happy
-          |]
-          (packageExecutables >>> (`shouldBe` Map.fromList [("foo", (section $ executable "Main.hs") {sectionBuildTools = deps ["alex", "happy"]})]))
-
       it "accepts global source-dirs" $ do
         withPackageConfig_ [i|
           source-dirs:
@@ -523,17 +528,6 @@
               main: Main.hs
           |]
           (packageExecutables >>> (`shouldBe` Map.fromList [("foo", (section (executable "Main.hs") {executableOtherModules = ["Paths_foo"]}) {sectionSourceDirs = ["foo", "bar"]})]))
-
-      it "accepts global build-tools" $ do
-        withPackageConfig_ [i|
-          build-tools:
-            - alex
-            - happy
-          executables:
-            foo:
-              main: Main.hs
-          |]
-          (packageExecutables >>> (`shouldBe` Map.fromList [("foo", (section $ executable "Main.hs") {sectionBuildTools = deps ["alex", "happy"]})]))
 
       it "accepts default-extensions" $ do
         withPackageConfig_ [i|
diff --git a/test/Hpack/Syntax/BuildToolsSpec.hs b/test/Hpack/Syntax/BuildToolsSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Hpack/Syntax/BuildToolsSpec.hs
@@ -0,0 +1,102 @@
+{-# LANGUAGE QuasiQuotes #-}
+{-# LANGUAGE OverloadedLists #-}
+module Hpack.Syntax.BuildToolsSpec (spec) where
+
+import           Helper
+
+import           Data.Aeson.Config.FromValueSpec (shouldDecodeTo_)
+
+import           Hpack.Syntax.DependencyVersion
+import           Hpack.Syntax.BuildTools
+
+spec :: Spec
+spec = do
+  describe "fromValue" $ do
+    context "when parsing BuildTools" $ do
+      context "with a scalar" $ do
+        it "accepts qualified names" $ do
+          [yaml|
+            foo:bar
+          |] `shouldDecodeTo_` BuildTools [(QualifiedBuildTool "foo" "bar", AnyVersion)]
+
+        it "accepts qualified names with a version" $ do
+          [yaml|
+            foo:bar >= 0.1.0
+          |] `shouldDecodeTo_` BuildTools [(QualifiedBuildTool "foo" "bar", VersionRange ">=0.1.0")]
+
+        it "accepts unqualified names" $ do
+          [yaml|
+            foo
+          |] `shouldDecodeTo_` BuildTools [(UnqualifiedBuildTool "foo", AnyVersion)]
+
+        it "accepts unqualified names with a version" $ do
+          [yaml|
+            foo >= 0.1.0
+          |] `shouldDecodeTo_` BuildTools [(UnqualifiedBuildTool "foo", VersionRange ">=0.1.0")]
+
+      context "with a mapping" $ do
+        it "accepts qualified names" $ do
+          [yaml|
+            foo:bar: 0.1.0
+          |] `shouldDecodeTo_` BuildTools [(QualifiedBuildTool "foo" "bar", VersionRange "==0.1.0")]
+
+        it "accepts unqualified names" $ do
+          [yaml|
+            foo: 0.1.0
+          |] `shouldDecodeTo_` BuildTools [(UnqualifiedBuildTool "foo", VersionRange "==0.1.0")]
+
+      context "with a list" $ do
+        it "accepts a list of build tools" $ do
+          [yaml|
+            - foo:one
+            - bar:two >= 0.1.0
+            - baz == 0.2.0
+          |] `shouldDecodeTo_` BuildTools [
+              (QualifiedBuildTool "foo" "one", AnyVersion)
+            , (QualifiedBuildTool "bar" "two", VersionRange ">=0.1.0")
+            , (UnqualifiedBuildTool "baz", VersionRange "==0.2.0")
+            ]
+
+        it "accepts source dependencies with a qualified name" $ do
+          let source = GitRef "https://github.com/sol/hpack" "master" Nothing
+          [yaml|
+            - name: hpack:foo
+              github: sol/hpack
+              ref: master
+          |] `shouldDecodeTo_` BuildTools [(QualifiedBuildTool "hpack" "foo", SourceDependency source)]
+
+        it "accepts source dependencies with an unqualified name" $ do
+          let source = GitRef "https://github.com/sol/hpack" "master" Nothing
+          [yaml|
+            - name: hpack
+              github: sol/hpack
+              ref: master
+          |] `shouldDecodeTo_` BuildTools [(UnqualifiedBuildTool "hpack", SourceDependency source)]
+
+    context "when parsing SystemBuildTools" $ do
+      context "with a scalar" $ do
+        it "accepts system build tools" $ do
+          [yaml|
+            g++
+          |] `shouldDecodeTo_` SystemBuildTools [("g++", AnyVersion)]
+
+        it "accepts system build tools with a version" $ do
+          [yaml|
+            g++ >= 0.1.0
+          |] `shouldDecodeTo_` SystemBuildTools [("g++", VersionRange ">=0.1.0")]
+
+      context "with a mapping" $ do
+        it "accepts system build tools" $ do
+          [yaml|
+            g++: 0.1.0
+          |] `shouldDecodeTo_` SystemBuildTools [("g++", VersionRange "==0.1.0")]
+
+      context "with a list" $ do
+        it "accepts a list of system build tools" $ do
+          [yaml|
+            - foo
+            - bar >= 0.1.0
+          |] `shouldDecodeTo_` SystemBuildTools [
+              ("foo", AnyVersion)
+            , ("bar", VersionRange ">=0.1.0")
+            ]
diff --git a/test/Hpack/UtilSpec.hs b/test/Hpack/UtilSpec.hs
--- a/test/Hpack/UtilSpec.hs
+++ b/test/Hpack/UtilSpec.hs
@@ -76,9 +76,14 @@
         tryReadFile "foo" `shouldReturn` Nothing
 
   describe "expandGlobs" $ around withTempDirectory $ do
-    it "accepts simple files" $ \dir -> do
+    it "accepts literal files" $ \dir -> do
       touch (dir </> "foo.js")
       expandGlobs "field-name" dir ["foo.js"] `shouldReturn` ([], ["foo.js"])
+
+    it "keeps declaration order for literal files" $ \dir -> do
+      touch (dir </> "foo.js")
+      touch (dir </> "bar.js")
+      expandGlobs "field-name" dir ["foo.js", "bar.js"] `shouldReturn` ([], ["foo.js", "bar.js"])
 
     it "removes duplicates" $ \dir -> do
       touch (dir </> "foo.js")
