liquidhaskell-cabal (empty) → 0.1.0.0
raw patch · 4 files changed
+353/−0 lines, 4 filesdep +Cabaldep +basedep +filepathsetup-changed
Dependencies added: Cabal, base, filepath
Files
- LICENSE +27/−0
- Setup.hs +2/−0
- liquidhaskell-cabal.cabal +31/−0
- src/LiquidHaskell/Cabal.hs +293/−0
+ LICENSE view
@@ -0,0 +1,27 @@+Copyright (c) 2016, Michael Smith+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 liquidhaskell-cabal 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 HOLDER 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
+ liquidhaskell-cabal.cabal view
@@ -0,0 +1,31 @@+name: liquidhaskell-cabal+version: 0.1.0.0+synopsis: Liquid Haskell integration for Cabal and stack+description: Provides support for checking projects using Cabal+ and/or stack with LiquidHaskell.+ + Please see the "LiquidHaskell.Cabal" module or the+ README on GitHub for documentation.+homepage: https://github.com/spinda/liquidhaskell-cabal#readme+bug-reports: https://github.com/spinda/liquidhaskell-cabal/issues+license: BSD3+license-file: LICENSE+author: Michael Smith+maintainer: Michael Smith <michael@spinda.net>+copyright: 2016 Michael Smith+category: Development+build-type: Simple+cabal-version: >=1.10++library+ hs-source-dirs: src+ exposed-modules: LiquidHaskell.Cabal+ build-depends: base >= 4.4 && < 5+ , Cabal >= 1.18 && < 1.26+ , filepath >= 1.3 && <1.5+ default-language: Haskell2010++source-repository head+ type: git+ location: https://github.com/spinda/liquidhaskell-cabal+
+ src/LiquidHaskell/Cabal.hs view
@@ -0,0 +1,293 @@+-- | This module provides a kit to create @Setup.hs@ files with LiquidHaskell+-- integration. A sample project configured with this package+-- <https://github.com/spinda/liquidhaskell-cabal-demo is available>.+--+-- The standard, basic @Setup.hs@ configuration looks like this:+--+-- > import Distribution.Simple+-- > main = defaultMain+--+-- A simple @Setup.hs@ file that performs the standard behavior, with the+-- addition of LiquidHaskell support, can look like the following:+--+-- > import LiquidHaskell.Cabal+-- > main = liquidHaskellMain+--+-- In order for Cabal/stack to properly execute your custom @Setup.hs@ file,+-- your package's @.cabal@ file will need to set the @build-type@ to @Custom@+-- (the default is @Simple@):+--+-- > build-type: Custom+--+-- A package with LiquidHaskell integration is expected to expose a flag called+-- @liquidhaskell@, which should normally default to disabled and can be+-- implemented by adding the following stanza to your package's @.cabal@ file:+--+-- > flag liquidhaskell+-- > description: After building, verify with LiquidHaskell+-- > default: False+--+-- (The spacing and description are arbitrary and open to customization.)+--+-- During the post-build phase, the hook will check whether this flag is+-- enabled and, if it is, will run the LiquidHaskell binary with the+-- appropriate command line arguments and your package's source files. This+-- assumes, of course, that the LiquidHaskell binary is installed and available+-- in @$PATH@ (please make sure you have a version >= 0.6.0.0 installed).+--+-- Via stack, this flag can be enabled on the fly with+-- @--flag <package name>:liquidhaskell@; for example:+--+-- > stack build --flag mypackage:liquidhaskell+--+-- Running @cabal-install@ directly, it will need to be enabled via @cabal+-- configure@:+--+-- > cabal configure -fliquidhaskell && cabal build+--+-- When Cabal 1.24 releases, you will be able to add a stanza like the+-- following to your @.cabal@ file to ensure @liquidhaskell-cabal@ is installed+-- and available when the @Setup.hs@ file is built:+--+-- > custom-setup+-- > setup-depends: base, Cabal, liquidhaskell-cabal+--+-- Unfortunately Cabal <1.24 has no means of tracking build dependencies for+-- @Setup.hs@ files. If you're using Cabal directly, you'll need to install+-- @liquidhaskell-cabal@ manually before configuring/building your project:+--+-- > cabal install liquidhaskell-cabal+--+-- If you're using stack, add @liquidhaskell-cabal@ to each of your components'+-- @build-depends@, add @liquidhaskell-cabal-0.1.0.0@ to your @stack.yaml@'s+-- <https://github.com/commercialhaskell/stack/blob/master/doc/yaml_configuration.md#extra-deps extra-deps section>,+-- and add an+-- <https://github.com/commercialhaskell/stack/blob/master/doc/yaml_configuration.md#explicit-setup-deps explicit-setup-deps section> to your @stack.yaml@:+--+-- > explicit-setup-deps:+-- > "*": true+-- > extra-deps:+-- > - liquidhaskell-cabal-0.1.0.0+--+-- Then you can build your project as you normally would with stack.+--+-- Each component of the package (library and executables) can specify its own+-- extra command line flags to pass to LiquidHaskell (these are described in+-- the <https://github.com/ucsd-progsys/liquidhaskell LiquidHaskell README>).+-- Simply add an @x-liquidhaskell-options@ field to the relevant components:+--+-- > library+-- > (... other fields ...)+-- > x-liquidhaskell-options: --diff --no-termination+--+-- > executable myexecutable+-- > (... other fields ...)+-- > x-liquidhaskell-options: --diff+--+-- For most projects, the simple sample @Setup.hs@ file given above, using+-- 'liquidHaskellMain', should be sufficient. However, for those already using+-- custom @Setup.hs@ files, @liquidhaskell-cabal@ exposes more granular means+-- of invoking the hook. Using 'liquidHaskellHooks', the basic @Setup.hs@ file+-- is equivalent to:+--+-- > import Distribution.Simple+-- > import LiquidHaskell.Cabal+-- > main = defaultMainWithHooks liquidHaskellHooks+--+-- Using 'liquidHaskellPostBuildHook', this is further equivalent to:+--+-- > import Distribution.Simple+-- > import LiquidHaskell.Cabal+-- > main = defaultMainWithHooks $+-- > simpleUserHooks { postBuild = liquidHaskellPostBuildHook }+--+-- Projects already using a @postBuild@ hook can invoke+-- 'liquidHaskellPostBuildHook' from within this existing hook where+-- appropriate.++module LiquidHaskell.Cabal (+ -- * Setup.hs Hooks Kit+ liquidHaskellMain+ , liquidHaskellHooks+ , liquidHaskellPostBuildHook+ ) where++import Control.Monad++import Data.List+import Data.Maybe++import Distribution.ModuleName hiding (main)+import Distribution.PackageDescription+import Distribution.PackageDescription.Parse+import Distribution.ParseUtils+import Distribution.Simple+import Distribution.Simple.GHC+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.Program+import Distribution.Simple.Program.Db+import Distribution.Simple.Program.GHC+import Distribution.Simple.Setup+import Distribution.Simple.Utils+import Distribution.Verbosity++import System.FilePath++--------------------------------------------------------------------------------+-- Setup.hs Hooks Kit ----------------------------------------------------------+--------------------------------------------------------------------------------++-- | The simplest method of incorporating LiquidHaskell into a @Setup.hs@ file.+--+-- > import LiquidHaskell.Cabal+-- > main = liquidHaskellMain+--+-- This is equivalent to:+--+-- > import Distribution.Simple+-- > import LiquidHaskell.Cabal+-- > main = defaultMainWithHooks liquidHaskellHooks+liquidHaskellMain :: IO ()+liquidHaskellMain = defaultMainWithHooks liquidHaskellHooks++-- | Cabal's 'simpleUserHooks' configured with 'liquidHaskellPostBuildHook' in+-- the 'postBuild' field. Can be customized with your project's own user hooks.+--+-- > import Distribution.Simple+-- > import LiquidHaskell.Cabal+-- > main = defaultMainWithHooks liquidHaskellHooks+--+-- This is equivalent to:+--+-- > import Distribution.Simple+-- > import LiquidHaskell.Cabal+-- > main = defaultMainWithHooks $+-- > simpleUserHooks { postBuild = liquidHaskellPostBuildHook }+liquidHaskellHooks :: UserHooks+liquidHaskellHooks = simpleUserHooks { postBuild = liquidHaskellPostBuildHook }++-- | The raw build hook, checking the @liquidhaskell@ flag and executing the+-- LiquidHaskell binary with appropriate arguments when enabled. Can be hooked+-- into a 'UserHooks' map or invoked from within your own custom 'postBuild'+-- hook.+--+-- > import Distribution.Simple+-- > import LiquidHaskell.Cabal+-- > main = defaultMainWithHooks $+-- > simpleUserHooks { postBuild = liquidHaskellPostBuildHook }+liquidHaskellPostBuildHook :: Args -> BuildFlags -> PackageDescription+ -> LocalBuildInfo -> IO ()+liquidHaskellPostBuildHook args flags pkg lbi = do+ enabled <- isFlagEnabled "liquidhaskell" lbi+ when enabled $ do+ let verbosity = fromFlag $ buildVerbosity flags+ withAllComponentsInBuildOrder pkg lbi $ \component clbi ->+ case component of+ CLib lib -> verifyComponent verbosity lbi clbi (libBuildInfo lib)+ "library"+ =<< findLibSources lib+ CExe exe -> verifyComponent verbosity lbi clbi (buildInfo exe)+ ("executable " ++ exeName exe)+ =<< findExeSources exe+ _ -> return ()++--------------------------------------------------------------------------------+-- Verify a Library or Executable Component ------------------------------------+--------------------------------------------------------------------------------++verifyComponent :: Verbosity -> LocalBuildInfo -> ComponentLocalBuildInfo+ -> BuildInfo -> String -> [FilePath] -> IO ()+verifyComponent verbosity lbi clbi bi desc sources = do+ userArgs <- getUserArgs desc bi+ let ghcFlags = renderGhcOptions (compiler lbi) $+ componentGhcOptions verbosity lbi bi clbi $ buildDir lbi+ let args = concat+ [ ("--ghc-option=" ++) <$> ghcFlags+ , ("--c-files=" ++) <$> (cSources bi)+ , userArgs+ , sources+ ]+ liquid <- requireLiquidProgram verbosity $ withPrograms lbi+ runProgram verbosity liquid args++getUserArgs :: String -> BuildInfo -> IO [ProgArg]+getUserArgs desc bi =+ case lookup "x-liquidhaskell-options" (customFieldsBI bi) of+ Nothing -> return []+ Just cmd ->+ case parseCommandArgs cmd of+ Right args -> return args+ Left err -> die $+ "failed to parse LiquidHaskell options for " ++ desc ++ ": " ++ err++--------------------------------------------------------------------------------+-- Find Component Haskell Sources ----------------------------------------------+--------------------------------------------------------------------------------++findLibSources :: Library -> IO [FilePath]+findLibSources lib = findModuleSources (libBuildInfo lib) (exposedModules lib)++findExeSources :: Executable -> IO [FilePath]+findExeSources exe = do+ moduleSrcs <- findModuleSources (buildInfo exe) []+ mainSrc <- findFile (hsSourceDirs $ buildInfo exe) (modulePath exe)+ return (mainSrc : moduleSrcs)++findModuleSources :: BuildInfo -> [ModuleName] -> IO [FilePath]+findModuleSources bi exposed = do+ let modules = exposed ++ otherModules bi+ hsSources <- mapM (findModuleSource ["hs", "lhs"] bi) modules+ hsBootSources <- mapM (findModuleSource ["hs-boot", "lhs-boot"] bi) modules+ return $ catMaybes (hsSources ++ hsBootSources)++findModuleSource :: [String] -> BuildInfo -> ModuleName -> IO (Maybe FilePath)+findModuleSource suffixes bi mod =+ findFileWithExtension suffixes (hsSourceDirs bi) (toFilePath mod)++--------------------------------------------------------------------------------+-- Located the LiquidHaskell Binary --------------------------------------------+--------------------------------------------------------------------------------++requireLiquidProgram :: Verbosity -> ProgramDb -> IO ConfiguredProgram+requireLiquidProgram verbosity db =+ fst <$> requireProgram verbosity liquidProgram db++liquidProgram :: Program+liquidProgram = simpleProgram "liquid"++--------------------------------------------------------------------------------+-- Cabal Flag Handling ---------------------------------------------------------+--------------------------------------------------------------------------------++isFlagEnabled :: String -> LocalBuildInfo -> IO Bool+isFlagEnabled name lbi = case getOverriddenFlagValue name lbi of+ Just enabled -> return enabled+ Nothing -> getDefaultFlagValue name lbi False++getOverriddenFlagValue :: String -> LocalBuildInfo -> Maybe Bool+getOverriddenFlagValue name lbi = lookup (FlagName name) overriddenFlags+ where+ overriddenFlags = configConfigurationsFlags $ configFlags lbi++getDefaultFlagValue :: String -> LocalBuildInfo -> Bool -> IO Bool+getDefaultFlagValue name lbi def = case pkgDescrFile lbi of+ Nothing -> return def+ Just cabalFile -> do+ descr <- readPackageDescription silent cabalFile+ let flag = find ((FlagName name ==) . flagName) $ genPackageFlags descr+ return $ maybe def flagDefault flag++--------------------------------------------------------------------------------+-- Splitting Command Line Arguments --------------------------------------------+--------------------------------------------------------------------------------++parseCommandArgs :: String -> Either String [ProgArg]+parseCommandArgs cmd =+ case fieldSet field 0 cmd [] of+ ParseOk _ out -> Right $ concat $ map snd out+ ParseFailed err -> Left $ snd $ locatedErrorMsg err+ where+ field = optsField "x-liquidhaskell-options"+ (OtherCompiler "LiquidHaskell")+ id (++)+