Commit dbc3d6dc authored by Kenton Varda's avatar Kenton Varda

Flesh out capnpc command-line interface.

parent 99bdcc1b
...@@ -89,7 +89,7 @@ capnpc_outputs = \ ...@@ -89,7 +89,7 @@ capnpc_outputs = \
# around in the generated Makefile a bit but couldn't figure it out. I give # around in the generated Makefile a bit but couldn't figure it out. I give
# up. Automake is terrible. # up. Automake is terrible.
test_capnpc_middleman: test_capnpc_middleman:
$(CAPNPC) $(capnpc_inputs) $(CAPNPC) -oc++ $(capnpc_inputs)
touch test_capnpc_middleman touch test_capnpc_middleman
$(capnpc_outputs): test_capnpc_middleman $(capnpc_outputs): test_capnpc_middleman
......
...@@ -49,4 +49,4 @@ fi ...@@ -49,4 +49,4 @@ fi
# When exception stack traces are needed, add: +RTS -xc -RTS # When exception stack traces are needed, add: +RTS -xc -RTS
LD_PRELOAD=$INTERCEPTOR DYLD_FORCE_FLAT_NAMESPACE= DYLD_INSERT_LIBRARIES=$INTERCEPTOR \ LD_PRELOAD=$INTERCEPTOR DYLD_FORCE_FLAT_NAMESPACE= DYLD_INSERT_LIBRARIES=$INTERCEPTOR \
$CAPNPC "$INPUT" 3>&1 4<&0 >&2 $CAPNPC -oc++ "$INPUT" 3>&1 4<&0 >&2
...@@ -19,8 +19,8 @@ executable capnpc ...@@ -19,8 +19,8 @@ executable capnpc
hastache, hastache,
array, array,
data-binary-ieee754, data-binary-ieee754,
filepath filepath,
-- When profiling is needed, add: -prof -fprof-auto -osuf p_o -hisuf p_hi directory
ghc-options: -Wall -fno-warn-missing-signatures ghc-options: -Wall -fno-warn-missing-signatures
other-modules: other-modules:
Lexer, Lexer,
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TemplateHaskell #-}
module CxxGenerator(generateCxxHeader, generateCxxSource) where module CxxGenerator(generateCxx) where
import qualified Data.ByteString.UTF8 as ByteStringUTF8 import qualified Data.ByteString.UTF8 as ByteStringUTF8
import Data.FileEmbed(embedFile) import Data.FileEmbed(embedFile)
...@@ -244,3 +244,8 @@ hastacheConfig = MuConfig ...@@ -244,3 +244,8 @@ hastacheConfig = MuConfig
generateCxxHeader file = hastacheStr hastacheConfig (encodeStr headerTemplate) (fileContext file) generateCxxHeader file = hastacheStr hastacheConfig (encodeStr headerTemplate) (fileContext file)
generateCxxSource file = hastacheStr hastacheConfig (encodeStr srcTemplate) (fileContext file) generateCxxSource file = hastacheStr hastacheConfig (encodeStr srcTemplate) (fileContext file)
generateCxx file = do
header <- generateCxxHeader file
source <- generateCxxSource file
return [(fileName file ++ ".h", header), (fileName file ++ ".c++", source)]
...@@ -25,66 +25,112 @@ module Main ( main ) where ...@@ -25,66 +25,112 @@ module Main ( main ) where
import System.Environment import System.Environment
import System.Console.GetOpt import System.Console.GetOpt
import System.Exit(exitFailure, exitSuccess)
import System.IO(hPutStr, stderr)
import System.FilePath(takeDirectory)
import System.Directory(createDirectoryIfMissing, doesDirectoryExist)
import Control.Monad
import Compiler import Compiler
import Util(delimit) import Util(delimit)
import Text.Parsec.Pos import Text.Parsec.Pos
import Text.Parsec.Error import Text.Parsec.Error
import Text.Printf(printf) import Text.Printf(printf)
import qualified Data.List as List import qualified Data.List as List
import qualified Data.Map as Map
import qualified Data.ByteString.Lazy.Char8 as LZ import qualified Data.ByteString.Lazy.Char8 as LZ
import Data.List
import Data.Maybe
import Semantics import Semantics
import CxxGenerator import CxxGenerator(generateCxx)
main::IO() type GeneratorFn = FileDesc -> IO [(FilePath, LZ.ByteString)]
generatorFns = Map.fromList [ ("c++", generateCxx) ]
data Opt = OutputOpt String (Maybe GeneratorFn) FilePath
| VerboseOpt
| HelpOpt
main :: IO ()
main = do main = do
let options = [Option ['o'] ["output"] (ReqArg id "FILE") "Where to send the files"] let optionDescs =
[ Option "o" ["output"] (ReqArg parseOutputArg "LANG[:DIR]")
("Generate output for language LANG\n\
\to directory DIR (default: current\n\
\directory). LANG may be any of:\n\
\ " ++ unwords (Map.keys generatorFns))
, Option "v" ["verbose"] (NoArg VerboseOpt) "Write information about parsed files."
, Option "h" ["help"] (NoArg HelpOpt) "Print usage info and exit."
]
let usage = usageInfo
"capnpc [OPTION]... [FILE]...\n\
\Generate source code based on Cap'n Proto definition FILEs.\n"
optionDescs
args <- getArgs args <- getArgs
let tup = getOpt RequireOrder options args let (options, files, optErrs) = getOpt Permute optionDescs args
let (optionResults, files, _) = tup let langErrs = map (printf "Unknown output language: %s\n")
let langDirs = catMaybes (map splitAtEquals optionResults) [lang | OutputOpt lang Nothing _ <- options]
handleFilesLangs (generatorFnsFor langDirs) files let errs = optErrs ++ langErrs
unless (null errs) (do
mapM_ (hPutStr stderr) errs
hPutStr stderr usage
exitFailure)
when (null options) (do
hPutStr stderr "Nothing to do.\n"
hPutStr stderr usage
exitFailure)
splitAtEquals :: String -> Maybe (String, String) let isHelp = not $ null [opt | opt@HelpOpt <- options]
splitAtEquals str = do
holder <- (elemIndex '=' str)
Just((splitAt holder str))
handleFilesLangs eithers files = mapM_ (\x -> handleFiles x files) eithers when isHelp (do
putStr usage
exitSuccess)
handleFiles (Right fn) files = mapM_ (handleFile fn) files let isVerbose = not $ null [opt | opt@VerboseOpt <- options]
handleFiles (Left str) _ = putStrLn str let outputs = [(fn, dir) | OutputOpt _ (Just fn) dir <- options]
handleFile generateCode filename = do let verifyDirectoryExists dir = do
exists <- doesDirectoryExist dir
unless exists (do
hPutStr stderr $ printf "no such directory: %s\n" dir
exitFailure)
mapM_ verifyDirectoryExists [dir | (_, dir) <- outputs]
mapM_ (handleFile outputs isVerbose) files
parseOutputArg :: String -> Opt
parseOutputArg str = case List.elemIndex ':' str of
Just i -> let (lang, _:dir) = splitAt i str in OutputOpt lang (Map.lookup lang generatorFns) dir
Nothing -> OutputOpt str (Map.lookup str generatorFns) "."
handleFile :: [(GeneratorFn, FilePath)] -> Bool -> FilePath -> IO ()
handleFile outputs isVerbose filename = do
text <- readFile filename text <- readFile filename
case parseAndCompileFile filename text of case parseAndCompileFile filename text of
Active desc [] -> do Active desc [] -> do
print desc when isVerbose (print desc)
generateCode desc filename
Active _ e -> mapM_ printError (List.sortBy compareErrors e)
Failed e -> mapM_ printError (List.sortBy compareErrors e)
generatorFnsFor :: [(String, String)] -> [Either String (FileDesc -> FilePath -> (IO ()))] let write dir (name, content) = do
generatorFnsFor langDirs = do let outFilename = dir ++ "/" ++ name
map (\langDir -> generatorFnFor (fst langDir) (tail (snd langDir)))langDirs createDirectoryIfMissing True $ takeDirectory outFilename
LZ.writeFile outFilename content
let generate (generatorFn, dir) = do
files <- generatorFn desc
mapM_ (write dir) files
mapM_ generate outputs
generatorFnFor :: String -> String -> Either String (FileDesc -> FilePath -> (IO ())) Active _ e -> do
generatorFnFor lang dir = case lang of mapM_ printError (List.sortBy compareErrors e)
"c++" -> Right (\desc filename -> do exitFailure
header <- generateCxxHeader desc Failed e -> do
LZ.writeFile (dir ++ "/" ++ filename ++ ".h") header mapM_ printError (List.sortBy compareErrors e)
source <- generateCxxSource desc exitFailure
LZ.writeFile (dir ++ "/" ++ filename ++ ".c++") source)
_ -> Left "Only c++ is supported for now"
compareErrors a b = compare (errorPos a) (errorPos b) compareErrors a b = compare (errorPos a) (errorPos b)
-- TODO: This is a fairly hacky way to make showErrorMessages' output not suck. We could do better -- TODO: This is a fairly hacky way to make showErrorMessages' output not suck. We could do better
-- by interpreting the error structure ourselves. -- by interpreting the error structure ourselves.
printError e = printf "%s:%d:%d: %s\n" f l c m' where printError e = hPutStr stderr $ printf "%s:%d:%d: %s\n" f l c m' where
pos = errorPos e pos = errorPos e
f = sourceName pos f = sourceName pos
l = sourceLine pos l = sourceLine pos
......
...@@ -389,7 +389,9 @@ data CompiledStatement = CompiledMember Desc ...@@ -389,7 +389,9 @@ data CompiledStatement = CompiledMember Desc
-- TODO: Print options as well as members. Will be ugly-ish. -- TODO: Print options as well as members. Will be ugly-ish.
descToCode :: String -> Desc -> String descToCode :: String -> Desc -> String
descToCode indent (DescFile desc) = concatMap (statementToCode indent) (fileStatements desc) descToCode indent (DescFile desc) = printf "# %s\n%s"
(fileName desc)
(concatMap (statementToCode indent) (fileStatements desc))
descToCode indent (DescAlias desc) = printf "%susing %s = %s;\n" indent descToCode indent (DescAlias desc) = printf "%susing %s = %s;\n" indent
(aliasName desc) (aliasName desc)
(descQualifiedName (aliasParent desc) (aliasTarget desc)) (descQualifiedName (aliasParent desc) (aliasTarget desc))
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment