b291fd2abbc591a39dd54c01756b321450addf1c
[ghc-hetmet.git] / utils / hsc2hs / Main.hs
1 {-# LANGUAGE CPP, ForeignFunctionInterface #-}
2
3 ------------------------------------------------------------------------
4 -- Program for converting .hsc files to .hs files, by converting the
5 -- file into a C program which is run to generate the Haskell source.
6 -- Certain items known only to the C compiler can then be used in
7 -- the Haskell module; for example #defined constants, byte offsets
8 -- within structures, etc.
9 --
10 -- See the documentation in the Users' Guide for more details.
11
12 #if defined(__GLASGOW_HASKELL__) && !defined(BUILD_NHC)
13 #include "../../includes/ghcconfig.h"
14 #endif
15
16 #if __GLASGOW_HASKELL__ >= 504 || __NHC__ >= 114
17 import System.Console.GetOpt
18 #else
19 import GetOpt
20 #endif
21
22 import System        (getProgName, getArgs, ExitCode(..), exitWith)
23 import Directory     (removeFile,doesFileExist)
24 import Monad         (MonadPlus(..), liftM, liftM2, when)
25 import Char          (isAlpha, isAlphaNum, isSpace, isDigit, toUpper, intToDigit, ord)
26 import List          (intersperse, isSuffixOf)
27 import IO            (hPutStr, hPutStrLn, stderr, bracket_)
28
29 #if defined(mingw32_HOST_OS)
30 import Foreign
31 #if __GLASGOW_HASKELL__ >= 504 || __NHC__ >= 114
32 import Foreign.C.String
33 #else
34 import CString
35 #endif
36 #endif
37
38 #if __GLASGOW_HASKELL__ >= 604
39 import System.Process           ( runProcess, waitForProcess )
40 import System.IO                ( openFile, IOMode(..), hClose )
41 #define HAVE_runProcess
42 #endif
43
44 #if defined(__GLASGOW_HASKELL__) && !defined(BUILD_NHC)
45 import System.Cmd       ( rawSystem )
46 #define HAVE_rawSystem
47 #elif __NHC__ >= 117
48 import System.Cmd               ( rawSystem )
49 #define HAVE_rawSystem
50 #endif
51
52 #if !defined(HAVE_runProcess) || !defined(HAVE_rawSystem)
53 -- we need system
54 #if __NHC__ >= 117 || __GLASGOW_HASKELL__ >= 600
55 import System.Cmd               ( system )
56 #else
57 import System                   ( system )
58 #endif
59 #endif
60
61 version :: String
62 version = "hsc2hs version 0.66\n"
63
64 data Flag
65     = Help
66     | Version
67     | Template  String
68     | Compiler  String
69     | Linker    String
70     | CompFlag  String
71     | LinkFlag  String
72     | NoCompile
73     | Include   String
74     | Define    String (Maybe String)
75     | Output    String
76     | Verbose
77
78 template_flag :: Flag -> Bool
79 template_flag (Template _) = True
80 template_flag _            = False
81
82 include :: String -> Flag
83 include s@('\"':_) = Include s
84 include s@('<' :_) = Include s
85 include s          = Include ("\""++s++"\"")
86
87 define :: String -> Flag
88 define s = case break (== '=') s of
89     (name, [])      -> Define name Nothing
90     (name, _:value) -> Define name (Just value)
91
92 options :: [OptDescr Flag]
93 options = [
94     Option ['o'] ["output"]     (ReqArg Output     "FILE")
95         "name of main output file",
96     Option ['t'] ["template"]   (ReqArg Template   "FILE")
97         "template file",
98     Option ['c'] ["cc"]         (ReqArg Compiler   "PROG")
99         "C compiler to use",
100     Option ['l'] ["ld"]         (ReqArg Linker     "PROG")
101         "linker to use",
102     Option ['C'] ["cflag"]      (ReqArg CompFlag   "FLAG")
103         "flag to pass to the C compiler",
104     Option ['I'] []             (ReqArg (CompFlag . ("-I"++)) "DIR")
105         "passed to the C compiler",
106     Option ['L'] ["lflag"]      (ReqArg LinkFlag   "FLAG")
107         "flag to pass to the linker",
108     Option ['i'] ["include"]    (ReqArg include    "FILE")
109         "as if placed in the source",
110     Option ['D'] ["define"]     (ReqArg define "NAME[=VALUE]")
111         "as if placed in the source",
112     Option []    ["no-compile"] (NoArg  NoCompile)
113         "stop after writing *_hsc_make.c",
114     Option ['v'] ["verbose"]    (NoArg  Verbose)
115         "dump commands to stderr",
116     Option ['?'] ["help"]       (NoArg  Help)
117         "display this help and exit",
118     Option ['V'] ["version"]    (NoArg  Version)
119         "output version information and exit" ]
120
121 main :: IO ()
122 main = do
123     prog <- getProgramName
124     let header = "Usage: "++prog++" [OPTIONS] INPUT.hsc [...]\n"
125     args <- getArgs
126     let (flags, files, errs) = getOpt Permute options args
127
128         -- If there is no Template flag explicitly specified, try
129         -- to find one by looking near the executable.  This only
130         -- works on Win32 or Hugs (getExecDir). On Unix, there's a wrapper
131         -- script which specifies an explicit template flag.
132     flags_w_tpl0 <- if any template_flag flags then
133                         return flags
134                    else
135                         do mb_path <- getExecDir "/bin/hsc2hs.exe"
136                            add_opt <-
137                             case mb_path of
138                               Nothing   -> return id
139                               Just path -> do
140                                 let templ = path ++ "/template-hsc.h"
141                                 flg <- doesFileExist templ
142                                 if flg
143                                  then return ((Template templ):)
144                                  else return id
145                            return (add_opt flags)
146
147     -- take only the last --template flag on the cmd line
148     let
149       (before,tpl:after) = break template_flag (reverse flags_w_tpl0)
150       flags_w_tpl = reverse (before ++ tpl : filter (not.template_flag) after)
151
152     case (files, errs) of
153         (_, _)
154             | any isHelp    flags_w_tpl -> bye (usageInfo header options)
155             | any isVersion flags_w_tpl -> bye version
156             where
157             isHelp    Help    = True; isHelp    _ = False
158             isVersion Version = True; isVersion _ = False
159         ((_:_), []) -> mapM_ (processFile flags_w_tpl) files
160         (_,     _ ) -> die (concat errs ++ usageInfo header options)
161
162 getProgramName :: IO String
163 getProgramName = liftM (`withoutSuffix` "-bin") getProgName
164    where str `withoutSuffix` suff
165             | suff `isSuffixOf` str = take (length str - length suff) str
166             | otherwise             = str
167
168 bye :: String -> IO a
169 bye s = putStr s >> exitWith ExitSuccess
170
171 die :: String -> IO a
172 die s = hPutStr stderr s >> exitWith (ExitFailure 1)
173
174 processFile :: [Flag] -> String -> IO ()
175 processFile flags name
176   = do let file_name = dosifyPath name
177        s <- readFile file_name
178        case parser of
179            Parser p -> case p (SourcePos file_name 1) s of
180                Success _ _ _ toks -> output flags file_name toks
181                Failure (SourcePos name' line) msg ->
182                    die (name'++":"++show line++": "++msg++"\n")
183
184 ------------------------------------------------------------------------
185 -- A deterministic parser which remembers the text which has been parsed.
186
187 newtype Parser a = Parser (SourcePos -> String -> ParseResult a)
188
189 data ParseResult a = Success !SourcePos String String a
190                    | Failure !SourcePos String
191
192 data SourcePos = SourcePos String !Int
193
194 updatePos :: SourcePos -> Char -> SourcePos
195 updatePos pos@(SourcePos name line) ch = case ch of
196     '\n' -> SourcePos name (line + 1)
197     _    -> pos
198
199 instance Monad Parser where
200     return a = Parser $ \pos s -> Success pos [] s a
201     Parser m >>= k =
202         Parser $ \pos s -> case m pos s of
203             Success pos' out1 s' a -> case k a of
204                 Parser k' -> case k' pos' s' of
205                     Success pos'' out2 imp'' b ->
206                         Success pos'' (out1++out2) imp'' b
207                     Failure pos'' msg -> Failure pos'' msg
208             Failure pos' msg -> Failure pos' msg
209     fail msg = Parser $ \pos _ -> Failure pos msg
210
211 instance MonadPlus Parser where
212     mzero                     = fail "mzero"
213     Parser m `mplus` Parser n =
214         Parser $ \pos s -> case m pos s of
215             success@(Success _ _ _ _) -> success
216             Failure _ _               -> n pos s
217
218 getPos :: Parser SourcePos
219 getPos = Parser $ \pos s -> Success pos [] s pos
220
221 setPos :: SourcePos -> Parser ()
222 setPos pos = Parser $ \_ s -> Success pos [] s ()
223
224 message :: Parser a -> String -> Parser a
225 Parser m `message` msg =
226     Parser $ \pos s -> case m pos s of
227         success@(Success _ _ _ _) -> success
228         Failure pos' _            -> Failure pos' msg
229
230 catchOutput_ :: Parser a -> Parser String
231 catchOutput_ (Parser m) =
232     Parser $ \pos s -> case m pos s of
233         Success pos' out s' _ -> Success pos' [] s' out
234         Failure pos' msg      -> Failure pos' msg
235
236 fakeOutput :: Parser a -> String -> Parser a
237 Parser m `fakeOutput` out =
238     Parser $ \pos s -> case m pos s of
239         Success pos' _ s' a -> Success pos' out s' a
240         Failure pos' msg    -> Failure pos' msg
241
242 lookAhead :: Parser String
243 lookAhead = Parser $ \pos s -> Success pos [] s s
244
245 satisfy :: (Char -> Bool) -> Parser Char
246 satisfy p =
247     Parser $ \pos s -> case s of
248         c:cs | p c -> Success (updatePos pos c) [c] cs c
249         _          -> Failure pos "Bad character"
250
251 char_ :: Char -> Parser ()
252 char_ c = do
253     satisfy (== c) `message` (show c++" expected")
254     return ()
255
256 anyChar_ :: Parser ()
257 anyChar_ = do
258     satisfy (const True) `message` "Unexpected end of file"
259     return ()
260
261 any2Chars_ :: Parser ()
262 any2Chars_ = anyChar_ >> anyChar_
263
264 many :: Parser a -> Parser [a]
265 many p = many1 p `mplus` return []
266
267 many1 :: Parser a -> Parser [a]
268 many1 p = liftM2 (:) p (many p)
269
270 many_ :: Parser a -> Parser ()
271 many_ p = many1_ p `mplus` return ()
272
273 many1_ :: Parser a -> Parser ()
274 many1_ p = p >> many_ p
275
276 manySatisfy, manySatisfy1 :: (Char -> Bool) -> Parser String
277 manySatisfy  = many  . satisfy
278 manySatisfy1 = many1 . satisfy
279
280 manySatisfy_, manySatisfy1_ :: (Char -> Bool) -> Parser ()
281 manySatisfy_  = many_  . satisfy
282 manySatisfy1_ = many1_ . satisfy
283
284 ------------------------------------------------------------------------
285 -- Parser of hsc syntax.
286
287 data Token
288     = Text    SourcePos String
289     | Special SourcePos String String
290
291 parser :: Parser [Token]
292 parser = do
293     pos <- getPos
294     t <- catchOutput_ text
295     s <- lookAhead
296     rest <- case s of
297         []  -> return []
298         _:_ -> liftM2 (:) (special `fakeOutput` []) parser
299     return (if null t then rest else Text pos t : rest)
300
301 text :: Parser ()
302 text = do
303     s <- lookAhead
304     case s of
305         []        -> return ()
306         c:_ | isAlpha c || c == '_' -> do
307             anyChar_
308             manySatisfy_ (\c' -> isAlphaNum c' || c' == '_' || c' == '\'')
309             text
310         c:_ | isHsSymbol c -> do
311             symb <- catchOutput_ (manySatisfy_ isHsSymbol)
312             case symb of
313                 "#" -> return ()
314                 '-':'-':symb' | all (== '-') symb' -> do
315                     return () `fakeOutput` symb
316                     manySatisfy_ (/= '\n')
317                     text
318                 _ -> do
319                     return () `fakeOutput` unescapeHashes symb
320                     text
321         '\"':_    -> do anyChar_; hsString '\"'; text
322         '\'':_    -> do anyChar_; hsString '\''; text
323         '{':'-':_ -> do any2Chars_; linePragma `mplus` hsComment; text
324         _:_       -> do anyChar_; text
325
326 hsString :: Char -> Parser ()
327 hsString quote = do
328     s <- lookAhead
329     case s of
330         []               -> return ()
331         c:_ | c == quote -> anyChar_
332         '\\':c:_
333             | isSpace c  -> do
334                 anyChar_
335                 manySatisfy_ isSpace
336                 char_ '\\' `mplus` return ()
337                 hsString quote
338             | otherwise  -> do any2Chars_; hsString quote
339         _:_              -> do anyChar_; hsString quote
340
341 hsComment :: Parser ()
342 hsComment = do
343     s <- lookAhead
344     case s of
345         []        -> return ()
346         '-':'}':_ -> any2Chars_
347         '{':'-':_ -> do any2Chars_; hsComment; hsComment
348         _:_       -> do anyChar_; hsComment
349
350 linePragma :: Parser ()
351 linePragma = do
352     char_ '#'
353     manySatisfy_ isSpace
354     satisfy (\c -> c == 'L' || c == 'l')
355     satisfy (\c -> c == 'I' || c == 'i')
356     satisfy (\c -> c == 'N' || c == 'n')
357     satisfy (\c -> c == 'E' || c == 'e')
358     manySatisfy1_ isSpace
359     line <- liftM read $ manySatisfy1 isDigit
360     manySatisfy1_ isSpace
361     char_ '\"'
362     name <- manySatisfy (/= '\"')
363     char_ '\"'
364     manySatisfy_ isSpace
365     char_ '#'
366     char_ '-'
367     char_ '}'
368     setPos (SourcePos name (line - 1))
369
370 isHsSymbol :: Char -> Bool
371 isHsSymbol '!' = True; isHsSymbol '#' = True; isHsSymbol '$'  = True
372 isHsSymbol '%' = True; isHsSymbol '&' = True; isHsSymbol '*'  = True
373 isHsSymbol '+' = True; isHsSymbol '.' = True; isHsSymbol '/'  = True
374 isHsSymbol '<' = True; isHsSymbol '=' = True; isHsSymbol '>'  = True
375 isHsSymbol '?' = True; isHsSymbol '@' = True; isHsSymbol '\\' = True
376 isHsSymbol '^' = True; isHsSymbol '|' = True; isHsSymbol '-'  = True
377 isHsSymbol '~' = True
378 isHsSymbol _   = False
379
380 unescapeHashes :: String -> String
381 unescapeHashes []          = []
382 unescapeHashes ('#':'#':s) = '#' : unescapeHashes s
383 unescapeHashes (c:s)       = c   : unescapeHashes s
384
385 lookAheadC :: Parser String
386 lookAheadC = liftM joinLines lookAhead
387     where
388     joinLines []            = []
389     joinLines ('\\':'\n':s) = joinLines s
390     joinLines (c:s)         = c : joinLines s
391
392 satisfyC :: (Char -> Bool) -> Parser Char
393 satisfyC p = do
394     s <- lookAhead
395     case s of
396         '\\':'\n':_ -> do any2Chars_ `fakeOutput` []; satisfyC p
397         _           -> satisfy p
398
399 charC_ :: Char -> Parser ()
400 charC_ c = do
401     satisfyC (== c) `message` (show c++" expected")
402     return ()
403
404 anyCharC_ :: Parser ()
405 anyCharC_ = do
406     satisfyC (const True) `message` "Unexpected end of file"
407     return ()
408
409 any2CharsC_ :: Parser ()
410 any2CharsC_ = anyCharC_ >> anyCharC_
411
412 manySatisfyC :: (Char -> Bool) -> Parser String
413 manySatisfyC = many . satisfyC
414
415 manySatisfyC_ :: (Char -> Bool) -> Parser ()
416 manySatisfyC_ = many_ . satisfyC
417
418 special :: Parser Token
419 special = do
420     manySatisfyC_ (\c -> isSpace c && c /= '\n')
421     s <- lookAheadC
422     case s of
423         '{':_ -> do
424             anyCharC_
425             manySatisfyC_ isSpace
426             sp <- keyArg (== '\n')
427             charC_ '}'
428             return sp
429         _ -> keyArg (const False)
430
431 keyArg :: (Char -> Bool) -> Parser Token
432 keyArg eol = do
433     pos <- getPos
434     key <- keyword `message` "hsc keyword or '{' expected"
435     manySatisfyC_ (\c' -> isSpace c' && c' /= '\n' || eol c')
436     arg <- catchOutput_ (argument eol)
437     return (Special pos key arg)
438
439 keyword :: Parser String
440 keyword = do
441     c  <- satisfyC (\c' -> isAlpha c' || c' == '_')
442     cs <- manySatisfyC (\c' -> isAlphaNum c' || c' == '_')
443     return (c:cs)
444
445 argument :: (Char -> Bool) -> Parser ()
446 argument eol = do
447     s <- lookAheadC
448     case s of
449         []          -> return ()
450         c:_ | eol c -> do anyCharC_;               argument eol
451         '\n':_      -> return ()
452         '\"':_      -> do anyCharC_; cString '\"'; argument eol
453         '\'':_      -> do anyCharC_; cString '\''; argument eol
454         '(':_       -> do anyCharC_; nested ')';   argument eol
455         ')':_       -> return ()
456         '/':'*':_   -> do any2CharsC_; cComment;   argument eol
457         '/':'/':_   -> do
458             any2CharsC_; manySatisfyC_ (/= '\n');  argument eol
459         '[':_       -> do anyCharC_; nested ']';   argument eol
460         ']':_       -> return ()
461         '{':_       -> do anyCharC_; nested '}';   argument eol
462         '}':_       -> return ()
463         _:_         -> do anyCharC_;               argument eol
464
465 nested :: Char -> Parser ()
466 nested c = do argument (== '\n'); charC_ c
467
468 cComment :: Parser ()
469 cComment = do
470     s <- lookAheadC
471     case s of
472         []        -> return ()
473         '*':'/':_ -> do any2CharsC_
474         _:_       -> do anyCharC_; cComment
475
476 cString :: Char -> Parser ()
477 cString quote = do
478     s <- lookAheadC
479     case s of
480         []               -> return ()
481         c:_ | c == quote -> anyCharC_
482         '\\':_:_         -> do any2CharsC_; cString quote
483         _:_              -> do anyCharC_; cString quote
484
485 ------------------------------------------------------------------------
486 -- Write the output files.
487
488 splitName :: String -> (String, String)
489 splitName name =
490     case break (== '/') name of
491         (file, [])       -> ([], file)
492         (dir,  sep:rest) -> (dir++sep:restDir, restFile)
493             where
494             (restDir, restFile) = splitName rest
495
496 splitExt :: String -> (String, String)
497 splitExt name =
498     case break (== '.') name of
499         (base, [])         -> (base, [])
500         (base, sepRest@(sep:rest))
501             | null restExt -> (base,               sepRest)
502             | otherwise    -> (base++sep:restBase, restExt)
503             where
504             (restBase, restExt) = splitExt rest
505
506 output :: [Flag] -> String -> [Token] -> IO ()
507 output flags name toks = do
508
509     (outName, outDir, outBase) <- case [f | Output f <- flags] of
510         [] -> if not (null ext) && last ext == 'c'
511                  then return (dir++base++init ext,  dir, base)
512                  else
513                     if ext == ".hs"
514                        then return (dir++base++"_out.hs", dir, base)
515                        else return (dir++base++".hs",     dir, base)
516               where
517                (dir,  file) = splitName name
518                (base, ext)  = splitExt  file
519         [f] -> let
520             (dir,  file) = splitName f
521             (base, _)    = splitExt file
522             in return (f, dir, base)
523         _ -> onlyOne "output file"
524
525     let cProgName    = outDir++outBase++"_hsc_make.c"
526         oProgName    = outDir++outBase++"_hsc_make.o"
527         progName     = outDir++outBase++"_hsc_make"
528 #if defined(mingw32_HOST_OS) || defined(__CYGWIN32__)
529 -- This is a real hack, but the quoting mechanism used for calling the C preprocesseor
530 -- via GHC has changed a few times, so this seems to be the only way...  :-P * * *
531                           ++ ".exe"
532 #endif
533         outHFile     = outBase++"_hsc.h"
534         outHName     = outDir++outHFile
535         outCName     = outDir++outBase++"_hsc.c"
536
537         beVerbose    = any (\ x -> case x of { Verbose -> True; _ -> False}) flags
538
539     let execProgName
540             | null outDir = dosifyPath ("./" ++ progName)
541             | otherwise   = progName
542
543     let specials = [(pos, key, arg) | Special pos key arg <- toks]
544
545     let needsC = any (\(_, key, _) -> key == "def") specials
546         needsH = needsC
547
548     let includeGuard = map fixChar outHName
549             where
550             fixChar c | isAlphaNum c = toUpper c
551                       | otherwise    = '_'
552
553         -- Try locating GHC..on Win32, look in the vicinity of hsc2hs.
554         -- Returns a native-format path
555         locateGhc def = do
556             mb <- getExecDir "bin/hsc2hs.exe"
557             case mb of
558               Nothing -> return def
559               Just x  -> do
560                  let ghc_path = dosifyPath (x ++ "bin/ghc.exe")
561                  flg <- doesFileExist ghc_path
562                  if flg
563                   then return ghc_path
564                   else return def
565
566         -- On a Win32 installation we execute the hsc2hs binary directly,
567         -- with no --cc flags, so we'll call locateGhc here, which will
568         -- succeed, via getExecDir.
569         --
570         -- On a Unix installation, we'll run the wrapper script hsc2hs.sh
571         -- (called plain hsc2hs in the installed tree), which will pass
572         -- a suitable C compiler via --cc
573         --
574         -- The in-place installation always uses the wrapper script,
575         -- (called hsc2hs-inplace, generated from hsc2hs.sh)
576     compiler <- case [c | Compiler c <- flags] of
577         []  -> locateGhc "ghc"
578         cs  -> return (last cs)
579
580     linker <- case [l | Linker l <- flags] of
581         []  -> locateGhc compiler
582         ls  -> return (last ls)
583
584     writeFile cProgName $
585         concatMap outFlagHeaderCProg flags++
586         concatMap outHeaderCProg specials++
587         "\nint main (int argc, char *argv [])\n{\n"++
588         outHeaderHs flags (if needsH then Just outHName else Nothing) specials++
589         outHsLine (SourcePos name 0)++
590         concatMap outTokenHs toks++
591         "    return 0;\n}\n"
592
593     -- NOTE: hbc compiles "[() | NoCompile <- flags]" into wrong code,
594     -- so we use something slightly more complicated.   :-P
595     when (any (\x -> case x of NoCompile -> True; _ -> False) flags) $
596        exitWith ExitSuccess
597
598     rawSystemL ("compiling " ++ cProgName) beVerbose compiler
599         (  ["-c"]
600         ++ [f | CompFlag f <- flags]
601         ++ [cProgName]
602         ++ ["-o", oProgName]
603         )
604     finallyRemove cProgName $ do
605
606     rawSystemL ("linking " ++ oProgName) beVerbose linker
607         (  [f | LinkFlag f <- flags]
608         ++ [oProgName]
609         ++ ["-o", progName]
610         )
611     finallyRemove oProgName $ do
612
613     rawSystemWithStdOutL ("running " ++ execProgName) beVerbose execProgName [] outName
614     finallyRemove progName $ do
615
616     when needsH $ writeFile outHName $
617         "#ifndef "++includeGuard++"\n" ++
618         "#define "++includeGuard++"\n" ++
619         "#if __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ < 409\n" ++
620         "#include <Rts.h>\n" ++
621         "#endif\n" ++
622         "#include <HsFFI.h>\n" ++
623         "#if __NHC__\n" ++
624         "#undef HsChar\n" ++
625         "#define HsChar int\n" ++
626         "#endif\n" ++
627         concatMap outFlagH flags++
628         concatMap outTokenH specials++
629         "#endif\n"
630
631     when needsC $ writeFile outCName $
632         "#include \""++outHFile++"\"\n"++
633         concatMap outTokenC specials
634         -- NB. outHFile not outHName; works better when processed
635         -- by gcc or mkdependC.
636
637 rawSystemL :: String -> Bool -> FilePath -> [String] -> IO ()
638 rawSystemL action flg prog args = do
639   let cmdLine = prog++" "++unwords args
640   when flg $ hPutStrLn stderr ("Executing: " ++ cmdLine)
641 #ifndef HAVE_rawSystem
642   exitStatus <- system cmdLine
643 #else
644   exitStatus <- rawSystem prog args
645 #endif
646   case exitStatus of
647     ExitFailure _ -> die $ action ++ " failed\ncommand was: " ++ cmdLine ++ "\n"
648     _             -> return ()
649
650 rawSystemWithStdOutL :: String -> Bool -> FilePath -> [String] -> FilePath -> IO ()
651 rawSystemWithStdOutL action flg prog args outFile = do
652   let cmdLine = prog++" "++unwords args++" >"++outFile
653   when flg (hPutStrLn stderr ("Executing: " ++ cmdLine))
654 #ifndef HAVE_runProcess
655   exitStatus <- system cmdLine
656 #else
657   hOut <- openFile outFile WriteMode
658   process <- runProcess prog args Nothing Nothing Nothing (Just hOut) Nothing
659   exitStatus <- waitForProcess process
660   hClose hOut
661 #endif
662   case exitStatus of
663     ExitFailure _ -> die $ action ++ " failed\ncommand was: " ++ cmdLine ++ "\n"
664     _             -> return ()
665
666
667 -- delay the cleanup of generated files until the end; attempts to
668 -- get around intermittent failure to delete files which has
669 -- just been exec'ed by a sub-process (Win32 only.)
670 finallyRemove :: FilePath -> IO a -> IO a
671 finallyRemove fp act = 
672   bracket_ (return fp)
673            (const $ noisyRemove fp)
674            act
675  where
676   noisyRemove fpath =
677     catch (removeFile fpath)
678           (\ e -> hPutStrLn stderr ("Failed to remove file " ++ fpath ++ "; error= " ++ show e))
679 onlyOne :: String -> IO a
680 onlyOne what = die ("Only one "++what++" may be specified\n")
681
682 outFlagHeaderCProg :: Flag -> String
683 outFlagHeaderCProg (Template t)          = "#include \""++t++"\"\n"
684 outFlagHeaderCProg (Include  f)          = "#include "++f++"\n"
685 outFlagHeaderCProg (Define   n Nothing)  = "#define "++n++" 1\n"
686 outFlagHeaderCProg (Define   n (Just v)) = "#define "++n++" "++v++"\n"
687 outFlagHeaderCProg _                     = ""
688
689 outHeaderCProg :: (SourcePos, String, String) -> String
690 outHeaderCProg (pos, key, arg) = case key of
691     "include"           -> outCLine pos++"#include "++arg++"\n"
692     "define"            -> outCLine pos++"#define "++arg++"\n"
693     "undef"             -> outCLine pos++"#undef "++arg++"\n"
694     "def"               -> case arg of
695         's':'t':'r':'u':'c':'t':' ':_ -> outCLine pos++arg++"\n"
696         't':'y':'p':'e':'d':'e':'f':' ':_ -> outCLine pos++arg++"\n"
697         _ -> ""
698     _ | conditional key -> outCLine pos++"#"++key++" "++arg++"\n"
699     "let"               -> case break (== '=') arg of
700         (_,      "")     -> ""
701         (header, _:body) -> case break isSpace header of
702             (name, args) ->
703                 outCLine pos++
704                 "#define hsc_"++name++"("++dropWhile isSpace args++") " ++
705                 "printf ("++joinLines body++");\n"
706     _ -> ""
707    where
708     joinLines = concat . intersperse " \\\n" . lines
709
710 outHeaderHs :: [Flag] -> Maybe String -> [(SourcePos, String, String)] -> String
711 outHeaderHs flags inH toks =
712     "#if " ++
713     "__GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ < 409\n" ++
714     "    printf (\"{-# OPTIONS -optc-D" ++
715     "__GLASGOW_HASKELL__=%d #-}\\n\", " ++
716     "__GLASGOW_HASKELL__);\n" ++
717     "#endif\n"++
718     case inH of
719         Nothing -> concatMap outFlag flags++concatMap outSpecial toks
720         Just f  -> outInclude ("\""++f++"\"")
721     where
722     outFlag (Include f)          = outInclude f
723     outFlag (Define  n Nothing)  = outOption ("-optc-D"++n)
724     outFlag (Define  n (Just v)) = outOption ("-optc-D"++n++"="++v)
725     outFlag _                    = ""
726     outSpecial (pos, key, arg) = case key of
727         "include"                  -> outInclude arg
728         "define" | goodForOptD arg -> outOption ("-optc-D"++toOptD arg)
729                  | otherwise       -> ""
730         _ | conditional key        -> outCLine pos++"#"++key++" "++arg++"\n"
731         _                          -> ""
732     goodForOptD arg = case arg of
733         ""              -> True
734         c:_ | isSpace c -> True
735         '(':_           -> False
736         _:s             -> goodForOptD s
737     toOptD arg = case break isSpace arg of
738         (name, "")      -> name
739         (name, _:value) -> name++'=':dropWhile isSpace value
740     outOption s =
741         "#if __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ < 603\n" ++
742         "    printf (\"{-# OPTIONS %s #-}\\n\", \""++
743                   showCString s++"\");\n"++
744         "#else\n"++
745         "    printf (\"{-# OPTIONS_GHC %s #-}\\n\", \""++
746                   showCString s++"\");\n"++
747         "#endif\n"
748     outInclude s =
749         "#if __GLASGOW_HASKELL__ && __GLASGOW_HASKELL__ < 603\n" ++
750         "    printf (\"{-# OPTIONS -#include %s #-}\\n\", \""++
751                   showCString s++"\");\n"++
752         "#else\n"++
753         "    printf (\"{-# INCLUDE %s #-}\\n\", \""++
754                   showCString s++"\");\n"++
755         "#endif\n"
756
757 outTokenHs :: Token -> String
758 outTokenHs (Text pos txt) =
759     case break (== '\n') txt of
760         (allTxt, [])       -> outText allTxt
761         (first, _:rest) ->
762             outText (first++"\n")++
763             outHsLine pos++
764             outText rest
765     where
766     outText s = "    fputs (\""++showCString s++"\", stdout);\n"
767 outTokenHs (Special pos key arg) =
768     case key of
769         "include"           -> ""
770         "define"            -> ""
771         "undef"             -> ""
772         "def"               -> ""
773         _ | conditional key -> outCLine pos++"#"++key++" "++arg++"\n"
774         "let"               -> ""
775         "enum"              -> outCLine pos++outEnum arg
776         _                   -> outCLine pos++"    hsc_"++key++" ("++arg++");\n"
777
778 outEnum :: String -> String
779 outEnum arg =
780     case break (== ',') arg of
781         (_, [])        -> ""
782         (t, _:afterT) -> case break (== ',') afterT of
783             (f, afterF) -> let
784                 enums []    = ""
785                 enums (_:s) = case break (== ',') s of
786                     (enum, rest) -> let
787                         this = case break (== '=') $ dropWhile isSpace enum of
788                             (name, []) ->
789                                 "    hsc_enum ("++t++", "++f++", " ++
790                                 "hsc_haskellize (\""++name++"\"), "++
791                                 name++");\n"
792                             (hsName, _:cName) ->
793                                 "    hsc_enum ("++t++", "++f++", " ++
794                                 "printf (\"%s\", \""++hsName++"\"), "++
795                                 cName++");\n"
796                         in this++enums rest
797                 in enums afterF
798
799 outFlagH :: Flag -> String
800 outFlagH (Include  f)          = "#include "++f++"\n"
801 outFlagH (Define   n Nothing)  = "#define "++n++" 1\n"
802 outFlagH (Define   n (Just v)) = "#define "++n++" "++v++"\n"
803 outFlagH _                     = ""
804
805 outTokenH :: (SourcePos, String, String) -> String
806 outTokenH (pos, key, arg) =
807     case key of
808         "include" -> outCLine pos++"#include "++arg++"\n"
809         "define"  -> outCLine pos++"#define " ++arg++"\n"
810         "undef"   -> outCLine pos++"#undef "  ++arg++"\n"
811         "def"     -> outCLine pos++case arg of
812             's':'t':'r':'u':'c':'t':' ':_ -> arg++"\n"
813             't':'y':'p':'e':'d':'e':'f':' ':_ -> arg++"\n"
814             'i':'n':'l':'i':'n':'e':' ':_ ->
815                 "#ifdef __GNUC__\n" ++
816                 "extern\n" ++
817                 "#endif\n"++
818                 arg++"\n"
819             _ -> "extern "++header++";\n"
820           where header = takeWhile (\c -> c /= '{' && c /= '=') arg
821         _ | conditional key -> outCLine pos++"#"++key++" "++arg++"\n"
822         _ -> ""
823
824 outTokenC :: (SourcePos, String, String) -> String
825 outTokenC (pos, key, arg) =
826     case key of
827         "def" -> case arg of
828             's':'t':'r':'u':'c':'t':' ':_ -> ""
829             't':'y':'p':'e':'d':'e':'f':' ':_ -> ""
830             'i':'n':'l':'i':'n':'e':' ':arg' ->
831                 case span (\c -> c /= '{' && c /= '=') arg' of
832                 (header, body) ->
833                     outCLine pos++
834                     "#ifndef __GNUC__\n" ++
835                     "extern inline\n" ++
836                     "#endif\n"++
837                     header++
838                     "\n#ifndef __GNUC__\n" ++
839                     ";\n" ++
840                     "#else\n"++
841                     body++
842                     "\n#endif\n"
843             _ -> outCLine pos++arg++"\n"
844         _ | conditional key -> outCLine pos++"#"++key++" "++arg++"\n"
845         _ -> ""
846
847 conditional :: String -> Bool
848 conditional "if"      = True
849 conditional "ifdef"   = True
850 conditional "ifndef"  = True
851 conditional "elif"    = True
852 conditional "else"    = True
853 conditional "endif"   = True
854 conditional "error"   = True
855 conditional "warning" = True
856 conditional _         = False
857
858 outCLine :: SourcePos -> String
859 outCLine (SourcePos name line) =
860     "#line "++show line++" \""++showCString (snd (splitName name))++"\"\n"
861
862 outHsLine :: SourcePos -> String
863 outHsLine (SourcePos name line) =
864     "    hsc_line ("++show (line + 1)++", \""++
865     showCString name++"\");\n"
866
867 showCString :: String -> String
868 showCString = concatMap showCChar
869     where
870     showCChar '\"' = "\\\""
871     showCChar '\'' = "\\\'"
872     showCChar '?'  = "\\?"
873     showCChar '\\' = "\\\\"
874     showCChar c | c >= ' ' && c <= '~' = [c]
875     showCChar '\a' = "\\a"
876     showCChar '\b' = "\\b"
877     showCChar '\f' = "\\f"
878     showCChar '\n' = "\\n\"\n           \""
879     showCChar '\r' = "\\r"
880     showCChar '\t' = "\\t"
881     showCChar '\v' = "\\v"
882     showCChar c    = ['\\',
883                       intToDigit (ord c `quot` 64),
884                       intToDigit (ord c `quot` 8 `mod` 8),
885                       intToDigit (ord c          `mod` 8)]
886
887 -----------------------------------------
888 -- Modified version from ghc/compiler/SysTools
889 -- Convert paths foo/baz to foo\baz on Windows
890
891 subst :: Char -> Char -> String -> String
892 #if defined(mingw32_HOST_OS) || defined(__CYGWIN32__)
893 subst a b = map (\x -> if x == a then b else x)
894 #else
895 subst _ _ = id
896 #endif
897
898 dosifyPath :: String -> String
899 dosifyPath = subst '/' '\\'
900
901 -- (getExecDir cmd) returns the directory in which the current
902 --                  executable, which should be called 'cmd', is running
903 -- So if the full path is /a/b/c/d/e, and you pass "d/e" as cmd,
904 -- you'll get "/a/b/c" back as the result
905 getExecDir :: String -> IO (Maybe String)
906 getExecDir cmd =
907     getExecPath >>= maybe (return Nothing) removeCmdSuffix
908     where unDosifyPath = subst '\\' '/'
909           initN n = reverse . drop n . reverse
910           removeCmdSuffix = return . Just . initN (length cmd) . unDosifyPath
911
912 getExecPath :: IO (Maybe String)
913 #if defined(mingw32_HOST_OS)
914 getExecPath =
915      allocaArray len $ \buf -> do
916          ret <- getModuleFileName nullPtr buf len
917          if ret == 0 then return Nothing
918                      else liftM Just $ peekCString buf
919     where len = 2048 -- Plenty, PATH_MAX is 512 under Win32.
920
921 foreign import stdcall unsafe "GetModuleFileNameA"
922     getModuleFileName :: Ptr () -> CString -> Int -> IO Int32
923 #else
924 getExecPath = return Nothing
925 #endif