packages feed

publish-2.5.3: src/Utilities.hs

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Utilities (
    ensureDirectory,
    ifNewer,
    isNewer,
) where

import Chrono.Compat (convertToUTC)
import Control.Monad (when)
import Core.Program
import Core.System
import System.Directory (
    createDirectoryIfMissing,
    doesDirectoryExist,
    doesFileExist,
    getModificationTime,
 )
import System.FilePath.Posix (takeDirectory)

{-
Some source files live in subdirectories. Replicate that directory
structure in the temporary build space
-}
ensureDirectory :: FilePath -> Program t ()
ensureDirectory target =
    let subdir = takeDirectory target
     in liftIO $ do
            probe <- doesDirectoryExist subdir
            when (not probe) $ do
                createDirectoryIfMissing True subdir

{- |
 If the source file is newer than the target file, then run an action. For
 example, if you want to install a file but only do so if the file has been
 rebuilt, then you could do this:

 @
 copyFileIfNewer :: 'FilePath' -> 'FilePath' -> 'Program' τ ()
 copyFileIfNewer source target = do
     'ifNewer' source target $ do
         'liftIO' ('copyFileWithMetadata' source target)
 @

 This is basically a build system in a box, although the usual caveats
 about the brittleness of timestamps apply.

 TODO this could potentially move to the **unbeliever** library
-}
ifNewer :: FilePath -> FilePath -> Program t () -> Program t ()
ifNewer source target program = do
    changed <- isNewer source target
    when changed $ do
        program

isNewer :: FilePath -> FilePath -> Program t Bool
isNewer source target = liftIO $ do
    time1 <- getModificationTime source
    time2 <-
        doesFileExist target >>= \case
            True -> getModificationTime target
            False -> return (convertToUTC 0) -- the epoch
    return (time1 > time2)