212 lines
6.8 KiB
Haskell
212 lines
6.8 KiB
Haskell
{-
|
|
Mackerel: a strawman device definition DSL for Barrelfish
|
|
|
|
Copyright (c) 2007-2011, ETH Zurich.
|
|
All rights reserved.
|
|
|
|
This file is distributed under the terms in the attached LICENSE file.
|
|
If you do not find this file, copies can be found by writing to:
|
|
ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
|
|
-}
|
|
|
|
module Main where
|
|
|
|
import System.IO
|
|
import System.IO.Error
|
|
import System.Console.GetOpt
|
|
import System.FilePath
|
|
import System.Exit
|
|
import System.Environment
|
|
import Data.Maybe
|
|
import Data.List
|
|
import Text.ParserCombinators.Parsec as Parsec
|
|
import Text.Printf
|
|
import qualified MackerelParser
|
|
import qualified BitFieldDriver
|
|
import qualified ShiftDriver
|
|
import Checks
|
|
import Dev
|
|
import Control.Exception
|
|
|
|
--
|
|
-- Command line options and parsing code
|
|
--
|
|
|
|
-- Datatypes for carrying command options around
|
|
data Target = BitFieldDriver | ShiftDriver | NullDriver deriving (Eq, Show)
|
|
|
|
data Options = Options {
|
|
opt_infilename :: Maybe String,
|
|
opt_outfilename :: Maybe String,
|
|
opt_includedirs :: [String],
|
|
opt_target :: Target,
|
|
opt_usage_error :: Bool,
|
|
opt_verbosity :: Integer
|
|
} deriving (Show,Eq)
|
|
|
|
defaultOptions :: Options
|
|
defaultOptions = Options {
|
|
opt_infilename = Nothing,
|
|
opt_outfilename = Nothing,
|
|
opt_includedirs = [],
|
|
opt_target = ShiftDriver,
|
|
opt_usage_error = False,
|
|
opt_verbosity = 1 }
|
|
|
|
-- For driving System.GetOpt
|
|
options :: [OptDescr (Options -> Options)]
|
|
options =
|
|
[ Option ['c'] ["input-file"]
|
|
(ReqArg (\f opts -> opts { opt_infilename = Just f } ) "file")
|
|
"input file"
|
|
, Option ['I'] ["include-dir"]
|
|
(ReqArg (\ d opts -> opts { opt_includedirs = opt_includedirs opts ++ [d] }) "dir")
|
|
"include directory (can be given multiple times)"
|
|
, Option ['v'] ["verbose"]
|
|
(NoArg (\ opts -> opts { opt_verbosity = opt_verbosity opts + 1 } ))
|
|
"increase verbosity level"
|
|
, Option ['o'] ["output"]
|
|
(ReqArg (\ f opts -> opts { opt_outfilename = Just f }) "file")
|
|
"output file name"
|
|
, Option ['S'] ["shift-driver"]
|
|
(NoArg (\ opts -> opts { opt_target = ShiftDriver } ))
|
|
"use shift driver (default; preferred)"
|
|
, Option ['n'] ["null-driver"]
|
|
(NoArg (\ opts -> opts { opt_target = NullDriver } ))
|
|
"use null output driver (don't generate any C)"
|
|
, Option ['B'] ["bitfield-driver"]
|
|
(NoArg (\ opts -> opts { opt_target = BitFieldDriver } ))
|
|
"use bitfield driver (deprecrated: do not use)"
|
|
]
|
|
|
|
--
|
|
-- Set the correct default input and output files
|
|
--
|
|
|
|
defaultOutput :: Options -> Options
|
|
defaultOutput opts =
|
|
case opt_outfilename opts of
|
|
(Just _) ->
|
|
opts
|
|
Nothing ->
|
|
opts { opt_outfilename =
|
|
Just (case opt_infilename opts of
|
|
(Just i) ->
|
|
((dropExtension $ takeFileName i) ++ "_dev.h")
|
|
Nothing -> "mackerel_output.h"
|
|
)
|
|
}
|
|
|
|
defaultInput :: Options -> [String] -> IO (Options)
|
|
defaultInput opts [f] =
|
|
if isNothing $ opt_infilename opts
|
|
then return (defaultOutput (opts { opt_infilename = Just f }))
|
|
else usageError []
|
|
defaultInput opts [] =
|
|
if (isJust $ opt_infilename opts)
|
|
then return (defaultOutput opts)
|
|
else usageError []
|
|
defaultInput _ _ = usageError []
|
|
|
|
compilerOpts :: [String] -> IO (Options)
|
|
compilerOpts argv =
|
|
case getOpt Permute options argv of
|
|
(o,n,[]) -> defaultInput (foldl (flip id) defaultOptions o) n
|
|
(_,_,errs) -> usageError errs
|
|
|
|
usageError :: [String] -> IO (Options)
|
|
usageError errs =
|
|
ioError (userError (concat errs ++ usageInfo usage options))
|
|
where usage = "Usage: mackerel <options> <input file>"
|
|
|
|
--
|
|
-- Processing source files
|
|
---
|
|
|
|
-- Null compilation
|
|
nullCompiler :: String -> String -> Dev.Rec -> String
|
|
nullCompiler _ _ _ = ""
|
|
|
|
-- Perform run-time checks
|
|
run_checks :: String -> Dev.Rec -> IO String
|
|
run_checks input_fn dev =
|
|
case (Checks.check_all input_fn dev) of
|
|
Just errors ->
|
|
do { (hPutStrLn stderr (unlines [ e ++ "\n" | e <-errors]))
|
|
; System.Exit.exitWith (ExitFailure 1)
|
|
}
|
|
Nothing -> do { return "" }
|
|
|
|
-- Parsing the input file into an AST
|
|
parseFile :: String -> IO MackerelParser.DeviceFile
|
|
parseFile fname = do
|
|
src <- readFile fname
|
|
case (runParser MackerelParser.devfile () fname src) of
|
|
Left err -> ioError $ userError ("Parse error at: " ++ (show err))
|
|
Right x -> return x
|
|
|
|
-- Traverse the include path to find an import file
|
|
findImport :: [String] -> String -> IO MackerelParser.DeviceFile
|
|
findImport [] f =
|
|
ioError (userError $ printf "Can't find import '%s'" f)
|
|
findImport (d:t) f =
|
|
do
|
|
catch (parseFile (d </> f))
|
|
(\e -> (if isDoesNotExistError e then findImport t f else ioError e))
|
|
|
|
-- Perform the transitive closure of all the imports
|
|
|
|
resolveImports :: [MackerelParser.DeviceFile] -> [String]
|
|
-> IO [MackerelParser.DeviceFile]
|
|
resolveImports dfl path =
|
|
let allimports = nub $ concat [ il | (MackerelParser.DeviceFile _ il) <- dfl ]
|
|
gotimports = [ n | (MackerelParser.DeviceFile (MackerelParser.Device n _ _ _ _) _) <- dfl ]
|
|
required = allimports \\ gotimports
|
|
in
|
|
case required of
|
|
[] -> return dfl
|
|
(t:_) -> do { i <- (findImport path (t ++ ".dev"))
|
|
; resolveImports (dfl ++ [i]) path
|
|
}
|
|
|
|
testentry :: IO ()
|
|
testentry =
|
|
let input_fn = "../../devices/xapic.dev"
|
|
output_fn = "x2apic.dev.h"
|
|
includedirs = ["../../devices"]
|
|
in
|
|
do { hPutStrLn stdout ("IN: " ++ input_fn)
|
|
; hPutStrLn stdout ("OUT: " ++ output_fn)
|
|
; df <- parseFile input_fn
|
|
; dfl <- resolveImports [df] includedirs
|
|
; let dev = make_dev df (tail dfl) in
|
|
do { _ <- run_checks input_fn dev
|
|
; outFileD <- openFile output_fn WriteMode
|
|
; hPutStr outFileD (ShiftDriver.compile input_fn output_fn dev)
|
|
; hClose outFileD
|
|
}
|
|
}
|
|
|
|
|
|
-- Main entry point of Mackernel
|
|
main :: IO ()
|
|
main = do { cli <- System.Environment.getArgs
|
|
; opts <- compilerOpts cli
|
|
; let input_fn = fromJust $ opt_infilename opts
|
|
output_fn = fromJust $ opt_outfilename opts
|
|
in
|
|
do { df <- parseFile input_fn
|
|
; dfl <- resolveImports [df] (opt_includedirs opts)
|
|
; let dev = make_dev df (tail dfl) in
|
|
do { _ <- run_checks input_fn dev
|
|
; outFileD <- openFile output_fn WriteMode
|
|
; hPutStr outFileD ((case (opt_target opts) of
|
|
NullDriver -> nullCompiler
|
|
ShiftDriver -> ShiftDriver.compile
|
|
BitFieldDriver -> BitFieldDriver.compile)
|
|
input_fn output_fn dev)
|
|
; hClose outFileD
|
|
}
|
|
}
|
|
}
|