packages feed

cabal-rpm 0.13.2 → 0.13.3

raw patch · 12 files changed

+127/−201 lines, 12 filesdep ~simple-cmd

Dependency ranges changed: simple-cmd

Files

ChangeLog view
@@ -1,3 +1,14 @@+* 0.13.3 (2019-05-10)+- default to lts-12+- repoquery for missing deps' package .conf file to avoid modular ghc conflicts+- only --assumeyes for dnf install when not tty+- give up if more than one spec file+- fix handling unversioned update+- map cabal build-tool to cabal-install+- use tmpdir for tmp spec files and building missing deps+- finalPackageDescription for debugging Cabal+- use simple-cmd-0.1.3.1 for sudo fixes+ * 0.13.2 (2019-04-09) - include ANNOUNCE in docs - if dependency parallel directory exists, don't check if installed
TODO view
@@ -1,5 +1,7 @@ ## Misc-* run "cabal update" if package-version not found+? run "cabal update" if package-version not found+- read options from header line+- use <> instead of ++  - unify hackage and package deps into a common data structure   with pkgShow and depShow to render strings
cabal-rpm.cabal view
@@ -1,5 +1,5 @@ Name:                cabal-rpm-Version:             0.13.2+Version:             0.13.3 Synopsis:            RPM packaging tool for Haskell Cabal-based packages Description:     This package provides a RPM packaging tool for Haskell Cabal-based packages.@@ -47,7 +47,7 @@                    directory,                    filepath,                    process,-                   simple-cmd >= 0.1.3,+                   simple-cmd >= 0.1.3.1,                    unix     if flag(old-locale)        Build-Depends: old-locale >= 1 && < 1.1,
man/cabal-rpm.1 view
@@ -1,116 +0,0 @@-.\" Automatically generated by Pandoc 2.7.1-.\"-.TH "CABAL-RPM" "1" "2013-01-21" "" ""-.hy-.SH NAME-.PP-cabal-rpm - a RPM packaging tool for Haskell Cabal packages-.SH SYNOPSIS-.PP-cabal-rpm [\f[I]options\f[R]] spec [\f[I]path-or-pkg\f[R]] cabal-rpm-[\f[I]options\f[R]] local [\f[I]path-or-pkg\f[R]] cabal-rpm-[\f[I]options\f[R]] srpm [\f[I]path-or-pkg\f[R]] cabal-rpm-[\f[I]options\f[R]] prep [\f[I]path-or-pkg\f[R]] cabal-rpm-[\f[I]options\f[R]] builddep [\f[I]path-or-pkg\f[R]] cabal-rpm-[\f[I]options\f[R]] install [\f[I]path-or-pkg\f[R]] cabal-rpm-[\f[I]options\f[R]] depends [\f[I]path-or-pkg\f[R]] cabal-rpm-[\f[I]options\f[R]] requires [\f[I]path-or-pkg\f[R]] cabal-rpm-[\f[I]options\f[R]] missingdeps [\f[I]path-or-pkg\f[R]] cabal-rpm-[\f[I]options\f[R]] diff [\f[I]path-or-pkg\f[R]] cabal-rpm-[\f[I]options\f[R]] update [\f[I]path-or-pkg\f[R]] cabal-rpm-[\f[I]options\f[R]] refresh [\f[I]path-or-pkg\f[R]]-.SH DESCRIPTION-.PP-cabal-rpm generates RPM packages and .spec files from Haskell Cabal-package.-.PP-If no \f[I]path-or-pkg\f[R] is specified, cabal-rpm looks for a .spec or-\&.cabal file in the current directory.-Otherwise, it will look for \f[I]path-or-pkg\f[R].-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.-.PP-If a .spec already exists, cabal-rpm outputs to .spec.cblrpm instead.-.SH OPTIONS-.TP-.B -h, \[en]help-Show the help text.-.TP-.B -b, \[en]binary-Force the base package name to be the Hackage package name.-.TP-.B -f \f[I]FLAGS\f[R], \[en]flags=\f[I]FLAGS\f[R]-Override one or more Cabal build configuration flags.-.TP-.B \[en]force-overwrite existing spec file-.TP-.B \[en]missing-comment out unavailable BuildRequires deps-.TP-.B \[en]release=\f[I]RELEASE\f[R]-Override the release number in the .spec file.-.TP-.B \[en]standalone-create a standalone package, built using cabal-install-.TP-.B -s \f[I]STREAM\f[R] , \[en]stream=\f[I]STREAM\f[R]-Stackage stream (eg lts-13) or `hackage' used to get package version.-.TP-.B -v \f[I]N\f[R], \[en]verbose=\f[I]N\f[R]-Set verbosity to \f[I]N\f[R].-.TP-.B \[en]version=\f[I]VERSION\f[R]-Override the version number in the .spec file.-.SH EXAMPLES-.PP-Below CMD can be one of: spec, srpm, prep, local, install, diff,-builddep, depends, requires, missingdeps, update, refresh-.PP-Do CMD for the package in current directory:-.IP-.nf-\f[C]-cabal-rpm CMD-\f[R]-.fi-.PP-Do CMD for package (directory or package name):-.IP-.nf-\f[C]-cabal-rpm CMD [package]-\f[R]-.fi-.PP-Do CMD for package-version (directory or package name):-.IP-.nf-\f[C]-cabal-rpm CMD [package-version]-\f[R]-.fi-.PP-Do CMD on a .cabal file:-.IP-.nf-\f[C]-cabal-rpm CMD path/to/some.cabal-\f[R]-.fi-.SH HISTORY-.PP-Cabal-rpm was originally written by Bryan O\[cq]Sullivan in 2007-2008-and resurrected by Jens Petersen in 2012 to replace cabal2spec.-.SH SEE ALSO-.PP-<http://github.com/juhp/cabal-rpm/>-.SH AUTHORS-This manpage was written by Jens Petersen.
src/Commands/Install.hs view
@@ -19,6 +19,7 @@  import Commands.RpmBuild (rpmBuild) import Dependencies (missingPackages, notInstalled, pkgInstallMissing)+import FileUtils (withTempDirectory) import Options (RpmFlags (..)) import PackageUtils (PackageData (..), rpmInstall, RpmStage (..), stripPkgDevel) import SimpleCmd (cmd_, (+-+))@@ -27,27 +28,31 @@  #if (defined(MIN_VERSION_base) && MIN_VERSION_base(4,8,0)) #else-import Control.Applicative ((<$>))+--import Control.Applicative ((<$>)) #endif import Control.Monad (unless, when)+import System.Directory (removeDirectoryRecursive) import System.FilePath ((</>))  install :: PackageData -> RpmFlags -> IO () install pkgdata flags = do   let pkgDesc = packageDesc pkgdata-  pkgInstallMissing pkgdata False+  pkgInstallMissing flags pkgdata   stillMissing <- missingPackages pkgDesc   unless (null stillMissing) $ do     putStrLn $ "Missing:" +-+ unwords stillMissing     mapM_ cblrpmInstallMissing stillMissing-  spec <- rpmBuild pkgdata flags Binary+  (spec, mtmpdir) <- rpmBuild pkgdata flags Binary   rpmdir <- rpmEval "%{_rpmdir}"   rpmspec [] (fmap (</> "%{arch}/%{name}-%{version}-%{release}.%{arch}.rpm") rpmdir) spec >>= rpmInstall+  maybe (return ()) removeDirectoryRecursive mtmpdir + cblrpmInstallMissing :: String -> IO () cblrpmInstallMissing pkg = do   noPkg <- notInstalled pkg   when noPkg $ do     let dep = stripPkgDevel pkg-    putStrLn $ "Running cabal-rpm install" +-+ dep-    cmd_ "cabal-rpm" ["install", dep]+    withTempDirectory $ \ _ -> do+      putStrLn $ "Running cabal-rpm install" +-+ dep+      cmd_ "cabal-rpm" ["install", dep]
src/Commands/RpmBuild.hs view
@@ -21,14 +21,21 @@  import Commands.Spec (createSpecFile) import Dependencies (pkgInstallMissing)+import FileUtils (mktempdir) import Options (RpmFlags (..)) import PackageUtils (bringTarball, PackageData (..), packageName,                      packageVersion, rpmbuild, RpmStage (..)) import SimpleCmd ((+-+)) +#if (defined(MIN_VERSION_base) && MIN_VERSION_base(4,8,0))+#else+import Control.Applicative ((<$>))+#endif --import Control.Exception (bracket)-import Control.Monad    (unless, void, when)+import Control.Monad    (unless, when)+import Data.Maybe (isNothing) import Distribution.PackageDescription (PackageDescription (..))+import System.Directory (removeDirectoryRecursive)  --import Distribution.Version (VersionRange, foldVersionRange') @@ -42,28 +49,32 @@ --             cmd_ "autoreconf" []  rpmBuild :: PackageData -> RpmFlags -> RpmStage ->-            IO FilePath+            IO (FilePath, Maybe FilePath) rpmBuild pkgdata flags stage = do --  let verbose = rpmVerbosity flags --  bracket (setFileCreationMask 0o022) setFileCreationMask $ \ _ -> do --    autoreconf verbose pkgDesc   let pkgDesc = packageDesc pkgdata       mspec = specFilename pkgdata-  specFile <- maybe (createSpecFile pkgdata flags Nothing)+  mtmp <- if isNothing mspec+          then Just <$> mktempdir+          else return Nothing+  specFile <- maybe (createSpecFile pkgdata flags mtmp)               (\ s -> putStrLn ("Using existing" +-+ s) >> return s)               mspec   let pkg = package pkgDesc       name = packageName pkg   when (stage `elem` [Binary,BuildDep]) $-    pkgInstallMissing pkgdata False+    pkgInstallMissing flags pkgdata    unless (stage == BuildDep) $ do     let version = packageVersion pkg     bringTarball (name ++ "-" ++ version) True specFile     rpmbuild stage specFile -  return specFile+  return (specFile, mtmp)  rpmBuild_ :: PackageData -> RpmFlags -> RpmStage -> IO ()-rpmBuild_ pkgdata flags stage =-  void (rpmBuild pkgdata flags stage)+rpmBuild_ pkgdata flags stage = do+  (_, mtmpdir) <- rpmBuild pkgdata flags stage+  maybe (return ()) removeDirectoryRecursive mtmpdir
src/Commands/Spec.hs view
@@ -143,7 +143,7 @@       ghcPkgDevel = if binlib then "-n ghc-%{name}-devel" else "devel"    put $ "# generated by cabal-rpm-" ++ showVersion Paths_cabal_rpm.version-    +-+ (unwords $ ["--missing" | rpmMissing flags] ++ ["--standalone" | standalone] ++ ["--subpackage" | rpmSubpackage flags])+    +-+ unwords (["--missing" | rpmMissing flags] ++ ["--standalone" | standalone] ++ ["--subpackage" | rpmSubpackage flags])   distro <- maybe detectDistro return (rpmDistribution flags)   if distro /= SUSE     then put "# https://fedoraproject.org/wiki/Packaging:Haskell"@@ -303,6 +303,8 @@     putNewline    -- haskell-gi generates lib source files at configure time+  -- (stricly should also check for otherModules (autogenModules?)+  --  but libraries wihout exposedModules should be useless/redundant)   let hasModules =         hasLib && ((notNull . exposedModules . fromJust . library) pkgDesc || "ghc-haskell-gi-devel" `elem` deps) @@ -380,9 +382,8 @@     let cabalFlags = [ "-f" ++ (if b then "" else "-") ++ unFlagName n | (n, b) <- rpmConfigurationsFlags flags ]     put $ "%define cabal_configure_options " ++ unwords cabalFlags   let pkgType = if hasLibPkg then "lib" else "bin"-  if hasLibPkg && not hasModules-    then put $ "%ghc_" ++ pkgType ++ "_build_without_haddock"-    else put $ "%ghc_" ++ pkgType ++ "_build"+  put $ "%ghc_" ++ pkgType ++ "_build" +++        if hasLibPkg && not hasModules then "_build_without_haddock" else []   put "# End cabal-rpm build"   sectionNewline 
src/Commands/Update.hs view
@@ -32,6 +32,7 @@ import Control.Applicative ((<$>)) #endif import Control.Monad (unless, when)+import Data.List (isPrefixOf) import Data.Maybe (isJust) import Distribution.PackageDescription (PackageDescription (..)) import System.Directory (createDirectory, renameFile, setCurrentDirectory)@@ -50,10 +51,10 @@           current = name ++ "-" ++ ver           revised = isJust $ lookup "x-revision" (customFieldsPD pkgDesc)       latest <- case mpkgver of-                    Just pv -> return pv-                    Nothing -> latestPackage (rpmStream flags) name+                    Just pv | (name ++ "-") `isPrefixOf` pv -> return pv+                    _ -> latestPackage (rpmStream flags) name       let newver = removePrefix (name ++ "-") latest-      let latestVer = readVersion newver+          latestVer = readVersion newver           updated = latestVer > curVer       if latestVer < curVer         then putStrLn $ "current" +-+ ver +-+ "is newer!"
src/Dependencies.hs view
@@ -26,11 +26,11 @@   testsuiteDependencies   ) where -import PackageUtils (PackageData(..), packageName, packageManager, removeSuffix,-                     repoquery)-import SimpleCmd (cmd, cmd_, cmdBool, (+-+))+import Options (RpmFlags(..))+import PackageUtils (PackageData(..), packageName, removeSuffix, repoquery,+                     rpmInstall, stripPkgDevel)+import SimpleCmd (cmd, cmdBool, (+-+)) import SimpleCmd.Rpm (rpmspec)-import SysCmd (optionalProgram, rpmEval, trySystem)  #if (defined(MIN_VERSION_base) && MIN_VERSION_base(4,8,0)) #else@@ -38,8 +38,8 @@ #endif import Control.Monad (filterM, when, unless) -import Data.List (delete, isSuffixOf, nub, (\\))-import Data.Maybe (catMaybes, fromMaybe, isNothing)+import Data.List (delete, isPrefixOf, isSuffixOf, nub, (\\))+import Data.Maybe (catMaybes, fromJust, isNothing)  import Distribution.Package  (Dependency (..), #if defined(MIN_VERSION_Cabal) && MIN_VERSION_Cabal(1,22,0)@@ -74,7 +74,6 @@ import System.Directory (doesDirectoryExist, doesFileExist) import System.FilePath ((<.>), (</>)) import System.IO (hPutStrLn, stderr)-import System.Posix.User (getEffectiveUserID)  excludedPkgs :: String -> Bool excludedPkgs = flip notElem ["Cabal", "base", "ghc-prim", "integer-gmp"]@@ -94,11 +93,11 @@  setupDependencies :: PackageDescription  -- ^pkg description                   -> [String]         -- ^depends-setupDependencies pkgDesc = #if defined(MIN_VERSION_Cabal) && MIN_VERSION_Cabal(1,24,0)+setupDependencies pkgDesc =   maybe [] (map depName . setupDepends) (setupBuildInfo pkgDesc) #else-  []+setupDependencies _pkgDesc = [] #endif  #if defined(MIN_VERSION_Cabal) && MIN_VERSION_Cabal(1,22,0)@@ -161,7 +160,7 @@          then cmd "rpm" (args ++ [file])          else repoquery args file   let pkgs = nub $ words out-      -- EL5 repoquery can return "No package provides <file>"+  -- EL5 repoquery can return "No package provides <file>"   case pkgs of     [pkg] -> return $ Just pkg     [] -> do@@ -181,6 +180,7 @@ packageDependencies strict pkgDesc = do     (deps, tools', clibs', pkgcfgs) <- dependencies pkgDesc     let excludedTools n = n `notElem` ["ghc", "hsc2hs", "perl"]+        mapTools "cabal" = "cabal-install"         mapTools "gtk2hsC2hs" = "gtk2hs-buildtools"         mapTools "gtk2hsHookGenerator" = "gtk2hs-buildtools"         mapTools "gtk2hsTypeGen" = "gtk2hs-buildtools"@@ -238,40 +238,41 @@   let self = packageName $ package pkgDesc   return $ delete (showDep self) develSubpkgs -pkgInstallMissing :: PackageData -> Bool -> IO ()-pkgInstallMissing pkgdata hard = do+pkgInstallMissing :: RpmFlags -> PackageData -> IO ()+pkgInstallMissing flags pkgdata = do   let pkgDesc = packageDesc pkgdata       mspec = specFilename pkgdata   missing <- missingPackages pkgDesc   unless (null missing) $ do     subpkgs <- subPackages mspec pkgDesc     let pkgs = missing \\ subpkgs-    pkginstaller <- packageManager+    pkgconfdir <- fromJust . lookup "Global Package DB" . read <$> cmd (ghcCmd flags) ["--info"]     putStrLn $ "Running repoquery" +-+ unwords pkgs-    repopkgs <- filter (/= "") <$> mapM (repoquery ["--qf", "%{name}"]) pkgs+    repopkgs <- filter (/= "") <$> mapM (repoqueryPackageConf pkgconfdir) pkgs     let missing' = pkgs \\ repopkgs-    if missing' /= [] && hard-      then error $ unwords missing' +-+ "not available."-      else do-      unless (null missing') $ do-        putStrLn "Unavailable dependencies:"-        mapM_ putStrLn missing'-      unless (null repopkgs) $ do-        putStrLn "Uninstalled dependencies:"-        mapM_ putStrLn repopkgs-        uid <- getEffectiveUserID-        maybeSudo <--          if uid == 0-          then return Nothing-          else do-            havesudo <- optionalProgram "sudo"-            return $ if havesudo then Just "sudo" else Nothing-        let args = map showPkg repopkgs-        putStrLn $ "Running:" +-+ fromMaybe "" maybeSudo +-+ pkginstaller +-+ "install" +-+ unwords args-        let exec = if hard then cmd_ else trySystem-        fedora <- rpmEval "%fedora"-        let nogpgcheck = ["--nogpgcheck" | fedora `elem` []]-        exec (fromMaybe pkginstaller maybeSudo) $ maybe [] (const [pkginstaller]) maybeSudo ++ ("install" : args ++ nogpgcheck)-          where-            showPkg :: String -> String-            showPkg p = if '(' `elem` p then show p else p+    unless (null missing') $ do+      putStrLn "Unavailable dependencies:"+      mapM_ putStrLn missing'+    unless (null repopkgs) $ do+      putStrLn "Uninstalled dependencies:"+      mapM_ putStrLn repopkgs+      -- fedora <- rpmEval "%fedora"+      -- let nogpgcheck = ["--nogpgcheck" | fedora `elem` []]+      rpmInstall $ map showPkg repopkgs+        where+          showPkg :: String -> String+          showPkg p = if '(' `elem` p then show p else p++repoqueryPackageConf :: String -> String -> IO String+repoqueryPackageConf pkgconfd pkg =+  let key = if isGhcDevelPkg pkg+        then pkgconfd </> stripPkgDevel pkg ++ "-[0-9]*.conf"+        else pkg in+    repoquery ["--qf", "%{name}"] key++ghcCmd :: RpmFlags -> FilePath+ghcCmd flags = maybe "ghc" show $ rpmCompilerId flags++isGhcDevelPkg :: String -> Bool+isGhcDevelPkg p =+  "ghc-" `isPrefixOf` p && "-devel" `isSuffixOf` p
src/FileUtils.hs view
@@ -33,7 +33,11 @@ import Data.List (isPrefixOf) import Data.Maybe (isJust) import System.Directory (getCurrentDirectory, getDirectoryContents,-                         setCurrentDirectory, removeDirectoryRecursive)+                         setCurrentDirectory, removeDirectoryRecursive,+#if (defined(MIN_VERSION_directory) && MIN_VERSION_directory(1,2,3))+                         withCurrentDirectory+#endif+                         ) import System.FilePath (takeExtension, (</>))  filesWithExtension :: FilePath -> String -> IO [FilePath]@@ -73,9 +77,11 @@ getDirectoryContents_ dir =   filter (not . isPrefixOf ".") <$> getDirectoryContents dir --- from directory-1.2.3.0+#if (defined(MIN_VERSION_directory) && MIN_VERSION_directory(1,2,3))+#else withCurrentDirectory :: FilePath -> IO a -> IO a withCurrentDirectory dir action =   bracket getCurrentDirectory setCurrentDirectory $ \ _ -> do     setCurrentDirectory dir     action+#endif
src/Options.hs view
@@ -86,7 +86,7 @@     }  defaultStream :: String-defaultStream = "lts"+defaultStream = "lts-12"  quiet :: RpmFlags quiet = emptyRpmFlags {rpmVerbosity = silent}
src/PackageUtils.hs view
@@ -56,7 +56,7 @@  import Data.Char (isDigit, toLower) import Data.List (groupBy, isPrefixOf, isSuffixOf, nub, sort, stripPrefix)-import Data.Maybe (fromJust, fromMaybe)+import Data.Maybe (isNothing, fromJust, fromMaybe) import Data.Time.Clock (diffUTCTime, getCurrentTime) import Data.Version ( #if (defined(MIN_VERSION_base) && MIN_VERSION_base(4,8,0))@@ -141,6 +141,7 @@ import System.Environment (getEnv) import System.FilePath ((</>), (<.>), dropFileName, takeBaseName, takeExtensions,                         takeFileName)+import System.IO (hIsTerminalDevice, stdout) import System.Posix.Files (accessTime, fileMode, getFileStatus,                            modificationTime, setFileMode) @@ -165,6 +166,13 @@ simplePackageDescription :: FilePath -> RpmFlags                          -> IO (PackageDescription, [FilePath], [FilePath]) simplePackageDescription cabalfile opts = do+  final <- finalPackageDescription cabalfile opts+  (docs, licensefiles) <- findDocsLicenses (dropFileName cabalfile) final+  return (final, docs, licensefiles)++finalPackageDescription :: FilePath -> RpmFlags+                          -> IO PackageDescription+finalPackageDescription cabalfile opts = do   let verbose = rpmVerbosity opts #if defined(MIN_VERSION_Cabal) && MIN_VERSION_Cabal(2,0,0) #else@@ -199,15 +207,14 @@ #if defined(MIN_VERSION_Cabal) && MIN_VERSION_Cabal(2,0,0)   let finalizePackageDescription flags = finalizePD flags defaultComponentRequestedSpec #endif-  case finalizePackageDescription (mkFlagAssignment $ rpmConfigurationsFlags opts)-       (const True) (Platform buildArch buildOS)-       compiler-       [] genPkgDesc of+  let final =+        finalizePackageDescription (mkFlagAssignment $ rpmConfigurationsFlags opts)+        (const True) (Platform buildArch buildOS)+        compiler+        [] genPkgDesc+  case final of     Left e -> die $ "finalize failed: " ++ show e-    Right (pd, _) -> do-      (docs, licensefiles) <- findDocsLicenses (dropFileName cabalfile) pd-      return (pd, docs, licensefiles)-+    Right res -> return $ fst res  findDocsLicenses :: FilePath -> PackageDescription -> IO ([FilePath], [FilePath]) findDocsLicenses dir pkgDesc = do@@ -468,21 +475,17 @@   return $ if binary || hasExec && not hasLib then (name, hasLib) else ("ghc-" ++ name, False)  checkForSpecFile :: Maybe String -> IO (Maybe FilePath)-checkForSpecFile Nothing = do-  -- emacs makes ".#*.spec" tmp files+checkForSpecFile mpkg = do   allSpecs <- allSpecfiles-  let specs = filter (\ f -> head f /= '.') allSpecs-  when (specs /= allSpecs) $+  -- emacs makes ".#*.spec" tmp files+  let predicate = maybe ((/= '.') . head) (\ pkg -> (`elem` [pkg <.> "spec", "ghc-" ++ pkg <.> "spec"])) mpkg+      specs = filter predicate allSpecs+  when (specs /= allSpecs && isNothing mpkg) $     putStrLn "Warning: dir contains a hidden spec file"   case specs of     [one] -> return $ Just one-    _ -> return Nothing-checkForSpecFile (Just pkg) = do-  let specname = pkg <.> "spec"-  specs <- filter (`elem` [specname, "ghc-" ++ specname]) <$> allSpecfiles-  case specs of-    [one] -> return $ Just one-    _ -> return Nothing+    [] -> return Nothing+    _ -> error "More than one spec file found!"  allSpecfiles :: IO [FilePath] allSpecfiles = filesWithExtension "." ".spec"@@ -572,7 +575,8 @@ rpmInstall rpms = do   pkginstaller <- packageManager   let (inst, arg) = if pkginstaller == "dnf" then ("dnf", "install") else ("yum", "localinstall")-  sudo inst $ ["-y", arg] ++ rpms+  tty <- hIsTerminalDevice stdout+  sudo inst $ ["-y" | not tty] ++ [arg] ++ rpms  editSpecField :: String -> String -> FilePath -> IO () editSpecField field new spec =