Skip to content

Commit 5727827

Browse files
committed
Store dir like nix
1 parent 6b32c7c commit 5727827

File tree

13 files changed

+147
-105
lines changed

13 files changed

+147
-105
lines changed

hnix-store-core/src/System/Nix/Derivation.hs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,22 @@ import qualified Data.Attoparsec.Text.Lazy as Text.Lazy
1111
( Parser )
1212
import Nix.Derivation ( Derivation )
1313
import qualified Nix.Derivation as Derivation
14-
import System.Nix.StorePath ( StorePath )
14+
import System.Nix.StorePath ( StoreDir
15+
, StorePath
16+
, storePathToFilePath
17+
)
1518
import qualified System.Nix.StorePath as StorePath
1619

1720

1821

19-
parseDerivation :: FilePath -> Text.Lazy.Parser (Derivation StorePath Text)
22+
parseDerivation :: StoreDir -> Text.Lazy.Parser (Derivation StorePath Text)
2023
parseDerivation expectedRoot =
2124
Derivation.parseDerivationWith
2225
("\"" *> StorePath.pathParser expectedRoot <* "\"")
2326
Derivation.textParser
2427

25-
buildDerivation :: Derivation StorePath Text -> Text.Lazy.Builder
26-
buildDerivation =
28+
buildDerivation :: StoreDir -> Derivation StorePath Text -> Text.Lazy.Builder
29+
buildDerivation storeDir =
2730
Derivation.buildDerivationWith
28-
(show . show)
31+
(show . storePathToFilePath storeDir)
2932
show

hnix-store-core/src/System/Nix/Internal/StorePath.hs

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ Description : Representation of Nix store paths.
1010

