packages feed

chs-cabal-0.1.1.3: src/Distribution/C2Hs/TopSort.hs

{-# LANGUAGE DataKinds #-}

-- | Topological sort for @.chs@ files according to @{\#import\#}@s.
module Distribution.C2Hs.TopSort ( reorderC2Hs ) where

import           Control.Applicative                  (pure)
import           Data.Functor                         (($>))
import           Data.Traversable                     (traverse)
import           Distribution.Compat.Graph            (Node (..),
                                                       fromDistinctList,
                                                       revTopSort)
import           Distribution.ModuleName              (ModuleName)
import           Distribution.Parsec                  (simpleParsec)
import           Distribution.Simple.PreProcess.Types (Suffix (..))
import           Distribution.Utils.Path               (FileOrDir (..), Pkg, Source, (</>),
                                                        SymbolicPath,
                                                        interpretSymbolicPathCWD)
import           Distribution.Simple.Utils            (findModuleFileEx,
                                                       warn)
import           Distribution.Verbosity               (Verbosity)
import           Language.Haskell.CHs.Deps            (getFileImports)

type CabalDir = SymbolicPath Pkg (Dir Source)

-- | Given a list of 'ModuleName's, sort it according to @c2hs@ @{\#import\#}@
-- declarations.
reorderC2Hs :: Verbosity
            -> [CabalDir] -- ^ Source directories
            -> [ModuleName] -- ^ Module names
            -> IO [ModuleName] -- ^ Sorted modules
reorderC2Hs v dirs preMods = do

    chsFiles <- traverse (fmap (interpretSymbolicPathCWD . uncurry (</>)) . findCHS) preMods

    modDeps <- traverse (extractDeps v) (zip preMods chsFiles)

    pure $ fmap (\(N m _ _) -> m) (revTopSort $ fromDistinctList modDeps)

        where findCHS = findModuleFileEx v dirs [Suffix ".chs"]

-- | Given a 'ModuleName' and its corresponding filepath, return a 'Node'
-- with its associated @c2hs@ dependencies
extractDeps :: Verbosity -> (ModuleName, FilePath) -> IO (Node ModuleName ModuleName)
extractDeps v (m, f) = do
    res <- getFileImports f
    mods <- case res of
        Right ms -> case traverse simpleParsec ms of
            Just ms' -> pure ms'
            Nothing -> warn v ("Cannot parse module name in .chs file " ++ f) $> []
        Left err -> warn v ("Cannot parse c2hs import in " ++ f ++ ": " ++ err) $> []
    pure (N m m mods)