packages feed

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 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 []+ )