import Distribution.Simple import Distribution.Simple.Setup import Distribution.Simple.Utils (rawSystemExit, rawSystemExitWithEnv, installOrdinaryFile, installExecutableFile, copyFileVerbose, createDirectoryIfMissingVerbose) import Distribution.Simple.LocalBuildInfo ( LocalBuildInfo(..), InstallDirs(..), absoluteInstallDirs) import Distribution.PackageDescription (PackageDescription(..), GenericPackageDescription(..), HookedBuildInfo(..), BuildInfo(..), emptyBuildInfo, updatePackageDescription, FlagAssignment(..), FlagName(..)) import Distribution.Verbosity (verbose, Verbosity(..)) import Distribution.System (OS(..), Arch(..), Platform (..), buildOS, buildPlatform) import qualified Distribution.Simple.Utils import Data.Version( Version, showVersion ) import System.Directory import System.FilePath import System.Environment( getEnvironment ) import Control.Monad(when) -- Here we install custom hooks to deal with fetching and building the ABC -- sources. This mostly involves calling into the "scripts/setup-abc.sh" -- and "scripts/build-abc.sh" scripts at the proper times. -- -- The other thing we must do is automagically munge the cabal description -- to handle the ABC source tree. We do this by editing, at runtime, the -- cabal package description to include the ABC sources files to `extra-source-files` -- (so `cabal sdist` works as expected), and to add the ABC source tree directories -- to `include-dirs`. This is done by reading the files `abc-build/abc-sources.txt` -- and `abc-build/abc-incl-dirs.txt`, which are set up by `scripts/setup-abc.sh`. -- -- Finally, we must also include some information about where do find the libabc.a -- and libabc.dll files. -- -- We do this by modifying the configure hook so it modifies the package description -- read from disk before returing the local build info that is used by other cabal actions. -- However, we also have to modify the sDistHook because it reads from the package description -- file directly rather than using the one from the confHook. The 'clean' action likewise reads -- the description file directly, but it causes no problems to use the unmodified package -- description for the clean action, so we do not modify that hook. main = defaultMainWithHooks simpleUserHooks { confHook = \(gpkg_desc, hbi) f -> do let v = fromFlag $ configVerbosity f let fs = configConfigurationsFlags f setupAbc v (packageDescription gpkg_desc) buildAbc v fs lbi <- confHook simpleUserHooks (gpkg_desc, hbi) f pkg_desc' <- abcPkgDesc (localPkgDescr lbi) return lbi{ localPkgDescr = pkg_desc' } , sDistHook = \pkg_desc lbi h f -> do let v = fromFlag $ sDistVerbosity f setupAbc v pkg_desc pkg_desc' <- abcPkgDesc pkg_desc sDistHook simpleUserHooks pkg_desc' lbi h f , postCopy = postCopyAbc , postInst = postInstAbc } -- This is where we stash the static compiled ABC libraries static_dir = "dist""build" -- Edit the package description to include the ABC source files, -- ABC include directories, and static library directories. abcPkgDesc :: PackageDescription -> IO PackageDescription abcPkgDesc pkg_desc = do cwd <- getCurrentDirectory abcSrcFiles <- fmap lines $ readFile $ "abc-build" "abc-sources.txt" abcInclDirs <- fmap lines $ readFile $ "abc-build" "abc-incl-dirs.txt" let pg' = updatePackageDescription (libDirAbc cwd abcInclDirs) pkg_desc return pg'{ extraSrcFiles = extraSrcFiles pg' ++ abcSrcFiles } libDirAbc :: FilePath -> [FilePath] -> HookedBuildInfo libDirAbc cwd abcInclDirs = (Just buildinfo, []) where buildinfo = emptyBuildInfo { includeDirs = abcInclDirs , extraLibDirs = [cwd static_dir] } onWindows :: Monad m => m () -> m () onWindows act = case buildPlatform of Platform _ Windows -> act _ -> return () -- If necessary, fetch the ABC sources and prepare for building setupAbc :: Verbosity -> PackageDescription -> IO () setupAbc verbosity pkg_desc = do putStrLn $ unwords ["Cabal library version:", showVersion Distribution.Simple.Utils.cabalVersion] let version = pkgVersion $ package $ pkg_desc let packageVersion = "PACKAGE_VERSION" env <- getEnvironment rawSystemExitWithEnv verbosity "sh" ( ("scripts" "setup-abc.sh") : (tail . words . show $ buildPlatform)) ([(packageVersion, showVersion version)] ++ filter ((/=packageVersion) . fst) env) -- Build the ABC library and put the files in the expected places buildAbc :: Verbosity -> FlagAssignment -> IO () buildAbc verbosity fs = do let pthreads = maybe "0" (\x -> if x then "1" else "0") $ lookup (FlagName "enable-pthreads") fs env <- getEnvironment rawSystemExitWithEnv verbosity "sh" (("scripts""build-abc.sh") : (tail . words . show $ buildPlatform)) ([("PTHREADS",pthreads)] ++ filter ((/="PTHREADS") . fst) env) createDirectoryIfMissingVerbose verbosity True static_dir copyFileVerbose verbosity ("abc-build""libabc.a") (static_dir"libabc.a") onWindows $ copyFileVerbose verbosity ("abc-build""libabc.dll") (static_dir"abc.dll") -- Make sure the ABC libraries are installed in the appropriate places postInstAbc :: Args -> InstallFlags -> PackageDescription -> LocalBuildInfo -> IO () postInstAbc _ flags pkg_descr lbi = do let copyflags = defaultCopyFlags { copyDistPref = installDistPref flags , copyDest = toFlag NoCopyDest , copyVerbosity = installVerbosity flags } postCopyAbc undefined copyflags pkg_descr lbi postCopyAbc :: Args -> CopyFlags -> PackageDescription -> LocalBuildInfo -> IO () postCopyAbc _ flags pkg_descr lbi = do let installDirs = absoluteInstallDirs pkg_descr lbi . fromFlag . copyDest $ flags libPref = libdir installDirs binPref = bindir installDirs verbosity = fromFlag $ copyVerbosity flags outDir = libPref copy dest f = installOrdinaryFile verbosity (static_dirf) (destf) createDirectoryIfMissingVerbose verbosity True binPref copy libPref "libabc.a" onWindows $ copy libPref "abc.dll"