packages feed

ivory-artifact (empty) → 0.1.0.3

raw patch · 7 files changed

+362/−0 lines, 7 filesdep +HStringTemplatedep +basedep +directorysetup-changed

Dependencies added: HStringTemplate, base, directory, filepath, text, utf8-string

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c)2014, Galois, Inc++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Galois, Inc nor the names of its contributors+      may be used to endorse or promote products derived from this software+      without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ ivory-artifact.cabal view
@@ -0,0 +1,32 @@+name:                ivory-artifact+synopsis:            Manage additional data files during Ivory compilation.+description:         Mostly used by other Ivory libraries and backends and typically shouldn't have to be used directly by the user.+version:             0.1.0.3+author:              Galois, Inc.+maintainer:          leepike@galois.com+category:            Language+homepage:            http://ivorylang.org+build-type:          Simple+cabal-version:       >= 1.10+license:             BSD3+license-file:        LICENSE+source-repository    this+  type:     git+  location: https://github.com/GaloisInc/ivory+  tag:      hackage-0103++library+  exposed-modules:      Ivory.Artifact,+                        Ivory.Artifact.Template,+                        Ivory.Artifact.Location,+                        Ivory.Artifact.Transformer+  build-depends:        base >= 4.6 && < 5,+                        filepath,+                        directory,+                        text,+                        utf8-string,+                        HStringTemplate+  hs-source-dirs:       src+  default-language:     Haskell2010++  ghc-options:          -Wall
+ src/Ivory/Artifact.hs view
@@ -0,0 +1,208 @@+module Ivory.Artifact (+  -- * Ivory Artifacts+  -- | Artifacts permit the user to generate non-Ivory language files+  -- as part of an Ivory program's build process. Artifacts are typically+  -- used to encapsulate helper code (such as Ivory.Stdlib's string functions+  -- helper .c and .h files) or to generate debug or metadata output. The+  -- contents of an `Artifact` are given by some external file, or as a string.+  --+  -- Artifacts are exposed as an abstract type `Artifact` with a set of+  -- constructors, an accessor `artifactFileName`, and two functions,+  -- `putArtifact` and `printArtifact`, which are used to write an+  -- Artifacts to a file or print it to stdout, respectively.+  --+  -- Users may specify transformations on the contents of an artifact.+  -- Optionally, these transformations can throw an error. This is useful when+  -- using artifact files as templates.+  --+    Artifact()+  -- | Gives the file name that will be used when writing the `Artifact` to the+  -- filesystem.+  , artifactFileName++  -- * Artifact constructors+  -- | `artifactFile` creates an `Artifact` from an output filename of type+  -- `FilePath` and an input filepath of type `IO FilePath`. The output filename+  -- should be a simple filename, any directory information will be dropped. The+  -- input filepath should be an absolute path. The input filepath is in the IO+  -- monad for compatiblility with Cabal's `getDataDir` data files functionality.+  , artifactFile++  -- | `artifactCabalFile` creates an `Artifact` given an input directory of+  -- type `IO FilePath` and an input filepath, relative to the input directory+  -- in the preceeding argument, of type `FilePath`.+  --+  -- This function is designed to be used with a Cabal Paths_(packagename)+  -- `getDataDir`, which has type `IO FilePath`, and a path to a file inside the+  -- data directory.  It is implemented in terms of `artifactFileName`. The+  -- output filename is given by dropping directory information from the input+  -- filepath.+  , artifactCabalFile++  -- | `artifactText` creates an `Artifact` from an output filename of type+  -- `FilePath` and with contents of type `Text`.+  , artifactText+  -- | `artifactString` creates an `Artifact` from an output filename of type+  -- `FilePath` with contents of type `String`. Implemented in terms of+  -- `artifactText`.+  , artifactString++  -- | `artifactTransform` and `artifactTransformString` specify a+  -- transformation on the contents of an `Artifact`.+  , artifactTransform, artifactTransformString++  -- | `artifactTransformErr` and `artifactTransformErrString` specify a+  -- transformation on the contents of an `Artifact` which may give an error.+  , artifactTransformErr, artifactTransformErrString++  -- | `artifactPath` prepends a `FilePath` to the output file path.+  , artifactPath++  -- * Artifact actions+  -- | Takes a directory of type `FilePath` and an `Artifact`+  -- writes each `Artifact` to the file system or gives an error explaining why+  -- not. `Maybe String` containins errors encountered when an `Artifact` is+  -- transformed, or specified by an input filename which does not exist.+  , putArtifact+  -- | like `putArtifact` but ignores any errors.+  , putArtifact_++  -- | Takes an `Artifact` and prints it, or an appropriate error message, to+  -- stdout.+  , printArtifact++  -- | Takes a guess at whether two artifacts might be equal.+  , mightBeEqArtifact++  , Located(..)+  , mightBeEqLocatedArtifact+  ) where++import Control.Monad (void)+import qualified Data.Text.Lazy as T+import qualified Data.Text.Lazy.IO as T+import System.FilePath+import System.Directory+import Ivory.Artifact.Transformer+import Ivory.Artifact.Location+import System.IO.Unsafe (unsafePerformIO)++data Artifact =+  Artifact+    { artifact_outputname  :: FilePath+    , artifact_contents    :: AContents+    , artifact_transformer :: Transformer T.Text+    }++mightBeEqLocatedArtifact :: Located Artifact -> Located Artifact -> Bool+mightBeEqLocatedArtifact (Root a) (Root b) = mightBeEqArtifact a b+mightBeEqLocatedArtifact (Src a) (Src b) = mightBeEqArtifact a b+mightBeEqLocatedArtifact (Incl a) (Incl b) = mightBeEqArtifact a b+mightBeEqLocatedArtifact _ _ = False++mightBeEqArtifact :: Artifact -> Artifact -> Bool+mightBeEqArtifact a b =+  and [ artifact_outputname a == artifact_outputname b+      , artifact_contents   a `mightBeEqAContents` artifact_contents   b]++data AContents = LiteralContents T.Text+               | FileContents (IO FilePath)++mightBeEqAContents :: AContents -> AContents -> Bool+mightBeEqAContents (LiteralContents a) (LiteralContents b) = a == b+mightBeEqAContents (FileContents a) (FileContents b) = unsafePerformIO a == unsafePerformIO b+mightBeEqAContents _ _ = False++artifactFileName :: Artifact -> FilePath+artifactFileName = artifact_outputname++artifactFile :: FilePath -> IO FilePath -> Artifact+artifactFile outputname inputpath = Artifact+  { artifact_outputname  = takeFileName outputname+  , artifact_contents    = FileContents inputpath+  , artifact_transformer = emptyTransformer+  }++artifactCabalFile :: IO FilePath -> FilePath -> Artifact+artifactCabalFile inputdir inputfname =+  artifactFile (takeFileName inputfname)+               (fmap (\i -> (i </> inputfname)) inputdir)++artifactText :: FilePath -> T.Text -> Artifact+artifactText outputname t = Artifact+  { artifact_outputname  = takeFileName outputname+  , artifact_contents    = LiteralContents t+  , artifact_transformer = emptyTransformer+  }++artifactPath :: FilePath -> Artifact -> Artifact+artifactPath f a = a { artifact_outputname = f </> artifact_outputname a }++artifactString :: FilePath -> String -> Artifact+artifactString f s = artifactText f (T.pack s)++artifactTransform :: (T.Text -> T.Text) -> Artifact -> Artifact+artifactTransform f a =+  a { artifact_transformer = transform f (artifact_transformer a) }++artifactTransformString :: (String -> String) -> Artifact -> Artifact+artifactTransformString f a = artifactTransform f' a+  where f' = T.pack . f . T.unpack++artifactTransformErr :: (T.Text -> Either String T.Text) -> Artifact -> Artifact+artifactTransformErr f a =+  a { artifact_transformer = transformErr f (artifact_transformer a) }++artifactTransformErrString :: (String -> Either String String) -> Artifact -> Artifact+artifactTransformErrString f a = artifactTransformErr f' a+  where f' t = fmap T.pack (f (T.unpack t))++getArtifact :: Artifact -> IO (Either String T.Text)+getArtifact a = g (artifact_contents a)+  where+  runT t = runTransformer (artifact_transformer a) t+  g (LiteralContents t) = return (runT t)+  g (FileContents getf) = do+      srcpath <- getf+      -- Check if srcpath exists. If it does not, give an error+      exists <- doesFileExist srcpath+      case exists of+        True -> do+          t <- T.readFile srcpath+          return (runT t)+        False -> return (Left (notfound srcpath))++  notfound srcpath = "Path " ++ srcpath ++ " for Artifact named "+     ++ artifact_outputname a ++ " could not be found."++withContents :: Artifact -> (T.Text -> IO ()) -> IO (Maybe String)+withContents a f = do+  contents <- getArtifact a+  case contents of+    Left err -> return (Just err)+    Right c  -> f c >> return Nothing++putArtifact :: FilePath -> Artifact -> IO (Maybe String)+putArtifact fp a = withContents a $ \c -> do+  let fname = fp </> artifact_outputname a+  createDirectoryIfMissing True (dropFileName fname)+  T.writeFile fname c++putArtifact_ :: FilePath -> Artifact -> IO ()+putArtifact_ fp a = void (putArtifact fp a)++printArtifact :: Artifact -> IO ()+printArtifact a = do+  res <- withContents a aux+  case res of+    Nothing -> return ()+    Just err -> putStrLn $+      "Encountered error when creating artifact " ++ artifact_outputname a+        ++ ":\n" ++ err+  where+  aux c = do+    putStrLn ("Artifact " ++ artifact_outputname a)+    putStrLn "================"+    T.putStrLn c+    putStrLn "================"+
+ src/Ivory/Artifact/Location.hs view
@@ -0,0 +1,17 @@+{-# LANGUAGE DeriveFunctor #-}++module Ivory.Artifact.Location+  ( Located(..)+  ) where++-- | In the build, we generally want to put artifacts in one+-- of three locations: somewhere relative to the root directory+-- of the build output, somewhere relative to the sources, or somewhere+-- relative to the includes (headers).++data Located a+  = Root a+  | Src a+  | Incl a+  deriving (Show, Eq, Functor)+
+ src/Ivory/Artifact/Template.hs view
@@ -0,0 +1,44 @@++module Ivory.Artifact.Template+  ( artifactCabalFileTemplate+  , artifactCabalFileTemplate'+  ) where++import Ivory.Artifact+import Text.StringTemplate+import System.FilePath++artifactCabalFileTemplate :: IO FilePath -> FilePath -> [(String, String)]+                          -> Artifact+artifactCabalFileTemplate datadir templatepath attrs =+  artifactCabalFileTemplate' datadir templatepath outputpath attrs+  where+  templatename = takeFileName templatepath+  outputpath = case splitExtension templatename of+    (a, ext) | ext == ".template" -> a+    _ -> templatename++artifactCabalFileTemplate' :: IO FilePath -> FilePath -> FilePath -> [(String, String)]+                          -> Artifact+artifactCabalFileTemplate' datadir templatepath outputpath attrs =+  artifactTransformErrString applyconf af+  where+  af = artifactFile outputpath (fmap (\f -> f </> templatepath) datadir)+  templatename = takeFileName templatepath++  applyconf s =+    let t  = newSTMP s :: StringTemplate String+        t' = setManyAttrib attrs t+    in case checkTemplate t' of+      (Just e, _, _) -> Left (parseErr e)+      (_, Just e, _) -> Left (missingAttrErr e)+      (_, _, Just e) -> Left (missingTemplate e)+      (_, _, _) -> Right (toString t')+  prefix = "Error in " ++ templatename ++ ": "+  parseErr e = prefix ++ "Failed to parse: \n" ++ e+  missingAttrErr es =  prefix ++ "The following attributes are missing:\n"+                    ++ unlines es+  missingTemplate es =  prefix ++ "Failed to lookup invoked templates: \n"+                     ++ unlines es++
+ src/Ivory/Artifact/Transformer.hs view
@@ -0,0 +1,29 @@++module Ivory.Artifact.Transformer+  ( Transformer()+  , emptyTransformer+  , transform+  , transformErr+  , runTransformer+  ) where++newtype Transformer a =+  Transformer+    { unTransformer :: a -> Either String a+    }++emptyTransformer :: Transformer a+emptyTransformer  = Transformer Right++transform :: (a -> a) -> Transformer a -> Transformer a+transform f (Transformer t) = Transformer $ \a -> do+  a' <- t a+  return (f a')++transformErr :: (a -> Either String a) -> Transformer a -> Transformer a+transformErr f (Transformer t) = Transformer $ \a -> do+  a' <- t a+  f a'++runTransformer :: Transformer a -> a -> Either String a+runTransformer  = unTransformer