module Data.ProtoLens.Setup
( defaultMainGeneratingProtos
, defaultMainGeneratingSpecificProtos
, generatingProtos
, generatingSpecificProtos
, generateProtosWithImports
, generateProtos
) where
#if __GLASGOW_HASKELL__ < 709
import Data.Functor ((<$>))
#endif
import Control.Monad (filterM, forM_, when)
import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo
import Distribution.PackageDescription
( PackageDescription(..)
, benchmarkBuildInfo
, buildInfo
, extraSrcFiles
, hsSourceDirs
, libBuildInfo
, testBuildInfo
)
import Distribution.Simple.BuildPaths (autogenModulesDir)
import Distribution.Simple.InstallDirs (datadir)
import Distribution.Simple.LocalBuildInfo
( LocalBuildInfo(..)
, absoluteInstallDirs
, componentPackageDeps
)
import Distribution.Simple.PackageIndex (lookupSourcePackageId)
import Distribution.Simple.Setup (fromFlag, copyDest, copyVerbosity)
import Distribution.Simple.Utils
( createDirectoryIfMissingVerbose
, installOrdinaryFile
, matchFileGlob
)
import Distribution.Simple
( defaultMainWithHooks
, simpleUserHooks
, UserHooks(..)
)
import Distribution.Verbosity (Verbosity)
import System.FilePath
( (</>)
, equalFilePath
, isRelative
, makeRelative
, takeExtension
, takeDirectory
)
import System.Directory
( createDirectoryIfMissing
, doesDirectoryExist
, findExecutable
, removeDirectoryRecursive
)
import System.Process (callProcess)
defaultMainGeneratingProtos
:: FilePath
-> IO ()
defaultMainGeneratingProtos root
= defaultMainWithHooks $ generatingProtos root simpleUserHooks
defaultMainGeneratingSpecificProtos
:: FilePath
-> (PackageDescription -> IO [FilePath])
-> IO ()
defaultMainGeneratingSpecificProtos root getProtos
= defaultMainWithHooks
$ generatingSpecificProtos root getProtos simpleUserHooks
generatingProtos
:: FilePath
-> UserHooks -> UserHooks
generatingProtos root = generatingSpecificProtos root getProtos
where
getProtos p = do
files <- concat <$> mapM matchFileGlob (extraSrcFiles p)
pure $ map (makeRelative root)
$ filter (isSubdirectoryOf root)
$ filter (\f -> takeExtension f == ".proto")
files
generatingSpecificProtos
:: FilePath
-> (PackageDescription -> IO [FilePath])
-> UserHooks -> UserHooks
generatingSpecificProtos root getProtos hooks = hooks
{ buildHook = \p l h f -> generate p l >> buildHook hooks p l h f
, haddockHook = \p l h f -> generate p l >> haddockHook hooks p l h f
, replHook = \p l h f args -> generate p l >> replHook hooks p l h f args
, sDistHook = \p maybe_l h f -> case maybe_l of
Nothing -> error "Can't run protoc; run 'cabal configure' first."
Just l -> do
generate p l
sDistHook hooks (fudgePackageDesc l p) maybe_l h f
, postCopy = \a flags pkg lbi -> do
let verb = fromFlag $ copyVerbosity flags
let destDir = datadir (absoluteInstallDirs pkg lbi
$ fromFlag $ copyDest flags)
</> protoLensImportsPrefix
getProtos pkg >>= copyProtosToDataDir verb root destDir
postCopy hooks a flags pkg lbi
}
where
generate p l = getProtos p >>= generateSources root l
generateSources :: FilePath
-> LocalBuildInfo
-> [FilePath]
-> IO ()
generateSources root l files = do
importDirs <- filterM doesDirectoryExist
[ InstalledPackageInfo.dataDir info </> protoLensImportsPrefix
| (_, c, _) <- componentsConfigs l
, (_, i) <- componentPackageDeps c
, info <- lookupSourcePackageId (installedPkgs l) i
]
generateProtosWithImports (root : importDirs) (autogenModulesDir l)
(map (root </>) files)
copyProtosToDataDir :: Verbosity
-> FilePath
-> FilePath
-> [FilePath]
-> IO ()
copyProtosToDataDir verb root destDir files = do
exists <- doesDirectoryExist destDir
when exists $ removeDirectoryRecursive destDir
forM_ files $ \f -> do
let srcFile = root </> f
let destFile = destDir </> f
createDirectoryIfMissingVerbose verb True
(takeDirectory destFile)
installOrdinaryFile verb srcFile destFile
protoLensImportsPrefix :: FilePath
protoLensImportsPrefix = "proto-lens-imports"
fudgePackageDesc :: LocalBuildInfo -> PackageDescription -> PackageDescription
fudgePackageDesc lbi p = p
{ library =
(\lib -> lib { libBuildInfo = fudgeBuildInfo (libBuildInfo lib) })
<$> library p
, executables =
(\exe -> exe { buildInfo = fudgeBuildInfo (buildInfo exe) })
<$> executables p
, testSuites =
(\test -> test { testBuildInfo = fudgeBuildInfo (testBuildInfo test) })
<$> testSuites p
, benchmarks =
(\bench -> bench { benchmarkBuildInfo =
fudgeBuildInfo (benchmarkBuildInfo bench) })
<$> benchmarks p
}
where
fudgeBuildInfo bi =
bi { hsSourceDirs = autogenModulesDir lbi : hsSourceDirs bi }
isSubdirectoryOf :: FilePath -> FilePath -> Bool
isSubdirectoryOf root f
= isRelative f
&& equalFilePath f (root </> makeRelative root f)
generateProtos
:: FilePath
-> FilePath
-> [FilePath]
-> IO ()
generateProtos root = generateProtosWithImports [root]
generateProtosWithImports
:: [FilePath]
-> FilePath
-> [FilePath]
-> IO ()
generateProtosWithImports imports output files = do
maybeProtoLensProtoc <- findExecutable "proto-lens-protoc"
case maybeProtoLensProtoc of
Nothing -> error "Couldn't find executable proto-lens-protoc."
Just protoLensProtoc -> do
createDirectoryIfMissing True output
callProcess "protoc" $
[ "--plugin=protoc-gen-haskell=" ++ protoLensProtoc
, "--haskell_out=" ++ output
]
++ ["--proto_path=" ++ p | p <- imports]
++ files