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 +21/−0
- compat-2.3/derivation-output.cxx +23/−0
- compat-2.3/include/nix/content-address.hh +77/−0
- compat-2.3/include/nix/derivation-output.hh +68/−0
- compat-2.3/include/nix/path-compat.hh +42/−0
- compat-2.3/include/nix/path-info.hh +3/−0
- compat-2.3/include/nix/path.hh +91/−0
- compat-2.3/path.cxx +54/−0
- compat-2.4/include/nix-compat.hh +26/−0
- hercules-ci-cnix-store.cabal +71/−6
- include/hercules-ci-cnix/store.hxx +2/−0
- src/Hercules/CNix.hs +10/−0
- src/Hercules/CNix/Encapsulation.hs +19/−0
- src/Hercules/CNix/Settings.hs +99/−0
- src/Hercules/CNix/Std/Set.hs +192/−0
- src/Hercules/CNix/Std/String.hs +93/−0
- src/Hercules/CNix/Std/String/Context.hs +21/−0
- src/Hercules/CNix/Std/String/Instances.hs +19/−0
- src/Hercules/CNix/Std/Vector.hs +171/−0
- src/Hercules/CNix/Store.hs +562/−205
- src/Hercules/CNix/Store/Context.hs +9/−13
- src/Hercules/CNix/Store/Instances.hs +34/−0
- test/Hercules/CNix/Std/SetSpec.hs +28/−0
- test/Hercules/CNix/Store/DerivationSpec.hs +53/−0
- test/Hercules/CNix/Store/TestUtil.hs +12/−0
- test/Spec.hs +1/−0
- test/TestMain.hs +18/−0
- test/data/fixed-output.drv +1/−0
- test/data/regular.drv +1/−0
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")])