packages feed

hercules-ci-cnix-store 0.1.1.0 → 0.2.0.0

raw patch · 29 files changed

+1821/−224 lines, 29 filesdep +exceptionsdep +hercules-ci-cnix-storedep +hspecdep ~base

Dependencies added: exceptions, hercules-ci-cnix-store, hspec, template-haskell, temporary, text, vector

Dependency ranges changed: base

Files

CHANGELOG.md view
@@ -5,6 +5,27 @@  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## 0.2.0.0 - 2021-06-16++### Added++ - Speculative support for nixUnstable++ - `StorePath` (compatible for stable Nix)++ - Use and build on inline-c-cpp's TemplateHaskell-based template support++ - `nixVersion`, `storeDir`++ - Some settings getters++### Changed++ - Many functions now work with `StorePath` instead of plain paths++ - Some functions now need a `Store` because of the `StorePath` change++ ## 0.1.1.0 - 2021-04-21  ### Added
+ compat-2.3/derivation-output.cxx view
@@ -0,0 +1,23 @@++#include <nix/config.h>+#include <nix/store-api.hh>+#include <nix/derivations.hh>+#include <nix/path-compat.hh>+#include "nix/derivation-output.hh"++using namespace nix;++static std::string outputPathName(std::string_view drvName, std::string_view outputName) {+    std::string res { drvName };+    if (outputName != "out") {+        res += "-";+        res += outputName;+    }+    return res;+}++StorePath DerivationOutputCAFixed::path(const Store & store, std::string_view drvName, std::string_view outputName) const {+    return parseStorePath(store, store.makeFixedOutputPath(+        hash.method == FileIngestionMethod::Recursive, hash.hash,+        outputPathName(drvName, outputName)));+}
+ compat-2.3/include/nix/content-address.hh view
@@ -0,0 +1,77 @@+#pragma once++#include <variant>+#include "hash.hh"++namespace nix {++enum struct FileIngestionMethod : uint8_t {+    Flat = false,+    Recursive = true+};++struct TextHash {+    Hash hash;+};++/// Pair of a hash, and how the file system was ingested+struct FixedOutputHash {+    FileIngestionMethod method;+    Hash hash;+    std::string printMethodAlgo() const;+};++/*+  We've accumulated several types of content-addressed paths over the years;+  fixed-output derivations support multiple hash algorithms and serialisation+  methods (flat file vs NAR). Thus, ‘ca’ has one of the following forms:++  * ‘text:sha256:<sha256 hash of file contents>’: For paths+    computed by makeTextPath() / addTextToStore().++  * ‘fixed:<r?>:<ht>:<h>’: For paths computed by+    makeFixedOutputPath() / addToStore().+*/+typedef std::variant<+    TextHash, // for paths computed by makeTextPath() / addTextToStore+    FixedOutputHash // for path computed by makeFixedOutputPath+> ContentAddress;++/* Compute the prefix to the hash algorithm which indicates how the files were+   ingested. */+std::string makeFileIngestionPrefix(const FileIngestionMethod m);++/* Compute the content-addressability assertion (ValidPathInfo::ca)+   for paths created by makeFixedOutputPath() / addToStore(). */+std::string makeFixedOutputCA(FileIngestionMethod method, const Hash & hash);++std::string renderContentAddress(ContentAddress ca);++std::string renderContentAddress(std::optional<ContentAddress> ca);++ContentAddress parseContentAddress(std::string_view rawCa);++std::optional<ContentAddress> parseContentAddressOpt(std::string_view rawCaOpt);++Hash getContentAddressHash(const ContentAddress & ca);++/*+  We only have one way to hash text with references, so this is single-value+  type is only useful in std::variant.+*/+struct TextHashMethod { };+struct FixedOutputHashMethod {+  FileIngestionMethod fileIngestionMethod;+  HashType hashType;+};++typedef std::variant<+    TextHashMethod,+    FixedOutputHashMethod+  > ContentAddressMethod;++ContentAddressMethod parseContentAddressMethod(std::string_view rawCaMethod);++std::string renderContentAddressMethod(ContentAddressMethod caMethod);++}
+ compat-2.3/include/nix/derivation-output.hh view
@@ -0,0 +1,68 @@+#pragma once++#include "path.hh"+#include "types.hh"+#include "hash.hh"+#include "content-address.hh"+#include "sync.hh"++#include <map>+#include <variant>++namespace nix {+/* Abstract syntax of derivations. */++/* The traditional non-fixed-output derivation type. */+struct DerivationOutputInputAddressed+{+    StorePath path;+};++/* Fixed-output derivations, whose output paths are content addressed+   according to that fixed output. */+struct DerivationOutputCAFixed+{+    std::string drvName; // !!! not in Nix 2.4, which stores name in Derivation.+    FixedOutputHash hash; /* hash used for expected hash computation */+    StorePath path(const Store & store, std::string_view drvName, std::string_view outputName) const;+};++/* Floating-output derivations, whose output paths are content addressed, but+   not fixed, and so are dynamically calculated from whatever the output ends+   up being. */+struct DerivationOutputCAFloating+{+    /* information used for expected hash computation */+    FileIngestionMethod method;+    HashType hashType;+};++/* Input-addressed output which depends on a (CA) derivation whose hash isn't+ * known atm+ */+struct DerivationOutputDeferred {};++}++namespace compat::nix {++using namespace ::nix;++struct DerivationOutput+{+    std::variant<+        DerivationOutputInputAddressed,+        DerivationOutputCAFixed,+        DerivationOutputCAFloating,+        DerivationOutputDeferred+    > output;++    /* Note, when you use this function you should make sure that you're passing+       the right derivation name. When in doubt, you should use the safer+       interface provided by BasicDerivation::outputsAndOptPaths */+    std::optional<StorePath> path(const Store & store, std::string_view drvName, std::string_view outputName) const;+};++typedef std::map<string, DerivationOutput> DerivationOutputs;++}
+ compat-2.3/include/nix/path-compat.hh view
@@ -0,0 +1,42 @@+#pragma once+#include "path.hh"+#include <vector>++inline nix::Path printPath23(const nix::Store &store, const nix::StorePath &sp) {+  return (store.storeDir + "/").append(sp.to_string());+}++inline nix::StorePath parseStorePath(const nix::Store &store, const nix::Path path) {+  nix::Path p = nix::canonPath(path);+  if (nix::dirOf(p) != store.storeDir)+    throw nix::Error("path '%s' is not in the Nix store", p);+  return nix::StorePath(nix::baseNameOf(p));+}++inline nix::PathSet printPathSet23(const nix::Store &store, std::vector<nix::StorePathWithOutputs> &sps) {+  nix::PathSet r;+  for (auto spwo : sps) {+    for (auto output : spwo.outputs) {+      r.insert(printPath23(store, spwo.path) + "!" + output);+    }+  }+  return r;+}++inline nix::PathSet compatPathSet(const nix::Store &store, nix::StorePathSet &sps) {+  nix::PathSet r;+  for (auto sp : sps) {+    r.insert(printPath23(store, sp));+  }+  return r;+}++inline nix::StorePathSet compatStorePathSet(const nix::Store &store, nix::PathSet &sps) {+  nix::StorePathSet r;+  for (auto sp : sps) {+    r.insert(parseStorePath(store, sp));+  }+  return r;+}++#define parseStorePath23(store, path) parseStorePath(store, path)
+ compat-2.3/include/nix/path-info.hh view
@@ -0,0 +1,3 @@+#pragma once++#include <nix/store-api.hh>
+ compat-2.3/include/nix/path.hh view
@@ -0,0 +1,91 @@+#pragma once++// #include "content-address.hh"+#include <nix/config.h>+#include "types.hh"++namespace nix {++class Store;+struct Hash;++class StorePath+{+    std::string baseName;++public:++    /* Size of the hash part of store paths, in base-32 characters. */+    constexpr static size_t HashLen = 32; // i.e. 160 bits++    StorePath() = delete;++    StorePath(std::string_view baseName);++    StorePath(const Hash & hash, std::string_view name);++    std::string_view to_string() const+    {+        return baseName;+    }++    bool operator < (const StorePath & other) const+    {+        return baseName < other.baseName;+    }++    bool operator == (const StorePath & other) const+    {+        return baseName == other.baseName;+    }++    bool operator != (const StorePath & other) const+    {+        return baseName != other.baseName;+    }++    /* Check whether a file name ends with the extension for+       derivations. */+    bool isDerivation() const;++    std::string_view name() const+    {+        return std::string_view(baseName).substr(HashLen + 1);+    }++    std::string_view hashPart() const+    {+        return std::string_view(baseName).substr(0, HashLen);+    }++    static StorePath dummy;+};++typedef std::set<StorePath> StorePathSet;+typedef std::vector<StorePath> StorePaths;+typedef std::map<string, StorePath> OutputPathMap;++// typedef std::map<StorePath, std::optional<ContentAddress>> StorePathCAMap;++struct StorePathWithOutputs+{+    StorePath path;+    std::set<std::string> outputs;++    std::string to_string(const Store & store) const;+};++std::pair<std::string_view, StringSet> parsePathWithOutputs(std::string_view s);++}++namespace std {++template<> struct hash<nix::StorePath> {+    std::size_t operator()(const nix::StorePath & path) const noexcept+    {+        return * (std::size_t *) path.to_string().data();+    }+};++}
+ compat-2.3/path.cxx view
@@ -0,0 +1,54 @@++#include <nix/config.h>+#include <nix/store-api.hh>+#include <nix/path-compat.hh>+#include <nix/path.hh>+#include <nix-compat.hh>++using namespace nix;++static void checkName(std::string_view path, std::string_view name)+{+    if (name.empty())+        throw Error("store path '%s' has an empty name", path);+    if (name.size() > 211)+        throw Error("store path '%s' has a name longer than 211 characters", path);+    for (auto c : name)+        if (!((c >= '0' && c <= '9')+                || (c >= 'a' && c <= 'z')+                || (c >= 'A' && c <= 'Z')+                || c == '+' || c == '-' || c == '.' || c == '_' || c == '?' || c == '='))+            throw Error("store path '%s' contains illegal character '%s'", path, c);+}++StorePath::StorePath(std::string_view _baseName)+    : baseName(_baseName)+{+    if (baseName.size() < HashLen + 1)+        throw Error("'%s' is too short to be a valid store path", baseName);+    for (auto c : hashPart())+        if (c == 'e' || c == 'o' || c == 'u' || c == 't'+            || !((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')))+            throw Error("store path '%s' contains illegal base-32 character '%s'", baseName, c);+    checkName(baseName, name());+}++StorePath::StorePath(const Hash & hash, std::string_view _name)+    : baseName((hash.to_string(Base32, false) + "-").append(std::string(_name)))+{+    checkName(baseName, name());+}++std::pair<std::string_view, StringSet> nix::parsePathWithOutputs(std::string_view s)+{+    size_t n = s.find("!");+    return n == s.npos+        ? std::make_pair(s, std::set<string>())+        : std::make_pair(((std::string_view) s).substr(0, n),+            tokenizeString<std::set<string>>((std::string(s)).substr(n + 1), ","));+}++StorePathWithOutputs nix::parsePathWithOutputs(const Store &store, const std::string & s) {+    auto [path, outputs] = nix::parsePathWithOutputs(s);+    return {parseStorePath(store, std::string(path)), std::move(outputs)};+}
+ compat-2.4/include/nix-compat.hh view
@@ -0,0 +1,26 @@+#pragma once+#include <nix/store-api.hh>+#include <nix/derivations.hh>+#include <nix/path-with-outputs.hh>++#define printPath23(store, path) (path)+#define compatPathSet(store, paths) (paths)+#define printPathSet23(store, pathSet) (pathSet)+#define parseStorePath23(store, path) (path)+#define parseStorePathSet23(store, pathSet) (pathSet)+#define parseOptionalStorePath23(store, path) (path)+#define toDerivedPaths24(x) toDerivedPaths(x)++inline nix::StorePath parseStorePath(const nix::Store &store, const nix::Path path) {+  return store.parseStorePath(path);+}++inline void compatComputeFSClosure(nix::Store &store, nix::StorePathSet &pathSet, nix::StorePathSet &closurePaths, bool flipDirection = false, bool includeOutputs = false, bool includeDerivers = false) {+  store.computeFSClosure(pathSet, closurePaths, flipDirection, includeOutputs, includeDerivers);+}++namespace nix {+  inline StorePathWithOutputs parsePathWithOutputs(Store &store, const std::string & s) {+    return parsePathWithOutputs(store, s);+  }+}
hercules-ci-cnix-store.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.4  name:           hercules-ci-cnix-store-version:        0.1.1.0+version:        0.2.0.0 synopsis:       Haskell bindings for Nix's libstore category:       Nix homepage:       https://docs.hercules-ci.com@@ -13,11 +13,16 @@ build-type:     Simple extra-source-files:     CHANGELOG.md+    test/data/*.drv  source-repository head   type: git   location: https://github.com/hercules-ci/hercules-ci-agent +flag nix-2_4+  description: Build for Nix >=2.4pre*+  default: False+ -- match the C++ language standard Nix is using common cxx-opts   cxx-options:@@ -44,13 +49,46 @@   import: cxx-opts   exposed-modules:       Hercules.CNix-      Hercules.CNix.Util-      Hercules.CNix.Store.Context+      Hercules.CNix.Encapsulation+      Hercules.CNix.Settings+      Hercules.CNix.Std.Set+      Hercules.CNix.Std.String+      Hercules.CNix.Std.String.Context+      Hercules.CNix.Std.String.Instances+      Hercules.CNix.Std.Vector       Hercules.CNix.Store+      Hercules.CNix.Store.Context+      Hercules.CNix.Store.Instances+      Hercules.CNix.Util   include-dirs:       include+  if flag(nix-2_4)+    include-dirs:+        compat-2.4/include+    cpp-options:+        -DNIX_2_4+    pkgconfig-depends:+        nix-store >= 2.4+      , nix-main >= 2.4+  else+    include-dirs:+        compat-2.3/include+    pkgconfig-depends:+        nix-store >= 2.0 && <2.4+      , nix-main >= 2.0 && <2.4+    cxx-sources:+        compat-2.3/derivation-output.cxx+        compat-2.3/path.cxx+    install-includes:+        nix/path-info.hh+        nix/path.hh+        nix/path-compat.hh+        nix/derivation-output.hh+        nix/content-address.hh+   install-includes:       hercules-ci-cnix/store.hxx+      nix-compat.hh    hs-source-dirs: src   build-depends:@@ -61,10 +99,9 @@     , conduit     , containers     , protolude+    , template-haskell     , unliftio-core-  pkgconfig-depends:-      nix-store >= 2.0-    , nix-main >= 2.0+    , vector   extra-libraries:       boost_context   default-language: Haskell2010@@ -77,3 +114,31 @@     -fwarn-missing-signatures     -fwarn-name-shadowing     -fwarn-incomplete-patterns++test-suite test+  type: exitcode-stdio-1.0+  main-is: TestMain.hs+  other-modules:+      Spec+      Hercules.CNix.Std.SetSpec+      Hercules.CNix.Store.DerivationSpec+      Hercules.CNix.Store.TestUtil+  hs-source-dirs:+      test+  default-extensions: DeriveGeneric DeriveTraversable DisambiguateRecordFields FlexibleContexts InstanceSigs LambdaCase MultiParamTypeClasses NoImplicitPrelude OverloadedStrings RankNTypes TupleSections TypeApplications TypeOperators+  ghc-options: -Werror=incomplete-patterns -Werror=missing-fields -Wall -fwarn-tabs -fwarn-unused-imports -fwarn-missing-signatures -fwarn-name-shadowing -fwarn-incomplete-patterns -threaded -rtsopts -with-rtsopts=-N+  build-depends:+    , base+    , bytestring+    , containers+    , exceptions+    , hercules-ci-cnix-store+    , hspec+    , inline-c+    , inline-c-cpp+    , protolude+    , temporary+    , text+  build-tool-depends:+      hspec-discover:hspec-discover+  default-language: Haskell2010
include/hercules-ci-cnix/store.hxx view
@@ -1,6 +1,8 @@  #pragma once +#include <nix/path-info.hh>+ typedef nix::ref<nix::Store> refStore;  typedef nix::Strings::iterator StringsIterator;
src/Hercules/CNix.hs view
@@ -12,10 +12,12 @@ -- TODO: No more NixStore when EvalState is already there -- TODO: Map Nix-specific C++ exceptions to a CNix exception type +import Data.ByteString.Unsafe (unsafePackMallocCString) import Hercules.CNix.Store import qualified Language.C.Inline.Cpp as C import qualified Language.C.Inline.Cpp.Exceptions as C import Protolude hiding (evalState, throwIO)+import System.IO.Unsafe (unsafePerformIO)  C.context context @@ -96,3 +98,11 @@   [C.block| void {     $(Strings *ss)->push_back(std::string($bs-ptr:s, $bs-len:s));   }|]++nixVersion :: ByteString+nixVersion = unsafePerformIO $ do+  p <-+    [C.exp| const char* {+      strdup(nix::nixVersion.c_str())+    }|]+  unsafePackMallocCString p
+ src/Hercules/CNix/Encapsulation.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE FunctionalDependencies #-}++module Hercules.CNix.Encapsulation+  ( HasEncapsulation (..),+    nullableMoveToForeignPtrWrapper,+  )+where++import Foreign (Ptr, nullPtr)+import Prelude++class HasEncapsulation a b | b -> a where+  -- | Takes ownership of the pointer, freeing/finalizing the pointer when+  -- collectable.+  moveToForeignPtrWrapper :: Ptr a -> IO b++nullableMoveToForeignPtrWrapper :: HasEncapsulation a b => Ptr a -> IO (Maybe b)+nullableMoveToForeignPtrWrapper rawPtr | rawPtr == nullPtr = pure Nothing+nullableMoveToForeignPtrWrapper rawPtr = Just <$> moveToForeignPtrWrapper rawPtr
+ src/Hercules/CNix/Settings.hs view
@@ -0,0 +1,99 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++module Hercules.CNix.Settings where++import Data.ByteString.Unsafe (unsafePackMallocCString)+import qualified Data.Set as S+import Hercules.CNix.Encapsulation (moveToForeignPtrWrapper)+import qualified Hercules.CNix.Std.Set as Std.Set+import qualified Hercules.CNix.Std.String as Std.String+import Hercules.CNix.Std.String.Instances ()+import qualified Hercules.CNix.Std.Vector as Std.Vector+import Hercules.CNix.Store.Context (context)+import qualified Language.C.Inline.Cpp as C+import Protolude hiding (evalState, throwIO)++C.context+  ( context+      <> Std.Set.stdSetCtx+      <> Std.String.stdStringCtx+      <> Std.Vector.stdVectorCtx+  )++C.include "<cstring>"+C.include "<nix/config.h>"+C.include "<nix/globals.hh>"+C.include "<set>"+C.include "<string>"++byteStringSet :: IO (Ptr (Std.Set.CStdSet Std.String.CStdString)) -> IO (Set ByteString)+byteStringSet x =+  x+    >>= moveToForeignPtrWrapper+    >>= Std.Set.toListFP+    >>= traverse Std.String.copyToByteString+    <&> S.fromList++byteStringList :: IO (Ptr (Std.Vector.CStdVector Std.String.CStdString)) -> IO [ByteString]+byteStringList x =+  x+    >>= moveToForeignPtrWrapper+    >>= Std.Vector.toListFP+    >>= traverse Std.String.copyToByteString++getExtraPlatforms :: IO (Set ByteString)+getExtraPlatforms =+  byteStringSet+    [C.block| std::set<std::string>*{+      return new nix::StringSet(nix::settings.extraPlatforms.get());+    }|]++getSystem :: IO ByteString+getSystem =+  unsafePackMallocCString+    =<< [C.exp| const char *{+      strdup(nix::settings.thisSystem.get().c_str())+    }|]++getSystemFeatures :: IO (Set ByteString)+getSystemFeatures =+  byteStringSet+    [C.block| std::set<std::string>*{+      return new nix::StringSet(nix::settings.systemFeatures.get());+    }|]++getSubstituters :: IO [ByteString]+getSubstituters =+  byteStringList+    [C.block| std::vector<std::string>*{+      auto r = new std::vector<std::string>();+      for (auto i : nix::settings.substituters.get())+        r->push_back(i);+      return r;+    }|]++getTrustedPublicKeys :: IO [ByteString]+getTrustedPublicKeys =+  byteStringList+    [C.block| std::vector<std::string>*{+      auto r = new std::vector<std::string>();+      for (auto i : nix::settings.trustedPublicKeys.get())+        r->push_back(i);+      return r;+    }|]++getNarinfoCacheNegativeTtl :: IO Word64+getNarinfoCacheNegativeTtl =+  [C.exp| uint64_t{+    nix::settings.ttlNegativeNarInfoCache.get()+  }|]++getNetrcFile :: IO ByteString+getNetrcFile =+  unsafePackMallocCString+    =<< [C.exp| const char *{+      strdup(nix::settings.netrcFile.get().c_str())+    }|]
+ src/Hercules/CNix/Std/Set.hs view
@@ -0,0 +1,192 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeApplications #-}+{-# OPTIONS_GHC -fno-warn-unused-matches #-}++-- | @std::set@+module Hercules.CNix.Std.Set+  ( stdSetCtx,+    instanceStdSet,+    instanceStdSetCopyable,+    CStdSet,+    StdSet (StdSet),+    Hercules.CNix.Std.Set.new,+    size,+    toSet,+    fromList,+    fromListP,+    fromListFP,+    Hercules.CNix.Std.Set.toList,+    insert,+    insertP,+    insertFP,+    toListFP,+  )+where++import Control.Exception (mask_)+import Data.Coerce (Coercible, coerce)+import Data.Foldable (for_)+import qualified Data.Set as S+import Data.Traversable (for)+import qualified Data.Vector.Storable as VS+import qualified Data.Vector.Storable.Mutable as VSM+import Foreign+  ( ForeignPtr,+    FunPtr,+    Ptr,+    Storable,+    newForeignPtr,+    withForeignPtr,+  )+import Foreign.C (CSize)+import Hercules.CNix.Encapsulation (HasEncapsulation (..))+import qualified Language.C.Inline as C+import qualified Language.C.Inline.Cpp as C+import qualified Language.C.Inline.Unsafe as CU+import Language.Haskell.TH (DecsQ)+import Language.Haskell.TH.Syntax (Dec, Q)+import Prelude++data CStdSet a++stdSetCtx :: C.Context+stdSetCtx = C.cppCtx `mappend` C.cppTypePairs [("std::set", [t|CStdSet|])]++newtype StdSet a = StdSet (ForeignPtr (CStdSet a))++instance HasStdSet a => HasEncapsulation (CStdSet a) (StdSet a) where+  moveToForeignPtrWrapper = fmap StdSet . newForeignPtr cDelete++class HasStdSet a where+  cNew :: IO (Ptr (CStdSet a))+  cDelete :: FunPtr (Ptr (CStdSet a) -> IO ())+  cSize :: Ptr (CStdSet a) -> IO CSize+  cInsertByPtr :: Ptr a -> Ptr (CStdSet a) -> IO ()+  cCopies :: Ptr (CStdSet a) -> Ptr (Ptr a) -> IO ()++class HasStdSet a => HasStdSetCopyable a where+  cCopyTo :: Ptr (CStdSet a) -> Ptr a -> IO ()+  cInsert :: a -> Ptr (CStdSet a) -> IO ()++-- | Helper for defining templated instances+roll :: String -> Q [Dec] -> Q [Dec]+roll cType d =+  concat+    <$> sequence+      [ C.include "<set>",+        C.include "<algorithm>",+        C.substitute+          [ ("T", const cType),+            ("SET", \var -> "$(std::set<" ++ cType ++ ">* " ++ var ++ ")")+          ]+          d+      ]++instanceStdSet :: String -> DecsQ+instanceStdSet cType =+  roll+    cType+    [d|+      instance HasStdSet $(C.getHaskellType False cType) where+        cNew = [CU.exp| std::set<@T()>* { new std::set<@T()>() } |]+        cDelete = [C.funPtr| void deleteStdSet(std::set<@T()>* set) { delete set; } |]+        cSize set = [CU.exp| size_t { @SET(set)->size() } |]+        cInsertByPtr ptr set = [CU.exp| void { @SET(set)->insert(*$(@T() *ptr)) } |]+        cCopies set dstPtr =+          [CU.block| void {+            const std::set<@T()>& set = *@SET(set);+            @T()** aim = $(@T()** dstPtr);+            for (auto item : set) {+              *aim = new @T()(item);+              aim++;+            }+          }|]+      |]++instanceStdSetCopyable :: String -> DecsQ+instanceStdSetCopyable cType =+  roll+    cType+    [d|+      instance HasStdSetCopyable $(C.getHaskellType False cType) where+        cCopyTo set dstPtr =+          [CU.block| void {+            const std::set<@T()>* set = @SET(set);+            std::copy(set->begin(), set->end(), $(@T()* dstPtr));+            } |]+        cInsert value set =+          [CU.exp| void { @SET(set)->insert($(@T() value)) }+        |]+      |]++new :: forall a. HasStdSet a => IO (StdSet a)+new = mask_ $ do+  moveToForeignPtrWrapper =<< cNew @a++size :: HasStdSet a => StdSet a -> IO Int+size (StdSet fptr) = fromIntegral <$> withForeignPtr fptr cSize++fromList :: HasStdSetCopyable a => [a] -> IO (StdSet a)+fromList as = do+  set <- new+  for_ as $ insert set+  pure set++fromListP :: HasStdSet a => [Ptr a] -> IO (StdSet a)+fromListP as = do+  set <- new+  for_ as $ insertP set+  pure set++fromListFP :: (Coercible a' (ForeignPtr a), HasStdSet a) => [a'] -> IO (StdSet a)+fromListFP as = do+  set <- new+  for_ as $ insertFP set+  pure set++toSet :: (HasStdSetCopyable a, Storable a, Ord a) => StdSet a -> IO (S.Set a)+toSet stdSet = do+  S.fromList . VS.toList <$> toVector stdSet++toVector :: (HasStdSetCopyable a, Storable a) => StdSet a -> IO (VS.Vector a)+toVector stdSet@(StdSet stdSetFPtr) = do+  vecSize <- size stdSet+  hsVec <- VSM.new vecSize+  withForeignPtr stdSetFPtr $ \stdSetPtr ->+    VSM.unsafeWith hsVec $ \hsVecPtr ->+      cCopyTo stdSetPtr hsVecPtr+  VS.unsafeFreeze hsVec++toList :: (HasStdSetCopyable a, Storable a) => StdSet a -> IO [a]+toList vec = VS.toList <$> toVector vec++toVectorP :: (HasStdSet a) => StdSet a -> IO (VS.Vector (Ptr a))+toVectorP stdSet@(StdSet stdSetFPtr) = do+  vecSize <- size stdSet+  hsVec <- VSM.new vecSize+  withForeignPtr stdSetFPtr $ \stdSetPtr ->+    VSM.unsafeWith hsVec $ \hsVecPtr ->+      cCopies stdSetPtr hsVecPtr+  VS.unsafeFreeze hsVec++toListP :: (HasStdSet a) => StdSet a -> IO [Ptr a]+toListP vec = VS.toList <$> toVectorP vec++toListFP :: (HasStdSet a, HasEncapsulation a b) => StdSet a -> IO [b]+toListFP vec = mask_ $ do+  ptrs <- toListP vec+  for ptrs moveToForeignPtrWrapper++insert :: HasStdSetCopyable a => StdSet a -> a -> IO ()+insert (StdSet fptr) value = withForeignPtr fptr (cInsert value)++insertP :: HasStdSet a => StdSet a -> Ptr a -> IO ()+insertP (StdSet fptr) ptr = withForeignPtr fptr (cInsertByPtr ptr)++insertFP :: (Coercible a' (ForeignPtr a), HasStdSet a) => StdSet a -> a' -> IO ()+insertFP (StdSet fptr) vfptr =+  withForeignPtr fptr $ \setPtr ->+    withForeignPtr (coerce vfptr) (\valPtr -> cInsertByPtr valPtr setPtr)
+ src/Hercules/CNix/Std/String.hs view
@@ -0,0 +1,93 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeApplications #-}++module Hercules.CNix.Std.String+  ( -- * Context+    CStdString,+    stdStringCtx,++    -- * Functions+    moveToByteString,+    withString,++    -- * Low level functions+    new,+    delete,++    -- * Wrapper-based functions+    copyToByteString,+  )+where++import Control.Exception (bracket, mask_)+import Data.ByteString (ByteString)+import Data.ByteString.Unsafe (unsafePackMallocCStringLen)+import Foreign hiding (new)+import Hercules.CNix.Encapsulation+import Hercules.CNix.Std.String.Context+import Hercules.CNix.Std.String.Instances ()+import qualified Language.C.Inline as C+import System.IO.Unsafe (unsafePerformIO)+import Prelude++C.context (stdStringCtx <> C.bsCtx <> C.fptrCtx)++C.include "<string>"+C.include "<cstring>"++moveToByteString :: Ptr CStdString -> IO ByteString+moveToByteString s = mask_ $ alloca \ptr -> alloca \sz -> do+  [C.block| void {+    const std::string &s = *$(std::string *s);+    size_t sz = *$(size_t *sz) = s.size();+    char *ptr = *$(char **ptr) = (char*)malloc(sz);+    std::memcpy((void *)ptr, s.c_str(), sz);+  }|]+  sz' <- peek sz+  ptr' <- peek ptr+  unsafePackMallocCStringLen (ptr', fromIntegral sz')++new :: ByteString -> IO (Ptr CStdString)+new bs =+  [C.block| std::string* {+    return new std::string($bs-ptr:bs, $bs-len:bs);+  }|]++delete :: Ptr CStdString -> IO ()+delete bs = [C.block| void { delete $(std::string *bs); }|]++withString :: ByteString -> (Ptr CStdString -> IO a) -> IO a+withString bs = bracket (new bs) delete++finalize :: FinalizerPtr CStdString+{-# NOINLINE finalize #-}+finalize =+  unsafePerformIO+    [C.exp|+      void (*)(std::string *) {+        [](std::string *v) {+          delete v;+        }+      }+    |]++newtype StdString = StdString (ForeignPtr CStdString)++instance HasEncapsulation CStdString StdString where+  moveToForeignPtrWrapper x = StdString <$> newForeignPtr finalize x++copyToByteString :: StdString -> IO ByteString+copyToByteString (StdString s) = mask_ $ alloca \ptr -> alloca \sz -> do+  [C.block| void {+    const std::string &s = *$fptr-ptr:(std::string *s);+    size_t sz = *$(size_t *sz) = s.size();+    char *ptr = *$(char **ptr) = (char*)malloc(sz);+    std::memcpy((void *)ptr, s.c_str(), sz);+  }|]+  sz' <- peek sz+  ptr' <- peek ptr+  unsafePackMallocCStringLen (ptr', fromIntegral sz')
+ src/Hercules/CNix/Std/String/Context.hs view
@@ -0,0 +1,21 @@+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE TemplateHaskell #-}++module Hercules.CNix.Std.String.Context where++import qualified Data.Map as M+import qualified Language.C.Inline as C+import qualified Language.C.Inline.Context as C+import qualified Language.C.Inline.Cpp as C+import qualified Language.C.Types as C+import Prelude++data CStdString++stdStringCtx :: C.Context+stdStringCtx =+  C.cppCtx+    <> mempty+      { C.ctxTypesTable =+          M.singleton (C.TypeName "std::string") [t|CStdString|]+      }
+ src/Hercules/CNix/Std/String/Instances.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TemplateHaskell #-}+{-# OPTIONS_GHC -Wno-orphans #-}++-- | Define instances for C++ types in the Context module that can't be in that+-- module because of TH staging restrictions.+module Hercules.CNix.Std.String.Instances where++import Data.Semigroup (Semigroup ((<>)))+import Hercules.CNix.Std.Set+import Hercules.CNix.Std.String.Context+import Hercules.CNix.Std.Vector+import qualified Language.C.Inline.Cpp as C++C.context (stdVectorCtx <> stdSetCtx <> stdStringCtx)+C.include "<string>"++instanceStdVector "std::string"+instanceStdSet "std::string"
+ src/Hercules/CNix/Std/Vector.hs view
@@ -0,0 +1,171 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE TypeApplications #-}+{-# OPTIONS_GHC -fno-warn-unused-matches #-}++-- | @std::vector@+--+-- Original author @chpatrick https://github.com/fpco/inline-c/blob/1ba35141e330981fef0457a1619701b8acc32f0b/inline-c-cpp/test/StdVector.hs+module Hercules.CNix.Std.Vector+  ( stdVectorCtx,+    instanceStdVector,+    instanceStdVectorCopyable,+    CStdVector,+    StdVector (StdVector),+    Hercules.CNix.Std.Vector.new,+    size,+    toVector,+    toVectorP,+    toListP,+    toListFP,+    Hercules.CNix.Std.Vector.toList,+    Hercules.CNix.Std.Vector.fromList,+    fromListFP,+    pushBack,+    pushBackP,+    pushBackFP,+  )+where++import Control.Exception (mask_)+import Data.Coerce (Coercible, coerce)+import Data.Foldable+import qualified Data.Vector.Storable as VS+import qualified Data.Vector.Storable.Mutable as VSM+import Foreign+import Foreign.C+import Hercules.CNix.Encapsulation+import qualified Language.C.Inline as C+import qualified Language.C.Inline.Cpp as C+import qualified Language.C.Inline.Unsafe as CU+import Language.Haskell.TH+import Prelude++data CStdVector a++stdVectorCtx :: C.Context+stdVectorCtx = C.cppCtx `mappend` C.cppTypePairs [("std::vector", [t|CStdVector|])]++newtype StdVector a = StdVector (ForeignPtr (CStdVector a))++instance HasStdVector a => HasEncapsulation (CStdVector a) (StdVector a) where+  moveToForeignPtrWrapper x = StdVector <$> newForeignPtr cDelete x++class HasStdVector a where+  cNew :: IO (Ptr (CStdVector a))+  cDelete :: FunPtr (Ptr (CStdVector a) -> IO ())+  cSize :: Ptr (CStdVector a) -> IO CSize+  cCopies :: Ptr (CStdVector a) -> Ptr (Ptr a) -> IO ()+  cPushBackByPtr :: Ptr a -> Ptr (CStdVector a) -> IO ()++class HasStdVector a => HasStdVectorCopyable a where+  cCopyTo :: Ptr (CStdVector a) -> Ptr a -> IO ()+  cPushBack :: a -> Ptr (CStdVector a) -> IO ()++-- | Helper for defining templated instances+roll :: String -> Q [Dec] -> Q [Dec]+roll cType d =+  concat+    <$> sequence+      [ C.include "<vector>",+        C.include "<algorithm>",+        C.substitute+          [ ("T", const cType),+            ("VEC", \var -> "$(std::vector<" ++ cType ++ ">* " ++ var ++ ")")+          ]+          d+      ]++instanceStdVector :: String -> DecsQ+instanceStdVector cType =+  roll+    cType+    [d|+      instance HasStdVector $(C.getHaskellType False cType) where+        cNew = [CU.exp| std::vector<@T()>* { new std::vector<@T()>() } |]+        cDelete = [C.funPtr| void deleteStdVector(std::vector<@T()>* vec) { delete vec; } |]+        cSize vec = [CU.exp| size_t { @VEC(vec)->size() } |]++        cCopies vec dstPtr =+          [CU.block| void {+          const std::vector<@T()>& vec = *@VEC(vec);+          @T()** aim = $(@T()** dstPtr);+          for (auto item : vec) {+            *aim = new @T()(item);+            aim++;+          }+        }|]+        cPushBackByPtr ptr vec = [CU.exp| void { @VEC(vec)->push_back(*$(@T() *ptr)) } |]+      |]++instanceStdVectorCopyable :: String -> DecsQ+instanceStdVectorCopyable cType =+  roll+    cType+    [d|+      instance HasStdVectorCopyable $(C.getHaskellType False cType) where+        cCopyTo vec dstPtr =+          [CU.block| void {+          const std::vector<@T()>* vec = @VEC(vec);+          std::copy(vec->begin(), vec->end(), $(@T()* dstPtr));+          } |]+        cPushBack value vec = [CU.exp| void { @VEC(vec)->push_back($(@T() value)) } |]+      |]++new :: forall a. HasStdVector a => IO (StdVector a)+new = mask_ $ do+  ptr <- cNew @a+  StdVector <$> newForeignPtr cDelete ptr++size :: HasStdVector a => StdVector a -> IO Int+size (StdVector fptr) = fromIntegral <$> withForeignPtr fptr cSize++toVector :: (HasStdVectorCopyable a, Storable a) => StdVector a -> IO (VS.Vector a)+toVector stdVec@(StdVector stdVecFPtr) = do+  vecSize <- size stdVec+  hsVec <- VSM.new vecSize+  withForeignPtr stdVecFPtr $ \stdVecPtr ->+    VSM.unsafeWith hsVec $ \hsVecPtr ->+      cCopyTo stdVecPtr hsVecPtr+  VS.unsafeFreeze hsVec++toVectorP :: HasStdVector a => StdVector a -> IO (VS.Vector (Ptr a))+toVectorP stdVec@(StdVector stdVecFPtr) = do+  vecSize <- size stdVec+  hsVec <- VSM.new vecSize+  withForeignPtr stdVecFPtr $ \stdVecPtr ->+    VSM.unsafeWith hsVec $ \hsVecPtr ->+      cCopies stdVecPtr hsVecPtr+  VS.unsafeFreeze hsVec++fromList :: HasStdVectorCopyable a => [a] -> IO (StdVector a)+fromList as = do+  vec <- Hercules.CNix.Std.Vector.new+  for_ as $ \a -> pushBack vec a+  pure vec++fromListFP :: (Coercible a' (ForeignPtr a), HasStdVector a) => [a'] -> IO (StdVector a)+fromListFP as = do+  vec <- Hercules.CNix.Std.Vector.new+  for_ as $ \a -> pushBackFP vec a+  pure vec++toList :: (HasStdVectorCopyable a, Storable a) => StdVector a -> IO [a]+toList vec = VS.toList <$> toVector vec++toListP :: (HasStdVector a) => StdVector a -> IO [Ptr a]+toListP vec = VS.toList <$> toVectorP vec++toListFP :: (HasEncapsulation a b, HasStdVector a) => StdVector a -> IO [b]+toListFP vec = traverse moveToForeignPtrWrapper =<< toListP vec++pushBack :: HasStdVectorCopyable a => StdVector a -> a -> IO ()+pushBack (StdVector fptr) value = withForeignPtr fptr (cPushBack value)++pushBackP :: HasStdVector a => StdVector a -> Ptr a -> IO ()+pushBackP (StdVector fptr) valueP = withForeignPtr fptr (cPushBackByPtr valueP)++pushBackFP :: (Coercible a' (ForeignPtr a), HasStdVector a) => StdVector a -> a' -> IO ()+pushBackFP vec vfptr = withForeignPtr (coerce vfptr) (pushBackP vec)
src/Hercules/CNix/Store.hs view
@@ -1,4 +1,6 @@ {-# LANGUAGE BlockArguments #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-}@@ -11,17 +13,28 @@  import Control.Exception import Control.Monad.IO.Unlift+import Data.ByteString.Short (ShortByteString)+import qualified Data.ByteString.Short as SBS import Data.ByteString.Unsafe (unsafePackMallocCString) import qualified Data.ByteString.Unsafe as BS import Data.Coerce (coerce) import qualified Data.Map as M+import Foreign (alloca, free, nullPtr) import Foreign.ForeignPtr+import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)+import Foreign.Storable (peek)+import Hercules.CNix.Encapsulation (HasEncapsulation (..))+import Hercules.CNix.Std.Set (StdSet, stdSetCtx)+import qualified Hercules.CNix.Std.Set as Std.Set+import Hercules.CNix.Std.String (stdStringCtx)+import qualified Hercules.CNix.Std.String as Std.String+import Hercules.CNix.Std.Vector+import qualified Hercules.CNix.Std.Vector as Std.Vector import Hercules.CNix.Store.Context-  ( Derivation,-    DerivationInputsIterator,+  ( DerivationInputsIterator,     DerivationOutputsIterator,     NixStore,-    PathSetIterator,+    NixStorePath,     Ref,     SecretKey,     StringPairs,@@ -31,13 +44,14 @@     unsafeMallocBS,   ) import qualified Hercules.CNix.Store.Context as C hiding (context)+import Hercules.CNix.Store.Instances () import qualified Language.C.Inline.Cpp as C import qualified Language.C.Inline.Cpp.Exceptions as C import Protolude import System.IO.Unsafe (unsafePerformIO)-import Prelude ()+import qualified Prelude -C.context context+C.context (context <> stdVectorCtx <> stdSetCtx <> stdStringCtx)  C.include "<cstring>" @@ -55,18 +69,38 @@  C.include "<nix/globals.hh>" +C.include "<nix/path.hh>"++C.include "<variant>"+ C.include "<nix/worker-protocol.hh>" +#ifdef NIX_2_4+C.include "<nix/path-with-outputs.hh>"+#endif+ C.include "hercules-ci-cnix/store.hxx" +C.include "nix-compat.hh"+ C.using "namespace nix" +#ifndef NIX_2_4+C.using "namespace compat::nix"+#endif++forNonNull :: Applicative m => Ptr a -> (Ptr a -> m b) -> m (Maybe b)+forNonNull = flip traverseNonNull++traverseNonNull :: Applicative m => (Ptr a -> m b) -> Ptr a -> m (Maybe b)+traverseNonNull f p = if p == nullPtr then pure Nothing else Just <$> f p+ newtype Store = Store (Ptr (Ref NixStore))  openStore :: IO Store openStore =   coerce-    [C.throwBlock| refStore* {+    [C.throwBlock| refStore * {       refStore s = openStore();       return new refStore(s);     } |]@@ -110,6 +144,15 @@        return strdup(uri.c_str());      } |] +-- | Usually @"/nix/store"@+storeDir :: MonadIO m => Store -> m ByteString+storeDir (Store store) =+  unsafeMallocBS+    [C.block| const char* {+       std::string uri = (*$(refStore* store))->storeDir;+       return strdup(uri.c_str());+     } |]+ getStoreProtocolVersion :: Store -> IO Int getStoreProtocolVersion (Store store) =   fromIntegral@@ -125,10 +168,128 @@        return PROTOCOL_VERSION;      } |] -ensurePath :: Store -> ByteString -> IO ()-ensurePath (Store store) path =+-- | Store-agnostic store path representation: hash and name. Does not have a storedir or subpath inside the store path.+newtype StorePath = StorePath (ForeignPtr NixStorePath)++instance HasEncapsulation NixStorePath StorePath where+  moveToForeignPtrWrapper = moveStorePath++finalizeStorePath :: FinalizerPtr NixStorePath+{-# NOINLINE finalizeStorePath #-}+finalizeStorePath =+  unsafePerformIO+    [C.exp|+      void (*)(nix::StorePath *) {+        [](StorePath *v) {+          delete v;+        }+      }+    |]++-- | Move ownership of a Ptr NixStorePath into 'StorePath'+moveStorePath :: Ptr NixStorePath -> IO StorePath+moveStorePath x = StorePath <$> newForeignPtr finalizeStorePath x++-- | Move ownership of a Ptr NixStorePath into 'StorePath'+moveStorePathMaybe :: Ptr NixStorePath -> IO (Maybe StorePath)+moveStorePathMaybe = traverseNonNull $ fmap StorePath . newForeignPtr finalizeStorePath++instance Prelude.Show StorePath where+  show storePath = unsafePerformIO do+    bs <-+      BS.unsafePackMallocCString+        =<< [C.block| const char* {+          std::string s($fptr-ptr:(nix::StorePath *storePath)->to_string());+          return strdup(s.c_str());+        }|]+    pure $ toS $ decodeUtf8With lenientDecode bs++instance Eq StorePath where+  a == b = compare a b == EQ++-- FIXME+instance Ord StorePath where+  compare (StorePath a) (StorePath b) =+    compare+      0+      [C.pure| int {+        $fptr-ptr:(nix::StorePath *a)->to_string().compare($fptr-ptr:(nix::StorePath *b)->to_string())+      }|]++-- | Create 'StorePath' from hash and name.+--+-- Throws C++ `BadStorePath` exception when invalid.+parseStorePathBaseName :: ByteString -> IO StorePath+parseStorePathBaseName bs =+  moveStorePath+    =<< [C.throwBlock| nix::StorePath *{+      return new StorePath(std::string($bs-ptr:bs, $bs-len:bs));+    }|]++-- | Parse a complete store path including storeDir into a 'StorePath'.+--+-- Throws C++ `BadStorePath` exception when invalid.+parseStorePath :: Store -> ByteString -> IO StorePath+#ifdef NIX_2_4+parseStorePath (Store store) bs =+  moveStorePath+    =<< [C.throwBlock| nix::StorePath *{+      return new StorePath(std::move((*$(refStore* store))->parseStorePath(std::string($bs-ptr:bs, $bs-len:bs))));+    }|]+#else+parseStorePath (Store store) bs =+  moveStorePath+    =<< [C.throwBlock| nix::StorePath *{+      auto p = canonPath(std::string($bs-ptr:bs, $bs-len:bs));+      if (dirOf(p) != (*$(refStore* store))->storeDir)+        throw Error("path '%s' is not in the Nix store", p);+      return new StorePath(baseNameOf(p));+    }|]+#endif++getStorePathBaseName :: StorePath -> IO ByteString+getStorePathBaseName (StorePath sp) = do+  BS.unsafePackMallocCString+    =<< [C.block| const char *{+      std::string s($fptr-ptr:(nix::StorePath *sp)->to_string());+      return strdup(s.c_str());+    }|]++getStorePathHash :: StorePath -> IO ByteString+getStorePathHash (StorePath sp) = do+  BS.unsafePackMallocCString+    =<< [C.block| const char *{+      std::string s($fptr-ptr:(nix::StorePath *sp)->hashPart());+      return strdup(s.c_str());+    }|]++storePathToPath :: Store -> StorePath -> IO ByteString+#ifdef NIX_2_4+storePathToPath (Store store) (StorePath sp) =+  BS.unsafePackMallocCString+    =<< [C.block| const char *{+      Store & store = **$(refStore* store);+      StorePath &sp = *$fptr-ptr:(nix::StorePath *sp);+      std::string s(store.printStorePath(sp));+      return strdup(s.c_str());+    }|]+#else+storePathToPath (Store store) (StorePath sp) =+  BS.unsafePackMallocCString+    =<< [C.block| const char *{+      Store & store = **$(refStore* store);+      StorePath &sp = *$fptr-ptr:(nix::StorePath *sp);+      std::string s(printPath23(store, sp));+      return strdup(s.c_str());+    }|]+#endif++ensurePath :: Store -> StorePath -> IO ()+ensurePath (Store store) (StorePath storePath) =   [C.throwBlock| void {-    (*$(refStore* store))->ensurePath(std::string($bs-ptr:path, $bs-len:path));+    Store &store = **$(refStore* store);+    StorePath &storePath = *$fptr-ptr:(nix::StorePath *storePath);+    store.ensurePath(printPath23(store, storePath));   } |]  clearPathInfoCache :: Store -> IO ()@@ -146,46 +307,67 @@     }   } |] -buildPaths :: Store -> [ByteString] -> IO ()-buildPaths (Store store) paths = do-  withStringsOf paths $ \pathStrings -> do-    [C.throwBlock| void {-      StringSet pathSet;-      for (auto path : *$(Strings *pathStrings)) {-        pathSet.insert(path);+newtype StorePathWithOutputs = StorePathWithOutputs (ForeignPtr C.NixStorePathWithOutputs)++instance HasEncapsulation C.NixStorePathWithOutputs StorePathWithOutputs where+  moveToForeignPtrWrapper x = StorePathWithOutputs <$> newForeignPtr finalizeStorePathWithOutputs x++finalizeStorePathWithOutputs :: FinalizerPtr C.NixStorePathWithOutputs+{-# NOINLINE finalizeStorePathWithOutputs #-}+finalizeStorePathWithOutputs =+  unsafePerformIO+    [C.exp|+      void (*)(nix::StorePathWithOutputs *) {+        [](StorePathWithOutputs *v) {+          delete v;+        }       }-      (*$(refStore* store))->buildPaths(pathSet);+    |]++newStorePathWithOutputs :: StorePath -> [ByteString] -> IO StorePathWithOutputs+newStorePathWithOutputs storePath outputs = do+  set <- Std.Set.new+  for_ outputs (\o -> Std.String.withString o (Std.Set.insertP set))+  moveToForeignPtrWrapper+    =<< [C.exp| nix::StorePathWithOutputs * {+    new StorePathWithOutputs {*$fptr-ptr:(nix::StorePath *storePath), *$fptr-ptr:(std::set<std::string>* set)}+  }|]++getStorePath :: StorePathWithOutputs -> IO StorePath+getStorePath swo = mask_ do+  moveToForeignPtrWrapper+    =<< [C.exp| nix::StorePath * {+    new StorePath($fptr-ptr:(nix::StorePathWithOutputs *swo)->path)+  }|]++getOutputs :: StorePathWithOutputs -> IO [ByteString]+getOutputs swo = mask_ do+  traverse Std.String.moveToByteString =<< toListP =<< moveToForeignPtrWrapper+    =<< [C.throwBlock| std::vector<std::string>* {+      auto r = new std::vector<std::string>();+      for (auto s : $fptr-ptr:(nix::StorePathWithOutputs *swo)->outputs)+        r->push_back(s);+      return r;     }|] -buildPath :: Store -> ByteString -> IO ()-buildPath (Store store) path =+buildPaths :: Store -> StdVector C.NixStorePathWithOutputs -> IO ()+buildPaths (Store store) (StdVector paths) = do   [C.throwBlock| void {-    PathSet ps({std::string($bs-ptr:path, $bs-len:path)});-    (*$(refStore* store))->buildPaths(ps);-   } |]+    Store &store = **$(refStore* store);+    std::vector<StorePathWithOutputs> &paths = *$fptr-ptr:(std::vector<nix::StorePathWithOutputs>* paths);+    store.buildPaths(toDerivedPaths24(printPathSet23(store, paths)));+  }|] -getDerivation :: Store -> ByteString -> IO (ForeignPtr Derivation)-getDerivation (Store store) path = do-  ptr <--    [C.throwBlock| Derivation *{-      return new Derivation(-          (*$(refStore* store))->derivationFromPath(std::string($bs-ptr:path, $bs-len:path))-        );-    } |]-  newForeignPtr finalizeDerivation ptr+buildPath :: Store -> StorePathWithOutputs -> IO ()+buildPath store spwo = do+  buildPaths store =<< Std.Vector.fromListFP [spwo] --- Useful for testingg-getDerivationFromFile :: ByteString -> IO (ForeignPtr Derivation)-getDerivationFromFile path = do-  ptr <--    [C.throwBlock| Derivation *{-      return new Derivation(-          readDerivation(std::string($bs-ptr:path, $bs-len:path))-        );-    } |]-  newForeignPtr finalizeDerivation ptr+newtype Derivation = Derivation (ForeignPtr C.Derivation) -finalizeDerivation :: FinalizerPtr Derivation+instance HasEncapsulation C.Derivation Derivation where+  moveToForeignPtrWrapper = fmap Derivation . newForeignPtr finalizeDerivation++finalizeDerivation :: FinalizerPtr C.Derivation {-# NOINLINE finalizeDerivation #-} finalizeDerivation =   unsafePerformIO@@ -196,27 +378,95 @@       }     } |] --- | Throws when missing-getDerivationOutputPath :: ForeignPtr Derivation -> ByteString -> IO ByteString-getDerivationOutputPath fd outputName = withForeignPtr fd $ \d ->-  [C.throwBlock|-    const char *{-      std::string outputName($bs-ptr:outputName, $bs-len:outputName);-      Derivation *d = $(Derivation *d);-      return strdup(d->outputs.at(outputName).path.c_str());-    }-  |]-    >>= BS.unsafePackMallocCString+getDerivation :: Store -> StorePath -> IO Derivation+getDerivation (Store store) (StorePath spwo) = do+  moveToForeignPtrWrapper+    =<< [C.throwBlock| Derivation *{+      Store &store = **$(refStore* store);+      return new Derivation(+          store.derivationFromPath(printPath23(store, *$fptr-ptr:(nix::StorePath *spwo)))+        );+    } |] +-- Useful for testing+getDerivationFromString ::+  Store ->+  -- | Derivation name (store path name with ".drv" extension removed)+  ByteString ->+  -- | Contents+  ByteString ->+  IO Derivation+#ifdef NIX_2_4+getDerivationFromString (Store store) name contents = do+  moveToForeignPtrWrapper+    =<< [C.throwBlock| Derivation *{+      Store &store = **$(refStore* store);+      std::string name($bs-ptr:name, $bs-len:name);+      return new Derivation(parseDerivation(store, std::string($bs-ptr:contents, $bs-len:contents), name));+    }|]+#else+getDerivationFromString _store name contents = do+  moveToForeignPtrWrapper =<< [C.throwBlock| Derivation *{+      std::string name($bs-ptr:name, $bs-len:name);+      std::string contents($bs-ptr:contents, $bs-len:contents);+      AutoDelete tmpDir(createTempDir(), true);+      Path tmpFile = (Path) tmpDir + "/" + name;+      writeFile(tmpFile, contents, 0600);+      return new Derivation(readDerivation(tmpFile));+    }|]+#endif++getDerivationNameFromPath :: StorePath -> IO ByteString+getDerivationNameFromPath storePath =+  BS.unsafePackMallocCString++#ifdef NIX_2_4+    =<< [C.throwBlock| const char *{+      StorePath &sp = *$fptr-ptr:(nix::StorePath *storePath);+      std::string s(Derivation::nameFromPath(sp));+      return strdup(s.c_str());+    }|]+#else+    =<< [C.throwBlock| const char *{+      StorePath &sp = *$fptr-ptr:(nix::StorePath *storePath);+      std::string pathName(sp.name());+      assert(isDerivation(pathName));+      std::string drvName = std::string(pathName, 0, pathName.size() - drvExtension.size());+      return strdup(drvName.c_str());+    }|]+#endif+ data DerivationOutput = DerivationOutput   { derivationOutputName :: !ByteString,-    derivationOutputPath :: !ByteString,-    derivationOutputHashAlgo :: !ByteString,-    derivationOutputHash :: !ByteString+    derivationOutputPath :: !(Maybe StorePath),+    derivationOutputDetail :: !DerivationOutputDetail   }+  deriving (Eq, Show) -getDerivationOutputs :: ForeignPtr Derivation -> IO [DerivationOutput]-getDerivationOutputs derivation =+data DerivationOutputDetail+  = DerivationOutputInputAddressed StorePath+  | DerivationOutputCAFixed FixedOutputHash StorePath+  | DerivationOutputCAFloating FileIngestionMethod HashType+  | DerivationOutputDeferred+  deriving (Eq, Show)++data FixedOutputHash = FixedOutputHash !FileIngestionMethod {-# UNPACK #-} !Hash+  deriving (Eq, Show)++-- | See @content-address.hh@+data FileIngestionMethod = Flat | Recursive+  deriving (Eq, Show)++-- | See @hash.hh@+data Hash = Hash !HashType {-# UNPACK #-} !ShortByteString+  deriving (Eq, Show)++-- | See @hash.hh@+data HashType = MD5 | SHA1 | SHA256 | SHA512+  deriving (Eq, Show)++getDerivationOutputs :: Store -> ByteString -> Derivation -> IO [DerivationOutput]+getDerivationOutputs (Store store) drvName (Derivation derivation) =   bracket     [C.exp| DerivationOutputsIterator* {       new DerivationOutputsIterator($fptr-ptr:(Derivation *derivation)->outputs.begin())@@ -226,32 +476,168 @@       isEnd <- (0 /=) <$> [C.exp| bool { *$(DerivationOutputsIterator *i) == $fptr-ptr:(Derivation *derivation)->outputs.end() }|]       if isEnd         then pure []-        else do-          name <- [C.exp| const char*{ strdup((*$(DerivationOutputsIterator *i))->first.c_str()) }|] >>= BS.unsafePackMallocCString-          path <- [C.exp| const char*{ strdup((*$(DerivationOutputsIterator *i))->second.path.c_str()) }|] >>= BS.unsafePackMallocCString-          hash_ <- [C.exp| const char*{ strdup((*$(DerivationOutputsIterator *i))->second.hash.c_str()) }|] >>= BS.unsafePackMallocCString-          hashAlgo <- [C.exp| const char*{ strdup((*$(DerivationOutputsIterator *i))->second.hashAlgo.c_str()) }|] >>= BS.unsafePackMallocCString-          [C.block| void { (*$(DerivationOutputsIterator *i))++; }|]-          (DerivationOutput name path hashAlgo hash_ :) <$> continue+        else+          ( mask_ do+              alloca \nameP -> alloca \pathP -> alloca \typP -> alloca \fimP ->+                alloca \hashTypeP -> alloca \hashValueP -> alloca \hashSizeP -> do+                  [C.throwBlock| void {+                    Store &store = **$(refStore *store);+                    std::string drvName = std::string($bs-ptr:drvName, $bs-len:drvName);+                    nix::DerivationOutputs::iterator &i = *$(DerivationOutputsIterator *i);+                    const char *&name = *$(const char **nameP);+                    int &typ = *$(int *typP);+                    StorePath *& path = *$(nix::StorePath **pathP);+                    int &fim = *$(int *fimP);+                    int &hashType = *$(int *hashTypeP);+                    char *&hashValue = *$(char **hashValueP);+                    int &hashSize = *$(int *hashSizeP); +                    std::string nameString = i->first;+                    name = strdup(nameString.c_str());+                    path = nullptr;+                    std::visit(overloaded {+                      [&](DerivationOutputInputAddressed doi) -> void {+                        typ = 0;+                        path = new StorePath(doi.path);+                      },+                      [&](DerivationOutputCAFixed dof) -> void {+                        typ = 1;+#ifdef NIX_2_4+                        path = new StorePath(dof.path(store, $fptr-ptr:(Derivation *derivation)->name, nameString));+#else+                        path = new StorePath(dof.path(store, dof.drvName, nameString));+#endif+                        switch (dof.hash.method) {+                          case nix::FileIngestionMethod::Flat:+                            fim = 0;+                            break;+                          case nix::FileIngestionMethod::Recursive:+                            fim = 1;+                            break;+                          default:+                            fim = -1;+                            break;+                        }+                        switch (dof.hash.hash.type) {+                          case htMD5: +                            hashType = 0;+                            break;+                          case htSHA1: +                            hashType = 1;+                            break;+                          case htSHA256: +                            hashType = 2;+                            break;+                          case htSHA512: +                            hashType = 3;+                            break;+                          default:+                            hashType = -1;+                            break;+                        }+                        hashSize = dof.hash.hash.hashSize;+                        hashValue = (char*)malloc(hashSize);+                        std::memcpy((void*)(hashValue),+                                    (void*)(dof.hash.hash.hash),+                                    hashSize);+                      },+                      [&](DerivationOutputCAFloating dof) -> void {+                        typ = 2;+                        switch (dof.method) {+                          case nix::FileIngestionMethod::Flat:+                            fim = 0;+                            break;+                          case nix::FileIngestionMethod::Recursive:+                            fim = 1;+                            break;+                          default:+                            fim = -1;+                            break;+                        }+                        switch (dof.hashType) {+                          case htMD5: +                            hashType = 0;+                            break;+                          case htSHA1: +                            hashType = 1;+                            break;+                          case htSHA256: +                            hashType = 2;+                            break;+                          case htSHA512: +                            hashType = 3;+                            break;+                          default:+                            hashType = -1;+                            break;+                        }+                      },+                      [&](DerivationOutputDeferred) -> void {+                        typ = 3;+                      },+#ifdef NIX_2_4+                    }, i->second.output);+#else+                    }, compatDerivationOutput(store, drvName, i->second).output);+#endif+                    i++;+                  }|]+                  name <- unsafePackMallocCString =<< peek nameP+                  path <- moveStorePathMaybe =<< peek pathP+                  typ <- peek typP+                  let getFileIngestionMethod = peek fimP <&> \case 0 -> Flat; 1 -> Recursive; _ -> panic "getDerivationOutputs: unknown fim"+                      getHashType =+                        peek hashTypeP <&> \case+                          0 -> MD5+                          1 -> SHA1+                          2 -> SHA256+                          3 -> SHA512+                          _ -> panic "getDerivationOutputs: unknown hashType"+                  detail <- case typ of+                    0 -> pure $ DerivationOutputInputAddressed (fromMaybe (panic "getDerivationOutputs: impossible DOIA path missing") path)+                    1 -> do+                      hashValue <- peek hashValueP+                      hashSize <- peek hashSizeP+                      hashString <- SBS.packCStringLen (hashValue, fromIntegral hashSize)+                      free hashValue+                      hashType <- getHashType+                      fim <- getFileIngestionMethod+                      pure $ DerivationOutputCAFixed (FixedOutputHash fim (Hash hashType hashString)) (fromMaybe (panic "getDerivationOutputs: impossible DOCF path missing") path)+                    2 -> do+                      hashType <- getHashType+                      fim <- getFileIngestionMethod+                      pure $ DerivationOutputCAFloating fim hashType+                    3 -> pure DerivationOutputDeferred+                    _ -> panic "getDerivationOutputs: impossible getDerivationOutputs typ"+                  pure+                    ( DerivationOutput+                        { derivationOutputName = name,+                          derivationOutputPath = path,+                          derivationOutputDetail = detail+                        }+                        :+                    )+          )+            <*> continue+ deleteDerivationOutputsIterator :: Ptr DerivationOutputsIterator -> IO () deleteDerivationOutputsIterator a = [C.block| void { delete $(DerivationOutputsIterator *a); }|] -getDerivationPlatform :: ForeignPtr Derivation -> IO ByteString+getDerivationPlatform :: Derivation -> IO ByteString getDerivationPlatform derivation =   unsafeMallocBS     [C.exp| const char* {        strdup($fptr-ptr:(Derivation *derivation)->platform.c_str())      } |] -getDerivationBuilder :: ForeignPtr Derivation -> IO ByteString+getDerivationBuilder :: Derivation -> IO ByteString getDerivationBuilder derivation =   unsafeMallocBS     [C.exp| const char* {        strdup($fptr-ptr:(Derivation *derivation)->builder.c_str())      } |] -getDerivationArguments :: ForeignPtr Derivation -> IO [ByteString]+getDerivationArguments :: Derivation -> IO [ByteString] getDerivationArguments derivation =   bracket     [C.throwBlock| Strings* {@@ -264,21 +650,21 @@     deleteStrings     toByteStrings -getDerivationSources :: ForeignPtr Derivation -> IO [ByteString]-getDerivationSources derivation =-  bracket-    [C.throwBlock| Strings* {-      Strings *r = new Strings();-      for (auto i : $fptr-ptr:(Derivation *derivation)->inputSrcs) {-        r->push_back(i);-      }-      return r;-    }|]-    deleteStrings-    toByteStrings+getDerivationSources :: Store -> Derivation -> IO [StorePath]+getDerivationSources (Store store) derivation = mask_ do+  vec <-+    moveToForeignPtrWrapper+      =<< [C.throwBlock| std::vector<nix::StorePath*>* {+        Store &store = **$(refStore* store);+        auto r = new std::vector<StorePath *>();+        for (auto s : $fptr-ptr:(Derivation *derivation)->inputSrcs)+          r->push_back(new StorePath(parseStorePath23(store, s)));+        return r;+      }|]+  traverse moveStorePath =<< Std.Vector.toList vec -getDerivationInputs :: ForeignPtr Derivation -> IO [(ByteString, [ByteString])]-getDerivationInputs derivation =+getDerivationInputs :: Store -> Derivation -> IO [(StorePath, [ByteString])]+getDerivationInputs (Store store) derivation =   bracket     [C.exp| DerivationInputsIterator* {       new DerivationInputsIterator($fptr-ptr:(Derivation *derivation)->inputDrvs.begin())@@ -289,7 +675,12 @@       if isEnd         then pure []         else do-          name <- [C.exp| const char*{ strdup((*$(DerivationInputsIterator *i))->first.c_str()) }|] >>= BS.unsafePackMallocCString+          name <-+            [C.throwBlock| nix::StorePath *{+              Store &store = **$(refStore* store);+              return new StorePath(parseStorePath23(store, (*$(DerivationInputsIterator *i))->first));+            }|]+              >>= moveStorePath           outs <-             bracket               [C.block| Strings*{ @@ -307,7 +698,7 @@ deleteDerivationInputsIterator :: Ptr DerivationInputsIterator -> IO () deleteDerivationInputsIterator a = [C.block| void { delete $(DerivationInputsIterator *a); }|] -getDerivationEnv :: ForeignPtr Derivation -> IO (Map ByteString ByteString)+getDerivationEnv :: Derivation -> IO (Map ByteString ByteString) getDerivationEnv derivation =   [C.exp| StringPairs* { &($fptr-ptr:(Derivation *derivation)->env) }|]     >>= toByteStringMap@@ -390,16 +781,25 @@ pushString strings s =   [C.block| void { $(Strings *strings)->push_back($bs-cstr:s); }|] -copyClosure :: Store -> Store -> [ByteString] -> IO ()-copyClosure (Store src) (Store dest) paths = do-  withStringsOf paths $ \pathStrings -> do+copyClosure :: Store -> Store -> [StorePath] -> IO ()+copyClosure (Store src) (Store dest) pathList = do+  (StdVector pathsVector') <- Std.Vector.fromList (pathList <&> \(StorePath c) -> unsafeForeignPtrToPtr c)+  withForeignPtr pathsVector' \pathsVector ->     [C.throwBlock| void {-      StringSet pathSet;-      for (auto path : *$(Strings *pathStrings)) {-        pathSet.insert(path);-      }-      nix::copyClosure(*$(refStore* src), *$(refStore* dest), pathSet);+      ref<Store> src = *$(refStore* src);+      ref<Store> dest = *$(refStore* dest);+      std::vector<nix::StorePath *> &pathsVector = *$(std::vector<nix::StorePath*>* pathsVector);++      StorePathSet pathSet;+      for (auto spp : pathsVector)+        pathSet.insert(*spp);++      StorePathSet closurePaths;+      compatComputeFSClosure(*src, pathSet, closurePaths);++      nix::copyPaths(src, dest, compatPathSet(*src, closurePaths));     }|]+  for_ pathList (\(StorePath c) -> touchForeignPtr c)  parseSecretKey :: ByteString -> IO (ForeignPtr SecretKey) parseSecretKey bs =@@ -424,104 +824,58 @@   -- | Secret signing key   Ptr SecretKey ->   -- | Store path-  ByteString ->+  StorePath ->   -- | False if the signature was already present, True if the signature was added   IO Bool-signPath (Store store) secretKey path =+signPath (Store store) secretKey (StorePath path) =   (== 1) <$> do     [C.throwBlock| int {     nix::ref<nix::Store> store = *$(refStore *store);-    std::string storePath($bs-cstr:path);-    auto currentInfo = store->queryPathInfo(storePath);+    const StorePath &storePath = *$fptr-ptr:(nix::StorePath *path);+    const SecretKey &secretKey = *$(SecretKey *secretKey);+    auto currentInfo = store->queryPathInfo(printPath23(*store, storePath));      auto info2(*currentInfo);     info2.sigs.clear();-    info2.sign(*$(SecretKey *secretKey));+#ifdef NIX_2_4+    info2.sign(*store, secretKey);+#else+    info2.sign(secretKey);+#endif     assert(!info2.sigs.empty());     auto sig = *info2.sigs.begin();      if (currentInfo->sigs.count(sig)) {       return 0;     } else {-      store->addSignatures(storePath, info2.sigs);+      store->addSignatures(printPath23(*store, storePath), info2.sigs);       return 1;     }   }|] ------ PathSet ------newtype PathSet = PathSet (ForeignPtr (C.StdSet C.StdString))--finalizePathSet :: FinalizerPtr C.PathSet-{-# NOINLINE finalizePathSet #-}-finalizePathSet =-  unsafePerformIO-    [C.exp|-  void (*)(PathSet *) {-    [](PathSet *v){-      delete v;-    }-  } |]--newEmptyPathSet :: IO PathSet-newEmptyPathSet = do-  ptr <- [C.exp| PathSet *{ new PathSet() }|]-  fptr <- newForeignPtr finalizePathSet ptr-  pure $ PathSet fptr--addToPathSet :: ByteString -> PathSet -> IO ()-addToPathSet bs pathSet_ = withPathSet pathSet_ $ \pathSet ->-  [C.throwBlock| void { -    $(PathSet *pathSet)->insert(std::string($bs-ptr:bs, $bs-len:bs));-  }|]--withPathSet :: PathSet -> (Ptr C.PathSet -> IO b) -> IO b-withPathSet (PathSet pathSetFptr) = withForeignPtr pathSetFptr--traversePathSet :: forall a. (ByteString -> IO a) -> PathSet -> IO [a]-traversePathSet f pathSet_ = withPathSet pathSet_ $ \pathSet -> do-  i <- [C.exp| PathSetIterator *{ new PathSetIterator($(PathSet *pathSet)->begin()) }|]-  end <- [C.exp| PathSetIterator *{ new PathSetIterator ($(PathSet *pathSet)->end()) }|]-  let cleanup =-        [C.throwBlock| void {-          delete $(PathSetIterator *i);-          delete $(PathSetIterator *end);-        }|]-  flip finally cleanup $-    let go :: ([a] -> [a]) -> IO [a]-        go acc = do-          isDone <--            [C.exp| int {-            *$(PathSetIterator *i) == *$(PathSetIterator *end)-          }|]-          if isDone /= 0-            then pure $ acc []-            else do-              somePath <- unsafePackMallocCString =<< [C.exp| const char *{ strdup((*$(PathSetIterator *i))->c_str()) } |]-              a <- f somePath-              [C.throwBlock| void { (*$(PathSetIterator *i))++; } |]-              go (acc . (a :))-     in go identity- -- | Follow symlinks to the store and chop off the parts after the top-level store name-followLinksToStorePath :: Store -> ByteString -> IO ByteString+followLinksToStorePath :: Store -> ByteString -> IO StorePath followLinksToStorePath (Store store) bs =-  unsafePackMallocCString-    =<< [C.throwBlock| const char *{-    return strdup((*$(refStore* store))->followLinksToStorePath(std::string($bs-ptr:bs, $bs-len:bs)).c_str());-  }|]+  moveStorePath+    =<< [C.throwBlock| nix::StorePath *{+      Store &store = **$(refStore* store);+      std::string s = std::string($bs-ptr:bs, $bs-len:bs);+      return new StorePath(parseStorePath23(store, store.followLinksToStorePath(s)));+    }|]  queryPathInfo ::   Store ->   -- | Exact store path, not a subpath-  ByteString ->+  StorePath ->   -- | ValidPathInfo or exception   IO (ForeignPtr (Ref ValidPathInfo))-queryPathInfo (Store store) path = do+queryPathInfo (Store store) (StorePath path) = do   vpi <--    [C.throwBlock| refValidPathInfo*-      {-        return new refValidPathInfo((*$(refStore* store))->queryPathInfo($bs-cstr:path));-      } |]+    [C.throwBlock| refValidPathInfo* {+      Store &store = **$(refStore* store);+      StorePath &path = *$fptr-ptr:(nix::StorePath *path);+      return new refValidPathInfo(store.queryPathInfo(printPath23(store, path)));+    }|]   newForeignPtr finalizeRefValidPathInfo vpi  finalizeRefValidPathInfo :: FinalizerPtr (Ref ValidPathInfo)@@ -529,11 +883,11 @@ finalizeRefValidPathInfo =   unsafePerformIO     [C.exp|-  void (*)(refValidPathInfo *) {-    [](refValidPathInfo *v){ delete v; }-  } |]+      void (*)(refValidPathInfo *) {+        [](refValidPathInfo *v){ delete v; }+      }|] --- | The narSize field of a ValidPathInfo struct. Source: store-api.hh+-- | The narSize field of a ValidPathInfo struct. Source: path-info.hh / store-api.hh validPathInfoNarSize :: ForeignPtr (Ref ValidPathInfo) -> Int64 validPathInfoNarSize vpi =   fromIntegral $@@ -542,41 +896,41 @@         { (*$fptr-ptr:(refValidPathInfo* vpi))->narSize }       |] --- | Copy the narHash field of a ValidPathInfo struct. Source: store-api.hh-validPathInfoNarHash :: ForeignPtr (Ref ValidPathInfo) -> IO ByteString-validPathInfoNarHash vpi =+-- | Copy the narHash field of a ValidPathInfo struct. Source: path-info.hh / store-api.hh+validPathInfoNarHash32 :: ForeignPtr (Ref ValidPathInfo) -> IO ByteString+validPathInfoNarHash32 vpi =   unsafePackMallocCString-    =<< [C.exp| const char-        *{ strdup((*$fptr-ptr:(refValidPathInfo* vpi))->narHash.to_string().c_str()) }-      |]+    =<< [C.block| const char *{ +      std::string s((*$fptr-ptr:(refValidPathInfo* vpi))->narHash.to_string(nix::Base32, true));+      return strdup(s.c_str()); }+    |]  -- | Deriver field of a ValidPathInfo struct. Source: store-api.hh -- -- Returns 'unknownDeriver' when missing.-validPathInfoDeriver :: ForeignPtr (Ref ValidPathInfo) -> IO ByteString-validPathInfoDeriver vpi =-  unsafePackMallocCString-    =<< [C.throwBlock| const char*-        {-          std::optional<Path> deriver = (*$fptr-ptr:(refValidPathInfo* vpi))->deriver;-          return strdup((deriver == "" ? "unknown-deriver" : deriver->c_str()));-        }-      |]---- | String constant representing the case when the deriver of a store path does--- not exist or is not known. Value: @unknown-deriver@-unknownDeriver :: Text-unknownDeriver = "unknown-deriver"+validPathInfoDeriver :: Store -> ForeignPtr (Ref ValidPathInfo) -> IO (Maybe StorePath)+validPathInfoDeriver (Store store) vpi =+  moveStorePathMaybe+    =<< [C.throwBlock| nix::StorePath * {+      Store &store = **$(refStore* store);+      std::optional<StorePath> deriver = parseOptionalStorePath23(store, (*$fptr-ptr:(refValidPathInfo* vpi))->deriver);+      return deriver ? new StorePath(*deriver) : nullptr;+    }|]  -- | References field of a ValidPathInfo struct. Source: store-api.hh-validPathInfoReferences :: ForeignPtr (Ref ValidPathInfo) -> IO PathSet-validPathInfoReferences vpi = do-  ptr <--    [C.exp| const PathSet*-            { new PathSet((*$fptr-ptr:(refValidPathInfo* vpi))->references) }-        |]-  fptr <- newForeignPtr finalizePathSet ptr-  pure $ PathSet fptr+validPathInfoReferences :: Store -> ForeignPtr (Ref ValidPathInfo) -> IO [StorePath]+validPathInfoReferences (Store store) vpi = do+  sps <-+    moveToForeignPtrWrapper+      =<< [C.throwBlock| std::vector<nix::StorePath *>* {+        Store &store = **$(refStore* store);+        auto sps = new std::vector<nix::StorePath *>();+        for (auto sp : parseStorePathSet23(store, (*$fptr-ptr:(refValidPathInfo* vpi))->references))+          sps->push_back(new StorePath(sp));+        return sps;+      }|]+  l <- Std.Vector.toList sps+  for l moveStorePath  ----- computeFSClosure ----- data ClosureParams = ClosureParams@@ -593,19 +947,22 @@       includeDerivers = False     } -computeFSClosure :: Store -> ClosureParams -> PathSet -> IO PathSet-computeFSClosure (Store store) params startingSet_ = withPathSet startingSet_ $ \startingSet -> do+computeFSClosure :: Store -> ClosureParams -> StdSet NixStorePath -> IO (StdSet NixStorePath)+computeFSClosure (Store store) params (Std.Set.StdSet startingSet) = do   let countTrue :: Bool -> C.CInt       countTrue True = 1       countTrue False = 0       flipDir = countTrue $ flipDirection params       inclOut = countTrue $ includeOutputs params       inclDrv = countTrue $ includeDerivers params-  ps <--    [C.throwBlock| PathSet* {-             PathSet *r = new PathSet();-             (*$(refStore* store))->computeFSClosure(*$(PathSet *startingSet), *r, $(int flipDir), $(int inclOut), $(int inclDrv));-             return r;-           } |]-  fp <- newForeignPtr finalizePathSet ps-  pure $ PathSet fp+  ret@(Std.Set.StdSet retSet) <- Std.Set.new+  [C.throwBlock| void {+    Store &store = **$(refStore* store);+    StorePathSet &ret = *$fptr-ptr:(std::set<nix::StorePath>* retSet);+    compatComputeFSClosure(store, *$fptr-ptr:(std::set<nix::StorePath>* startingSet), ret,+      $(int flipDir), $(int inclOut), $(int inclDrv));+  }|]+  pure ret++withPtr' :: (Coercible a' (ForeignPtr a)) => a' -> (Ptr a -> IO b) -> IO b+withPtr' p = withForeignPtr (coerce p)
src/Hercules/CNix/Store/Context.hs view
@@ -12,23 +12,12 @@ import qualified Language.C.Types as C import Protolude --- | A C++ STL @std::set@, to be used in phantom types.-data StdSet a---- | A C++ STL @<a>::iterator@-data Iterator a- -- | A C++ @std::string@ data StdString  -- | A Nix @ref@, to be used in phantom types. data Ref a --- | A Nix @PathSet@ aka @std::set<Path>@ (Nix <= 2.3.*: aka @std::set<std::string>>@)-type PathSet = StdSet StdString--type PathSetIterator = Iterator StdString- -- | A Nix @Strings@ aka @std::list<std::string>@ data Strings @@ -50,15 +39,22 @@  data SecretKey +data NixStorePath++data NixStorePathWithOutputs++data CDerivation+ context :: C.Context context =   C.cppCtx <> C.fptrCtx     <> C.bsCtx+    <> mempty       { C.ctxTypesTable =           M.singleton (C.TypeName "refStore") [t|Ref NixStore|]+            <> M.singleton (C.TypeName "nix::StorePath") [t|NixStorePath|]+            <> M.singleton (C.TypeName "nix::StorePathWithOutputs") [t|NixStorePathWithOutputs|]             <> M.singleton (C.TypeName "refValidPathInfo") [t|Ref ValidPathInfo|]-            <> M.singleton (C.TypeName "PathSet") [t|PathSet|]-            <> M.singleton (C.TypeName "PathSetIterator") [t|PathSetIterator|]             <> M.singleton (C.TypeName "Strings") [t|Strings|]             <> M.singleton (C.TypeName "StringsIterator") [t|StringsIterator|]             <> M.singleton (C.TypeName "StringPairs") [t|StringPairs|]
+ src/Hercules/CNix/Store/Instances.hs view
@@ -0,0 +1,34 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TemplateHaskell #-}+{-# OPTIONS_GHC -Wno-orphans #-}++-- | Define instances for C++ types in the Context module that can't be in that+-- module because of TH staging restrictions.+module Hercules.CNix.Store.Instances where++import Data.Data (Proxy (Proxy))+import Data.Function (($))+import Data.Semigroup (Semigroup ((<>)))+import Hercules.CNix.Std.Set+import Hercules.CNix.Std.Vector+import Hercules.CNix.Store.Context+import qualified Language.C.Inline.Cpp as C++C.context $ context <> stdVectorCtx <> stdSetCtx++C.include "<nix/path.hh>"+C.include "<nix/derivations.hh>"++#ifdef NIX_2_4+C.include "<nix/path-with-outputs.hh>"+#endif++_ = Proxy :: Proxy NixStorePath++instanceStdVector "nix::StorePath *"+instanceStdVectorCopyable "nix::StorePath *"+instanceStdSet "nix::StorePath"++instanceStdVector "nix::StorePathWithOutputs *"+instanceStdVector "nix::StorePathWithOutputs"
+ test/Hercules/CNix/Std/SetSpec.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE BlockArguments #-}+{-# LANGUAGE TemplateHaskell #-}+{-# OPTIONS_GHC -Wno-orphans #-}++module Hercules.CNix.Std.SetSpec where++import Hercules.CNix.Std.Set as Std.Set+import qualified Language.C.Inline.Cpp as C+import Protolude+import Test.Hspec++C.context stdSetCtx++instanceStdSet "int"+instanceStdSetCopyable "int"++spec :: Spec+spec =+  describe "Std.Set" do+    it "can insert and retrieve copyable values" do+      set <- Std.Set.new+      Std.Set.insert set 1+      Std.Set.insert set 2+      Std.Set.insert set 3+      Std.Set.insert set minBound+      Std.Set.insert set maxBound+      l <- Std.Set.toList set+      sort l `shouldBe` ([minBound, 1, 2, 3, maxBound] :: [C.CInt])
+ test/Hercules/CNix/Store/DerivationSpec.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE BlockArguments #-}++module Hercules.CNix.Store.DerivationSpec (spec) where++import qualified Data.ByteString as BS+import qualified Data.Map as M+import Hercules.CNix.Store (getDerivationArguments, getDerivationBuilder, getDerivationEnv, getDerivationFromString, getDerivationInputs, getDerivationOutputs, getDerivationPlatform, getDerivationSources)+import Hercules.CNix.Store.TestUtil (withTempStore)+import Protolude+import Test.Hspec++spec :: Spec+spec = do+  it "parses a fixed-output derivation correctly" $ withTempStore \store -> do+    bs <- BS.readFile "test/data/fixed-output.drv"+    let drvName = "hello-2.10.tar.gz"+    d <- getDerivationFromString store drvName bs+    platform <- getDerivationPlatform d+    platform `shouldBe` "x86_64-linux"+    builder <- getDerivationBuilder d+    builder `shouldBe` "/nix/store/a3fc4zqaiak11jks9zd579mz5v0li8bg-bash-4.4-p23/bin/bash"+    arguments <- getDerivationArguments d+    arguments `shouldBe` ["-e", "/nix/store/720ikgx7yaapyb8hvi8lkicjqwzcx3xr-builder.sh"]+    sources <- getDerivationSources store d+    show' sources `shouldBe` "[720ikgx7yaapyb8hvi8lkicjqwzcx3xr-builder.sh]"+    inputs <- getDerivationInputs store d+    show' inputs `shouldBe` "[(07apmkf04yjy8scnvz6xh08377kfdyhq-mirrors-list.drv,[\"out\"]),(abwabzabxq9vgd1rcz7m8wwgqd0fdzpw-bash-4.4-p23.drv,[\"out\"]),(fhajhb521vwlcjhm5ymh3pyh7f0rd8ay-curl-7.74.0.drv,[\"dev\"]),(qgdxmika1avgrgxpdz1af8v953c1qgvh-stdenv-linux.drv,[\"out\"])]"+    env <- getDerivationEnv d+    env `shouldBe` M.fromList [("SSL_CERT_FILE", "/no-cert-file.crt"), ("buildInputs", ""), ("builder", "/nix/store/a3fc4zqaiak11jks9zd579mz5v0li8bg-bash-4.4-p23/bin/bash"), ("configureFlags", ""), ("curlOpts", ""), ("depsBuildBuild", ""), ("depsBuildBuildPropagated", ""), ("depsBuildTarget", ""), ("depsBuildTargetPropagated", ""), ("depsHostHost", ""), ("depsHostHostPropagated", ""), ("depsTargetTarget", ""), ("depsTargetTargetPropagated", ""), ("doCheck", ""), ("doInstallCheck", ""), ("downloadToTemp", ""), ("executable", ""), ("impureEnvVars", "http_proxy https_proxy ftp_proxy all_proxy no_proxy NIX_CURL_FLAGS NIX_HASHED_MIRRORS NIX_CONNECT_TIMEOUT NIX_MIRRORS_alsa NIX_MIRRORS_apache NIX_MIRRORS_bioc NIX_MIRRORS_bitlbee NIX_MIRRORS_centos NIX_MIRRORS_cpan NIX_MIRRORS_debian NIX_MIRRORS_fedora NIX_MIRRORS_gcc NIX_MIRRORS_gentoo NIX_MIRRORS_gnome NIX_MIRRORS_gnu NIX_MIRRORS_gnupg NIX_MIRRORS_hackage NIX_MIRRORS_hashedMirrors NIX_MIRRORS_imagemagick NIX_MIRRORS_kde NIX_MIRRORS_kernel NIX_MIRRORS_luarocks NIX_MIRRORS_maven NIX_MIRRORS_metalab NIX_MIRRORS_mozilla NIX_MIRRORS_mysql NIX_MIRRORS_oldsuse NIX_MIRRORS_openbsd NIX_MIRRORS_opensuse NIX_MIRRORS_osdn NIX_MIRRORS_postgresql NIX_MIRRORS_pypi NIX_MIRRORS_roy NIX_MIRRORS_sageupstream NIX_MIRRORS_samba NIX_MIRRORS_savannah NIX_MIRRORS_sourceforge NIX_MIRRORS_steamrt NIX_MIRRORS_ubuntu NIX_MIRRORS_xfce NIX_MIRRORS_xorg"), ("mirrorsFile", "/nix/store/mv7ky6l8q2zk8khp7xvmany2nb8phwi6-mirrors-list"), ("name", "hello-2.10.tar.gz"), ("nativeBuildInputs", "/nix/store/0bnxpjfknrwxi8kh9yb2js01xd6wz566-curl-7.74.0-dev"), ("nixpkgsVersion", "20.09"), ("out", "/nix/store/3x7dwzq014bblazs7kq20p9hyzz0qh8g-hello-2.10.tar.gz"), ("outputHash", "0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i"), ("outputHashAlgo", "sha256"), ("outputHashMode", "flat"), ("outputs", "out"), ("patches", ""), ("postFetch", ""), ("preferHashedMirrors", "1"), ("preferLocalBuild", "1"), ("propagatedBuildInputs", ""), ("propagatedNativeBuildInputs", ""), ("showURLs", ""), ("stdenv", "/nix/store/av0pavd6vn698g82ml66gd2hnjd01nzb-stdenv-linux"), ("strictDeps", ""), ("system", "x86_64-linux"), ("urls", "mirror://gnu/hello/hello-2.10.tar.gz")]+    os <- getDerivationOutputs store drvName d+    show' os `shouldBe` "[DerivationOutput {derivationOutputName = \"out\", derivationOutputPath = Just 3x7dwzq014bblazs7kq20p9hyzz0qh8g-hello-2.10.tar.gz, derivationOutputDetail = DerivationOutputCAFixed (FixedOutputHash Flat (Hash SHA256 \"1\\224f\\DC3z\\150&v\\232\\159i\\209\\182S\\130\\222\\149\\167\\239}\\145K\\140\\185V\\244\\RS\\167.\\SIQk\")) 3x7dwzq014bblazs7kq20p9hyzz0qh8g-hello-2.10.tar.gz}]"++  it "parses a regular derivation correctly" $ withTempStore \store -> do+    bs <- BS.readFile "test/data/regular.drv"+    let drvName = "hello-2.10"+    d <- getDerivationFromString store drvName bs+    platform <- getDerivationPlatform d+    platform `shouldBe` "x86_64-linux"+    builder <- getDerivationBuilder d+    builder `shouldBe` "/nix/store/a3fc4zqaiak11jks9zd579mz5v0li8bg-bash-4.4-p23/bin/bash"+    arguments <- getDerivationArguments d+    arguments `shouldBe` ["-e", "/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh"]+    sources <- getDerivationSources store d+    show' sources `shouldBe` "[9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh]"+    inputs <- getDerivationInputs store d+    show' inputs `shouldBe` "[(7wbzr8nfcibnybvj38rrzkx8xzr7a1xl-stdenv-linux.drv,[\"out\"]),(abwabzabxq9vgd1rcz7m8wwgqd0fdzpw-bash-4.4-p23.drv,[\"out\"]),(w8z5d97dskfaj7wi12132pp7cn69f7xm-hello-2.10.tar.gz.drv,[\"out\"])]"+    env <- getDerivationEnv d+    env `shouldBe` M.fromList [("buildInputs", ""), ("builder", "/nix/store/a3fc4zqaiak11jks9zd579mz5v0li8bg-bash-4.4-p23/bin/bash"), ("configureFlags", ""), ("depsBuildBuild", ""), ("depsBuildBuildPropagated", ""), ("depsBuildTarget", ""), ("depsBuildTargetPropagated", ""), ("depsHostHost", ""), ("depsHostHostPropagated", ""), ("depsTargetTarget", ""), ("depsTargetTargetPropagated", ""), ("doCheck", "1"), ("doInstallCheck", ""), ("name", "hello-2.10"), ("nativeBuildInputs", ""), ("out", "/nix/store/8mygch54p7brcqn83xnfa22ik9y3km95-hello-2.10"), ("outputs", "out"), ("patches", ""), ("pname", "hello"), ("propagatedBuildInputs", ""), ("propagatedNativeBuildInputs", ""), ("src", "/nix/store/3x7dwzq014bblazs7kq20p9hyzz0qh8g-hello-2.10.tar.gz"), ("stdenv", "/nix/store/f95jkch26wxmqd076bcm976qrn7ml46i-stdenv-linux"), ("strictDeps", ""), ("system", "x86_64-linux"), ("version", "2.10")]+    os <- getDerivationOutputs store drvName d+    show' os `shouldBe` "[DerivationOutput {derivationOutputName = \"out\", derivationOutputPath = Just 8mygch54p7brcqn83xnfa22ik9y3km95-hello-2.10, derivationOutputDetail = DerivationOutputInputAddressed 8mygch54p7brcqn83xnfa22ik9y3km95-hello-2.10}]"++show' :: Show a => a -> Text+show' = show
+ test/Hercules/CNix/Store/TestUtil.hs view
@@ -0,0 +1,12 @@+{-# LANGUAGE BlockArguments #-}++module Hercules.CNix.Store.TestUtil where++import Hercules.CNix.Store (Store, withStoreFromURI)+import Protolude+import System.IO.Temp (withSystemTempDirectory)++withTempStore :: (Store -> IO a) -> IO a+withTempStore f =+  withSystemTempDirectory "cnix-test-store" \d ->+    withStoreFromURI (toS d) f
+ test/Spec.hs view
@@ -0,0 +1,1 @@+{-# OPTIONS_GHC -F -pgmF hspec-discover -optF --module-name=Spec #-}
+ test/TestMain.hs view
@@ -0,0 +1,18 @@+{-# LANGUAGE BlockArguments #-}++import Hercules.CNix (init)+import Protolude+import qualified Spec+import System.Mem (performMajorGC)+import Test.Hspec.Runner++main :: IO ()+main = do+  init+  hspecWith config Spec.spec+  performMajorGC+  where+    config =+      defaultConfig+        { configColorMode = ColorAlways+        }
+ test/data/fixed-output.drv view
@@ -0,0 +1,1 @@+Derive([("out","/nix/store/3x7dwzq014bblazs7kq20p9hyzz0qh8g-hello-2.10.tar.gz","sha256","31e066137a962676e89f69d1b65382de95a7ef7d914b8cb956f41ea72e0f516b")],[("/nix/store/07apmkf04yjy8scnvz6xh08377kfdyhq-mirrors-list.drv",["out"]),("/nix/store/abwabzabxq9vgd1rcz7m8wwgqd0fdzpw-bash-4.4-p23.drv",["out"]),("/nix/store/fhajhb521vwlcjhm5ymh3pyh7f0rd8ay-curl-7.74.0.drv",["dev"]),("/nix/store/qgdxmika1avgrgxpdz1af8v953c1qgvh-stdenv-linux.drv",["out"])],["/nix/store/720ikgx7yaapyb8hvi8lkicjqwzcx3xr-builder.sh"],"x86_64-linux","/nix/store/a3fc4zqaiak11jks9zd579mz5v0li8bg-bash-4.4-p23/bin/bash",["-e","/nix/store/720ikgx7yaapyb8hvi8lkicjqwzcx3xr-builder.sh"],[("SSL_CERT_FILE","/no-cert-file.crt"),("buildInputs",""),("builder","/nix/store/a3fc4zqaiak11jks9zd579mz5v0li8bg-bash-4.4-p23/bin/bash"),("configureFlags",""),("curlOpts",""),("depsBuildBuild",""),("depsBuildBuildPropagated",""),("depsBuildTarget",""),("depsBuildTargetPropagated",""),("depsHostHost",""),("depsHostHostPropagated",""),("depsTargetTarget",""),("depsTargetTargetPropagated",""),("doCheck",""),("doInstallCheck",""),("downloadToTemp",""),("executable",""),("impureEnvVars","http_proxy https_proxy ftp_proxy all_proxy no_proxy NIX_CURL_FLAGS NIX_HASHED_MIRRORS NIX_CONNECT_TIMEOUT NIX_MIRRORS_alsa NIX_MIRRORS_apache NIX_MIRRORS_bioc NIX_MIRRORS_bitlbee NIX_MIRRORS_centos NIX_MIRRORS_cpan NIX_MIRRORS_debian NIX_MIRRORS_fedora NIX_MIRRORS_gcc NIX_MIRRORS_gentoo NIX_MIRRORS_gnome NIX_MIRRORS_gnu NIX_MIRRORS_gnupg NIX_MIRRORS_hackage NIX_MIRRORS_hashedMirrors NIX_MIRRORS_imagemagick NIX_MIRRORS_kde NIX_MIRRORS_kernel NIX_MIRRORS_luarocks NIX_MIRRORS_maven NIX_MIRRORS_metalab NIX_MIRRORS_mozilla NIX_MIRRORS_mysql NIX_MIRRORS_oldsuse NIX_MIRRORS_openbsd NIX_MIRRORS_opensuse NIX_MIRRORS_osdn NIX_MIRRORS_postgresql NIX_MIRRORS_pypi NIX_MIRRORS_roy NIX_MIRRORS_sageupstream NIX_MIRRORS_samba NIX_MIRRORS_savannah NIX_MIRRORS_sourceforge NIX_MIRRORS_steamrt NIX_MIRRORS_ubuntu NIX_MIRRORS_xfce NIX_MIRRORS_xorg"),("mirrorsFile","/nix/store/mv7ky6l8q2zk8khp7xvmany2nb8phwi6-mirrors-list"),("name","hello-2.10.tar.gz"),("nativeBuildInputs","/nix/store/0bnxpjfknrwxi8kh9yb2js01xd6wz566-curl-7.74.0-dev"),("nixpkgsVersion","20.09"),("out","/nix/store/3x7dwzq014bblazs7kq20p9hyzz0qh8g-hello-2.10.tar.gz"),("outputHash","0ssi1wpaf7plaswqqjwigppsg5fyh99vdlb9kzl7c9lng89ndq1i"),("outputHashAlgo","sha256"),("outputHashMode","flat"),("outputs","out"),("patches",""),("postFetch",""),("preferHashedMirrors","1"),("preferLocalBuild","1"),("propagatedBuildInputs",""),("propagatedNativeBuildInputs",""),("showURLs",""),("stdenv","/nix/store/av0pavd6vn698g82ml66gd2hnjd01nzb-stdenv-linux"),("strictDeps",""),("system","x86_64-linux"),("urls","mirror://gnu/hello/hello-2.10.tar.gz")])
+ test/data/regular.drv view
@@ -0,0 +1,1 @@+Derive([("out","/nix/store/8mygch54p7brcqn83xnfa22ik9y3km95-hello-2.10","","")],[("/nix/store/7wbzr8nfcibnybvj38rrzkx8xzr7a1xl-stdenv-linux.drv",["out"]),("/nix/store/abwabzabxq9vgd1rcz7m8wwgqd0fdzpw-bash-4.4-p23.drv",["out"]),("/nix/store/w8z5d97dskfaj7wi12132pp7cn69f7xm-hello-2.10.tar.gz.drv",["out"])],["/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh"],"x86_64-linux","/nix/store/a3fc4zqaiak11jks9zd579mz5v0li8bg-bash-4.4-p23/bin/bash",["-e","/nix/store/9krlzvny65gdc8s7kpb6lkx8cd02c25b-default-builder.sh"],[("buildInputs",""),("builder","/nix/store/a3fc4zqaiak11jks9zd579mz5v0li8bg-bash-4.4-p23/bin/bash"),("configureFlags",""),("depsBuildBuild",""),("depsBuildBuildPropagated",""),("depsBuildTarget",""),("depsBuildTargetPropagated",""),("depsHostHost",""),("depsHostHostPropagated",""),("depsTargetTarget",""),("depsTargetTargetPropagated",""),("doCheck","1"),("doInstallCheck",""),("name","hello-2.10"),("nativeBuildInputs",""),("out","/nix/store/8mygch54p7brcqn83xnfa22ik9y3km95-hello-2.10"),("outputs","out"),("patches",""),("pname","hello"),("propagatedBuildInputs",""),("propagatedNativeBuildInputs",""),("src","/nix/store/3x7dwzq014bblazs7kq20p9hyzz0qh8g-hello-2.10.tar.gz"),("stdenv","/nix/store/f95jkch26wxmqd076bcm976qrn7ml46i-stdenv-linux"),("strictDeps",""),("system","x86_64-linux"),("version","2.10")])