-- | A crude, unsafe and preliminary solution to building B9 'SharedImage's -- from Shake. module B9.Shake.SharedImageRules ( customSharedImageAction , needSharedImage , enableSharedImageRules ) where import B9 import qualified Data.Binary as Binary import qualified Data.ByteString as ByteString import qualified Data.ByteString.Builder as Builder import Data.ByteString.Builder (stringUtf8) import qualified Data.ByteString.Lazy as LazyByteString import Development.Shake import Development.Shake.Classes import Development.Shake.Rule -- | In order to use 'needSharedImage' and 'customSharedImageAction' you need to -- call this action before using any of the aforementioned 'Rules'. enableSharedImageRules :: B9ConfigOverride -> Rules () enableSharedImageRules b9inv = addBuiltinRule noLint sharedImageIdentity go where sharedImageIdentity :: BuiltinIdentity SharedImageName SharedImageBuildId sharedImageIdentity (SharedImageName k) (SharedImageBuildId v) = Just (LazyByteString.toStrict (Builder.toLazyByteString (stringUtf8 k <> stringUtf8 v))) go :: BuiltinRun SharedImageName SharedImageBuildId go nameQ mOldBIdBinary dependenciesChanged = do mCurrentBId <- getImgBuildId case mCurrentBId of Just currentBId -> let currentBIdBinary = encodeBuildId currentBId in if dependenciesChanged == RunDependenciesChanged && mOldBIdBinary == Just currentBIdBinary then return $ RunResult ChangedNothing currentBIdBinary currentBId else rebuild (Just currentBIdBinary) _ -> rebuild Nothing where getImgBuildId = liftIO (runB9ConfigActionWithOverrides (runLookupLocalSharedImage nameQ) b9inv) encodeBuildId :: SharedImageBuildId -> ByteString.ByteString encodeBuildId = LazyByteString.toStrict . Binary.encode rebuild :: Maybe ByteString.ByteString -> Action (RunResult SharedImageBuildId) rebuild mCurrentBIdBinary = do (_, act) <- getUserRuleOne nameQ (const Nothing) imgMatch act b9inv mNewBId <- getImgBuildId newBId <- maybe (error ("failed to get SharedImageBuildId for " ++ show nameQ ++ " in context of " ++ show b9inv)) return mNewBId let newBIdBinary = encodeBuildId newBId let change = if Just newBIdBinary == mCurrentBIdBinary then ChangedRecomputeSame else ChangedRecomputeDiff return $ RunResult change newBIdBinary newBId where imgMatch (SharedImageCustomActionRule name mkImage) = if name == nameQ then Just mkImage else Nothing -- | Add a dependency to the creation of a 'SharedImage'. The build action -- for the shared image must have been supplied by e.g. 'customSharedImageAction'. -- NOTE: You must call 'enableSharedImageRules' before this action works. needSharedImage :: SharedImageName -> Action SharedImageBuildId needSharedImage = apply1 -- | Specify an arbitrary action that is supposed to build the given shared -- image identified by a 'SharedImageName'. -- NOTE: You must call 'enableSharedImageRules' before this action works. customSharedImageAction :: SharedImageName -> Action () -> Rules () customSharedImageAction b9img customAction = addUserRule (SharedImageCustomActionRule b9img customAction') where customAction' b9inv = do customAction mCurrentBuildId <- liftIO (runB9ConfigActionWithOverrides (runLookupLocalSharedImage b9img) b9inv) putLoud (printf "Finished custom action, for %s, build-id is: %s" (show b9img) (show mCurrentBuildId)) maybe (errorSharedImageNotFound b9img) return mCurrentBuildId type instance RuleResult SharedImageName = SharedImageBuildId data SharedImageCustomActionRule = SharedImageCustomActionRule SharedImageName (B9ConfigOverride -> Action SharedImageBuildId) deriving (Typeable) errorSharedImageNotFound :: Monad m => SharedImageName -> m a errorSharedImageNotFound = fail . printf "Error: %s not found." . show