import Control.Monad
import Data.Char (toLower)
import Data.Functor (($>))
import Data.Maybe (fromMaybe)
import Distribution.PackageDescription
import Distribution.Simple
import Distribution.Simple.LocalBuildInfo (
InstallDirs (..),
LocalBuildInfo (..),
absoluteInstallDirs,
buildDir,
localPkgDescr,
)
import Distribution.Simple.Program.Find (
defaultProgramSearchPath,
findProgramOnSearchPath,
)
import Distribution.Simple.Setup
import Distribution.Simple.Utils (
IODataMode (IODataModeBinary),
installExecutableFile,
maybeExit,
rawSystemStdInOut,
)
import Distribution.System
import Distribution.Verbosity (Verbosity)
import qualified Distribution.Verbosity as Verbosity
import System.Directory (
getCurrentDirectory,
)
import System.Environment (getEnv)
import System.FilePath ((</>))
import System.Posix.Files (createSymbolicLink)
main :: IO ()
main = defaultMainWithHooks hooks
where
hooks =
simpleUserHooks
{ preConf = \_ flags -> do
unlessFlagM externalLibFlag flags $ rsMake (fromFlag $ configVerbosity flags)
pure emptyHookedBuildInfo
, confHook = \a flags -> do
lbi <- confHook simpleUserHooks a flags >>= rsAddLibraryInfo flags
unless (cabalFlag externalLibFlag flags) $ do
copyTemporalBridge lbi (buildDir lbi)
pure lbi
, postClean = \_ flags _ _ ->
rsClean (fromFlag $ cleanVerbosity flags)
, postCopy =
installTemporalBridge
}
rsFolder :: FilePath
rsFolder = "rust"
externalLibFlagStr :: String
externalLibFlagStr = "external_lib"
externalLibFlag :: FlagName
externalLibFlag = mkFlagName externalLibFlagStr
execCargo :: Verbosity -> String -> [String] -> IO ()
execCargo verbosity command args = do
cargoPath <- findProgramOnSearchPath Verbosity.silent 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 = execCargo verbosity "build" ["--release", "--lib"]
rsAddLibraryInfo :: ConfigFlags -> LocalBuildInfo -> IO LocalBuildInfo
rsAddLibraryInfo fl lbi' = do
dir <- getCurrentDirectory
let external = getCabalFlag externalLibFlagStr fl
let updateLbi lbi =
lbi
{ localPkgDescr = updatePkgDescr (localPkgDescr lbi)
}
updatePkgDescr pkgDescr =
pkgDescr
{ library = updateLib <$> library pkgDescr
}
updateLib lib =
lib
{ libBuildInfo = updateLibBi (libBuildInfo lib)
}
updateLibBi libBuild =
libBuild
{ extraLibDirs =
if external
then extraLibDirs libBuild
else (dir </> buildDir lbi') : extraLibDirs libBuild
}
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 = do
putStrLn "Skipping cargo action because external_lib flag is set"
pure ()
| otherwise = do
putStrLn ("Running cargo action because we don't have the flag: " ++ show name)
action
copyLib :: ConfigFlags -> FilePath -> Bool -> IO ()
copyLib fl libPref shared = do
libraryPath <- getLibraryPath
installExecutableFile
verb
libraryPath
(libPref ++ "/libtemporal_bridge." ++ ext)
where
verb = fromFlag $ configVerbosity fl
external = getCabalFlag externalLibFlagStr fl
ext =
if shared
then case buildOS of
Windows -> "dll"
OSX -> "dylib"
_ -> "so"
else "a"
getLibraryPath =
pure ("rust/target/release/libtemporal_bridge." ++ ext)
copyTemporalBridge :: LocalBuildInfo -> FilePath -> IO ()
copyTemporalBridge lbi fp = do
copyLib config fp False
where
config = configFlags lbi
installTemporalBridge :: Args -> CopyFlags -> PackageDescription -> LocalBuildInfo -> IO ()
installTemporalBridge _ flags pkg_descr lbi = do
let
libPref =
libdir
. absoluteInstallDirs pkg_descr lbi
. fromFlag
. copyDest
$ flags
config = configFlags lbi
unless (cabalFlag externalLibFlag $ configFlags lbi) $ do
copyLib config libPref True
copyLib config libPref False
getCabalFlag :: String -> ConfigFlags -> Bool
getCabalFlag name flags = fromMaybe False (lookupFlagAssignment (mkFlagName name') allFlags)
where
allFlags = configConfigurationsFlags flags
name' = map toLower name