purescript-0.7.5.1: psc/Main.hs
-----------------------------------------------------------------------------
--
-- Module : Main
-- Copyright : (c) 2013-15 Phil Freeman, (c) 2014-15 Gary Burgess
-- License : MIT (http://opensource.org/licenses/MIT)
--
-- Maintainer : Phil Freeman <paf31@cantab.net>
-- Stability : experimental
-- Portability :
--
-- |
--
-----------------------------------------------------------------------------
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TupleSections #-}
module Main where
import Control.Applicative
import Control.Monad
import Control.Monad.Error.Class (MonadError(..))
import Control.Monad.Writer.Strict
import Data.List (isSuffixOf, partition)
import Data.Version (showVersion)
import qualified Data.Map as M
import Options.Applicative as Opts
import System.Exit (exitSuccess, exitFailure)
import System.IO (hPutStrLn, stderr)
import System.IO.UTF8
import System.FilePath.Glob (glob)
import qualified Language.PureScript as P
import qualified Paths_purescript as Paths
import Language.PureScript.Make
data PSCMakeOptions = PSCMakeOptions
{ pscmInput :: [FilePath]
, pscmForeignInput :: [FilePath]
, pscmOutputDir :: FilePath
, pscmOpts :: P.Options
, pscmUsePrefix :: Bool
}
data InputOptions = InputOptions
{ ioInputFiles :: [FilePath]
}
compile :: PSCMakeOptions -> IO ()
compile (PSCMakeOptions inputGlob inputForeignGlob outputDir opts usePrefix) = do
input <- globWarningOnMisses warnFileTypeNotFound inputGlob
when (null input) $ do
hPutStrLn stderr "psc: No input files."
exitFailure
let (jsFiles, pursFiles) = partition (isSuffixOf ".js") input
moduleFiles <- readInput (InputOptions pursFiles)
inputForeign <- globWarningOnMisses warnFileTypeNotFound inputForeignGlob
foreignFiles <- forM (inputForeign ++ jsFiles) (\inFile -> (inFile,) <$> readUTF8File inFile)
case runWriterT (parseInputs moduleFiles foreignFiles) of
Left errs -> do
hPutStrLn stderr (P.prettyPrintMultipleErrors (P.optionsVerboseErrors opts) errs)
exitFailure
Right ((ms, foreigns), warnings) -> do
when (P.nonEmpty warnings) $
hPutStrLn stderr (P.prettyPrintMultipleWarnings (P.optionsVerboseErrors opts) warnings)
let filePathMap = M.fromList $ map (\(fp, P.Module _ _ mn _ _) -> (mn, fp)) ms
makeActions = buildMakeActions outputDir filePathMap foreigns usePrefix
(e, warnings') <- runMake opts $ P.make makeActions (map snd ms)
when (P.nonEmpty warnings') $
hPutStrLn stderr (P.prettyPrintMultipleWarnings (P.optionsVerboseErrors opts) warnings')
case e of
Left errs -> do
hPutStrLn stderr (P.prettyPrintMultipleErrors (P.optionsVerboseErrors opts) errs)
exitFailure
Right _ -> exitSuccess
warnFileTypeNotFound :: String -> IO ()
warnFileTypeNotFound = hPutStrLn stderr . ("psc: No files found using pattern: " ++)
globWarningOnMisses :: (String -> IO ()) -> [FilePath] -> IO [FilePath]
globWarningOnMisses warn = concatMapM globWithWarning
where
globWithWarning pattern = do
paths <- glob pattern
when (null paths) $ warn pattern
return paths
concatMapM f = liftM concat . mapM f
readInput :: InputOptions -> IO [(Either P.RebuildPolicy FilePath, String)]
readInput InputOptions{..} = forM ioInputFiles $ \inFile -> (Right inFile, ) <$> readUTF8File inFile
parseInputs :: (Functor m, Applicative m, MonadError P.MultipleErrors m, MonadWriter P.MultipleErrors m)
=> [(Either P.RebuildPolicy FilePath, String)]
-> [(FilePath, P.ForeignJS)]
-> m ([(Either P.RebuildPolicy FilePath, P.Module)], M.Map P.ModuleName FilePath)
parseInputs modules foreigns =
(,) <$> P.parseModulesFromFiles (either (const "") id) modules
<*> P.parseForeignModulesFromFiles foreigns
inputFile :: Parser FilePath
inputFile = strArgument $
metavar "FILE"
<> help "The input .purs file(s)"
inputForeignFile :: Parser FilePath
inputForeignFile = strOption $
short 'f'
<> long "ffi"
<> help "The input .js file(s) providing foreign import implementations"
outputDirectory :: Parser FilePath
outputDirectory = strOption $
short 'o'
<> long "output"
<> Opts.value "output"
<> showDefault
<> help "The output directory"
requirePath :: Parser (Maybe FilePath)
requirePath = optional $ strOption $
short 'r'
<> long "require-path"
<> help "The path prefix to use for require() calls in the generated JavaScript"
noTco :: Parser Bool
noTco = switch $
long "no-tco"
<> help "Disable tail call optimizations"
noMagicDo :: Parser Bool
noMagicDo = switch $
long "no-magic-do"
<> help "Disable the optimization that overloads the do keyword to generate efficient code specifically for the Eff monad"
noOpts :: Parser Bool
noOpts = switch $
long "no-opts"
<> help "Skip the optimization phase"
comments :: Parser Bool
comments = switch $
short 'c'
<> long "comments"
<> help "Include comments in the generated code"
verboseErrors :: Parser Bool
verboseErrors = switch $
short 'v'
<> long "verbose-errors"
<> help "Display verbose error messages"
noPrefix :: Parser Bool
noPrefix = switch $
short 'p'
<> long "no-prefix"
<> help "Do not include comment header"
options :: Parser P.Options
options = P.Options <$> noTco
<*> noMagicDo
<*> pure Nothing
<*> noOpts
<*> verboseErrors
<*> (not <$> comments)
<*> requirePath
pscMakeOptions :: Parser PSCMakeOptions
pscMakeOptions = PSCMakeOptions <$> many inputFile
<*> many inputForeignFile
<*> outputDirectory
<*> options
<*> (not <$> noPrefix)
main :: IO ()
main = execParser opts >>= compile
where
opts = info (version <*> helper <*> pscMakeOptions) infoModList
infoModList = fullDesc <> headerInfo <> footerInfo
headerInfo = header "psc - Compiles PureScript to Javascript"
footerInfo = footer $ "psc " ++ showVersion Paths.version
version :: Parser (a -> a)
version = abortOption (InfoMsg (showVersion Paths.version)) $ long "version" <> help "Show the version number" <> hidden