#!/usr/bin/env runhaskell import Distribution.Simple import Distribution.Simple.UserHooks import Distribution.Simple.PreProcess import Distribution.Simple.Setup import Distribution.PackageDescription import Distribution.Simple.LocalBuildInfo import System.Cmd(system) import System.Exit import qualified System.Info as Info import System.IO.Unsafe import System.Directory import Data.Maybe import Data.List import Data.IORef import Debug.Trace main = do putStrLn$ " [intel-aes] Running Setup.hs..." defaultMainWithHooks $ simpleUserHooks { postConf = my_postConf , confHook = my_conf , preBuild = my_preBuild , buildHook = my_build , instHook = my_install , cleanHook = my_clean } -- A file will be our global variable: tmpfile = ".temp.install_dir.txt" -------------------------------------------------------------------------------- my_clean :: PackageDescription -> () -> UserHooks -> CleanFlags -> IO () my_clean desc () hooks flags = do putStrLn$ "\n [intel-aes] Running external clean via Makefile" setCurrentDirectory "./cbits/" system "make clean" setCurrentDirectory ".." putStrLn$ " [intel-aes] Done. Now running normal cabal clean action.\n" (cleanHook simpleUserHooks) desc () hooks flags -------------------------------------------------------------------------------- my_conf :: (GenericPackageDescription, HookedBuildInfo) -> ConfigFlags -> IO LocalBuildInfo my_conf (gpd,hbi) flags = do (confHook simpleUserHooks) (gpd,hbi) flags -------------------------------------------------------------------------------- -- we override postConf to keep it from complaining about a missing library: my_postConf :: Args -> ConfigFlags -> PackageDescription -> LocalBuildInfo -> IO () my_postConf args conf desc localinfo = do let buildinfos = allBuildInfo desc desc2 = stripDesc desc putStrLn$ " [intel-aes] Extra libraries initially: "++ show (map extraLibs buildinfos) putStrLn$ " [intel-aes] Stripped extra libraries to: "++ show (map extraLibs $ allBuildInfo desc2) desc3 <- patchDesc desc2 localinfo -- Now call the default hook with a stripped down version: (postConf simpleUserHooks) args conf desc3 localinfo -------------------------------------------------------------------------------- -- Patch a package description for our quirky builda: patchDesc desc localinfo = do let libd = libdir$ absoluteInstallDirs desc localinfo NoCopyDest -- This is lame but I'm not sure how to get the same information at the preBuild step. -- writeIORef global_var_hack libd putStrLn$ " [intel-aes] Determined install dir to be: " ++ show libd writeFile tmpfile libd putStrLn$ " [intel-aes] Recorded install dir in " ++ show tmpfile root <- getCurrentDirectory -- Let's try a friendlier way to change the options: let Just lib = library desc lbi = libBuildInfo lib oldO = ldOptions lbi -- I'm not sure what the best policy is. For now I'm adding -- BOTH the build dir and the eventual install dir... -- This will allow the package to function when it is built-but-not-installed. newO = ("-Wl,-rpath=" ++ libd) : ("-Wl,-rpath=" ++ cbitsd) : oldO cbitsd = root++"/cbits/" newlbi = lbi { ldOptions = newO , extraLibDirs = cbitsd : extraLibDirs lbi } -- Whew... nested record updates are painful: desc3 = desc { library = Just (lib { libBuildInfo = newlbi})} putStrLn$ " [intel-aes] Modified package info. " return desc3 ---------------------------------------- -- Strip out the special extra-libraries: entry stripDesc desc = desc { library = fmap stripLib (library desc) , executables = map stripExe (executables desc) } stripLib lib = lib { libBuildInfo = stripBI (libBuildInfo lib) } stripExe exe = exe { buildInfo = stripBI (buildInfo exe) } stripBI bi = bi { extraLibs = filter filt (extraLibs bi) } filt str = not (isInfixOf "intel_aes" str) ------------------------------------------------------------ my_preBuild :: Args -> BuildFlags -> IO HookedBuildInfo my_preBuild args flags = do putStrLn$ "\n================================================================================" let ext = case Info.os of "linux" -> ".so" "mac" -> ".dylib" "windows" -> ".dll" _ -> error$ "Unexpected " cached_so = "./cbits/prebuilt/libintel_aes_"++ Info.os ++"_"++ Info.arch ++ ext dest = "./cbits/libintel_aes"++ext e <- doesFileExist cached_so if e then do putStrLn$ " [intel-aes] Using prebuilt dynamic library: "++ cached_so copyFile cached_so dest putStrLn$ " [intel-aes] Done copying into position: "++ dest else do putStrLn$ " [intel-aes] Running Makefile to build C/asm source..." rootdir <- getCurrentDirectory setCurrentDirectory "./cbits/" system "make" setCurrentDirectory rootdir putStrLn$ " [intel-aes] Done with external build job." putStrLn$ "================================================================================\n" (preBuild simpleUserHooks) args flags -------------------------------------------------------------------------------- my_build :: PackageDescription -> LocalBuildInfo -> UserHooks -> BuildFlags -> IO () my_build desc linfo hooks flags = do putStrLn$ " [intel-aes] Build action started." desc2 <- patchDesc desc linfo (buildHook simpleUserHooks) desc2 linfo hooks flags putStrLn$ " [intel-aes] Build action finished.\n\n" -------------------------------------------------------------------------------- my_install :: PackageDescription -> LocalBuildInfo -> UserHooks -> InstallFlags -> IO () my_install desc linfo hooks flags = do putStrLn$ " [intel-aes] Install action:" putStrLn$ "\n================================================================================" desc2 <- patchDesc desc linfo libd <- readFile tmpfile let dest = (libd ++ "/libintel_aes.so") putStrLn$ " [intel-aes] Copying shared library to: " ++ show dest system$ "mkdir -p "++ libd -- NONPORTABLE copyFile "./cbits/libintel_aes.so" dest putStrLn$ " [intel-aes] Done copying." -- removeFile tmpfile -- Might install more than once, right? putStrLn$ "================================================================================\n" (instHook simpleUserHooks) desc2 linfo hooks flags