2 {-# LANGUAGE FlexibleContexts #-}
3 {-# LANGUAGE OverloadedStrings #-}
4 {-# LANGUAGE RankNTypes #-}
6 -----------------------------------------------------------------------------
9 -- Module : Distribution.Simple.Program.HcPkg
10 -- Copyright : Duncan Coutts 2009, 2013
12 -- Maintainer : cabal-devel@haskell.org
13 -- Portability : portable
15 -- This module provides an library interface to the @hc-pkg@ program.
16 -- Currently only GHC and GHCJS have hc-pkg programs.
17 module Distribution
.Simple
.Program
.HcPkg
20 , RegisterOptions
(..)
21 , defaultRegisterOptions
35 -- * Program invocations
38 , unregisterInvocation
47 import Distribution
.Compat
.Prelude
hiding (init)
50 import Distribution
.InstalledPackageInfo
51 import Distribution
.Parsec
52 import Distribution
.Pretty
53 import Distribution
.Simple
.Compiler
54 import Distribution
.Simple
.Errors
55 import Distribution
.Simple
.Program
.Run
56 import Distribution
.Simple
.Program
.Types
57 import Distribution
.Simple
.Utils
58 import Distribution
.Types
.ComponentId
59 import Distribution
.Types
.PackageId
60 import Distribution
.Types
.UnitId
61 import Distribution
.Verbosity
63 import Data
.List
(stripPrefix
)
64 import System
.FilePath as FilePath (isPathSeparator
, joinPath
, splitDirectories
, splitPath
, (<.>), (</>))
66 import qualified Data
.ByteString
as BS
67 import qualified Data
.ByteString
.Lazy
as LBS
68 import qualified Data
.List
.NonEmpty
as NE
69 import qualified System
.FilePath.Posix
as FilePath.Posix
71 -- | Information about the features and capabilities of an @hc-pkg@
73 data HcPkgInfo
= HcPkgInfo
74 { hcPkgProgram
:: ConfiguredProgram
75 , noPkgDbStack
:: Bool
76 -- ^ no package DB stack supported
77 , noVerboseFlag
:: Bool
78 -- ^ hc-pkg does not support verbosity flags
79 , flagPackageConf
:: Bool
80 -- ^ use package-conf option instead of package-db
81 , supportsDirDbs
:: Bool
82 -- ^ supports directory style package databases
83 , requiresDirDbs
:: Bool
84 -- ^ requires directory style package databases
85 , nativeMultiInstance
:: Bool
86 -- ^ supports --enable-multi-instance flag
87 , recacheMultiInstance
:: Bool
88 -- ^ supports multi-instance via recache
89 , suppressFilesCheck
:: Bool
90 -- ^ supports --force-files or equivalent
93 -- | Call @hc-pkg@ to initialise a package database at the location {path}.
95 -- > hc-pkg init {path}
96 init :: HcPkgInfo
-> Verbosity
-> Bool -> FilePath -> IO ()
97 init hpi verbosity preferCompat path
98 |
not (supportsDirDbs hpi
)
99 ||
(not (requiresDirDbs hpi
) && preferCompat
) =
102 runProgramInvocation verbosity
(initInvocation hpi verbosity path
)
104 -- | Run @hc-pkg@ using a given package DB stack, directly forwarding the
105 -- provided command-line arguments to it.
106 invoke
:: HcPkgInfo
-> Verbosity
-> PackageDBStack
-> [String] -> IO ()
107 invoke hpi verbosity dbStack extraArgs
=
108 runProgramInvocation verbosity invocation
110 args
= packageDbStackOpts hpi dbStack
++ extraArgs
111 invocation
= programInvocation
(hcPkgProgram hpi
) args
113 -- | Additional variations in the behaviour for 'register'.
114 data RegisterOptions
= RegisterOptions
115 { registerAllowOverwrite
:: Bool
116 -- ^ Allows re-registering \/ overwriting an existing package
117 , registerMultiInstance
:: Bool
118 -- ^ Insist on the ability to register multiple instances of a
119 -- single version of a single package. This will fail if the @hc-pkg@
120 -- does not support it, see 'nativeMultiInstance' and
121 -- 'recacheMultiInstance'.
122 , registerSuppressFilesCheck
:: Bool
123 -- ^ Require that no checks are performed on the existence of package
124 -- files mentioned in the registration info. This must be used if
125 -- registering prior to putting the files in their final place. This will
126 -- fail if the @hc-pkg@ does not support it, see 'suppressFilesCheck'.
129 -- | Defaults are @True@, @False@ and @False@
130 defaultRegisterOptions
:: RegisterOptions
131 defaultRegisterOptions
=
133 { registerAllowOverwrite
= True
134 , registerMultiInstance
= False
135 , registerSuppressFilesCheck
= False
138 -- | Call @hc-pkg@ to register a package.
140 -- > hc-pkg register {filename | -} [--user | --global | --package-db]
145 -> InstalledPackageInfo
148 register hpi verbosity packagedbs pkgInfo registerOptions
149 | registerMultiInstance registerOptions
150 , not (nativeMultiInstance hpi || recacheMultiInstance hpi
) =
151 dieWithException verbosity RegMultipleInstancePkg
152 | registerSuppressFilesCheck registerOptions
153 , not (suppressFilesCheck hpi
) =
154 dieWithException verbosity SuppressingChecksOnFile
155 -- This is a trick. Older versions of GHC do not support the
156 -- --enable-multi-instance flag for ghc-pkg register but it turns out that
157 -- the same ability is available by using ghc-pkg recache. The recache
158 -- command is there to support distro package managers that like to work
159 -- by just installing files and running update commands, rather than
160 -- special add/remove commands. So the way to register by this method is
161 -- to write the package registration file directly into the package db and
162 -- then call hc-pkg recache.
164 | registerMultiInstance registerOptions
165 , recacheMultiInstance hpi
=
167 let pkgdb
= registrationPackageDB packagedbs
168 writeRegistrationFileDirectly verbosity hpi pkgdb pkgInfo
169 recache hpi verbosity pkgdb
173 (registerInvocation hpi verbosity packagedbs pkgInfo registerOptions
)
175 writeRegistrationFileDirectly
179 -> InstalledPackageInfo
181 writeRegistrationFileDirectly verbosity hpi
(SpecificPackageDB dir
) pkgInfo
182 | supportsDirDbs hpi
=
184 let pkgfile
= dir
</> prettyShow
(installedUnitId pkgInfo
) <.> "conf"
185 writeUTF8File pkgfile
(showInstalledPackageInfo pkgInfo
)
187 dieWithException verbosity NoSupportDirStylePackageDb
188 writeRegistrationFileDirectly verbosity _ _ _
=
189 -- We don't know here what the dir for the global or user dbs are,
190 -- if that's needed it'll require a bit more plumbing to support.
191 dieWithException verbosity OnlySupportSpecificPackageDb
193 -- | Call @hc-pkg@ to unregister a package
195 -- > hc-pkg unregister [pkgid] [--user | --global | --package-db]
196 unregister
:: HcPkgInfo
-> Verbosity
-> PackageDB
-> PackageId
-> IO ()
197 unregister hpi verbosity packagedb pkgid
=
200 (unregisterInvocation hpi verbosity packagedb pkgid
)
202 -- | Call @hc-pkg@ to recache the registered packages.
204 -- > hc-pkg recache [--user | --global | --package-db]
205 recache
:: HcPkgInfo
-> Verbosity
-> PackageDB
-> IO ()
206 recache hpi verbosity packagedb
=
209 (recacheInvocation hpi verbosity packagedb
)
211 -- | Call @hc-pkg@ to expose a package.
213 -- > hc-pkg expose [pkgid] [--user | --global | --package-db]
214 expose
:: HcPkgInfo
-> Verbosity
-> PackageDB
-> PackageId
-> IO ()
215 expose hpi verbosity packagedb pkgid
=
218 (exposeInvocation hpi verbosity packagedb pkgid
)
220 -- | Call @hc-pkg@ to retrieve a specific package
222 -- > hc-pkg describe [pkgid] [--user | --global | --package-db]
223 describe
:: HcPkgInfo
-> Verbosity
-> PackageDBStack
-> PackageId
-> IO [InstalledPackageInfo
]
224 describe hpi verbosity packagedb pid
= do
226 getProgramInvocationLBS
228 (describeInvocation hpi verbosity packagedb pid
)
229 `catchIO`
\_
-> return mempty
231 case parsePackages output
of
233 _
-> dieWithException verbosity
$ FailedToParseOutputDescribe
(programId
(hcPkgProgram hpi
)) pid
235 -- | Call @hc-pkg@ to hide a package.
237 -- > hc-pkg hide [pkgid] [--user | --global | --package-db]
238 hide
:: HcPkgInfo
-> Verbosity
-> PackageDB
-> PackageId
-> IO ()
239 hide hpi verbosity packagedb pkgid
=
242 (hideInvocation hpi verbosity packagedb pkgid
)
244 -- | Call @hc-pkg@ to get all the details of all the packages in the given
246 dump
:: HcPkgInfo
-> Verbosity
-> PackageDB
-> IO [InstalledPackageInfo
]
247 dump hpi verbosity packagedb
= do
249 getProgramInvocationLBS
251 (dumpInvocation hpi verbosity packagedb
)
253 dieWithException verbosity
$ DumpFailed
(programId
(hcPkgProgram hpi
)) (displayException e
)
255 case parsePackages output
of
257 _
-> dieWithException verbosity
$ FailedToParseOutputDump
(programId
(hcPkgProgram hpi
))
259 parsePackages
:: LBS
.ByteString
-> Either [InstalledPackageInfo
] [String]
261 case traverse parseInstalledPackageInfo
$ splitPkgs lbs0
of
262 Right ok
-> Left
[setUnitId
. maybe id mungePackagePaths
(pkgRoot pkg
) $ pkg |
(_
, pkg
) <- ok
]
263 Left msgs
-> Right
(NE
.toList msgs
)
265 splitPkgs
:: LBS
.ByteString
-> [BS
.ByteString
]
266 splitPkgs
= checkEmpty
. doSplit
268 -- Handle the case of there being no packages at all.
269 checkEmpty
[s
] | BS
.all isSpace8 s
= []
272 isSpace8
:: Word8
-> Bool
273 isSpace8
9 = True -- '\t'
274 isSpace8
10 = True -- '\n'
275 isSpace8
13 = True -- '\r'
276 isSpace8
32 = True -- ' '
279 doSplit
:: LBS
.ByteString
-> [BS
.ByteString
]
280 doSplit lbs
= go
(LBS
.findIndices (\w
-> w
== 10 || w
== 13) lbs
)
282 go
:: [Int64
] -> [BS
.ByteString
]
283 go
[] = [LBS
.toStrict lbs
]
285 let (pfx
, sfx
) = LBS
.splitAt idx lbs
286 in case foldr (<|
>) Nothing
$ map (`lbsStripPrefix` sfx
) separators
of
287 Just sfx
' -> LBS
.toStrict pfx
: doSplit sfx
'
290 separators
:: [LBS
.ByteString
]
291 separators
= ["\n---\n", "\r\n---\r\n", "\r---\r"]
293 lbsStripPrefix
:: LBS
.ByteString
-> LBS
.ByteString
-> Maybe LBS
.ByteString
294 #if MIN_VERSION_bytestring
(0,10,8)
295 lbsStripPrefix pfx lbs
= LBS
.stripPrefix pfx lbs
297 lbsStripPrefix pfx lbs
298 | LBS
.isPrefixOf pfx lbs
= Just
(LBS
.drop (LBS
.length pfx
) lbs
)
299 |
otherwise = Nothing
302 mungePackagePaths
:: FilePath -> InstalledPackageInfo
-> InstalledPackageInfo
303 -- Perform path/URL variable substitution as per the Cabal ${pkgroot} spec
304 -- (http://www.haskell.org/pipermail/libraries/2009-May/011772.html)
305 -- Paths/URLs can be relative to ${pkgroot} or ${pkgrooturl}.
306 -- The "pkgroot" is the directory containing the package database.
307 mungePackagePaths pkgroot pkginfo
=
309 { importDirs
= mungePaths
(importDirs pkginfo
)
310 , includeDirs
= mungePaths
(includeDirs pkginfo
)
311 , libraryDirs
= mungePaths
(libraryDirs pkginfo
)
312 , libraryDirsStatic
= mungePaths
(libraryDirsStatic pkginfo
)
313 , libraryDynDirs
= mungePaths
(libraryDynDirs pkginfo
)
314 , frameworkDirs
= mungePaths
(frameworkDirs pkginfo
)
315 , haddockInterfaces
= mungePaths
(haddockInterfaces pkginfo
)
316 , haddockHTMLs
= mungeUrls
(haddockHTMLs pkginfo
)
319 mungePaths
= map mungePath
320 mungeUrls
= map mungeUrl
322 mungePath p
= case stripVarPrefix
"${pkgroot}" p
of
323 Just p
' -> pkgroot
</> p
'
326 mungeUrl p
= case stripVarPrefix
"${pkgrooturl}" p
of
327 Just p
' -> toUrlPath pkgroot p
'
332 -- URLs always use posix style '/' separators:
333 ++ FilePath.Posix
.joinPath
(r
: FilePath.splitDirectories p
)
335 stripVarPrefix var p
=
337 (root
: path
') -> case stripPrefix var root
of
338 Just
[sep
] | isPathSeparator sep
-> Just
(joinPath path
')
342 -- Older installed package info files did not have the installedUnitId
343 -- field, so if it is missing then we fill it as the source package ID.
344 -- NB: Internal libraries not supported.
345 setUnitId
:: InstalledPackageInfo
-> InstalledPackageInfo
347 pkginfo
@InstalledPackageInfo
348 { installedUnitId
= uid
349 , sourcePackageId
= pid
351 | unUnitId uid
== "" =
353 { installedUnitId
= mkLegacyUnitId pid
354 , installedComponentId_
= mkComponentId
(prettyShow pid
)
356 setUnitId pkginfo
= pkginfo
358 -- | Call @hc-pkg@ to get the source package Id of all the packages in the
359 -- given package database.
361 -- This is much less information than with 'dump', but also rather quicker.
362 -- Note in particular that it does not include the 'UnitId', just
363 -- the source 'PackageId' which is not necessarily unique in any package db.
369 list hpi verbosity packagedb
= do
371 getProgramInvocationOutput
373 (listInvocation hpi verbosity packagedb
)
374 `catchIO`
\_
-> dieWithException verbosity
$ ListFailed
(programId
(hcPkgProgram hpi
))
376 case parsePackageIds output
of
378 _
-> dieWithException verbosity
$ FailedToParseOutputList
(programId
(hcPkgProgram hpi
))
380 parsePackageIds
= traverse simpleParsec
. words
382 --------------------------
383 -- The program invocations
386 initInvocation
:: HcPkgInfo
-> Verbosity
-> FilePath -> ProgramInvocation
387 initInvocation hpi verbosity path
=
388 programInvocation
(hcPkgProgram hpi
) args
392 ++ verbosityOpts hpi verbosity
398 -> InstalledPackageInfo
401 registerInvocation hpi verbosity packagedbs pkgInfo registerOptions
=
402 (programInvocation
(hcPkgProgram hpi
) (args
"-"))
403 { progInvokeInput
= Just
$ IODataText
$ showInstalledPackageInfo pkgInfo
404 , progInvokeInputEncoding
= IOEncodingUTF8
408 | registerAllowOverwrite registerOptions
= "update"
409 | registerMultiInstance registerOptions
= "update"
410 |
otherwise = "register"
414 ++ packageDbStackOpts hpi packagedbs
415 ++ [ "--enable-multi-instance"
416 | registerMultiInstance registerOptions
419 | registerSuppressFilesCheck registerOptions
421 ++ verbosityOpts hpi verbosity
429 unregisterInvocation hpi verbosity packagedb pkgid
=
430 programInvocation
(hcPkgProgram hpi
) $
431 ["unregister", packageDbOpts hpi packagedb
, prettyShow pkgid
]
432 ++ verbosityOpts hpi verbosity
439 recacheInvocation hpi verbosity packagedb
=
440 programInvocation
(hcPkgProgram hpi
) $
441 ["recache", packageDbOpts hpi packagedb
]
442 ++ verbosityOpts hpi verbosity
450 exposeInvocation hpi verbosity packagedb pkgid
=
451 programInvocation
(hcPkgProgram hpi
) $
452 ["expose", packageDbOpts hpi packagedb
, prettyShow pkgid
]
453 ++ verbosityOpts hpi verbosity
461 describeInvocation hpi verbosity packagedbs pkgid
=
462 programInvocation
(hcPkgProgram hpi
) $
463 ["describe", prettyShow pkgid
]
464 ++ packageDbStackOpts hpi packagedbs
465 ++ verbosityOpts hpi verbosity
473 hideInvocation hpi verbosity packagedb pkgid
=
474 programInvocation
(hcPkgProgram hpi
) $
475 ["hide", packageDbOpts hpi packagedb
, prettyShow pkgid
]
476 ++ verbosityOpts hpi verbosity
478 dumpInvocation
:: HcPkgInfo
-> Verbosity
-> PackageDB
-> ProgramInvocation
479 dumpInvocation hpi _verbosity packagedb
=
480 (programInvocation
(hcPkgProgram hpi
) args
)
481 { progInvokeOutputEncoding
= IOEncodingUTF8
485 ["dump", packageDbOpts hpi packagedb
]
486 ++ verbosityOpts hpi silent
488 -- We use verbosity level 'silent' because it is important that we
489 -- do not contaminate the output with info/debug messages.
491 listInvocation
:: HcPkgInfo
-> Verbosity
-> PackageDB
-> ProgramInvocation
492 listInvocation hpi _verbosity packagedb
=
493 (programInvocation
(hcPkgProgram hpi
) args
)
494 { progInvokeOutputEncoding
= IOEncodingUTF8
498 ["list", "--simple-output", packageDbOpts hpi packagedb
]
499 ++ verbosityOpts hpi silent
501 -- We use verbosity level 'silent' because it is important that we
502 -- do not contaminate the output with info/debug messages.
504 packageDbStackOpts
:: HcPkgInfo
-> PackageDBStack
-> [String]
505 packageDbStackOpts hpi dbstack
506 | noPkgDbStack hpi
= [packageDbOpts hpi
(registrationPackageDB dbstack
)]
507 |
otherwise = case dbstack
of
508 (GlobalPackageDB
: UserPackageDB
: dbs
) ->
512 (GlobalPackageDB
: dbs
) ->
514 : ("--no-user-" ++ packageDbFlag hpi
)
518 specific
(SpecificPackageDB db
) = "--" ++ packageDbFlag hpi
++ "=" ++ db
521 ierror
= error ("internal error: unexpected package db stack: " ++ show dbstack
)
523 packageDbFlag
:: HcPkgInfo
-> String
525 | flagPackageConf hpi
=
530 packageDbOpts
:: HcPkgInfo
-> PackageDB
-> String
531 packageDbOpts _ GlobalPackageDB
= "--global"
532 packageDbOpts _ UserPackageDB
= "--user"
533 packageDbOpts hpi
(SpecificPackageDB db
) = "--" ++ packageDbFlag hpi
++ "=" ++ db
535 verbosityOpts
:: HcPkgInfo
-> Verbosity
-> [String]
537 | noVerboseFlag hpi
=
539 | v
>= deafening
= ["-v2"]
540 | v
== silent
= ["-v0"]