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 +11/−0
- TODO +3/−1
- cabal-rpm.cabal +2/−2
- man/cabal-rpm.1 +0/−116
- src/Commands/Install.hs +10/−5
- src/Commands/RpmBuild.hs +18/−7
- src/Commands/Spec.hs +5/−4
- src/Commands/Update.hs +4/−3
- src/Dependencies.hs +40/−39
- src/FileUtils.hs +8/−2
- src/Options.hs +1/−1
- src/PackageUtils.hs +25/−21
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 =