#!/usr/bin/env runhaskell import Distribution.PackageDescription ( PackageDescription(..), Library(..), BuildInfo(..), HookedBuildInfo , emptyHookedBuildInfo, writeHookedBuildInfo, emptyBuildInfo ) import Distribution.Package (Dependency(..)) import Distribution.Simple.Setup ( ConfigFlags(..) ) import Distribution.Simple ( defaultMainWithHooks, autoconfUserHooks, UserHooks(..), Args ) import Distribution.Simple.Program (findProgramOnPath, simpleProgram, Program(..)) import Distribution.Simple.LocalBuildInfo (LocalBuildInfo) import Distribution.Simple.Utils (warn, info, rawSystemStdout) import Distribution.Verbosity (Verbosity) import qualified System.Info (os) import System.Directory (canonicalizePath, removeFile) import System.Environment (getEnv) import System.FilePath (combine, dropFileName, FilePath) import System.IO.Error (try) import Data.Maybe (fromJust) import Data.Monoid (mconcat) {- One install script to rule them all, and in the darkness build them... See this page for useful notes on tagging and releasing: http://www.haskell.org/haskellwiki/How_to_write_a_Haskell_program To-dos for Takusen: - Oracle resource leak: Ref Cursors (StmtHandles) not freed - Out bind parameters for ODBC - Multiple result-sets from ODBC procedure call - better result-set <-> iteratee validation. Check column types? - use hsc2hs to create #define constants from header files, rather than hard-code them. - Blob support (and clob?). - FreeTDS back-end. - POP3 & IMAP back-ends? - Unwritten tests: * incorrect fold function (doesn't match result-set) -} main = defaultMainWithHooks autoconfUserHooks { preConf=preConf, postConf=postConf , hookedPrograms = [pgConfigProgram, odbcConfigProgram, sqlite3Program, sqlplusProgram] } where preConf :: Args -> ConfigFlags -> IO HookedBuildInfo preConf args flags = do try (removeFile "Takusen.buildinfo") return emptyHookedBuildInfo postConf :: Args -> ConfigFlags -> PackageDescription -> LocalBuildInfo -> IO () postConf args flags pkgdesc localbuildinfo = do let verbose = (configVerbose flags) let lbi = libBuildInfo (fromJust (library pkgdesc)) let buildtools = buildTools lbi sqliteBI <- configSqlite3 verbose buildtools pgBI <- configPG verbose buildtools oraBI <- configOracle verbose buildtools odbcBI <- configOdbc verbose buildtools let bi = mconcat [sqliteBI, pgBI, oraBI, odbcBI, Just lbi] writeHookedBuildInfo "Takusen.buildinfo" (bi, []) -- ODBCConf.exe - MDAC install actions - command line? odbcConfigProgram = simpleProgram "odbcconf" -- ODBC Admin console - GUI program --odbcConfigProgram = simpleProgram "odbcad32" sqlplusProgram = simpleProgram "sqlplus" pgConfigProgram = simpleProgram "pg_config" sqlite3Program = simpleProgram "sqlite3" isWindows = System.Info.os == "mingw32" || System.Info.os == "windows" makeConfig path libDir includeDir = do libDirs <- canonicalizePath (combine path libDir) includeDirs <- canonicalizePath (combine path includeDir) return (Just emptyBuildInfo { extraLibDirs = [libDirs], includeDirs = [includeDirs] }) maybeGetEnv :: String -> IO (Maybe String) maybeGetEnv env = do catch ( getEnv env >>= return . Just ) ( const (return Nothing) ) -- Check that the program is in the buildtools. -- If it is, then run the action (which should return Maybe BuildInfo). -- If not, return Nothing. guardProg :: Program -> [Dependency] -> IO (Maybe BuildInfo) -> IO (Maybe BuildInfo) guardProg prog tools action = do if prog `isElem` tools then action else return Nothing where isElem program buildtools = or (map (match program) buildtools) match program (Dependency tool _) = (programName program) == tool -- Run the first action to give a Maybe FilePath. -- If this is Nothing then emit a warning about library not found. -- Otherwise, run the second action over the FilePath. guardPath :: (IO (Maybe FilePath)) -> String -> Verbosity -> (FilePath -> IO (Maybe BuildInfo)) -> IO (Maybe BuildInfo) guardPath pathAction libName verbose resAction = do mb <- pathAction case mb of Nothing -> warn verbose ("No " ++libName++ " library found") >> return Nothing Just path -> info verbose ("Using " ++libName++ ": " ++ path) >> resAction path -- From the Oracle 10g manual: -- -- Appendix D - Getting Started with OCI for Windows: -- Compiling OCI Applications for Windows: -- http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14250/ociadwin.htm#i634569 -- Header files are in: ORACLE_BASE\ORACLE_HOME\oci\include -- DLLs are in: ORACLE_BASE\ORACLE_HOME\bin -- -- For Unix: -- Appendix B - OCI Demonstration Programs: -- http://download.oracle.com/docs/cd/B19306_01/appdev.102/b14250/ociabdem.htm#i459676 -- Header files are in: $ORACLE_HOME/rdbms/public -- Header files are in: $ORACLE_HOME/lib configOracle verbose buildtools = do guardProg sqlplusProgram buildtools $ do guardPath (maybeGetEnv "ORACLE_HOME") "Oracle" verbose $ \path -> do let (libDir, incDir) = if isWindows then ("bin", "oci/include") else ("lib", "rdbms/public") makeConfig path libDir incDir configSqlite3 verbose buildtools = do guardProg sqlite3Program buildtools $ do if isWindows then guardPath (programFindLocation sqlite3Program verbose) "Sqlite3" verbose $ \path -> do makeConfig (dropFileName path) "" "" else return Nothing configPG verbose buildtools = do guardProg sqlplusProgram buildtools $ do guardPath (programFindLocation pgConfigProgram verbose) "PostgreSQL" verbose $ \pq_config_path -> do lib_dirs <- rawSystemStdout verbose pq_config_path ["--libdir"] inc_dirs <- rawSystemStdout verbose pq_config_path ["--includedir"] inc_dirs_server <- rawSystemStdout verbose pq_config_path ["--includedir-server"] return ( Just emptyBuildInfo { extraLibDirs = words lib_dirs , includeDirs = words inc_dirs ++ words inc_dirs_server }) -- On Windows the ODBC stuff is in c:\windows\system32, which is always in the PATH. -- So I think we only need to pass -lodbc32. -- The include files are already in the ghc/include/mingw folder. -- FIXME: I don't know how this should look for unixODBC. configOdbc verbose buildtools | isWindows = do info verbose "Using ODBC: lib already in PATH>" return Nothing configOdbc verbose buildtools = do --info verbose "Using odbc: assume lib already in PATH>" return Nothing