diff --git a/ChangeLog b/ChangeLog
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+# 2.2.0 (2024-03-27)
+- Dependencies excludedPkgs: add system-cxx-std-lib
+- Dependencies: withGhcProfLibType to respect %with_ghc_prof for builddeps
+- PackageUtils dependencySortCabals: do nothing if empty
+- Snapshots: update stackage snapshots.json url
+- Spec: usesOptparse, maybeGenerateBashCompletion, maybeGenerateManpage
+- spec: add %{?haskell_setup}
+- spec: define and use %ghc_major for --with-compiler
+- spec: do not BR ghcX.Y-prof for standalone
+
 # 2.1.5 (2023-11-10)
 - bugfix: --flags should be optional!
 - PackageUtils pkgSpecPkgData: error displaying an unparsable pkgid
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -71,15 +71,15 @@
 ## Help output
 `$ cabal-rpm --version`
 ```
-2.1.5
+2.2.0
 ```
 `$ cabal-rpm --help`
 ```
-Cabal-rpm tool
+Tool to create RPM packages from Haskell Hackage/Stackage
 
 Usage: cabal-rpm [--version] COMMAND
 
-  RPM package tool for Haskell Stackage/Hackage packages
+  See https://github.com/juhp/cabal-rpm#readme
 
 Available options:
   -h,--help                Show this help text
diff --git a/TODO b/TODO
--- a/TODO
+++ b/TODO
@@ -1,3 +1,7 @@
+- write the complete spec file atomically
+  - with Writer monad to avoid writing empty spec
+- respect bounds when querying subpackages
+
 - 'install' in empty package dir fails (have to local or spec first)
 - 'install' in a source dir: "package not found for directory"
   - maybe just run "cabal install" for executable?
@@ -79,11 +83,10 @@
 - revisions of subpackages
 - use Set for deps?
 
-write the complete spec file atomically
-
 ## Misc
 ? run "cabal update" if package-version not found
-- use <> instead of ++
+- add individual command help pages
+- use shelly for easy tracing?
 
 # Commands
 - fetch
@@ -98,6 +101,7 @@
 - --releasever option (for repoquery)
 - --no-revision
 - add demo/ & example[s]/ to doc subpackage (gtk2hs)
+- replace --flag with --enable/disable?
 
 ## version ranges
 - warn about missing dependencies/versions
diff --git a/cabal-rpm.cabal b/cabal-rpm.cabal
--- a/cabal-rpm.cabal
+++ b/cabal-rpm.cabal
@@ -1,18 +1,21 @@
 Cabal-version:       1.18
 Name:                cabal-rpm
-Version:             2.1.5
+Version:             2.2.0
 Synopsis:            RPM packaging tool for Haskell Cabal-based packages
 Description:
-    This package provides a RPM packaging tool for Haskell Cabal-based packages.
+    This package provides a RPM packaging tool for Haskell Cabal-based
+    packages.
     .
     cabal-rpm has commands to generate a RPM spec file and srpm for a package.
-    It can rpmbuild packages, yum/dnf install their dependencies, prep packages,
-    and install them. There are commands to list package dependencies and
-    missing dependencies. The diff command compares the current spec file with
-    a freshly generated one, the update command updates the spec file to latest
-    version from Stackage or Hackage, and the refresh command updates the spec
-    file to the current cabal-rpm packaging. It also handles Hackage revisions
-    of packages. Standalone packages can also be made, built with cabal-install.
+    It can rpmbuild packages, dnf/yum install their dependencies,
+    prep packages, and install them.
+    There are commands to list package dependencies and missing dependencies.
+    The diff command compares the current spec file with a freshly generated
+    one,
+    the update command updates the spec file to latest version from Stackage
+    or Hackage, and the refresh command updates the spec file to the current
+    cabal-rpm packaging. It also handles Hackage revisions of packages.
+    Standalone packages can also be made, built with cabal-install.
 Homepage:            https://github.com/juhp/cabal-rpm
 Bug-reports:         https://github.com/juhp/cabal-rpm/issues
 License:             GPL-3
@@ -21,15 +24,14 @@
                      Bryan O'Sullivan <bos@serpentine.com>
 Maintainer:          Jens Petersen <petersen@fedoraproject.org>
 Copyright:           2007-2008 Bryan O'Sullivan <bos@serpentine.com>,
-                     2012-2023 Jens Petersen <juhpetersen@gmail.com>
+                     2012-2024 Jens Petersen <juhpetersen@gmail.com>
 Category:            Distribution
 Build-type:          Simple
 Extra-doc-files:     ChangeLog README.md TODO
-Extra-source-files:  man/cabal-rpm.1.md
 Tested-with:         GHC == 8.0.2, GHC == 8.2.2, GHC == 8.4.4,
                      GHC == 8.6.5, GHC == 8.8.4, GHC == 8.10.7,
-                     GHC == 9.0.2, GHC == 9.2.8, GHC == 9.4.7,
-                     GHC == 9.6.3
+                     GHC == 9.0.2, GHC == 9.2.8, GHC == 9.4.8,
+                     GHC == 9.6.4, GHC == 9.8.2
 
 source-repository head
   type:     git
diff --git a/man/cabal-rpm.1.md b/man/cabal-rpm.1.md
deleted file mode 100644
--- a/man/cabal-rpm.1.md
+++ /dev/null
@@ -1,94 +0,0 @@
-% CABAL-RPM(1)
-% This manpage was written by Jens Petersen
-% 2013-01-21
-
-# NAME
-cabal-rpm - a RPM packaging tool for Haskell Cabal packages
-
-# SYNOPSIS
-cabal-rpm [*options*] spec [*path-or-pkg*]\
-cabal-rpm [*options*] local [*path-or-pkg*]\
-cabal-rpm [*options*] srpm [*path-or-pkg*]\
-cabal-rpm [*options*] prep [*path-or-pkg*]\
-cabal-rpm [*options*] builddep [*path-or-pkg*]\
-cabal-rpm [*options*] install [*path-or-pkg*]\
-cabal-rpm [*options*] depends [*path-or-pkg*]\
-cabal-rpm [*options*] requires [*path-or-pkg*]\
-cabal-rpm [*options*] missingdeps [*path-or-pkg*]\
-cabal-rpm [*options*] diff [*path-or-pkg*]\
-cabal-rpm [*options*] update [*path-or-pkg*\]\
-cabal-rpm [*options*] refresh [*path-or-pkg*]
-
-# DESCRIPTION
-cabal-rpm generates RPM packages and .spec files from Haskell Cabal package.
-
-If no *path-or-pkg* is specified, cabal-rpm looks for a .spec or .cabal file
-in the current directory.  Otherwise, it will look for *path-or-pkg*. If
-the argument is a directory then it will look there for a .spec or .cabal file.
-If the argument is a path to a .cabal file then it will use it.
-Otherwise if there is no '/' in the argument and it does not exist
-then cabal-rpm will try to unpack the package and use its .cabal file.
-cabal-rpm uses a temporary directory for unpackaging tarballs or packages.
-cabal-rpm then parses the .cabal file and uses it to generate a .spec file
-that can be built.
-
-If a <PKG>.spec already exists, cabal-rpm outputs to <PKG>.spec.cblrpm instead.
-
-# OPTIONS
--h, --help
-:   Show the help text.
-
--b, --binary
-:   Force the base package name to be the Hackage package name.
-
--f *FLAGS*, --flags=*FLAGS*
-: Override one or more Cabal build configuration flags.
-
---force
-: overwrite existing spec file
-
---missing
-: comment out unavailable BuildRequires deps
-
---release=*RELEASE*
-: Override the release number in the .spec file.
-
---standalone
-: create a standalone package, built using cabal-install
-
--s *STREAM* , --stream=*STREAM*
-: Stackage stream (eg lts-13) or 'hackage' used to get package version.
-
--v *N*, --verbose=*N*
-: Set verbosity to *N*.
-
---version=*VERSION*
-: Override the version number in the .spec file.
-
-# EXAMPLES
-Below CMD can be one of:
-    spec, srpm, prep, local, install, diff, builddep, depends, requires,
-    missingdeps, update, refresh
-
-Do CMD for the package in current directory:
-
-    cabal-rpm CMD
-
-Do CMD for package (directory or package name):
- 
-    cabal-rpm CMD [package]
-
-Do CMD for package-version (directory or package name):
-
-    cabal-rpm CMD [package-version]
-
-Do CMD on a .cabal file:
-
-    cabal-rpm CMD path/to/some.cabal
-
-# HISTORY
-Cabal-rpm was originally written by Bryan O'Sullivan in 2007-2008
-and resurrected by Jens Petersen in 2012 to replace cabal2spec.
-
-# SEE ALSO
-<http://github.com/juhp/cabal-rpm/>
diff --git a/src/Commands/Spec.hs b/src/Commands/Spec.hs
--- a/src/Commands/Spec.hs
+++ b/src/Commands/Spec.hs
@@ -26,7 +26,8 @@
                      prettyShow,
                      recurseMissing, subPackages, testsuiteDependencies')
 import Header (headerOption, withSpecHead)
-import PackageUtils (dependencySortCabals, PackageData (..), prepare)
+import PackageUtils (dependencySortCabals, PackageData (..), prepare,
+                     packageMacro)
 import SimpleCabal (buildable, mkPackageName, PackageDescription (..),
                     PackageIdentifier(..))
 import SimpleCmd ((+-+), cmd, cmdLines, cmdMaybe, grep, grep_, removePrefix)
@@ -204,8 +205,9 @@
   put "# https://docs.fedoraproject.org/en-US/packaging-guidelines/Haskell/"
   putNewline
 
-  when (isJust mwithghc) $ do
-    global "ghc_name" ghcname
+  whenJust mwithghc $ \withghc -> do
+    global "ghc_major" $ majorVer withghc
+    global "ghc_name" "ghc%{ghc_major}"
     putNewline
 
   -- Some packages conflate the synopsis and description fields.  Ugh.
@@ -261,6 +263,7 @@
   when hasLib $ do
     global "pkg_name" name
     global "pkgver" "%{pkg_name}-%{version}"
+    put "%{?haskell_setup}"
     putNewline
 
   let pkgver = if hasLib then "%{pkgver}" else pkg_name ++ "-%{version}"
@@ -276,7 +279,7 @@
   subpkgsOrdered <- dependencySortCabals mspec $ map fst missingLibsLicenses
   --print subpkgs
   let subpkgs = sort subpkgsOrdered
-      mkSubpkgMacro = filter (/= '-') . display . pkgName
+      mkSubpkgMacro = packageMacro . display . pkgName
       subpkgMacro pid = "%{" ++ mkSubpkgMacro pid ++ "}"
       subpkgMacros = map subpkgMacro subpkgs
       subpkgMacrosOrdered = map subpkgMacro subpkgsOrdered
@@ -310,9 +313,11 @@
     let local = display pkgid <.> "cabal"
     have <- doesFileExist local
     if have then grep_ "x-revision" local else return False
-  revisedDOS <- if revised then do
-    filetype <- cmd "file" ["-b", display pkgid <.> "cabal"]
-    return $ "CRLF" `isInfixOf` filetype
+  revisedDOS <-
+    if revised
+    then do
+      filetype <- cmd "file" ["-b", display pkgid <.> "cabal"]
+      return $ "CRLF" `isInfixOf` filetype
     else return False
 
   putHdr "Name" (if binlib then "%{pkg_name}" else basename)
@@ -350,7 +355,8 @@
     when (isJust mwithghc) $ do
       put "%if %{defined ghc_name}"
       putHdr "BuildRequires" $ "%{ghc_name}" ++ "-devel"
-      putHdr "BuildRequires" $ "%{ghc_name}" ++ "-prof"
+      unless standalone $
+        putHdr "BuildRequires" $ "%{ghc_name}" ++ "-prof"
       put "%else"
 
     let metaPackages = [mkPackageName "haskell-gi-overloading"]
@@ -373,7 +379,9 @@
 
   when (isJust mwithghc) $
     put "%endif"
-
+  let usesOptparse = hasExecPkg && any ((`elem` buildDeps pkgdeps) . mkPackageName) ["optparse-applicative","optparse-simple","simple-cmd-args"]
+  when usesOptparse $
+    putHdr "BuildRequires" "help2man"
   let common = binlib && datafiles /= [] && not standalone
       versionRelease = " = %{version}-%{release}"
   when common $
@@ -525,7 +533,7 @@
     put "mkdir -p %{buildroot}%{_bindir}"
     put "%if 0%{?fedora} || 0%{?rhel} >= 9"
     put "%ghc_set_gcc_flags"
-    put $ "%cabal_install install" +-+ maybe "" (\v -> "-w ghc-" ++ V.showVersion v) mwithghc +-+ "--install-method=copy --enable-executable-stripping --installdir=%{buildroot}%{_bindir}"
+    put $ "%cabal_install install" +-+ (if isJust mwithghc then "-w ghc-%{ghc_major}" else "") +-+ "--install-method=copy --enable-executable-stripping --installdir=%{buildroot}%{_bindir}"
     put "%else"
     put "for i in .cabal-sandbox/bin/*; do"
     put "strip -s -o %{buildroot}%{_bindir}/$(basename $i) $i"
@@ -553,11 +561,32 @@
   let execs = sort $ map exeName $ filter isBuildable $ executables pkgDesc
       execNaming p = let pn = unUnqualComponentName p in
                      if pn == name then "%{name}" else pn
-      bashCompletions = hasExecPkg && any ((`elem` buildDeps pkgdeps) . mkPackageName) ["optparse-applicative","optparse-simple","simple-cmd-args"]
+      -- FIXME check for included file
+      maybeGenerateBashCompletion exn = do
+        put $ "outfile=%{buildroot}%{_datadir}/bash-completion/completions" </> exn
+        put "if [ ! -e $outfile ]; then"
+        put $ "  " ++ "%{buildroot}%{_bindir}" </> exn +-+ "--bash-completion-script" +-+ exn +-+ "| sed s/filenames/default/ > $outfile"
+        put "else"
+        put "  echo \"$outfile exists!\"; exit 1"
+        put "fi"
+      -- FIXME check for included file
+      maybeGenerateManpage exn = do
+        put $ "outfile=%{buildroot}%{_mandir}/man1" </> exn <.> "1"
+        put "if [ ! -e $outfile ]; then"
+        put $ "  help2man --no-info %{buildroot}%{_bindir}" </> exn +-+ "> $outfile"
+        put "else"
+        put "  echo \"$outfile exists!\"; exit 1"
+        put "fi"
 
-  when bashCompletions $ do
+  when usesOptparse $ do
+    putNewline
     put "mkdir -p %{buildroot}%{_datadir}/bash-completion/completions/"
-    mapM_ (\ex -> let exn = execNaming ex in put ("%{buildroot}%{_bindir}" </> exn ++ " --bash-completion-script " ++ exn ++ " | sed s/filenames/default/ > %{buildroot}%{_datadir}/bash-completion/completions" </> exn)) execs
+    -- FIXME convert to for loop
+    mapM_ (maybeGenerateBashCompletion . execNaming) execs
+    putNewline
+    put "mkdir -p %{buildroot}%{_mandir}/man1/"
+    -- FIXME convert to for loop
+    mapM_ (maybeGenerateManpage . execNaming) execs
 
   put "# End cabal-rpm install"
   sectionNewline
@@ -583,8 +612,9 @@
     mapM_ (put . ("%{_bindir}" </>) . execNaming) execs
     unless (common || null datafiles) $
       put $ "%{_datadir}" </> pkgver
-    when bashCompletions $
+    when usesOptparse $ do
       mapM_ (put . ("%{_datadir}/bash-completion/completions" </>) . execNaming) execs
+      mapM_ (put . (\e -> "%{_mandir}/man1" </> e <.> "1*") . execNaming) execs
     put "# End cabal-rpm files"
     sectionNewline
 
diff --git a/src/Dependencies.hs b/src/Dependencies.hs
--- a/src/Dependencies.hs
+++ b/src/Dependencies.hs
@@ -38,6 +38,7 @@
   ) where
 
 import PackageUtils (PackageData(..), prepare, repoquery, rpmInstall)
+import SysCmd (rpmEval)
 import Types
 
 import SimpleCabal (allLibraries, buildDependencies, mkPackageName,
@@ -62,7 +63,7 @@
 import Control.Monad (filterM, when, unless)
 
 import Data.List (delete, isSuffixOf, nub, (\\))
-import Data.Maybe (catMaybes, fromJust, isNothing, mapMaybe)
+import Data.Maybe (catMaybes, fromJust, isJust, isNothing, mapMaybe)
 
 #if !MIN_VERSION_Cabal(2,2,0)
 import Distribution.License (License (..))
@@ -82,7 +83,7 @@
 
 excludedPkgs :: PackageName -> Bool
 excludedPkgs =
-  flip notElem $ map mkPackageName ["ghc-prim", "integer-gmp"]
+  flip notElem $ map mkPackageName ["ghc-prim", "integer-gmp", "system-cxx-std-lib"]
 
 pkgSuffix :: LibPkgType -> String
 pkgSuffix lpt =
@@ -208,7 +209,10 @@
 uninstalledLibraries :: Bool -> PackageDescription -> IO [PackageName]
 uninstalledLibraries hasLibPkg pkgDesc = do
   pkgdeps <- packageDependencies pkgDesc
-  let libType = if hasLibPkg then Prof else Devel
+  libType <-
+    if hasLibPkg
+    then withGhcProfLibType
+    else return Devel
   bdeps <- filterM (notInstalled . RpmHsLib libType) $ buildDeps pkgdeps
   sdeps <- filterM (notInstalled . RpmHsLib Devel) $ (mkPackageName "Cabal" : setupDeps pkgdeps) \\ buildDeps pkgdeps
   return $ bdeps ++ sdeps
@@ -288,7 +292,8 @@
       -- fedora <- rpmEval "%fedora"
       -- let nogpgcheck = ["--nogpgcheck" | fedora `elem` []]
       noMacros <- notInstalled (RpmOther "ghc-rpm-macros")
-      rpmInstall True $ map (showRpm . RpmHsLib Prof) repopkgs ++ ["ghc-rpm-macros" | noMacros]
+      libtype <- withGhcProfLibType
+      rpmInstall True $ map (showRpm . RpmHsLib libtype) repopkgs ++ ["ghc-rpm-macros" | noMacros]
     return missing'
       where
         repoqueryPackageConf :: String -> PackageName -> IO (Maybe PackageName)
@@ -296,6 +301,11 @@
               let key = pkgconfd </> display pkg ++ "-[0-9]*.conf"
               res <- repoquery ["--qf", "%{name}"] key
               return $ if null res then Nothing else  Just pkg
+
+withGhcProfLibType :: IO LibPkgType
+withGhcProfLibType = do
+  with_ghc_prof <- rpmEval "{?with_ghc_prof}"
+  return $ if isJust with_ghc_prof then Prof else Devel
 
 showDep :: RpmPackage -> String
 showDep (RpmHsLib _ n) = display n
diff --git a/src/Main.hs b/src/Main.hs
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -53,8 +53,9 @@
 main :: IO ()
 main = do
   hSetBuffering stdout LineBuffering
-  simpleCmdArgs (Just version) "Cabal-rpm tool"
-    "RPM package tool for Haskell Stackage/Hackage packages" $
+  simpleCmdArgs (Just version)
+    "Tool to create RPM packages from Haskell Hackage/Stackage"
+    "See https://github.com/juhp/cabal-rpm#readme" $
     subcommands
     [ Subcommand "spec" "Generate a spec file" $
       createSpecFile_
diff --git a/src/PackageUtils.hs b/src/PackageUtils.hs
--- a/src/PackageUtils.hs
+++ b/src/PackageUtils.hs
@@ -33,7 +33,9 @@
   repoquery,
   rpmbuild,
   rpmInstall,
-  RpmStage (..)
+  RpmStage (..),
+  packageMacro,
+  readGlobalMacro
   ) where
 
 import FileUtils (assertFileNonEmpty, filesWithExtension, fileWithExtension,
@@ -42,7 +44,7 @@
                     PackageDescription, PackageIdentifier(..), PackageName,
                     showPkgId, tryFindPackageDesc)
 import SimpleCmd (cmd, cmd_, cmdBool, cmdIgnoreErr, cmdLines,
-                  cmdStderrToStdoutIn, error', grep_,
+                  cmdStderrToStdoutIn, error', grep, grep_,
                   removePrefix, sudo, sudo_, (+-+))
 import SimpleCmd.Git (isGitDir, grepGitConfig)
 import SimpleCmd.Rpm (rpmspec)
@@ -523,6 +525,7 @@
 
 dependencySortCabals :: Maybe FilePath -> [PackageIdentifier]
                      -> IO [PackageIdentifier]
+dependencySortCabals _ [] = return []
 dependencySortCabals mspec pkgids = do
   cabalsort <- optionalProgram "cabal-sort"
   if cabalsort
@@ -537,3 +540,16 @@
     else do
     mapM_ (`bringTarball` mspec) pkgids
     return pkgids
+
+readGlobalMacro :: String -> FilePath -> IO (Maybe String)
+readGlobalMacro macro spec = do
+            ps <- grep ("%global" +-+ macro ++ " ") spec
+            return $
+              case ps of
+                [] -> Nothing
+                [m] -> Just $ last $ words m
+                _ -> error' $
+                     "multiple %" ++ macro +-+ "definitions in" +-+ spec
+
+packageMacro :: String -> String
+packageMacro = filter (/= '-')
diff --git a/src/Stackage/Snapshots.hs b/src/Stackage/Snapshots.hs
--- a/src/Stackage/Snapshots.hs
+++ b/src/Stackage/Snapshots.hs
@@ -22,7 +22,7 @@
 
 getSnapshots :: IO Object
 getSnapshots =
-  getCachedJSON "stackage-snapshots" "snapshots.json" "http://haddock.stackage.org/snapshots.json" 200
+  getCachedJSON "stackage-snapshots" "snapshots.json" "https://www.stackage.org/download/snapshots.json" 200
 
 latestLTS :: IO MajorVer
 latestLTS = do
