module Main (main) where
import Version ( version, targetOS, targetARCH )
-import Distribution.InstalledPackageInfo.Binary
+import Distribution.InstalledPackageInfo.Binary()
import qualified Distribution.Simple.PackageIndex as PackageIndex
import Distribution.ModuleName hiding (main)
import Distribution.InstalledPackageInfo
import qualified Data.Binary as Bin
import qualified Data.Binary.Get as Bin
+#if __GLASGOW_HASKELL__ < 612 || defined(mingw32_HOST_OS)
+-- mingw32 needs these for getExecDir, GHC <6.12 needs them for openNewFile
import Foreign
import Foreign.C
-#ifdef mingw32_HOST_OS
-import GHC.ConsoleHandler
-#else
-import System.Posix hiding (fdToHandle)
#endif
-import IO ( isPermissionError )
+#if __GLASGOW_HASKELL__ < 612
import System.Posix.Internals
#if __GLASGOW_HASKELL__ >= 611
import GHC.IO.Handle.FD (fdToHandle)
#else
import GHC.Handle (fdToHandle)
#endif
+#endif
+
+#ifdef mingw32_HOST_OS
+import GHC.ConsoleHandler
+#else
+import System.Posix hiding (fdToHandle)
+#endif
+
+import IO ( isPermissionError )
#if defined(GLOB)
import System.Process(runInteractiveCommand)
case e_tcache of
Left ex -> do
when (verbosity > Normal) $
- putStrLn ("warning: cannot read cache file " ++ cache ++ ": " ++ show ex)
+ warn ("warning: cannot read cache file " ++ cache ++ ": " ++ show ex)
ignore_cache
Right tcache
| tcache >= tdir -> do
return PackageDB { location = path, packages = pkgs' }
| otherwise -> do
when (verbosity >= Normal) $ do
- putStrLn ("WARNING: cache is out of date: " ++ cache)
- putStrLn " use 'ghc-pkg recache' to fix."
+ warn ("WARNING: cache is out of date: " ++ cache)
+ warn " use 'ghc-pkg recache' to fix."
ignore_cache
where
ignore_cache = do
do_cmd (AddPackage p) = do
let file = location db </> display (installedPackageId p) <.> "conf"
when (verbosity > Normal) $ putStrLn ("writing " ++ file)
- writeFileAtomic file (showInstalledPackageInfo p)
+ writeFileUtf8Atomic file (showInstalledPackageInfo p)
do_cmd (ModifyPackage p) =
do_cmd (AddPackage p)
let filename = location db </> cachefilename
when (verbosity > Normal) $
putStrLn ("writing cache " ++ filename)
- writeBinPackageDB filename (map convertPackageInfoOut (packages db))
+ writeBinaryFileAtomic filename (map convertPackageInfoOut (packages db))
`catch` \e ->
if isPermissionError e
then die (filename ++ ": you don't have permission to modify this file")
when (not (null broken) && not simple_output && verbosity /= Silent) $ do
prog <- getProgramName
- putStrLn ("WARNING: there are broken packages. Run '" ++ prog ++ " check' for more details.")
+ warn ("WARNING: there are broken packages. Run '" ++ prog ++ " check' for more details.")
if simple_output then show_simple stack else do
let pkgs = allPackagesInStack db_stack
checkPackage p = do
- (_,es) <- runValidate $ checkPackageConfig p db_stack False True
+ (_,es,ws) <- runValidate $ checkPackageConfig p db_stack False True
if null es
- then return []
+ then do when (not simple_output) $ do
+ _ <- reportValidateErrors [] ws "" Nothing
+ return ()
+ return []
else do
when (not simple_output) $ do
reportError ("There are problems in package " ++ display (sourcePackageId p) ++ ":")
- _ <- reportValidateErrors es " " Nothing
+ _ <- reportValidateErrors es ws " " Nothing
return ()
return [p]
let shown = concat $ intersperse ",\n "
$ map (show . convertPackageInfoOut) ipis
fileContents = "[" ++ shown ++ "\n]"
- writeFileAtomic filename fileContents
+ writeFileUtf8Atomic filename fileContents
`catch` \e ->
if isPermissionError e
then die (filename ++ ": you don't have permission to modify this file")
-- Sanity-check a new package config, and automatically build GHCi libs
-- if requested.
-type ValidateError = (Force,String)
+type ValidateError = (Force,String)
+type ValidateWarning = String
-newtype Validate a = V { runValidate :: IO (a, [ValidateError]) }
+newtype Validate a = V { runValidate :: IO (a, [ValidateError],[ValidateWarning]) }
instance Monad Validate where
- return a = V $ return (a, [])
+ return a = V $ return (a, [], [])
m >>= k = V $ do
- (a, es) <- runValidate m
- (b, es') <- runValidate (k a)
- return (b,es++es')
+ (a, es, ws) <- runValidate m
+ (b, es', ws') <- runValidate (k a)
+ return (b,es++es',ws++ws')
verror :: Force -> String -> Validate ()
-verror f s = V (return ((),[(f,s)]))
+verror f s = V (return ((),[(f,s)],[]))
+
+vwarn :: String -> Validate ()
+vwarn s = V (return ((),[],["Warning: " ++ s]))
liftIO :: IO a -> Validate a
-liftIO k = V (k >>= \a -> return (a,[]))
+liftIO k = V (k >>= \a -> return (a,[],[]))
-- returns False if we should die
-reportValidateErrors :: [ValidateError] -> String -> Maybe Force -> IO Bool
-reportValidateErrors es prefix mb_force = do
+reportValidateErrors :: [ValidateError] -> [ValidateWarning]
+ -> String -> Maybe Force -> IO Bool
+reportValidateErrors es ws prefix mb_force = do
+ mapM_ (warn . (prefix++)) ws
oks <- mapM report es
return (and oks)
where
-> Force
-> IO ()
validatePackageConfig pkg db_stack auto_ghci_libs update force = do
- (_,es) <- runValidate $ checkPackageConfig pkg db_stack auto_ghci_libs update
- ok <- reportValidateErrors es (display (sourcePackageId pkg) ++ ": ") (Just force)
+ (_,es,ws) <- runValidate $ checkPackageConfig pkg db_stack auto_ghci_libs update
+ ok <- reportValidateErrors es ws (display (sourcePackageId pkg) ++ ": ") (Just force)
when (not ok) $ exitWith (ExitFailure 1)
checkPackageConfig :: InstalledPackageInfo
checkDuplicates db_stack pkg update
mapM_ (checkDep db_stack) (depends pkg)
checkDuplicateDepends (depends pkg)
- mapM_ (checkDir "import-dirs") (importDirs pkg)
- mapM_ (checkDir "library-dirs") (libraryDirs pkg)
- mapM_ (checkDir "include-dirs") (includeDirs pkg)
+ mapM_ (checkDir False "import-dirs") (importDirs pkg)
+ mapM_ (checkDir True "library-dirs") (libraryDirs pkg)
+ mapM_ (checkDir True "include-dirs") (includeDirs pkg)
checkModules pkg
mapM_ (checkHSLib (libraryDirs pkg) auto_ghci_libs) (hsLibraries pkg)
-- ToDo: check these somehow?
" overlaps with: " ++ unwords (map display dups)
-checkDir :: String -> String -> Validate ()
-checkDir thisfield d
+checkDir :: Bool -> String -> String -> Validate ()
+checkDir warn_only thisfield d
| "$topdir" `isPrefixOf` d = return ()
| "$httptopdir" `isPrefixOf` d = return ()
-- can't check these, because we don't know what $(http)topdir is
+ | isRelative d = verror ForceFiles $
+ thisfield ++ ": " ++ d ++ " is a relative path"
+ -- relative paths don't make any sense; #4134
| otherwise = do
there <- liftIO $ doesDirectoryExist d
when (not there) $
- verror ForceFiles (thisfield ++ ": " ++ d ++ " doesn't exist or isn't a directory")
+ let msg = thisfield ++ ": " ++ d ++ " doesn't exist or isn't a directory"
+ in
+ if warn_only
+ then vwarn msg
+ else verror ForceFiles msg
checkDep :: PackageDBStack -> InstalledPackageId -> Validate ()
checkDep db_stack pkgid
| otherwise = do
m <- doesFileExistOnPath ghci_lib_file dirs
when (isNothing m && ghci_lib_file /= "HSrts.o") $
- hPutStrLn stderr ("warning: can't find GHCi lib " ++ ghci_lib_file)
+ warn ("warning: can't find GHCi lib " ++ ghci_lib_file)
where
ghci_lib_file = lib <.> "o"
dieOrForceAll ForceAll s = ignoreError s
dieOrForceAll _other s = dieForcible s
+warn :: String -> IO ()
+warn = reportError
+
ignoreError :: String -> IO ()
ignoreError s = reportError (s ++ " (ignoring)")
where handler' (Exception.ErrorCall err) = handler err
+writeBinaryFileAtomic :: Bin.Binary a => FilePath -> a -> IO ()
+writeBinaryFileAtomic targetFile obj =
+ withFileAtomic targetFile $ \h -> do
+ hSetBinaryMode h True
+ B.hPutStr h (Bin.encode obj)
+
+writeFileUtf8Atomic :: FilePath -> String -> IO ()
+writeFileUtf8Atomic targetFile content =
+ withFileAtomic targetFile $ \h -> do
+#if __GLASGOW_HASKELL__ >= 612
+ hSetEncoding h utf8
+#endif
+ hPutStr h content
+
-- copied from Cabal's Distribution.Simple.Utils, except that we want
-- to use text files here, rather than binary files.
-writeFileAtomic :: FilePath -> String -> IO ()
-writeFileAtomic targetFile content = do
+withFileAtomic :: FilePath -> (Handle -> IO ()) -> IO ()
+withFileAtomic targetFile write_content = do
(newFile, newHandle) <- openNewFile targetDir template
- do hPutStr newHandle content
+ do write_content newHandle
hClose newHandle
#if mingw32_HOST_OS || mingw32_TARGET_OS
renameFile newFile targetFile
-- to always return a valid dir
(targetDir_,targetName) = splitFileName targetFile
--- Ugh, this is a copy/paste of code from the base library, but
--- if uses 666 rather than 600 for the permissions.
openNewFile :: FilePath -> String -> IO (FilePath, Handle)
openNewFile dir template = do
+#if __GLASGOW_HASKELL__ >= 612
+ -- this was added to System.IO in 6.12.1
+ -- we must use this version because the version below opens the file
+ -- in binary mode.
+ openTempFileWithDefaultPermissions dir template
+#else
+ -- Ugh, this is a copy/paste of code from the base library, but
+ -- if uses 666 rather than 600 for the permissions.
pid <- c_getpid
findTempName pid
where
std_flags = o_NONBLOCK .|. o_NOCTTY
output_flags = std_flags .|. o_CREAT
rw_flags = output_flags .|. o_RDWR
+#endif /* GLASGOW_HASKELL < 612 */
-- | The function splits the given string to substrings
-- using 'isSearchPathSeparator'.