Skip to content

Commit

Permalink
Nix.Derivation: vendor parser from sorki's Haskell-Nix-Derivation-Lib…
Browse files Browse the repository at this point in the history
…rary fork

All credit goes to @sorki and @rickynils for their work on:

- Gabriella439/Haskell-Nix-Derivation-Library#26
- Gabriella439/Haskell-Nix-Derivation-Library#24

and of course to @Gabriella439 for the entire library!

This commit takes the changes relevant to `Nix.Derivation` and vendors them in-tree.

Co-Authored-By: Gabriella Gonzalez <[email protected]>
Co-Authored-By: sorki <[email protected]>
Co-Authored-By: Rickard Nilsson <[email protected]>
  • Loading branch information
4 people committed Jan 25, 2024
1 parent 7c72280 commit ed3d927
Show file tree
Hide file tree
Showing 6 changed files with 309 additions and 6 deletions.
13 changes: 9 additions & 4 deletions lib/NOM/Update.hs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ lookupDerivationInfos drvName = do
drvId <- lookupDerivation drvName
getDerivationInfos drvId

insertDerivation :: Nix.Derivation FilePath Text -> DerivationId -> ProcessingT m ()
insertDerivation :: Nix.Derivation FilePath Text Text Nix.DerivationOutput Nix.DerivationInputs -> DerivationId -> ProcessingT m ()
insertDerivation derivation drvId = do
-- We need to be really careful in this function. The Nix.Derivation keeps the
-- read-in derivation file in memory. When using Texts from it we must make
Expand All @@ -371,20 +371,25 @@ insertDerivation derivation drvId = do

outputs <-
derivation.outputs & Map.mapKeys (parseOutputName . Text.copy) & Map.traverseMaybeWithKey \_ path ->
parseStorePath (toText (Nix.path path)) & mapM \pathName -> do
let
storePath = case path of
Nix.ContentAddressedDerivationOutput{} -> error "God help us all it's a content-addressed output"
_ -> path.path
in
parseStorePath (toText storePath) & mapM \pathName -> do
pathId <- getStorePathId pathName
modify' (gfield @"storePathInfos" %~ CMap.adjust (gfield @"producer" .~ Strict.Just drvId) pathId)
pure pathId
inputSources <-
derivation.inputSrcs & flip foldlM mempty \acc path -> do
derivation.inputs.srcs & flip foldlM mempty \acc path -> do
pathIdMay <-
parseStorePath (toText path) & mapM \pathName -> do
pathId <- getStorePathId pathName
modify' (gfield @"storePathInfos" %~ CMap.adjust (gfield @"inputFor" %~ CSet.insert drvId) pathId)
pure pathId
pure $ maybe id CSet.insert pathIdMay acc
inputDerivationsList <-
derivation.inputDrvs & Map.toList & mapMaybeM \(drvPath, outputs_of_input) -> do
derivation.inputs.drvs & Map.toList & mapMaybeM \(drvPath, outputs_of_input) -> do
depIdMay <-
parseDerivation (toText drvPath) & mapM \depName -> do
depId <- lookupDerivation depName
Expand Down
2 changes: 1 addition & 1 deletion lib/NOM/Update/Monad.hs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ instance (MonadNow m) => MonadNow (WriterT a m) where
getNow = lift getNow

class (Monad m) => MonadReadDerivation m where
getDerivation :: Derivation -> m (Either NOMError (Nix.Derivation FilePath Text))
getDerivation :: Derivation -> m (Either NOMError (Nix.Derivation FilePath Text Text Nix.DerivationOutput Nix.DerivationInputs))

instance MonadReadDerivation IO where
getDerivation =
Expand Down
23 changes: 23 additions & 0 deletions lib/Nix/Derivation.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module Nix.Derivation
( -- * Types
Derivation (..),
DerivationInputs (..),
DerivationOutput (..),

-- * Parse derivations
parseDerivation,
parseDerivationWith,
textParser,
)
where

