    -- | Maintainer: Sean Whitton <spwhitton@spwhitton.name>

    module Propellor.Property.Ccache (
    	hasCache,
    	hasLimits,
    	Limit(..),
    	DataSize,
    ) where

    import Propellor.Base
    import qualified Propellor.Property.File as File
    import qualified Propellor.Property.Apt as Apt

    import Utility.DataUnits
    import System.Posix.Files
    import qualified Data.Semigroup as Sem

    -- | Limits on the size of a ccache
    data Limit
    	-- | The maximum size of the cache, as a string such as "4G"
    	= MaxSize DataSize
    	-- | The maximum number of files in the cache
    	| MaxFiles Integer
    	-- | A cache with no limit specified
    	| NoLimit
    	| Limit :+ Limit

    instance Sem.Semigroup Limit where
    	<> :: Limit -> Limit -> Limit
    (<>) = Limit -> Limit -> Limit
    (:+)

    instance Monoid Limit where
    	mempty :: Limit
    mempty  = Limit
    NoLimit
    	mappend :: Limit -> Limit -> Limit
    mappend = Limit -> Limit -> Limit
    forall a. Semigroup a => a -> a -> a
    (Sem.<>)

    -- | A string that will be parsed to get a data size.
    --
    -- Examples: "100 megabytes" or "0.5tb"
    type DataSize = String

    maxSizeParam :: DataSize -> Maybe String
    maxSizeParam :: DataSize -> Maybe DataSize
    maxSizeParam s :: DataSize
    s = [Unit] -> DataSize -> Maybe ByteSize
    readSize [Unit]
    dataUnits DataSize
    s
    	Maybe ByteSize -> (ByteSize -> Maybe DataSize) -> Maybe DataSize
    forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
    >>= \sz :: ByteSize
    sz -> DataSize -> Maybe DataSize
    forall a. a -> Maybe a
    Just (DataSize -> Maybe DataSize) -> DataSize -> Maybe DataSize
    forall a b. (a -> b) -> a -> b
    $ "--max-size=" DataSize -> DataSize -> DataSize
    forall a. [a] -> [a] -> [a]
    ++ ByteSize -> DataSize
    ccacheSizeUnits ByteSize
    sz

    -- Generates size units as used in ccache.conf.  The smallest unit we can
    -- specify in a ccache config files is a kilobyte
    ccacheSizeUnits :: Integer -> String
    ccacheSizeUnits :: ByteSize -> DataSize
    ccacheSizeUnits sz :: ByteSize
    sz = (Char -> Bool) -> DataSize -> DataSize
    forall a. (a -> Bool) -> [a] -> [a]
    filter (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    /= ' ') ([Unit] -> Bool -> ByteSize -> DataSize
    roughSize [Unit]
    cfgfileunits Bool
    True ByteSize
    sz)
      where
    	cfgfileunits :: [Unit]
    	cfgfileunits :: [Unit]
    cfgfileunits =
    	        [ ByteSize -> DataSize -> DataSize -> Unit
    Unit (ByteSize -> ByteSize
    p 4) "Ti" "terabyte"
    		, ByteSize -> DataSize -> DataSize -> Unit
    Unit (ByteSize -> ByteSize
    p 3) "Gi" "gigabyte"
    		, ByteSize -> DataSize -> DataSize -> Unit
    Unit (ByteSize -> ByteSize
    p 2) "Mi" "megabyte"
    		, ByteSize -> DataSize -> DataSize -> Unit
    Unit (ByteSize -> ByteSize
    p 1) "Ki" "kilobyte"
    		]
    	p :: Integer -> Integer
    	p :: ByteSize -> ByteSize
    p n :: ByteSize
    n = 1024ByteSize -> ByteSize -> ByteSize
    forall a b. (Num a, Integral b) => a -> b -> a
    ^ByteSize
    n

    -- | Set limits on a given ccache
    hasLimits :: FilePath -> Limit -> Property DebianLike
    path :: DataSize
    path hasLimits :: DataSize -> Limit -> Property DebianLike
    `hasLimits` limit :: Limit
    limit = Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    go Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property DebianLike
    -> CombinedType
         (Property
            (MetaTypes
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
         (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property DebianLike
    installed
      where
    	go :: Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    go
    		| [DataSize] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [DataSize]
    params' = Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall k (t :: k). SingI t => Property (MetaTypes t)
    doNothing
    		-- We invoke ccache itself to set the limits, so that it can
    		-- handle replacing old limits in the config file, duplicates
    		-- etc.
    		| [DataSize] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null [DataSize]
    errors =
    			DataSize
    -> [DataSize]
    -> [(DataSize, DataSize)]
    -> UncheckedProperty
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    cmdPropertyEnv "ccache" [DataSize]
    params' [("CCACHE_DIR", DataSize
    path)]
    			UncheckedProperty
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> DataSize
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall (p :: * -> *) i.
    Checkable p i =>
    p i -> DataSize -> Property i
    `changesFileContent` (DataSize
    path DataSize -> DataSize -> DataSize
    </> "ccache.conf")
    		| Bool
    otherwise = DataSize
    -> Propellor Result
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall k (metatypes :: k).
    SingI metatypes =>
    DataSize -> Propellor Result -> Property (MetaTypes metatypes)
    property "couldn't parse ccache limits" (Propellor Result
     -> Property
          (MetaTypes
             '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> Propellor Result
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall a b. (a -> b) -> a -> b
    $
    			DataSize -> Propellor Result
    forall (m :: * -> *) a. MonadIO m => DataSize -> m a
    errorMessage (DataSize -> Propellor Result) -> DataSize -> Propellor Result
    forall a b. (a -> b) -> a -> b
    $ [DataSize] -> DataSize
    unlines [DataSize]
    errors

    	params :: [Either DataSize DataSize]
    params = Limit -> [Either DataSize DataSize]
    limitToParams Limit
    limit
    	(errors :: [DataSize]
    errors, params' :: [DataSize]
    params') = [Either DataSize DataSize] -> ([DataSize], [DataSize])
    forall a b. [Either a b] -> ([a], [b])
    partitionEithers [Either DataSize DataSize]
    params

    limitToParams :: Limit -> [Either String String]
    limitToParams :: Limit -> [Either DataSize DataSize]
    limitToParams NoLimit = []
    limitToParams (MaxSize s :: DataSize
    s) = case DataSize -> Maybe DataSize
    maxSizeParam DataSize
    s of
    	Just param :: DataSize
    param -> [DataSize -> Either DataSize DataSize
    forall a b. b -> Either a b
    Right DataSize
    param]
    	Nothing -> [DataSize -> Either DataSize DataSize
    forall a b. a -> Either a b
    Left (DataSize -> Either DataSize DataSize)
    -> DataSize -> Either DataSize DataSize
    forall a b. (a -> b) -> a -> b
    $ "unable to parse data size " DataSize -> DataSize -> DataSize
    forall a. [a] -> [a] -> [a]
    ++ DataSize
    s]
    limitToParams (MaxFiles f :: ByteSize
    f) = [DataSize -> Either DataSize DataSize
    forall a b. b -> Either a b
    Right (DataSize -> Either DataSize DataSize)
    -> DataSize -> Either DataSize DataSize
    forall a b. (a -> b) -> a -> b
    $ "--max-files=" DataSize -> DataSize -> DataSize
    forall a. [a] -> [a] -> [a]
    ++ ByteSize -> DataSize
    forall t. ConfigurableValue t => t -> DataSize
    val ByteSize
    f]
    limitToParams (l1 :: Limit
    l1 :+ l2 :: Limit
    l2) = Limit -> [Either DataSize DataSize]
    limitToParams Limit
    l1 [Either DataSize DataSize]
    -> [Either DataSize DataSize] -> [Either DataSize DataSize]
    forall a. Semigroup a => a -> a -> a
    <> Limit -> [Either DataSize DataSize]
    limitToParams Limit
    l2

    -- | Configures a ccache in /var/cache for a group
    --
    -- If you say
    --
    -- > & (Group "foo") `Ccache.hasGroupCache`
    -- > 	(Ccache.MaxSize "4G" <> Ccache.MaxFiles 10000)
    --
    -- you instruct propellor to create a ccache in /var/cache/ccache-foo owned and
    -- writeable by the foo group, with a maximum cache size of 4GB or 10000 files.
    hasCache :: Group -> Limit -> RevertableProperty DebianLike UnixLike
    group :: Group
    group@(Group g :: DataSize
    g) hasCache :: Group
    -> Limit
    -> RevertableProperty
         DebianLike
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    `hasCache` limit :: Limit
    limit = (Property DebianLike
    make Property DebianLike
    -> Property DebianLike
    -> CombinedType (Property DebianLike) (Property DebianLike)
    forall x y. Combines x y => x -> y -> CombinedType x y
    `requires` Property DebianLike
    installed) Property DebianLike
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> RevertableProperty
         DebianLike
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall setupmetatypes undometatypes.
    Property setupmetatypes
    -> Property undometatypes
    -> RevertableProperty setupmetatypes undometatypes
    <!> Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    delete
      where
    	make :: Property DebianLike
    make = DataSize -> Props DebianLike -> Property DebianLike
    forall k (metatypes :: k).
    SingI metatypes =>
    DataSize
    -> Props (MetaTypes metatypes) -> Property (MetaTypes metatypes)
    propertyList ("ccache for " DataSize -> DataSize -> DataSize
    forall a. [a] -> [a] -> [a]
    ++ DataSize
    g DataSize -> DataSize -> DataSize
    forall a. [a] -> [a] -> [a]
    ++ " group exists") (Props DebianLike -> Property DebianLike)
    -> Props DebianLike -> Property DebianLike
    forall a b. (a -> b) -> a -> b
    $ Props
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    props
    			Props
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & DataSize
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    File.dirExists DataSize
    path
    			Props
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & DataSize
    -> User
    -> Group
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    File.ownerGroup DataSize
    path (DataSize -> User
    User "root") Group
    group
    			Props
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & DataSize
    -> FileMode
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    File.mode DataSize
    path ([FileMode] -> FileMode
    combineModes ([FileMode] -> FileMode) -> [FileMode] -> FileMode
    forall a b. (a -> b) -> a -> b
    $
    					[FileMode]
    readModes [FileMode] -> [FileMode] -> [FileMode]
    forall a. [a] -> [a] -> [a]
    ++ [FileMode]
    executeModes [FileMode] -> [FileMode] -> [FileMode]
    forall a. [a] -> [a] -> [a]
    ++
    					[ FileMode
    ownerWriteMode
    					, FileMode
    groupWriteMode
    					, FileMode
    setGroupIDMode
    					]) Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> CombinedType
         (Property
            (MetaTypes
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
         (Property
            (MetaTypes
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall x y. Combines x y => x -> y -> CombinedType x y
    `onChange` Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    fixSetgidBit
    				-- here, we use onChange to catch upgrades from
    				-- 3.0.5 where the setGroupIDMode line was not
    				-- present
    			Props
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property DebianLike
    -> Props
         (MetaTypes
            (Combine
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish]))
    forall a p (y :: [a]) (x :: [a]).
    (IsProp p, MetaTypes y ~ GetMetaTypes p,
     CheckCombinableNote x y (NoteFor ('Text "&"))) =>
    Props (MetaTypes x) -> p -> Props (MetaTypes (Combine x y))
    & DataSize -> Limit -> Property DebianLike
    hasLimits DataSize
    path Limit
    limit

    	delete :: Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    delete = IO Bool
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall (p :: * -> *) i (m :: * -> *).
    (Checkable p i, LiftPropellor m) =>
    m Bool -> p i -> Property i
    check (DataSize -> IO Bool
    doesDirectoryExist DataSize
    path) (Property
       (MetaTypes
          '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
             'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
     -> Property
          (MetaTypes
             '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall a b. (a -> b) -> a -> b
    $
    		DataSize
    -> [DataSize]
    -> UncheckedProperty
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    cmdProperty "rm" ["-r", DataSize
    path] UncheckedProperty
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Result
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall (p :: * -> *) i.
    Checkable p i =>
    p i -> Result -> Property i
    `assume` Result
    MadeChange
    		Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> DataSize
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall p. IsProp p => p -> DataSize -> p
    `describe` ("ccache for " DataSize -> DataSize -> DataSize
    forall a. [a] -> [a] -> [a]
    ++ DataSize
    g DataSize -> DataSize -> DataSize
    forall a. [a] -> [a] -> [a]
    ++ " does not exist")

    	-- Here we deal with a bug in Propellor 3.0.5.  If the ccache was
    	-- created with that version, it will not have the setgid bit set.  That
    	-- means its subdirectories won't have inherited the setgid bit, and
    	-- then the files in those directories won't be owned by group sbuild.
    	-- This breaks ccache.
    	fixSetgidBit :: Property UnixLike
    	fixSetgidBit :: Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    fixSetgidBit =
    		(DataSize
    -> [DataSize]
    -> UncheckedProperty
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    cmdProperty "find"
    			[ DataSize
    path
    			, "-type", "d"
    			, "-exec", "chmod", "g+s"
    			, "{}", "+"
    			] UncheckedProperty
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Result
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall (p :: * -> *) i.
    Checkable p i =>
    p i -> Result -> Property i
    `assume` Result
    MadeChange)
    		Property
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> CombinedType
         (Property
            (MetaTypes
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
         (Property
            (MetaTypes
               '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
                  'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD]))
    forall x y. Combines x y => x -> y -> CombinedType x y
    `before`
    		(DataSize
    -> [DataSize]
    -> UncheckedProperty
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    cmdProperty "chown"
    			[ "-R"
    			, "root:" DataSize -> DataSize -> DataSize
    forall a. [a] -> [a] -> [a]
    ++ DataSize
    g
    			, DataSize
    path
    			] UncheckedProperty
      (MetaTypes
         '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
            'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    -> Result
    -> Property
         (MetaTypes
            '[ 'Targeting 'OSDebian, 'Targeting 'OSBuntish,
               'Targeting 'OSArchLinux, 'Targeting 'OSFreeBSD])
    forall (p :: * -> *) i.
    Checkable p i =>
    p i -> Result -> Property i
    `assume` Result
    MadeChange)

    	path :: DataSize
    path = "/var/cache/ccache-" DataSize -> DataSize -> DataSize
    forall a. [a] -> [a] -> [a]
    ++ DataSize
    g

    installed :: Property DebianLike
    installed :: Property DebianLike
    installed = [DataSize] -> Property DebianLike
    Apt.installed ["ccache"]