packages feed

diagrams-builder 0.3.0.1 → 0.4

raw patch · 4 files changed

+199/−130 lines, 4 filesdep +splitdep +transformersdep ~diagrams-cairodep ~diagrams-libdep ~diagrams-postscriptPVP ok

version bump matches the API change (PVP)

Dependencies added: split, transformers

Dependency ranges changed: diagrams-cairo, diagrams-lib, diagrams-postscript, diagrams-svg

API changes (from Hackage documentation)

- Diagrams.Builder: buildDiagram :: (Typeable b, Typeable v, InnerSpace v, OrderedField (Scalar v), Backend b v) => b -> v -> Options b v -> [String] -> String -> [String] -> [String] -> (String -> IO (x, Maybe (Options b v -> Options b v))) -> IO (BuildResult b v x)
+ Diagrams.Builder: buildDiagram :: (Typeable b, Typeable v, InnerSpace v, OrderedField (Scalar v), Backend b v, Show (Options b v)) => b -> v -> Options b v -> [String] -> String -> [String] -> [String] -> (String -> IO (x, Maybe (Options b v -> Options b v))) -> IO (BuildResult b v x)

Files

CHANGES.markdown view
@@ -1,3 +1,15 @@+0.4: 9 August 2013+------------------++* Add hsenv compatibility.+* Big improvements in the way rebuilding is handled:+    - Strip comments before deciding whether to rebuild, so+      changing only comments does not trigger a rebuild+    - Take local imports into account: if a diagram has an import+      which corresponds to a local file, rebuild if that file has+      changed+    - Rebuild when options (e.g. size) change+ 0.3.0.1 (3 April 2013) ---------------------- 
LICENSE view
@@ -1,4 +1,7 @@-Copyright (c) 2012, Brent Yorgey+Copyright (c) 2012-2013 diagrams-builder team:++  Jeffrey Rosenbluth <jeffrey.rosenbluth@gmail.com>+  Brent Yorgey <byorgey@gmail.com>  All rights reserved. 
diagrams-builder.cabal view
@@ -1,5 +1,5 @@ name:                diagrams-builder-version:             0.3.0.1+version:             0.4 synopsis:            hint-based build service for the diagrams graphics EDSL.  description:         @diagrams-builder@ provides backend-agnostic tools for@@ -25,7 +25,7 @@                      A LaTeX package, @diagrams-latex.sty@, is also                      provided in the @latex/@ directory of the source                      distribution, which renders diagrams code found-                     within @diagram@ environments. Note that +                     within @diagram@ environments. Note that                      @diagrams-latex.sty@ is licensed under the GPL. homepage:            http://projects.haskell.org/diagrams license:             BSD3@@ -47,10 +47,12 @@                        Diagrams.Builder.Modules                        Diagrams.Builder.CmdLine   build-depends:       base >=4.2 && < 4.7,-                       diagrams-lib >=0.6 && < 0.7,+                       diagrams-lib >=0.6 && < 0.8,                        hint ==0.3.*,                        directory,                        filepath,+                       transformers >= 0.3 && < 0.4,+                       split >= 0.2 && < 0.3,                        haskell-src-exts >= 1.13.1 && < 1.14,                        cryptohash >= 0.8 && < 0.10,                        bytestring >= 0.9.2 && < 0.11,@@ -92,8 +94,8 @@                        filepath,                        directory,                        diagrams-builder,-                       diagrams-lib >= 0.6 && < 0.7,-                       diagrams-cairo >= 0.6 && < 0.7,+                       diagrams-lib >= 0.6 && < 0.8,+                       diagrams-cairo >= 0.6 && < 0.8,                        cmdargs >= 0.6 && < 0.11  executable diagrams-builder-svg@@ -111,8 +113,8 @@                        filepath,                        directory,                        diagrams-builder,-                       diagrams-lib >= 0.6 && < 0.7,-                       diagrams-svg >= 0.6 && < 0.7,+                       diagrams-lib >= 0.6 && < 0.8,+                       diagrams-svg >= 0.6 && < 0.8,                        blaze-svg >= 0.3.3 && < 0.4,                        bytestring >= 0.9.2 && < 0.11,                        cmdargs >= 0.6 && < 0.11@@ -123,7 +125,7 @@   default-language:    Haskell2010   other-extensions:    DeriveDataTypeable                        RecordWildCards-  +   if !flag(ps)     buildable: False @@ -132,6 +134,6 @@                        filepath,                        directory,                        diagrams-builder,-                       diagrams-lib >= 0.6 && < 0.7,-                       diagrams-postscript >= 0.6 && < 0.7,+                       diagrams-lib >= 0.6 && < 0.8,+                       diagrams-postscript >= 0.6 && < 0.8,                        cmdargs >= 0.6 && < 0.11
src/Diagrams/Builder.hs view
@@ -1,8 +1,8 @@-{-# LANGUAGE StandaloneDeriving-           , DeriveDataTypeable-           , ScopedTypeVariables-           , FlexibleContexts-  #-}+{-# LANGUAGE DeriveDataTypeable    #-}+{-# LANGUAGE FlexibleContexts      #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE StandaloneDeriving    #-} {-# OPTIONS_GHC -fno-warn-orphans #-}  -----------------------------------------------------------------------------@@ -37,23 +37,33 @@         ) where -import Diagrams.Builder.Modules-import Diagrams.Builder.CmdLine--import Diagrams.Prelude hiding ((<.>), e)--import Language.Haskell.Exts (prettyPrint)-import Language.Haskell.Interpreter hiding (ModuleName)+import           Control.Monad                (guard, mplus, mzero)+import           Control.Monad.Trans.Maybe    (MaybeT, runMaybeT)+import           Crypto.Hash                  (Digest, MD5,+                                               digestToHexByteString, hash)+import qualified Data.ByteString.Char8        as B+import           Data.List                    (nub)+import           Data.List.Split              (splitOn)+import           Data.Maybe                   (catMaybes, fromMaybe)+import           Data.Typeable                (Typeable)+import           System.Directory             (doesFileExist,+                                               getDirectoryContents,+                                               getTemporaryDirectory,+                                               removeFile)+import           System.FilePath              (takeBaseName, (<.>), (</>))+import           System.IO                    (hClose, hPutStr, openTempFile) -import System.IO-import System.FilePath-import System.Directory+import           Language.Haskell.Exts        (ImportDecl, Module (..),+                                               ModuleName (..), importModule,+                                               prettyPrint)+import           Language.Haskell.Interpreter hiding (ModuleName) -import Crypto.Hash (Digest, MD5, digestToHexByteString, hash)+import           Diagrams.Builder.CmdLine+import           Diagrams.Builder.Modules+import           Diagrams.Prelude             hiding (e, (<.>))+import           System.Environment           (getEnvironment)+import           Language.Haskell.Interpreter.Unsafe (unsafeRunInterpreterWithArgs) -import qualified Data.ByteString.Char8 as B-import Data.List (nub)-import Data.Typeable deriving instance Typeable Any  ------------------------------------------------------------@@ -67,15 +77,18 @@  -- | Set up the module to be interpreted, in the context of the --   necessary imports.-setDiagramImports :: MonadInterpreter m-                  => String      -- ^ Filename of the module containing the diagrams-                  -> [String]    -- ^ Additional necessary-                                 --   imports. @Prelude@,-                                 --   @Diagrams.Prelude@,-                                 --   @Diagrams.Core.Types@,-                                 --   and @Data.Monoid@ are included-                                 --   by default.-                  -> m ()+setDiagramImports+  :: MonadInterpreter m+  => String+     -- ^ Filename of the module containing the diagrams++  -> [String]+     -- ^ Additional necessary imports. @Prelude@, @Diagrams.Prelude@,+     --   @Diagrams.Core.Types@, and @Data.Monoid@ are included by+     --   default.++  -> m ()+ setDiagramImports m imps = do     loadModules [m]     setTopLevelModules [takeBaseName m]@@ -86,21 +99,31 @@                  ]                  ++ imps +getHsenvArgv :: IO [String]+getHsenvArgv = do+  env <- getEnvironment+  return $ case (lookup "HSENV" env) of+             Nothing -> []+             _       -> hsenvArgv+                 where hsenvArgv = words $ fromMaybe "" (lookup "PACKAGE_DB_FOR_GHC" env)+ -- | Interpret a diagram expression based on the contents of a given --   source file, using some backend to produce a result.-interpretDiagram :: forall b v.-                  ( Typeable b, Typeable v-                  , InnerSpace v, OrderedField (Scalar v), Backend b v-                  )-               => b             -- ^ Backend token-               -> v             -- ^ Dummy vector to identify the vector space-               -> Options b v   -- ^ Rendering options-               -> FilePath      -- ^ Filename of the module containing the example-               -> [String]      -- ^ Additional imports needed-               -> String        -- ^ Expression of type @Diagram b v@ to be compiled-               -> IO (Either InterpreterError (Result b v))-interpretDiagram b _ opts m imps dexp =-    runInterpreter $ do+interpretDiagram+  :: forall b v.+     ( Typeable b, Typeable v+     , InnerSpace v, OrderedField (Scalar v), Backend b v+     )+  => b             -- ^ Backend token+  -> v             -- ^ Dummy vector to identify the vector space+  -> Options b v   -- ^ Rendering options+  -> FilePath      -- ^ Filename of the module containing the example+  -> [String]      -- ^ Additional imports needed+  -> String        -- ^ Expression of type @Diagram b v@ to be compiled+  -> IO (Either InterpreterError (Result b v))+interpretDiagram b _ opts m imps dexp = do+    args <- liftIO getHsenvArgv+    unsafeRunInterpreterWithArgs args $ do       setDiagramImports m imps       d <- interpret dexp (as :: Diagram b v)       return (renderDia b opts d)@@ -131,63 +154,60 @@ --   module and interpreting the given expression.  Can return either --   a parse error if the source does not parse, an interpreter error, --   or the final result.-buildDiagram :: ( Typeable b, Typeable v-                , InnerSpace v, OrderedField (Scalar v), Backend b v-                )-             => b              -- ^ Backend token-             -> v              -- ^ Dummy vector to fix the vector type-             -> Options b v    -- ^ Backend-specific options to use-             -> [String]       -- ^ Source code snippets.  Each should-                               --   be a syntactically valid Haskell-                               --   module.  They will be combined-                               --   intelligently, /i.e./ not just-                               --   pasted together textually but-                               --   combining pragmas, imports,-                               --   /etc./ separately.-             -> String         -- ^ Diagram expression to interpret-             -> [String]       -- ^ Extra @LANGUAGE@ pragmas to use-                               --   (@NoMonomorphismRestriction@ is used-                               --   by default.)-             -> [String]       -- ^ Additional imports-                               --   ("Diagrams.Prelude" is imported by-                               --   default).-             -> (String -> IO (x, Maybe (Options b v -> Options b v)))-                               -- ^ A function to decide whether a-                               --   particular diagram needs to be-                               --   regenerated.  It will be passed-                               --   the final assembled source for the-                               --   diagram (but with the module name-                               --   set to @Main@ instead of something-                               --   auto-generated, so that hashing-                               --   the source will produce consistent-                               --   results across runs). It can-                               --   return some information (such as a-                               --   hash of the source) via the @x@-                               --   result, which will be passed-                               --   through to the result of-                               --   'buildDiagram'.  More importantly,-                               --   it decides whether the diagram-                               --   should be built: a result of-                               --   'Just' means the diagram /should/-                               --   be built; 'Nothing' means it-                               --   should not. In the case that it-                               --   should be built, it returns a-                               --   function for updating the-                               --   rendering options.  This could be-                               --   used, /e.g./, to request a-                               --   filename based on a hash of the-                               --   source.-                               ---                               --   Two standard decision functions-                               --   are provided for convenience:-                               --   'alwaysRegenerate' returns no-                               --   extra information and always-                               --   decides to regenerate the diagram;-                               --   'hashedRegenerate' creates a hash-                               --   of the diagram source and looks-                               --   for a file with that name in a-                               --   given directory.-             -> IO (BuildResult b v x)+buildDiagram+  :: ( Typeable b, Typeable v+     , InnerSpace v, OrderedField (Scalar v), Backend b v+     , Show (Options b v)+     )+  => b+     -- ^ Backend token++  -> v+     -- ^ Dummy vector to fix the vector type++  -> Options b v+     -- ^ Backend-specific options to use++  -> [String]+     -- ^ Source code snippets.  Each should be a syntactically valid+     --   Haskell module.  They will be combined intelligently, /i.e./+     --   not just pasted together textually but combining pragmas,+     --   imports, /etc./ separately.++  -> String+     -- ^ Diagram expression to interpret++  -> [String]+     -- ^ Extra @LANGUAGE@ pragmas to use (@NoMonomorphismRestriction@+     --   is used by default.)++  -> [String]+     -- ^ Additional imports ("Diagrams.Prelude" is imported by+     --   default).++  -> (String -> IO (x, Maybe (Options b v -> Options b v)))+     -- ^ A function to decide whether a particular diagram needs to+     --   be regenerated.  It will be passed the final assembled+     --   source for the diagram (but with the module name set to+     --   @Main@ instead of something auto-generated, so that hashing+     --   the source will produce consistent results across runs). It+     --   can return some information (such as a hash of the source)+     --   via the @x@ result, which will be passed through to the+     --   result of 'buildDiagram'.  More importantly, it decides+     --   whether the diagram should be built: a result of 'Just'+     --   means the diagram /should/ be built; 'Nothing' means it+     --   should not. In the case that it should be built, it returns+     --   a function for updating the rendering options.  This could+     --   be used, /e.g./, to request a filename based on a hash of+     --   the source.+     --+     --   Two standard decision functions are provided for+     --   convenience: 'alwaysRegenerate' returns no extra information+     --   and always decides to regenerate the diagram;+     --   'hashedRegenerate' creates a hash of the diagram source and+     --   looks for a file with that name in a given directory.++  -> IO (BuildResult b v x) buildDiagram b v opts source dexp langs imps shouldRegen = do   let source'   = map unLit source   case createModule@@ -196,8 +216,9 @@          ("Diagrams.Prelude" : imps)          source' of     Left  err -> return (ParseErr err)-    Right m   -> do-      regen <- shouldRegen (prettyPrint m ++ dexp)+    Right m@(Module _ _ _ _ _ srcImps _) -> do+      liHashes <- getLocalImportHashes srcImps+      regen    <- shouldRegen (prettyPrint m ++ dexp ++ show opts ++ concat liHashes)       case regen of         (info, Nothing)  -> return $ Skipped info         (info, Just upd) -> do@@ -211,6 +232,39 @@           removeFile tmp           return $ either InterpErr (OK info) compilation +-- | Take a list of imports, and return hashes of the contents of+--   those imports which are local.  Note, this only finds imports+--   which exist relative to the current directory, which is not as+--   general as it probably should be --- we could be calling+--   'buildDiagram' on source code which lives anywhere.+getLocalImportHashes :: [ImportDecl] -> IO [String]+getLocalImportHashes+  = (fmap . map) hashStr+  . fmap catMaybes+  . mapM getLocalSource+  . map (foldr1 (</>) . splitOn "." . getModuleName . importModule)++-- | Given a relative path with no extension, like+--   @\"Foo\/Bar\/Baz\"@, check whether such a file exists with either+--   a @.hs@ or @.lhs@ extension; if so, return its /pretty-printed/+--   contents (removing all comments, canonicalizing formatting, /etc./).+getLocalSource :: FilePath -> IO (Maybe String)+getLocalSource f = runMaybeT $ do+  contents <- getLocal f+  case (doModuleParse . unLit) contents of+    Left _  -> mzero+    Right m -> return (prettyPrint m)++-- | Given a relative path with no extension, like+--   @\"Foo\/Bar\/Baz\"@, check whether such a file exists with either a+--   @.hs@ or @.lhs@ extension; if so, return its contents.+getLocal :: FilePath -> MaybeT IO String+getLocal m = tryExt "hs" `mplus` tryExt "lhs"+  where+    tryExt ext = do+      let f = m <.> ext+      liftIO (doesFileExist f) >>= guard >> liftIO (readFile f)+ -- | Convenience function suitable to be given as the final argument --   to 'buildDiagram'.  It implements the simple policy of always --   rebuilding every diagram.@@ -227,25 +281,23 @@ --   hash.  (Most likely, one would want to set the requested output --   file to the hash followed by some extension.)  It also returns --   the generated hash.-hashedRegenerate :: (String -> a -> a)  -- ^ A function for computing-                                        --   an update to rendering-                                        --   options, given a new base-                                        --   filename computed from a-                                        --   hash of the diagram-                                        --   source.-                 -> FilePath            -- ^ The directory in which to-                                        --   look for generated files-                 -> String              -- ^ The \"source\" to-                                        -- hash. Note that this does-                                        -- not actually have to be-                                        -- valid source code.  A-                                        -- common trick is to-                                        -- concatenate the actual-                                        -- source code with String-                                        -- representations of any-                                        -- other information on which-                                        -- the diagram depends.-                 -> IO (String, Maybe (a -> a))+hashedRegenerate+  :: (String -> a -> a)+     -- ^ A function for computing an update to rendering options,+     --   given a new base filename computed from a hash of the+     --   diagram source.++  -> FilePath+     -- ^ The directory in which to look for generated files++  -> String+     -- ^ The \"source\" to hash. Note that this does not actually+     -- have to be valid source code.  A common trick is to+     -- concatenate the actual source code with String representations+     -- of any other information on which the diagram depends.++  -> IO (String, Maybe (a -> a))+ hashedRegenerate upd dir src = do   let fileBase = hashStr src   files <- getDirectoryContents dir