{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
import Control.Monad.Extra (ifM)
import qualified Data.ByteString as BS
import qualified Data.List.NonEmpty as NEL
import Data.Text.Encoding (encodeUtf8)
import Distribution.Types.PackageName (mkPackageName)
import qualified Options.Applicative as Opts
import StackageToHackage.Hackage (printFreeze, printProject,
stackToCabal)
import StackageToHackage.Stackage (localDirs, readStack)
import System.Directory (doesDirectoryExist,
listDirectory)
import System.FilePath (takeBaseName, takeDirectory,
takeExtension, (</>))
main :: IO ()
main = do
Options{input} <- Opts.execParser $
Opts.info (Opts.helper <*> optionsParser) Opts.fullDesc
stack <- readStack =<< BS.readFile input
let dir = (takeDirectory input)
subs = NEL.toList $ (dir </>) <$> localDirs stack
cabals <- concat <$> traverse (globExt ".cabal") subs
-- assumes that .cabal files are named correctly, otherwise we need
-- PackageDescription-Parsec.html#v:readGenericPackageDescription
let ignore = (mkPackageName . takeBaseName) <$> cabals
(project, freeze) <- stackToCabal ignore dir stack
BS.writeFile (dir </> "cabal.project") (encodeUtf8 $ printProject project)
BS.writeFile (dir </> "cabal.project.freeze") (encodeUtf8 $ printFreeze freeze)
globExt :: String -> FilePath -> IO [FilePath]
globExt ext path = do
files <- ifM (doesDirectoryExist path) (listDirectory path) (pure [])
pure $ filter ((ext ==) . takeExtension) files
data Options = Options
{ input :: FilePath
}
optionsParser :: Opts.Parser Options
optionsParser = Options
<$> file
where
file = Opts.strArgument
( Opts.metavar "FILENAME"
<> Opts.help "Input stack.yaml")