module Pier.Build.Module
( findModule
, findMainFile
, findBootFile
, sourceDirArtifacts
) where
import Control.Applicative ((<|>))
import Control.Monad (guard, msum)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Maybe
import Data.List (intercalate)
import Data.Semigroup
import Development.Shake
import Distribution.ModuleName
import Distribution.Package (PackageIdentifier(..), mkPackageName)
import Distribution.PackageDescription
import Distribution.Version (versionNumbers)
import Development.Shake.FilePath
import Distribution.Text (display)
import qualified Data.Set as Set
import Pier.Build.ConfiguredPackage
import Pier.Build.Executable
import Pier.Build.CFlags
import Pier.Build.Stackage
import Pier.Core.Artifact
findModule
:: InstalledGhc
-> ConfiguredPackage
-> CFlags
-> [Artifact]
-> ModuleName
-> Action Artifact
findModule ghc confd flags paths m = do
found <- runMaybeT $ genPathsModule m confd <|>
msum (map (search ghc flags m) paths)
maybe (error $ "Missing module " ++ display m
++ "; searched " ++ show paths)
return found
findMainFile
:: InstalledGhc
-> CFlags
-> [Artifact]
-> FilePath
-> Action Artifact
findMainFile ghc flags paths f = do
found <- runMaybeT $ msum $
map findFileDirectly paths ++
map (search ghc flags $ filePathToModule f) paths
maybe (error $ "Missing main file " ++ f
++ "; searched " ++ show paths)
return found
where
findFileDirectly path = do
let candidate = path /> f
exists candidate
return candidate
genPathsModule
:: ModuleName -> ConfiguredPackage -> MaybeT Action Artifact
genPathsModule m confd = do
guard $ m == pathsModule
lift $ writeArtifact ("paths" </> display m <.> "hs") $ unlines
[ "{-# LANGUAGE CPP #-}"
, "{-# LANGUAGE ImplicitPrelude #-}"
, "module " ++ display m ++ " (getDataFileName, getDataDir, version) where"
, "import Data.Version (Version(..))"
, "version = Version " ++ show (versionNumbers
$ pkgVersion pkg)
++ ""
++ " []"
, "getDataFileName :: FilePath -> IO FilePath"
, "getDataFileName f = (\\d -> d ++ \"/\" ++ f) <$> getDataDir"
, "getDataDir :: IO FilePath"
, "getDataDir = " ++ maybe err (("return " ++) . show . pathIn)
(confdDataFiles confd)
]
where
pkg = package (confdDesc confd)
pathsModule = fromString $ "Paths_" ++ map fixHyphen (display $ pkgName pkg)
fixHyphen '-' = '_'
fixHyphen c = c
err = "error " ++ show ("Missing data files from package " ++ display pkg)
search
:: InstalledGhc
-> CFlags
-> ModuleName
-> Artifact
-> MaybeT Action Artifact
search ghc flags m srcDir
= genHsc2hs <|>
genHappy "y" <|>
genHappy "ly" <|>
genAlex "x" <|>
genC2hs <|>
existing "lhs" <|>
existing "hs"
where
existing ext = let f = srcDir /> toFilePath m <.> ext
in exists f >> return f
genHappy ext = do
let yFile = srcDir /> toFilePath m <.> ext
exists yFile
let relOutput = toFilePath m <.> "hs"
happy <- lift $ askBuiltExecutable (mkPackageName "happy") "happy"
lift . runCommand (output relOutput)
$ progExe happy
["-o", relOutput, pathIn yFile]
<> input yFile
genHsc2hs = do
let hsc = srcDir /> toFilePath m <.> "hsc"
exists hsc
let relOutput = toFilePath m <.> "hs"
lift $ runCommand (output relOutput)
$ hsc2hsProg ghc
(["-o", relOutput
, pathIn hsc
]
++ ["--cflag=" ++ f | f <- ccFlags flags
++ cppFlags flags]
++ ["-I" ++ pathIn f | f <- Set.toList $ cIncludeDirs flags]
++ ghcDefines ghc)
<> input hsc <> inputs (cIncludeDirs flags)
genAlex ext = do
let xFile = srcDir /> toFilePath m <.> ext
exists xFile
let relOutput = toFilePath m <.> "hs"
alex <- lift $ askBuiltExecutable (mkPackageName "alex") "alex"
lift . runCommand (output relOutput)
$ progExe alex
["-o", relOutput, pathIn xFile]
<> input xFile
genC2hs = do
let chsFile = srcDir /> toFilePath m <.> "chs"
exists chsFile
let relOutput = toFilePath m <.> "hs"
c2hs <- lift $ askBuiltExecutable (mkPackageName "c2hs") "c2hs"
lift . runCommand (output relOutput)
$ input chsFile
<> inputs (cIncludeDirs flags)
<> progExe c2hs
(["-o", relOutput, pathIn chsFile]
++ ["--include=" ++ pathIn f | f <- Set.toList (cIncludeDirs flags)]
++ ["--cppopts=" ++ f | f <- ccFlags flags ++ cppFlags flags
++ ghcDefines ghc]
)
filePathToModule :: FilePath -> ModuleName
filePathToModule = fromString . intercalate "." . splitDirectories . dropExtension
exists :: Artifact -> MaybeT Action ()
exists f = lift (doesArtifactExist f) >>= guard
findBootFile :: Artifact -> Action (Maybe Artifact)
findBootFile hs = do
let hsBoot = replaceArtifactExtension hs "hs-boot"
bootExists <- doesArtifactExist hsBoot
return $ guard bootExists >> return hsBoot
sourceDirArtifacts :: Artifact -> BuildInfo -> [Artifact]
sourceDirArtifacts packageSourceDir bi
= map (packageSourceDir />) $ ifNullDirs $ hsSourceDirs bi
ifNullDirs :: [FilePath] -> [FilePath]
ifNullDirs [] = [""]
ifNullDirs xs = xs