1111
module System.Nix.Internal.StorePath
1212
( -- * Basic store path types
13-
StorePath(..)
13+
StoreDir(..)
14+
, StorePath(..)
1415
, StorePathName(..)
1516
, StorePathSet
1617
, mkStorePathHashPart
@@ -54,10 +55,9 @@ import Crypto.Hash ( SHA256
5455
-- From the Nix thesis: A store path is the full path of a store
5556
-- object. It has the following anatomy: storeDir/hashPart-name.
5657
--
57-
-- @storeDir@: The root of the Nix store (e.g. \/nix\/store).
58-
--
59-
-- See the 'StoreDir' haddocks for details on why we represent this at
60-
-- the type level.
58+
-- The store directory is *not* included, and must be known from the
59+
-- context. This matches modern C++ Nix, and also represents the fact
60+
-- that store paths for different store directories cannot be mixed.
6161
data StorePath = StorePath
6262
{ -- | The 160-bit hash digest reflecting the "address" of the name.
6363
-- Currently, this is a truncated SHA256 hash.
@@ -66,18 +66,13 @@ data StorePath = StorePath
6666
-- this is typically the package name and version (e.g.
6767
-- hello-1.2.3).
6868
storePathName :: !StorePathName
69-
, -- | Root of the store
70-
storePathRoot :: !FilePath
7169
}
72-
deriving (Eq, Ord)
70+
deriving (Eq, Ord, Show)
7371

7472
instance Hashable StorePath where
7573
hashWithSalt s StorePath{..} =
7674
s `hashWithSalt` storePathHash `hashWithSalt` storePathName
7775

78-
instance Show StorePath where
79-
show p = Bytes.Char8.unpack $ storePathToRawFilePath p
80-
8176
-- | The name portion of a Nix path.
8277
--
8378
-- 'unStorePathName' must only contain a-zA-Z0-9+._?=-, can't start
@@ -86,7 +81,7 @@ instance Show StorePath where
8681
newtype StorePathName = StorePathName
8782
{ -- | Extract the contents of the name.
8883
unStorePathName :: Text
89-
} deriving (Eq, Hashable, Ord)
84+
} deriving (Eq, Hashable, Ord, Show)
9085

9186
-- | The hash algorithm used for store path hashes.
9287
newtype StorePathHashPart = StorePathHashPart ByteString
@@ -161,22 +156,29 @@ validStorePathNameChar c =
161156
-- to avoid the dependency.
162157
type RawFilePath = ByteString
163158

159+
-- | The path to the store dir
160+
--
161+
-- Many operations need to be parameterized with this, since store paths
162+
-- do not know their own store dir by design.
163+
newtype StoreDir = StoreDir {
164+
unStoreDir :: RawFilePath
165+
} deriving (Eq, Hashable, Ord, Show)
166+
164167
-- | Render a 'StorePath' as a 'RawFilePath'.
165-
storePathToRawFilePath :: StorePath -> RawFilePath
166-
storePathToRawFilePath StorePath{..} =
167-
root <> "/" <> hashPart <> "-" <> name
168+
storePathToRawFilePath :: StoreDir -> StorePath -> RawFilePath
169+
storePathToRawFilePath storeDir StorePath{..} =
170+
unStoreDir storeDir <> "/" <> hashPart <> "-" <> name
168171
where
169-
root = Bytes.Char8.pack storePathRoot
170172
hashPart = encodeUtf8 $ encodeWith NixBase32 $ coerce storePathHash
171173
name = encodeUtf8 $ unStorePathName storePathName
172174

173175
-- | Render a 'StorePath' as a 'FilePath'.
174-
storePathToFilePath :: StorePath -> FilePath
175-
storePathToFilePath = Bytes.Char8.unpack . storePathToRawFilePath
176+
storePathToFilePath :: StoreDir -> StorePath -> FilePath
177+
storePathToFilePath storeDir = Bytes.Char8.unpack . storePathToRawFilePath storeDir
176178

177179
-- | Render a 'StorePath' as a 'Text'.
178-
storePathToText :: StorePath -> Text
179-
storePathToText = toText . Bytes.Char8.unpack . storePathToRawFilePath
180+
storePathToText :: StoreDir -> StorePath -> Text
181+
storePathToText storeDir = toText . Bytes.Char8.unpack . storePathToRawFilePath storeDir
180182

181183
-- | Build `narinfo` suffix from `StorePath` which
182184
-- can be used to query binary caches.
@@ -186,7 +188,7 @@ storePathToNarInfo StorePath{..} =
186188

187189
-- | Parse `StorePath` from `Bytes.Char8.ByteString`, checking
188190
-- that store directory matches `expectedRoot`.
189-
parsePath :: FilePath -> Bytes.Char8.ByteString -> Either String StorePath
191+
parsePath :: StoreDir -> Bytes.Char8.ByteString -> Either String StorePath
190192
parsePath expectedRoot x =
191193
let
192194
(rootDir, fname) = FilePath.splitFileName . Bytes.Char8.unpack $ x
@@ -196,17 +198,20 @@ parsePath expectedRoot x =
196198
--rootDir' = dropTrailingPathSeparator rootDir
197199
-- cannot use ^^ as it drops multiple slashes /a/b/// -> /a/b
198200
rootDir' = Unsafe.init rootDir
201+
expectedRootS = Bytes.Char8.unpack (unStoreDir expectedRoot)
199202
storeDir =
200-
if expectedRoot == rootDir'
203+
if expectedRootS == rootDir'
201204
then pure rootDir'
202-
else Left $ "Root store dir mismatch, expected" <> expectedRoot <> "got" <> rootDir'
205+
else Left $ "Root store dir mismatch, expected" <> expectedRootS <> "got" <> rootDir'
203206
in
204-
StorePath <$> coerce storeHash <*> name <*> storeDir
207+
StorePath <$> coerce storeHash <*> name
205208

206-
pathParser :: FilePath -> Parser StorePath
209+
pathParser :: StoreDir -> Parser StorePath
207210
pathParser expectedRoot = do
211+
let expectedRootS = Bytes.Char8.unpack (unStoreDir expectedRoot)
212+
208213
_ <-
209-
Parser.Text.Lazy.string (toText expectedRoot)
214+
Parser.Text.Lazy.string (toText expectedRootS)
210215
<?> "Store root mismatch" -- e.g. /nix/store
211216

212217
_ <- Parser.Text.Lazy.char '/'
@@ -232,4 +237,4 @@ pathParser expectedRoot = do
232237
either
233238
fail
234239
pure
235-
(StorePath <$> coerce digest <*> name <*> pure expectedRoot)
240+
(StorePath <$> coerce digest <*> name)

hnix-store-core/src/System/Nix/ReadonlyStore.hs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
module System.Nix.ReadonlyStore where
55

66

7+
import qualified Data.ByteString.Char8 as Bytes.Char8
78
import qualified Data.ByteString as BS
89
import qualified Data.HashSet as HS
910
import System.Nix.Hash
@@ -23,43 +24,42 @@ import Crypto.Hash ( Context
2324
makeStorePath
2425
:: forall h
2526
. (NamedAlgo h)
26-
=> FilePath
27+
=> StoreDir
2728
-> ByteString
2829
-> Digest h
2930
-> StorePathName
3031
-> StorePath
31-
makeStorePath fp ty h nm = StorePath (coerce storeHash) nm fp
32+
makeStorePath storeDir ty h nm = StorePath (coerce storeHash) nm
3233
where
3334
storeHash = mkStorePathHash @h s
34-
3535
s =
3636
BS.intercalate ":" $
3737
ty:fmap encodeUtf8
3838
[ algoName @h
3939
, encodeDigestWith Base16 h
40-
, toText fp
40+
, toText . Bytes.Char8.unpack $ unStoreDir storeDir
4141
, coerce nm
4242
]
4343

4444
makeTextPath
45-
:: FilePath -> StorePathName -> Digest SHA256 -> StorePathSet -> StorePath
46-
makeTextPath fp nm h refs = makeStorePath fp ty h nm
45+
:: StoreDir -> StorePathName -> Digest SHA256 -> StorePathSet -> StorePath
46+
makeTextPath storeDir nm h refs = makeStorePath storeDir ty h nm
4747
where
4848
ty =
49-
BS.intercalate ":" $ "text" : sort (storePathToRawFilePath <$> HS.toList refs)
49+
BS.intercalate ":" $ "text" : sort (storePathToRawFilePath storeDir <$> HS.toList refs)
5050

5151
makeFixedOutputPath
5252
:: forall hashAlgo
5353
. NamedAlgo hashAlgo
54-
=> FilePath
54+
=> StoreDir
5555
-> Bool
5656
-> Digest hashAlgo
5757
-> StorePathName
5858
-> StorePath
59-
makeFixedOutputPath fp recursive h =
59+
makeFixedOutputPath storeDir recursive h =
6060
if recursive && (algoName @hashAlgo) == "sha256"
61-
then makeStorePath fp "source" h
62-
else makeStorePath fp "output:out" h'
61+
then makeStorePath storeDir "source" h
62+
else makeStorePath storeDir "output:out" h'
6363
where
6464
h' =
6565
hash @ByteString @SHA256
@@ -70,19 +70,20 @@ makeFixedOutputPath fp recursive h =
7070
<> ":"
7171

7272
computeStorePathForText
73-
:: FilePath -> StorePathName -> ByteString -> (StorePathSet -> StorePath)
74-
computeStorePathForText fp nm = makeTextPath fp nm . hash
73+
:: StoreDir -> StorePathName -> ByteString -> (StorePathSet -> StorePath)
74+
computeStorePathForText storeDir nm = makeTextPath storeDir nm . hash
7575

7676
computeStorePathForPath
77-
:: StorePathName -- ^ Name part of the newly created `StorePath`
77+
:: StoreDir
78+
-> StorePathName -- ^ Name part of the newly created `StorePath`
7879
-> FilePath -- ^ Local `FilePath` to add
7980
-> Bool -- ^ Add target directory recursively
8081
-> (FilePath -> Bool) -- ^ Path filter function
8182
-> Bool -- ^ Only used by local store backend
8283
-> IO StorePath
83-
computeStorePathForPath name pth recursive _pathFilter _repair = do
84+
computeStorePathForPath storeDir name pth recursive _pathFilter _repair = do
8485
selectedHash <- if recursive then recursiveContentHash else flatContentHash
85-
pure $ makeFixedOutputPath "/nix/store" recursive selectedHash name
86+
pure $ makeFixedOutputPath storeDir recursive selectedHash name
8687
where
8788
recursiveContentHash :: IO (Digest SHA256)
8889
recursiveContentHash = hashFinalize <$> execStateT streamNarUpdate (hashInit @SHA256)

hnix-store-core/src/System/Nix/StorePath.hs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ Description : Representation of Nix store paths.
33
-}
44
module System.Nix.StorePath
55
( -- * Basic store path types
6-
StorePath(..)
6+
StoreDir(..)
7+
, StorePath(..)
78
, StorePathName(..)
89
, StorePathSet
910
, mkStorePathHashPart

hnix-store-core/tests/Arbitrary.hs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,23 +35,24 @@ instance Arbitrary StorePathHashPart where
3535
instance Arbitrary (Digest SHA256) where
3636
arbitrary = hash . BSC.pack <$> arbitrary
3737

38+
instance Arbitrary StoreDir where
39+
arbitrary = StoreDir . ("/" <>) . BSC.pack <$> arbitrary
40+
3841
newtype NixLike = NixLike {getNixLike :: StorePath}
3942
deriving (Eq, Ord, Show)
4043

4144
instance Arbitrary NixLike where
4245
arbitrary =
4346
NixLike <$>
44-
liftA3 StorePath
47+
liftA2 StorePath
4548
arbitraryTruncatedDigest
4649
arbitrary
47-
(pure "/nix/store")
4850
where
4951
-- 160-bit hash, 20 bytes, 32 chars in base32
5052
arbitraryTruncatedDigest = coerce . BSC.pack <$> replicateM 20 genSafeChar
5153

5254
instance Arbitrary StorePath where
5355
arbitrary =
54-
liftA3 StorePath
56+
liftA2 StorePath
5557
arbitrary
5658
arbitrary
57-
dir

hnix-store-core/tests/Derivation.hs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Test.Tasty ( TestTree
66
)
77
import Test.Tasty.Golden ( goldenVsFile )
88

9+
import System.Nix.StorePath ( StoreDir(..) )
910
import System.Nix.Derivation ( parseDerivation
1011
, buildDerivation
1112
)
@@ -23,10 +24,10 @@ processDerivation source dest = do
2324
(Data.Text.IO.writeFile dest
2425
. toText
2526
. Data.Text.Lazy.Builder.toLazyText
26-
. buildDerivation
27+
. buildDerivation (StoreDir "/nix/store")
2728
)
2829
(Data.Attoparsec.Text.parseOnly
29-
(parseDerivation "/nix/store")
30+
(parseDerivation $ StoreDir "/nix/store")
3031
contents
3132
)
3233

hnix-store-core/tests/StorePath.hs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{-# language DataKinds #-}
22
{-# language ScopedTypeVariables #-}
3+
{-# language OverloadedStrings #-}
34

45
module StorePath where
56

@@ -11,19 +12,19 @@ import System.Nix.StorePath
1112
import Arbitrary
1213

1314
-- | Test that Nix(OS) like paths roundtrip
14-
prop_storePathRoundtrip :: NixLike -> NixLike -> Property
15-
prop_storePathRoundtrip (_ :: NixLike) (NixLike x) =
16-
parsePath "/nix/store" (storePathToRawFilePath x) === pure x
15+
prop_storePathRoundtrip :: StoreDir -> NixLike -> NixLike -> Property
16+
prop_storePathRoundtrip storeDir (_ :: NixLike) (NixLike x) =
17+
parsePath storeDir (storePathToRawFilePath storeDir x) === pure x
1718

1819
-- | Test that any `StorePath` roundtrips
19-
prop_storePathRoundtrip' :: StorePath -> Property
20-
prop_storePathRoundtrip' x =
21-
parsePath (storePathRoot x) (storePathToRawFilePath x) === pure x
20+
prop_storePathRoundtrip' :: StoreDir -> StorePath -> Property
21+
prop_storePathRoundtrip' storeDir x =
22+
parsePath storeDir (storePathToRawFilePath storeDir x) === pure x
2223

23-
prop_storePathRoundtripParser :: NixLike -> NixLike -> Property
24-
prop_storePathRoundtripParser (_ :: NixLike) (NixLike x) =
25-
Data.Attoparsec.Text.parseOnly (pathParser $ storePathRoot x) (storePathToText x) === pure x
24+
prop_storePathRoundtripParser :: StoreDir -> NixLike -> NixLike -> Property
25+
prop_storePathRoundtripParser storeDir (_ :: NixLike) (NixLike x) =
26+
Data.Attoparsec.Text.parseOnly (pathParser storeDir) (storePathToText storeDir x) === pure x
2627

27-
prop_storePathRoundtripParser' :: StorePath -> Property
28-
prop_storePathRoundtripParser' x =
29-
Data.Attoparsec.Text.parseOnly (pathParser $ storePathRoot x) (storePathToText x) === pure x
28+
prop_storePathRoundtripParser' :: StoreDir -> StorePath -> Property
29+
prop_storePathRoundtripParser' storeDir x =
30+
Data.Attoparsec.Text.parseOnly (pathParser storeDir) (storePathToText storeDir x) === pure x

hnix-store-remote/hnix-store-remote.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ library
5353
, mtl
5454
, unordered-containers
5555
, hnix-store-core >= 0.6 && <0.7
56+
, transformers
5657
mixins:
5758
base hiding (Prelude)
5859
, relude (Relude as Prelude)
@@ -98,6 +99,7 @@ test-suite hnix-store-remote-tests
9899
tasty-discover:tasty-discover
99100
build-depends:
100101
base
102+
, bytestring
101103
, relude
102104
, hnix-store-core >= 0.3
103105
, hnix-store-remote

0 commit comments

Comments
 (0)