-- -*- coding: utf-8; mode: haskell; -*- -- File: library/Language/Ninja/IR/Target.hs -- -- License: -- Copyright 2017 Awake Security -- -- Licensed under the Apache License, Version 2.0 (the "License"); -- you may not use this file except in compliance with the License. -- You may obtain a copy of the License at -- -- http://www.apache.org/licenses/LICENSE-2.0 -- -- Unless required by applicable law or agreed to in writing, software -- distributed under the License is distributed on an "AS IS" BASIS, -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -- See the License for the specific language governing permissions and -- limitations under the License. {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE UndecidableInstances #-} -- | -- Module : Language.Ninja.IR.Target -- Copyright : Copyright 2017 Awake Security -- License : Apache-2.0 -- Maintainer : opensource@awakesecurity.com -- Stability : experimental -- -- Types relating to Ninja build targets, outputs, and dependencies. -- -- @since 0.1.0 module Language.Ninja.IR.Target ( -- * @Target@ Target, makeTarget, targetIText, targetText -- * @Output@ , Output, makeOutput, outputTarget -- * @Dependency@ , Dependency, makeDependency, dependencyTarget, dependencyType , DependencyType (..), _NormalDependency, _OrderOnlyDependency ) where import qualified Control.Lens as Lens import Data.Text (Text) import qualified Data.Text as Text import Data.Aeson ((.:), (.=)) import qualified Data.Aeson as Aeson import Control.DeepSeq (NFData) import Data.Hashable (Hashable) import Data.String (IsString) import GHC.Generics (Generic) import Test.SmallCheck.Series ((>>-)) import qualified Test.SmallCheck.Series as SC import qualified Language.Ninja.Misc.IText as Misc import Flow ((.>), (|>)) -------------------------------------------------------------------------------- -- | This type represents a Ninja target name. -- -- @since 0.1.0 newtype Target = MkTarget { _targetIText :: Misc.IText } deriving ( Eq, Ord, Show, Read, IsString, Generic, Hashable, NFData , Aeson.ToJSON, Aeson.FromJSON, Aeson.ToJSONKey, Aeson.FromJSONKey ) -- | Construct a 'Target' from some 'Text'. -- -- @since 0.1.0 {-# INLINE makeTarget #-} makeTarget :: Text -> Target makeTarget = Lens.view Misc.itext .> MkTarget -- | An isomorphism between a 'Target' and its underlying 'IText'. -- -- @since 0.1.0 {-# INLINE targetIText #-} targetIText :: Lens.Iso' Target Misc.IText targetIText = Lens.iso _targetIText MkTarget -- | An isomorphism that gives access to a 'Text'-typed view of a 'Target', -- even though the underlying data has type 'IText'. -- -- This is equivalent to @targetIText . from itext@. -- -- @since 0.1.0 {-# INLINE targetText #-} targetText :: Lens.Iso' Target Text targetText = targetIText . Lens.from Misc.itext -- | Uses the underlying 'IText' instance. -- -- @since 0.1.0 instance (Monad m, SC.Serial m Text) => SC.Serial m Target where series = SC.newtypeCons MkTarget -- | Uses the underlying 'IText' instance. -- -- @since 0.1.0 instance (Monad m, SC.CoSerial m Text) => SC.CoSerial m Target where coseries rs = SC.newtypeAlts rs >>- \f -> pure (_targetIText .> f) -------------------------------------------------------------------------------- -- | A Ninja build output. -- -- More information is available -- . -- -- @since 0.1.0 newtype Output = MkOutput { _outputTarget :: Target } deriving (Eq, Ord, Show, Read, Generic) -- | Construct an 'Output'. -- -- @since 0.1.0 {-# INLINE makeOutput #-} makeOutput :: Target -- ^ The underlying target. -> Output makeOutput = MkOutput -- | A lens for the 'Target' of an 'Output'. -- -- @since 0.1.0 {-# INLINE outputTarget #-} outputTarget :: Lens.Lens' Output Target outputTarget = Lens.lens _outputTarget $ \(MkOutput {..}) new -> MkOutput { _outputTarget = new, .. } -- | Converts to @{target: …, type: …}@. -- -- @since 0.1.0 instance Aeson.ToJSON Output where toJSON (MkOutput {..}) = [ "target" .= _outputTarget ] |> Aeson.object -- | Inverse of the 'Aeson.ToJSON' instance. -- -- @since 0.1.0 instance Aeson.FromJSON Output where parseJSON = (Aeson.withObject "Output" $ \o -> do _outputTarget <- (o .: "target") >>= pure pure (MkOutput {..})) -- | Default 'Hashable' instance via 'Generic'. -- -- @since 0.1.0 instance Hashable Output -- | Default 'NFData' instance via 'Generic'. -- -- @since 0.1.0 instance NFData Output -- | Default 'SC.Serial' instance via 'Generic'. -- -- @since 0.1.0 instance (Monad m, SC.Serial m Text) => SC.Serial m Output -- | Default 'SC.CoSerial' instance via 'Generic'. -- -- @since 0.1.0 instance (Monad m, SC.CoSerial m Text) => SC.CoSerial m Output -------------------------------------------------------------------------------- -- | A build dependency. -- -- More information is available -- . -- -- @since 0.1.0 data Dependency = MkDependency { _dependencyTarget :: !Target , _dependencyType :: !DependencyType } deriving (Eq, Ord, Show, Read, Generic) -- | Construct a 'Dependency'. -- -- @since 0.1.0 {-# INLINE makeDependency #-} makeDependency :: Target -- ^ The underlying target. -> DependencyType -- ^ The dependency type (normal, implicit, or order-only). -> Dependency makeDependency = MkDependency -- | A lens for the 'Target' of a 'Dependency'. -- -- @since 0.1.0 {-# INLINE dependencyTarget #-} dependencyTarget :: Lens.Lens' Dependency Target dependencyTarget = Lens.lens _dependencyTarget $ \(MkDependency {..}) new -> MkDependency { _dependencyTarget = new, .. } -- | A lens for the 'DependencyType' of a 'Dependency'. -- -- @since 0.1.0 {-# INLINE dependencyType #-} dependencyType :: Lens.Lens' Dependency DependencyType dependencyType = Lens.lens _dependencyType $ \(MkDependency {..}) new -> MkDependency { _dependencyType = new, .. } -- | Converts to @{target: …, type: …}@. -- -- @since 0.1.0 instance Aeson.ToJSON Dependency where toJSON (MkDependency {..}) = [ "target" .= _dependencyTarget , "type" .= _dependencyType ] |> Aeson.object -- | Inverse of the 'Aeson.ToJSON' instance. -- -- @since 0.1.0 instance Aeson.FromJSON Dependency where parseJSON = (Aeson.withObject "Dependency" $ \o -> do _dependencyTarget <- (o .: "target") >>= pure _dependencyType <- (o .: "type") >>= pure pure (MkDependency {..})) -- | Default 'Hashable' instance via 'Generic'. -- -- @since 0.1.0 instance Hashable Dependency -- | Default 'NFData' instance via 'Generic'. -- -- @since 0.1.0 instance NFData Dependency -- | Default 'SC.Serial' instance via 'Generic'. -- -- @since 0.1.0 instance (Monad m, SC.Serial m Text) => SC.Serial m Dependency -- | Default 'SC.CoSerial' instance via 'Generic'. -- -- @since 0.1.0 instance (Monad m, SC.CoSerial m Text) => SC.CoSerial m Dependency -------------------------------------------------------------------------------- -- | The type of a 'Dependency': normal, implicit, or order-only. -- -- @since 0.1.0 data DependencyType = -- | A normal dependency. -- -- @since 0.1.0 NormalDependency | -- | An order-only dependency. These are only rebuilt if there is at least -- one non-order-only dependency that is out of date. -- -- Section 4.3 "Types of Prerequisites" in the GNU Make manual has a good -- explanation of this concept. -- -- @since 0.1.0 OrderOnlyDependency deriving (Eq, Ord, Show, Read, Generic) -- | A prism for the 'NormalDependency' constructor. -- -- @since 0.1.0 {-# INLINE _NormalDependency #-} _NormalDependency :: Lens.Prism' DependencyType () _NormalDependency = Lens.prism' (const NormalDependency) $ \case NormalDependency -> Just () _ -> Nothing -- | A prism for the 'OrderOnlyDependency' constructor. -- -- @since 0.1.0 {-# INLINE _OrderOnlyDependency #-} _OrderOnlyDependency :: Lens.Prism' DependencyType () _OrderOnlyDependency = Lens.prism' (const OrderOnlyDependency) $ \case OrderOnlyDependency -> Just () _ -> Nothing -- | Converts to @"normal"@, @"implicit"@, and @"order-only"@ respectively. -- -- @since 0.1.0 instance Aeson.ToJSON DependencyType where toJSON NormalDependency = "normal" toJSON OrderOnlyDependency = "order-only" -- | Inverse of the 'Aeson.ToJSON' instance. -- -- @since 0.1.0 instance Aeson.FromJSON DependencyType where parseJSON = (Aeson.withText "DependencyType" $ \case "normal" -> pure NormalDependency "order-only" -> pure OrderOnlyDependency owise -> [ "Invalid dependency type " , "\"", owise, "\"; should be one of " , "[\"normal\", \"order-only\"]" ] |> mconcat |> Text.unpack |> fail) -- | Default 'Hashable' instance via 'Generic'. -- -- @since 0.1.0 instance Hashable DependencyType -- | Default 'NFData' instance via 'Generic'. -- -- @since 0.1.0 instance NFData DependencyType -- | Default 'SC.Serial' instance via 'Generic'. -- -- @since 0.1.0 instance (Monad m) => SC.Serial m DependencyType -- | Default 'SC.CoSerial' instance via 'Generic'. -- -- @since 0.1.0 instance (Monad m) => SC.CoSerial m DependencyType --------------------------------------------------------------------------------