# Copyright 2005,2008 David Roundy # Redistribution and use in source and binary forms of this file, with or # without modification, are permitted provided that redistributions of # source code must retain the above copyright notice. # TRY_COMPILE_GHC(PROGRAM, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) # ----------- # Compile and link using ghc. AC_DEFUN([TRY_COMPILE_GHC],[ cat << \EOF > conftest.hs [$1] -- this file generated by TRY-COMPILE-GHC EOF rm -f Main.hi Main.o # Convert LDFLAGS and LIBS to the format GHC wants them in GHCLDFLAGS="" for f in $LDFLAGS ; do GHCLDFLAGS="$GHCLDFLAGS -optl$f" done GHCLIBS="" for l in $LIBS ; do GHCLIBS="$GHCLIBS -optl$l" done if AC_TRY_COMMAND($GHC $GHCFLAGS $GHCLDFLAGS -o conftest conftest.hs $GHCLIBS) && test -s conftest then dnl Don't remove the temporary files here, so they can be examined. ifelse([$2], , :, [$2]) else echo "configure: failed program was:" >&AS_MESSAGE_LOG_FD cat conftest.hs >&AS_MESSAGE_LOG_FD echo "end of failed program." >&AS_MESSAGE_LOG_FD ifelse([$3], , , [ rm -f Main.hi Main.o $3 ])dnl fi]) # TRY_RUN_GHC(PROGRAM, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) # ----------- # Compile, link and run using ghc. AC_DEFUN([TRY_RUN_GHC],[ TRY_COMPILE_GHC([$1], AS_IF([AC_TRY_COMMAND(./conftest)],[$2],[$3]), [$3]) ]) # GHC_CHECK_ONE_MODULE(MODULE, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # ----------- # Compile and link using ghc. AC_DEFUN([GHC_CHECK_ONE_MODULE],[ TRY_COMPILE_GHC([import $1 main = seq ($2) (putStr "Hello world.\n") ],[$3],[$4]) ]) # GHC_CHECK_MODULE(MODULE, PACKAGE, CODE, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # ----------- # Compile and link using ghc. AC_DEFUN([GHC_CHECK_MODULE],[ AC_MSG_CHECKING([for module $1]) GHC_CHECK_ONE_MODULE([$1], [$3], [AC_MSG_RESULT([yes]) $4], [ check_module_save_GHCFLAGS=$GHCFLAGS GHCFLAGS="$GHCFLAGS -package $2" GHC_CHECK_ONE_MODULE([$1], [$3], [AC_MSG_RESULT([in package $2]) $4],[ GHCFLAGS=$check_module_save_GHCFLAGS AC_MSG_RESULT(no; and neither in package $2) $5]) ]) ]) # GHC_COMPILE_FFI(IMPORT, TYPE, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # ----------- # Compile and link ffi code using ghc. AC_DEFUN([GHC_COMPILE_FFI],[ TRY_COMPILE_GHC([{-# OPTIONS -fffi -Werror #-} module Main where foreign import ccall unsafe "$1" fun :: $2 main = fun `seq` putStrLn "hello world" ],[$3],[$4])]) # GHC_CHECK_LIBRARY(LIBRARY, IMPORT, TYPE, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) # ----------- # Compile and link with C library using ghc. AC_DEFUN([GHC_CHECK_LIBRARY],[ AC_MSG_CHECKING([for library $1]) GHC_COMPILE_FFI([$2], [$3], [AC_MSG_RESULT([yes]) $4], [ check_library_save_LIBS=$LIBS LIBS="$LIBS -l$1" GHC_COMPILE_FFI([$2], [$3], [AC_MSG_RESULT([in -l$1]) $4],[ LIBS=$check_library_save_LIBS AC_MSG_RESULT(no; and not with -l$1 either) $5]) ]) ]) # INIT_WORKAROUND # --------------- # Initialize src/Workaround.hs module. AC_DEFUN([INIT_WORKAROUND],[ rm -f src/Workaround.hs.beginning src/Workaround.hs.ending touch src/Workaround.hs.ending cat << \EOF > src/Workaround.hs.prefix {-# OPTIONS -w #-} {- Workaround.hs This file was created automatically by configure. We disable warnings to avoid issues with duplicate imports. -} module Workaround( EOF cat << \EOF > src/Workaround.hs.beginning ) where EOF ]) # OUTPUT_WORKAROUND # ----------------- # Create the src/Workaround.hs module. AC_DEFUN([OUTPUT_WORKAROUND],[ cat src/Workaround.hs.prefix src/Workaround.hs.beginning src/Workaround.hs.ending > src/Workaround.hs rm -f src/Workaround.hs.beginning src/Workaround.hs.ending src/Workaround.hs.prefix ]) # IMPORT_WORKAROUND(CODE) # ----------------------- # Import a module into src/Workaround.hs AC_DEFUN([IMPORT_WORKAROUND],[ cat << \EOF >> src/Workaround.hs.beginning $1 EOF ]) # EXPORT_WORKAROUND(CODE) # ----------------------- # Export from src/Workaround.hs AC_DEFUN([EXPORT_WORKAROUND],[ cat << \EOF >> src/Workaround.hs.prefix $1 EOF ]) # CODE_WORKAROUND(CODE) # --------------------- # Import a module into src/Workaround.hs AC_DEFUN([CODE_WORKAROUND],[ cat << \EOF >> src/Workaround.hs.ending $1 EOF ]) # WORKAROUND_POSIXSIGNALS(IMPORTS) # ----------------------- # Work around missing POSIX signals code. AC_DEFUN([WORKAROUND_POSIXSIGNALS],[ EXPORT_WORKAROUND([$1]) GHC_CHECK_MODULE(System.Posix.Signals($1), unix, undefined, [IMPORT_WORKAROUND([import System.Posix.Signals($1)])], GHC_CHECK_MODULE(Posix($1), util, undefined, [IMPORT_WORKAROUND([import Posix($1)])], [CODE_WORKAROUND([[ -- Dummy implementation of POSIX signals data Handler = Default | Ignore | Catch (IO ()) type Signal = Int installHandler :: Signal -> Handler -> Maybe () -> IO () installHandler _ _ _ = return () raiseSignal :: Signal -> IO () raiseSignal _ = return () sigINT, {- sigKILL, -} sigHUP, {- sigQUIT, -} sigABRT, sigALRM, sigTERM, sigPIPE :: Signal sigINT = 0 -- not used: sigKILL = 0 sigHUP = 0 -- not used: sigQUIT = 0 sigABRT = 0 sigTERM = 0 sigPIPE = 0 sigALRM = 0 -- not used: raiseSignal :: Signal -> IO () -- not used: raiseSignal _ = return () ]])] ) ) ]) # WORKAROUND_bracketOnError # ----------------------- # Work around missing bracketOnError AC_DEFUN([WORKAROUND_bracketOnError],[ EXPORT_WORKAROUND([ bracketOnError, ]) GHC_CHECK_MODULE(Control.Exception( bracketOnError ), base , bracketOnError (return ()) (const $ return ()) (const $ return ()), [IMPORT_WORKAROUND([import Control.Exception( bracketOnError )])], [IMPORT_WORKAROUND([import qualified Control.Exception( catch, throw, block, unblock )]) CODE_WORKAROUND([[ -- | Like bracket, but only performs the final action if there was an -- exception raised by the in-between computation. -- From GHC 6.6 (with twiddling for qualified block, catch, etc) bracketOnError :: IO a -- ^ computation to run first (\"acquire resource\") -> (a -> IO b) -- ^ computation to run last (\"release resource\") -> (a -> IO c) -- ^ computation to run in-between -> IO c -- returns the value from the in-between computation bracketOnError before after thing = Control.Exception.block (do a <- before Control.Exception.catch (Control.Exception.unblock (thing a)) (\e -> do { after a; Control.Exception.throw e }) ) ]]) ]) ]) # WORKAROUND_createLink # ----------------------- # Work around missing POSIX createLink code. AC_DEFUN([WORKAROUND_createLink],[ EXPORT_WORKAROUND([ createLink, ]) GHC_CHECK_MODULE(System.Posix.Files( createLink ), unix, createLink "a" "b", [IMPORT_WORKAROUND([import System.Posix.Files( createLink )])], GHC_CHECK_MODULE(Posix( createLink ), util, createLink "a" "b", [IMPORT_WORKAROUND([import Posix( createLink )])], [CODE_WORKAROUND([[ -- Dummy implementation of createLink. createLink :: FilePath -> FilePath -> IO () createLink _ _ = fail "Dummy create link error should be caught." ]])] ) ) ]) # WORKAROUND_createDirectoryIfMissing # ------------------------------ # Work around missing createDirectoryIfMissing. AC_DEFUN([WORKAROUND_createDirectoryIfMissing],[ EXPORT_WORKAROUND([ createDirectoryIfMissing, ]) AC_MSG_CHECKING([createDirectoryIfMissing]) TRY_COMPILE_GHC([ import System.Directory(createDirectoryIfMissing) main = createDirectoryIfMissing True "" ], [AC_MSG_RESULT([has createDirectoryIfMissing]) IMPORT_WORKAROUND([import System.Directory(createDirectoryIfMissing)])], [AC_MSG_RESULT([doesn't have createDirectoryIfMissing]) IMPORT_WORKAROUND([import System.Directory ( doesDirectoryExist, createDirectory )]) CODE_WORKAROUND([[ createDirectoryIfMissing :: Bool -- ^ Create its parents too? -> FilePath -- ^ The path to the directory you want to make -> IO () createDirectoryIfMissing parents file = do b <- doesDirectoryExist file case (b,parents, file) of (_, _, "") -> return () (True, _, _) -> return () (_, True, _) -> mapM_ (createDirectoryIfMissing False) (tail (pathParents file)) (_, False, _) -> createDirectory file where pathParents :: FilePath -> [FilePath] pathParents p = root'' : map ((++) root') (dropEmptyPath $ inits path') where #ifdef WIN32 (root,path) = case break (== ':') p of (rel, "") -> ("",rel) (drv,_:rel) -> (drv++":",rel) #else (root,path) = ("",p) #endif (root',root'',path') = case path of (c:path'') | isPathSeparator c -> (root++[pathSeparator],root++[pathSeparator],path'') _ -> (root ,root++"." ,path) dropEmptyPath ("":paths) = paths dropEmptyPath paths = paths inits :: String -> [String] inits [] = [""] inits cs = case pre of "." -> inits suf ".." -> map (joinFileName pre) (dropEmptyPath $ inits suf) _ -> "" : map (joinFileName pre) (inits suf) where (pre,suf) = case break isPathSeparator cs of (pre',"") -> (pre', "") (pre',_:suf') -> (pre',suf') isPathSeparator :: Char -> Bool isPathSeparator ch = ch == pathSeparator || ch == '/' pathSeparator :: Char #ifdef WIN32 pathSeparator = '\\' #else pathSeparator = '/' #endif joinFileName :: String -> String -> FilePath joinFileName "" fname = fname joinFileName "." fname = fname joinFileName dir "" = dir joinFileName dir fname | isPathSeparator (last dir) = dir++fname | otherwise = dir++pathSeparator:fname ]]) ] ) ]) # WORKAROUND_getCurrentDirectory # ------------------------------ # Work around getCurrentDirectory that uses '\\' rather than '/'. AC_DEFUN([WORKAROUND_getCurrentDirectory],[ EXPORT_WORKAROUND([ getCurrentDirectory, ]) AC_MSG_CHECKING([getCurrentDirectory]) TRY_RUN_GHC([ import System.Directory(getCurrentDirectory, setCurrentDirectory) main = do setCurrentDirectory "src" d <- getCurrentDirectory case reverse $ take 4 $ reverse d of "/src" -> return () ], [AC_MSG_RESULT([uses /]) IMPORT_WORKAROUND([import System.Directory(getCurrentDirectory)])] GHC_SEPARATOR='/', [AC_MSG_RESULT([uses \\]) IMPORT_WORKAROUND([import qualified System.Directory(getCurrentDirectory)]) GHC_SEPARATOR='\\' CODE_WORKAROUND([[ {- System.Directory.getCurrentDirectory returns a path with backslashes in it under windows, and some of the code gets confused by that, so we override getCurrentDirectory and translates '\\' to '/' -} getCurrentDirectory :: IO FilePath getCurrentDirectory = do d <- System.Directory.getCurrentDirectory return $ map rb d where rb '\\' = '/' rb c = c ]]) ] ) ]) # WORKAROUND_renameFile # ----------------------- # Work around buggy renameFile. AC_DEFUN([WORKAROUND_renameFile],[ EXPORT_WORKAROUND([ renameFile, ]) AC_MSG_CHECKING([renameFile]) TRY_RUN_GHC([ import System.Directory ( renameFile ) main = do writeFile "conftest.data" "orig_data" writeFile "conftest.newdata" "new_data" renameFile "conftest.newdata" "conftest.data" ], [AC_MSG_RESULT([okay]) IMPORT_WORKAROUND([import System.Directory ( renameFile )])], AC_MSG_RESULT([buggy!]) IMPORT_WORKAROUND([import qualified System.Directory( renameFile, removeFile )]) IMPORT_WORKAROUND([import qualified System.IO.Error]) IMPORT_WORKAROUND([import qualified Control.Exception ( block )]) CODE_WORKAROUND([ {- System.Directory.renameFile incorrectly fails when the new file already exists. This code works around that bug at the cost of losing atomic writes. -} renameFile :: FilePath -> FilePath -> IO () renameFile old new = Control.Exception.block $ do System.Directory.removeFile new `System.IO.Error.catch` (\e -> if System.IO.Error.isDoesNotExistError e then return () else System.IO.Error.ioError e) System.Directory.renameFile old new ]) ) ]) # WORKAROUND_fileModes # -------------------- # Figure out how to set unix permissions on a file (or creates a dummy # function for this). AC_DEFUN([WORKAROUND_fileModes],[ EXPORT_WORKAROUND([ fileMode, getFileStatus, setFileMode, ]) GHC_CHECK_MODULE(System.Posix.Files( fileMode, getFileStatus, setFileMode ), unix, getFileStatus "", IMPORT_WORKAROUND([import System.Posix.Files(fileMode,getFileStatus,setFileMode)]), CODE_WORKAROUND([ fileMode :: () -> () fileMode _ = () getFileStatus :: FilePath -> IO () getFileStatus _ = return () setFileMode :: FilePath -> () -> IO () setFileMode _ _ = return () ]) ) ]) # WORKAROUND_executable # -------------------- # Figure out how to make a file executable (or test if it is). AC_DEFUN([WORKAROUND_executable],[ EXPORT_WORKAROUND([ setExecutable, ]) GHC_CHECK_MODULE(System.Posix.Files( fileMode, getFileStatus, setFileMode, setFileCreationMask ), unix, getFileStatus "", IMPORT_WORKAROUND([ import System.Posix.Files (fileMode,getFileStatus, setFileMode, nullFileMode, setFileCreationMask, ownerReadMode, ownerWriteMode, ownerExecuteMode, groupReadMode, groupWriteMode, groupExecuteMode, otherReadMode, otherWriteMode, otherExecuteMode) import Data.Bits ( (.&.), (.|.), complement ) setExecutable :: FilePath -> Bool -> IO () setExecutable f ex = do st <- getFileStatus f umask <- setFileCreationMask 0 setFileCreationMask umask let rw = fileMode st .&. (ownerReadMode .|. ownerWriteMode .|. groupReadMode .|. groupWriteMode .|. otherReadMode .|. otherWriteMode) total = if ex then rw .|. ((ownerExecuteMode .|. groupExecuteMode .|. otherExecuteMode) .&. complement umask) else rw setFileMode f total ]), CODE_WORKAROUND([ setExecutable :: FilePath -> Bool -> IO () setExecutable _ _ = return () ]) ) ]) # WORKAROUND_openFd # ----------------------- # Work around changing openFd function AC_DEFUN([WORKAROUND_openFd],[ EXPORT_WORKAROUND([ openFd, ]) AC_MSG_CHECKING([GHC.Handle.openFd]) TRY_RUN_GHC([ import GHC.Handle ( openFd ) import IO ( IOMode(..)) main = openFd 1 Nothing "stdout" WriteMode True False ], AC_MSG_RESULT([okay]) IMPORT_WORKAROUND([import GHC.Handle( openFd )]), AC_MSG_RESULT([NOT old API]) AC_MSG_CHECKING([GHC.Handle.openFd new API]) TRY_RUN_GHC([ import GHC.Handle ( openFd ) import IO ( IOMode(..)) main = openFd 1 Nothing False "stdout" WriteMode True ], AC_MSG_RESULT([okay]) IMPORT_WORKAROUND([import qualified GHC.Handle ( openFd ) import System.IO ( Handle, IOMode ) import System.Posix.Internals ( FDType ) ]) CODE_WORKAROUND([[ {- Work around change in the GHC.Handle.openFd API. -} openFd :: Int -> Maybe FDType -> FilePath -> IOMode -> Bool -> Bool -> IO Handle openFd fd x y z a b = GHC.Handle.openFd fd x b y z a ]]), AC_MSG_RESULT([NOT 'new' API]) AC_MSG_CHECKING([GHC.Handle.fdToHandle' API]) TRY_RUN_GHC([ import GHC.Handle ( fdToHandle' ) import IO ( IOMode(..) ) main = fdToHandle' 1 Nothing False "stdout" WriteMode True ], AC_MSG_RESULT([okay]) IMPORT_WORKAROUND([import qualified GHC.Handle ( fdToHandle' ) import System.IO ( Handle, IOMode ) import System.Posix.Internals ( FDType ) ]) CODE_WORKAROUND([[ {- Work around renaming of GHC.Handle.openFd and change in its API. -} openFd :: Int -> Maybe FDType -> FilePath -> IOMode -> Bool -> Bool -> IO Handle openFd fd x y z a b = GHC.Handle.fdToHandle' (fromIntegral fd) x b y z a ]]), AC_MSG_RESULT([failed]) AC_MSG_ERROR([Couldnt figure out how to call GHC.Handle.openFd!]) ) ) )])