diff --git a/src/Nix/Builtins.hs b/src/Nix/Builtins.hs index 6c83aa2..43d6a61 100644 --- a/src/Nix/Builtins.hs +++ b/src/Nix/Builtins.hs @@ -38,7 +38,6 @@ import qualified "hashing" Crypto.Hash.SHA1 as SHA1 import qualified "hashing" Crypto.Hash.SHA256 as SHA256 import qualified "hashing" Crypto.Hash.SHA512 as SHA512 #else -import Data.ByteString.Base16 as Base16 import qualified "cryptohash-md5" Crypto.Hash.MD5 as MD5 import qualified "cryptohash-sha1" Crypto.Hash.SHA1 as SHA1 import qualified "cryptohash-sha256" Crypto.Hash.SHA256 as SHA256 @@ -52,6 +51,7 @@ import Data.Array import Data.Bits import Data.ByteString (ByteString) import qualified Data.ByteString as B +import Data.ByteString.Base16 as Base16 import qualified Data.ByteString.Lazy as LBS import Data.Char (isDigit) import Data.Fix @@ -945,12 +945,13 @@ hashString nsAlgo ns = Prim $ do _ -> throwError $ ErrorCall $ "builtins.hashString: " ++ "expected \"md5\", \"sha1\", \"sha256\", or \"sha512\", got " ++ show algo --- TODO Double-check this placeHolder :: MonadNix e m => m (NValue m) -> m (NValue m) placeHolder = fromValue >=> fromStringNoContext >=> \t -> do h <- runPrim (hashString (principledMakeNixStringWithoutContext "sha256") - (principledMakeNixStringWithoutContext t)) - toNix h + (principledMakeNixStringWithoutContext ("nix-output:" <> t))) + toNix $ principledMakeNixStringWithoutContext $ Text.cons '/' $ printHash32 $ + -- The result coming out of hashString is base16 encoded + fst $ Base16.decode $ encodeUtf8 $ principledStringIgnoreContext h absolutePathFromValue :: MonadNix e m => NValue m -> m FilePath absolutePathFromValue = \case @@ -1062,6 +1063,7 @@ exec_ xs = do xs <- traverse (coerceToString DontCopyToStore CoerceStringy <=< force') ls -- TODO Still need to do something with the context here -- See prim_exec in nix/src/libexpr/primops.cc + -- Requires the implementation of EvalState::realiseContext exec (map (Text.unpack . hackyStringIgnoreContext) xs) fetchurl :: forall e m. MonadNix e m => m (NValue m) -> m (NValue m) diff --git a/src/Nix/Utils.hs b/src/Nix/Utils.hs index fe51370..e2808a5 100644 --- a/src/Nix/Utils.hs +++ b/src/Nix/Utils.hs @@ -2,6 +2,7 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE MultiParamTypeClasses #-} +{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TupleSections #-} @@ -13,6 +14,9 @@ import Control.Monad import Control.Monad.Fix import qualified Data.Aeson as A import qualified Data.Aeson.Encoding as A +import Data.Bits +import Data.ByteString (ByteString) +import qualified Data.ByteString as B import Data.Fix import Data.HashMap.Lazy (HashMap) import qualified Data.HashMap.Lazy as M @@ -123,3 +127,26 @@ uriAwareSplit = go where let ((suffix, _):path) = go (Text.drop 3 e2) in (e1 <> Text.pack "://" <> suffix, PathEntryURI) : path | otherwise -> (e1, PathEntryPath) : go (Text.drop 1 e2) + +printHash32 :: ByteString -> Text +printHash32 bs = go (base32Len bs - 1) "" + where + go n s + | n >= 0 = go (n-1) (Text.snoc s $ nextCharHash32 bs n) + | otherwise = s + +nextCharHash32 :: ByteString -> Int -> Char +nextCharHash32 bs n = Text.index base32Chars (c .&. 0x1f) + where + b = n * 5 + i = b `div` 8 + j = b `mod` 8 + c = fromIntegral $ shiftR (B.index bs i) j .|. mask + mask = if i >= B.length bs - 1 + then 0 + else shiftL (B.index bs (i+1)) (8 - j) + -- e, o, u, and t are omitted (see base32Chars in nix/src/libutil/hash.cc) + base32Chars = "0123456789abcdfghijklmnpqrsvwxyz" + +base32Len :: ByteString -> Int +base32Len bs = ((B.length bs * 8 - 1) `div` 5) + 1 diff --git a/tests/eval-compare/placeholder.nix b/tests/eval-compare/placeholder.nix new file mode 100644 index 0000000..57d792d --- /dev/null +++ b/tests/eval-compare/placeholder.nix @@ -0,0 +1 @@ +builtins.placeholder "foo" \ No newline at end of file