1{-# LANGUAGE OverloadedStrings #-}
2{-# LANGUAGE RecordWildCards #-}
3{-# LANGUAGE ScopedTypeVariables #-}
4{-# LANGUAGE DeriveGeneric #-}
6module Distribution.Nixpkgs.Fetch
9 , DerivationSource(..), fromDerivationSource
14import Control.Applicative
17import Control.Monad.IO.Class
18import Control.Monad.Trans.Maybe
20import qualified Data.ByteString.Lazy.Char8 as BS
21import GHC.Generics ( Generic )
22import System.Directory
23import System.Environment
28-- | A source is a location from which we can fetch, such as a HTTP URL, a GIT URL, ....
30 { sourceUrl :: String -- ^ URL to fetch from.
31 , sourceRevision :: String -- ^ Revision to use. For protocols where this doesn't make sense (such as HTTP), this
32 -- should be the empty string.
33 , sourceHash :: Hash -- ^ The expected hash of the source, if available.
34 , sourceCabalDir :: String -- ^ Directory where Cabal file is found.
35 } deriving (Show, Eq, Ord, Generic)
39data Hash = Certain String | Guess String | UnknownHash
40 deriving (Show, Eq, Ord, Generic)
44isUnknown :: Hash -> Bool
45isUnknown UnknownHash = True
48hashToList :: Hash -> [String]
49hashToList (Certain s) = [s]
52-- | A source for a derivation. It always needs a hash and also has a protocol attached to it (url, git, svn, ...).
53-- A @DerivationSource@ also always has it's revision fully resolved (not relative revisions like @master@, @HEAD@, etc).
54data DerivationSource = DerivationSource
55 { derivKind :: String -- ^ The kind of the source. The name of the build-support fetch derivation should be fetch<kind>.
56 , derivUrl :: String -- ^ URL to fetch from.
57 , derivRevision :: String -- ^ Revision to use. Leave empty if the fetcher doesn't support revisions.
58 , derivHash :: String -- ^ The hash of the source.
60 deriving (Show, Eq, Ord, Generic)
62instance NFData DerivationSource
64instance FromJSON DerivationSource where
65 parseJSON (Object o) = DerivationSource (error "undefined DerivationSource.kind")
69 parseJSON _ = error "invalid DerivationSource"
71fromDerivationSource :: DerivationSource -> Source
72fromDerivationSource DerivationSource{..} = Source derivUrl derivRevision (Certain derivHash) "."
74-- | Fetch a source, trying any of the various nix-prefetch-* scripts.
75fetch :: forall a. (String -> MaybeT IO a) -- ^ This function is passed the output path name as an argument.
76 -- It should return 'Nothing' if the file doesn't match the expected format.
77 -- This is required, because we cannot always check if a download succeeded otherwise.
78 -> Source -- ^ The source to fetch from.
79 -> IO (Maybe (DerivationSource, a)) -- ^ The derivation source and the result of the processing function. Returns Nothing if the download failed.
80fetch f = runMaybeT . fetchers where
81 fetchers :: Source -> MaybeT IO (DerivationSource, a)
82 fetchers source = msum . (fetchLocal source :) $ map (\fetcher -> fetchWith fetcher source >>= process)
84 , (True, "git", ["--fetch-submodules"])
90 -- | Remove '/' from the end of the path. Nix doesn't accept paths that
92 stripSlashSuffix :: String -> String
93 stripSlashSuffix = reverse . dropWhile (== '/') . reverse
95 fetchLocal :: Source -> MaybeT IO (DerivationSource, a)
97 let path = stripSlashSuffix $ stripPrefix "file://" $ sourceUrl src
98 existsFile <- liftIO $ doesFileExist path
99 existsDir <- liftIO $ doesDirectoryExist path
100 guard $ existsDir || existsFile
101 let path' | '/' `elem` path = path
102 | otherwise = "./" ++ path
103 process (DerivationSource "" path' "" "", path') <|> localArchive path'
105 localArchive :: FilePath -> MaybeT IO (DerivationSource, a)
106 localArchive path = do
107 absolutePath <- liftIO $ canonicalizePath path
108 unpacked <- snd <$> fetchWith (False, "url", ["--unpack"]) (Source ("file://" ++ absolutePath) "" UnknownHash ".")
109 process (DerivationSource "" absolutePath "" "", unpacked)
111 process :: (DerivationSource, FilePath) -> MaybeT IO (DerivationSource, a)
112 process (derivSource, file) = (,) derivSource <$> f file
114-- | Like 'fetch', but allows to specify which script to use.
115fetchWith :: (Bool, String, [String]) -> Source -> MaybeT IO (DerivationSource, FilePath)
116fetchWith (supportsRev, kind, addArgs) source = do
117 unless ((sourceRevision source /= "") || isUnknown (sourceHash source) || not supportsRev) $
118 liftIO (hPutStrLn stderr "** need a revision for VCS when the hash is given. skipping.") >> mzero
121 envs <- getEnvironment
122 (Nothing, Just stdoutH, _, processH) <- createProcess (proc script args)
123 { env = Just $ ("PRINT_PATH", "1") : envs
126 , std_out = CreatePipe
129 exitCode <- waitForProcess processH
131 ExitFailure _ -> return Nothing
133 buf <- BS.hGetContents stdoutH
134 let (l:ls) = reverse (BS.lines buf)
135 buf' = BS.unlines (reverse ls)
138 1 -> return (Just (DerivationSource kind (sourceUrl source) "" (BS.unpack (head ls)) , sourceUrl source))
139 _ -> case eitherDecode buf' of
140 Left err -> error ("invalid JSON: " ++ err ++ " in " ++ show buf')
141 Right ds -> return (Just (ds { derivKind = kind }, BS.unpack l))
145 script = "nix-prefetch-" ++ kind
148 args = addArgs ++ sourceUrl source : [ sourceRevision source | supportsRev ] ++ hashToList (sourceHash source)
150stripPrefix :: Eq a => [a] -> [a] -> [a]
152 | prefix' == prefix = stripped
155 (prefix', stripped) = splitAt (length prefix) as
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list