import Control.Exception (throw) import Control.Monad (forM_, when) import Data.Maybe (fromMaybe) import Distribution.PackageDescription import Distribution.Simple import Distribution.Simple.LocalBuildInfo (LocalBuildInfo(..), localPkgDescr) import Distribution.Simple.PreProcess import Distribution.Simple.Program.Find ( defaultProgramSearchPath , findProgramOnSearchPath ) import Distribution.Simple.Setup import Distribution.Simple.Utils ( IODataMode(IODataModeBinary) , maybeExit , rawSystemStdInOut ) import Distribution.Verbosity (Verbosity) import qualified Distribution.Verbosity as Verbosity import GHC.Generics import System.Directory ( XdgDirectory(..) , copyFile , createDirectory , createDirectoryIfMissing , doesDirectoryExist , doesFileExist , getCurrentDirectory , getDirectoryContents , getHomeDirectory , getXdgDirectory ) import System.Environment import System.FilePath (()) import Text.Regex import Text.Regex.Base main :: IO () main = defaultMainWithHooks hooks where hooks = simpleUserHooks { preConf = \_ flags -> do rsMake (fromFlag $ configVerbosity flags) pure emptyHookedBuildInfo , hookedPreProcessors = knownSuffixHandlers , confHook = \a flags -> confHook simpleUserHooks a flags >>= rsAddDirs , postClean = \_ flags _ _ -> rsClean (fromFlag $ cleanVerbosity flags) } rsFolder :: FilePath rsFolder = "librustzcash-wrapper" execCargo :: Verbosity -> String -> [String] -> IO () execCargo verbosity command args = do cargoPath <- findProgramOnSearchPath Verbosity.normal defaultProgramSearchPath "cargo" dir <- getCurrentDirectory let cargoExec = case cargoPath of Just (p, _) -> p Nothing -> "cargo" cargoArgs = command : args workingDir = Just (dir rsFolder) thirdComponent (_, _, c) = c maybeExit . fmap thirdComponent $ rawSystemStdInOut verbosity cargoExec cargoArgs workingDir Nothing Nothing IODataModeBinary rsMake :: Verbosity -> IO () rsMake verbosity = do execCargo verbosity "cbuild" [] rsAddDirs :: LocalBuildInfo -> IO LocalBuildInfo rsAddDirs lbi' = do localData <- getXdgDirectory XdgData "zcash-haskell" createDirectoryIfMissing True localData dir <- getCurrentDirectory let rustIncludeDir = dir rsFolder "target/x86_64-unknown-linux-gnu/debug" rustLibDir = dir rsFolder "target/x86_64-unknown-linux-gnu/debug" updateLbi lbi = lbi {localPkgDescr = updatePkgDescr (localPkgDescr lbi)} updatePkgDescr pkgDescr = pkgDescr {library = updateLib <$> library pkgDescr} updateLib lib = lib {libBuildInfo = updateLibBi (libBuildInfo lib)} updateLibBi libBuild = libBuild { includeDirs = rustIncludeDir : includeDirs libBuild , extraLibDirs = rustLibDir : extraLibDirs libBuild } copyDir rustLibDir localData pure $ updateLbi lbi' rsClean :: Verbosity -> IO () rsClean verbosity = execCargo verbosity "clean" [] cabalFlag :: FlagName -> ConfigFlags -> Bool cabalFlag name = fromMaybe False . lookupFlagAssignment name . configConfigurationsFlags unlessFlagM :: FlagName -> ConfigFlags -> IO () -> IO () unlessFlagM name flags action | cabalFlag name flags = pure () | otherwise = action applyUnlessM :: FlagName -> ConfigFlags -> (a -> IO a) -> a -> IO a applyUnlessM name flags apply a | cabalFlag name flags = pure a | otherwise = apply a copyDir :: FilePath -> FilePath -> IO () copyDir src dst = do whenM (not <$> doesDirectoryExist src) $ throw (userError "source does not exist") --whenM (doesFileOrDirectoryExist dst) $ --throw (userError "destination already exists") createDirectoryIfMissing True dst content <- getDirectoryContents src let xs = filter (`notElem` [".", ".."]) content forM_ xs $ \name -> do let srcPath = src name let dstPath = dst name isDirectory <- doesDirectoryExist srcPath if isDirectory then copyDir srcPath dstPath else copyFile srcPath dstPath where doesFileOrDirectoryExist x = orM [doesDirectoryExist x, doesFileExist x] orM xs = or <$> sequence xs whenM s r = s >>= flip when r