diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## Changes in 0.38.0
+  - Generate `build-tool-depends` instead of `build-tools` starting with
+    `cabal-version: 2` (fixes #596)
+
 ## Changes in 0.37.0
   - Add support for `asm-options` and `asm-sources` (see #573)
 
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2014-2023 Simon Hengel <sol@typeful.net>
+Copyright (c) 2014-2025 Simon Hengel <sol@typeful.net>
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/hpack.cabal b/hpack.cabal
--- a/hpack.cabal
+++ b/hpack.cabal
@@ -1,11 +1,11 @@
 cabal-version: 2.0
 
--- This file has been generated from package.yaml by hpack version 0.36.0.
+-- This file has been generated from package.yaml by hpack version 0.38.0.
 --
 -- see: https://github.com/sol/hpack
 
 name:           hpack
-version:        0.37.0
+version:        0.38.0
 synopsis:       A modern format for Haskell packages
 description:    See README at <https://github.com/sol/hpack#readme>
 category:       Development
diff --git a/resources/test/hpack.cabal b/resources/test/hpack.cabal
--- a/resources/test/hpack.cabal
+++ b/resources/test/hpack.cabal
@@ -1,11 +1,11 @@
 cabal-version: 2.0
 
--- This file has been generated from package.yaml by hpack version 0.36.0.
+-- This file has been generated from package.yaml by hpack version 0.38.0.
 --
 -- see: https://github.com/sol/hpack
 
 name:           hpack
-version:        0.37.0
+version:        0.38.0
 synopsis:       A modern format for Haskell packages
 description:    See README at <https://github.com/sol/hpack#readme>
 category:       Development
diff --git a/src/Hpack/Config.hs b/src/Hpack/Config.hs
--- a/src/Hpack/Config.hs
+++ b/src/Hpack/Config.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE NamedFieldPuns #-}
 {-# LANGUAGE ConstraintKinds #-}
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE DataKinds #-}
@@ -41,6 +42,8 @@
 , package
 , section
 , Package(..)
+, CabalVersion(..)
+, makeCabalVersion
 , Dependencies(..)
 , DependencyInfo(..)
 , VersionConstraint(..)
@@ -104,7 +107,8 @@
 import qualified Control.Monad.State as State
 import           Control.Monad.Writer (MonadWriter, WriterT, runWriterT, tell)
 import           Control.Monad.Except
-import           Data.Version (Version, makeVersion, showVersion)
+import           Data.Version (Version, showVersion)
+import qualified Data.Version as Version
 
 import           Distribution.Pretty (prettyShow)
 import qualified Distribution.SPDX.License as SPDX
@@ -132,9 +136,13 @@
 
 import qualified Paths_hpack as Hpack (version)
 
+defaultCabalVersion :: Version
+defaultCabalVersion = Version.makeVersion [1,12]
+
 package :: String -> String -> Package
 package name version = Package {
-    packageName = name
+    packageCabalVersion = CabalVersion defaultCabalVersion
+  , packageName = name
   , packageVersion = version
   , packageSynopsis = Nothing
   , packageDescription = Nothing
@@ -698,8 +706,13 @@
   userDataDir <- liftIO $ maybe (getAppUserDataDirectory "hpack") return mUserDataDir
   toPackage formatYamlParseError userDataDir dir config
   where
-    addCabalFile :: ((Package, String), [String]) -> DecodeResult
-    addCabalFile ((pkg, cabalVersion), warnings) = DecodeResult pkg cabalVersion (takeDirectory_ file </> (packageName pkg ++ ".cabal")) warnings
+    addCabalFile :: (Package, [String]) -> DecodeResult
+    addCabalFile (pkg, warnings) = DecodeResult {
+        decodeResultPackage = pkg
+      , decodeResultCabalVersion = "cabal-version: " ++ showCabalVersion (packageCabalVersion pkg) ++ "\n\n"
+      , decodeResultCabalFile = takeDirectory_ file </> packageName pkg <.> "cabal"
+      , decodeResultWarnings = warnings
+      }
 
     takeDirectory_ :: FilePath -> FilePath
     takeDirectory_ p
@@ -718,9 +731,9 @@
   VerbatimBool b -> show b
   VerbatimNull -> ""
 
-addPathsModuleToGeneratedModules  :: Package -> Version -> Package
-addPathsModuleToGeneratedModules pkg cabalVersion
-  | cabalVersion < makeVersion [2] = pkg
+addPathsModuleToGeneratedModules :: Package -> Package
+addPathsModuleToGeneratedModules pkg
+  | packageCabalVersion pkg < makeCabalVersion [2] = pkg
   | otherwise = pkg {
       packageLibrary = fmap mapLibrary <$> packageLibrary pkg
     , packageInternalLibraries = fmap mapLibrary <$> packageInternalLibraries pkg
@@ -749,22 +762,53 @@
       where
         generatedModules = executableGeneratedModules executable
 
-determineCabalVersion :: Maybe (License SPDX.License) -> Package -> (Package, String, Maybe Version)
-determineCabalVersion inferredLicense pkg@Package{..} = (
-    pkg {
-        packageVerbatim = deleteVerbatimField "cabal-version" packageVerbatim
-      , packageLicense = formatLicense <$> license
-      }
-  , "cabal-version: " ++ effectiveCabalVersion ++ "\n\n"
-  , parseVersion effectiveCabalVersion
-  )
+data CabalVersion = CabalVersion Version | VerbatimCabalVersion String
+  deriving (Eq, Ord, Show)
+
+makeCabalVersion :: [Int] -> CabalVersion
+makeCabalVersion = CabalVersion . Version.makeVersion
+
+showCabalVersion :: CabalVersion -> String
+showCabalVersion = \ case
+  CabalVersion v -> showVersion v
+  VerbatimCabalVersion v -> v
+
+extractVerbatimCabalVersion :: [Verbatim] -> (Maybe CabalVersion, [Verbatim])
+extractVerbatimCabalVersion verbatim = case listToMaybe (mapMaybe extractCabalVersion verbatim) of
+  Nothing -> (Nothing, verbatim)
+  Just verbatimVersion -> (Just cabalVersion, deleteVerbatimField "cabal-version" verbatim)
+    where
+      cabalVersion :: CabalVersion
+      cabalVersion = case parseVersion verbatimVersion of
+        Nothing -> VerbatimCabalVersion verbatimVersion
+        Just v -> CabalVersion v
   where
-    effectiveCabalVersion = fromMaybe inferredCabalVersion verbatimCabalVersion
+    extractCabalVersion :: Verbatim -> Maybe String
+    extractCabalVersion = \ case
+      VerbatimLiteral _ -> Nothing
+      VerbatimObject o -> case Map.lookup "cabal-version" o of
+        Just v -> Just (verbatimValueToString v)
+        Nothing -> Nothing
 
+ensureRequiredCabalVersion :: Maybe (License SPDX.License) -> Package -> Package
+ensureRequiredCabalVersion inferredLicense pkg@Package{..} = pkg {
+    packageCabalVersion = version
+  , packageLicense = formatLicense <$> license
+  , packageVerbatim = verbatim
+  }
+  where
+    makeVersion :: [Int] -> CabalVersion
+    makeVersion = makeCabalVersion
+
+    (verbatimCabalVersion, verbatim) = extractVerbatimCabalVersion packageVerbatim
+
+    license :: Maybe (License String)
     license = fmap prettyShow <$> (parsedLicense <|> inferredLicense)
 
+    parsedLicense :: Maybe (License SPDX.License)
     parsedLicense = parseLicense <$> packageLicense
 
+    formatLicense :: License String -> String
     formatLicense = \ case
       MustSPDX spdx -> spdx
       CanSPDX _ spdx | version >= makeVersion [2,2] -> spdx
@@ -779,21 +823,14 @@
           CanSPDX _ _ -> False
           MustSPDX _ -> True
 
-    verbatimCabalVersion :: Maybe String
-    verbatimCabalVersion = listToMaybe (mapMaybe f packageVerbatim)
-      where
-        f :: Verbatim -> Maybe String
-        f = \ case
-          VerbatimLiteral _ -> Nothing
-          VerbatimObject o -> case Map.lookup "cabal-version" o of
-            Just v -> Just (verbatimValueToString v)
-            Nothing -> Nothing
-
-    inferredCabalVersion :: String
-    inferredCabalVersion = showVersion version
+    version :: CabalVersion
+    version = fromMaybe inferredVersion verbatimCabalVersion
 
-    version = fromMaybe (makeVersion [1,12]) $ maximum [
-        packageCabalVersion
+    inferredVersion :: CabalVersion
+    inferredVersion = fromMaybe packageCabalVersion $ maximum [
+        makeVersion [2,2] <$ guard mustSPDX
+      , makeVersion [1,24] <$ packageCustomSetup
+      , makeVersion [1,18] <$ guard (not (null packageExtraDocFiles))
       , packageLibrary >>= libraryCabalVersion
       , internalLibsCabalVersion packageInternalLibraries
       , executablesCabalVersion packageExecutables
@@ -801,15 +838,7 @@
       , executablesCabalVersion packageBenchmarks
       ]
 
-    packageCabalVersion :: Maybe Version
-    packageCabalVersion = maximum [
-        Nothing
-      , makeVersion [2,2] <$ guard mustSPDX
-      , makeVersion [1,24] <$ packageCustomSetup
-      , makeVersion [1,18] <$ guard (not (null packageExtraDocFiles))
-      ]
-
-    libraryCabalVersion :: Section Library -> Maybe Version
+    libraryCabalVersion :: Section Library -> Maybe CabalVersion
     libraryCabalVersion sect = maximum [
         makeVersion [1,22] <$ guard (has libraryReexportedModules)
       , makeVersion [2,0]  <$ guard (has librarySignatures)
@@ -820,17 +849,17 @@
       where
         has field = any (not . null . field) sect
 
-    internalLibsCabalVersion :: Map String (Section Library) -> Maybe Version
+    internalLibsCabalVersion :: Map String (Section Library) -> Maybe CabalVersion
     internalLibsCabalVersion internalLibraries
       | Map.null internalLibraries = Nothing
       | otherwise = foldr max (Just $ makeVersion [2,0]) versions
       where
         versions = libraryCabalVersion <$> Map.elems internalLibraries
 
-    executablesCabalVersion :: Map String (Section Executable) -> Maybe Version
+    executablesCabalVersion :: Map String (Section Executable) -> Maybe CabalVersion
     executablesCabalVersion = foldr max Nothing . map executableCabalVersion . Map.elems
 
-    executableCabalVersion :: Section Executable -> Maybe Version
+    executableCabalVersion :: Section Executable -> Maybe CabalVersion
     executableCabalVersion sect = maximum [
         makeVersion [2,0] <$ guard (executableHasGeneratedModules sect)
       , sectionCabalVersion (concatMap getExecutableModules) sect
@@ -839,7 +868,7 @@
     executableHasGeneratedModules :: Section Executable -> Bool
     executableHasGeneratedModules = any (not . null . executableGeneratedModules)
 
-    sectionCabalVersion :: (Section a -> [Module]) -> Section a -> Maybe Version
+    sectionCabalVersion :: (Section a -> [Module]) -> Section a -> Maybe CabalVersion
     sectionCabalVersion getMentionedModules sect = maximum $ [
         makeVersion [2,2] <$ guard (sectionSatisfies (not . null . sectionCxxSources) sect)
       , makeVersion [2,2] <$ guard (sectionSatisfies (not . null . sectionCxxOptions) sect)
@@ -853,17 +882,23 @@
           && pathsModule `elem` getMentionedModules sect)
       ] ++ map versionFromSystemBuildTool systemBuildTools
       where
+        defaultExtensions :: [String]
         defaultExtensions = sectionAll sectionDefaultExtensions sect
+
+        uses :: String -> Bool
         uses = (`elem` defaultExtensions)
 
+        pathsModule :: Module
         pathsModule = pathsModuleFromPackageName packageName
 
+        versionFromSystemBuildTool :: String -> Maybe CabalVersion
         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 :: [String]
         known_1_10 = [
             "ghc"
           , "ghc-pkg"
@@ -891,9 +926,13 @@
           , "lhc"
           , "lhc-pkg"
           ]
+
+        known_1_14 :: [String]
         known_1_14 = [
             "hpc"
           ]
+
+        known_1_22 :: [String]
         known_1_22 = [
             "ghcjs"
           , "ghcjs-pkg"
@@ -966,7 +1005,8 @@
       Nothing -> fail ("invalid value " ++ show s)
 
 data Package = Package {
-  packageName :: String
+  packageCabalVersion :: CabalVersion
+, packageName :: String
 , packageVersion :: String
 , packageSynopsis :: Maybe String
 , packageDescription :: Maybe String
@@ -1093,7 +1133,7 @@
 type CommonOptionsWithDefaults a = Product DefaultsConfig (CommonOptions ParseAsmSources ParseCSources ParseCxxSources ParseJsSources a)
 type WithCommonOptionsWithDefaults a = Product DefaultsConfig (WithCommonOptions ParseAsmSources ParseCSources ParseCxxSources ParseJsSources a)
 
-toPackage :: FormatYamlParseError -> FilePath -> FilePath -> ConfigWithDefaults -> ConfigM IO (Package, String)
+toPackage :: FormatYamlParseError -> FilePath -> FilePath -> ConfigWithDefaults -> ConfigM IO Package
 toPackage formatYamlParseError userDataDir dir =
       expandDefaultsInConfig formatYamlParseError userDataDir dir
   >=> setDefaultLanguage "Haskell2010"
@@ -1195,9 +1235,9 @@
 
 type GlobalOptions = CommonOptions AsmSources CSources CxxSources JsSources Empty
 
-toPackage_ :: (MonadIO m, Warnings m, State m) => FilePath -> Product GlobalOptions (PackageConfig AsmSources CSources CxxSources JsSources) -> m (Package, String)
+toPackage_ :: (MonadIO m, Warnings m, State m) => FilePath -> Product GlobalOptions (PackageConfig AsmSources CSources CxxSources JsSources) -> m Package
 toPackage_ dir (Product g PackageConfig{..}) = do
-  executableMap <- toExecutableMap packageName_ packageConfigExecutables packageConfigExecutable
+  executableMap <- toExecutableMap packageName packageConfigExecutables packageConfigExecutable
   let
     globalVerbatim = commonOptionsVerbatim g
     globalOptions = g {commonOptionsVerbatim = Nothing}
@@ -1205,13 +1245,13 @@
     executableNames = maybe [] Map.keys executableMap
 
     toSect :: (Warnings m, Monoid a) => WithCommonOptions AsmSources CSources CxxSources JsSources a -> m (Section a)
-    toSect = toSection packageName_ executableNames . first ((mempty <$ globalOptions) <>)
+    toSect = toSection packageName executableNames . first ((mempty <$ globalOptions) <>)
 
     toSections :: (Warnings m, Monoid a) => Maybe (Map String (WithCommonOptions AsmSources CSources CxxSources JsSources a)) -> m (Map String (Section a))
     toSections = maybe (return mempty) (traverse toSect)
 
-    toLib = toLibrary dir packageName_
-    toExecutables = toSections >=> traverse (toExecutable dir packageName_)
+    toLib = toLibrary dir packageName
+    toExecutables = toSections >=> traverse (toExecutable dir packageName)
 
   mLibrary <- traverse (toSect >=> toLib) packageConfigLibrary
   internalLibraries <- toSections packageConfigInternalLibraries >>= traverse toLib
@@ -1257,7 +1297,8 @@
       defaultBuildType = maybe Simple (const Custom) mCustomSetup
 
       pkg = Package {
-        packageName = packageName_
+        packageCabalVersion = CabalVersion defaultCabalVersion
+      , packageName
       , packageVersion = maybe "0.0.0" unPackageVersion packageConfigVersion
       , packageSynopsis = packageConfigSynopsis
       , packageDescription = packageConfigDescription
@@ -1290,12 +1331,11 @@
   tell nameWarnings
   tell (formatMissingSourceDirs missingSourceDirs)
 
-  let (pkg_, renderedCabalVersion, cabalVersion) = determineCabalVersion inferredLicense pkg
-  return (maybe pkg_ (addPathsModuleToGeneratedModules pkg_) cabalVersion, renderedCabalVersion)
+  return $ addPathsModuleToGeneratedModules $ ensureRequiredCabalVersion inferredLicense pkg
   where
     nameWarnings :: [String]
-    packageName_ :: String
-    (nameWarnings, packageName_) = case packageConfigName of
+    packageName :: String
+    (nameWarnings, packageName) = case packageConfigName of
       Nothing -> let inferredName = takeBaseName dir in
         (["Package name not specified, inferred " ++ show inferredName], inferredName)
       Just n -> ([], n)
@@ -1418,7 +1458,7 @@
   let
     pathsModule :: [Module]
     pathsModule = case specVersion of
-      SpecVersion v | v >= makeVersion [0,36,0] -> []
+      SpecVersion v | v >= Version.makeVersion [0,36,0] -> []
       _ -> [pathsModuleFromPackageName packageName_]
 
   removeConditionalsThatAreAlwaysFalse <$> traverseSectionAndConditionals
diff --git a/src/Hpack/Render.hs b/src/Hpack/Render.hs
--- a/src/Hpack/Render.hs
+++ b/src/Hpack/Render.hs
@@ -23,6 +23,7 @@
 , Alignment(..)
 , CommaStyle(..)
 #ifdef TEST
+, RenderEnv(..)
 , renderConditional
 , renderDependencies
 , renderLibraryFields
@@ -40,6 +41,7 @@
 import           Data.Maybe
 import           Data.Map.Lazy (Map)
 import qualified Data.Map.Lazy as Map
+import           Control.Monad.Reader
 
 import           Hpack.Util
 import           Hpack.Config
@@ -47,6 +49,19 @@
 import           Hpack.Render.Dsl hiding (sortFieldsBy)
 import qualified Hpack.Render.Dsl as Dsl
 
+data RenderEnv = RenderEnv {
+  renderEnvCabalVersion :: CabalVersion
+, renderEnvPackageName :: String
+}
+
+type RenderM = Reader RenderEnv
+
+getCabalVersion :: RenderM CabalVersion
+getCabalVersion = asks renderEnvCabalVersion
+
+getPackageName :: RenderM String
+getPackageName = asks renderEnvPackageName
+
 renderPackage :: [String] -> Package -> String
 renderPackage oldCabalFile = renderPackageWith settings headerFieldsAlignment formattingHintsFieldOrder formattingHintsSectionsFieldOrder
   where
@@ -78,20 +93,23 @@
     customSetup :: [Element]
     customSetup = maybe [] (return . renderCustomSetup) packageCustomSetup
 
-    library :: [Element]
-    library = maybe [] (return . renderLibrary) packageLibrary
-
     stanzas :: [Element]
-    stanzas = concat [
-        sourceRepository
-      , customSetup
-      , map renderFlag packageFlags
-      , library
-      , renderInternalLibraries packageInternalLibraries
-      , renderExecutables packageExecutables
-      , renderTests packageTests
-      , renderBenchmarks packageBenchmarks
-      ]
+    stanzas = flip runReader (RenderEnv packageCabalVersion packageName) $ do
+      library <- maybe (return []) (fmap return . renderLibrary) packageLibrary
+      internalLibraries <- renderInternalLibraries packageInternalLibraries
+      executables <- renderExecutables packageExecutables
+      tests <- renderTests packageTests
+      benchmarks <- renderBenchmarks packageBenchmarks
+      return $ concat [
+          sourceRepository
+        , customSetup
+        , map renderFlag packageFlags
+        , library
+        , internalLibraries
+        , executables
+        , tests
+        , benchmarks
+        ]
 
     headerFields :: [Element]
     headerFields = mapMaybe (\(name, value) -> Field name . Literal <$> value) $ [
@@ -155,37 +173,37 @@
   where
     description = maybe [] (return . Field "description" . Literal) flagDescription
 
-renderInternalLibraries :: Map String (Section Library) -> [Element]
-renderInternalLibraries = map renderInternalLibrary . Map.toList
+renderInternalLibraries :: Map String (Section Library) -> RenderM [Element]
+renderInternalLibraries = traverse renderInternalLibrary . Map.toList
 
-renderInternalLibrary :: (String, Section Library) -> Element
-renderInternalLibrary (name, sect) =
-  Stanza ("library " ++ name) (renderLibrarySection sect)
+renderInternalLibrary :: (String, Section Library) -> RenderM Element
+renderInternalLibrary (name, sect) = do
+  Stanza ("library " ++ name) <$> renderLibrarySection sect
 
-renderExecutables :: Map String (Section Executable) -> [Element]
-renderExecutables = map renderExecutable . Map.toList
+renderExecutables :: Map String (Section Executable) -> RenderM [Element]
+renderExecutables = traverse renderExecutable . Map.toList
 
-renderExecutable :: (String, Section Executable) -> Element
-renderExecutable (name, sect) =
-  Stanza ("executable " ++ name) (renderExecutableSection [] sect)
+renderExecutable :: (String, Section Executable) -> RenderM Element
+renderExecutable (name, sect) = do
+  Stanza ("executable " ++ name) <$> renderExecutableSection [] sect
 
-renderTests :: Map String (Section Executable) -> [Element]
-renderTests = map renderTest . Map.toList
+renderTests :: Map String (Section Executable) -> RenderM [Element]
+renderTests = traverse renderTest . Map.toList
 
-renderTest :: (String, Section Executable) -> Element
-renderTest (name, sect) =
-  Stanza ("test-suite " ++ name)
-    (renderExecutableSection [Field "type" "exitcode-stdio-1.0"] sect)
+renderTest :: (String, Section Executable) -> RenderM Element
+renderTest (name, sect) = do
+  Stanza ("test-suite " ++ name) <$>
+    renderExecutableSection [Field "type" "exitcode-stdio-1.0"] sect
 
-renderBenchmarks :: Map String (Section Executable) -> [Element]
-renderBenchmarks = map renderBenchmark . Map.toList
+renderBenchmarks :: Map String (Section Executable) -> RenderM [Element]
+renderBenchmarks = traverse renderBenchmark . Map.toList
 
-renderBenchmark :: (String, Section Executable) -> Element
-renderBenchmark (name, sect) =
-  Stanza ("benchmark " ++ name)
-    (renderExecutableSection [Field "type" "exitcode-stdio-1.0"] sect)
+renderBenchmark :: (String, Section Executable) -> RenderM Element
+renderBenchmark (name, sect) = do
+  Stanza ("benchmark " ++ name) <$>
+    renderExecutableSection [Field "type" "exitcode-stdio-1.0"] sect
 
-renderExecutableSection :: [Element] -> Section Executable -> [Element]
+renderExecutableSection :: [Element] -> Section Executable -> RenderM [Element]
 renderExecutableSection extraFields = renderSection renderExecutableFields extraFields
 
 renderExecutableFields :: Executable -> [Element]
@@ -199,10 +217,10 @@
 renderCustomSetup CustomSetup{..} =
   Stanza "custom-setup" $ renderDependencies "setup-depends" customSetupDependencies
 
-renderLibrary :: Section Library -> Element
-renderLibrary sect = Stanza "library" $ renderLibrarySection sect
+renderLibrary :: Section Library -> RenderM Element
+renderLibrary sect = Stanza "library" <$> renderLibrarySection sect
 
-renderLibrarySection :: Section Library -> [Element]
+renderLibrarySection :: Section Library -> RenderM [Element]
 renderLibrarySection = renderSection renderLibraryFields []
 
 renderLibraryFields :: Library -> [Element]
@@ -222,39 +240,43 @@
 renderVisibility :: String -> Element
 renderVisibility = Field "visibility" . Literal
 
-renderSection :: (a -> [Element]) -> [Element] -> Section a -> [Element]
-renderSection renderSectionData extraFieldsStart Section{..} = addVerbatim sectionVerbatim $
-     extraFieldsStart
-  ++ renderSectionData sectionData ++ [
-    renderDirectories "hs-source-dirs" sectionSourceDirs
-  , renderDefaultExtensions sectionDefaultExtensions
-  , renderOtherExtensions sectionOtherExtensions
-  , renderGhcOptions sectionGhcOptions
-  , renderGhcProfOptions sectionGhcProfOptions
-  , renderGhcSharedOptions sectionGhcSharedOptions
-  , renderGhcjsOptions sectionGhcjsOptions
-  , renderCppOptions sectionCppOptions
-  , renderAsmOptions sectionAsmOptions
-  , renderCcOptions sectionCcOptions
-  , renderCxxOptions sectionCxxOptions
-  , renderDirectories "include-dirs" sectionIncludeDirs
-  , Field "install-includes" (LineSeparatedList sectionInstallIncludes)
-  , Field "asm-sources" (renderPaths sectionAsmSources)
-  , Field "c-sources" (renderPaths sectionCSources)
-  , Field "cxx-sources" (renderPaths sectionCxxSources)
-  , Field "js-sources" (renderPaths sectionJsSources)
-  , renderDirectories "extra-lib-dirs" sectionExtraLibDirs
-  , Field "extra-libraries" (LineSeparatedList sectionExtraLibraries)
-  , renderDirectories "extra-frameworks-dirs" sectionExtraFrameworksDirs
-  , Field "frameworks" (LineSeparatedList sectionFrameworks)
-  , renderLdOptions sectionLdOptions
-  , Field "pkgconfig-depends" (CommaSeparatedList sectionPkgConfigDependencies)
-  ]
-  ++ renderBuildTools sectionBuildTools sectionSystemBuildTools
-  ++ renderDependencies "build-depends" sectionDependencies
-  ++ maybe [] (return . renderBuildable) sectionBuildable
-  ++ maybe [] (return . renderLanguage) sectionLanguage
-  ++ map (renderConditional renderSectionData) sectionConditionals
+renderSection :: (a -> [Element]) -> [Element] -> Section a -> RenderM [Element]
+renderSection renderSectionData extraFieldsStart Section{..} = do
+  buildTools <- renderBuildTools sectionBuildTools sectionSystemBuildTools
+  conditionals <- traverse (renderConditional renderSectionData) sectionConditionals
+  return . addVerbatim sectionVerbatim $
+       extraFieldsStart
+    ++ renderSectionData sectionData
+    ++ [
+      renderDirectories "hs-source-dirs" sectionSourceDirs
+    , renderDefaultExtensions sectionDefaultExtensions
+    , renderOtherExtensions sectionOtherExtensions
+    , renderGhcOptions sectionGhcOptions
+    , renderGhcProfOptions sectionGhcProfOptions
+    , renderGhcSharedOptions sectionGhcSharedOptions
+    , renderGhcjsOptions sectionGhcjsOptions
+    , renderCppOptions sectionCppOptions
+    , renderAsmOptions sectionAsmOptions
+    , renderCcOptions sectionCcOptions
+    , renderCxxOptions sectionCxxOptions
+    , renderDirectories "include-dirs" sectionIncludeDirs
+    , Field "install-includes" (LineSeparatedList sectionInstallIncludes)
+    , Field "asm-sources" (renderPaths sectionAsmSources)
+    , Field "c-sources" (renderPaths sectionCSources)
+    , Field "cxx-sources" (renderPaths sectionCxxSources)
+    , Field "js-sources" (renderPaths sectionJsSources)
+    , renderDirectories "extra-lib-dirs" sectionExtraLibDirs
+    , Field "extra-libraries" (LineSeparatedList sectionExtraLibraries)
+    , renderDirectories "extra-frameworks-dirs" sectionExtraFrameworksDirs
+    , Field "frameworks" (LineSeparatedList sectionFrameworks)
+    , renderLdOptions sectionLdOptions
+    , Field "pkgconfig-depends" (CommaSeparatedList sectionPkgConfigDependencies)
+    ]
+    ++ buildTools
+    ++ renderDependencies "build-depends" sectionDependencies
+    ++ maybe [] (return . renderBuildable) sectionBuildable
+    ++ maybe [] (return . renderLanguage) sectionLanguage
+    ++ conditionals
 
 addVerbatim :: [Verbatim] -> [Element] -> [Element]
 addVerbatim verbatim fields = filterVerbatim verbatim fields ++ renderVerbatim verbatim
@@ -285,12 +307,12 @@
       [x] -> Field key (Literal x)
       xs -> Field key (LineSeparatedList xs)
 
-renderConditional :: (a -> [Element]) -> Conditional (Section a) -> Element
+renderConditional :: (a -> [Element]) -> Conditional (Section a) -> RenderM Element
 renderConditional renderSectionData (Conditional condition sect mElse) = case mElse of
   Nothing -> if_
-  Just else_ -> Group if_ (Stanza "else" $ renderSection renderSectionData [] else_)
+  Just else_ -> Group <$> if_ <*> (Stanza "else" <$> renderSection renderSectionData [] else_)
   where
-    if_ = Stanza ("if " ++ renderCond condition) (renderSection renderSectionData [] sect)
+    if_ = Stanza ("if " ++ renderCond condition) <$> renderSection renderSectionData [] sect
 
 renderCond :: Cond -> String
 renderCond = \ case
@@ -343,21 +365,32 @@
   AnyVersion -> ""
   VersionRange x -> " " ++ x
 
-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])
-  ]
+renderBuildTools :: Map BuildTool DependencyVersion -> SystemBuildTools -> RenderM [Element]
+renderBuildTools buildTools systemBuildTools = do
+  xs <- traverse renderBuildTool $ Map.toList buildTools
+  return [
+      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)
+renderBuildTool :: (BuildTool,  DependencyVersion) -> RenderM RenderBuildTool
+renderBuildTool (buildTool, renderVersion -> version) = do
+  cabalVersion <- getCabalVersion
+  packageName <- getPackageName
+  let supportsBuildTools = cabalVersion < makeCabalVersion [2]
+  return $ case buildTool of
+    LocalBuildTool executable
+      | supportsBuildTools -> BuildTools (executable ++ version)
+      | otherwise -> BuildToolDepends (packageName ++ ":" ++ executable ++ version)
+    BuildTool pkg executable
+      | supportsBuildTools && isknownBuildTool pkg executable -> BuildTools (executable ++ version)
+      | otherwise -> BuildToolDepends (pkg ++ ":" ++ executable ++ version)
   where
+    isknownBuildTool :: String -> String -> Bool
+    isknownBuildTool pkg executable = pkg == executable && executable `elem` knownBuildTools
+
     knownBuildTools :: [String]
     knownBuildTools = [
         "alex"
diff --git a/test/EndToEndSpec.hs b/test/EndToEndSpec.hs
--- a/test/EndToEndSpec.hs
+++ b/test/EndToEndSpec.hs
@@ -19,7 +19,7 @@
 import           Data.Version (showVersion)
 
 import qualified Hpack.Render as Hpack
-import           Hpack.Config (packageConfig, readPackageConfig, DecodeOptions(..), DecodeResult(..), defaultDecodeOptions)
+import           Hpack.Config (packageConfig, readPackageConfig, DecodeOptions(..), defaultDecodeOptions, DecodeResult(..))
 import           Hpack.Render.Hints (FormattingHints(..), sniffFormattingHints)
 
 import qualified Paths_hpack as Hpack (version)
@@ -639,43 +639,63 @@
         |] `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_ "my-package" [i|
-        build-tools:
-            alex ==0.1.0
-        |]
+      context "with known build tools" $ do
+        context "when cabal-version < 2" $ do
+          it "adds them to build-tools" $ do
+            [i|
+            executable:
+              build-tools:
+                alex == 0.1.0
+            |] `shouldRenderTo` executable_ "my-package" [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_ "my-package" [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 cabal-version >= 2" $ do
+          it "adds them to build-tool-depends" $ do
+            [i|
+            verbatim:
+              cabal-version: 2.0
+            executable:
+              build-tools:
+                alex == 0.1.0
+            |] `shouldRenderTo` (executable_ "my-package" [i|
+            autogen-modules:
+                Paths_my_package
+            build-tool-depends:
+                alex:alex ==0.1.0
+            |]) {
+              packageCabalVersion = "2.0"
+            }
 
-      it "accepts build-tool-depends as an alias" $ do
-        [i|
-        executable:
+      context "with other build tools" $ do
+        it "adds them to build-tool-depends" $ do
+          [i|
+          executable:
+            build-tools:
+              hspec-discover: 0.1.0
+          |] `shouldRenderTo` (executable_ "my-package" [i|
           build-tool-depends:
-            hspec-discover: 0.1.0
-        |] `shouldRenderTo` (executable_ "my-package" [i|
-        build-tool-depends:
-            hspec-discover:hspec-discover ==0.1.0
-        |]) {
-          packageCabalVersion = "1.12"
-        , packageWarnings = ["package.yaml: $.executable.build-tool-depends is deprecated, use $.executable.build-tools instead"]
-        }
+              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"
+          }
 
+        it "accepts build-tool-depends as an alias" $ do
+          [i|
+          executable:
+            build-tool-depends:
+              hspec-discover: 0.1.0
+          |] `shouldRenderTo` (executable_ "my-package" [i|
+          build-tool-depends:
+              hspec-discover:hspec-discover ==0.1.0
+          |]) {
+            packageCabalVersion = "1.12"
+          , packageWarnings = ["package.yaml: $.executable.build-tool-depends is deprecated, use $.executable.build-tools instead"]
+          }
+
       context "when the name of a build tool matches an executable from the same package" $ do
         it "adds it to build-tools" $ do
           [i|
@@ -714,6 +734,22 @@
               bar ==0.2.0
           |]
 
+        context "when cabal-version >= 2" $ do
+          it "adds it to build-tool-depends" $ do
+            [i|
+            verbatim:
+              cabal-version: 2.0
+            executables:
+              bar:
+                build-tools:
+                  - bar
+            |] `shouldRenderTo` (executable_ "bar" [i|
+            autogen-modules:
+                Paths_my_package
+            build-tool-depends:
+                my-package:bar
+            |]) {packageCabalVersion = "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|
@@ -1888,8 +1924,25 @@
         it "overrides header fields" $ do
           [i|
           verbatim:
-            cabal-version: foo
-          |] `shouldRenderTo` (package "") {packageCabalVersion = "foo"}
+            build-type: foo
+          |] `shouldRenderTo` (package "") {packageBuildType = "foo"}
+
+        context "with cabal-version" $ do
+          context "with a string value" $ do
+            it "takes precedence over inferred version" $ do
+              [i|
+              license: BSD-3-Clause
+              verbatim:
+                cabal-version: foo
+              |] `shouldRenderTo` (package "license: BSD-3-Clause") {packageCabalVersion = "foo"}
+
+          context "with a version" $ do
+            it "takes precedence over inferred version" $ do
+              [i|
+              license: BSD-3-Clause
+              verbatim:
+                cabal-version: 0.8
+              |] `shouldRenderTo` (package "license: BSD-3-Clause") {packageCabalVersion = "0.8"}
 
         it "overrides other fields" $ do
           touch "foo"
diff --git a/test/Hpack/RenderSpec.hs b/test/Hpack/RenderSpec.hs
--- a/test/Hpack/RenderSpec.hs
+++ b/test/Hpack/RenderSpec.hs
@@ -4,6 +4,8 @@
 
 import           Helper
 
+import           Control.Monad.Reader (runReader)
+
 import           Hpack.Syntax.DependencyVersion
 import           Hpack.ConfigSpec hiding (spec)
 import           Hpack.Config hiding (package)
@@ -21,6 +23,9 @@
 renderEmptySection :: Empty -> [Element]
 renderEmptySection Empty = []
 
+cabalVersion :: CabalVersion
+cabalVersion = makeCabalVersion [1,12]
+
 spec :: Spec
 spec = do
   describe "renderPackageWith" $ do
@@ -220,9 +225,11 @@
           ]
 
   describe "renderConditional" $ do
+    let run = flip runReader (RenderEnv cabalVersion "foo")
+
     it "renders conditionals" $ do
       let conditional = Conditional "os(windows)" (section Empty) {sectionDependencies = deps ["Win32"]} Nothing
-      render defaultRenderSettings 0 (renderConditional renderEmptySection conditional) `shouldBe` [
+      render defaultRenderSettings 0 (run $ renderConditional renderEmptySection conditional) `shouldBe` [
           "if os(windows)"
         , "  build-depends:"
         , "      Win32"
@@ -230,7 +237,7 @@
 
     it "renders conditionals with else-branch" $ do
       let conditional = Conditional "os(windows)" (section Empty) {sectionDependencies = deps ["Win32"]} (Just $ (section Empty) {sectionDependencies = deps ["unix"]})
-      render defaultRenderSettings 0 (renderConditional renderEmptySection conditional) `shouldBe` [
+      render defaultRenderSettings 0 (run $ renderConditional renderEmptySection conditional) `shouldBe` [
           "if os(windows)"
         , "  build-depends:"
         , "      Win32"
@@ -242,7 +249,7 @@
     it "renders nested conditionals" $ do
       let conditional = Conditional "arch(i386)" (section Empty) {sectionGhcOptions = ["-threaded"], sectionConditionals = [innerConditional]} Nothing
           innerConditional = Conditional "os(windows)" (section Empty) {sectionDependencies = deps ["Win32"]} Nothing
-      render defaultRenderSettings 0 (renderConditional renderEmptySection conditional) `shouldBe` [
+      render defaultRenderSettings 0 (run $ renderConditional renderEmptySection conditional) `shouldBe` [
           "if arch(i386)"
         , "  ghc-options: -threaded"
         , "  if os(windows)"
@@ -253,7 +260,7 @@
     it "conditionalises both build-depends and mixins" $ do
       let conditional = Conditional "os(windows)" (section Empty) {sectionDependencies = [("Win32", depInfo)]} Nothing
           depInfo = defaultInfo { dependencyInfoMixins = ["hiding (Blah)"] }
-      render defaultRenderSettings 0 (renderConditional renderEmptySection conditional) `shouldBe` [
+      render defaultRenderSettings 0 (run $ renderConditional renderEmptySection conditional) `shouldBe` [
           "if os(windows)"
         , "  build-depends:"
         , "      Win32"
