1 -----------------------------------------------------------------------------
3 -----------------------------------------------------------------------------
6 -- Module : Distribution.Client.Get
7 -- Copyright : (c) Andrea Vezzosi 2008
12 -- Maintainer : cabal-devel@haskell.org
13 -- Stability : provisional
14 -- Portability : portable
16 -- The 'cabal get' command.
17 module Distribution
.Client
.Get
20 -- * Cloning 'SourceRepo's
22 -- | Mainly exported for testing purposes
23 , clonePackagesFromSourceRepo
24 , ClonePackageException
(..)
27 import Distribution
.Client
.Compat
.Prelude
hiding (get
)
28 import Distribution
.Client
.Types
.SourceRepo
(SourceRepoProxy
, SourceRepositoryPackage
(..), srpToProxy
)
29 import Distribution
.Compat
.Directory
32 import Distribution
.Package
37 import qualified Distribution
.PackageDescription
as PD
38 import Distribution
.Simple
.Program
41 import Distribution
.Simple
.Setup
47 import Distribution
.Simple
.Utils
54 import Distribution
.Types
.SourceRepo
(RepoKind
(..))
57 import Distribution
.Client
.Dependency
58 import Distribution
.Client
.FetchUtils
59 import Distribution
.Client
.IndexUtils
62 , getSourcePackagesAtIndexState
64 import Distribution
.Client
.Setup
69 import qualified Distribution
.Client
.Tar
as Tar
(extractTarGzFile
)
70 import Distribution
.Client
.Targets
71 import Distribution
.Client
.Types
72 import Distribution
.Client
.VCS
73 import Distribution
.PackageDescription
.PrettyPrint
74 ( writeGenericPackageDescription
76 import Distribution
.Solver
.Types
.SourcePackage
78 import Control
.Monad
(mapM_)
79 import qualified Data
.Map
as Map
80 import Distribution
.Client
.Errors
81 import Distribution
.Utils
.NubList
84 import System
.Directory
85 ( createDirectoryIfMissing
89 import System
.FilePath
90 ( addTrailingPathSeparator
95 -- | Entry point for the 'cabal get' command.
103 get verbosity _ _ _
[] =
104 notice verbosity
"No packages requested. Nothing to do."
105 get verbosity repoCtxt globalFlags getFlags userTargets
= do
106 let useSourceRepo
= case getSourceRepository getFlags
of
110 unless useSourceRepo
$
111 traverse_
(checkTarget verbosity
) userTargets
113 let idxState
:: Maybe TotalIndexState
114 idxState
= flagToMaybe
$ getIndexState getFlags
116 activeRepos
:: Maybe ActiveRepos
117 activeRepos
= flagToMaybe
$ getActiveRepos getFlags
119 (sourcePkgDb
, _
, _
) <- getSourcePackagesAtIndexState verbosity repoCtxt idxState activeRepos
125 (packageIndex sourcePkgDb
)
129 either (dieWithException verbosity
. PkgSpecifierException
. map show) return $
130 resolveWithoutDependencies
131 (resolverParams sourcePkgDb pkgSpecifiers
)
133 unless (null prefix
) $
134 createDirectoryIfMissing
True prefix
140 "Ignoring --source-repository for --only-package-description"
142 mapM_ (unpackOnlyPkgDescr verbosity prefix
) pkgs
148 resolverParams
:: SourcePackageDb
-> [PackageSpecifier UnresolvedSourcePackage
] -> DepResolverParams
149 resolverParams sourcePkgDb pkgSpecifiers
=
150 -- TODO: add command-line constraint and preference args for unpack
151 standardInstallPolicy mempty sourcePkgDb pkgSpecifiers
153 onlyPkgDescr
= fromFlagOrDefault
False (getOnlyPkgDescr getFlags
)
156 prefix
= fromFlagOrDefault
"" (getDestDir getFlags
)
158 clone
:: [UnresolvedSourcePackage
] -> IO ()
160 clonePackagesFromSourceRepo verbosity prefix kind
(fromNubList
$ globalProgPathExtra globalFlags
)
161 . map (\pkg
-> (packageId pkg
, packageSourceRepos pkg
))
163 kind
:: Maybe RepoKind
164 kind
= fromFlag
. getSourceRepository
$ getFlags
165 packageSourceRepos
:: SourcePackage loc
-> [PD
.SourceRepo
]
168 . PD
.packageDescription
171 unpack
:: [UnresolvedSourcePackage
] -> IO ()
173 for_ pkgs
$ \pkg
-> do
174 location
<- fetchPackage verbosity repoCtxt
(srcpkgSource pkg
)
175 let pkgid
= packageId pkg
177 | usePristine
= Nothing
178 |
otherwise = srcpkgDescrOverride pkg
180 LocalTarballPackage tarballPath
->
181 unpackPackage verbosity prefix pkgid descOverride tarballPath
182 RemoteTarballPackage _tarballURL tarballPath
->
183 unpackPackage verbosity prefix pkgid descOverride tarballPath
184 RepoTarballPackage _repo _pkgid tarballPath
->
185 unpackPackage verbosity prefix pkgid descOverride tarballPath
186 RemoteSourceRepoPackage _repo _
->
187 dieWithException verbosity UnpackGet
188 LocalUnpackedPackage _
->
189 error "Distribution.Client.Get.unpack: the impossible happened."
192 usePristine
= fromFlagOrDefault
False (getPristine getFlags
)
194 checkTarget
:: Verbosity
-> UserTarget
-> IO ()
195 checkTarget verbosity target
= case target
of
196 UserTargetLocalDir dir
-> dieWithException verbosity
$ NotTarballDir dir
197 UserTargetLocalCabalFile file
-> dieWithException verbosity
$ NotTarballDir file
202 "The 'get' command is for tarball packages. "
205 ++ "' is not a tarball."
207 -- ------------------------------------------------------------
209 -- * Unpacking the source tarball
211 -- ------------------------------------------------------------
217 -> PackageDescriptionOverride
220 unpackPackage verbosity prefix pkgid descOverride pkgPath
= do
221 let pkgdirname
= prettyShow pkgid
222 pkgdir
= prefix
</> pkgdirname
223 pkgdir
' = addTrailingPathSeparator pkgdir
224 emptyDirectory directory
= null <$> listDirectory directory
225 existsDir
<- doesDirectoryExist pkgdir
227 isEmpty
<- emptyDirectory pkgdir
229 dieWithException verbosity
$
230 DirectoryAlreadyExists pkgdir
'
231 existsFile
<- doesFileExist pkgdir
233 dieWithException verbosity
$
235 notice verbosity
$ "Unpacking to " ++ pkgdir
'
236 Tar
.extractTarGzFile prefix pkgdirname pkgPath
241 let descFilePath
= pkgdir
</> prettyShow
(packageName pkgid
) <.> "cabal"
245 ++ " with the latest revision from the index."
246 writeFileAtomic descFilePath pkgtxt
248 -- | Write a @pkgId.cabal@ file with the package description to the destination
249 -- directory, unless one already exists.
250 unpackOnlyPkgDescr
:: Verbosity
-> FilePath -> UnresolvedSourcePackage
-> IO ()
251 unpackOnlyPkgDescr verbosity dstDir pkg
= do
252 let pkgFile
= dstDir
</> prettyShow
(packageId pkg
) <.> "cabal"
253 existsFile
<- doesFileExist pkgFile
255 dieWithException verbosity
$
256 FileAlreadyExists pkgFile
257 existsDir
<- doesDirectoryExist (addTrailingPathSeparator pkgFile
)
259 dieWithException verbosity
$
260 DirectoryExists pkgFile
261 notice verbosity
$ "Writing package description to " ++ pkgFile
262 case srcpkgDescrOverride pkg
of
263 Just pkgTxt
-> writeFileAtomic pkgFile pkgTxt
265 writeGenericPackageDescription pkgFile
(srcpkgDescription pkg
)
267 -- ------------------------------------------------------------
269 -- * Cloning packages from their declared source repositories
271 -- ------------------------------------------------------------
273 data ClonePackageException
274 = ClonePackageNoSourceRepos PackageId
275 | ClonePackageNoSourceReposOfKind PackageId
(Maybe RepoKind
)
276 | ClonePackageNoRepoType PackageId PD
.SourceRepo
277 | ClonePackageUnsupportedRepoType PackageId SourceRepoProxy RepoType
278 | ClonePackageNoRepoLocation PackageId PD
.SourceRepo
279 | ClonePackageDestinationExists PackageId
FilePath Bool
280 | ClonePackageFailedWithExitCode PackageId SourceRepoProxy
String ExitCode
283 instance Exception ClonePackageException
where
284 displayException
(ClonePackageNoSourceRepos pkgid
) =
285 "Cannot fetch a source repository for package "
287 ++ ". The package does not specify any source repositories."
288 displayException
(ClonePackageNoSourceReposOfKind pkgid repoKind
) =
289 "Cannot fetch a source repository for package "
291 ++ ". The package does not specify a source repository of the requested "
293 ++ maybe "." (\k
-> " (kind " ++ prettyShow k
++ ").") repoKind
294 displayException
(ClonePackageNoRepoType pkgid _repo
) =
295 "Cannot fetch the source repository for package "
297 ++ ". The package's description specifies a source repository but does "
298 ++ "not specify the repository 'type' field (e.g. git, darcs or hg)."
299 displayException
(ClonePackageUnsupportedRepoType pkgid _ repoType
) =
300 "Cannot fetch the source repository for package "
302 ++ ". The repository type '"
303 ++ prettyShow repoType
304 ++ "' is not yet supported."
305 displayException
(ClonePackageNoRepoLocation pkgid _repo
) =
306 "Cannot fetch the source repository for package "
308 ++ ". The package's description specifies a source repository but does "
309 ++ "not specify the repository 'location' field (i.e. the URL)."
310 displayException
(ClonePackageDestinationExists pkgid dest isdir
) =
311 "Not fetching the source repository for package "
315 then "The destination directory " ++ dest
++ " already exists."
316 else "A file " ++ dest
++ " is in the way."
318 ( ClonePackageFailedWithExitCode
324 "Failed to fetch the source repository for package "
326 ++ ", repository location "
334 -- | Given a bunch of package ids and their corresponding available
335 -- 'SourceRepo's, pick a single 'SourceRepo' for each one and clone into
336 -- new subdirs of the given directory.
337 clonePackagesFromSourceRepo
340 -- ^ destination dir prefix
342 -- ^ preferred 'RepoKind'
344 -- ^ Extra prog paths
345 -> [(PackageId
, [PD
.SourceRepo
])]
346 -- ^ the packages and their
347 -- available 'SourceRepo's
349 clonePackagesFromSourceRepo
355 -- Do a bunch of checks and collect the required info
356 pkgrepos
' <- traverse preCloneChecks pkgrepos
358 -- Configure the VCS drivers for all the repository types we may need
360 configureVCSs verbosity progPaths
$
362 [ (vcsRepoType vcs
, vcs
)
363 |
(_
, _
, vcs
, _
) <- pkgrepos
'
366 -- Now execute all the required commands for each repo
368 [ cloneSourceRepo verbosity vcs
' repo destDir
371 ( ClonePackageFailedWithExitCode
374 (programName
(vcsProgram vcs
))
377 |
(pkgid
, repo
, vcs
, destDir
) <- pkgrepos
'
378 , let vcs
' = Map
.findWithDefault
(error $ "Cannot configure " ++ prettyShow
(vcsRepoType vcs
)) (vcsRepoType vcs
) vcss
382 :: (PackageId
, [PD
.SourceRepo
])
383 -> IO (PackageId
, SourceRepositoryPackage
Maybe, VCS Program
, FilePath)
384 preCloneChecks
(pkgid
, repos
) = do
385 repo
<- case selectPackageSourceRepo preferredRepoKind repos
of
386 Just repo
-> return repo
387 Nothing |
null repos
-> throwIO
(ClonePackageNoSourceRepos pkgid
)
390 ( ClonePackageNoSourceReposOfKind
395 (repo
', vcs
) <- case validatePDSourceRepo repo
of
396 Right
(repo
', _
, _
, vcs
) -> return (repo
', vcs
)
397 Left SourceRepoRepoTypeUnspecified
->
398 throwIO
(ClonePackageNoRepoType pkgid repo
)
399 Left
(SourceRepoRepoTypeUnsupported repo
' repoType
) ->
400 throwIO
(ClonePackageUnsupportedRepoType pkgid repo
' repoType
)
401 Left SourceRepoLocationUnspecified
->
402 throwIO
(ClonePackageNoRepoLocation pkgid repo
)
404 let destDir
:: FilePath
405 destDir
= destDirPrefix
</> prettyShow
(packageName pkgid
)
406 destDirExists
<- doesDirectoryExist destDir
407 destFileExists
<- doesFileExist destDir
408 when (destDirExists || destFileExists
) $
409 throwIO
(ClonePackageDestinationExists pkgid destDir destDirExists
)
411 return (pkgid
, repo
', vcs
, destDir
)
413 -------------------------------------------------------------------------------
415 -------------------------------------------------------------------------------
417 -- | Pick the 'SourceRepo' to use to get the package sources from.
419 -- Note that this does /not/ depend on what 'VCS' drivers we are able to
420 -- successfully configure. It is based only on the 'SourceRepo's declared
421 -- in the package, and optionally on a preferred 'RepoKind'.
422 selectPackageSourceRepo
425 -> Maybe PD
.SourceRepo
426 selectPackageSourceRepo preferredRepoKind
=
428 -- Sort repositories by kind, from This to Head to Unknown. Repositories
429 -- with equivalent kinds are selected based on the order they appear in
430 -- the Cabal description file.
431 . sortBy (comparing thisFirst
)
432 -- If the user has specified the repo kind, filter out the repositories
433 -- they're not interested in.
434 . filter (\repo
-> maybe True (PD
.repoKind repo
==) preferredRepoKind
)
436 thisFirst
:: PD
.SourceRepo
-> Int
437 thisFirst r
= case PD
.repoKind r
of
439 RepoHead
-> case PD
.repoTag r
of
440 -- If the type is 'head' but the author specified a tag, they
441 -- probably meant to create a 'this' repository but screwed up.
444 RepoKindUnknown _
-> 2