add-dependent-file (empty) → 0.0.1
raw patch · 6 files changed
+222/−0 lines, 6 filesdep +Cabaldep +Cabal-syntaxdep +base
Dependencies added: Cabal, Cabal-syntax, base, bytestring, directory, filepath, regex-compat, template-haskell
Files
- LICENSE +32/−0
- README.md +17/−0
- add-dependent-file.cabal +63/−0
- changelog.md +4/−0
- src/AddDependentFile.hs +86/−0
- src/AddDependentFile/Test.hs +20/−0
+ LICENSE view
@@ -0,0 +1,32 @@+This module is under this "3 clause" BSD license:++Copyright (c) 2025-2025, Daniil Iaitskov+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.++ * The names of the contributors may not 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.
+ README.md view
@@ -0,0 +1,17 @@+# add-dependent-file++The library provides a safer wrapper around+[addDependentFile](https://hackage.haskell.org/package/template-haskell/docs/Language-Haskell-TH-Syntax.html#v:addDependentFile)+that checks that `extra-source-files` section in the cabal file+contains **addDependendFile** argument and issues a correspondent+compilation warning if it does not, because `addDependentFile` has no+effect otherwise.++## Development environment++```shell+$ nix develop+$ emacs &+$ cabal build+$ cabal test+```
+ add-dependent-file.cabal view
@@ -0,0 +1,63 @@+cabal-version: 3.0+name: add-dependent-file+version: 0.0.1+synopsis: Safer TH addDependentFile wrapper+description:+ The library provides a safer wrapper around+ <https://hackage.haskell.org/package/template-haskell/docs/Language-Haskell-TH-Syntax.html#v:addDependentFile addDependentFile>+ that checks that @extra-source-files@ section in the cabal file contains+ __addDependendFile__ argument and issues a correspondent compilation+ warning if it does not, because @addDependentFile@ has no effect+ otherwise.+ + == Development environment+ #development-environment#+ + > $ nix develop+ > $ emacs &+ > $ cabal build+ > $ cabal test+homepage: http://github.com/yaitskov/add-dependent-file+license: BSD-3-Clause+license-file: LICENSE+author: Daniil Iaitskov+maintainer: dyaitskov@gmail.com+copyright: Daniil Iaitkov 2026+category: System+build-type: Simple+bug-reports: https://github.com/yaitskov/add-dependent-file/issues+extra-source-files:+ README.md+extra-doc-files:+ changelog.md+tested-with:+ GHC == 9.12.2++source-repository head+ type:+ git+ location:+ https://github.com/yaitskov/add-dependent-file.git++library+ hs-source-dirs: src+ exposed-modules:+ AddDependentFile+ other-modules:+ AddDependentFile.Test+ default-language: GHC2024+ ghc-options: -Wall+ default-extensions:+ DefaultSignatures+ NoImplicitPrelude+ OverloadedStrings+ TemplateHaskell+ build-depends:+ , Cabal < 4+ , Cabal-syntax < 4+ , base >=4.7 && < 5+ , bytestring < 1+ , directory < 2+ , filepath < 2+ , regex-compat < 1+ , template-haskell < 3
+ changelog.md view
@@ -0,0 +1,4 @@+# add-dependent-file changelog++## Version 0.0.1 2026-02-26+ * init
+ src/AddDependentFile.hs view
@@ -0,0 +1,86 @@+{-# LANGUAGE OverloadedRecordDot #-}+module AddDependentFile+ ( addDependentFile+ , addDependentFile'+ , getPackageRoot+ , (</>)+ ) where++import Control.Monad ( unless )+import Control.Monad.IO.Class ( MonadIO(..) )+import Data.ByteString qualified as BS+import Distribution.PackageDescription.Configuration ( flattenPackageDescription )+import Distribution.Simple.PackageDescription ( readGenericPackageDescription )+import Distribution.Types.PackageDescription ( PackageDescription(extraSrcFiles) )+import Distribution.Utils.Path ( getSymbolicPath, makeSymbolicPath )+import Distribution.Simple.Utils ( isAbsoluteOnAnyPlatform )+import Distribution.Verbosity ( verbose )+import Language.Haskell.TH.Syntax+ ( Q, location, reportError, Loc(loc_package), runIO )+import Language.Haskell.TH.Syntax qualified as TH+import Prelude+import System.Directory (makeAbsolute)+import System.FilePath ( (</>), addTrailingPathSeparator )+import Text.Regex ( mkRegex, subRegex )++-- copy from Distribution.Simple.Utils+stripCommonPrefix :: String -> String -> String+stripCommonPrefix (x : xs) (y : ys)+ | x == y = stripCommonPrefix xs ys+ | otherwise = y : ys+stripCommonPrefix _ ys = ys++parseCabalFile :: MonadIO m => FilePath -> m PackageDescription+parseCabalFile cabPath =+ flattenPackageDescription <$> liftIO (readGenericPackageDescription verbose Nothing (makeSymbolicPath cabPath))++stripInplace :: String -> String+stripInplace pn = subRegex massagePackageName pn "\\1.cabal"+ where+ massagePackageName =+ mkRegex+ "^([A-Z_a-z0-9-]+)[-]([0-9]+[.])+[0-9]+[-]([A-Za-z0-9]{10,}|inplace)(-test)?$"++findCabalFile :: Q FilePath+findCabalFile = do+ cabalFileName <- stripInplace . loc_package <$> location+ (</> cabalFileName) <$> getPackageRoot++assertCabalExtraSourceContains :: FilePath -> FilePath -> Q ()+assertCabalExtraSourceContains fp cabalFile = do+ pd <- runIO $ do+ parseCabalFile cabalFile+ let extraSourceFiles :: [FilePath] = getSymbolicPath <$> pd.extraSrcFiles in+ unless (fp `elem` extraSourceFiles) $ do+ reportError $ "File [" <> fp+ <> "] is not mentioned in the extra-source-files section of "+ <> cabalFile <> ". Changes in that file wouldn't trigger rebuild."++-- | Calls 'Language.Haskell.TH.Syntax.addDependentFile' and checks the argument+-- should be an absolute path and the location it refers+-- is mentioned in the @extra-sources-files@ section of the cabal file+addDependentFile :: FilePath -> Q ()+addDependentFile p = do+ if isAbsoluteOnAnyPlatform p+ then do+ pRelativeToCabal <- (`stripCommonPrefix` p) . addTrailingPathSeparator <$> getPackageRoot+ assertCabalExtraSourceContains pRelativeToCabal =<< findCabalFile+ TH.addDependentFile p+ else+ reportError $ "addDependentFile got [" <> p <> "] that is not an absolute path"++-- | Besides 'addDependentFile' checks, it enusures that consumed content is not changed+addDependentFile' :: FilePath -> BS.ByteString -> Q ()+addDependentFile' p expectedContent = do+ addDependentFile p+ pContent <- runIO $ BS.readFile p+ unless (pContent == expectedContent) $+ reportError $ "File [" <> p <> "] has changed after it is consumed"+++liftIO1 :: (a -> IO b) -> a -> Q b+liftIO1 f x = runIO (f x)++-- | Absolute path to 'Language.Haskell.TH.Syntax.getPackageRoot'+getPackageRoot :: Q FilePath+getPackageRoot = TH.getPackageRoot >>= liftIO1 makeAbsolute
+ src/AddDependentFile/Test.hs view
@@ -0,0 +1,20 @@+{-# LANGUAGE TemplateHaskell #-}+module AddDependentFile.Test where++import AddDependentFile ( addDependentFile, addDependentFile', getPackageRoot, (</>) )+import Data.ByteString qualified as BS+import Language.Haskell.TH.Syntax (runIO)+import Prelude+++$(+ getPackageRoot >>= addDependentFile . (</> "README.md") >> pure []+ )+++$(do+ readmeAbsPath <- (</> "README.md") <$> getPackageRoot+ !readmeContent <- runIO $ BS.readFile readmeAbsPath+ addDependentFile' readmeAbsPath readmeContent+ pure []+ )