X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=compiler%2Fmain%2FSysTools.lhs;h=26c2f1b132f7172b244837094fb5ffe2bb57bd8a;hb=5d536b42c88dbe9523c4b0c799a0e46c2d984aa6;hp=9885b8d202144bf140a09d7e9df4b3aa8743846d;hpb=cf411c9ae5d61d6e5baa5e5e6b0ad9803b041236;p=ghc-hetmet.git
diff --git a/compiler/main/SysTools.lhs b/compiler/main/SysTools.lhs
index 9885b8d..26c2f1b 100644
--- a/compiler/main/SysTools.lhs
+++ b/compiler/main/SysTools.lhs
@@ -7,6 +7,13 @@
-----------------------------------------------------------------------------
\begin{code}
+{-# OPTIONS -w #-}
+-- The above warning supression flag is a temporary kludge.
+-- While working on this module you are encouraged to remove it and fix
+-- any warnings in the module. See
+-- http://hackage.haskell.org/trac/ghc/wiki/Commentary/CodingStyle#Warnings
+-- for details
+
module SysTools (
-- Initialisation
initSysTools,
@@ -17,11 +24,13 @@ module SysTools (
runMangle, runSplit, -- [Option] -> IO ()
runAs, runLink, -- [Option] -> IO ()
runMkDLL,
+ runWindres,
touch, -- String -> String -> IO ()
copy,
copyWithHeader,
normalisePath, -- FilePath -> FilePath
+ getExtraViaCOpts,
-- Temporary-file management
setTmpDir,
@@ -29,9 +38,6 @@ module SysTools (
cleanTempDirs, cleanTempFiles, cleanTempFilesExcept,
addFilesToClean,
- -- System interface
- system, -- String -> IO ExitCode
-
Option(..)
) where
@@ -49,47 +55,31 @@ import FiniteMap
import Control.Exception
import Data.IORef
-import Data.Int
import Control.Monad
import System.Exit
import System.Environment
import System.IO
import SYSTEM_IO_ERROR as IO
import System.Directory
+import Data.Char
import Data.Maybe
import Data.List
--- GHC <= 4.08 didn't have rawSystem, and runs into problems with long command
--- lines on mingw32, so we disallow it now.
-#if __GLASGOW_HASKELL__ < 500
-#error GHC >= 5.00 is required for bootstrapping GHC
-#endif
-
#ifndef mingw32_HOST_OS
-#if __GLASGOW_HASKELL__ > 504
import qualified System.Posix.Internals
-#else
-import qualified Posix
-#endif
#else /* Must be Win32 */
-import List ( isPrefixOf )
-import Util ( dropList )
import Foreign
import CString ( CString, peekCString )
#endif
-import Text.Regex
-
#if __GLASGOW_HASKELL__ < 603
-- rawSystem comes from libghccompat.a in stage1
import Compat.RawSystem ( rawSystem )
import System.Cmd ( system )
import GHC.IOBase ( IOErrorType(..) )
#else
-import System.Cmd ( rawSystem, system )
import System.Process ( runInteractiveProcess, getProcessExitCode )
import Control.Concurrent( forkIO, newChan, readChan, writeChan )
-import Data.Char ( isSpace )
import FastString ( mkFastString )
import SrcLoc ( SrcLoc, mkSrcLoc, noSrcSpan, mkSrcSpan )
#endif
@@ -214,6 +204,10 @@ initSysTools mbMinusB dflags
| am_installed = installed_bin cGHC_MANGLER_PGM
| otherwise = inplace cGHC_MANGLER_DIR_REL cGHC_MANGLER_PGM
+ windres_path
+ | am_installed = installed_bin "bin/windres"
+ | otherwise = "windres"
+
; let dflags0 = defaultDynFlags
#ifndef mingw32_HOST_OS
-- check whether TMPDIR is set in the environment
@@ -272,9 +266,6 @@ initSysTools mbMinusB dflags
-- later on; although gcc_args are in NATIVE format,
-- gcc can cope
-- (see comments with declarations of global variables)
- --
- -- The quotes round the -B argument are in case TopDir
- -- has spaces in it
perl_path | am_installed = installed_bin cGHC_PERL
| otherwise = cGHC_PERL
@@ -344,7 +335,8 @@ initSysTools mbMinusB dflags
pgm_l = (ld_prog,ld_args),
pgm_dll = (mkdll_prog,mkdll_args),
pgm_T = touch_path,
- pgm_sysman = top_dir ++ "/ghc/rts/parallel/SysMan"
+ pgm_sysman = top_dir ++ "/ghc/rts/parallel/SysMan",
+ pgm_windres = windres_path
-- Hans: this isn't right in general, but you can
-- elaborate it in the same way as the others
}
@@ -425,21 +417,85 @@ runPp dflags args = do
runCc :: DynFlags -> [Option] -> IO ()
runCc dflags args = do
let (p,args0) = pgm_c dflags
- runSomethingFiltered dflags cc_filter "C Compiler" p (args0++args)
+ args1 = args0 ++ args
+ mb_env <- getGccEnv args1
+ runSomethingFiltered dflags cc_filter "C Compiler" p args1 mb_env
where
-- discard some harmless warnings from gcc that we can't turn off
- cc_filter str = unlines (do_filter (lines str))
+ cc_filter = unlines . doFilter . lines
+
+ {-
+ gcc gives warnings in chunks like so:
+ In file included from /foo/bar/baz.h:11,
+ from /foo/bar/baz2.h:22,
+ from wibble.c:33:
+ /foo/flibble:14: global register variable ...
+ /foo/flibble:15: warning: call-clobbered r...
+ We break it up into its chunks, remove any call-clobbered register
+ warnings from each chunk, and then delete any chunks that we have
+ emptied of warnings.
+ -}
+ doFilter = unChunkWarnings . filterWarnings . chunkWarnings []
+ -- We can't assume that the output will start with an "In file inc..."
+ -- line, so we start off expecting a list of warnings rather than a
+ -- location stack.
+ chunkWarnings :: [String] -- The location stack to use for the next
+ -- list of warnings
+ -> [String] -- The remaining lines to look at
+ -> [([String], [String])]
+ chunkWarnings loc_stack [] = [(loc_stack, [])]
+ chunkWarnings loc_stack xs
+ = case break loc_stack_start xs of
+ (warnings, lss:xs') ->
+ case span loc_start_continuation xs' of
+ (lsc, xs'') ->
+ (loc_stack, warnings) : chunkWarnings (lss : lsc) xs''
+ _ -> [(loc_stack, xs)]
+
+ filterWarnings :: [([String], [String])] -> [([String], [String])]
+ filterWarnings [] = []
+ -- If the warnings are already empty then we are probably doing
+ -- something wrong, so don't delete anything
+ filterWarnings ((xs, []) : zs) = (xs, []) : filterWarnings zs
+ filterWarnings ((xs, ys) : zs) = case filter wantedWarning ys of
+ [] -> filterWarnings zs
+ ys' -> (xs, ys') : filterWarnings zs
+
+ unChunkWarnings :: [([String], [String])] -> [String]
+ unChunkWarnings [] = []
+ unChunkWarnings ((xs, ys) : zs) = xs ++ ys ++ unChunkWarnings zs
+
+ loc_stack_start s = "In file included from " `isPrefixOf` s
+ loc_start_continuation s = " from " `isPrefixOf` s
+ wantedWarning w
+ | "warning: call-clobbered register used" `isContainedIn` w = False
+ | otherwise = True
+
+isContainedIn :: String -> String -> Bool
+xs `isContainedIn` ys = any (xs `isPrefixOf`) (tails ys)
+
+-- If the -B
option is set, add to PATH. This works around
+-- a bug in gcc on Windows Vista where it can't find its auxiliary
+-- binaries (see bug #1110).
+getGccEnv :: [Option] -> IO (Maybe [(String,String)])
+getGccEnv opts =
+#if __GLASGOW_HASKELL__ < 603
+ return Nothing
+#else
+ if null b_dirs
+ then return Nothing
+ else do env <- getEnvironment
+ return (Just (map mangle_path env))
+ where
+ (b_dirs, _) = partitionWith get_b_opt opts
- do_filter [] = []
- do_filter ls@(l:ls')
- | (w:rest) <- dropWhile (isJust .matchRegex r_from) ls,
- isJust (matchRegex r_warn w)
- = do_filter rest
- | otherwise
- = l : do_filter ls'
+ get_b_opt (Option ('-':'B':dir)) = Left dir
+ get_b_opt other = Right other
- r_from = mkRegex "from.*:[0-9]+"
- r_warn = mkRegex "warning: call-clobbered register used"
+ mangle_path (path,paths) | map toUpper path == "PATH"
+ = (path, '\"' : head b_dirs ++ "\";" ++ paths)
+ mangle_path other = other
+#endif
runMangle :: DynFlags -> [Option] -> IO ()
runMangle dflags args = do
@@ -454,17 +510,45 @@ runSplit dflags args = do
runAs :: DynFlags -> [Option] -> IO ()
runAs dflags args = do
let (p,args0) = pgm_a dflags
- runSomething dflags "Assembler" p (args0++args)
+ args1 = args0 ++ args
+ mb_env <- getGccEnv args1
+ runSomethingFiltered dflags id "Assembler" p args1 mb_env
runLink :: DynFlags -> [Option] -> IO ()
runLink dflags args = do
let (p,args0) = pgm_l dflags
- runSomething dflags "Linker" p (args0++args)
+ args1 = args0 ++ args
+ mb_env <- getGccEnv args1
+ runSomethingFiltered dflags id "Linker" p args1 mb_env
runMkDLL :: DynFlags -> [Option] -> IO ()
runMkDLL dflags args = do
let (p,args0) = pgm_dll dflags
- runSomething dflags "Make DLL" p (args0++args)
+ args1 = args0 ++ args
+ mb_env <- getGccEnv (args0++args)
+ runSomethingFiltered dflags id "Make DLL" p args1 mb_env
+
+runWindres :: DynFlags -> [Option] -> IO ()
+runWindres dflags args = do
+ let (gcc,gcc_args) = pgm_c dflags
+ windres = pgm_windres dflags
+ mb_env <- getGccEnv gcc_args
+ runSomethingFiltered dflags id "Windres" windres
+ -- we must tell windres where to find gcc: it might not be on PATH
+ (Option ("--preprocessor=" ++
+ unwords (map quote (gcc : map showOpt gcc_args ++
+ ["-E", "-xc", "-DRC_INVOKED"])))
+ -- -- use-temp-file is required for windres to interpret the
+ -- quoting in the preprocessor arg above correctly. Without
+ -- this, windres calls the preprocessor with popen, which gets
+ -- the quoting wrong (discovered by experimentation and
+ -- reading the windres sources). See #1828.
+ : Option "--use-temp-file"
+ : args)
+ -- we must use the PATH workaround here too, since windres invokes gcc
+ mb_env
+ where
+ quote x = '\"' : x ++ "\""
touch :: DynFlags -> String -> String -> IO ()
touch dflags purpose arg =
@@ -485,6 +569,10 @@ copyWithHeader dflags purpose maybe_header from to = do
hPutStr h ls
hClose h
+getExtraViaCOpts :: DynFlags -> IO [String]
+getExtraViaCOpts dflags = do
+ f <- readFile (topDir dflags `joinFileName` "extra-gcc-opts")
+ return (words f)
\end{code}
%************************************************************************
@@ -528,7 +616,8 @@ newTempName dflags extn
= do d <- getTempDir dflags
x <- getProcessID
findTempName (d ++ "/ghc" ++ show x ++ "_") 0
- where
+ where
+ findTempName :: FilePath -> Integer -> IO FilePath
findTempName prefix x
= do let filename = (prefix ++ show x) `joinFileExt` extn
b <- doesFileExist filename
@@ -545,6 +634,8 @@ getTempDir dflags@(DynFlags{tmpDir=tmp_dir})
Nothing ->
do x <- getProcessID
let prefix = tmp_dir ++ "/ghc" ++ show x ++ "_"
+ let
+ mkTempDir :: Integer -> IO FilePath
mkTempDir x
= let dirname = prefix ++ show x
in do createDirectory dirname
@@ -613,17 +704,18 @@ runSomething :: DynFlags
-> IO ()
runSomething dflags phase_name pgm args =
- runSomethingFiltered dflags id phase_name pgm args
+ runSomethingFiltered dflags id phase_name pgm args Nothing
runSomethingFiltered
- :: DynFlags -> (String->String) -> String -> String -> [Option] -> IO ()
+ :: DynFlags -> (String->String) -> String -> String -> [Option]
+ -> Maybe [(String,String)] -> IO ()
-runSomethingFiltered dflags filter_fn phase_name pgm args = do
+runSomethingFiltered dflags filter_fn phase_name pgm args mb_env = do
let real_args = filter notNull (map showOpt args)
traceCmd dflags phase_name (unwords (pgm:real_args)) $ do
(exit_code, doesn'tExist) <-
IO.catch (do
- rc <- builderMainLoop dflags filter_fn pgm real_args
+ rc <- builderMainLoop dflags filter_fn pgm real_args mb_env
case rc of
ExitSuccess{} -> return (rc, False)
ExitFailure n
@@ -655,19 +747,23 @@ runSomethingFiltered dflags filter_fn phase_name pgm args = do
#if __GLASGOW_HASKELL__ < 603
-builderMainLoop dflags filter_fn pgm real_args = do
+builderMainLoop dflags filter_fn pgm real_args mb_env = do
rawSystem pgm real_args
#else
-builderMainLoop dflags filter_fn pgm real_args = do
+builderMainLoop dflags filter_fn pgm real_args mb_env = do
chan <- newChan
- (hStdIn, hStdOut, hStdErr, hProcess) <- runInteractiveProcess pgm real_args Nothing Nothing
+ (hStdIn, hStdOut, hStdErr, hProcess) <- runInteractiveProcess pgm real_args Nothing mb_env
-- and run a loop piping the output from the compiler to the log_action in DynFlags
hSetBuffering hStdOut LineBuffering
hSetBuffering hStdErr LineBuffering
forkIO (readerProc chan hStdOut filter_fn)
forkIO (readerProc chan hStdErr filter_fn)
- rc <- loop chan hProcess 2 1 ExitSuccess
+ -- we don't want to finish until 2 streams have been completed
+ -- (stdout and stderr)
+ -- nor until 1 exit code has been retrieved.
+ rc <- loop chan hProcess (2::Integer) (1::Integer) ExitSuccess
+ -- after that, we're done here.
hClose hStdIn
hClose hStdOut
hClose hStdErr
@@ -722,23 +818,41 @@ readerProc chan hdl filter_fn =
checkError l ls
checkError l ls
- = case matchRegex errRegex l of
+ = case parseError l of
Nothing -> do
writeChan chan (BuildMsg (text l))
loop ls Nothing
- Just (file':lineno':colno':msg:_) -> do
- let file = mkFastString file'
- lineno = read lineno'::Int
- colno = case colno' of
- "" -> 0
- _ -> read (init colno') :: Int
- srcLoc = mkSrcLoc file lineno colno
+ Just (file, lineNum, colNum, msg) -> do
+ let srcLoc = mkSrcLoc (mkFastString file) lineNum colNum
loop ls (Just (BuildError srcLoc (text msg)))
leading_whitespace [] = False
leading_whitespace (x:_) = isSpace x
-errRegex = mkRegex "^([^:]*):([0-9]+):([0-9]+:)?(.*)"
+parseError :: String -> Maybe (String, Int, Int, String)
+parseError s0 = case breakColon s0 of
+ Just (filename, s1) ->
+ case breakIntColon s1 of
+ Just (lineNum, s2) ->
+ case breakIntColon s2 of
+ Just (columnNum, s3) ->
+ Just (filename, lineNum, columnNum, s3)
+ Nothing ->
+ Just (filename, lineNum, 0, s2)
+ Nothing -> Nothing
+ Nothing -> Nothing
+
+breakColon :: String -> Maybe (String, String)
+breakColon xs = case break (':' ==) xs of
+ (ys, _:zs) -> Just (ys, zs)
+ _ -> Nothing
+
+breakIntColon :: String -> Maybe (Int, String)
+breakIntColon xs = case break (':' ==) xs of
+ (ys, _:zs)
+ | not (null ys) && all isAscii ys && all isDigit ys ->
+ Just (read ys, zs)
+ _ -> Nothing
data BuildMessage
= BuildMsg !SDoc
@@ -747,7 +861,6 @@ data BuildMessage
#endif
showOpt (FileOption pre f) = pre ++ platformPath f
-showOpt (Option "") = ""
showOpt (Option s) = s
traceCmd :: DynFlags -> String -> String -> IO () -> IO ()
@@ -803,12 +916,9 @@ getBaseDir = return Nothing
#ifdef mingw32_HOST_OS
foreign import ccall unsafe "_getpid" getProcessID :: IO Int -- relies on Int == Int32 on Windows
-#elif __GLASGOW_HASKELL__ > 504
-getProcessID :: IO Int
-getProcessID = System.Posix.Internals.c_getpid >>= return . fromIntegral
#else
getProcessID :: IO Int
-getProcessID = Posix.getProcessID
+getProcessID = System.Posix.Internals.c_getpid >>= return . fromIntegral
#endif
-- Divvy up text stream into lines, taking platform dependent