import Nix.Derivation.Parser
( parseDerivation,
parseDerivationWith,
textParser,
)
import Nix.Derivation.Types
( Derivation (..),
DerivationInputs (..),
DerivationOutput (..),
)
194 changes: 194 additions & 0 deletions lib/Nix/Derivation/Parser.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
{-# LANGUAGE MultiWayIf #-}
{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}

-- | Parsing logic
module Nix.Derivation.Parser
( -- * Parser
parseDerivation,
parseDerivationWith,
textParser,
)
where

import Control.Applicative (Applicative (pure))
import Control.Monad (MonadFail (fail))
import Data.Attoparsec.Text qualified
import Data.Attoparsec.Text.Lazy (Parser)
import Data.Attoparsec.Text.Lazy qualified
import Data.Bool (Bool (..), not, otherwise, (&&), (||))
import Data.Eq (Eq (..))
import Data.Functor ((<$>))
import Data.Map (Map)
import Data.Map qualified
import Data.Maybe (Maybe (Just))
import Data.Monoid (Monoid (mempty))
import Data.Ord (Ord)
import Data.Semigroup (Semigroup ((<>)))
import Data.Set (Set)
import Data.Set qualified
import Data.Text (Text)
import Data.Text qualified
import Data.Vector (Vector)
import Data.Vector qualified
import Nix.Derivation.Types
( Derivation (..),
DerivationInputs (..),
DerivationOutput (..),
)
import System.FilePath (FilePath)
import System.FilePath qualified

listOf :: Parser a -> Parser [a]
listOf element = do
"["
es <- Data.Attoparsec.Text.Lazy.sepBy element ","
"]"
pure es

-- | Parse a derivation
parseDerivation ::
Parser
( Derivation
FilePath
Text
Text
DerivationOutput
DerivationInputs
)
parseDerivation =
parseDerivationWith
textParser
textParser
(parseDerivationOutputWith filepathParser)
(parseDerivationInputsWith filepathParser textParser)

-- | Parse a derivation using custom
-- parsers for filepaths, texts, outputNames and derivation inputs/outputs
parseDerivationWith ::
( Ord txt,
Ord outputName
) =>
Parser txt ->
Parser outputName ->
Parser (drvOutput fp) ->
Parser (drvInputs fp outputName) ->
Parser (Derivation fp txt outputName drvOutput drvInputs)
parseDerivationWith string outputName parseOutput parseInputs = do
"Derive("
let keyValue0 = do
"("
key <- outputName
","
out <- parseOutput
")"
pure (key, out)
outputs <- mapOf keyValue0
","
inputs <- parseInputs
","
platform <- string
","
builder <- string
","
args <- vectorOf string
","
let keyValue1 = do
"("
key <- string
","
value <- string
")"
pure (key, value)
env <- mapOf keyValue1
")"
pure Derivation {..}

-- | Parse a derivation output
parseDerivationOutputWith ::
( Eq fp,
Monoid fp
) =>
Parser fp ->
Parser (DerivationOutput fp)
parseDerivationOutputWith filepath = do
path <- filepath
","
hashAlgo <- textParser
","
hash <- textParser
if
| path /= mempty && hashAlgo == mempty && hash == mempty ->
pure DerivationOutput {..}
| path /= mempty && hashAlgo /= mempty && hash /= mempty ->
pure FixedDerivationOutput {..}
| path == mempty && hashAlgo /= mempty && hash == mempty ->
pure ContentAddressedDerivationOutput {..}
| otherwise ->
fail "bad output in derivation"

-- | Parse a derivation inputs
parseDerivationInputsWith ::
( Ord fp,
Ord outputName
) =>
Parser fp ->
Parser outputName ->
Parser (DerivationInputs fp outputName)
parseDerivationInputsWith filepath outputName = do
let keyValue = do
"("
key <- filepath
","
value <- setOf outputName
")"
pure (key, value)
drvs <- mapOf keyValue
","
srcs <- setOf filepath
pure DerivationInputs {..}

textParser :: Parser Text
textParser = do
"\""
let predicate c = not (c == '"' || c == '\\')
let loop = do
text0 <- Data.Attoparsec.Text.takeWhile predicate
char0 <- Data.Attoparsec.Text.anyChar
case char0 of
'"' -> do
pure [text0]
_ -> do
char1 <- Data.Attoparsec.Text.anyChar
char2 <- case char1 of
'n' -> pure '\n'
'r' -> pure '\r'
't' -> pure '\t'
_ -> pure char1
textChunks <- loop
pure (text0 : Data.Text.singleton char2 : textChunks)
Data.Text.concat <$> loop

filepathParser :: Parser FilePath
filepathParser = do
text <- textParser
let str = Data.Text.unpack text
case (Data.Text.uncons text, System.FilePath.isValid str) of
(Just ('/', _), True) -> do
pure str
_ -> do
fail ("bad path ‘" <> Data.Text.unpack text <> "’ in derivation")

setOf :: (Ord a) => Parser a -> Parser (Set a)
setOf element = do
es <- listOf element
pure (Data.Set.fromList es)

vectorOf :: Parser a -> Parser (Vector a)
vectorOf element = do
es <- listOf element
pure (Data.Vector.fromList es)

mapOf :: (Ord k) => Parser (k, v) -> Parser (Map k v)
mapOf keyValue = do
keyValues <- listOf keyValue
pure (Data.Map.fromList keyValues)
77 changes: 77 additions & 0 deletions lib/Nix/Derivation/Types.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{-# OPTIONS_GHC -Wno-partial-fields #-}
-- | Shared types
module Nix.Derivation.Types
( -- * Types
Derivation (..),
DerivationInputs (..),
DerivationOutput (..),
)
where

import Control.DeepSeq (NFData)
import Data.Map (Map)
import Data.Set (Set)
import Data.Text (Text)
import Data.Vector (Vector)
import GHC.Generics (Generic)
import Prelude (Eq, Ord, Show)

-- | A Nix derivation
data Derivation fp txt outputName drvOutput drvInputs = Derivation
{ -- | Outputs produced by this derivation where keys are output names
outputs :: Map outputName (drvOutput fp),
-- | Inputs (sources and derivations)
inputs :: drvInputs fp outputName,
-- | Platform required for this derivation
platform :: txt,
-- | Code to build the derivation, which can be a path or a builtin function
builder :: txt,
-- | Arguments passed to the executable used to build to derivation
args :: Vector txt,
-- | Environment variables provided to the executable used to build the
-- derivation
env :: Map txt txt
}
deriving stock (Eq, Generic, Ord, Show)

instance
( NFData fp,
NFData txt,
NFData outputName,
NFData (drvOutput fp),
NFData (drvInputs fp outputName)
) =>
NFData (Derivation fp txt outputName drvOutput drvInputs)

data DerivationInputs fp drvOutput = DerivationInputs
{ -- | Inputs that are derivations where keys specify derivation paths and
-- values specify which output names are used by this derivation
drvs :: Map fp (Set drvOutput),
-- | Inputs that are sources
srcs :: Set fp
}
deriving stock (Eq, Generic, Ord, Show)

instance (NFData a, NFData b) => NFData (DerivationInputs a b)

-- | An output of a Nix derivation
data DerivationOutput fp
= DerivationOutput
{ -- | Path where the output will be saved
path :: fp
}
| FixedDerivationOutput
{ -- | Path where the output will be saved
path :: fp,
-- | Hash used for expected hash computation
hashAlgo :: Text,
-- | Expected hash
hash :: Text
}
| ContentAddressedDerivationOutput
{ -- | Hash used for expected hash computation
hashAlgo :: Text
}
deriving stock (Eq, Generic, Ord, Show)

instance (NFData a) => NFData (DerivationOutput a)
6 changes: 5 additions & 1 deletion nix-output-monitor.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,13 @@ common common-config
, cassava
, containers
, data-default
, deepseq
, directory
, extra
, filepath
, hermes-json >=0.6.0.0
, lock-file
, MemoTrie
, nix-derivation
, optics
, relude
, safe
Expand All @@ -82,6 +82,7 @@ common common-config
, text
, time
, transformers
, vector
, word8

default-language: GHC2021
Expand All @@ -99,6 +100,9 @@ library
hs-source-dirs: lib
exposed-modules:
Data.Sequence.Strict
Nix.Derivation
Nix.Derivation.Parser
Nix.Derivation.Types
NOM.Builds
NOM.Error
NOM.IO
Expand Down

0 comments on commit ed3d927

Please sign in to comment.