packages feed

bamse-0.9.5: Bamse/Builder.hs

--------------------------------------------------------------------
-- |
-- Module      : Bamse.Builder
-- Description : Toplevel module for a bamse library/app.
-- Copyright   : (c) Sigbjorn Finne, 2004-2009
-- License     : BSD3
--
-- Maintainer  : Sigbjorn Finne <sof@forkIO.com>
-- Stability   : provisional
-- Portability : portable
--
-- Toplevel module for the @Bamse@ library/app. Use @genBuilder@
-- to do the generation of an MSI; it taking a specification of the
-- installer you are wanting to create. That along with the command-line
-- settings are then used to kick off the creation of an MSI database,
-- which will incorporate not only the metadata (installer name, shortcuts etc.)
-- but also the file content and structure that makes up the tree of
-- files you want to install on the user's machine.
-- 
--------------------------------------------------------------------
module Bamse.Builder 
       ( genBuilder 
       , genBuilderArgs
       ) where

import Bamse.Package
import Bamse.Writer
import Bamse.IMonad
import Bamse.PackageGen
import Bamse.MSIExtra
import Bamse.PackageUtils
import Bamse.DialogUtils

import System.Win32.Com ( coRun )
import System.FilePath
import Bamse.Util.Dir
import Bamse.Util.List ( ifCons )

import System.Cmd
import System.IO
import Bamse.Options

import Data.Maybe
import Control.Monad
import System.Directory
import System.Exit

-- ToDo:
--     - ability to organise installed bits into features/sub parts.
-- 

-- | @genBuilderArgs pkg argv@ constructs an MSI from the given package
-- description + a set of command-line of arguments @args@.
genBuilderArgs :: PackageData -> [String] -> IO ()
genBuilderArgs pkg args = do
  opts      <- getOptionsFrom args (p_defOutFile pkg)
  genBuilderOpts pkg opts

-- | @genBuilder pkg @ constructs an MSI from the given package
-- description, plus taking the command-line arguments from @getArgs@.
genBuilder :: PackageData -> IO ()
genBuilder pkg = do
  putStrLn ("Installer builder for: " ++ name (p_pkgInfo pkg)) >> hFlush stdout
  opts           <- getOptions (p_defOutFile pkg)
  genBuilderOpts pkg opts

genBuilderOpts :: PackageData -> Options -> IO ()
genBuilderOpts pkg opts = do
  ds              <- (p_fileMap pkg) (opt_ienv opts)
  (dsDist, ienvN) <- mkDistTree (opt_ienv opts)
                                (normalise $ dropTrailingPathSeparator $ srcDir $ opt_ienv opts)
  			        (name $ p_pkgInfo pkg)
				(p_distFileMap pkg)
  			        ds
  let pkg' = pkg{ p_files   = dsDist
		, p_dialogs = 
		       ifCons (p_userInstall pkg) setupTypeDialog $
		         ifCons (isJust (p_cabalPackage pkg)) (cabalDialog (fromJust (p_cabalPackage pkg))) $
--retired:	         ifCons (isJust (p_ghcPackage pkg)) ghcPkgDialog $
			   -- add customization selection dialog only if the
			   -- builder supplies the relevant features.
			  case options_ (opt_ienv opts) of
			    [] -> []
			    os -> [customizeDialog os]
  		, p_productGUID  = fromJust (opt_productGUID opts)
		, p_revisionGUID = fromJust (opt_revisionGUID opts)
		, p_ienv         = ienvN
		, p_verbose      = opt_verbose opts
  		}
  let bamseDir = toolDir (p_ienv pkg')
  coRun $ do
    (_, ts, tabs, reps) <-  doInstall [] (genTables pkg')
    let wenv = 
         WriterEnv 
            { w_toolDir     = bamseDir
   	    , w_templateDir = lFile (lFile bamseDir "data") "msi"
	    , w_outFile     = outFile ienvN
	       -- Note: using the _adjusted_ source directory here
	       -- from the dist tree, not the original one (i.e., want
	       -- to chop out the 'out/..' prefix, of course.)
	    , w_srcDir      = normalise (takeDirectory (srcDir ienvN))
	    , w_package     = pkg'
	    }
    outputMSI wenv tabs ts reps
    return ()
 where
  options_ ienv
   = ifCons (not (null (p_extensions pkg ienv)))
   	    ("Register file extensions", "OptFileExt", True) $
       ifCons (not (null (p_desktopShortcuts pkg ienv)))
              ("Create desktop shortcuts", "OptDesktopShortcuts", True) $
	 ifCons (not (null (snd $ p_startMenu pkg ienv)))
	        ("Create start menu folder", "OptStartMenu", True)
		[]


mkDistTree :: InstallEnv
	   -> FilePath
	   -> String
	   -> Maybe (InstallEnv -> FilePath -> Maybe FilePath)
	   -> DirTree
	   -> IO (DirTree, InstallEnv)
mkDistTree ienv _      _  Nothing   ds = return (ds, ienv)
mkDistTree ienv topDir nm (Just fn) ds = do
     -- copy over directory tree into temporary 'outDir'
    fp <- getCurrentDirectory
    catch (createDirectory (appendP fp "out")) (\ _ -> return ())
    let outDir = appendP fp (appendP "out" nm)
    catch (createDirectory outDir) (\ _ -> return ())
    copyOver outDir ds
    ds1 <- allFiles outDir
    return (ds1, ienv{srcDir=outDir})
  where
    copyOver _ Empty         = return ()
    copyOver outDir (File f) = do
      case fn ienv f of
        Nothing  -> return ()
	Just fnm -> do
          let cmd = ("copy /b \"" ++ f ++ "\" \"" ++ 
	             appendP outDir (dropDirPrefix topDir fnm) ++ "\" > nul")
--          putStrLn ("Creating dir: " ++ fnm ++ ' ':(dropFileName $ appendP outDir (dropDirPrefix topDir fnm)))
          createDirectoryIfMissing True (dropFileName $ appendP outDir (dropDirPrefix topDir fnm))
          system' cmd
          return ()
    copyOver outDir (Directory fp subs) = do
       maybe (return ())
             (\ f -> do
--                 putStrLn ("Creating dirs: " ++ fp ++ ' ':(appendP outDir (dropDirPrefix topDir f)))
	         createDirectoryIfMissing True (appendP outDir (dropDirPrefix topDir f))
		 return ())
	     (fn ienv fp)
       mapM_ (copyOver outDir) subs

    appendP a b = normalise (a </> b)

    system' cmd = do
--      when vb (hPutStrLn stderr ("Command exec: " ++ cmd))
      rc <- system cmd
      case rc of
       ExitSuccess{} -> return ()
       _ -> putStrLn $ "ERROR: failed exec'ing " ++ show cmd