2 {-# LANGUAGE DataKinds #-}
3 {-# LANGUAGE FlexibleContexts #-}
4 {-# LANGUAGE OverloadedStrings #-}
5 {-# LANGUAGE RankNTypes #-}
7 -----------------------------------------------------------------------------
10 -- Module : Distribution.Simple.Program.HcPkg
11 -- Copyright : Duncan Coutts 2009, 2013
13 -- Maintainer : cabal-devel@haskell.org
14 -- Portability : portable
16 -- This module provides an library interface to the @hc-pkg@ program.
17 -- Currently only GHC and GHCJS have hc-pkg programs.
18 module Distribution
.Simple
.Program
.HcPkg
21 , RegisterOptions
(..)
22 , defaultRegisterOptions
36 -- * Program invocations
39 , unregisterInvocation
48 import Distribution
.Compat
.Prelude
hiding (init)
51 import Distribution
.InstalledPackageInfo
52 import Distribution
.Parsec
53 import Distribution
.Pretty
54 import Distribution
.Simple
.Compiler
55 import Distribution
.Simple
.Errors
56 import Distribution
.Simple
.Program
.Run
57 import Distribution
.Simple
.Program
.Types
58 import Distribution
.Simple
.Utils
59 import Distribution
.Types
.ComponentId
60 import Distribution
.Types
.PackageId
61 import Distribution
.Types
.UnitId
62 import Distribution
.Utils
.Path
63 import Distribution
.Verbosity
65 import Data
.List
(stripPrefix
)
66 import System
.FilePath as FilePath
73 import qualified Data
.ByteString
as BS
74 import qualified Data
.ByteString
.Lazy
as LBS
75 import qualified Data
.List
.NonEmpty
as NE
76 import qualified System
.FilePath.Posix
as FilePath.Posix
78 -- | Information about the features and capabilities of an @hc-pkg@
80 data HcPkgInfo
= HcPkgInfo
81 { hcPkgProgram
:: ConfiguredProgram
82 , noPkgDbStack
:: Bool
83 -- ^ no package DB stack supported
84 , noVerboseFlag
:: Bool
85 -- ^ hc-pkg does not support verbosity flags
86 , flagPackageConf
:: Bool
87 -- ^ use package-conf option instead of package-db
88 , supportsDirDbs
:: Bool
89 -- ^ supports directory style package databases
90 , requiresDirDbs
:: Bool
91 -- ^ requires directory style package databases
92 , nativeMultiInstance
:: Bool
93 -- ^ supports --enable-multi-instance flag
94 , recacheMultiInstance
:: Bool
95 -- ^ supports multi-instance via recache
96 , suppressFilesCheck
:: Bool
97 -- ^ supports --force-files or equivalent
100 -- | Call @hc-pkg@ to initialise a package database at the location {path}.
102 -- > hc-pkg init {path}
103 init :: HcPkgInfo
-> Verbosity
-> Bool -> FilePath -> IO ()
104 init hpi verbosity preferCompat path
105 |
not (supportsDirDbs hpi
)
106 ||
(not (requiresDirDbs hpi
) && preferCompat
) =
109 runProgramInvocation verbosity
(initInvocation hpi verbosity path
)
111 -- | Run @hc-pkg@ using a given package DB stack, directly forwarding the
112 -- provided command-line arguments to it.
116 -> Maybe (SymbolicPath CWD
(Dir Pkg
))
120 invoke hpi verbosity mbWorkDir dbStack extraArgs
=
121 runProgramInvocation verbosity invocation
123 args
= packageDbStackOpts hpi dbStack
++ extraArgs
124 invocation
= programInvocationCwd mbWorkDir
(hcPkgProgram hpi
) args
126 -- | Additional variations in the behaviour for 'register'.
127 data RegisterOptions
= RegisterOptions
128 { registerAllowOverwrite
:: Bool
129 -- ^ Allows re-registering \/ overwriting an existing package
130 , registerMultiInstance
:: Bool
131 -- ^ Insist on the ability to register multiple instances of a
132 -- single version of a single package. This will fail if the @hc-pkg@
133 -- does not support it, see 'nativeMultiInstance' and
134 -- 'recacheMultiInstance'.
135 , registerSuppressFilesCheck
:: Bool
136 -- ^ Require that no checks are performed on the existence of package
137 -- files mentioned in the registration info. This must be used if
138 -- registering prior to putting the files in their final place. This will
139 -- fail if the @hc-pkg@ does not support it, see 'suppressFilesCheck'.
142 -- | Defaults are @True@, @False@ and @False@
143 defaultRegisterOptions
:: RegisterOptions
144 defaultRegisterOptions
=
146 { registerAllowOverwrite
= True
147 , registerMultiInstance
= False
148 , registerSuppressFilesCheck
= False
151 -- | Call @hc-pkg@ to register a package.
153 -- > hc-pkg register {filename | -} [--user | --global | --package-db]
157 -> Maybe (SymbolicPath CWD
(Dir from
))
158 -> PackageDBStackS from
159 -> InstalledPackageInfo
162 register hpi verbosity mbWorkDir packagedbs pkgInfo registerOptions
163 | registerMultiInstance registerOptions
164 , not (nativeMultiInstance hpi || recacheMultiInstance hpi
) =
165 dieWithException verbosity RegMultipleInstancePkg
166 | registerSuppressFilesCheck registerOptions
167 , not (suppressFilesCheck hpi
) =
168 dieWithException verbosity SuppressingChecksOnFile
169 -- This is a trick. Older versions of GHC do not support the
170 -- --enable-multi-instance flag for ghc-pkg register but it turns out that
171 -- the same ability is available by using ghc-pkg recache. The recache
172 -- command is there to support distro package managers that like to work
173 -- by just installing files and running update commands, rather than
174 -- special add/remove commands. So the way to register by this method is
175 -- to write the package registration file directly into the package db and
176 -- then call hc-pkg recache.
178 | registerMultiInstance registerOptions
179 , recacheMultiInstance hpi
=
181 let pkgdb
= registrationPackageDB packagedbs
182 writeRegistrationFileDirectly verbosity hpi mbWorkDir pkgdb pkgInfo
183 recache hpi verbosity mbWorkDir pkgdb
187 (registerInvocation hpi verbosity mbWorkDir packagedbs pkgInfo registerOptions
)
189 writeRegistrationFileDirectly
192 -> Maybe (SymbolicPath CWD
(Dir from
))
194 -> InstalledPackageInfo
196 writeRegistrationFileDirectly verbosity hpi mbWorkDir
(SpecificPackageDB dir
) pkgInfo
197 | supportsDirDbs hpi
=
199 let pkgfile
= interpretSymbolicPath mbWorkDir dir
</> prettyShow
(installedUnitId pkgInfo
) <.> "conf"
200 writeUTF8File pkgfile
(showInstalledPackageInfo pkgInfo
)
202 dieWithException verbosity NoSupportDirStylePackageDb
203 writeRegistrationFileDirectly verbosity _ _ _ _
=
204 -- We don't know here what the dir for the global or user dbs are,
205 -- if that's needed it'll require a bit more plumbing to support.
206 dieWithException verbosity OnlySupportSpecificPackageDb
208 -- | Call @hc-pkg@ to unregister a package
210 -- > hc-pkg unregister [pkgid] [--user | --global | --package-db]
211 unregister
:: HcPkgInfo
-> Verbosity
-> Maybe (SymbolicPath CWD
(Dir Pkg
)) -> PackageDB
-> PackageId
-> IO ()
212 unregister hpi verbosity mbWorkDir packagedb pkgid
=
215 (unregisterInvocation hpi verbosity mbWorkDir packagedb pkgid
)
217 -- | Call @hc-pkg@ to recache the registered packages.
219 -- > hc-pkg recache [--user | --global | --package-db]
220 recache
:: HcPkgInfo
-> Verbosity
-> Maybe (SymbolicPath CWD
(Dir from
)) -> PackageDBS from
-> IO ()
221 recache hpi verbosity mbWorkDir packagedb
=
224 (recacheInvocation hpi verbosity mbWorkDir packagedb
)
226 -- | Call @hc-pkg@ to expose a package.
228 -- > hc-pkg expose [pkgid] [--user | --global | --package-db]
232 -> Maybe (SymbolicPath CWD
(Dir Pkg
))
236 expose hpi verbosity mbWorkDir packagedb pkgid
=
239 (exposeInvocation hpi verbosity mbWorkDir packagedb pkgid
)
241 -- | Call @hc-pkg@ to retrieve a specific package
243 -- > hc-pkg describe [pkgid] [--user | --global | --package-db]
247 -> Maybe (SymbolicPath CWD
(Dir Pkg
))
250 -> IO [InstalledPackageInfo
]
251 describe hpi verbosity mbWorkDir packagedb pid
= do
253 getProgramInvocationLBS
255 (describeInvocation hpi verbosity mbWorkDir packagedb pid
)
256 `catchIO`
\_
-> return mempty
258 case parsePackages output
of
260 _
-> dieWithException verbosity
$ FailedToParseOutputDescribe
(programId
(hcPkgProgram hpi
)) pid
262 -- | Call @hc-pkg@ to hide a package.
264 -- > hc-pkg hide [pkgid] [--user | --global | --package-db]
268 -> Maybe (SymbolicPath CWD
(Dir Pkg
))
272 hide hpi verbosity mbWorkDir packagedb pkgid
=
275 (hideInvocation hpi verbosity mbWorkDir packagedb pkgid
)
277 -- | Call @hc-pkg@ to get all the details of all the packages in the given
282 -> Maybe (SymbolicPath CWD
(Dir from
))
283 -> PackageDBX
(SymbolicPath from
(Dir PkgDB
))
284 -> IO [InstalledPackageInfo
]
285 dump hpi verbosity mbWorkDir packagedb
= do
287 getProgramInvocationLBS
289 (dumpInvocation hpi verbosity mbWorkDir packagedb
)
291 dieWithException verbosity
$ DumpFailed
(programId
(hcPkgProgram hpi
)) (displayException e
)
293 case parsePackages output
of
295 _
-> dieWithException verbosity
$ FailedToParseOutputDump
(programId
(hcPkgProgram hpi
))
297 parsePackages
:: LBS
.ByteString
-> Either [InstalledPackageInfo
] [String]
299 case traverse parseInstalledPackageInfo
$ splitPkgs lbs0
of
300 Right ok
-> Left
[setUnitId
. maybe id mungePackagePaths
(pkgRoot pkg
) $ pkg |
(_
, pkg
) <- ok
]
301 Left msgs
-> Right
(NE
.toList msgs
)
303 splitPkgs
:: LBS
.ByteString
-> [BS
.ByteString
]
304 splitPkgs
= checkEmpty
. doSplit
306 -- Handle the case of there being no packages at all.
307 checkEmpty
[s
] | BS
.all isSpace8 s
= []
310 isSpace8
:: Word8
-> Bool
311 isSpace8
9 = True -- '\t'
312 isSpace8
10 = True -- '\n'
313 isSpace8
13 = True -- '\r'
314 isSpace8
32 = True -- ' '
317 doSplit
:: LBS
.ByteString
-> [BS
.ByteString
]
318 doSplit lbs
= go
(LBS
.findIndices (\w
-> w
== 10 || w
== 13) lbs
)
320 go
:: [Int64
] -> [BS
.ByteString
]
321 go
[] = [LBS
.toStrict lbs
]
323 let (pfx
, sfx
) = LBS
.splitAt idx lbs
324 in case foldr (<|
>) Nothing
$ map (`lbsStripPrefix` sfx
) separators
of
325 Just sfx
' -> LBS
.toStrict pfx
: doSplit sfx
'
328 separators
:: [LBS
.ByteString
]
329 separators
= ["\n---\n", "\r\n---\r\n", "\r---\r"]
331 lbsStripPrefix
:: LBS
.ByteString
-> LBS
.ByteString
-> Maybe LBS
.ByteString
332 #if MIN_VERSION_bytestring
(0,10,8)
333 lbsStripPrefix pfx lbs
= LBS
.stripPrefix pfx lbs
335 lbsStripPrefix pfx lbs
336 | LBS
.isPrefixOf pfx lbs
= Just
(LBS
.drop (LBS
.length pfx
) lbs
)
337 |
otherwise = Nothing
340 mungePackagePaths
:: FilePath -> InstalledPackageInfo
-> InstalledPackageInfo
341 -- Perform path/URL variable substitution as per the Cabal ${pkgroot} spec
342 -- (http://www.haskell.org/pipermail/libraries/2009-May/011772.html)
343 -- Paths/URLs can be relative to ${pkgroot} or ${pkgrooturl}.
344 -- The "pkgroot" is the directory containing the package database.
345 mungePackagePaths pkgroot pkginfo
=
347 { importDirs
= mungePaths
(importDirs pkginfo
)
348 , includeDirs
= mungePaths
(includeDirs pkginfo
)
349 , libraryDirs
= mungePaths
(libraryDirs pkginfo
)
350 , libraryDirsStatic
= mungePaths
(libraryDirsStatic pkginfo
)
351 , libraryDynDirs
= mungePaths
(libraryDynDirs pkginfo
)
352 , frameworkDirs
= mungePaths
(frameworkDirs pkginfo
)
353 , haddockInterfaces
= mungePaths
(haddockInterfaces pkginfo
)
354 , haddockHTMLs
= mungeUrls
(haddockHTMLs pkginfo
)
357 mungePaths
= map mungePath
358 mungeUrls
= map mungeUrl
360 mungePath p
= case stripVarPrefix
"${pkgroot}" p
of
361 Just p
' -> pkgroot
</> p
'
364 mungeUrl p
= case stripVarPrefix
"${pkgrooturl}" p
of
365 Just p
' -> toUrlPath pkgroot p
'
370 -- URLs always use posix style '/' separators:
371 ++ FilePath.Posix
.joinPath
(r
: FilePath.splitDirectories p
)
373 stripVarPrefix var p
=
375 (root
: path
') -> case stripPrefix var root
of
376 Just
[sep
] | isPathSeparator sep
-> Just
(joinPath path
')
380 -- Older installed package info files did not have the installedUnitId
381 -- field, so if it is missing then we fill it as the source package ID.
382 -- NB: Internal libraries not supported.
383 setUnitId
:: InstalledPackageInfo
-> InstalledPackageInfo
385 pkginfo
@InstalledPackageInfo
386 { installedUnitId
= uid
387 , sourcePackageId
= pid
389 | unUnitId uid
== "" =
391 { installedUnitId
= mkLegacyUnitId pid
392 , installedComponentId_
= mkComponentId
(prettyShow pid
)
394 setUnitId pkginfo
= pkginfo
396 -- | Call @hc-pkg@ to get the source package Id of all the packages in the
397 -- given package database.
399 -- This is much less information than with 'dump', but also rather quicker.
400 -- Note in particular that it does not include the 'UnitId', just
401 -- the source 'PackageId' which is not necessarily unique in any package db.
405 -> Maybe (SymbolicPath CWD
(Dir Pkg
))
408 list hpi verbosity mbWorkDir packagedb
= do
410 getProgramInvocationOutput
412 (listInvocation hpi verbosity mbWorkDir packagedb
)
413 `catchIO`
\_
-> dieWithException verbosity
$ ListFailed
(programId
(hcPkgProgram hpi
))
415 case parsePackageIds output
of
417 _
-> dieWithException verbosity
$ FailedToParseOutputList
(programId
(hcPkgProgram hpi
))
419 parsePackageIds
= traverse simpleParsec
. words
421 --------------------------
422 -- The program invocations
425 initInvocation
:: HcPkgInfo
-> Verbosity
-> FilePath -> ProgramInvocation
426 initInvocation hpi verbosity path
=
427 programInvocation
(hcPkgProgram hpi
) args
431 ++ verbosityOpts hpi verbosity
436 -> Maybe (SymbolicPath CWD
(Dir from
))
437 -> PackageDBStackS from
438 -> InstalledPackageInfo
441 registerInvocation hpi verbosity mbWorkDir packagedbs pkgInfo registerOptions
=
442 (programInvocationCwd mbWorkDir
(hcPkgProgram hpi
) (args
"-"))
443 { progInvokeInput
= Just
$ IODataText
$ showInstalledPackageInfo pkgInfo
444 , progInvokeInputEncoding
= IOEncodingUTF8
448 | registerAllowOverwrite registerOptions
= "update"
449 | registerMultiInstance registerOptions
= "update"
450 |
otherwise = "register"
454 ++ packageDbStackOpts hpi packagedbs
455 ++ [ "--enable-multi-instance"
456 | registerMultiInstance registerOptions
459 | registerSuppressFilesCheck registerOptions
461 ++ verbosityOpts hpi verbosity
466 -> Maybe (SymbolicPath CWD
(Dir Pkg
))
470 unregisterInvocation hpi verbosity mbWorkDir packagedb pkgid
=
471 programInvocationCwd mbWorkDir
(hcPkgProgram hpi
) $
472 ["unregister", packageDbOpts hpi packagedb
, prettyShow pkgid
]
473 ++ verbosityOpts hpi verbosity
478 -> Maybe (SymbolicPath CWD
(Dir from
))
481 recacheInvocation hpi verbosity mbWorkDir packagedb
=
482 programInvocationCwd mbWorkDir
(hcPkgProgram hpi
) $
483 ["recache", packageDbOpts hpi packagedb
]
484 ++ verbosityOpts hpi verbosity
489 -> Maybe (SymbolicPath CWD
(Dir Pkg
))
493 exposeInvocation hpi verbosity mbWorkDir packagedb pkgid
=
494 programInvocationCwd mbWorkDir
(hcPkgProgram hpi
) $
495 ["expose", packageDbOpts hpi packagedb
, prettyShow pkgid
]
496 ++ verbosityOpts hpi verbosity
501 -> Maybe (SymbolicPath CWD
(Dir Pkg
))
505 describeInvocation hpi verbosity mbWorkDir packagedbs pkgid
=
506 programInvocationCwd mbWorkDir
(hcPkgProgram hpi
) $
507 ["describe", prettyShow pkgid
]
508 ++ packageDbStackOpts hpi packagedbs
509 ++ verbosityOpts hpi verbosity
514 -> Maybe (SymbolicPath CWD
(Dir Pkg
))
518 hideInvocation hpi verbosity mbWorkDir packagedb pkgid
=
519 programInvocationCwd mbWorkDir
(hcPkgProgram hpi
) $
520 ["hide", packageDbOpts hpi packagedb
, prettyShow pkgid
]
521 ++ verbosityOpts hpi verbosity
526 -> Maybe (SymbolicPath CWD
(Dir from
))
527 -> PackageDBX
(SymbolicPath from
(Dir PkgDB
))
529 dumpInvocation hpi _verbosity mbWorkDir packagedb
=
530 (programInvocationCwd mbWorkDir
(hcPkgProgram hpi
) args
)
531 { progInvokeOutputEncoding
= IOEncodingUTF8
535 ["dump", packageDbOpts hpi packagedb
]
536 ++ verbosityOpts hpi silent
538 -- We use verbosity level 'silent' because it is important that we
539 -- do not contaminate the output with info/debug messages.
544 -> Maybe (SymbolicPath CWD
(Dir Pkg
))
547 listInvocation hpi _verbosity mbWorkDir packagedb
=
548 (programInvocationCwd mbWorkDir
(hcPkgProgram hpi
) args
)
549 { progInvokeOutputEncoding
= IOEncodingUTF8
553 ["list", "--simple-output", packageDbOpts hpi packagedb
]
554 ++ verbosityOpts hpi silent
556 -- We use verbosity level 'silent' because it is important that we
557 -- do not contaminate the output with info/debug messages.
559 packageDbStackOpts
:: HcPkgInfo
-> PackageDBStackS from
-> [String]
560 packageDbStackOpts hpi dbstack
561 | noPkgDbStack hpi
= [packageDbOpts hpi
(registrationPackageDB dbstack
)]
562 |
otherwise = case dbstack
of
563 (GlobalPackageDB
: UserPackageDB
: dbs
) ->
567 (GlobalPackageDB
: dbs
) ->
569 : ("--no-user-" ++ packageDbFlag hpi
)
573 specific
(SpecificPackageDB db
) = "--" ++ packageDbFlag hpi
++ "=" ++ interpretSymbolicPathCWD db
576 ierror
= error ("internal error: unexpected package db stack: " ++ show dbstack
)
578 packageDbFlag
:: HcPkgInfo
-> String
580 | flagPackageConf hpi
=
585 packageDbOpts
:: HcPkgInfo
-> PackageDBX
(SymbolicPath from
(Dir PkgDB
)) -> String
586 packageDbOpts _ GlobalPackageDB
= "--global"
587 packageDbOpts _ UserPackageDB
= "--user"
588 packageDbOpts hpi
(SpecificPackageDB db
) = "--" ++ packageDbFlag hpi
++ "=" ++ interpretSymbolicPathCWD db
590 verbosityOpts
:: HcPkgInfo
-> Verbosity
-> [String]
592 | noVerboseFlag hpi
=
594 | v
>= deafening
= ["-v2"]
595 | v
== silent
= ["-v0"]