packages feed

hinstaller-2007.4.21: src/System/Installer.hs

{-
    System.Installer : Installer wrapper for Haskell applications
    Copyright (C) 2007  Matthew Sackman

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
    USA
-}

-- | This module allows you to include any file into a module during
--   compilation. You can then write out the file at run time.
--
--   Expected use is via the Template Haskell splicing syntax. E.g.
--
-- @
--   $(installBinariesFunc \"installMyFiles\"
--     [(\"FileOne\", \"\/foo\/bar\/fileOne.txt\"),
--      (\"FileTwo\", \"\/foo\/bar\/fileTwo.txt\"),
--      (\"Three\",   \"\/foo\/baz\/thirdFile.qux\")
--     ])
-- @
--
--   This will cause an enumeration ADT to be defined as:
--
-- @
--   data Installer_installMyFiles =
--            Installer_installMyFiles_FileOne
--          | Installer_installMyFiles_FileTwo
--          | Installer_installMyFiles_Three
-- @
--
--    with instances for Enum, Eq, Ord and Show. The install for Show
--    will return the @fst@ element of the tuple in the list passed to
--    'installBinariesFunc'. A function called @installMyFiles@ will
--    also be defined of type
--    @Installer_installMyFiles -> FilePath -> IO ()@. Calling this
--    will cause the file content to be written out to the filepath.
--    If the filepath provided is a directory, then the file will be
--    created within that directory with the same name as the leaf of
--    the path in the @snd@ elem of the tuples passed to
--    'installBinariesFunc'.
--
--    Note that the files written out are not set executable so you
--    must correct file permissions yourself.
--
--    Also note that the current implementation includes the entire
--    file content as a String. This is horribly inefficient.
--    Consequently GHC may well stack overflow during compilation.
--    Set the stack bigger with @+RTS -K32M -RTS@. Compilation will
--    be /very/ slow, especially with any optimisations enabled.
module System.Installer 
    (installBinariesFunc
    )
    where

import System.Directory
import System.IO
import qualified System.Installer.TH as TH
import Language.Haskell.TH.Syntax

installBinariesFunc :: String -> [(String, FilePath)] -> Q [Dec]
installBinariesFunc funcName binaries
    = do { func <- TH.makeInstallFunc funcName clauses
         ; dataDecls <- TH.makeDataDecls funcName binaries
         ; return $ func : dataDecls
         }
    where
      clauses = map (loadAndMakeClause funcName) binaries

loadAndMakeClause :: String -> (String, FilePath) -> Q Clause
loadAndMakeClause funcName (name, bin)
    = do { contents <- runIO (loadBinary bin)
         ; TH.makeInstallFuncCase funcName name bin contents
         }

loadBinary :: FilePath -> IO String
loadBinary path
    = do { exists <- doesFileExist path
         ; case exists of
             True -> do { hdl <- openBinaryFile path ReadMode
                        ; contents <- hGetContents hdl
                        ; length contents `seq` return ()
                        ; hClose hdl
                        ; return contents
                        }
             False -> error $ "Unable to find file at " ++ path
         }