Cabal 3.6.3.0 → 3.16.1.0
raw patch · 295 files changed
Files
- Cabal.cabal +230/−191
- ChangeLog.md +44/−5
- LICENSE +1/−1
- README.md +5/−5
- Setup.hs +1/−0
- src/Distribution/Backpack.hs +0/−269
- src/Distribution/Backpack/ComponentsGraph.hs +51/−43
- src/Distribution/Backpack/Configure.hs +360/−253
- src/Distribution/Backpack/ConfiguredComponent.hs +234/−193
- src/Distribution/Backpack/DescribeUnitId.hs +32/−21
- src/Distribution/Backpack/FullUnitId.hs +13/−12
- src/Distribution/Backpack/Id.hs +84/−72
- src/Distribution/Backpack/LinkedComponent.hs +302/−212
- src/Distribution/Backpack/MixLink.hs +168/−132
- src/Distribution/Backpack/ModSubst.hs +22/−22
- src/Distribution/Backpack/ModuleScope.hs +51/−50
- src/Distribution/Backpack/ModuleShape.hs +21/−21
- src/Distribution/Backpack/PreExistingComponent.hs +51/−39
- src/Distribution/Backpack/PreModuleShape.hs +18/−19
- src/Distribution/Backpack/ReadyComponent.hs +271/−235
- src/Distribution/Backpack/UnifyM.hs +319/−268
- src/Distribution/CabalSpecVersion.hs +0/−150
- src/Distribution/Compat/Async.hs +45/−38
- src/Distribution/Compat/Binary.hs +0/−71
- src/Distribution/Compat/Binary/Class.hs +0/−519
- src/Distribution/Compat/Binary/Generic.hs +0/−128
- src/Distribution/Compat/CharParsing.hs +0/−357
- src/Distribution/Compat/CopyFile.hs +29/−25
- src/Distribution/Compat/CreatePipe.hs +3/−79
- src/Distribution/Compat/DList.hs +0/−53
- src/Distribution/Compat/Directory.hs +4/−5
- src/Distribution/Compat/Environment.hs +7/−55
- src/Distribution/Compat/Exception.hs +0/−37
- src/Distribution/Compat/FilePath.hs +5/−6
- src/Distribution/Compat/GetShortPathName.hs +7/−5
- src/Distribution/Compat/Graph.hs +0/−408
- src/Distribution/Compat/Internal/TempFile.hs +16/−105
- src/Distribution/Compat/Lens.hs +0/−262
- src/Distribution/Compat/MonadFail.hs +0/−5
- src/Distribution/Compat/Newtype.hs +0/−91
- src/Distribution/Compat/NonEmptySet.hs +0/−155
- src/Distribution/Compat/Parsing.hs +0/−362
- src/Distribution/Compat/Prelude.hs +0/−316
- src/Distribution/Compat/Prelude/Internal.hs +3/−3
- src/Distribution/Compat/Process.hs +13/−53
- src/Distribution/Compat/ResponseFile.hs +14/−41
- src/Distribution/Compat/Semigroup.hs +0/−119
- src/Distribution/Compat/SnocList.hs +10/−9
- src/Distribution/Compat/Stack.hs +23/−82
- src/Distribution/Compat/Time.hs +36/−40
- src/Distribution/Compat/Typeable.hs +0/−18
- src/Distribution/Compiler.hs +0/−223
- src/Distribution/FieldGrammar.hs +0/−88
- src/Distribution/FieldGrammar/Class.hs +0/−205
- src/Distribution/FieldGrammar/FieldDescrs.hs +0/−121
- src/Distribution/FieldGrammar/Newtypes.hs +0/−392
- src/Distribution/FieldGrammar/Parsec.hs +0/−439
- src/Distribution/FieldGrammar/Pretty.hs +0/−104
- src/Distribution/Fields.hs +0/−42
- src/Distribution/Fields/ConfVar.hs +0/−126
- src/Distribution/Fields/Field.hs +0/−141
- src/Distribution/Fields/Lexer.hs +0/−496
- src/Distribution/Fields/LexerMonad.hs +0/−155
- src/Distribution/Fields/ParseResult.hs +0/−196
- src/Distribution/Fields/Parser.hs +0/−377
- src/Distribution/Fields/Pretty.hs +0/−176
- src/Distribution/GetOpt.hs +221/−145
- src/Distribution/InstalledPackageInfo.hs +0/−145
- src/Distribution/Lex.hs +32/−22
- src/Distribution/License.hs +0/−252
- src/Distribution/Make.hs +78/−59
- src/Distribution/ModuleName.hs +0/−131
- src/Distribution/Package.hs +0/−96
- src/Distribution/PackageDescription.hs +0/−113
- src/Distribution/PackageDescription/Check.hs +1111/−2392
- src/Distribution/PackageDescription/Check/Common.hs +148/−0
- src/Distribution/PackageDescription/Check/Conditional.hs +265/−0
- src/Distribution/PackageDescription/Check/Monad.hs +371/−0
- src/Distribution/PackageDescription/Check/Paths.hs +418/−0
- src/Distribution/PackageDescription/Check/Target.hs +1119/−0
- src/Distribution/PackageDescription/Check/Warning.hs +1527/−0
- src/Distribution/PackageDescription/Configuration.hs +0/−600
- src/Distribution/PackageDescription/FieldGrammar.hs +0/−802
- src/Distribution/PackageDescription/Parsec.hs +0/−980
- src/Distribution/PackageDescription/PrettyPrint.hs +0/−282
- src/Distribution/PackageDescription/Quirks.hs +0/−340
- src/Distribution/PackageDescription/Utils.hs +0/−23
- src/Distribution/Parsec.hs +0/−524
- src/Distribution/Parsec/Error.hs +0/−21
- src/Distribution/Parsec/FieldLineStream.hs +0/−91
- src/Distribution/Parsec/Position.hs +0/−44
- src/Distribution/Parsec/Warning.hs +0/−61
- src/Distribution/Pretty.hs +0/−110
- src/Distribution/ReadE.hs +37/−15
- src/Distribution/SPDX.hs +0/−40
- src/Distribution/SPDX/License.hs +0/−65
- src/Distribution/SPDX/LicenseExceptionId.hs +0/−316
- src/Distribution/SPDX/LicenseExpression.hs +0/−162
- src/Distribution/SPDX/LicenseId.hs +0/−2152
- src/Distribution/SPDX/LicenseListVersion.hs +0/−22
- src/Distribution/SPDX/LicenseReference.hs +0/−80
- src/Distribution/Simple.hs +1132/−836
- src/Distribution/Simple/Bench.hs +136/−81
- src/Distribution/Simple/Build.hs +1240/−734
- src/Distribution/Simple/Build/Inputs.hs +74/−0
- src/Distribution/Simple/Build/Macros.hs +59/−55
- src/Distribution/Simple/Build/Macros/Z.hs +3/−2
- src/Distribution/Simple/Build/PackageInfoModule.hs +60/−0
- src/Distribution/Simple/Build/PackageInfoModule/Z.hs +83/−0
- src/Distribution/Simple/Build/PathsModule.hs +97/−90
- src/Distribution/Simple/Build/PathsModule/Z.hs +121/−33
- src/Distribution/Simple/BuildPaths.hs +322/−117
- src/Distribution/Simple/BuildTarget.hs +1110/−1064
- src/Distribution/Simple/BuildToolDepends.hs +48/−28
- src/Distribution/Simple/BuildWay.hs +14/−0
- src/Distribution/Simple/CCompiler.hs +66/−52
- src/Distribution/Simple/Command.hs +589/−382
- src/Distribution/Simple/Compiler.hs +345/−191
- src/Distribution/Simple/Configure.hs +2975/−2082
- src/Distribution/Simple/ConfigureScript.hs +247/−0
- src/Distribution/Simple/Errors.hs +806/−0
- src/Distribution/Simple/FileMonitor/Types.hs +217/−0
- src/Distribution/Simple/Flag.hs +62/−69
- src/Distribution/Simple/GHC.hs +1166/−2088
- src/Distribution/Simple/GHC/Build.hs +155/−0
- src/Distribution/Simple/GHC/Build/ExtraSources.hs +272/−0
- src/Distribution/Simple/GHC/Build/Link.hs +812/−0
- src/Distribution/Simple/GHC/Build/Modules.hs +418/−0
- src/Distribution/Simple/GHC/Build/Utils.hs +246/−0
- src/Distribution/Simple/GHC/EnvironmentParser.hs +33/−29
- src/Distribution/Simple/GHC/ImplInfo.hs +105/−66
- src/Distribution/Simple/GHC/Internal.hs +864/−686
- src/Distribution/Simple/GHCJS.hs +2113/−1823
- src/Distribution/Simple/Glob.hs +441/−239
- src/Distribution/Simple/Glob/Internal.hs +131/−0
- src/Distribution/Simple/Haddock.hs +1634/−926
- src/Distribution/Simple/HaskellSuite.hs +0/−228
- src/Distribution/Simple/Hpc.hs +138/−126
- src/Distribution/Simple/Install.hs +293/−181
- src/Distribution/Simple/InstallDirs.hs +288/−240
- src/Distribution/Simple/InstallDirs/Internal.hs +130/−98
- src/Distribution/Simple/LocalBuildInfo.hs +350/−232
- src/Distribution/Simple/PackageDescription.hs +136/−0
- src/Distribution/Simple/PackageIndex.hs +411/−311
- src/Distribution/Simple/PreProcess.hs +932/−779
- src/Distribution/Simple/PreProcess/Types.hs +128/−0
- src/Distribution/Simple/PreProcess/Unlit.hs +101/−91
- src/Distribution/Simple/Program.hs +182/−114
- src/Distribution/Simple/Program/Ar.hs +165/−113
- src/Distribution/Simple/Program/Builtin.hs +297/−259
- src/Distribution/Simple/Program/Db.hs +303/−242
- src/Distribution/Simple/Program/Find.hs +118/−70
- src/Distribution/Simple/Program/GHC.hs +1075/−840
- src/Distribution/Simple/Program/HcPkg.hs +386/−306
- src/Distribution/Simple/Program/Hpc.hs +103/−70
- src/Distribution/Simple/Program/Internal.hs +32/−26
- src/Distribution/Simple/Program/Ld.hs +71/−53
- src/Distribution/Simple/Program/ResponseFile.hs +28/−19
- src/Distribution/Simple/Program/Run.hs +222/−181
- src/Distribution/Simple/Program/Script.hs +75/−75
- src/Distribution/Simple/Program/Strip.hs +46/−34
- src/Distribution/Simple/Program/Types.hs +122/−123
- src/Distribution/Simple/Register.hs +536/−376
- src/Distribution/Simple/Setup.hs +251/−2348
- src/Distribution/Simple/Setup/Benchmark.hs +157/−0
- src/Distribution/Simple/Setup/Build.hs +184/−0
- src/Distribution/Simple/Setup/Clean.hs +123/−0
- src/Distribution/Simple/Setup/Common.hs +499/−0
- src/Distribution/Simple/Setup/Config.hs +1109/−0
- src/Distribution/Simple/Setup/Copy.hs +174/−0
- src/Distribution/Simple/Setup/Global.hs +129/−0
- src/Distribution/Simple/Setup/Haddock.hs +642/−0
- src/Distribution/Simple/Setup/Hscolour.hs +181/−0
- src/Distribution/Simple/Setup/Install.hs +183/−0
- src/Distribution/Simple/Setup/Register.hs +217/−0
- src/Distribution/Simple/Setup/Repl.hs +240/−0
- src/Distribution/Simple/Setup/SDist.hs +141/−0
- src/Distribution/Simple/Setup/Test.hs +284/−0
- src/Distribution/Simple/SetupHooks/Errors.hs +231/−0
- src/Distribution/Simple/SetupHooks/Internal.hs +1095/−0
- src/Distribution/Simple/SetupHooks/Rule.hs +1140/−0
- src/Distribution/Simple/ShowBuildInfo.hs +134/−77
- src/Distribution/Simple/SrcDist.hs +420/−312
- src/Distribution/Simple/Test.hs +161/−89
- src/Distribution/Simple/Test/ExeV10.hs +197/−136
- src/Distribution/Simple/Test/LibV09.hs +226/−176
- src/Distribution/Simple/Test/Log.hs +113/−86
- src/Distribution/Simple/UHC.hs +242/−161
- src/Distribution/Simple/UserHooks.hs +138/−149
- src/Distribution/Simple/Utils.hs +2071/−1608
- src/Distribution/Simple/Utils/Json.hs +0/−46
- src/Distribution/System.hs +0/−275
- src/Distribution/TestSuite.hs +73/−66
- src/Distribution/Text.hs +0/−12
- src/Distribution/Types/AbiDependency.hs +0/−44
- src/Distribution/Types/AbiHash.hs +0/−61
- src/Distribution/Types/AnnotatedId.hs +15/−16
- src/Distribution/Types/Benchmark.hs +0/−77
- src/Distribution/Types/Benchmark/Lens.hs +0/−27
- src/Distribution/Types/BenchmarkInterface.hs +0/−46
- src/Distribution/Types/BenchmarkType.hs +0/−40
- src/Distribution/Types/BuildInfo.hs +0/−266
- src/Distribution/Types/BuildInfo/Lens.hs +0/−342
- src/Distribution/Types/BuildType.hs +0/−55
- src/Distribution/Types/Component.hs +0/−91
- src/Distribution/Types/ComponentId.hs +0/−69
- src/Distribution/Types/ComponentInclude.hs +15/−15
- src/Distribution/Types/ComponentLocalBuildInfo.hs +109/−99
- src/Distribution/Types/ComponentName.hs +0/−78
- src/Distribution/Types/ComponentRequestedSpec.hs +0/−127
- src/Distribution/Types/CondTree.hs +0/−181
- src/Distribution/Types/Condition.hs +0/−135
- src/Distribution/Types/ConfVar.hs +0/−25
- src/Distribution/Types/Dependency.hs +0/−179
- src/Distribution/Types/DependencyMap.hs +0/−57
- src/Distribution/Types/DumpBuildInfo.hs +15/−0
- src/Distribution/Types/ExeDependency.hs +0/−71
- src/Distribution/Types/Executable.hs +0/−65
- src/Distribution/Types/Executable/Lens.hs +0/−31
- src/Distribution/Types/ExecutableScope.hs +0/−43
- src/Distribution/Types/ExposedModule.hs +0/−45
- src/Distribution/Types/Flag.hs +0/−346
- src/Distribution/Types/ForeignLib.hs +0/−199
- src/Distribution/Types/ForeignLib/Lens.hs +0/−45
- src/Distribution/Types/ForeignLibOption.hs +0/−39
- src/Distribution/Types/ForeignLibType.hs +0/−69
- src/Distribution/Types/GenericPackageDescription.hs +0/−105
- src/Distribution/Types/GenericPackageDescription/Lens.hs +0/−131
- src/Distribution/Types/GivenComponent.hs +30/−11
- src/Distribution/Types/HookedBuildInfo.hs +0/−66
- src/Distribution/Types/IncludeRenaming.hs +0/−68
- src/Distribution/Types/InstalledPackageInfo.hs +0/−173
- src/Distribution/Types/InstalledPackageInfo/FieldGrammar.hs +0/−317
- src/Distribution/Types/InstalledPackageInfo/Lens.hs +0/−190
- src/Distribution/Types/LegacyExeDependency.hs +0/−47
- src/Distribution/Types/Lens.hs +0/−25
- src/Distribution/Types/Library.hs +0/−96
- src/Distribution/Types/Library/Lens.hs +0/−45
- src/Distribution/Types/LibraryName.hs +0/−73
- src/Distribution/Types/LibraryVisibility.hs +0/−52
- src/Distribution/Types/LocalBuildConfig.hs +229/−0
- src/Distribution/Types/LocalBuildInfo.hs +380/−182
- src/Distribution/Types/Mixin.hs +0/−93
- src/Distribution/Types/Module.hs +0/−46
- src/Distribution/Types/ModuleReexport.hs +0/−50
- src/Distribution/Types/ModuleRenaming.hs +0/−134
- src/Distribution/Types/MungedPackageId.hs +0/−83
- src/Distribution/Types/MungedPackageName.hs +0/−157
- src/Distribution/Types/PackageDescription.hs +0/−457
- src/Distribution/Types/PackageDescription/Lens.hs +0/−221
- src/Distribution/Types/PackageId.hs +0/−74
- src/Distribution/Types/PackageId/Lens.hs +0/−22
- src/Distribution/Types/PackageName.hs +0/−73
- src/Distribution/Types/PackageName/Magic.hs +5/−1
- src/Distribution/Types/PackageVersionConstraint.hs +0/−74
- src/Distribution/Types/ParStrat.hs +24/−0
- src/Distribution/Types/PkgconfigDependency.hs +0/−39
- src/Distribution/Types/PkgconfigName.hs +0/−68
- src/Distribution/Types/PkgconfigVersion.hs +0/−119
- src/Distribution/Types/PkgconfigVersionRange.hs +0/−161
- src/Distribution/Types/SetupBuildInfo.hs +0/−41
- src/Distribution/Types/SetupBuildInfo/Lens.hs +0/−21
- src/Distribution/Types/SourceRepo.hs +0/−201
- src/Distribution/Types/SourceRepo/Lens.hs +0/−39
- src/Distribution/Types/TargetInfo.hs +24/−18
- src/Distribution/Types/TestSuite.hs +0/−83
- src/Distribution/Types/TestSuite/Lens.hs +0/−27
- src/Distribution/Types/TestSuiteInterface.hs +0/−52
- src/Distribution/Types/TestType.hs +0/−43
- src/Distribution/Types/UnitId.hs +0/−134
- src/Distribution/Types/UnqualComponentName.hs +0/−93
- src/Distribution/Types/Version.hs +0/−242
- src/Distribution/Types/VersionInterval.hs +0/−345
- src/Distribution/Types/VersionInterval/Legacy.hs +0/−368
- src/Distribution/Types/VersionRange.hs +0/−180
- src/Distribution/Types/VersionRange/Internal.hs +0/−526
- src/Distribution/Utils/Base62.hs +0/−22
- src/Distribution/Utils/Generic.hs +0/−576
- src/Distribution/Utils/IOData.hs +52/−47
- src/Distribution/Utils/Json.hs +63/−0
- src/Distribution/Utils/LogProgress.hs +39/−38
- src/Distribution/Utils/MD5.hs +0/−79
- src/Distribution/Utils/MapAccum.hs +14/−22
- src/Distribution/Utils/NubList.hs +25/−29
- src/Distribution/Utils/Path.hs +0/−103
- src/Distribution/Utils/Progress.hs +25/−23
- src/Distribution/Utils/ShortText.hs +0/−159
- src/Distribution/Utils/String.hs +0/−93
- src/Distribution/Utils/Structured.hs +0/−477
- src/Distribution/Utils/UnionFind.hs +46/−44
- src/Distribution/Verbosity.hs +148/−116
- src/Distribution/Verbosity/Internal.hs +14/−12
- src/Distribution/Version.hs +0/−185
- src/Distribution/ZinzaPrelude.hs +23/−22
- src/Language/Haskell/Extension.hs +0/−933
Cabal.cabal view
@@ -1,8 +1,8 @@-cabal-version: >=1.10+cabal-version: 3.6 name: Cabal-version: 3.6.3.0-copyright: 2003-2021, Cabal Development Team (see AUTHORS file)-license: BSD3+version: 3.16.1.0+copyright: 2003-2025, Cabal Development Team (see AUTHORS file)+license: BSD-3-Clause license-file: LICENSE author: Cabal Development Team <cabal-devel@haskell.org> maintainer: cabal-devel@haskell.org@@ -21,7 +21,7 @@ -- If we use a new Cabal feature, this needs to be changed to Custom so -- we can bootstrap. -extra-source-files:+extra-doc-files: README.md ChangeLog.md source-repository head@@ -29,56 +29,55 @@ location: https://github.com/haskell/cabal/ subdir: Cabal -flag bundled-binary-generic+flag git-rev+ description: include Git revision hash in version default: False+ manual: True library default-language: Haskell2010 hs-source-dirs: src build-depends:- array >= 0.4.0.1 && < 0.6,- base >= 4.6 && < 5,- bytestring >= 0.10.0.0 && < 0.12,- containers >= 0.5.0.0 && < 0.7,- deepseq >= 1.3.0.1 && < 1.5,- directory >= 1.2 && < 1.4,- filepath >= 1.3.0.1 && < 1.5,- pretty >= 1.1.1 && < 1.2,- process >= 1.1.0.2 && < 1.7,- time >= 1.4.0.1 && < 1.13-- if flag(bundled-binary-generic)- build-depends: binary >= 0.5.1.1 && < 0.7- else- build-depends: binary >= 0.7 && < 0.9+ , Cabal-syntax ^>= 3.16.1.0+ , array >= 0.4.0.1 && < 0.6+ , base >= 4.13 && < 5+ , bytestring >= 0.10.0.0 && < 0.13+ , containers >= 0.5.0.0 && < 0.9+ , deepseq >= 1.3.0.1 && < 1.7+ , directory >= 1.2 && < 1.4+ , filepath >= 1.3.0.1 && < 1.6+ , pretty >= 1.1.1 && < 1.2+ , process >= 1.2.1.0 && < 1.7+ , time >= 1.4.0.1 && < 1.16 if os(windows)- build-depends: Win32 >= 2.3.0.0 && < 2.13+ build-depends:+ , Win32 >= 2.3.0.0 && < 2.15 else- build-depends: unix >= 2.6.0.0 && < 2.8-- ghc-options: -Wall -fno-ignore-asserts -fwarn-tabs -fwarn-incomplete-uni-patterns -fwarn-incomplete-record-updates- if impl(ghc >= 8.0)- ghc-options: -Wcompat -Wnoncanonical-monad-instances+ build-depends:+ , unix >= 2.8.6.0 && < 2.9 - if impl(ghc <8.8)- ghc-options: -Wnoncanonical-monadfail-instances+ if flag(git-rev)+ build-depends:+ , githash ^>= 0.1.7.0+ cpp-options: -DGIT_REV - if !impl(ghc >= 8.0)- -- at least one of lib:Cabal's dependency (i.e. `parsec`)- -- already depends on `fail` and `semigroups` transitively- build-depends: fail == 4.9.*, semigroups >= 0.18.3 && < 0.21+ ghc-options:+ -Wall+ -fno-ignore-asserts+ -Wtabs+ -Wincomplete-uni-patterns+ -Wincomplete-record-updates+ -Wno-unticked-promoted-constructors - if !impl(ghc >= 7.10)- build-depends: void >= 0.7.3 && < 0.8+ if impl(ghc >= 8.0)+ ghc-options: -Wcompat -Wnoncanonical-monad-instances - if !impl(ghc >= 7.8)- -- semigroups depends on tagged.- build-depends: tagged >=0.8.6 && <0.9+ if impl(ghc >= 8.0) && impl(ghc < 8.8)+ ghc-options: -Wnoncanonical-monadfail-instances exposed-modules:- Distribution.Backpack Distribution.Backpack.Configure Distribution.Backpack.ComponentsGraph Distribution.Backpack.ConfiguredComponent@@ -88,71 +87,54 @@ Distribution.Backpack.ModSubst Distribution.Backpack.ModuleShape Distribution.Backpack.PreModuleShape- Distribution.CabalSpecVersion Distribution.Utils.IOData- Distribution.Utils.Path Distribution.Utils.LogProgress Distribution.Utils.MapAccum- Distribution.Utils.MD5- Distribution.Utils.Structured Distribution.Compat.CreatePipe Distribution.Compat.Directory Distribution.Compat.Environment- Distribution.Compat.Exception Distribution.Compat.FilePath- Distribution.Compat.Graph Distribution.Compat.Internal.TempFile- Distribution.Compat.Newtype- Distribution.Compat.NonEmptySet Distribution.Compat.ResponseFile Distribution.Compat.Prelude.Internal Distribution.Compat.Process- Distribution.Compat.Semigroup Distribution.Compat.Stack Distribution.Compat.Time- Distribution.Compat.Typeable- Distribution.Compat.DList- Distribution.Compiler- Distribution.InstalledPackageInfo- Distribution.Types.AbiDependency- Distribution.Types.ExposedModule- Distribution.Types.InstalledPackageInfo- Distribution.Types.InstalledPackageInfo.FieldGrammar- Distribution.License Distribution.Make- Distribution.ModuleName- Distribution.Package- Distribution.PackageDescription Distribution.PackageDescription.Check- Distribution.PackageDescription.Configuration- Distribution.PackageDescription.PrettyPrint- Distribution.PackageDescription.Utils Distribution.ReadE Distribution.Simple Distribution.Simple.Bench Distribution.Simple.Build+ Distribution.Simple.Build.Inputs Distribution.Simple.Build.Macros+ Distribution.Simple.Build.PackageInfoModule Distribution.Simple.Build.PathsModule Distribution.Simple.BuildPaths Distribution.Simple.BuildTarget Distribution.Simple.BuildToolDepends+ Distribution.Simple.BuildWay Distribution.Simple.CCompiler Distribution.Simple.Command Distribution.Simple.Compiler Distribution.Simple.Configure+ Distribution.Simple.Errors+ Distribution.Simple.FileMonitor.Types Distribution.Simple.Flag Distribution.Simple.GHC Distribution.Simple.GHCJS Distribution.Simple.Haddock Distribution.Simple.Glob- Distribution.Simple.HaskellSuite+ Distribution.Simple.Glob.Internal Distribution.Simple.Hpc Distribution.Simple.Install Distribution.Simple.InstallDirs Distribution.Simple.InstallDirs.Internal Distribution.Simple.LocalBuildInfo+ Distribution.Simple.PackageDescription Distribution.Simple.PackageIndex Distribution.Simple.PreProcess+ Distribution.Simple.PreProcess.Types Distribution.Simple.PreProcess.Unlit Distribution.Simple.Program Distribution.Simple.Program.Ar@@ -179,140 +161,178 @@ Distribution.Simple.Test.Log Distribution.Simple.UHC Distribution.Simple.UserHooks+ Distribution.Simple.SetupHooks.Errors+ Distribution.Simple.SetupHooks.Internal+ Distribution.Simple.SetupHooks.Rule Distribution.Simple.Utils- Distribution.SPDX- Distribution.SPDX.License- Distribution.SPDX.LicenseId- Distribution.SPDX.LicenseExceptionId- Distribution.SPDX.LicenseExpression- Distribution.SPDX.LicenseListVersion- Distribution.SPDX.LicenseReference- Distribution.System Distribution.TestSuite- Distribution.Text- Distribution.Pretty- Distribution.Types.AbiHash Distribution.Types.AnnotatedId- Distribution.Types.Benchmark- Distribution.Types.BenchmarkInterface- Distribution.Types.BenchmarkType- Distribution.Types.BuildInfo- Distribution.Types.BuildType Distribution.Types.ComponentInclude- Distribution.Types.ConfVar- Distribution.Types.Dependency- Distribution.Types.ExeDependency- Distribution.Types.LegacyExeDependency- Distribution.Types.PkgconfigDependency- Distribution.Types.DependencyMap- Distribution.Types.ComponentId- Distribution.Types.MungedPackageId- Distribution.Types.PackageId- Distribution.Types.UnitId- Distribution.Types.Executable- Distribution.Types.ExecutableScope- Distribution.Types.Library- Distribution.Types.LibraryVisibility- Distribution.Types.Flag- Distribution.Types.ForeignLib- Distribution.Types.ForeignLibType- Distribution.Types.ForeignLibOption- Distribution.Types.Module- Distribution.Types.ModuleReexport- Distribution.Types.ModuleRenaming- Distribution.Types.ComponentName- Distribution.Types.LibraryName- Distribution.Types.MungedPackageName- Distribution.Types.PackageName+ Distribution.Types.DumpBuildInfo Distribution.Types.PackageName.Magic- Distribution.Types.PkgconfigName- Distribution.Types.PkgconfigVersion- Distribution.Types.PkgconfigVersionRange- Distribution.Types.UnqualComponentName- Distribution.Types.IncludeRenaming- Distribution.Types.Mixin- Distribution.Types.SetupBuildInfo- Distribution.Types.TestSuite- Distribution.Types.TestSuiteInterface- Distribution.Types.TestType- Distribution.Types.GenericPackageDescription- Distribution.Types.Condition- Distribution.Types.CondTree- Distribution.Types.HookedBuildInfo- Distribution.Types.PackageDescription- Distribution.Types.SourceRepo- Distribution.Types.Component Distribution.Types.ComponentLocalBuildInfo+ Distribution.Types.LocalBuildConfig Distribution.Types.LocalBuildInfo- Distribution.Types.ComponentRequestedSpec Distribution.Types.TargetInfo- Distribution.Types.Version- Distribution.Types.VersionRange- Distribution.Types.VersionRange.Internal- Distribution.Types.VersionInterval- Distribution.Types.VersionInterval.Legacy Distribution.Types.GivenComponent- Distribution.Types.PackageVersionConstraint- Distribution.Utils.Generic+ Distribution.Types.ParStrat+ Distribution.Utils.Json Distribution.Utils.NubList- Distribution.Utils.ShortText Distribution.Utils.Progress Distribution.Verbosity Distribution.Verbosity.Internal- Distribution.Version++ -- We reexport all of Cabal-syntax to aid in compatibility for downstream+ -- users. In the future we may opt to deprecate some or all of these exports.+ -- See haskell/Cabal#7974.+ reexported-modules:+ Distribution.Backpack,+ Distribution.CabalSpecVersion,+ Distribution.Compat.Binary,+ Distribution.Compat.CharParsing,+ Distribution.Compat.DList,+ Distribution.Compat.Exception,+ Distribution.Compat.Graph,+ Distribution.Compat.Lens,+ Distribution.Compat.MonadFail,+ Distribution.Compat.Newtype,+ Distribution.Compat.NonEmptySet,+ Distribution.Compat.Parsing,+ Distribution.Compat.Prelude,+ Distribution.Compat.Semigroup,+ Distribution.Compiler,+ Distribution.FieldGrammar,+ Distribution.FieldGrammar.Class,+ Distribution.FieldGrammar.FieldDescrs,+ Distribution.FieldGrammar.Newtypes,+ Distribution.FieldGrammar.Parsec,+ Distribution.FieldGrammar.Pretty,+ Distribution.Fields,+ Distribution.Fields.ConfVar,+ Distribution.Fields.Field,+ Distribution.Fields.Lexer,+ Distribution.Fields.LexerMonad,+ Distribution.Fields.ParseResult,+ Distribution.Fields.Parser,+ Distribution.Fields.Pretty,+ Distribution.InstalledPackageInfo,+ Distribution.License,+ Distribution.ModuleName,+ Distribution.Package,+ Distribution.PackageDescription,+ Distribution.PackageDescription.Configuration,+ Distribution.PackageDescription.FieldGrammar,+ Distribution.PackageDescription.Parsec,+ Distribution.PackageDescription.PrettyPrint,+ Distribution.PackageDescription.Quirks,+ Distribution.PackageDescription.Utils,+ Distribution.Parsec,+ Distribution.Parsec.Error,+ Distribution.Parsec.FieldLineStream,+ Distribution.Parsec.Position,+ Distribution.Parsec.Warning,+ Distribution.Pretty,+ Distribution.SPDX,+ Distribution.SPDX.License,+ Distribution.SPDX.LicenseExceptionId,+ Distribution.SPDX.LicenseExpression,+ Distribution.SPDX.LicenseId,+ Distribution.SPDX.LicenseListVersion,+ Distribution.SPDX.LicenseReference,+ Distribution.System,+ Distribution.Text,+ Distribution.Types.AbiDependency,+ Distribution.Types.AbiHash,+ Distribution.Types.Benchmark,+ Distribution.Types.Benchmark.Lens,+ Distribution.Types.BenchmarkInterface,+ Distribution.Types.BenchmarkType,+ Distribution.Types.BuildInfo,+ Distribution.Types.BuildInfo.Lens,+ Distribution.Types.BuildType,+ Distribution.Types.Component,+ Distribution.Types.ComponentId,+ Distribution.Types.ComponentName,+ Distribution.Types.ComponentRequestedSpec,+ Distribution.Types.CondTree,+ Distribution.Types.Condition,+ Distribution.Types.ConfVar,+ Distribution.Types.Dependency,+ Distribution.Types.DependencyMap,+ Distribution.Types.DependencySatisfaction,+ Distribution.Types.ExeDependency,+ Distribution.Types.Executable,+ Distribution.Types.Executable.Lens,+ Distribution.Types.ExecutableScope,+ Distribution.Types.ExposedModule,+ Distribution.Types.Flag,+ Distribution.Types.ForeignLib,+ Distribution.Types.ForeignLib.Lens,+ Distribution.Types.ForeignLibOption,+ Distribution.Types.ForeignLibType,+ Distribution.Types.GenericPackageDescription,+ Distribution.Types.GenericPackageDescription.Lens,+ Distribution.Types.HookedBuildInfo,+ Distribution.Types.IncludeRenaming,+ Distribution.Types.InstalledPackageInfo,+ Distribution.Types.InstalledPackageInfo.Lens,+ Distribution.Types.InstalledPackageInfo.FieldGrammar,+ Distribution.Types.LegacyExeDependency,+ Distribution.Types.Lens,+ Distribution.Types.Library,+ Distribution.Types.Library.Lens,+ Distribution.Types.LibraryName,+ Distribution.Types.LibraryVisibility,+ Distribution.Types.MissingDependency,+ Distribution.Types.MissingDependencyReason,+ Distribution.Types.Mixin,+ Distribution.Types.Module,+ Distribution.Types.ModuleReexport,+ Distribution.Types.ModuleRenaming,+ Distribution.Types.MungedPackageId,+ Distribution.Types.MungedPackageName,+ Distribution.Types.PackageDescription,+ Distribution.Types.PackageDescription.Lens,+ Distribution.Types.PackageId,+ Distribution.Types.PackageId.Lens,+ Distribution.Types.PackageName,+ Distribution.Types.PackageVersionConstraint,+ Distribution.Types.PkgconfigDependency,+ Distribution.Types.PkgconfigName,+ Distribution.Types.PkgconfigVersion,+ Distribution.Types.PkgconfigVersionRange,+ Distribution.Types.SetupBuildInfo,+ Distribution.Types.SetupBuildInfo.Lens,+ Distribution.Types.SourceRepo,+ Distribution.Types.SourceRepo.Lens,+ Distribution.Types.TestSuite,+ Distribution.Types.TestSuite.Lens,+ Distribution.Types.TestSuiteInterface,+ Distribution.Types.TestType,+ Distribution.Types.UnitId,+ Distribution.Types.UnqualComponentName,+ Distribution.Types.Version,+ Distribution.Types.VersionInterval,+ Distribution.Types.VersionInterval.Legacy,+ Distribution.Types.VersionRange,+ Distribution.Types.VersionRange.Internal,+ Distribution.Utils.Base62,+ Distribution.Utils.Generic,+ Distribution.Utils.MD5,+ Distribution.Utils.Path,+ Distribution.Utils.ShortText,+ Distribution.Utils.String,+ Distribution.Utils.Structured,+ Distribution.Version, Language.Haskell.Extension- Distribution.Compat.Binary -- Parsec parser-related modules build-depends: -- transformers-0.4.0.0 doesn't have record syntax e.g. for Identity -- See also https://github.com/ekmett/transformers-compat/issues/35- transformers (>= 0.3 && < 0.4) || (>=0.4.1.0 && <0.6),- mtl >= 2.1 && < 2.3,- text (>= 1.2.3.0 && < 1.3) || (>= 2.0 && < 2.1),- parsec >= 3.1.13.0 && < 3.2- exposed-modules:- Distribution.Compat.Parsing- Distribution.Compat.CharParsing- Distribution.FieldGrammar- Distribution.FieldGrammar.Class- Distribution.FieldGrammar.FieldDescrs- Distribution.FieldGrammar.Newtypes- Distribution.FieldGrammar.Parsec- Distribution.FieldGrammar.Pretty- Distribution.PackageDescription.FieldGrammar- Distribution.PackageDescription.Parsec- Distribution.PackageDescription.Quirks- Distribution.Parsec- Distribution.Parsec.Error- Distribution.Parsec.Position- Distribution.Parsec.Warning- Distribution.Parsec.FieldLineStream- Distribution.Fields- Distribution.Fields.ConfVar- Distribution.Fields.Field- Distribution.Fields.Lexer- Distribution.Fields.LexerMonad- Distribution.Fields.ParseResult- Distribution.Fields.Parser- Distribution.Fields.Pretty-- -- Lens functionality- exposed-modules:- Distribution.Compat.Lens- Distribution.Types.Lens- Distribution.Types.Benchmark.Lens- Distribution.Types.BuildInfo.Lens- Distribution.Types.Executable.Lens- Distribution.Types.ForeignLib.Lens- Distribution.Types.GenericPackageDescription.Lens- Distribution.Types.InstalledPackageInfo.Lens- Distribution.Types.Library.Lens- Distribution.Types.PackageDescription.Lens- Distribution.Types.PackageId.Lens- Distribution.Types.SetupBuildInfo.Lens- Distribution.Types.SourceRepo.Lens- Distribution.Types.TestSuite.Lens+ , transformers (>= 0.3 && < 0.4) || (>=0.4.1.0 && <0.7)+ , mtl >= 2.1 && < 2.4+ , parsec >= 3.1.13.0 && < 3.2 other-modules: Distribution.Backpack.PreExistingComponent@@ -322,29 +342,49 @@ Distribution.Backpack.UnifyM Distribution.Backpack.Id Distribution.Utils.UnionFind- Distribution.Utils.Base62 Distribution.Compat.Async Distribution.Compat.CopyFile Distribution.Compat.GetShortPathName- Distribution.Compat.MonadFail- Distribution.Compat.Prelude Distribution.Compat.SnocList Distribution.GetOpt Distribution.Lex- Distribution.Utils.String+ Distribution.PackageDescription.Check.Common+ Distribution.PackageDescription.Check.Conditional+ Distribution.PackageDescription.Check.Monad+ Distribution.PackageDescription.Check.Paths+ Distribution.PackageDescription.Check.Target+ Distribution.PackageDescription.Check.Warning Distribution.Simple.Build.Macros.Z+ Distribution.Simple.Build.PackageInfoModule.Z Distribution.Simple.Build.PathsModule.Z+ Distribution.Simple.GHC.Build+ Distribution.Simple.GHC.Build.ExtraSources+ Distribution.Simple.GHC.Build.Link+ Distribution.Simple.GHC.Build.Modules+ Distribution.Simple.GHC.Build.Utils Distribution.Simple.GHC.EnvironmentParser Distribution.Simple.GHC.Internal Distribution.Simple.GHC.ImplInfo- Distribution.Simple.Utils.Json+ Distribution.Simple.ConfigureScript+ Distribution.Simple.Setup.Benchmark+ Distribution.Simple.Setup.Build+ Distribution.Simple.Setup.Clean+ Distribution.Simple.Setup.Common+ Distribution.Simple.Setup.Config+ Distribution.Simple.Setup.Copy+ Distribution.Simple.Setup.Global+ Distribution.Simple.Setup.Haddock+ Distribution.Simple.Setup.Hscolour+ Distribution.Simple.Setup.Install+ Distribution.Simple.Setup.Register+ Distribution.Simple.Setup.Repl+ Distribution.Simple.Setup.SDist+ Distribution.Simple.Setup.Test Distribution.ZinzaPrelude Paths_Cabal - if flag(bundled-binary-generic)- other-modules:- Distribution.Compat.Binary.Class- Distribution.Compat.Binary.Generic+ autogen-modules:+ Paths_Cabal other-extensions: BangPatterns@@ -361,8 +401,10 @@ GeneralizedNewtypeDeriving ImplicitParams KindSignatures+ LambdaCase NondecreasingIndentation OverloadedStrings+ PatternSynonyms RankNTypes RecordWildCards ScopedTypeVariables@@ -372,6 +414,3 @@ TypeOperators TypeSynonymInstances UndecidableInstances-- if impl(ghc >= 7.11)- other-extensions: PatternSynonyms
ChangeLog.md view
@@ -1,16 +1,55 @@+# 3.16.1.0 [Artem Pelenitsyn](mailto:a@pelenitsyn.top) December 2025+* See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.16.1.0.md++# 3.16.0.0 [Artem Pelenitsyn](mailto:a@pelenitsyn.top) July 2025+* See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.16.0.0.md++# 3.14.2.0 [Mikolaj Konarski](mailto:mikolaj@well-typed.com) April 2025+* See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.14.2.0.md++# 3.14.1.0 [Hécate](mailto:hecate+github@glitchbra.in) November 2024+* See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.14.1.0.md++# 3.14.0.0 [Hécate](mailto:hecate+github@glitchbra.in) September 2024+* See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.14.0.0.md++# 3.12.1.0 [Artem Pelenitsyn](mailto:a.pelenitsyn@gmail.com) June 2024+* See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.12.1.0.md++# 3.12.0.0 [Francesco Ariis](mailto:fa-ml@ariis.it) May 2024+* See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.12.0.0.md++# 3.10.3.0 [Hécate](mailto:hecate+github@glitchbra.in) January 2024+* See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.10.3.0.md++# 3.10.2.1 [Hécate](mailto:hecate+github@glitchbra.in) October 2023+* See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.10.2.1.md++# 3.10.2.0 [Hécate](mailto:hecate+github@glitchbra.in) August 2023+* See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.10.2.0.md++# 3.10.1.0 [Mikolaj Konarski](mailto:mikolaj@well-typed.com) March 2023+* See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.10.1.0.md++# 3.8.1.0 [Mikolaj Konarski](mailto:mikolaj@well-typed.com) August 2022+ * See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.8.1.0.md+ # 3.6.3.0 March 2022 * See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.6.3.0.md -# 3.6.2.0 [Emily Pillmore](mailgo:emilypi@cohomolo.gy) October 2021+# 3.6.2.0 [Emily Pillmore](mailto:emilypi@cohomolo.gy) October 2021 * See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.6.2.0.md -# 3.6.1.0 [Emily Pillmore](mailgo:emilypi@cohomolo.gy) August 2021+# 3.6.1.0 [Emily Pillmore](mailto:emilypi@cohomolo.gy) August 2021 * See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.6.1.0.md -# 3.6.0.0 [Emily Pillmore](mailgo:emilypi@cohomolo.gy) August 2021+# 3.6.0.0 [Emily Pillmore](mailto:emilypi@cohomolo.gy) August 2021 * See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.6.0.0.md -# 3.4.0.0 [Oleg Grenrus](mailgo:oleg.grnerus@iki.fi) February 2021+# 3.4.1.0 [Emily Pillmore](mailto:emilypi@cohomolo.gy) October 2021+ * See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.4.1.0.md++# 3.4.0.0 [Oleg Grenrus](mailto:oleg.grnerus@iki.fi) February 2021 * See https://github.com/haskell/cabal/blob/master/release-notes/Cabal-3.4.0.0.md # 3.2.1.0 [Oleg Grenrus](mailto:oleg.grenris@iki.fi) October 2020@@ -743,7 +782,7 @@ * Many checks added for common mistakes * New `--package-db=` option for specific package databases * Many internal changes to support cabal-install- * Stricter parsing for version strings, eg dissalows "1.05"+ * Stricter parsing for version strings, eg disallows "1.05" * Improved user guide introduction * Programatica support removed * New options `--program-prefix/suffix` allows eg versioned programs
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2003-2020, Cabal Development Team.+Copyright (c) 2003-2025, Cabal Development Team. See the AUTHORS file for the full list of copyright holders. See */LICENSE for the copyright holders of the subcomponents.
README.md view
@@ -6,17 +6,17 @@ If you also want the `cabal` command-line program, you need the [cabal-install] package in addition to this library. -[cabal-install]: ../cabal-install/README.md+[cabal-install]: ../cabal-install More information ================ -Please see the [Cabal web site] for the [user guide] and [API+Please see the [Cabal web site], the [user guide] and the [API documentation]. There is additional information available on the [development wiki]. [user guide]: http://www.haskell.org/cabal/users-guide-[API documentation]: http://www.haskell.org/cabal/release/cabal-latest/doc/API/Cabal/Distribution-Simple.html+[API documentation]: https://hackage.haskell.org/package/Cabal/docs/Distribution-Simple.html [development wiki]: https://github.com/haskell/cabal/wiki @@ -34,8 +34,8 @@ [Hackage]. You may have a favourite Cabal bug or limitation. Look at Cabal's [bug tracker]. Ensure that the problem is reported there and adequately described. Comment on the issue to report how much of a-problem the bug is for you. Subscribe to the issues's notifications to-discussed requirements and keep informed on progress. For feature+problem the bug is for you. Subscribe to the issue's notifications to+discuss requirements and keep informed on progress. For feature requests, it is helpful if there is a description of how you would expect to interact with the new feature.
Setup.hs view
@@ -1,4 +1,5 @@ import Distribution.Simple+ main :: IO () main = defaultMain
− src/Distribution/Backpack.hs
@@ -1,269 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE PatternGuards #-}-{-# LANGUAGE RankNTypes #-}---- | This module defines the core data types for Backpack. For more--- details, see:------ <https://github.com/ezyang/ghc-proposals/blob/backpack/proposals/0000-backpack.rst>--module Distribution.Backpack (- -- * OpenUnitId- OpenUnitId(..),- openUnitIdFreeHoles,- mkOpenUnitId,-- -- * DefUnitId- DefUnitId,- unDefUnitId,- mkDefUnitId,-- -- * OpenModule- OpenModule(..),- openModuleFreeHoles,-- -- * OpenModuleSubst- OpenModuleSubst,- dispOpenModuleSubst,- dispOpenModuleSubstEntry,- parsecOpenModuleSubst,- parsecOpenModuleSubstEntry,- openModuleSubstFreeHoles,-- -- * Conversions to 'UnitId'- abstractUnitId,- hashModuleSubst,-) where--import Distribution.Compat.Prelude hiding (mod)-import Distribution.Parsec-import Distribution.Pretty-import Prelude ()-import Text.PrettyPrint (hcat)--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp--import Distribution.ModuleName-import Distribution.Types.ComponentId-import Distribution.Types.Module-import Distribution.Types.UnitId-import Distribution.Utils.Base62--import qualified Data.Map as Map-import qualified Data.Set as Set---------------------------------------------------------------------------- OpenUnitId---- | An 'OpenUnitId' describes a (possibly partially) instantiated--- Backpack component, with a description of how the holes are filled--- in. Unlike 'OpenUnitId', the 'ModuleSubst' is kept in a structured--- form that allows for substitution (which fills in holes.) This form--- of unit cannot be installed. It must first be converted to a--- 'UnitId'.------ In the absence of Backpack, there are no holes to fill, so any such--- component always has an empty module substitution; thus we can lossly--- represent it as an 'OpenUnitId uid'.------ For a source component using Backpack, however, there is more--- structure as components may be parametrized over some signatures, and--- these \"holes\" may be partially or wholly filled.------ OpenUnitId plays an important role when we are mix-in linking,--- and is recorded to the installed packaged database for indefinite--- packages; however, for compiled packages that are fully instantiated,--- we instantiate 'OpenUnitId' into 'UnitId'.------ For more details see the Backpack spec--- <https://github.com/ezyang/ghc-proposals/blob/backpack/proposals/0000-backpack.rst>-----data OpenUnitId- -- | Identifies a component which may have some unfilled holes;- -- specifying its 'ComponentId' and its 'OpenModuleSubst'.- -- TODO: Invariant that 'OpenModuleSubst' is non-empty?- -- See also the Text instance.- = IndefFullUnitId ComponentId OpenModuleSubst- -- | Identifies a fully instantiated component, which has- -- been compiled and abbreviated as a hash. The embedded 'UnitId'- -- MUST NOT be for an indefinite component; an 'OpenUnitId'- -- is guaranteed not to have any holes.- | DefiniteUnitId DefUnitId- deriving (Generic, Read, Show, Eq, Ord, Typeable, Data)--- TODO: cache holes?--instance Binary OpenUnitId-instance Structured OpenUnitId -instance NFData OpenUnitId where- rnf (IndefFullUnitId cid subst) = rnf cid `seq` rnf subst- rnf (DefiniteUnitId uid) = rnf uid--instance Pretty OpenUnitId where- pretty (IndefFullUnitId cid insts)- -- TODO: arguably a smart constructor to enforce invariant would be- -- better- | Map.null insts = pretty cid- | otherwise = pretty cid <<>> Disp.brackets (dispOpenModuleSubst insts)- pretty (DefiniteUnitId uid) = pretty uid---- |------ >>> eitherParsec "foobar" :: Either String OpenUnitId---Right (DefiniteUnitId (DefUnitId {unDefUnitId = UnitId "foobar"}))------ >>> eitherParsec "foo[Str=text-1.2.3:Data.Text.Text]" :: Either String OpenUnitId--- Right (IndefFullUnitId (ComponentId "foo") (fromList [(ModuleName "Str",OpenModule (DefiniteUnitId (DefUnitId {unDefUnitId = UnitId "text-1.2.3"})) (ModuleName "Data.Text.Text"))]))----instance Parsec OpenUnitId where- parsec = P.try parseOpenUnitId <|> fmap DefiniteUnitId parsec- where- parseOpenUnitId = do- cid <- parsec- insts <- P.between (P.char '[') (P.char ']')- parsecOpenModuleSubst- return (IndefFullUnitId cid insts)---- | Get the set of holes ('ModuleVar') embedded in a 'UnitId'.-openUnitIdFreeHoles :: OpenUnitId -> Set ModuleName-openUnitIdFreeHoles (IndefFullUnitId _ insts) = openModuleSubstFreeHoles insts-openUnitIdFreeHoles _ = Set.empty---- | Safe constructor from a UnitId. The only way to do this safely--- is if the instantiation is provided.-mkOpenUnitId :: UnitId -> ComponentId -> OpenModuleSubst -> OpenUnitId-mkOpenUnitId uid cid insts =- if Set.null (openModuleSubstFreeHoles insts)- then DefiniteUnitId (unsafeMkDefUnitId uid) -- invariant holds!- else IndefFullUnitId cid insts---------------------------------------------------------------------------- DefUnitId---- | Create a 'DefUnitId' from a 'ComponentId' and an instantiation--- with no holes.-mkDefUnitId :: ComponentId -> Map ModuleName Module -> DefUnitId-mkDefUnitId cid insts =- unsafeMkDefUnitId (mkUnitId- (unComponentId cid ++ maybe "" ("+"++) (hashModuleSubst insts)))- -- impose invariant!---------------------------------------------------------------------------- OpenModule---- | Unlike a 'Module', an 'OpenModule' is either an ordinary--- module from some unit, OR an 'OpenModuleVar', representing a--- hole that needs to be filled in. Substitutions are over--- module variables.-data OpenModule- = OpenModule OpenUnitId ModuleName- | OpenModuleVar ModuleName- deriving (Generic, Read, Show, Eq, Ord, Typeable, Data)--instance Binary OpenModule-instance Structured OpenModule--instance NFData OpenModule where- rnf (OpenModule uid mod_name) = rnf uid `seq` rnf mod_name- rnf (OpenModuleVar mod_name) = rnf mod_name--instance Pretty OpenModule where- pretty (OpenModule uid mod_name) =- hcat [pretty uid, Disp.text ":", pretty mod_name]- pretty (OpenModuleVar mod_name) =- hcat [Disp.char '<', pretty mod_name, Disp.char '>']---- |------ >>> eitherParsec "Includes2-0.1.0.0-inplace-mysql:Database.MySQL" :: Either String OpenModule--- Right (OpenModule (DefiniteUnitId (DefUnitId {unDefUnitId = UnitId "Includes2-0.1.0.0-inplace-mysql"})) (ModuleName "Database.MySQL"))----instance Parsec OpenModule where- parsec = parsecModuleVar <|> parsecOpenModule- where- parsecOpenModule = do- uid <- parsec- _ <- P.char ':'- mod_name <- parsec- return (OpenModule uid mod_name)-- parsecModuleVar = do- _ <- P.char '<'- mod_name <- parsec- _ <- P.char '>'- return (OpenModuleVar mod_name)---- | Get the set of holes ('ModuleVar') embedded in a 'Module'.-openModuleFreeHoles :: OpenModule -> Set ModuleName-openModuleFreeHoles (OpenModuleVar mod_name) = Set.singleton mod_name-openModuleFreeHoles (OpenModule uid _n) = openUnitIdFreeHoles uid---------------------------------------------------------------------------- OpenModuleSubst---- | An explicit substitution on modules.------ NB: These substitutions are NOT idempotent, for example, a--- valid substitution is (A -> B, B -> A).-type OpenModuleSubst = Map ModuleName OpenModule---- | Pretty-print the entries of a module substitution, suitable--- for embedding into a 'OpenUnitId' or passing to GHC via @--instantiate-with@.-dispOpenModuleSubst :: OpenModuleSubst -> Disp.Doc-dispOpenModuleSubst subst- = Disp.hcat- . Disp.punctuate Disp.comma- $ map dispOpenModuleSubstEntry (Map.toAscList subst)---- | Pretty-print a single entry of a module substitution.-dispOpenModuleSubstEntry :: (ModuleName, OpenModule) -> Disp.Doc-dispOpenModuleSubstEntry (k, v) = pretty k <<>> Disp.char '=' <<>> pretty v---- | Inverse to 'dispModSubst'.------ @since 2.2-parsecOpenModuleSubst :: CabalParsing m => m OpenModuleSubst-parsecOpenModuleSubst = fmap Map.fromList- . flip P.sepBy (P.char ',')- $ parsecOpenModuleSubstEntry---- | Inverse to 'dispModSubstEntry'.------ @since 2.2-parsecOpenModuleSubstEntry :: CabalParsing m => m (ModuleName, OpenModule)-parsecOpenModuleSubstEntry =- do k <- parsec- _ <- P.char '='- v <- parsec- return (k, v)---- | Get the set of holes ('ModuleVar') embedded in a 'OpenModuleSubst'.--- This is NOT the domain of the substitution.-openModuleSubstFreeHoles :: OpenModuleSubst -> Set ModuleName-openModuleSubstFreeHoles insts = Set.unions (map openModuleFreeHoles (Map.elems insts))---------------------------------------------------------------------------- Conversions to UnitId---- | When typechecking, we don't demand that a freshly instantiated--- 'IndefFullUnitId' be compiled; instead, we just depend on the--- installed indefinite unit installed at the 'ComponentId'.-abstractUnitId :: OpenUnitId -> UnitId-abstractUnitId (DefiniteUnitId def_uid) = unDefUnitId def_uid-abstractUnitId (IndefFullUnitId cid _) = newSimpleUnitId cid---- | Take a module substitution and hash it into a string suitable for--- 'UnitId'. Note that since this takes 'Module', not 'OpenModule',--- you are responsible for recursively converting 'OpenModule'--- into 'Module'. See also "Distribution.Backpack.ReadyComponent".-hashModuleSubst :: Map ModuleName Module -> Maybe String-hashModuleSubst subst- | Map.null subst = Nothing- | otherwise =- Just . hashToBase62 $- concat [ prettyShow mod_name ++ "=" ++ prettyShow m ++ "\n"- | (mod_name, m) <- Map.toList subst]
src/Distribution/Backpack/ComponentsGraph.hs view
@@ -1,24 +1,24 @@ -- | See <https://github.com/ezyang/ghc-proposals/blob/backpack/proposals/0000-backpack.rst>-module Distribution.Backpack.ComponentsGraph (- ComponentsGraph,- ComponentsWithDeps,- mkComponentsGraph,- componentsGraphToList,- dispComponentsWithDeps,- componentCycleMsg-) where+module Distribution.Backpack.ComponentsGraph+ ( ComponentsGraph+ , ComponentsWithDeps+ , mkComponentsGraph+ , componentsGraphToList+ , dispComponentsWithDeps+ , componentCycleMsg+ ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () +import Distribution.Compat.Graph (Graph, Node (..))+import qualified Distribution.Compat.Graph as Graph+import qualified Distribution.Compat.NonEmptySet as NES import Distribution.Package import Distribution.PackageDescription import Distribution.Simple.BuildToolDepends import Distribution.Simple.LocalBuildInfo import Distribution.Types.ComponentRequestedSpec-import Distribution.Compat.Graph (Graph, Node(..))-import qualified Distribution.Compat.Graph as Graph-import qualified Distribution.Compat.NonEmptySet as NES import Distribution.Utils.Generic import Distribution.Pretty (pretty)@@ -30,68 +30,76 @@ -- | A graph of source-level components by their source-level -- dependencies--- type ComponentsGraph = Graph (Node ComponentName Component) -- | A list of components associated with the source level -- dependencies between them.--- type ComponentsWithDeps = [(Component, [ComponentName])] -- | Pretty-print 'ComponentsWithDeps'.--- dispComponentsWithDeps :: ComponentsWithDeps -> Doc dispComponentsWithDeps graph =- vcat [ hang (text "component" <+> pretty (componentName c)) 4- (vcat [ text "dependency" <+> pretty cdep | cdep <- cdeps ])- | (c, cdeps) <- graph ]+ vcat+ [ hang+ (text "component" <+> pretty (componentName c))+ 4+ (vcat [text "dependency" <+> pretty cdep | cdep <- cdeps])+ | (c, cdeps) <- graph+ ] -- | Create a 'Graph' of 'Component', or report a cycle if there is a -- problem.----mkComponentsGraph :: ComponentRequestedSpec- -> PackageDescription- -> Either [ComponentName] ComponentsGraph+mkComponentsGraph+ :: ComponentRequestedSpec+ -> PackageDescription+ -> Either [ComponentName] ComponentsGraph mkComponentsGraph enabled pkg_descr =- let g = Graph.fromDistinctList- [ N c (componentName c) (componentDeps c)- | c <- pkgBuildableComponents pkg_descr- , componentEnabled enabled c ]- in case Graph.cycles g of- [] -> Right g- ccycles -> Left [ componentName c | N c _ _ <- concat ccycles ]+ let g =+ Graph.fromDistinctList+ [ N c (componentName c) (componentDeps c)+ | c <- pkgBuildableComponents pkg_descr+ , componentEnabled enabled c+ ]+ in case Graph.cycles g of+ [] -> Right g+ ccycles -> Left [componentName c | N c _ _ <- concat ccycles] where -- The dependencies for the given component componentDeps component =- toolDependencies ++ libDependencies+ toolDependencies ++ libDependencies where bi = componentBuildInfo component toolDependencies = CExeName <$> getAllInternalToolDependencies pkg_descr bi libDependencies = do- Dependency pkgname _ lns <- targetBuildDepends bi- guard (pkgname == packageName pkg_descr)+ Dependency pkgname _ lns <- targetBuildDepends bi+ guard (pkgname == packageName pkg_descr) - ln <- NES.toList lns- return (CLibName ln)+ ln <- NES.toList lns+ return (CLibName ln) -- | Given the package description and a 'PackageDescription' (used -- to determine if a package name is internal or not), sort the -- components in dependency order (fewest dependencies first). This is -- NOT necessarily the build order (although it is in the absence of -- Backpack.)----componentsGraphToList :: ComponentsGraph- -> ComponentsWithDeps+componentsGraphToList+ :: ComponentsGraph+ -> ComponentsWithDeps componentsGraphToList =- map (\(N c _ cs) -> (c, cs)) . Graph.revTopSort+ map (\(N c _ cs) -> (c, cs)) . Graph.revTopSort -- | Error message when there is a cycle; takes the SCC of components. componentCycleMsg :: PackageIdentifier -> [ComponentName] -> Doc componentCycleMsg pn cnames =- text "Components in the package" <+> pretty pn <+> text "depend on each other in a cyclic way:"- $$- text (intercalate " depends on "- [ "'" ++ showComponentName cname ++ "'"- | cname <- cnames ++ maybeToList (safeHead cnames) ])+ text "Components in the package"+ <+> pretty pn+ <+> text "depend on each other in a cyclic way:"+ $$ text+ ( intercalate+ " depends on "+ [ "'" ++ showComponentName cname ++ "'"+ | cname <- cnames ++ maybeToList (safeHead cnames)+ ]+ )
src/Distribution/Backpack/Configure.hs view
@@ -1,56 +1,58 @@-{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE NondecreasingIndentation #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE PatternGuards #-}+{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE NoMonoLocalBinds #-}-{-# LANGUAGE NondecreasingIndentation #-} -- | See <https://github.com/ezyang/ghc-proposals/blob/backpack/proposals/0000-backpack.rst> -- -- WARNING: The contents of this module are HIGHLY experimental. -- We may refactor it under you.-module Distribution.Backpack.Configure (- configureComponentLocalBuildInfos,-) where+module Distribution.Backpack.Configure+ ( configureComponentLocalBuildInfos+ ) where -import Prelude () import Distribution.Compat.Prelude hiding ((<>))+import Prelude () import Distribution.Backpack-import Distribution.Backpack.FullUnitId-import Distribution.Backpack.PreExistingComponent+import Distribution.Backpack.ComponentsGraph import Distribution.Backpack.ConfiguredComponent+import Distribution.Backpack.FullUnitId+import Distribution.Backpack.Id import Distribution.Backpack.LinkedComponent+import Distribution.Backpack.PreExistingComponent import Distribution.Backpack.ReadyComponent-import Distribution.Backpack.ComponentsGraph-import Distribution.Backpack.Id -import Distribution.Simple.Compiler-import Distribution.Package+import Distribution.Backpack.ModuleShape+import Distribution.Compat.Graph (Graph, IsNode (..))+import qualified Distribution.Compat.Graph as Graph+import Distribution.InstalledPackageInfo+ ( InstalledPackageInfo+ , emptyInstalledPackageInfo+ ) import qualified Distribution.InstalledPackageInfo as Installed-import Distribution.InstalledPackageInfo (InstalledPackageInfo- ,emptyInstalledPackageInfo)-import qualified Distribution.Simple.PackageIndex as PackageIndex-import Distribution.Simple.PackageIndex (InstalledPackageIndex)-import Distribution.PackageDescription import Distribution.ModuleName-import Distribution.Simple.Setup as Setup+import Distribution.Package+import Distribution.PackageDescription (FlagAssignment, PackageDescription (..), libName)+import Distribution.Simple.Compiler+import Distribution.Simple.Flag import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.PackageIndex (InstalledPackageIndex)+import qualified Distribution.Simple.PackageIndex as PackageIndex import Distribution.Types.AnnotatedId-import Distribution.Types.ComponentRequestedSpec import Distribution.Types.ComponentInclude+import Distribution.Types.ComponentRequestedSpec import Distribution.Types.MungedPackageName-import Distribution.Verbosity-import qualified Distribution.Compat.Graph as Graph-import Distribution.Compat.Graph (Graph, IsNode(..)) import Distribution.Utils.LogProgress+import Distribution.Verbosity import Data.Either- ( lefts )-import qualified Data.Set as Set+ ( lefts+ ) import qualified Data.Map as Map+import qualified Data.Set as Set import Distribution.Pretty import Text.PrettyPrint @@ -59,144 +61,235 @@ ------------------------------------------------------------------------------ configureComponentLocalBuildInfos- :: Verbosity- -> Bool -- use_external_internal_deps- -> ComponentRequestedSpec- -> Bool -- deterministic- -> Flag String -- configIPID- -> Flag ComponentId -- configCID- -> PackageDescription- -> [PreExistingComponent]- -> FlagAssignment -- configConfigurationsFlags- -> [(ModuleName, Module)] -- configInstantiateWith- -> InstalledPackageIndex- -> Compiler- -> LogProgress ([ComponentLocalBuildInfo], InstalledPackageIndex)+ :: Verbosity+ -> Bool -- use_external_internal_deps+ -> ComponentRequestedSpec+ -> Bool -- deterministic+ -> Flag String -- configIPID+ -> Flag ComponentId -- configCID+ -> PackageDescription+ -> ([PreExistingComponent], [ConfiguredPromisedComponent])+ -> FlagAssignment -- configConfigurationsFlags+ -> [(ModuleName, Module)] -- configInstantiateWith+ -> InstalledPackageIndex+ -> Compiler+ -> LogProgress ([ComponentLocalBuildInfo], InstalledPackageIndex) configureComponentLocalBuildInfos- verbosity use_external_internal_deps enabled deterministic ipid_flag cid_flag pkg_descr- prePkgDeps flagAssignment instantiate_with installedPackageSet comp = do+ verbosity+ use_external_internal_deps+ enabled+ deterministic+ ipid_flag+ cid_flag+ pkg_descr+ (prePkgDeps, promisedPkgDeps)+ flags+ instantiate_with+ installedPackageSet+ comp = do -- NB: In single component mode, this returns a *single* component. -- In this graph, the graph is NOT closed. graph0 <- case mkComponentsGraph enabled pkg_descr of- Left ccycle -> dieProgress (componentCycleMsg (package pkg_descr) ccycle)- Right g -> return (componentsGraphToList g)- infoProgress $ hang (text "Source component graph:") 4- (dispComponentsWithDeps graph0)+ Left ccycle -> dieProgress (componentCycleMsg (package pkg_descr) ccycle)+ Right g -> return (componentsGraphToList g)+ infoProgress $+ hang+ (text "Source component graph:")+ 4+ (dispComponentsWithDeps graph0) - let conf_pkg_map = Map.fromListWith Map.union- [(pc_pkgname pkg,- Map.singleton (pc_compname pkg)- (AnnotatedId {- ann_id = pc_cid pkg,- ann_pid = packageId pkg,- ann_cname = pc_compname pkg- }))- | pkg <- prePkgDeps]- graph1 <- toConfiguredComponents use_external_internal_deps- flagAssignment- deterministic ipid_flag cid_flag pkg_descr- conf_pkg_map (map fst graph0)- infoProgress $ hang (text "Configured component graph:") 4- (vcat (map dispConfiguredComponent graph1))+ let conf_pkg_map =+ Map.fromListWith+ Map.union+ $+ -- Normal dependencies+ [ ( pc_pkgname pkg+ , Map.singleton+ (pc_compname pkg)+ ( AnnotatedId+ { ann_id = pc_cid pkg+ , ann_pid = packageId pkg+ , ann_cname = pc_compname pkg+ }+ )+ )+ | pkg <- prePkgDeps+ ]+ +++ -- Promised dependencies+ [ (pkg, Map.singleton (ann_cname aid) aid)+ | ConfiguredPromisedComponent pkg aid <- promisedPkgDeps+ ]+ graph1 <-+ toConfiguredComponents+ use_external_internal_deps+ flags+ deterministic+ ipid_flag+ cid_flag+ pkg_descr+ conf_pkg_map+ (map fst graph0)+ infoProgress $+ hang+ (text "Configured component graph:")+ 4+ (vcat (map dispConfiguredComponent graph1)) - let shape_pkg_map = Map.fromList+ let shape_pkg_map =+ Map.fromList [ (pc_cid pkg, (pc_open_uid pkg, pc_shape pkg))- | pkg <- prePkgDeps]+ | pkg <- prePkgDeps+ ]+ `Map.union` Map.fromList+ [ ( ann_id aid+ ,+ ( DefiniteUnitId+ ( unsafeMkDefUnitId+ (mkUnitId (unComponentId (ann_id aid)))+ )+ , emptyModuleShape+ )+ )+ | ConfiguredPromisedComponent _ aid <- promisedPkgDeps+ ] uid_lookup def_uid- | Just pkg <- PackageIndex.lookupUnitId installedPackageSet uid- = FullUnitId (Installed.installedComponentId pkg)- (Map.fromList (Installed.instantiatedWith pkg))- | otherwise = error ("uid_lookup: " ++ prettyShow uid)- where uid = unDefUnitId def_uid- graph2 <- toLinkedComponents verbosity uid_lookup- (package pkg_descr) shape_pkg_map graph1+ | Just pkg <- PackageIndex.lookupUnitId installedPackageSet uid =+ FullUnitId+ (Installed.installedComponentId pkg)+ (Map.fromList (Installed.instantiatedWith pkg))+ | otherwise = error ("uid_lookup: " ++ prettyShow uid)+ where+ uid = unDefUnitId def_uid+ graph2 <-+ toLinkedComponents+ verbosity+ (not (null promisedPkgDeps))+ uid_lookup+ (package pkg_descr)+ shape_pkg_map+ graph1 infoProgress $- hang (text "Linked component graph:") 4- (vcat (map dispLinkedComponent graph2))+ hang+ (text "Linked component graph:")+ 4+ (vcat (map dispLinkedComponent graph2)) - let pid_map = Map.fromList $+ let pid_map =+ Map.fromList $ [ (pc_uid pkg, pc_munged_id pkg)- | pkg <- prePkgDeps] ++- [ (Installed.installedUnitId pkg, mungedId pkg)- | (_, Module uid _) <- instantiate_with- , Just pkg <- [PackageIndex.lookupUnitId- installedPackageSet (unDefUnitId uid)] ]+ | pkg <- prePkgDeps+ ]+ ++ [ (Installed.installedUnitId pkg, mungedId pkg)+ | (_, Module uid _) <- instantiate_with+ , Just pkg <-+ [ PackageIndex.lookupUnitId+ installedPackageSet+ (unDefUnitId uid)+ ]+ ] subst = Map.fromList instantiate_with graph3 = toReadyComponents pid_map subst graph2 graph4 = Graph.revTopSort (Graph.fromDistinctList graph3) - infoProgress $ hang (text "Ready component graph:") 4- (vcat (map dispReadyComponent graph4))+ infoProgress $+ hang+ (text "Ready component graph:")+ 4+ (vcat (map dispReadyComponent graph4)) - toComponentLocalBuildInfos comp installedPackageSet pkg_descr prePkgDeps graph4+ toComponentLocalBuildInfos comp installedPackageSet promisedPkgDeps pkg_descr prePkgDeps graph4 ------------------------------------------------------------------------------ -- ComponentLocalBuildInfo ------------------------------------------------------------------------------ toComponentLocalBuildInfos- :: Compiler- -> InstalledPackageIndex -- FULL set- -> PackageDescription- -> [PreExistingComponent] -- external package deps- -> [ReadyComponent]- -> LogProgress ([ComponentLocalBuildInfo],- InstalledPackageIndex) -- only relevant packages+ :: Compiler+ -> InstalledPackageIndex -- FULL set+ -> [ConfiguredPromisedComponent]+ -> PackageDescription+ -> [PreExistingComponent] -- external package deps+ -> [ReadyComponent]+ -> LogProgress+ ( [ComponentLocalBuildInfo]+ , InstalledPackageIndex -- only relevant packages+ ) toComponentLocalBuildInfos- comp installedPackageSet pkg_descr externalPkgDeps graph = do+ comp+ installedPackageSet+ promisedPkgDeps+ pkg_descr+ externalPkgDeps+ graph = do -- Check and make sure that every instantiated component exists. -- We have to do this now, because prior to linking/instantiating -- we don't actually know what the full set of 'UnitId's we need -- are.- let -- TODO: This is actually a bit questionable performance-wise,- -- since we will pay for the ALL installed packages even if- -- they are not related to what we are building. This was true- -- in the old configure code.- external_graph :: Graph (Either InstalledPackageInfo ReadyComponent)- external_graph = Graph.fromDistinctList- . map Left- $ PackageIndex.allPackages installedPackageSet- internal_graph :: Graph (Either InstalledPackageInfo ReadyComponent)- internal_graph = Graph.fromDistinctList- . map Right- $ graph- combined_graph = Graph.unionRight external_graph internal_graph- local_graph = fromMaybe (error "toComponentLocalBuildInfos: closure returned Nothing")- $ Graph.closure combined_graph (map nodeKey graph)- -- The database of transitively reachable installed packages that the- -- external components the package (as a whole) depends on. This will be- -- used in several ways:- --- -- * We'll use it to do a consistency check so we're not depending- -- on multiple versions of the same package (TODO: someday relax- -- this for private dependencies.) See right below.- --- -- * We'll pass it on in the LocalBuildInfo, where preprocessors- -- and other things will incorrectly use it to determine what- -- the include paths and everything should be.- --- packageDependsIndex = PackageIndex.fromList (lefts local_graph)- fullIndex = Graph.fromDistinctList local_graph+ let+ -- TODO: This is actually a bit questionable performance-wise,+ -- since we will pay for the ALL installed packages even if+ -- they are not related to what we are building. This was true+ -- in the old configure code.+ external_graph :: Graph (Either InstalledPackageInfo ReadyComponent)+ external_graph =+ Graph.fromDistinctList+ . map Left+ $ PackageIndex.allPackages installedPackageSet+ internal_graph :: Graph (Either InstalledPackageInfo ReadyComponent)+ internal_graph =+ Graph.fromDistinctList+ . map Right+ $ graph+ combined_graph = Graph.unionRight external_graph internal_graph+ local_graph =+ fromMaybe (error "toComponentLocalBuildInfos: closure returned Nothing") $+ Graph.closure combined_graph (map nodeKey graph)+ -- The database of transitively reachable installed packages that the+ -- external components the package (as a whole) depends on. This will be+ -- used in several ways:+ --+ -- * We'll use it to do a consistency check so we're not depending+ -- on multiple versions of the same package (TODO: someday relax+ -- this for private dependencies.) See right below.+ --+ -- * We'll pass it on in the LocalBuildInfo, where preprocessors+ -- and other things will incorrectly use it to determine what+ -- the include paths and everything should be.+ --+ packageDependsIndex = PackageIndex.fromList (lefts local_graph)+ fullIndex = Graph.fromDistinctList local_graph+ case Graph.broken fullIndex of- [] -> return ()- broken ->- -- TODO: ppr this- dieProgress . text $- "The following packages are broken because other"- ++ " packages they depend on are missing. These broken "- ++ "packages must be rebuilt before they can be used.\n"- -- TODO: Undupe.- ++ unlines [ "installed package "- ++ prettyShow (packageId pkg)- ++ " is broken due to missing package "- ++ intercalate ", " (map prettyShow deps)- | (Left pkg, deps) <- broken ]- ++ unlines [ "planned package "- ++ prettyShow (packageId pkg)- ++ " is broken due to missing package "- ++ intercalate ", " (map prettyShow deps)- | (Right pkg, deps) <- broken ]+ [] -> return ()+ -- If there are promised dependencies, we don't know what the dependencies+ -- of these are and that can easily lead to a broken graph. So assume that+ -- any promised package is not broken (ie all its dependencies, transitively,+ -- will be there). That's a promise.+ broken+ | not (null promisedPkgDeps) -> return ()+ | otherwise ->+ -- TODO: ppr this+ dieProgress . text $+ "The following packages are broken because other"+ ++ " packages they depend on are missing. These broken "+ ++ "packages must be rebuilt before they can be used.\n"+ -- TODO: Undupe.+ ++ unlines+ [ "installed package "+ ++ prettyShow (packageId pkg)+ ++ " is broken due to missing package "+ ++ intercalate ", " (map prettyShow deps)+ | (Left pkg, deps) <- broken+ ]+ ++ unlines+ [ "planned package "+ ++ prettyShow (packageId pkg)+ ++ " is broken due to missing package "+ ++ intercalate ", " (map prettyShow deps)+ | (Right pkg, deps) <- broken+ ] -- In this section, we'd like to look at the 'packageDependsIndex' -- and see if we've picked multiple versions of the same@@ -213,25 +306,34 @@ -- -- TODO: This is probably wrong for Backpack let pseudoTopPkg :: InstalledPackageInfo- pseudoTopPkg = emptyInstalledPackageInfo {- Installed.installedUnitId = mkLegacyUnitId (packageId pkg_descr),- Installed.sourcePackageId = packageId pkg_descr,- Installed.depends = map pc_uid externalPkgDeps- }+ pseudoTopPkg =+ emptyInstalledPackageInfo+ { Installed.installedUnitId = mkLegacyUnitId (packageId pkg_descr)+ , Installed.sourcePackageId = packageId pkg_descr+ , Installed.depends = map pc_uid externalPkgDeps+ } case PackageIndex.dependencyInconsistencies- . PackageIndex.insert pseudoTopPkg- $ packageDependsIndex of+ . PackageIndex.insert pseudoTopPkg+ $ packageDependsIndex of [] -> return () inconsistencies -> warnProgress $- hang (text "This package indirectly depends on multiple versions of the same" <+>- text "package. This is very likely to cause a compile failure.") 2- (vcat [ text "package" <+> pretty (packageName user) <+>- parens (pretty (installedUnitId user)) <+> text "requires" <+>- pretty inst- | (_dep_key, insts) <- inconsistencies- , (inst, users) <- insts- , user <- users ])+ hang+ ( text "This package indirectly depends on multiple versions of the same"+ <+> text "package. This is very likely to cause a compile failure."+ )+ 2+ ( vcat+ [ text "package"+ <+> pretty (packageName user)+ <+> parens (pretty (installedUnitId user))+ <+> text "requires"+ <+> pretty inst+ | (_dep_key, insts) <- inconsistencies+ , (inst, users) <- insts+ , user <- users+ ]+ ) let clbis = mkLinkedComponentsLocalBuildInfo comp graph -- forM clbis $ \(clbi,deps) -> info verbosity $ "UNIT" ++ hashUnitId (componentUnitId clbi) ++ "\n" ++ intercalate "\n" (map hashUnitId deps) return (clbis, packageDependsIndex)@@ -241,119 +343,124 @@ -- -- This conversion is lossy; we lose some invariants from ReadyComponent mkLinkedComponentsLocalBuildInfo- :: Compiler- -> [ReadyComponent]- -> [ComponentLocalBuildInfo]+ :: Compiler+ -> [ReadyComponent]+ -> [ComponentLocalBuildInfo] mkLinkedComponentsLocalBuildInfo comp rcs = map go rcs where internalUnits = Set.fromList (map rc_uid rcs) isInternal x = Set.member x internalUnits go rc = case rc_component rc of- CLib lib ->- let convModuleExport (modname', (Module uid modname))- | this_uid == unDefUnitId uid- , modname' == modname- = Installed.ExposedModule modname' Nothing- | otherwise- = Installed.ExposedModule modname'- (Just (OpenModule (DefiniteUnitId uid) modname))- convOpenModuleExport (modname', modu@(OpenModule uid modname))- | uid == this_open_uid- , modname' == modname- = Installed.ExposedModule modname' Nothing- | otherwise- = Installed.ExposedModule modname' (Just modu)- convOpenModuleExport (_, OpenModuleVar _)- = error "convOpenModuleExport: top-level modvar"- exports =+ CLib lib ->+ let convModuleExport (modname', (Module uid modname))+ | this_uid == unDefUnitId uid+ , modname' == modname =+ Installed.ExposedModule modname' Nothing+ | otherwise =+ Installed.ExposedModule+ modname'+ (Just (OpenModule (DefiniteUnitId uid) modname))+ convOpenModuleExport (modname', modu@(OpenModule uid modname))+ | uid == this_open_uid+ , modname' == modname =+ Installed.ExposedModule modname' Nothing+ | otherwise =+ Installed.ExposedModule modname' (Just modu)+ convOpenModuleExport (_, OpenModuleVar _) =+ error "convOpenModuleExport: top-level modvar"+ exports = -- Loses invariants case rc_i rc of- Left indefc -> map convOpenModuleExport- $ Map.toList (indefc_provides indefc)- Right instc -> map convModuleExport- $ Map.toList (instc_provides instc)- insts =+ Left indefc ->+ map convOpenModuleExport $+ Map.toList (indefc_provides indefc)+ Right instc ->+ map convModuleExport $+ Map.toList (instc_provides instc)+ insts = case rc_i rc of- Left indefc -> [ (m, OpenModuleVar m) | m <- indefc_requires indefc ]- Right instc -> [ (m, OpenModule (DefiniteUnitId uid') m')- | (m, Module uid' m') <- instc_insts instc ]-- compat_name = MungedPackageName (packageName rc) (libName lib)- compat_key = computeCompatPackageKey comp compat_name (packageVersion rc) this_uid+ Left indefc -> [(m, OpenModuleVar m) | m <- indefc_requires indefc]+ Right instc ->+ [ (m, OpenModule (DefiniteUnitId uid') m')+ | (m, Module uid' m') <- instc_insts instc+ ] - in LibComponentLocalBuildInfo {- componentPackageDeps = cpds,- componentUnitId = this_uid,- componentComponentId = this_cid,- componentInstantiatedWith = insts,- componentIsIndefinite_ = is_indefinite,- componentLocalName = cname,- componentInternalDeps = internal_deps,- componentExeDeps = exe_deps,- componentIncludes = includes,- componentExposedModules = exports,- componentIsPublic = rc_public rc,- componentCompatPackageKey = compat_key,- componentCompatPackageName = compat_name- }- CFLib _ ->- FLibComponentLocalBuildInfo {- componentUnitId = this_uid,- componentComponentId = this_cid,- componentLocalName = cname,- componentPackageDeps = cpds,- componentExeDeps = exe_deps,- componentInternalDeps = internal_deps,- componentIncludes = includes- }- CExe _ ->- ExeComponentLocalBuildInfo {- componentUnitId = this_uid,- componentComponentId = this_cid,- componentLocalName = cname,- componentPackageDeps = cpds,- componentExeDeps = exe_deps,- componentInternalDeps = internal_deps,- componentIncludes = includes- }- CTest _ ->- TestComponentLocalBuildInfo {- componentUnitId = this_uid,- componentComponentId = this_cid,- componentLocalName = cname,- componentPackageDeps = cpds,- componentExeDeps = exe_deps,- componentInternalDeps = internal_deps,- componentIncludes = includes- }- CBench _ ->- BenchComponentLocalBuildInfo {- componentUnitId = this_uid,- componentComponentId = this_cid,- componentLocalName = cname,- componentPackageDeps = cpds,- componentExeDeps = exe_deps,- componentInternalDeps = internal_deps,- componentIncludes = includes- }- where- this_uid = rc_uid rc- this_open_uid = rc_open_uid rc- this_cid = rc_cid rc- cname = componentName (rc_component rc)- cpds = rc_depends rc- exe_deps = map ann_id $ rc_exe_deps rc- is_indefinite =- case rc_i rc of+ compat_name = MungedPackageName (packageName rc) (libName lib)+ compat_key = computeCompatPackageKey comp compat_name (packageVersion rc) this_uid+ in LibComponentLocalBuildInfo+ { componentPackageDeps = cpds+ , componentUnitId = this_uid+ , componentComponentId = this_cid+ , componentInstantiatedWith = insts+ , componentIsIndefinite_ = is_indefinite+ , componentLocalName = cname+ , componentInternalDeps = internal_deps+ , componentExeDeps = exe_deps+ , componentIncludes = includes+ , componentExposedModules = exports+ , componentIsPublic = rc_public rc+ , componentCompatPackageKey = compat_key+ , componentCompatPackageName = compat_name+ }+ CFLib _ ->+ FLibComponentLocalBuildInfo+ { componentUnitId = this_uid+ , componentComponentId = this_cid+ , componentLocalName = cname+ , componentPackageDeps = cpds+ , componentExeDeps = exe_deps+ , componentInternalDeps = internal_deps+ , componentIncludes = includes+ }+ CExe _ ->+ ExeComponentLocalBuildInfo+ { componentUnitId = this_uid+ , componentComponentId = this_cid+ , componentLocalName = cname+ , componentPackageDeps = cpds+ , componentExeDeps = exe_deps+ , componentInternalDeps = internal_deps+ , componentIncludes = includes+ }+ CTest _ ->+ TestComponentLocalBuildInfo+ { componentUnitId = this_uid+ , componentComponentId = this_cid+ , componentLocalName = cname+ , componentPackageDeps = cpds+ , componentExeDeps = exe_deps+ , componentInternalDeps = internal_deps+ , componentIncludes = includes+ }+ CBench _ ->+ BenchComponentLocalBuildInfo+ { componentUnitId = this_uid+ , componentComponentId = this_cid+ , componentLocalName = cname+ , componentPackageDeps = cpds+ , componentExeDeps = exe_deps+ , componentInternalDeps = internal_deps+ , componentIncludes = includes+ }+ where+ this_uid = rc_uid rc+ this_open_uid = rc_open_uid rc+ this_cid = rc_cid rc+ cname = componentName (rc_component rc)+ cpds = rc_depends rc+ exe_deps = map ann_id $ rc_exe_deps rc+ is_indefinite =+ case rc_i rc of Left _ -> True Right _ -> False- includes =- map (\ci -> (ci_id ci, ci_renaming ci)) $+ includes =+ map (\ci -> (ci_id ci, ci_renaming ci)) $ case rc_i rc of- Left indefc ->- indefc_includes indefc- Right instc ->- map (\ci -> ci { ci_ann_id = fmap DefiniteUnitId (ci_ann_id ci) })- (instc_includes instc)- internal_deps = filter isInternal (nodeNeighbors rc)+ Left indefc ->+ indefc_includes indefc+ Right instc ->+ map+ (\ci -> ci{ci_ann_id = fmap DefiniteUnitId (ci_ann_id ci)})+ (instc_includes instc)+ internal_deps = filter isInternal (nodeNeighbors rc)
src/Distribution/Backpack/ConfiguredComponent.hs view
@@ -1,80 +1,66 @@-{-# LANGUAGE PatternGuards #-} -- | See <https://github.com/ezyang/ghc-proposals/blob/backpack/proposals/0000-backpack.rst>-module Distribution.Backpack.ConfiguredComponent (- ConfiguredComponent(..),- cc_name,- cc_cid,- cc_pkgid,- toConfiguredComponent,- toConfiguredComponents,- dispConfiguredComponent,-- ConfiguredComponentMap,- extendConfiguredComponentMap,-- -- TODO: Should go somewhere else- newPackageDepsBehaviour-) where+module Distribution.Backpack.ConfiguredComponent+ ( ConfiguredComponent (..)+ , cc_name+ , cc_cid+ , cc_pkgid+ , toConfiguredComponent+ , toConfiguredComponents+ , dispConfiguredComponent+ , ConfiguredComponentMap+ , extendConfiguredComponentMap+ -- TODO: Should go somewhere else+ , newPackageDepsBehaviour+ ) where -import Prelude () import Distribution.Compat.Prelude hiding ((<>))+import Prelude () import Distribution.Backpack.Id import Distribution.CabalSpecVersion-import Distribution.Types.AnnotatedId-import Distribution.Types.Dependency-import Distribution.Types.ExeDependency-import Distribution.Types.IncludeRenaming-import Distribution.Types.ComponentId-import Distribution.Types.PackageId-import Distribution.Types.PackageName-import Distribution.Types.Mixin-import Distribution.Types.ComponentName-import Distribution.Types.LibraryName-import Distribution.Types.ComponentInclude import Distribution.Package import Distribution.PackageDescription import Distribution.Simple.BuildToolDepends-import Distribution.Simple.Setup as Setup+import Distribution.Simple.Flag (Flag) import Distribution.Simple.LocalBuildInfo+import Distribution.Types.AnnotatedId+import Distribution.Types.ComponentInclude+import Distribution.Utils.Generic import Distribution.Utils.LogProgress import Distribution.Utils.MapAccum-import Distribution.Utils.Generic import Control.Monad+import qualified Data.Map as Map import qualified Data.Set as Set import qualified Distribution.Compat.NonEmptySet as NonEmptySet-import qualified Data.Map as Map import Distribution.Pretty-import Text.PrettyPrint (Doc, hang, text, vcat, hsep, quotes, ($$))+import Text.PrettyPrint (Doc, hang, hsep, quotes, text, vcat, ($$)) import qualified Text.PrettyPrint as PP -- | A configured component, we know exactly what its 'ComponentId' is, -- and the 'ComponentId's of the things it depends on.-data ConfiguredComponent- = ConfiguredComponent {- -- | Unique identifier of component, plus extra useful info.- cc_ann_id :: AnnotatedId ComponentId,- -- | The fragment of syntax from the Cabal file describing this- -- component.- cc_component :: Component,- -- | Is this the public library component of the package?- -- (If we invoke Setup with an instantiation, this is the- -- component the instantiation applies to.)- -- Note that in one-component configure mode, this is- -- always True, because any component is the "public" one.)- cc_public :: Bool,- -- | Dependencies on executables from @build-tools@ and- -- @build-tool-depends@.- cc_exe_deps :: [AnnotatedId ComponentId],- -- | The mixins of this package, including both explicit (from- -- the @mixins@ field) and implicit (from @build-depends@). Not- -- mix-in linked yet; component configuration only looks at- -- 'ComponentId's.- cc_includes :: [ComponentInclude ComponentId IncludeRenaming]- }-+data ConfiguredComponent = ConfiguredComponent+ { cc_ann_id :: AnnotatedId ComponentId+ -- ^ Unique identifier of component, plus extra useful info.+ , cc_component :: Component+ -- ^ The fragment of syntax from the Cabal file describing this+ -- component.+ , cc_public :: Bool+ -- ^ Is this the public library component of the package?+ -- (If we invoke Setup with an instantiation, this is the+ -- component the instantiation applies to.)+ -- Note that in one-component configure mode, this is+ -- always True, because any component is the "public" one.)+ , cc_exe_deps :: [AnnotatedId ComponentId]+ -- ^ Dependencies on executables from @build-tools@ and+ -- @build-tool-depends@.+ , cc_includes :: [ComponentInclude ComponentId IncludeRenaming]+ -- ^ The mixins of this package, including both explicit (from+ -- the @mixins@ field) and implicit (from @build-depends@). Not+ -- mix-in linked yet; component configuration only looks at+ -- 'ComponentId's.+ } -- | Uniquely identifies a configured component. cc_cid :: ConfiguredComponent -> ComponentId@@ -93,110 +79,132 @@ -- | Pretty-print a 'ConfiguredComponent'. dispConfiguredComponent :: ConfiguredComponent -> Doc dispConfiguredComponent cc =- hang (text "component" <+> pretty (cc_cid cc)) 4- (vcat [ hsep $ [ text "include"- , pretty (ci_id incl), pretty (ci_renaming incl) ]- | incl <- cc_includes cc- ])+ hang+ (text "component" <+> pretty (cc_cid cc))+ 4+ ( vcat+ [ hsep $+ [ text "include"+ , pretty (ci_id incl)+ , pretty (ci_renaming incl)+ ]+ | incl <- cc_includes cc+ ]+ ) -- | Construct a 'ConfiguredComponent', given that the 'ComponentId' -- and library/executable dependencies are known. The primary -- work this does is handling implicit @backpack-include@ fields. mkConfiguredComponent- :: PackageDescription- -> ComponentId- -> [AnnotatedId ComponentId] -- lib deps- -> [AnnotatedId ComponentId] -- exe deps- -> Component- -> LogProgress ConfiguredComponent+ :: PackageDescription+ -> ComponentId+ -> [AnnotatedId ComponentId] -- lib deps+ -> [AnnotatedId ComponentId] -- exe deps+ -> Component+ -> LogProgress ConfiguredComponent mkConfiguredComponent pkg_descr this_cid lib_deps exe_deps component = do- -- Resolve each @mixins@ into the actual dependency- -- from @lib_deps@.- explicit_includes <- forM (mixins bi) $ \(Mixin pn ln rns) -> do- aid <- case Map.lookup (pn, CLibName ln) deps_map of- Nothing ->- dieProgress $- text "Mix-in refers to non-existent library" <+>- quotes (pretty pn <<>> prettyLN ln) $$- text "(did you forget to add the package to build-depends?)"- Just r -> return r- return ComponentInclude {- ci_ann_id = aid,- ci_renaming = rns,- ci_implicit = False- }+ -- Resolve each @mixins@ into the actual dependency+ -- from @lib_deps@.+ explicit_includes <- forM (mixins bi) $ \(Mixin pn ln rns) -> do+ aid <- case Map.lookup (pn, CLibName ln) deps_map of+ Nothing ->+ dieProgress $+ text "Mix-in refers to non-existent library"+ <+> quotes (pretty pn <<>> prettyLN ln)+ $$ text "(did you forget to add the package to build-depends?)"+ Just r -> return r+ return+ ComponentInclude+ { ci_ann_id = aid+ , ci_renaming = rns+ , ci_implicit = False+ } - -- Any @build-depends@ which is not explicitly mentioned in- -- @backpack-include@ is converted into an "implicit" include.- let used_explicitly = Set.fromList (map ci_id explicit_includes)- implicit_includes- = map (\aid -> ComponentInclude {- ci_ann_id = aid,- ci_renaming = defaultIncludeRenaming,- ci_implicit = True- })- $ filter (flip Set.notMember used_explicitly . ann_id) lib_deps+ -- Any @build-depends@ which is not explicitly mentioned in+ -- @backpack-include@ is converted into an "implicit" include.+ let used_explicitly = Set.fromList (map ci_id explicit_includes)+ implicit_includes =+ map+ ( \aid ->+ ComponentInclude+ { ci_ann_id = aid+ , ci_renaming = defaultIncludeRenaming+ , ci_implicit = True+ }+ )+ $ filter (flip Set.notMember used_explicitly . ann_id) lib_deps - return ConfiguredComponent {- cc_ann_id = AnnotatedId {- ann_id = this_cid,- ann_pid = package pkg_descr,- ann_cname = componentName component- },- cc_component = component,- cc_public = is_public,- cc_exe_deps = exe_deps,- cc_includes = explicit_includes ++ implicit_includes- }+ return+ ConfiguredComponent+ { cc_ann_id =+ AnnotatedId+ { ann_id = this_cid+ , ann_pid = package pkg_descr+ , ann_cname = componentName component+ }+ , cc_component = component+ , cc_public = is_public+ , cc_exe_deps = exe_deps+ , cc_includes = explicit_includes ++ implicit_includes+ } where bi :: BuildInfo bi = componentBuildInfo component prettyLN :: LibraryName -> Doc- prettyLN LMainLibName = PP.empty+ prettyLN LMainLibName = PP.empty prettyLN (LSubLibName n) = PP.colon <<>> pretty n deps_map :: Map (PackageName, ComponentName) (AnnotatedId ComponentId)- deps_map = Map.fromList [ ((packageName dep, ann_cname dep), dep)- | dep <- lib_deps ]+ deps_map =+ Map.fromList+ [ ((packageName dep, ann_cname dep), dep)+ | dep <- lib_deps+ ] is_public = componentName component == CLibName LMainLibName type ConfiguredComponentMap =- Map PackageName (Map ComponentName (AnnotatedId ComponentId))+ Map PackageName (Map ComponentName (AnnotatedId ComponentId)) toConfiguredComponent- :: PackageDescription- -> ComponentId- -> ConfiguredComponentMap- -> ConfiguredComponentMap- -> Component- -> LogProgress ConfiguredComponent+ :: PackageDescription+ -> ComponentId+ -> ConfiguredComponentMap+ -> ConfiguredComponentMap+ -> Component+ -> LogProgress ConfiguredComponent toConfiguredComponent pkg_descr this_cid lib_dep_map exe_dep_map component = do- lib_deps <-- if newPackageDepsBehaviour pkg_descr- then fmap concat $ forM (targetBuildDepends bi) $- \(Dependency name _ sublibs) -> do- pkg <- case Map.lookup name lib_dep_map of+ lib_deps <-+ if newPackageDepsBehaviour pkg_descr+ then fmap concat $+ forM (targetBuildDepends bi) $+ \(Dependency name _ sublibs) -> do+ case Map.lookup name lib_dep_map of+ Nothing ->+ dieProgress $+ text "Dependency on unbuildable"+ <+> text "package"+ <+> pretty name+ Just pkg -> do+ -- Return all library components+ forM (NonEmptySet.toList sublibs) $ \lib ->+ let comp = CLibName lib+ in case Map.lookup comp pkg of Nothing ->- dieProgress $- text "Dependency on unbuildable" <+>- text "package" <+> pretty name- Just p -> return p- -- Return all library components- forM (NonEmptySet.toList sublibs) $ \lib ->- let comp = CLibName lib in- case Map.lookup comp pkg of- Nothing ->- dieProgress $- text "Dependency on unbuildable" <+>- text (showLibraryName lib) <+>- text "from" <+> pretty name- Just v -> return v- else return old_style_lib_deps- mkConfiguredComponent- pkg_descr this_cid- lib_deps exe_deps component+ dieProgress $+ text "Dependency on unbuildable"+ <+> text (showLibraryName lib)+ <+> text "from"+ <+> pretty name+ Just v -> return v+ else return old_style_lib_deps+ mkConfiguredComponent+ pkg_descr+ this_cid+ lib_deps+ exe_deps+ component where bi = componentBuildInfo component -- lib_dep_map contains a mix of internal and external deps.@@ -206,62 +214,84 @@ -- this is not supported by old-style deps behavior -- because it would imply a cyclic dependency for the -- library itself.- old_style_lib_deps = [ e- | (pn, comp_map) <- Map.toList lib_dep_map- , pn /= packageName pkg_descr- , (cn, e) <- Map.toList comp_map- , cn == CLibName LMainLibName ]+ old_style_lib_deps =+ [ e+ | (pn, comp_map) <- Map.toList lib_dep_map+ , pn /= packageName pkg_descr+ , (cn, e) <- Map.toList comp_map+ , cn == CLibName LMainLibName+ ] -- We have to nub here, because 'getAllToolDependencies' may return -- duplicates (see #4986). (NB: This is not needed for lib_deps, -- since those elaborate into includes, for which there explicitly -- may be multiple instances of a package)- exe_deps = ordNub $+ exe_deps =+ ordNub $ [ exe | ExeDependency pn cn _ <- getAllToolDependencies pkg_descr bi- -- The error suppression here is important, because in general+ , -- The error suppression here is important, because in general -- we won't know about external dependencies (e.g., 'happy') -- which the package is attempting to use (those deps are only -- fed in when cabal-install uses this codepath.) -- TODO: Let cabal-install request errors here- , Just exe <- [Map.lookup (CExeName cn) =<< Map.lookup pn exe_dep_map]+ Just exe <- [Map.lookup (CExeName cn) =<< Map.lookup pn exe_dep_map] ] -- | Also computes the 'ComponentId', and sets cc_public if necessary. -- This is Cabal-only; cabal-install won't use this. toConfiguredComponent'- :: Bool -- use_external_internal_deps- -> FlagAssignment- -> PackageDescription- -> Bool -- deterministic- -> Flag String -- configIPID (todo: remove me)- -> Flag ComponentId -- configCID- -> ConfiguredComponentMap- -> Component- -> LogProgress ConfiguredComponent-toConfiguredComponent' use_external_internal_deps flags- pkg_descr deterministic ipid_flag cid_flag- dep_map component = do- cc <- toConfiguredComponent- pkg_descr this_cid- dep_map dep_map component- return $ if use_external_internal_deps- then cc { cc_public = True }- else cc- where- -- TODO: pass component names to it too!- this_cid = computeComponentId deterministic ipid_flag cid_flag- (package pkg_descr) (componentName component) (Just (deps, flags))- deps = [ ann_id aid | m <- Map.elems dep_map- , aid <- Map.elems m ]+ :: Bool -- use_external_internal_deps+ -> FlagAssignment+ -> PackageDescription+ -> Bool -- deterministic+ -> Flag String -- configIPID (todo: remove me)+ -> Flag ComponentId -- configCID+ -> ConfiguredComponentMap+ -> Component+ -> LogProgress ConfiguredComponent+toConfiguredComponent'+ use_external_internal_deps+ flags+ pkg_descr+ deterministic+ ipid_flag+ cid_flag+ dep_map+ component = do+ cc <-+ toConfiguredComponent+ pkg_descr+ this_cid+ dep_map+ dep_map+ component+ return $+ if use_external_internal_deps+ then cc{cc_public = True}+ else cc+ where+ -- TODO: pass component names to it too!+ this_cid =+ computeComponentId+ deterministic+ ipid_flag+ cid_flag+ (package pkg_descr)+ (componentName component)+ (Just (deps, flags))+ deps =+ [ ann_id aid | m <- Map.elems dep_map, aid <- Map.elems m+ ] extendConfiguredComponentMap- :: ConfiguredComponent- -> ConfiguredComponentMap- -> ConfiguredComponentMap+ :: ConfiguredComponent+ -> ConfiguredComponentMap+ -> ConfiguredComponentMap extendConfiguredComponentMap cc =- Map.insertWith Map.union- (pkgName (cc_pkgid cc))- (Map.singleton (cc_name cc) (cc_ann_id cc))+ Map.insertWith+ Map.union+ (pkgName (cc_pkgid cc))+ (Map.singleton (cc_name cc) (cc_ann_id cc)) -- Compute the 'ComponentId's for a graph of 'Component's. The -- list of internal components must be topologically sorted@@ -273,31 +303,42 @@ -- be used to configure a component that depends on one version of a package for -- a library and another version for a build-tool. toConfiguredComponents- :: Bool -- use_external_internal_deps- -> FlagAssignment- -> Bool -- deterministic- -> Flag String -- configIPID- -> Flag ComponentId -- configCID- -> PackageDescription- -> ConfiguredComponentMap- -> [Component]- -> LogProgress [ConfiguredComponent]+ :: Bool -- use_external_internal_deps+ -> FlagAssignment+ -> Bool -- deterministic+ -> Flag String -- configIPID+ -> Flag ComponentId -- configCID+ -> PackageDescription+ -> ConfiguredComponentMap+ -> [Component]+ -> LogProgress [ConfiguredComponent] toConfiguredComponents- use_external_internal_deps flags deterministic ipid_flag cid_flag pkg_descr- dep_map comps- = fmap snd (mapAccumM go dep_map comps)- where- go m component = do- cc <- toConfiguredComponent'- use_external_internal_deps flags pkg_descr- deterministic ipid_flag cid_flag- m component+ use_external_internal_deps+ flags+ deterministic+ ipid_flag+ cid_flag+ pkg_descr+ dep_map+ comps =+ fmap snd (mapAccumM go dep_map comps)+ where+ go m component = do+ cc <-+ toConfiguredComponent'+ use_external_internal_deps+ flags+ pkg_descr+ deterministic+ ipid_flag+ cid_flag+ m+ component return (extendConfiguredComponentMap cc m, cc) newPackageDepsBehaviourMinVersion :: CabalSpecVersion newPackageDepsBehaviourMinVersion = CabalSpecV1_8 - -- In older cabal versions, there was only one set of package dependencies for -- the whole package. In this version, we can have separate dependencies per -- target, but we only enable this behaviour if the minimum cabal version@@ -305,4 +346,4 @@ -- old behaviour. newPackageDepsBehaviour :: PackageDescription -> Bool newPackageDepsBehaviour pkg =- specVersion pkg >= newPackageDepsBehaviourMinVersion+ specVersion pkg >= newPackageDepsBehaviourMinVersion
src/Distribution/Backpack/DescribeUnitId.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE Rank2Types #-}+ module Distribution.Backpack.DescribeUnitId where import Distribution.Compat.Prelude@@ -36,26 +37,36 @@ -- | Print a Setup message stating (1) what operation we are doing, -- for (2) which component (with enough details to uniquely identify -- the build in question.)----setupMessage' :: Pretty a => Verbosity- -> String -- ^ Operation being done (capitalized), on:- -> PackageIdentifier -- ^ Package- -> ComponentName -- ^ Component name- -> Maybe [(ModuleName, a)] -- ^ Instantiation, if available.- -- Polymorphic to take- -- 'OpenModule' or 'Module'- -> IO ()+setupMessage'+ :: Pretty a+ => Verbosity+ -> String+ -- ^ Operation being done (capitalized), on:+ -> PackageIdentifier+ -- ^ Package+ -> ComponentName+ -- ^ Component name+ -> Maybe [(ModuleName, a)]+ -- ^ Instantiation, if available.+ -- Polymorphic to take+ -- 'OpenModule' or 'Module'+ -> IO () setupMessage' verbosity msg pkgid cname mb_insts = withFrozenCallStack $ do- noticeDoc verbosity $- case mb_insts of- Just insts | not (null insts) ->- hang (msg_doc <+> text "instantiated with") 2- (vcat [ pretty k <+> text "=" <+> pretty v- | (k,v) <- insts ]) $$- for_doc- _ ->- msg_doc <+> for_doc-+ noticeDoc verbosity $+ case mb_insts of+ Just insts+ | not (null insts) ->+ hang+ (msg_doc <+> text "instantiated with")+ 2+ ( vcat+ [ pretty k <+> text "=" <+> pretty v+ | (k, v) <- insts+ ]+ )+ $$ for_doc+ _ ->+ msg_doc <+> for_doc where msg_doc = text msg <+> text (showComponentName cname)- for_doc = text "for" <+> pretty pkgid <<>> text ".."+ for_doc = text "for" <+> pretty pkgid <<>> text "..."
src/Distribution/Backpack/FullUnitId.hs view
@@ -1,26 +1,27 @@ {-# LANGUAGE DeriveGeneric #-}-module Distribution.Backpack.FullUnitId (- FullUnitId(..),- FullDb,- expandOpenUnitId,- expandUnitId-) where +module Distribution.Backpack.FullUnitId+ ( FullUnitId (..)+ , FullDb+ , expandOpenUnitId+ , expandUnitId+ ) where+ import Distribution.Backpack-import Distribution.Types.ComponentId import Distribution.Compat.Prelude+import Distribution.Types.ComponentId -- Unlike OpenUnitId, which could direct to a UnitId. data FullUnitId = FullUnitId ComponentId OpenModuleSubst- deriving (Show, Generic)+ deriving (Show, Generic) type FullDb = DefUnitId -> FullUnitId expandOpenUnitId :: FullDb -> OpenUnitId -> FullUnitId-expandOpenUnitId _db (IndefFullUnitId cid subst)- = FullUnitId cid subst-expandOpenUnitId db (DefiniteUnitId uid)- = expandUnitId db uid+expandOpenUnitId _db (IndefFullUnitId cid subst) =+ FullUnitId cid subst+expandOpenUnitId db (DefiniteUnitId uid) =+ expandUnitId db uid expandUnitId :: FullDb -> DefUnitId -> FullUnitId expandUnitId db uid = db uid
src/Distribution/Backpack/Id.hs view
@@ -1,74 +1,87 @@ {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE RankNTypes #-} {-# LANGUAGE PatternGuards #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RankNTypes #-}+ -- | See <https://github.com/ezyang/ghc-proposals/blob/backpack/proposals/0000-backpack.rst>-module Distribution.Backpack.Id(- computeComponentId,- computeCompatPackageKey,-) where+module Distribution.Backpack.Id+ ( computeComponentId+ , computeCompatPackageKey+ ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -import Distribution.Types.UnqualComponentName-import Distribution.Simple.Compiler import Distribution.PackageDescription-import Distribution.Simple.Setup as Setup+import Distribution.Simple.Compiler+import Distribution.Simple.Flag (Flag, pattern Flag, pattern NoFlag) import qualified Distribution.Simple.InstallDirs as InstallDirs import Distribution.Simple.LocalBuildInfo import Distribution.Types.ComponentId-import Distribution.Types.UnitId import Distribution.Types.MungedPackageName+import Distribution.Types.UnitId import Distribution.Utils.Base62 import Distribution.Version +import Distribution.Parsec (simpleParsec) import Distribution.Pretty- ( prettyShow )-import Distribution.Parsec ( simpleParsec )+ ( prettyShow+ ) -- | This method computes a default, "good enough" 'ComponentId' -- for a package. The intent is that cabal-install (or the user) will -- specify a more detailed IPID via the @--ipid@ flag if necessary. computeComponentId- :: Bool -- deterministic mode- -> Flag String- -> Flag ComponentId- -> PackageIdentifier- -> ComponentName- -- This is used by cabal-install's legacy codepath- -> Maybe ([ComponentId], FlagAssignment)- -> ComponentId+ :: Bool -- deterministic mode+ -> Flag String+ -> Flag ComponentId+ -> PackageIdentifier+ -> ComponentName+ -- This is used by cabal-install's legacy codepath+ -> Maybe ([ComponentId], FlagAssignment)+ -> ComponentId computeComponentId deterministic mb_ipid mb_cid pid cname mb_details =- -- show is found to be faster than intercalate and then replacement of- -- special character used in intercalating. We cannot simply hash by- -- doubly concating list, as it just flatten out the nested list, so- -- different sources can produce same hash- let hash_suffix- | Just (dep_ipids, flags) <- mb_details- = "-" ++ hashToBase62+ -- show is found to be faster than intercalate and then replacement of+ -- special character used in intercalating. We cannot simply hash by+ -- doubly concatenating list, as it just flatten out the nested list, so+ -- different sources can produce same hash+ let hash_suffix+ | Just (dep_ipids, flags) <- mb_details =+ "-"+ ++ hashToBase62 -- For safety, include the package + version here -- for GHC 7.10, where just the hash is used as -- the package key- ( prettyShow pid- ++ show dep_ipids- ++ show flags )- | otherwise = ""- generated_base = prettyShow pid ++ hash_suffix- explicit_base cid0 = fromPathTemplate (InstallDirs.substPathTemplate env- (toPathTemplate cid0))- -- Hack to reuse install dirs machinery- -- NB: no real IPID available at this point- where env = packageTemplateEnv pid (mkUnitId "")- actual_base = case mb_ipid of- Flag ipid0 -> explicit_base ipid0- NoFlag | deterministic -> prettyShow pid- | otherwise -> generated_base- in case mb_cid of- Flag cid -> cid- NoFlag -> mkComponentId $ actual_base- ++ (case componentNameString cname of- Nothing -> ""- Just s -> "-" ++ unUnqualComponentName s)+ ( prettyShow pid+ ++ show dep_ipids+ ++ show flags+ )+ | otherwise = ""+ generated_base = prettyShow pid ++ hash_suffix+ explicit_base cid0 =+ fromPathTemplate+ ( InstallDirs.substPathTemplate+ env+ (toPathTemplate cid0)+ )+ where+ -- Hack to reuse install dirs machinery+ -- NB: no real IPID available at this point+ env = packageTemplateEnv pid (mkUnitId "")+ actual_base = case mb_ipid of+ Flag ipid0 -> explicit_base ipid0+ NoFlag+ | deterministic -> prettyShow pid+ | otherwise -> generated_base+ in case mb_cid of+ Flag cid -> cid+ NoFlag ->+ mkComponentId $+ actual_base+ ++ ( case componentNameString cname of+ Nothing -> ""+ Just s -> "-" ++ unUnqualComponentName s+ ) -- | In GHC 8.0, the string we pass to GHC to use for symbol -- names for a package can be an arbitrary, IPID-compatible string.@@ -116,30 +129,29 @@ -- -- * For sub-components, we rehash the IPID into the correct format -- and pass that.--- computeCompatPackageKey- :: Compiler- -> MungedPackageName- -> Version- -> UnitId- -> String+ :: Compiler+ -> MungedPackageName+ -> Version+ -> UnitId+ -> String computeCompatPackageKey comp pkg_name pkg_version uid- | not (packageKeySupported comp || unitIdSupported comp)- = prettyShow pkg_name ++ "-" ++ prettyShow pkg_version- | not (unifiedIPIDRequired comp) =- let str = unUnitId uid -- assume no Backpack support- mb_verbatim_key- = case simpleParsec str :: Maybe PackageId of- -- Something like 'foo-0.1', use it verbatim.- -- (NB: hash tags look like tags, so they are parsed,- -- so the extra equality check tests if a tag was dropped.)- Just pid0 | prettyShow pid0 == str -> Just str- _ -> Nothing- mb_truncated_key- = let cand = reverse (takeWhile isAlphaNum (reverse str))- in if length cand == 22 && all isAlphaNum cand- then Just cand- else Nothing- rehashed_key = hashToBase62 str- in fromMaybe rehashed_key (mb_verbatim_key `mplus` mb_truncated_key)- | otherwise = prettyShow uid+ | not (packageKeySupported comp || unitIdSupported comp) =+ prettyShow pkg_name ++ "-" ++ prettyShow pkg_version+ | not (unifiedIPIDRequired comp) =+ let str = unUnitId uid -- assume no Backpack support+ mb_verbatim_key =+ case simpleParsec str :: Maybe PackageId of+ -- Something like 'foo-0.1', use it verbatim.+ -- (NB: hash tags look like tags, so they are parsed,+ -- so the extra equality check tests if a tag was dropped.)+ Just pid0 | prettyShow pid0 == str -> Just str+ _ -> Nothing+ mb_truncated_key =+ let cand = reverse (takeWhile isAlphaNum (reverse str))+ in if length cand == 22 && all isAlphaNum cand+ then Just cand+ else Nothing+ rehashed_key = hashToBase62 str+ in fromMaybe rehashed_key (mb_verbatim_key `mplus` mb_truncated_key)+ | otherwise = prettyShow uid
src/Distribution/Backpack/LinkedComponent.hs view
@@ -1,82 +1,76 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-}+ -- | See <https://github.com/ezyang/ghc-proposals/blob/backpack/proposals/0000-backpack.rst>-module Distribution.Backpack.LinkedComponent (- LinkedComponent(..),- lc_insts,- lc_uid,- lc_cid,- lc_pkgid,- toLinkedComponent,- toLinkedComponents,- dispLinkedComponent,- LinkedComponentMap,- extendLinkedComponentMap,-) where+module Distribution.Backpack.LinkedComponent+ ( LinkedComponent (..)+ , lc_insts+ , lc_uid+ , lc_cid+ , lc_pkgid+ , toLinkedComponent+ , toLinkedComponents+ , dispLinkedComponent+ , LinkedComponentMap+ , extendLinkedComponentMap+ ) where -import Prelude () import Distribution.Compat.Prelude hiding ((<>))+import Prelude () import Distribution.Backpack-import Distribution.Backpack.FullUnitId import Distribution.Backpack.ConfiguredComponent+import Distribution.Backpack.FullUnitId+import Distribution.Backpack.MixLink+import Distribution.Backpack.ModuleScope import Distribution.Backpack.ModuleShape import Distribution.Backpack.PreModuleShape-import Distribution.Backpack.ModuleScope import Distribution.Backpack.UnifyM-import Distribution.Backpack.MixLink import Distribution.Utils.MapAccum -import Distribution.Types.AnnotatedId-import Distribution.Types.ComponentName-import Distribution.Types.ModuleReexport-import Distribution.Types.ModuleRenaming-import Distribution.Types.IncludeRenaming-import Distribution.Types.ComponentInclude-import Distribution.Types.ComponentId-import Distribution.Types.PackageId+import Distribution.ModuleName import Distribution.Package import Distribution.PackageDescription-import Distribution.ModuleName import Distribution.Simple.LocalBuildInfo-import Distribution.Verbosity+import Distribution.Types.AnnotatedId+import Distribution.Types.ComponentInclude import Distribution.Utils.LogProgress+import Distribution.Verbosity -import qualified Data.Set as Set import qualified Data.Map as Map+import qualified Data.Set as Set import Distribution.Pretty (pretty)-import Text.PrettyPrint (Doc, hang, text, vcat, ($+$), hsep, quotes)+import Text.PrettyPrint (Doc, hang, hsep, quotes, text, vcat, ($+$)) -- | A linked component is a component that has been mix-in linked, at -- which point we have determined how all the dependencies of the -- component are explicitly instantiated (in the form of an OpenUnitId). -- 'ConfiguredComponent' is mix-in linked into 'LinkedComponent', which -- is then instantiated into 'ReadyComponent'.-data LinkedComponent- = LinkedComponent {- -- | Uniquely identifies linked component- lc_ann_id :: AnnotatedId ComponentId,- -- | Corresponds to 'cc_component'.- lc_component :: Component,- -- | @build-tools@ and @build-tool-depends@ dependencies.- -- Corresponds to 'cc_exe_deps'.- lc_exe_deps :: [AnnotatedId OpenUnitId],- -- | Is this the public library of a package? Corresponds to- -- 'cc_public'.- lc_public :: Bool,- -- | Corresponds to 'cc_includes', but (1) this does not contain- -- includes of signature packages (packages with no exports),- -- and (2) the 'ModuleRenaming' for requirements (stored in- -- 'IncludeRenaming') has been removed, as it is reflected in- -- 'OpenUnitId'.)- lc_includes :: [ComponentInclude OpenUnitId ModuleRenaming],- -- | Like 'lc_includes', but this specifies includes on- -- signature packages which have no exports.- lc_sig_includes :: [ComponentInclude OpenUnitId ModuleRenaming],- -- | The module shape computed by mix-in linking. This is- -- newly computed from 'ConfiguredComponent'- lc_shape :: ModuleShape- }+data LinkedComponent = LinkedComponent+ { lc_ann_id :: AnnotatedId ComponentId+ -- ^ Uniquely identifies linked component+ , lc_component :: Component+ -- ^ Corresponds to 'cc_component'.+ , lc_exe_deps :: [AnnotatedId OpenUnitId]+ -- ^ @build-tools@ and @build-tool-depends@ dependencies.+ -- Corresponds to 'cc_exe_deps'.+ , lc_public :: Bool+ -- ^ Is this the public library of a package? Corresponds to+ -- 'cc_public'.+ , lc_includes :: [ComponentInclude OpenUnitId ModuleRenaming]+ -- ^ Corresponds to 'cc_includes', but (1) this does not contain+ -- includes of signature packages (packages with no exports),+ -- and (2) the 'ModuleRenaming' for requirements (stored in+ -- 'IncludeRenaming') has been removed, as it is reflected in+ -- 'OpenUnitId'.)+ , lc_sig_includes :: [ComponentInclude OpenUnitId ModuleRenaming]+ -- ^ Like 'lc_includes', but this specifies includes on+ -- signature packages which have no exports.+ , lc_shape :: ModuleShape+ -- ^ The module shape computed by mix-in linking. This is+ -- newly computed from 'ConfiguredComponent'+ } -- | Uniquely identifies a 'LinkedComponent'. Corresponds to -- 'cc_cid'.@@ -96,94 +90,125 @@ -- | The instantiation of 'lc_uid'; this always has the invariant -- that it is a mapping from a module name @A@ to @<A>@ (the hole A). lc_insts :: LinkedComponent -> [(ModuleName, OpenModule)]-lc_insts lc = [ (req, OpenModuleVar req)- | req <- Set.toList (modShapeRequires (lc_shape lc)) ]+lc_insts lc =+ [ (req, OpenModuleVar req)+ | req <- Set.toList (modShapeRequires (lc_shape lc))+ ] dispLinkedComponent :: LinkedComponent -> Doc dispLinkedComponent lc =- hang (text "unit" <+> pretty (lc_uid lc)) 4 $- vcat [ text "include" <+> pretty (ci_id incl) <+> pretty (ci_renaming incl)- | incl <- lc_includes lc ]- $+$- vcat [ text "signature include" <+> pretty (ci_id incl)- | incl <- lc_sig_includes lc ]- $+$ dispOpenModuleSubst (modShapeProvides (lc_shape lc))+ hang (text "unit" <+> pretty (lc_uid lc)) 4 $+ vcat+ [ text "include" <+> pretty (ci_id incl) <+> pretty (ci_renaming incl)+ | incl <- lc_includes lc+ ]+ $+$ vcat+ [ text "signature include" <+> pretty (ci_id incl)+ | incl <- lc_sig_includes lc+ ]+ $+$ dispOpenModuleSubst (modShapeProvides (lc_shape lc)) instance Package LinkedComponent where- packageId = lc_pkgid+ packageId = lc_pkgid toLinkedComponent- :: Verbosity- -> FullDb- -> PackageId- -> LinkedComponentMap- -> ConfiguredComponent- -> LogProgress LinkedComponent-toLinkedComponent verbosity db this_pid pkg_map ConfiguredComponent {- cc_ann_id = aid@AnnotatedId { ann_id = this_cid },- cc_component = component,- cc_exe_deps = exe_deps,- cc_public = is_public,- cc_includes = cid_includes- } = do+ :: Verbosity+ -> Bool+ -- ^ Whether there are any "promised" package dependencies which we won't find already installed.+ -> FullDb+ -> PackageId+ -> LinkedComponentMap+ -> ConfiguredComponent+ -> LogProgress LinkedComponent+toLinkedComponent+ verbosity+ anyPromised+ db+ this_pid+ pkg_map+ ConfiguredComponent+ { cc_ann_id = aid@AnnotatedId{ann_id = this_cid}+ , cc_component = component+ , cc_exe_deps = exe_deps+ , cc_public = is_public+ , cc_includes = cid_includes+ } = do let- -- The explicitly specified requirements, provisions and- -- reexports from the Cabal file. These are only non-empty for- -- libraries; everything else is trivial.- (src_reqs :: [ModuleName],- src_provs :: [ModuleName],- src_reexports :: [ModuleReexport]) =- case component of- CLib lib -> (signatures lib,- exposedModules lib,- reexportedModules lib)- _ -> ([], [], [])- src_hidden = otherModules (componentBuildInfo component)+ -- The explicitly specified requirements, provisions and+ -- reexports from the Cabal file. These are only non-empty for+ -- libraries; everything else is trivial.+ ( src_reqs :: [ModuleName]+ , src_provs :: [ModuleName]+ , src_reexports :: [ModuleReexport]+ ) =+ case component of+ CLib lib ->+ ( signatures lib+ , exposedModules lib+ , reexportedModules lib+ )+ _ -> ([], [], [])+ src_hidden = otherModules (componentBuildInfo component) - -- Take each included ComponentId and resolve it into an- -- *unlinked* unit identity. We will use unification (relying- -- on the ModuleShape) to resolve these into linked identities.- unlinked_includes :: [ComponentInclude (OpenUnitId, ModuleShape) IncludeRenaming]- unlinked_includes = [ ComponentInclude (fmap lookupUid dep_aid) rns i- | ComponentInclude dep_aid rns i <- cid_includes ]+ -- Take each included ComponentId and resolve it into an+ -- \*unlinked* unit identity. We will use unification (relying+ -- on the ModuleShape) to resolve these into linked identities.+ unlinked_includes :: [ComponentInclude (OpenUnitId, ModuleShape) IncludeRenaming]+ unlinked_includes =+ [ ComponentInclude (fmap lookupUid dep_aid) rns i+ | ComponentInclude dep_aid rns i <- cid_includes+ ] - lookupUid :: ComponentId -> (OpenUnitId, ModuleShape)- lookupUid cid = fromMaybe (error "linkComponent: lookupUid")- (Map.lookup cid pkg_map)+ lookupUid :: ComponentId -> (OpenUnitId, ModuleShape)+ lookupUid cid =+ fromMaybe+ (error "linkComponent: lookupUid")+ (Map.lookup cid pkg_map) let orErr (Right x) = return x orErr (Left [err]) = dieProgress err orErr (Left errs) = do- dieProgress (vcat (intersperse (text "") -- double newline!- [ hang (text "-") 2 err | err <- errs]))+ dieProgress+ ( vcat+ ( intersperse+ (text "") -- double newline!+ [hang (text "-") 2 err | err <- errs]+ )+ ) -- Pre-shaping- let pre_shape = mixLinkPreModuleShape $- PreModuleShape {- preModShapeProvides = Set.fromList (src_provs ++ src_hidden),- preModShapeRequires = Set.fromList src_reqs- } : [ renamePreModuleShape (toPreModuleShape sh) rns- | ComponentInclude (AnnotatedId { ann_id = (_, sh) }) rns _ <- unlinked_includes ]- reqs = preModShapeRequires pre_shape- insts = [ (req, OpenModuleVar req)- | req <- Set.toList reqs ]+ let pre_shape =+ mixLinkPreModuleShape $+ PreModuleShape+ { preModShapeProvides = Set.fromList (src_provs ++ src_hidden)+ , preModShapeRequires = Set.fromList src_reqs+ }+ : [ renamePreModuleShape (toPreModuleShape sh) rns+ | ComponentInclude (AnnotatedId{ann_id = (_, sh)}) rns _ <- unlinked_includes+ ]+ reqs = preModShapeRequires pre_shape+ insts =+ [ (req, OpenModuleVar req)+ | req <- Set.toList reqs+ ] this_uid = IndefFullUnitId this_cid . Map.fromList $ insts -- OK, actually do unification -- TODO: the unification monad might return errors, in which -- case we have to deal. Use monadic bind for now.- (linked_shape0 :: ModuleScope,- linked_includes0 :: [ComponentInclude OpenUnitId ModuleRenaming],- linked_sig_includes0 :: [ComponentInclude OpenUnitId ModuleRenaming])- <- orErr $ runUnifyM verbosity this_cid db $ do+ ( linked_shape0 :: ModuleScope+ , linked_includes0 :: [ComponentInclude OpenUnitId ModuleRenaming]+ , linked_sig_includes0 :: [ComponentInclude OpenUnitId ModuleRenaming]+ ) <-+ orErr $ runUnifyM verbosity this_cid db $ do -- The unification monad is implemented using mutable -- references. Thus, we must convert our *pure* data -- structures into mutable ones to perform unification. let convertMod :: (ModuleName -> ModuleSource) -> ModuleName -> UnifyM s (ModuleScopeU s) convertMod from m = do- m_u <- convertModule (OpenModule this_uid m)- return (Map.singleton m [WithSource (from m) m_u], Map.empty)+ m_u <- convertModule (OpenModule this_uid m)+ return (Map.singleton m [WithSource (from m) m_u], Map.empty) -- Handle 'exposed-modules' exposed_mod_shapes_u <- traverse (convertMod FromExposedModules) src_provs -- Handle 'other-modules'@@ -192,8 +217,8 @@ -- Handle 'signatures' let convertReq :: ModuleName -> UnifyM s (ModuleScopeU s) convertReq req = do- req_u <- convertModule (OpenModuleVar req)- return (Map.empty, Map.singleton req [WithSource (FromSignatures req) req_u])+ req_u <- convertModule (OpenModuleVar req)+ return (Map.empty, Map.singleton req [WithSource (FromSignatures req) req_u]) req_shapes_u <- traverse convertReq src_reqs -- Handle 'mixins'@@ -201,21 +226,27 @@ failIfErrs -- Prevent error cascade -- Mix-in link everything! mixLink is the real workhorse.- shape_u <- mixLink $ exposed_mod_shapes_u- ++ other_mod_shapes_u- ++ req_shapes_u- ++ incl_shapes_u+ shape_u <-+ mixLink $+ exposed_mod_shapes_u+ ++ other_mod_shapes_u+ ++ req_shapes_u+ ++ incl_shapes_u -- src_reqs_u <- traverse convertReq src_reqs -- Read out all the final results by converting back -- into a pure representation. let convertIncludeU (ComponentInclude dep_aid rns i) = do- uid <- convertUnitIdU (ann_id dep_aid)- return (ComponentInclude {- ci_ann_id = dep_aid { ann_id = uid },- ci_renaming = rns,- ci_implicit = i- })+ let component_name = pretty $ ann_cname dep_aid+ uid <- convertUnitIdU (ann_id dep_aid) component_name+ return+ ( ComponentInclude+ { ci_ann_id = dep_aid{ann_id = uid}+ , ci_renaming = rns+ , ci_implicit = i+ }+ )+ shape <- convertModuleScopeU shape_u let (includes_u, sig_includes_u) = partitionEithers all_includes_u incls <- traverse convertIncludeU includes_u@@ -223,23 +254,26 @@ return (shape, incls, sig_incls) let isNotLib (CLib _) = False- isNotLib _ = True+ isNotLib _ = True when (not (Set.null reqs) && isNotLib component) $- dieProgress $- hang (text "Non-library component has unfilled requirements:")- 4 (vcat [pretty req | req <- Set.toList reqs])+ dieProgress $+ hang+ (text "Non-library component has unfilled requirements:")+ 4+ (vcat [pretty req | req <- Set.toList reqs]) -- NB: do NOT include hidden modules here: GHC 7.10's ghc-pkg -- won't allow it (since someone could directly synthesize -- an 'InstalledPackageInfo' that violates abstraction.) -- Though, maybe it should be relaxed? let src_hidden_set = Set.fromList src_hidden- linked_shape = linked_shape0 {- modScopeProvides =+ linked_shape =+ linked_shape0+ { modScopeProvides = -- Would rather use withoutKeys but need BC Map.filterWithKey- (\k _ -> not (k `Set.member` src_hidden_set))- (modScopeProvides linked_shape0)+ (\k _ -> not (k `Set.member` src_hidden_set))+ (modScopeProvides linked_shape0) } -- OK, compute the reexports@@ -248,65 +282,102 @@ -- once. let hdl :: [Either Doc a] -> LogProgress [a] hdl es =- case partitionEithers es of- ([], rs) -> return rs- (ls, _) ->- dieProgress $- hang (text "Problem with module re-exports:") 2- (vcat [hang (text "-") 2 l | l <- ls])+ case partitionEithers es of+ ([], rs) -> return rs+ (ls, _) ->+ dieProgress $+ hang+ (text "Problem with module re-exports:")+ 2+ (vcat [hang (text "-") 2 l | l <- ls]) reexports_list <- hdl . (flip map) src_reexports $ \reex@(ModuleReexport mb_pn from to) -> do case Map.lookup from (modScopeProvides linked_shape) of- Just cands@(x0:xs0) -> do+ Just cands@(x0 : xs0) -> do -- Make sure there is at least one candidate (x, xs) <- case mb_pn of Just pn ->- let matches_pn (FromMixins pn' _ _) = pn == pn'+ let matches_pn (FromMixins pn' _ _) = pn == pn' matches_pn (FromBuildDepends pn' _) = pn == pn' matches_pn (FromExposedModules _) = pn == packageName this_pid- matches_pn (FromOtherModules _) = pn == packageName this_pid- matches_pn (FromSignatures _) = pn == packageName this_pid- in case filter (matches_pn . getSource) cands of- (x1:xs1) -> return (x1, xs1)- _ -> Left (brokenReexportMsg reex)+ matches_pn (FromOtherModules _) = pn == packageName this_pid+ matches_pn (FromSignatures _) = pn == packageName this_pid+ in case filter (matches_pn . getSource) cands of+ (x1 : xs1) -> return (x1, xs1)+ _ -> Left (brokenReexportMsg reex) Nothing -> return (x0, xs0) -- Test that all the candidates are consistent case filter (\x' -> unWithSource x /= unWithSource x') xs of [] -> return () _ -> Left $ ambiguousReexportMsg reex x xs- return (to, unWithSource x)+ return (to, Just (unWithSource x)) _ ->- Left (brokenReexportMsg reex)+ -- Can't resolve it right now.. carry on with the assumption it will be resolved+ -- dynamically later by an in-memory package which hasn't been installed yet.+ if anyPromised+ then return (to, Nothing)+ else -- But if nothing is promised, eagerly report an error, as we already know everything.+ Left (brokenReexportMsg reex) -- TODO: maybe check this earlier; it's syntactically obvious. let build_reexports m (k, v)- | Map.member k m =- dieProgress $ hsep- [ text "Module name ", pretty k, text " is exported multiple times." ]- | otherwise = return (Map.insert k v m)- provs <- foldM build_reexports Map.empty $- -- TODO: doublecheck we have checked for- -- src_provs duplicates already!- [ (mod_name, OpenModule this_uid mod_name) | mod_name <- src_provs ] ++- reexports_list+ | Map.member k m =+ dieProgress $+ hsep+ [text "Module name ", pretty k, text " is exported multiple times."]+ | otherwise = return (Map.insert k v m)+ provs <-+ foldM build_reexports Map.empty $+ -- TODO: doublecheck we have checked for+ -- src_provs duplicates already!+ -- These are normal module exports.+ [(mod_name, (OpenModule this_uid mod_name)) | mod_name <- src_provs]+ +++ -- These are reexports, which we managed to resolve to something in an external package.+ [(mn_new, om) | (mn_new, Just om) <- reexports_list]+ +++ -- These ones.. we didn't resolve but also we might not have to+ -- resolve them because they could come from a promised unit,+ -- which we don't know anything about yet. GHC will resolve+ -- these itself when it is dealing with the multi-session.+ -- These ones will not be built, registered and put+ -- into a package database, we only need them to make it as far+ -- as generating GHC options where the info will be used to+ -- pass the reexported-module option to GHC. + -- We also know that in the case there are promised units that+ -- we will not be doing anything to do with backpack like+ -- unification etc..+ [ ( mod_name+ , OpenModule+ ( DefiniteUnitId+ ( unsafeMkDefUnitId+ (mkUnitId "fake")+ )+ )+ mod_name+ )+ | (mod_name, Nothing) <- reexports_list+ ]+ let final_linked_shape = ModuleShape provs (Map.keysSet (modScopeRequires linked_shape)) -- See Note Note [Signature package special case] let (linked_includes, linked_sig_includes)- | Set.null reqs = (linked_includes0 ++ linked_sig_includes0, [])- | otherwise = (linked_includes0, linked_sig_includes0)+ | Set.null reqs = (linked_includes0 ++ linked_sig_includes0, [])+ | otherwise = (linked_includes0, linked_sig_includes0) - return $ LinkedComponent {- lc_ann_id = aid,- lc_component = component,- lc_public = is_public,- -- These must be executables- lc_exe_deps = map (fmap (\cid -> IndefFullUnitId cid Map.empty)) exe_deps,- lc_shape = final_linked_shape,- lc_includes = linked_includes,- lc_sig_includes = linked_sig_includes- }+ return $+ LinkedComponent+ { lc_ann_id = aid+ , lc_component = component+ , lc_public = is_public+ , -- These must be executables+ lc_exe_deps = map (fmap (\cid -> IndefFullUnitId cid Map.empty)) exe_deps+ , lc_shape = final_linked_shape+ , lc_includes = linked_includes+ , lc_sig_includes = linked_sig_includes+ } -- Note [Signature package special case] -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -334,63 +405,82 @@ -- Handle mix-in linking for components. In the absence of Backpack, -- every ComponentId gets converted into a UnitId by way of SimpleUnitId. toLinkedComponents- :: Verbosity- -> FullDb- -> PackageId- -> LinkedComponentMap- -> [ConfiguredComponent]- -> LogProgress [LinkedComponent]-toLinkedComponents verbosity db this_pid lc_map0 comps- = fmap snd (mapAccumM go lc_map0 comps)- where- go :: Map ComponentId (OpenUnitId, ModuleShape)- -> ConfiguredComponent- -> LogProgress (Map ComponentId (OpenUnitId, ModuleShape), LinkedComponent)- go lc_map cc = do- lc <- addProgressCtx (text "In the stanza" <+> text (componentNameStanza (cc_name cc))) $- toLinkedComponent verbosity db this_pid lc_map cc- return (extendLinkedComponentMap lc lc_map, lc)+ :: Verbosity+ -> Bool+ -- ^ Whether there are any "promised" package dependencies which we won't+ -- find already installed.+ -> FullDb+ -> PackageId+ -> LinkedComponentMap+ -> [ConfiguredComponent]+ -> LogProgress [LinkedComponent]+toLinkedComponents verbosity anyPromised db this_pid lc_map0 comps =+ fmap snd (mapAccumM go lc_map0 comps)+ where+ go+ :: Map ComponentId (OpenUnitId, ModuleShape)+ -> ConfiguredComponent+ -> LogProgress (Map ComponentId (OpenUnitId, ModuleShape), LinkedComponent)+ go lc_map cc = do+ lc <-+ addProgressCtx (text "In the stanza" <+> text (componentNameStanza (cc_name cc))) $+ toLinkedComponent verbosity anyPromised db this_pid lc_map cc+ return (extendLinkedComponentMap lc lc_map, lc) type LinkedComponentMap = Map ComponentId (OpenUnitId, ModuleShape) -extendLinkedComponentMap :: LinkedComponent- -> LinkedComponentMap- -> LinkedComponentMap+extendLinkedComponentMap+ :: LinkedComponent+ -> LinkedComponentMap+ -> LinkedComponentMap extendLinkedComponentMap lc m =- Map.insert (lc_cid lc) (lc_uid lc, lc_shape lc) m+ Map.insert (lc_cid lc) (lc_uid lc, lc_shape lc) m brokenReexportMsg :: ModuleReexport -> Doc brokenReexportMsg (ModuleReexport (Just pn) from _to) =- vcat [ text "The package" <+> quotes (pretty pn)- , text "does not export a module" <+> quotes (pretty from) ]+ vcat+ [ text "The package" <+> quotes (pretty pn)+ , text "does not export a module" <+> quotes (pretty from)+ ] brokenReexportMsg (ModuleReexport Nothing from _to) =- vcat [ text "The module" <+> quotes (pretty from)- , text "is not exported by any suitable package."- , text "It occurs in neither the 'exposed-modules' of this package,"- , text "nor any of its 'build-depends' dependencies." ]+ vcat+ [ text "The module" <+> quotes (pretty from)+ , text "is not exported by any suitable package."+ , text "It occurs in neither the 'exposed-modules' of this package,"+ , text "nor any of its 'build-depends' dependencies."+ ] ambiguousReexportMsg :: ModuleReexport -> ModuleWithSource -> [ModuleWithSource] -> Doc ambiguousReexportMsg (ModuleReexport mb_pn from _to) y1 ys =- vcat [ text "Ambiguous reexport" <+> quotes (pretty from)- , hang (text "It could refer to either:") 2- (vcat (msg : msgs))- , help_msg mb_pn ]+ vcat+ [ text "Ambiguous reexport" <+> quotes (pretty from)+ , hang+ (text "It could refer to either:")+ 2+ (vcat (msg : msgs))+ , help_msg mb_pn+ ] where- msg = text " " <+> displayModuleWithSource y1+ msg = text " " <+> displayModuleWithSource y1 msgs = [text "or" <+> displayModuleWithSource y | y <- ys] help_msg Nothing = -- TODO: This advice doesn't help if the ambiguous exports -- come from a package named the same thing- vcat [ text "The ambiguity can be resolved by qualifying the"- , text "re-export with a package name."- , text "The syntax is 'packagename:ModuleName [as NewName]'." ]+ vcat+ [ text "The ambiguity can be resolved by qualifying the"+ , text "re-export with a package name."+ , text "The syntax is 'packagename:ModuleName [as NewName]'."+ ] -- Qualifying won't help that much. help_msg (Just _) =- vcat [ text "The ambiguity can be resolved by using the"- , text "mixins field to rename one of the module"- , text "names differently." ]- displayModuleWithSource y- = vcat [ quotes (pretty (unWithSource y))- , text "brought into scope by" <+>- dispModuleSource (getSource y)- ]+ vcat+ [ text "The ambiguity can be resolved by using the"+ , text "mixins field to rename one of the module"+ , text "names differently."+ ]+ displayModuleWithSource y =+ vcat+ [ quotes (pretty (unWithSource y))+ , text "brought into scope by"+ <+> dispModuleSource (getSource y)+ ]
src/Distribution/Backpack/MixLink.hs view
@@ -1,26 +1,27 @@ {-# LANGUAGE NondecreasingIndentation #-}+ -- | See <https://github.com/ezyang/ghc-proposals/blob/backpack/proposals/0000-backpack.rst>-module Distribution.Backpack.MixLink (- mixLink,-) where+module Distribution.Backpack.MixLink+ ( mixLink+ ) where -import Prelude () import Distribution.Compat.Prelude hiding (mod)+import Prelude () import Distribution.Backpack-import Distribution.Backpack.UnifyM import Distribution.Backpack.FullUnitId import Distribution.Backpack.ModuleScope+import Distribution.Backpack.UnifyM -import qualified Distribution.Utils.UnionFind as UnionFind import Distribution.ModuleName import Distribution.Pretty import Distribution.Types.ComponentId+import qualified Distribution.Utils.UnionFind as UnionFind -import Text.PrettyPrint import Control.Monad-import qualified Data.Map as Map import qualified Data.Foldable as F+import qualified Data.Map as Map+import Text.PrettyPrint ----------------------------------------------------------------------- -- Linking@@ -28,78 +29,98 @@ -- | Given to scopes of provisions and requirements, link them together. mixLink :: [ModuleScopeU s] -> UnifyM s (ModuleScopeU s) mixLink scopes = do- let provs = Map.unionsWith (++) (map fst scopes)- -- Invariant: any identically named holes refer to same mutable cell- reqs = Map.unionsWith (++) (map snd scopes)- filled = Map.intersectionWithKey linkProvision provs reqs- F.sequenceA_ filled- let remaining = Map.difference reqs filled- return (provs, remaining)+ let provs = Map.unionsWith (++) (map fst scopes)+ -- Invariant: any identically named holes refer to same mutable cell+ reqs = Map.unionsWith (++) (map snd scopes)+ filled = Map.intersectionWithKey linkProvision provs reqs+ F.sequenceA_ filled+ let remaining = Map.difference reqs filled+ return (provs, remaining) -- | Link a list of possibly provided modules to a single -- requirement. This applies a side-condition that all -- of the provided modules at the same name are *actually* -- the same module.-linkProvision :: ModuleName- -> [ModuleWithSourceU s] -- provs- -> [ModuleWithSourceU s] -- reqs- -> UnifyM s [ModuleWithSourceU s]-linkProvision mod_name ret@(prov:provs) (req:reqs) = do- -- TODO: coalesce all the non-unifying modules together- forM_ provs $ \prov' -> do- -- Careful: read it out BEFORE unifying, because the- -- unification algorithm preemptively unifies modules- mod <- convertModuleU (unWithSource prov)- mod' <- convertModuleU (unWithSource prov')- r <- unify prov prov'- case r of- Just () -> return ()- Nothing -> do- addErr $- text "Ambiguous module" <+> quotes (pretty mod_name) $$- text "It could refer to" <+>- ( text " " <+> (quotes (pretty mod) $$ in_scope_by (getSource prov)) $$- text "or" <+> (quotes (pretty mod') $$ in_scope_by (getSource prov')) ) $$- link_doc+linkProvision+ :: ModuleName+ -> [ModuleWithSourceU s] -- provs+ -> [ModuleWithSourceU s] -- reqs+ -> UnifyM s [ModuleWithSourceU s]+linkProvision mod_name ret@(prov : provs) (req : reqs) = do+ -- TODO: coalesce all the non-unifying modules together+ forM_ provs $ \prov' -> do+ -- Careful: read it out BEFORE unifying, because the+ -- unification algorithm preemptively unifies modules mod <- convertModuleU (unWithSource prov)- req_mod <- convertModuleU (unWithSource req)- self_cid <- fmap unify_self_cid getUnifEnv- case mod of- OpenModule (IndefFullUnitId cid _) _- | cid == self_cid -> addErr $- text "Cannot instantiate requirement" <+> quotes (pretty mod_name) <+>- in_scope_by (getSource req) $$- text "with locally defined module" <+> in_scope_by (getSource prov) $$- text "as this would create a cyclic dependency, which GHC does not support." $$- text "Try moving this module to a separate library, e.g.," $$- text "create a new stanza: library 'sublib'."- _ -> return ()- r <- unify prov req+ mod' <- convertModuleU (unWithSource prov')+ r <- unify prov prov' case r of- Just () -> return ()- Nothing -> do- -- TODO: Record and report WHERE the bad constraint came from- addErr $ text "Could not instantiate requirement" <+> quotes (pretty mod_name) $$- nest 4 (text "Expected:" <+> pretty mod $$- text "Actual: " <+> pretty req_mod) $$- parens (text "This can occur if an exposed module of" <+>- text "a libraries shares a name with another module.") $$- link_doc- return ret+ Just () -> return ()+ Nothing -> do+ addErr $+ text "Ambiguous module"+ <+> quotes (pretty mod_name)+ $$ text "It could refer to"+ <+> ( text " "+ <+> (quotes (pretty mod) $$ in_scope_by (getSource prov))+ $$ text "or"+ <+> (quotes (pretty mod') $$ in_scope_by (getSource prov'))+ )+ $$ link_doc+ mod <- convertModuleU (unWithSource prov)+ req_mod <- convertModuleU (unWithSource req)+ self_cid <- fmap unify_self_cid getUnifEnv+ case mod of+ OpenModule (IndefFullUnitId cid _) _+ | cid == self_cid ->+ addErr $+ text "Cannot instantiate requirement"+ <+> quotes (pretty mod_name)+ <+> in_scope_by (getSource req)+ $$ text "with locally defined module"+ <+> in_scope_by (getSource prov)+ $$ text "as this would create a cyclic dependency, which GHC does not support."+ $$ text "Try moving this module to a separate library, e.g.,"+ $$ text "create a new stanza: library 'sublib'."+ _ -> return ()+ r <- unify prov req+ case r of+ Just () -> return ()+ Nothing -> do+ -- TODO: Record and report WHERE the bad constraint came from+ addErr $+ text "Could not instantiate requirement"+ <+> quotes (pretty mod_name)+ $$ nest+ 4+ ( text "Expected:"+ <+> pretty mod+ $$ text "Actual: "+ <+> pretty req_mod+ )+ $$ parens+ ( text "This can occur if an exposed module of"+ <+> text "a libraries shares a name with another module."+ )+ $$ link_doc+ return ret where- unify s1 s2 = tryM $ addErrContext short_link_doc- $ unifyModule (unWithSource s1) (unWithSource s2)+ unify s1 s2 =+ tryM $+ addErrContext short_link_doc $+ unifyModule (unWithSource s1) (unWithSource s2) in_scope_by s = text "brought into scope by" <+> dispModuleSource s short_link_doc = text "While filling requirement" <+> quotes (pretty mod_name) link_doc = text "While filling requirements of" <+> reqs_doc reqs_doc | null reqs = dispModuleSource (getSource req)- | otherwise = ( text " " <+> dispModuleSource (getSource req) $$- vcat [ text "and" <+> dispModuleSource (getSource r) | r <- reqs])+ | otherwise =+ ( text " "+ <+> dispModuleSource (getSource req)+ $$ vcat [text "and" <+> dispModuleSource (getSource r) | r <- reqs]+ ) linkProvision _ _ _ = error "linkProvision" -- ----------------------------------------------------------------------- -- The unification algorithm @@ -108,78 +129,93 @@ unifyUnitId :: UnitIdU s -> UnitIdU s -> UnifyM s () unifyUnitId uid1_u uid2_u- | uid1_u == uid2_u = return ()- | otherwise = do- xuid1 <- liftST $ UnionFind.find uid1_u- xuid2 <- liftST $ UnionFind.find uid2_u- case (xuid1, xuid2) of- (UnitIdThunkU u1, UnitIdThunkU u2)- | u1 == u2 -> return ()- | otherwise ->- failWith $ hang (text "Couldn't match unit IDs:") 4- (text " " <+> pretty u1 $$- text "and" <+> pretty u2)- (UnitIdThunkU uid1, UnitIdU _ cid2 insts2)- -> unifyThunkWith cid2 insts2 uid2_u uid1 uid1_u- (UnitIdU _ cid1 insts1, UnitIdThunkU uid2)- -> unifyThunkWith cid1 insts1 uid1_u uid2 uid2_u- (UnitIdU _ cid1 insts1, UnitIdU _ cid2 insts2)- -> unifyInner cid1 insts1 uid1_u cid2 insts2 uid2_u+ | uid1_u == uid2_u = return ()+ | otherwise = do+ xuid1 <- liftST $ UnionFind.find uid1_u+ xuid2 <- liftST $ UnionFind.find uid2_u+ case (xuid1, xuid2) of+ (UnitIdThunkU u1, UnitIdThunkU u2)+ | u1 == u2 -> return ()+ | otherwise ->+ failWith $+ hang+ (text "Couldn't match unit IDs:")+ 4+ ( text " "+ <+> pretty u1+ $$ text "and"+ <+> pretty u2+ )+ (UnitIdThunkU uid1, UnitIdU _ cid2 insts2) ->+ unifyThunkWith cid2 insts2 uid2_u uid1 uid1_u+ (UnitIdU _ cid1 insts1, UnitIdThunkU uid2) ->+ unifyThunkWith cid1 insts1 uid1_u uid2 uid2_u+ (UnitIdU _ cid1 insts1, UnitIdU _ cid2 insts2) ->+ unifyInner cid1 insts1 uid1_u cid2 insts2 uid2_u -unifyThunkWith :: ComponentId- -> Map ModuleName (ModuleU s)- -> UnitIdU s- -> DefUnitId- -> UnitIdU s- -> UnifyM s ()+unifyThunkWith+ :: ComponentId+ -> Map ModuleName (ModuleU s)+ -> UnitIdU s+ -> DefUnitId+ -> UnitIdU s+ -> UnifyM s () unifyThunkWith cid1 insts1 uid1_u uid2 uid2_u = do- db <- fmap unify_db getUnifEnv- let FullUnitId cid2 insts2' = expandUnitId db uid2- insts2 <- convertModuleSubst insts2'- unifyInner cid1 insts1 uid1_u cid2 insts2 uid2_u+ db <- fmap unify_db getUnifEnv+ let FullUnitId cid2 insts2' = expandUnitId db uid2+ insts2 <- convertModuleSubst insts2'+ unifyInner cid1 insts1 uid1_u cid2 insts2 uid2_u -unifyInner :: ComponentId- -> Map ModuleName (ModuleU s)- -> UnitIdU s- -> ComponentId- -> Map ModuleName (ModuleU s)- -> UnitIdU s- -> UnifyM s ()+unifyInner+ :: ComponentId+ -> Map ModuleName (ModuleU s)+ -> UnitIdU s+ -> ComponentId+ -> Map ModuleName (ModuleU s)+ -> UnitIdU s+ -> UnifyM s () unifyInner cid1 insts1 uid1_u cid2 insts2 uid2_u = do- when (cid1 /= cid2) $- -- TODO: if we had a package identifier, could be an- -- easier to understand error message.- failWith $- hang (text "Couldn't match component IDs:") 4- (text " " <+> pretty cid1 $$- text "and" <+> pretty cid2)- -- The KEY STEP which makes this a Huet-style unification- -- algorithm. (Also a payoff of using union-find.)- -- We can build infinite unit IDs this way, which is necessary- -- for support mutual recursion. NB: union keeps the SECOND- -- descriptor, so we always arrange for a UnitIdThunkU to live- -- there.- liftST $ UnionFind.union uid1_u uid2_u- F.sequenceA_ $ Map.intersectionWith unifyModule insts1 insts2+ when (cid1 /= cid2) $+ -- TODO: if we had a package identifier, could be an+ -- easier to understand error message.+ failWith $+ hang+ (text "Couldn't match component IDs:")+ 4+ ( text " "+ <+> pretty cid1+ $$ text "and"+ <+> pretty cid2+ )+ -- The KEY STEP which makes this a Huet-style unification+ -- algorithm. (Also a payoff of using union-find.)+ -- We can build infinite unit IDs this way, which is necessary+ -- for support mutual recursion. NB: union keeps the SECOND+ -- descriptor, so we always arrange for a UnitIdThunkU to live+ -- there.+ liftST $ UnionFind.union uid1_u uid2_u+ F.sequenceA_ $ Map.intersectionWith unifyModule insts1 insts2 -- | Imperatively unify two modules. unifyModule :: ModuleU s -> ModuleU s -> UnifyM s () unifyModule mod1_u mod2_u- | mod1_u == mod2_u = return ()- | otherwise = do- mod1 <- liftST $ UnionFind.find mod1_u- mod2 <- liftST $ UnionFind.find mod2_u- case (mod1, mod2) of- (ModuleVarU _, _) -> liftST $ UnionFind.union mod1_u mod2_u- (_, ModuleVarU _) -> liftST $ UnionFind.union mod2_u mod1_u- (ModuleU uid1 mod_name1, ModuleU uid2 mod_name2) -> do- when (mod_name1 /= mod_name2) $- failWith $- hang (text "Cannot match module names") 4 $- text " " <+> pretty mod_name1 $$- text "and" <+> pretty mod_name2- -- NB: this is not actually necessary (because we'll- -- detect loops eventually in 'unifyUnitId'), but it- -- seems harmless enough- liftST $ UnionFind.union mod1_u mod2_u- unifyUnitId uid1 uid2+ | mod1_u == mod2_u = return ()+ | otherwise = do+ mod1 <- liftST $ UnionFind.find mod1_u+ mod2 <- liftST $ UnionFind.find mod2_u+ case (mod1, mod2) of+ (ModuleVarU _, _) -> liftST $ UnionFind.union mod1_u mod2_u+ (_, ModuleVarU _) -> liftST $ UnionFind.union mod2_u mod1_u+ (ModuleU uid1 mod_name1, ModuleU uid2 mod_name2) -> do+ when (mod_name1 /= mod_name2) $+ failWith $+ hang (text "Cannot match module names") 4 $+ text " "+ <+> pretty mod_name1+ $$ text "and"+ <+> pretty mod_name2+ -- NB: this is not actually necessary (because we'll+ -- detect loops eventually in 'unifyUnitId'), but it+ -- seems harmless enough+ liftST $ UnionFind.union mod1_u mod2_u+ unifyUnitId uid1 uid2
src/Distribution/Backpack/ModSubst.hs view
@@ -5,13 +5,12 @@ -- applied to them. -- -- See also <https://github.com/ezyang/ghc-proposals/blob/backpack/proposals/0000-backpack.rst>--module Distribution.Backpack.ModSubst (- ModSubst(..),-) where+module Distribution.Backpack.ModSubst+ ( ModSubst (..)+ ) where -import Prelude () import Distribution.Compat.Prelude hiding (mod)+import Prelude () import Distribution.Backpack import Distribution.ModuleName@@ -21,32 +20,33 @@ -- | Applying module substitutions to semantic objects. class ModSubst a where- -- In notation, substitution is postfix, which implies- -- putting it on the right hand side, but for partial- -- application it's more convenient to have it on the left- -- hand side.- modSubst :: OpenModuleSubst -> a -> a+ -- In notation, substitution is postfix, which implies+ -- putting it on the right hand side, but for partial+ -- application it's more convenient to have it on the left+ -- hand side.+ modSubst :: OpenModuleSubst -> a -> a instance ModSubst OpenModule where- modSubst subst (OpenModule cid mod_name) = OpenModule (modSubst subst cid) mod_name- modSubst subst mod@(OpenModuleVar mod_name)- | Just mod' <- Map.lookup mod_name subst = mod'- | otherwise = mod+ modSubst subst (OpenModule cid mod_name) = OpenModule (modSubst subst cid) mod_name+ modSubst subst mod@(OpenModuleVar mod_name)+ | Just mod' <- Map.lookup mod_name subst = mod'+ | otherwise = mod instance ModSubst OpenUnitId where- modSubst subst (IndefFullUnitId cid insts) = IndefFullUnitId cid (modSubst subst insts)- modSubst _subst uid = uid+ modSubst subst (IndefFullUnitId cid insts) = IndefFullUnitId cid (modSubst subst insts)+ modSubst _subst uid = uid instance ModSubst (Set ModuleName) where- modSubst subst reqs- = Set.union (Set.difference reqs (Map.keysSet subst))- (openModuleSubstFreeHoles subst)+ modSubst subst reqs =+ Set.union+ (Set.difference reqs (Map.keysSet subst))+ (openModuleSubstFreeHoles subst) -- Substitutions are functorial. NB: this means that -- there is an @instance 'ModSubst' 'ModuleSubst'@! instance ModSubst a => ModSubst (Map k a) where- modSubst subst = fmap (modSubst subst)+ modSubst subst = fmap (modSubst subst) instance ModSubst a => ModSubst [a] where- modSubst subst = fmap (modSubst subst)+ modSubst subst = fmap (modSubst subst) instance ModSubst a => ModSubst (k, a) where- modSubst subst (x,y) = (x, modSubst subst y)+ modSubst subst (x, y) = (x, modSubst subst y)
src/Distribution/Backpack/ModuleScope.hs view
@@ -1,30 +1,29 @@-{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveTraversable #-}-{-# LANGUAGE DeriveFoldable #-}+ -- | See <https://github.com/ezyang/ghc-proposals/blob/backpack/proposals/0000-backpack.rst>-module Distribution.Backpack.ModuleScope (- -- * Module scopes- ModuleScope(..),- ModuleProvides,- ModuleRequires,- ModuleSource(..),- dispModuleSource,- WithSource(..),- unWithSource,- getSource,- ModuleWithSource,- emptyModuleScope,-) where+module Distribution.Backpack.ModuleScope+ ( -- * Module scopes+ ModuleScope (..)+ , ModuleProvides+ , ModuleRequires+ , ModuleSource (..)+ , dispModuleSource+ , WithSource (..)+ , unWithSource+ , getSource+ , ModuleWithSource+ , emptyModuleScope+ ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () import Distribution.ModuleName-import Distribution.Types.IncludeRenaming-import Distribution.Types.PackageName+import Distribution.Pretty import Distribution.Types.ComponentName+import Distribution.Types.IncludeRenaming import Distribution.Types.LibraryName-import Distribution.Pretty+import Distribution.Types.PackageName import Distribution.Backpack import Distribution.Backpack.ModSubst@@ -32,7 +31,6 @@ import qualified Data.Map as Map import Text.PrettyPrint - ----------------------------------------------------------------------- -- Module scopes @@ -60,17 +58,16 @@ -- Alternate strategy: go ahead and unify, and then if it is revealed -- that some requirements got filled "out-of-thin-air", error. - -- | A 'ModuleScope' describes the modules and requirements that -- are in-scope as we are processing a Cabal package. Unlike -- a 'ModuleShape', there may be multiple modules in scope at -- the same 'ModuleName'; this is only an error if we attempt -- to use those modules to fill a requirement. A 'ModuleScope' -- can influence the 'ModuleShape' via a reexport.-data ModuleScope = ModuleScope {- modScopeProvides :: ModuleProvides,- modScopeRequires :: ModuleRequires- }+data ModuleScope = ModuleScope+ { modScopeProvides :: ModuleProvides+ , modScopeRequires :: ModuleRequires+ } -- | An empty 'ModuleScope'. emptyModuleScope :: ModuleScope@@ -79,49 +76,53 @@ -- | Every 'Module' in scope at a 'ModuleName' is annotated with -- the 'PackageName' it comes from. type ModuleProvides = Map ModuleName [ModuleWithSource]+ -- | INVARIANT: entries for ModuleName m, have msrc_module is OpenModuleVar m type ModuleRequires = Map ModuleName [ModuleWithSource]+ -- TODO: consider newtping the two types above. -- | Description of where a module participating in mixin linking came -- from. data ModuleSource- = FromMixins PackageName ComponentName IncludeRenaming- | FromBuildDepends PackageName ComponentName- | FromExposedModules ModuleName- | FromOtherModules ModuleName- | FromSignatures ModuleName+ = FromMixins PackageName ComponentName IncludeRenaming+ | FromBuildDepends PackageName ComponentName+ | FromExposedModules ModuleName+ | FromOtherModules ModuleName+ | FromSignatures ModuleName+ -- We don't have line numbers, but if we did, we'd want to record that -- too -- TODO: Deduplicate this with Distribution.Backpack.UnifyM.ci_msg dispModuleSource :: ModuleSource -> Doc-dispModuleSource (FromMixins pn cn incls)- = text "mixins:" <+> dispComponent pn cn <+> pretty incls-dispModuleSource (FromBuildDepends pn cn)- = text "build-depends:" <+> dispComponent pn cn-dispModuleSource (FromExposedModules m)- = text "exposed-modules:" <+> pretty m-dispModuleSource (FromOtherModules m)- = text "other-modules:" <+> pretty m-dispModuleSource (FromSignatures m)- = text "signatures:" <+> pretty m+dispModuleSource (FromMixins pn cn incls) =+ text "mixins:" <+> dispComponent pn cn <+> pretty incls+dispModuleSource (FromBuildDepends pn cn) =+ text "build-depends:" <+> dispComponent pn cn+dispModuleSource (FromExposedModules m) =+ text "exposed-modules:" <+> pretty m+dispModuleSource (FromOtherModules m) =+ text "other-modules:" <+> pretty m+dispModuleSource (FromSignatures m) =+ text "signatures:" <+> pretty m -- Dependency dispComponent :: PackageName -> ComponentName -> Doc dispComponent pn cn =- -- NB: This syntax isn't quite the source syntax, but it- -- should be clear enough. To do source syntax, we'd- -- need to know what the package we're linking is.- case cn of- CLibName LMainLibName -> pretty pn- CLibName (LSubLibName ucn) -> pretty pn <<>> colon <<>> pretty ucn- -- Case below shouldn't happen- _ -> pretty pn <+> parens (pretty cn)+ -- NB: This syntax isn't quite the source syntax, but it+ -- should be clear enough. To do source syntax, we'd+ -- need to know what the package we're linking is.+ case cn of+ CLibName LMainLibName -> pretty pn+ CLibName (LSubLibName ucn) -> pretty pn <<>> colon <<>> pretty ucn+ -- Case below shouldn't happen+ _ -> pretty pn <+> parens (pretty cn) -- | An 'OpenModule', annotated with where it came from in a Cabal file. data WithSource a = WithSource ModuleSource a- deriving (Functor, Foldable, Traversable)+ deriving (Functor, Foldable, Traversable)+ unWithSource :: WithSource a -> a unWithSource (WithSource _ x) = x getSource :: WithSource a -> ModuleSource@@ -129,4 +130,4 @@ type ModuleWithSource = WithSource OpenModule instance ModSubst a => ModSubst (WithSource a) where- modSubst subst (WithSource s m) = WithSource s (modSubst subst m)+ modSubst subst (WithSource s m) = WithSource s (modSubst subst m)
src/Distribution/Backpack/ModuleShape.hs view
@@ -1,21 +1,21 @@-{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-}+ -- | See <https://github.com/ezyang/ghc-proposals/blob/backpack/proposals/0000-backpack.rst>-module Distribution.Backpack.ModuleShape (- -- * Module shapes- ModuleShape(..),- emptyModuleShape,- shapeInstalledPackage,-) where+module Distribution.Backpack.ModuleShape+ ( -- * Module shapes+ ModuleShape (..)+ , emptyModuleShape+ , shapeInstalledPackage+ ) where -import Prelude () import Distribution.Compat.Prelude hiding (mod)+import Prelude () -import Distribution.ModuleName import Distribution.InstalledPackageInfo as IPI+import Distribution.ModuleName -import Distribution.Backpack.ModSubst import Distribution.Backpack+import Distribution.Backpack.ModSubst import qualified Data.Map as Map import qualified Data.Set as Set@@ -26,18 +26,18 @@ -- | A 'ModuleShape' describes the provisions and requirements of -- a library. We can extract a 'ModuleShape' from an -- 'InstalledPackageInfo'.-data ModuleShape = ModuleShape {- modShapeProvides :: OpenModuleSubst,- modShapeRequires :: Set ModuleName- }- deriving (Eq, Show, Generic, Typeable)+data ModuleShape = ModuleShape+ { modShapeProvides :: OpenModuleSubst+ , modShapeRequires :: Set ModuleName+ }+ deriving (Eq, Show, Generic) instance Binary ModuleShape instance Structured ModuleShape instance ModSubst ModuleShape where- modSubst subst (ModuleShape provs reqs)- = ModuleShape (modSubst subst provs) (modSubst subst reqs)+ modSubst subst (ModuleShape provs reqs) =+ ModuleShape (modSubst subst provs) (modSubst subst reqs) -- | The default module shape, with no provisions and no requirements. emptyModuleShape :: ModuleShape@@ -78,7 +78,7 @@ uid = installedOpenUnitId ipi provs = map shapeExposedModule (IPI.exposedModules ipi) reqs = requiredSignatures ipi- shapeExposedModule (IPI.ExposedModule mod_name Nothing)- = (mod_name, OpenModule uid mod_name)- shapeExposedModule (IPI.ExposedModule mod_name (Just mod))- = (mod_name, mod)+ shapeExposedModule (IPI.ExposedModule mod_name Nothing) =+ (mod_name, OpenModule uid mod_name)+ shapeExposedModule (IPI.ExposedModule mod_name (Just mod)) =+ (mod_name, mod)
src/Distribution/Backpack/PreExistingComponent.hs view
@@ -1,59 +1,70 @@ -- | See <https://github.com/ezyang/ghc-proposals/blob/backpack/proposals/0000-backpack.rst>-module Distribution.Backpack.PreExistingComponent (- PreExistingComponent(..),- ipiToPreExistingComponent,-) where+module Distribution.Backpack.PreExistingComponent+ ( PreExistingComponent (..)+ , ConfiguredPromisedComponent (..)+ , ipiToPreExistingComponent+ ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -import Distribution.Backpack.ModuleShape import Distribution.Backpack-import Distribution.Types.ComponentId-import Distribution.Types.MungedPackageId-import Distribution.Types.PackageId-import Distribution.Types.UnitId-import Distribution.Types.ComponentName-import Distribution.Types.PackageName+import Distribution.Backpack.ModuleShape import Distribution.Package+import Distribution.Types.ComponentName+import Distribution.Types.MungedPackageId import qualified Data.Map as Map-import qualified Distribution.InstalledPackageInfo as Installed import Distribution.InstalledPackageInfo (InstalledPackageInfo)+import qualified Distribution.InstalledPackageInfo as Installed+import Distribution.Types.AnnotatedId +-- | A /promised/ component.+--+-- These components are promised to @configure@ but are not yet built.+--+-- In other words this is 'PreExistingComponent' which doesn't yet exist.+data ConfiguredPromisedComponent = ConfiguredPromisedComponent+ { pr_pkgname :: PackageName+ , pr_cid :: AnnotatedId ComponentId+ }++instance Package ConfiguredPromisedComponent where+ packageId = packageId . pr_cid+ -- | Stripped down version of 'LinkedComponent' for things -- we don't need to know how to build.-data PreExistingComponent- = PreExistingComponent {- -- | The actual name of the package. This may DISAGREE with 'pc_pkgid'- -- for internal dependencies: e.g., an internal component @lib@ may be- -- munged to @z-pkg-z-lib@, but we still want to use it when we see- -- @lib@ in @build-depends@- pc_pkgname :: PackageName,- -- | The actual name of the component.- pc_compname :: ComponentName,- pc_munged_id :: MungedPackageId,- pc_uid :: UnitId,- pc_cid :: ComponentId,- pc_open_uid :: OpenUnitId,- pc_shape :: ModuleShape- }+data PreExistingComponent = PreExistingComponent+ { pc_pkgname :: PackageName+ -- ^ The actual name of the package. This may DISAGREE with 'pc_pkgid'+ -- for internal dependencies: e.g., an internal component @lib@ may be+ -- munged to @z-pkg-z-lib@, but we still want to use it when we see+ -- @lib@ in @build-depends@+ , pc_compname :: ComponentName+ -- ^ The actual name of the component.+ , pc_munged_id :: MungedPackageId+ , pc_uid :: UnitId+ , pc_cid :: ComponentId+ , pc_open_uid :: OpenUnitId+ , pc_shape :: ModuleShape+ } -- | Convert an 'InstalledPackageInfo' into a 'PreExistingComponent', -- which was brought into scope under the 'PackageName' (important for -- a package qualified reference.) ipiToPreExistingComponent :: InstalledPackageInfo -> PreExistingComponent ipiToPreExistingComponent ipi =- PreExistingComponent {- pc_pkgname = packageName ipi,- pc_compname = CLibName $ Installed.sourceLibName ipi,- pc_munged_id = mungedId ipi,- pc_uid = Installed.installedUnitId ipi,- pc_cid = Installed.installedComponentId ipi,- pc_open_uid =- IndefFullUnitId (Installed.installedComponentId ipi)- (Map.fromList (Installed.instantiatedWith ipi)),- pc_shape = shapeInstalledPackage ipi+ PreExistingComponent+ { pc_pkgname = packageName ipi+ , pc_compname = CLibName $ Installed.sourceLibName ipi+ , pc_munged_id = mungedId ipi+ , pc_uid = Installed.installedUnitId ipi+ , pc_cid = Installed.installedComponentId ipi+ , pc_open_uid =+ IndefFullUnitId+ (Installed.installedComponentId ipi)+ (Map.fromList (Installed.instantiatedWith ipi))+ , pc_shape = shapeInstalledPackage ipi } instance HasMungedPackageId PreExistingComponent where@@ -61,7 +72,8 @@ instance Package PreExistingComponent where packageId pec = PackageIdentifier (pc_pkgname pec) v- where MungedPackageId _ v = pc_munged_id pec+ where+ MungedPackageId _ v = pc_munged_id pec instance HasUnitId PreExistingComponent where installedUnitId = pc_uid
src/Distribution/Backpack/PreModuleShape.hs view
@@ -1,38 +1,37 @@-{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} -module Distribution.Backpack.PreModuleShape (- PreModuleShape(..),- toPreModuleShape,- renamePreModuleShape,- mixLinkPreModuleShape,-) where+module Distribution.Backpack.PreModuleShape+ ( PreModuleShape (..)+ , toPreModuleShape+ , renamePreModuleShape+ , mixLinkPreModuleShape+ ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -import qualified Data.Set as Set import qualified Data.Map as Map+import qualified Data.Set as Set import Distribution.Backpack.ModuleShape+import Distribution.ModuleName import Distribution.Types.IncludeRenaming import Distribution.Types.ModuleRenaming-import Distribution.ModuleName -data PreModuleShape = PreModuleShape {- preModShapeProvides :: Set ModuleName,- preModShapeRequires :: Set ModuleName- }- deriving (Eq, Show, Generic)+data PreModuleShape = PreModuleShape+ { preModShapeProvides :: Set ModuleName+ , preModShapeRequires :: Set ModuleName+ }+ deriving (Eq, Show, Generic) toPreModuleShape :: ModuleShape -> PreModuleShape toPreModuleShape (ModuleShape provs reqs) = PreModuleShape (Map.keysSet provs) reqs renamePreModuleShape :: PreModuleShape -> IncludeRenaming -> PreModuleShape renamePreModuleShape (PreModuleShape provs reqs) (IncludeRenaming prov_rn req_rn) =- PreModuleShape- (Set.fromList (mapMaybe prov_fn (Set.toList provs)))- (Set.map req_fn reqs)+ PreModuleShape+ (Set.fromList (mapMaybe prov_fn (Set.toList provs)))+ (Set.map req_fn reqs) where prov_fn = interpModuleRenaming prov_rn req_fn k = fromMaybe k (interpModuleRenaming req_rn k)@@ -41,4 +40,4 @@ mixLinkPreModuleShape shapes = PreModuleShape provs (Set.difference reqs provs) where provs = Set.unions (map preModShapeProvides shapes)- reqs = Set.unions (map preModShapeRequires shapes)+ reqs = Set.unions (map preModShapeRequires shapes)
src/Distribution/Backpack/ReadyComponent.hs view
@@ -1,186 +1,199 @@-{-# LANGUAGE TypeFamilies #-} {-# LANGUAGE PatternGuards #-}+{-# LANGUAGE TypeFamilies #-}+ -- | See <https://github.com/ezyang/ghc-proposals/blob/backpack/proposals/0000-backpack.rst>-module Distribution.Backpack.ReadyComponent (- ReadyComponent(..),- InstantiatedComponent(..),- IndefiniteComponent(..),- rc_depends,- rc_uid,- rc_pkgid,- dispReadyComponent,- toReadyComponents,-) where+module Distribution.Backpack.ReadyComponent+ ( ReadyComponent (..)+ , InstantiatedComponent (..)+ , IndefiniteComponent (..)+ , rc_depends+ , rc_uid+ , rc_pkgid+ , dispReadyComponent+ , toReadyComponents+ ) where -import Prelude () import Distribution.Compat.Prelude hiding ((<>))+import Prelude () import Distribution.Backpack import Distribution.Backpack.LinkedComponent import Distribution.Backpack.ModuleShape +import Distribution.Compat.Graph (IsNode (..)) import Distribution.Types.AnnotatedId-import Distribution.Types.ModuleRenaming import Distribution.Types.Component-import Distribution.Types.ComponentInclude import Distribution.Types.ComponentId+import Distribution.Types.ComponentInclude import Distribution.Types.ComponentName-import Distribution.Types.PackageId-import Distribution.Types.PackageName.Magic-import Distribution.Types.UnitId-import Distribution.Compat.Graph (IsNode(..))+import Distribution.Types.Library+import Distribution.Types.LibraryName import Distribution.Types.Module+import Distribution.Types.ModuleRenaming import Distribution.Types.MungedPackageId import Distribution.Types.MungedPackageName-import Distribution.Types.Library-import Distribution.Types.LibraryName+import Distribution.Types.PackageId+import Distribution.Types.PackageName.Magic+import Distribution.Types.UnitId import Distribution.ModuleName import Distribution.Package import Distribution.Simple.Utils import Control.Monad-import Text.PrettyPrint import qualified Data.Map as Map import qualified Data.Set as Set+import Text.PrettyPrint -import Distribution.Version import Distribution.Pretty+import Distribution.Version -- | A 'ReadyComponent' is one that we can actually generate build -- products for. We have a ready component for the typecheck-only -- products of every indefinite package, as well as a ready component -- for every way these packages can be fully instantiated.----data ReadyComponent- = ReadyComponent {- rc_ann_id :: AnnotatedId UnitId,- -- | The 'OpenUnitId' for this package. At the moment, this- -- is used in only one case, which is to determine if an- -- export is of a module from this library (indefinite- -- libraries record these exports as 'OpenModule');- -- 'rc_open_uid' can be conveniently used to test for- -- equality, whereas 'UnitId' cannot always be used in this- -- case.- rc_open_uid :: OpenUnitId,- -- | Corresponds to 'lc_cid'. Invariant: if 'rc_open_uid'- -- records a 'ComponentId', it coincides with this one.- rc_cid :: ComponentId,- -- | Corresponds to 'lc_component'.- rc_component :: Component,- -- | Corresponds to 'lc_exe_deps'.- -- Build-tools don't participate in mix-in linking.- -- (but what if they could?)- rc_exe_deps :: [AnnotatedId UnitId],- -- | Corresponds to 'lc_public'.- rc_public :: Bool,- -- | Extra metadata depending on whether or not this is an- -- indefinite library (typechecked only) or an instantiated- -- component (can be compiled).- rc_i :: Either IndefiniteComponent InstantiatedComponent- }+data ReadyComponent = ReadyComponent+ { rc_ann_id :: AnnotatedId UnitId+ , rc_open_uid :: OpenUnitId+ -- ^ The 'OpenUnitId' for this package. At the moment, this+ -- is used in only one case, which is to determine if an+ -- export is of a module from this library (indefinite+ -- libraries record these exports as 'OpenModule');+ -- 'rc_open_uid' can be conveniently used to test for+ -- equality, whereas 'UnitId' cannot always be used in this+ -- case.+ , rc_cid :: ComponentId+ -- ^ Corresponds to 'lc_cid'. Invariant: if 'rc_open_uid'+ -- records a 'ComponentId', it coincides with this one.+ , rc_component :: Component+ -- ^ Corresponds to 'lc_component'.+ , rc_exe_deps :: [AnnotatedId UnitId]+ -- ^ Corresponds to 'lc_exe_deps'.+ -- Build-tools don't participate in mix-in linking.+ -- (but what if they could?)+ , rc_public :: Bool+ -- ^ Corresponds to 'lc_public'.+ , rc_i :: Either IndefiniteComponent InstantiatedComponent+ -- ^ Extra metadata depending on whether or not this is an+ -- indefinite library (typechecked only) or an instantiated+ -- component (can be compiled).+ } -- | The final, string 'UnitId' that will uniquely identify -- the compilation products of this component.-rc_uid :: ReadyComponent -> UnitId+rc_uid :: ReadyComponent -> UnitId rc_uid = ann_id . rc_ann_id -- | Corresponds to 'lc_pkgid'.-rc_pkgid :: ReadyComponent -> PackageId+rc_pkgid :: ReadyComponent -> PackageId rc_pkgid = ann_pid . rc_ann_id -- | An 'InstantiatedComponent' is a library which is fully instantiated -- (or, possibly, has no requirements at all.)-data InstantiatedComponent- = InstantiatedComponent {- -- | How this library was instantiated.- instc_insts :: [(ModuleName, Module)],- -- | Dependencies induced by 'instc_insts'. These are recorded- -- here because there isn't a convenient way otherwise to get- -- the 'PackageId' we need to fill 'componentPackageDeps' as needed.- instc_insts_deps :: [(UnitId, MungedPackageId)],- -- | The modules exported/reexported by this library.- instc_provides :: Map ModuleName Module,- -- | The dependencies which need to be passed to the compiler- -- to bring modules into scope. These always refer to installed- -- fully instantiated libraries.- instc_includes :: [ComponentInclude DefUnitId ModuleRenaming]- }+data InstantiatedComponent = InstantiatedComponent+ { instc_insts :: [(ModuleName, Module)]+ -- ^ How this library was instantiated.+ , instc_insts_deps :: [(UnitId, MungedPackageId)]+ -- ^ Dependencies induced by 'instc_insts'. These are recorded+ -- here because there isn't a convenient way otherwise to get+ -- the 'PackageId' we need to fill 'componentPackageDeps' as needed.+ , instc_provides :: Map ModuleName Module+ -- ^ The modules exported/reexported by this library.+ , instc_includes :: [ComponentInclude DefUnitId ModuleRenaming]+ -- ^ The dependencies which need to be passed to the compiler+ -- to bring modules into scope. These always refer to installed+ -- fully instantiated libraries.+ } -- | An 'IndefiniteComponent' is a library with requirements -- which we will typecheck only.-data IndefiniteComponent- = IndefiniteComponent {- -- | The requirements of the library.- indefc_requires :: [ModuleName],- -- | The modules exported/reexported by this library.- indefc_provides :: Map ModuleName OpenModule,- -- | The dependencies which need to be passed to the compiler- -- to bring modules into scope. These are 'OpenUnitId' because- -- these may refer to partially instantiated libraries.- indefc_includes :: [ComponentInclude OpenUnitId ModuleRenaming]- }+data IndefiniteComponent = IndefiniteComponent+ { indefc_requires :: [ModuleName]+ -- ^ The requirements of the library.+ , indefc_provides :: Map ModuleName OpenModule+ -- ^ The modules exported/reexported by this library.+ , indefc_includes :: [ComponentInclude OpenUnitId ModuleRenaming]+ -- ^ The dependencies which need to be passed to the compiler+ -- to bring modules into scope. These are 'OpenUnitId' because+ -- these may refer to partially instantiated libraries.+ } -- | Compute the dependencies of a 'ReadyComponent' that should -- be recorded in the @depends@ field of 'InstalledPackageInfo'. rc_depends :: ReadyComponent -> [(UnitId, MungedPackageId)] rc_depends rc = ordNub $- case rc_i rc of- Left indefc ->- map (\ci -> (abstractUnitId $ ci_id ci, toMungedPackageId ci))- (indefc_includes indefc)- Right instc ->- map (\ci -> (unDefUnitId $ ci_id ci, toMungedPackageId ci))- (instc_includes instc)- ++ instc_insts_deps instc+ case rc_i rc of+ Left indefc ->+ map+ (\ci -> (abstractUnitId $ ci_id ci, toMungedPackageId ci))+ (indefc_includes indefc)+ Right instc ->+ map+ (\ci -> (unDefUnitId $ ci_id ci, toMungedPackageId ci))+ (instc_includes instc)+ ++ instc_insts_deps instc where toMungedPackageId :: Pretty id => ComponentInclude id rn -> MungedPackageId toMungedPackageId ci =- computeCompatPackageId- (ci_pkgid ci)- (case ci_cname ci of- CLibName name -> name- _ -> error $ prettyShow (rc_cid rc) ++- " depends on non-library " ++ prettyShow (ci_id ci))+ computeCompatPackageId+ (ci_pkgid ci)+ ( case ci_cname ci of+ CLibName name -> name+ _ ->+ error $+ prettyShow (rc_cid rc)+ ++ " depends on non-library "+ ++ prettyShow (ci_id ci)+ ) -- | Get the 'MungedPackageId' of a 'ReadyComponent' IF it is -- a library. rc_munged_id :: ReadyComponent -> MungedPackageId rc_munged_id rc =- computeCompatPackageId- (rc_pkgid rc)- (case rc_component rc of- CLib lib -> libName lib- _ -> error "rc_munged_id: not library")+ computeCompatPackageId+ (rc_pkgid rc)+ ( case rc_component rc of+ CLib lib -> libName lib+ _ -> error "rc_munged_id: not library"+ ) instance Package ReadyComponent where- packageId = rc_pkgid+ packageId = rc_pkgid instance HasUnitId ReadyComponent where- installedUnitId = rc_uid+ installedUnitId = rc_uid instance IsNode ReadyComponent where- type Key ReadyComponent = UnitId- nodeKey = rc_uid- nodeNeighbors rc =- (case rc_i rc of- Right inst | [] <- instc_insts inst- -> []- | otherwise- -> [newSimpleUnitId (rc_cid rc)]- _ -> []) ++- ordNub (map fst (rc_depends rc)) ++- map ann_id (rc_exe_deps rc)+ type Key ReadyComponent = UnitId+ nodeKey = rc_uid+ nodeNeighbors rc =+ ( case rc_i rc of+ Right inst+ | [] <- instc_insts inst ->+ []+ | otherwise ->+ [newSimpleUnitId (rc_cid rc)]+ _ -> []+ )+ ++ ordNub (map fst (rc_depends rc))+ ++ map ann_id (rc_exe_deps rc) dispReadyComponent :: ReadyComponent -> Doc dispReadyComponent rc =- hang (text (case rc_i rc of- Left _ -> "indefinite"- Right _ -> "definite")- <+> pretty (nodeKey rc)- {- <+> dispModSubst (Map.fromList (lc_insts lc)) -} ) 4 $- vcat [ text "depends" <+> pretty uid- | uid <- nodeNeighbors rc ]+ hang+ ( text+ ( case rc_i rc of+ Left _ -> "indefinite"+ Right _ -> "definite"+ )+ <+> pretty (nodeKey rc)+ {- <+> dispModSubst (Map.fromList (lc_insts lc)) -}+ )+ 4+ $ vcat+ [ text "depends" <+> pretty uid+ | uid <- nodeNeighbors rc+ ] -- | The state of 'InstM'; a mapping from 'UnitId's to their -- ready component, or @Nothing@ if its an external@@ -189,22 +202,25 @@ -- | A state monad for doing instantiations (can't use actual -- State because that would be an extra dependency.)-newtype InstM a = InstM { runInstM :: InstS -> (a, InstS) }+newtype InstM a = InstM {runInstM :: InstS -> (a, InstS)} instance Functor InstM where- fmap f (InstM m) = InstM $ \s -> let (x, s') = m s- in (f x, s')+ fmap f (InstM m) = InstM $ \s ->+ let (x, s') = m s+ in (f x, s') instance Applicative InstM where- pure a = InstM $ \s -> (a, s)- InstM f <*> InstM x = InstM $ \s -> let (f', s') = f s- (x', s'') = x s'- in (f' x', s'')+ pure a = InstM $ \s -> (a, s)+ InstM f <*> InstM x = InstM $ \s ->+ let (f', s') = f s+ (x', s'') = x s'+ in (f' x', s'') instance Monad InstM where- return = pure- InstM m >>= f = InstM $ \s -> let (x, s') = m s- in runInstM (f x) s'+ return = pure+ InstM m >>= f = InstM $ \s ->+ let (x, s') = m s+ in runInstM (f x) s' -- | Given a list of 'LinkedComponent's, expand the module graph -- so that we have an instantiated graph containing all of the@@ -222,27 +238,30 @@ -- -- We also call 'improveUnitId' during this process, so that fully -- instantiated components are given 'HashedUnitId'.--- toReadyComponents- :: Map UnitId MungedPackageId- -> Map ModuleName Module -- subst for the public component- -> [LinkedComponent]- -> [ReadyComponent]-toReadyComponents pid_map subst0 comps- = catMaybes (Map.elems ready_map)+ :: Map UnitId MungedPackageId+ -> Map ModuleName Module -- subst for the public component+ -> [LinkedComponent]+ -> [ReadyComponent]+toReadyComponents pid_map subst0 comps =+ catMaybes (Map.elems ready_map) where- cmap = Map.fromList [ (lc_cid lc, lc) | lc <- comps ]+ cmap = Map.fromList [(lc_cid lc, lc) | lc <- comps] - instantiateUnitId :: ComponentId -> Map ModuleName Module- -> InstM DefUnitId+ instantiateUnitId+ :: ComponentId+ -> Map ModuleName Module+ -> InstM DefUnitId instantiateUnitId cid insts = InstM $ \s ->- case Map.lookup uid s of- Nothing ->- -- Knot tied- let (r, s') = runInstM (instantiateComponent uid cid insts)- (Map.insert uid r s)- in (def_uid, Map.insert uid r s')- Just _ -> (def_uid, s)+ case Map.lookup uid s of+ Nothing ->+ -- Knot tied+ let (r, s') =+ runInstM+ (instantiateComponent uid cid insts)+ (Map.insert uid r s)+ in (def_uid, Map.insert uid r s')+ Just _ -> (def_uid, s) where -- The mkDefUnitId here indicates that we assume -- that Cabal handles unit id hash allocation.@@ -252,125 +271,142 @@ uid = unDefUnitId def_uid instantiateComponent- :: UnitId -> ComponentId -> Map ModuleName Module- -> InstM (Maybe ReadyComponent)+ :: UnitId+ -> ComponentId+ -> Map ModuleName Module+ -> InstM (Maybe ReadyComponent) instantiateComponent uid cid insts | Just lc <- Map.lookup cid cmap = do- provides <- traverse (substModule insts) (modShapeProvides (lc_shape lc))- -- NB: lc_sig_includes is omitted here, because we don't- -- need them to build- includes <- forM (lc_includes lc) $ \ci -> do- uid' <- substUnitId insts (ci_id ci)- return ci { ci_ann_id = fmap (const uid') (ci_ann_id ci) }- exe_deps <- traverse (substExeDep insts) (lc_exe_deps lc)- s <- InstM $ \s -> (s, s)- let getDep (Module dep_def_uid _)- | let dep_uid = unDefUnitId dep_def_uid+ provides <- traverse (substModule insts) (modShapeProvides (lc_shape lc))+ -- NB: lc_sig_includes is omitted here, because we don't+ -- need them to build+ includes <- forM (lc_includes lc) $ \ci -> do+ uid' <- substUnitId insts (ci_id ci)+ return ci{ci_ann_id = fmap (const uid') (ci_ann_id ci)}+ exe_deps <- traverse (substExeDep insts) (lc_exe_deps lc)+ s <- InstM $ \s -> (s, s)+ let getDep (Module dep_def_uid _)+ | let dep_uid = unDefUnitId dep_def_uid = -- Lose DefUnitId invariant for rc_depends- = [(dep_uid,- fromMaybe err_pid $- Map.lookup dep_uid pid_map <|>- fmap rc_munged_id (join (Map.lookup dep_uid s)))]- where- err_pid = MungedPackageId- (MungedPackageName nonExistentPackageThisIsCabalBug LMainLibName)- (mkVersion [0])- instc = InstantiatedComponent {- instc_insts = Map.toList insts,- instc_insts_deps = concatMap getDep (Map.elems insts),- instc_provides = provides,- instc_includes = includes- -- NB: there is no dependency on the- -- indefinite version of this instantiated package here,- -- as (1) it doesn't go in depends in the- -- IPI: it's not a run time dep, and (2)- -- we don't have to tell GHC about it, it- -- will match up the ComponentId- -- automatically- }- return $ Just ReadyComponent {- rc_ann_id = (lc_ann_id lc) { ann_id = uid },- rc_open_uid = DefiniteUnitId (unsafeMkDefUnitId uid),- rc_cid = lc_cid lc,- rc_component = lc_component lc,- rc_exe_deps = exe_deps,- rc_public = lc_public lc,- rc_i = Right instc- }+ [+ ( dep_uid+ , fromMaybe err_pid $+ Map.lookup dep_uid pid_map+ <|> fmap rc_munged_id (join (Map.lookup dep_uid s))+ )+ ]+ where+ err_pid =+ MungedPackageId+ (MungedPackageName nonExistentPackageThisIsCabalBug LMainLibName)+ (mkVersion [0])+ instc =+ InstantiatedComponent+ { instc_insts = Map.toList insts+ , instc_insts_deps = concatMap getDep (Map.elems insts)+ , instc_provides = provides+ , instc_includes = includes+ -- NB: there is no dependency on the+ -- indefinite version of this instantiated package here,+ -- as (1) it doesn't go in depends in the+ -- IPI: it's not a run time dep, and (2)+ -- we don't have to tell GHC about it, it+ -- will match up the ComponentId+ -- automatically+ }+ return $+ Just+ ReadyComponent+ { rc_ann_id = (lc_ann_id lc){ann_id = uid}+ , rc_open_uid = DefiniteUnitId (unsafeMkDefUnitId uid)+ , rc_cid = lc_cid lc+ , rc_component = lc_component lc+ , rc_exe_deps = exe_deps+ , rc_public = lc_public lc+ , rc_i = Right instc+ } | otherwise = return Nothing substUnitId :: Map ModuleName Module -> OpenUnitId -> InstM DefUnitId substUnitId _ (DefiniteUnitId uid) =- return uid+ return uid substUnitId subst (IndefFullUnitId cid insts) = do- insts' <- substSubst subst insts- instantiateUnitId cid insts'+ insts' <- substSubst subst insts+ instantiateUnitId cid insts' -- NB: NOT composition- substSubst :: Map ModuleName Module- -> Map ModuleName OpenModule- -> InstM (Map ModuleName Module)+ substSubst+ :: Map ModuleName Module+ -> Map ModuleName OpenModule+ -> InstM (Map ModuleName Module) substSubst subst insts = traverse (substModule subst) insts substModule :: Map ModuleName Module -> OpenModule -> InstM Module substModule subst (OpenModuleVar mod_name)- | Just m <- Map.lookup mod_name subst = return m- | otherwise = error "substModule: non-closing substitution"+ | Just m <- Map.lookup mod_name subst = return m+ | otherwise = error "substModule: non-closing substitution" substModule subst (OpenModule uid mod_name) = do- uid' <- substUnitId subst uid- return (Module uid' mod_name)+ uid' <- substUnitId subst uid+ return (Module uid' mod_name) - substExeDep :: Map ModuleName Module- -> AnnotatedId OpenUnitId -> InstM (AnnotatedId UnitId)+ substExeDep+ :: Map ModuleName Module+ -> AnnotatedId OpenUnitId+ -> InstM (AnnotatedId UnitId) substExeDep insts exe_aid = do- exe_uid' <- substUnitId insts (ann_id exe_aid)- return exe_aid { ann_id = unDefUnitId exe_uid' }+ exe_uid' <- substUnitId insts (ann_id exe_aid)+ return exe_aid{ann_id = unDefUnitId exe_uid'} indefiniteUnitId :: ComponentId -> InstM UnitId indefiniteUnitId cid = do- let uid = newSimpleUnitId cid- r <- indefiniteComponent uid cid- InstM $ \s -> (uid, Map.insert uid r s)+ let uid = newSimpleUnitId cid+ r <- indefiniteComponent uid cid+ InstM $ \s -> (uid, Map.insert uid r s) indefiniteComponent :: UnitId -> ComponentId -> InstM (Maybe ReadyComponent) indefiniteComponent uid cid | Just lc <- Map.lookup cid cmap = do- -- We're going to process includes, in case some of them- -- are fully definite even without any substitution. We- -- want to build those too; see #5634.- inst_includes <- forM (lc_includes lc) $ \ci ->- if Set.null (openUnitIdFreeHoles (ci_id ci))- then do uid' <- substUnitId Map.empty (ci_id ci)- return $ ci { ci_ann_id = fmap (const (DefiniteUnitId uid')) (ci_ann_id ci) }- else return ci- exe_deps <- traverse (substExeDep Map.empty) (lc_exe_deps lc)- let indefc = IndefiniteComponent {- indefc_requires = map fst (lc_insts lc),- indefc_provides = modShapeProvides (lc_shape lc),- indefc_includes = inst_includes ++ lc_sig_includes lc- }- return $ Just ReadyComponent {- rc_ann_id = (lc_ann_id lc) { ann_id = uid },- rc_cid = lc_cid lc,- rc_open_uid = lc_uid lc,- rc_component = lc_component lc,- -- It's always fully built- rc_exe_deps = exe_deps,- rc_public = lc_public lc,- rc_i = Left indefc+ -- We're going to process includes, in case some of them+ -- are fully definite even without any substitution. We+ -- want to build those too; see #5634.+ inst_includes <- forM (lc_includes lc) $ \ci ->+ if Set.null (openUnitIdFreeHoles (ci_id ci))+ then do+ uid' <- substUnitId Map.empty (ci_id ci)+ return $ ci{ci_ann_id = fmap (const (DefiniteUnitId uid')) (ci_ann_id ci)}+ else return ci+ exe_deps <- traverse (substExeDep Map.empty) (lc_exe_deps lc)+ let indefc =+ IndefiniteComponent+ { indefc_requires = map fst (lc_insts lc)+ , indefc_provides = modShapeProvides (lc_shape lc)+ , indefc_includes = inst_includes ++ lc_sig_includes lc+ }+ return $+ Just+ ReadyComponent+ { rc_ann_id = (lc_ann_id lc){ann_id = uid}+ , rc_cid = lc_cid lc+ , rc_open_uid = lc_uid lc+ , rc_component = lc_component lc+ , -- It's always fully built+ rc_exe_deps = exe_deps+ , rc_public = lc_public lc+ , rc_i = Left indefc } | otherwise = return Nothing ready_map = snd $ runInstM work Map.empty work- -- Top-level instantiation per subst0- | not (Map.null subst0)- , [lc] <- filter lc_public (Map.elems cmap)- = do _ <- instantiateUnitId (lc_cid lc) subst0- return ()- | otherwise- = forM_ (Map.elems cmap) $ \lc ->+ -- Top-level instantiation per subst0+ | not (Map.null subst0)+ , [lc] <- filter lc_public (Map.elems cmap) =+ do+ _ <- instantiateUnitId (lc_cid lc) subst0+ return ()+ | otherwise =+ forM_ (Map.elems cmap) $ \lc -> if null (lc_insts lc)- then instantiateUnitId (lc_cid lc) Map.empty >> return ()- else indefiniteUnitId (lc_cid lc) >> return ()+ then instantiateUnitId (lc_cid lc) Map.empty >> return ()+ else indefiniteUnitId (lc_cid lc) >> return ()
src/Distribution/Backpack/UnifyM.hs view
@@ -1,163 +1,163 @@ {-# LANGUAGE Rank2Types #-} {-# LANGUAGE ScopedTypeVariables #-}--- | See <https://github.com/ezyang/ghc-proposals/blob/backpack/proposals/0000-backpack.rst>-module Distribution.Backpack.UnifyM (- -- * Unification monad- UnifyM,- runUnifyM,- failWith,- addErr,- failIfErrs,- tryM,- addErrContext,- addErrContextM,- liftST, - UnifEnv(..),- getUnifEnv,+-- | See <https://github.com/ezyang/ghc-proposals/blob/backpack/proposals/0000-backpack.rst>+module Distribution.Backpack.UnifyM+ ( -- * Unification monad+ UnifyM+ , runUnifyM+ , failWith+ , addErr+ , failIfErrs+ , tryM+ , addErrContext+ , addErrContextM+ , liftST+ , UnifEnv (..)+ , getUnifEnv -- * Modules and unit IDs- ModuleU,- ModuleU'(..),- convertModule,- convertModuleU,-- UnitIdU,- UnitIdU'(..),- convertUnitId,- convertUnitIdU,-- ModuleSubstU,- convertModuleSubstU,- convertModuleSubst,-- ModuleScopeU,- emptyModuleScopeU,- convertModuleScopeU,-- ModuleWithSourceU,-- convertInclude,- convertModuleProvides,- convertModuleProvidesU,--) where+ , ModuleU+ , ModuleU' (..)+ , convertModule+ , convertModuleU+ , UnitIdU+ , UnitIdU' (..)+ , convertUnitId+ , convertUnitIdU+ , ModuleSubstU+ , convertModuleSubstU+ , convertModuleSubst+ , ModuleScopeU+ , emptyModuleScopeU+ , convertModuleScopeU+ , ModuleWithSourceU+ , convertInclude+ , convertModuleProvides+ , convertModuleProvidesU+ ) where -import Prelude () import Distribution.Compat.Prelude hiding (mod)+import Prelude () -import Distribution.Backpack.ModuleShape-import Distribution.Backpack.ModuleScope-import Distribution.Backpack.ModSubst-import Distribution.Backpack.FullUnitId import Distribution.Backpack+import Distribution.Backpack.FullUnitId+import Distribution.Backpack.ModSubst+import Distribution.Backpack.ModuleScope+import Distribution.Backpack.ModuleShape -import qualified Distribution.Utils.UnionFind as UnionFind import Distribution.ModuleName import Distribution.Package import Distribution.PackageDescription import Distribution.Pretty-import Distribution.Types.ComponentInclude import Distribution.Types.AnnotatedId+import Distribution.Types.ComponentInclude+import qualified Distribution.Utils.UnionFind as UnionFind import Distribution.Verbosity -import Data.STRef-import Data.Traversable import Control.Monad.ST-import qualified Data.Map as Map-import qualified Data.Set as Set import Data.IntMap (IntMap) import qualified Data.IntMap as IntMap+import qualified Data.Map as Map+import Data.STRef+import qualified Data.Set as Set+import Data.Traversable import Text.PrettyPrint -- TODO: more detailed trace output on high verbosity would probably -- be appreciated by users debugging unification errors. Collect -- some good examples! -data ErrMsg = ErrMsg {- err_msg :: Doc,- err_ctx :: [Doc]- }+data ErrMsg = ErrMsg+ { err_msg :: Doc+ , err_ctx :: [Doc]+ } type MsgDoc = Doc renderErrMsg :: ErrMsg -> MsgDoc-renderErrMsg ErrMsg { err_msg = msg, err_ctx = ctx } =- msg $$ vcat ctx+renderErrMsg ErrMsg{err_msg = msg, err_ctx = ctx} =+ msg $$ vcat ctx -- | The unification monad, this monad encapsulates imperative -- unification.-newtype UnifyM s a = UnifyM { unUnifyM :: UnifEnv s -> ST s (Maybe a) }+newtype UnifyM s a = UnifyM {unUnifyM :: UnifEnv s -> ST s (Maybe a)} -- | Run a computation in the unification monad. runUnifyM :: Verbosity -> ComponentId -> FullDb -> (forall s. UnifyM s a) -> Either [MsgDoc] a-runUnifyM verbosity self_cid db m- = runST $ do i <- newSTRef 0- hmap <- newSTRef Map.empty- errs <- newSTRef []- mb_r <- unUnifyM m UnifEnv {- unify_uniq = i,- unify_reqs = hmap,- unify_self_cid = self_cid,- unify_verbosity = verbosity,- unify_ctx = [],- unify_db = db,- unify_errs = errs }- final_errs <- readSTRef errs- case mb_r of- Just x | null final_errs -> return (Right x)- _ -> return (Left (map renderErrMsg (reverse final_errs)))+runUnifyM verbosity self_cid db m =+ runST $ do+ i <- newSTRef 0+ hmap <- newSTRef Map.empty+ errs <- newSTRef []+ mb_r <-+ unUnifyM+ m+ UnifEnv+ { unify_uniq = i+ , unify_reqs = hmap+ , unify_self_cid = self_cid+ , unify_verbosity = verbosity+ , unify_ctx = []+ , unify_db = db+ , unify_errs = errs+ }+ final_errs <- readSTRef errs+ case mb_r of+ Just x | null final_errs -> return (Right x)+ _ -> return (Left (map renderErrMsg (reverse final_errs)))+ -- NB: GHC 7.6 throws a hissy fit if you pattern match on 'm'. type ErrCtx s = MsgDoc -- | The unification environment.-data UnifEnv s = UnifEnv {- -- | A supply of unique integers to label 'UnitIdU'- -- cells. This is used to determine loops in unit- -- identifiers (which can happen with mutual recursion.)- unify_uniq :: UnifRef s UnitIdUnique,- -- | The set of requirements in scope. When- -- a provision is brought into scope, we unify with- -- the requirement at the same module name to fill it.- -- This mapping grows monotonically.- unify_reqs :: UnifRef s (Map ModuleName (ModuleU s)),- -- | Component id of the unit we're linking. We use this- -- to detect if we fill a requirement with a local module,- -- which in principle should be OK but is not currently- -- supported by GHC.- unify_self_cid :: ComponentId,- -- | How verbose the error message should be- unify_verbosity :: Verbosity,- -- | The error reporting context- unify_ctx :: [ErrCtx s],- -- | The package index for expanding unit identifiers- unify_db :: FullDb,- -- | Accumulated errors- unify_errs :: UnifRef s [ErrMsg]- }+data UnifEnv s = UnifEnv+ { unify_uniq :: UnifRef s UnitIdUnique+ -- ^ A supply of unique integers to label 'UnitIdU'+ -- cells. This is used to determine loops in unit+ -- identifiers (which can happen with mutual recursion.)+ , unify_reqs :: UnifRef s (Map ModuleName (ModuleU s))+ -- ^ The set of requirements in scope. When+ -- a provision is brought into scope, we unify with+ -- the requirement at the same module name to fill it.+ -- This mapping grows monotonically.+ , unify_self_cid :: ComponentId+ -- ^ Component id of the unit we're linking. We use this+ -- to detect if we fill a requirement with a local module,+ -- which in principle should be OK but is not currently+ -- supported by GHC.+ , unify_verbosity :: Verbosity+ -- ^ How verbose the error message should be+ , unify_ctx :: [ErrCtx s]+ -- ^ The error reporting context+ , unify_db :: FullDb+ -- ^ The package index for expanding unit identifiers+ , unify_errs :: UnifRef s [ErrMsg]+ -- ^ Accumulated errors+ } instance Functor (UnifyM s) where- fmap f (UnifyM m) = UnifyM (fmap (fmap (fmap f)) m)+ fmap f (UnifyM m) = UnifyM (fmap (fmap (fmap f)) m) instance Applicative (UnifyM s) where- pure = UnifyM . pure . pure . pure- UnifyM f <*> UnifyM x = UnifyM $ \r -> do- f' <- f r- case f' of+ pure = UnifyM . pure . pure . pure+ UnifyM f <*> UnifyM x = UnifyM $ \r -> do+ f' <- f r+ case f' of+ Nothing -> return Nothing+ Just f'' -> do+ x' <- x r+ case x' of Nothing -> return Nothing- Just f'' -> do- x' <- x r- case x' of- Nothing -> return Nothing- Just x'' -> return (Just (f'' x''))+ Just x'' -> return (Just (f'' x'')) instance Monad (UnifyM s) where- return = pure- UnifyM m >>= f = UnifyM $ \r -> do- x <- m r- case x of- Nothing -> return Nothing- Just x' -> unUnifyM (f x') r+ return = pure+ UnifyM m >>= f = UnifyM $ \r -> do+ x <- m r+ case x of+ Nothing -> return Nothing+ Just x' -> unUnifyM (f x') r -- | Lift a computation from 'ST' monad to 'UnifyM' monad. -- Internal use only.@@ -166,32 +166,35 @@ addErr :: MsgDoc -> UnifyM s () addErr msg = do- env <- getUnifEnv- let err = ErrMsg {- err_msg = msg,- err_ctx = unify_ctx env- }- liftST $ modifySTRef (unify_errs env) (\errs -> err:errs)+ env <- getUnifEnv+ let err =+ ErrMsg+ { err_msg = msg+ , err_ctx = unify_ctx env+ }+ liftST $ modifySTRef (unify_errs env) (\errs -> err : errs) failWith :: MsgDoc -> UnifyM s a failWith msg = do- addErr msg- failM+ addErr msg+ failM failM :: UnifyM s a failM = UnifyM $ \_ -> return Nothing failIfErrs :: UnifyM s () failIfErrs = do- env <- getUnifEnv- errs <- liftST $ readSTRef (unify_errs env)- when (not (null errs)) failM+ env <- getUnifEnv+ errs <- liftST $ readSTRef (unify_errs env)+ when (not (null errs)) failM tryM :: UnifyM s a -> UnifyM s (Maybe a) tryM m =- UnifyM (\env -> do+ UnifyM+ ( \env -> do mb_r <- unUnifyM m env- return (Just mb_r))+ return (Just mb_r)+ ) {- otherFail :: ErrMsg -> UnifyM s a@@ -236,8 +239,7 @@ -- | Add a message to the error context. It may make monadic queries. addErrContextM :: ErrCtx s -> UnifyM s a -> UnifyM s a addErrContextM ctx m =- UnifyM $ \r -> unUnifyM m r { unify_ctx = ctx : unify_ctx r }-+ UnifyM $ \r -> unUnifyM m r{unify_ctx = ctx : unify_ctx r} ----------------------------------------------------------------------- -- The "unifiable" variants of the data types@@ -251,13 +253,13 @@ -- | Contents of a mutable 'ModuleU' reference. data ModuleU' s- = ModuleU (UnitIdU s) ModuleName- | ModuleVarU ModuleName+ = ModuleU (UnitIdU s) ModuleName+ | ModuleVarU ModuleName -- | Contents of a mutable 'UnitIdU' reference. data UnitIdU' s- = UnitIdU UnitIdUnique ComponentId (Map ModuleName (ModuleU s))- | UnitIdThunkU DefUnitId+ = UnitIdU UnitIdUnique ComponentId (Map ModuleName (ModuleU s))+ | UnitIdThunkU DefUnitId -- | A mutable version of 'Module' which can be imperatively unified. type ModuleU s = UnionFind.Point s (ModuleU' s)@@ -273,7 +275,6 @@ -- participate in unification! type UnitIdUnique = Int - ----------------------------------------------------------------------- -- Conversion to the unifiable data types @@ -286,7 +287,7 @@ extendMuEnv :: MuEnv s -> UnitIdU s -> MuEnv s extendMuEnv (m, i) x =- (IntMap.insert (i + 1) x m, i + 1)+ (IntMap.insert (i + 1) x m, i + 1) {- lookupMuEnv :: MuEnv s -> Int {- de Bruijn index -} -> UnitIdU s@@ -308,37 +309,42 @@ -- @hole:A@ binders. -- * @MuEnv@ - the environment for mu-binders. -convertUnitId' :: MuEnv s- -> OpenUnitId- -> UnifyM s (UnitIdU s)+convertUnitId'+ :: MuEnv s+ -> OpenUnitId+ -> UnifyM s (UnitIdU s) -- TODO: this could be more lazy if we know there are no internal -- references convertUnitId' _ (DefiniteUnitId uid) =- liftST $ UnionFind.fresh (UnitIdThunkU uid)+ liftST $ UnionFind.fresh (UnitIdThunkU uid) convertUnitId' stk (IndefFullUnitId cid insts) = do- fs <- fmap unify_uniq getUnifEnv- x <- liftST $ UnionFind.fresh (error "convertUnitId") -- tie the knot later- insts_u <- for insts $ convertModule' (extendMuEnv stk x)- u <- readUnifRef fs- writeUnifRef fs (u+1)- y <- liftST $ UnionFind.fresh (UnitIdU u cid insts_u)- liftST $ UnionFind.union x y- return y+ fs <- fmap unify_uniq getUnifEnv+ x <- liftST $ UnionFind.fresh (error "convertUnitId") -- tie the knot later+ insts_u <- for insts $ convertModule' (extendMuEnv stk x)+ u <- readUnifRef fs+ writeUnifRef fs (u + 1)+ y <- liftST $ UnionFind.fresh (UnitIdU u cid insts_u)+ liftST $ UnionFind.union x y+ return y+ -- convertUnitId' stk (UnitIdVar i) = return (lookupMuEnv stk i) -convertModule' :: MuEnv s- -> OpenModule -> UnifyM s (ModuleU s)+convertModule'+ :: MuEnv s+ -> OpenModule+ -> UnifyM s (ModuleU s) convertModule' _stk (OpenModuleVar mod_name) = do- hmap <- fmap unify_reqs getUnifEnv- hm <- readUnifRef hmap- case Map.lookup mod_name hm of- Nothing -> do mod <- liftST $ UnionFind.fresh (ModuleVarU mod_name)- writeUnifRef hmap (Map.insert mod_name mod hm)- return mod- Just mod -> return mod+ hmap <- fmap unify_reqs getUnifEnv+ hm <- readUnifRef hmap+ case Map.lookup mod_name hm of+ Nothing -> do+ mod <- liftST $ UnionFind.fresh (ModuleVarU mod_name)+ writeUnifRef hmap (Map.insert mod_name mod hm)+ return mod+ Just mod -> return mod convertModule' stk (OpenModule uid mod_name) = do- uid_u <- convertUnitId' stk uid- liftST $ UnionFind.fresh (ModuleU uid_u mod_name)+ uid_u <- convertUnitId' stk uid+ liftST $ UnionFind.fresh (ModuleU uid_u mod_name) convertUnitId :: OpenUnitId -> UnifyM s (UnitIdU s) convertUnitId = convertUnitId' emptyMuEnv@@ -346,8 +352,6 @@ convertModule :: OpenModule -> UnifyM s (ModuleU s) convertModule = convertModule' emptyMuEnv -- ----------------------------------------------------------------------- -- Substitutions @@ -380,38 +384,50 @@ lookupMooEnv :: MooEnv -> UnitIdUnique -> Maybe Int lookupMooEnv (m, i) k =- case IntMap.lookup k m of- Nothing -> Nothing- Just v -> Just (i-v) -- de Bruijn indexize+ case IntMap.lookup k m of+ Nothing -> Nothing+ Just v -> Just (i - v) -- de Bruijn indexize -- The workhorse functions -convertUnitIdU' :: MooEnv -> UnitIdU s -> UnifyM s OpenUnitId-convertUnitIdU' stk uid_u = do- x <- liftST $ UnionFind.find uid_u- case x of- UnitIdThunkU uid -> return (DefiniteUnitId uid)- UnitIdU u cid insts_u ->- case lookupMooEnv stk u of- Just _i ->- failWith (text "Unsupported mutually recursive unit identifier")- -- return (UnitIdVar i)- Nothing -> do- insts <- for insts_u $ convertModuleU' (extendMooEnv stk u)- return (IndefFullUnitId cid insts)+-- | Returns `OpenUnitId` if there is no a mutually recursive unit.+-- | Otherwise returns a list of signatures instantiated by given `UnitIdU`.+convertUnitIdU' :: MooEnv -> UnitIdU s -> Doc -> UnifyM s OpenUnitId+convertUnitIdU' stk uid_u required_mod_name = do+ x <- liftST $ UnionFind.find uid_u+ case x of+ UnitIdThunkU uid -> return $ DefiniteUnitId uid+ UnitIdU u cid insts_u ->+ case lookupMooEnv stk u of+ Just _ ->+ let mod_names = Map.keys insts_u+ in failWithMutuallyRecursiveUnitsError required_mod_name mod_names+ Nothing -> do+ insts <- for insts_u $ convertModuleU' (extendMooEnv stk u)+ return $ IndefFullUnitId cid insts convertModuleU' :: MooEnv -> ModuleU s -> UnifyM s OpenModule convertModuleU' stk mod_u = do- mod <- liftST $ UnionFind.find mod_u- case mod of- ModuleVarU mod_name -> return (OpenModuleVar mod_name)- ModuleU uid_u mod_name -> do- uid <- convertUnitIdU' stk uid_u- return (OpenModule uid mod_name)+ mod <- liftST $ UnionFind.find mod_u+ case mod of+ ModuleVarU mod_name -> return (OpenModuleVar mod_name)+ ModuleU uid_u mod_name -> do+ uid <- convertUnitIdU' stk uid_u (pretty mod_name)+ return (OpenModule uid mod_name) +failWithMutuallyRecursiveUnitsError :: Doc -> [ModuleName] -> UnifyM s a+failWithMutuallyRecursiveUnitsError required_mod_name mod_names =+ let sigsList = hcat $ punctuate (text ", ") $ map (quotes . pretty) mod_names+ in failWith $+ text "Cannot instantiate requirement"+ <+> quotes required_mod_name+ $$ text "Ensure \"build-depends:\" doesn't include any library with signatures:"+ <+> sigsList+ $$ text "as this creates a cyclic dependency, which GHC does not support."+ -- Helper functions -convertUnitIdU :: UnitIdU s -> UnifyM s OpenUnitId+convertUnitIdU :: UnitIdU s -> Doc -> UnifyM s OpenUnitId convertUnitIdU = convertUnitIdU' emptyMooEnv convertModuleU :: ModuleU s -> UnifyM s OpenModule@@ -421,11 +437,12 @@ emptyModuleScopeU :: ModuleScopeU s emptyModuleScopeU = (Map.empty, Map.empty) - -- | The mutable counterpart of 'ModuleScope'. type ModuleScopeU s = (ModuleProvidesU s, ModuleRequiresU s)+ -- | The mutable counterpart of 'ModuleProvides' type ModuleProvidesU s = Map ModuleName [ModuleWithSourceU s]+ type ModuleRequiresU s = ModuleProvidesU s type ModuleWithSourceU s = WithSource (ModuleU s) @@ -437,33 +454,41 @@ where pn = pkgName (ci_pkgid ci) pp_pn =- case ci_cname ci of- CLibName LMainLibName -> pretty pn- CLibName (LSubLibName cn) -> pretty pn <<>> colon <<>> pretty cn- -- Shouldn't happen- cn -> pretty pn <+> parens (pretty cn)+ case ci_cname ci of+ CLibName LMainLibName -> pretty pn+ CLibName (LSubLibName cn) -> pretty pn <<>> colon <<>> pretty cn+ -- Shouldn't happen+ cn -> pretty pn <+> parens (pretty cn) -- | Convert a 'ModuleShape' into a 'ModuleScopeU', so we can do -- unification on it. convertInclude- :: ComponentInclude (OpenUnitId, ModuleShape) IncludeRenaming- -> UnifyM s (ModuleScopeU s,- Either (ComponentInclude (UnitIdU s) ModuleRenaming) {- normal -}- (ComponentInclude (UnitIdU s) ModuleRenaming) {- sig -})-convertInclude ci@(ComponentInclude {- ci_ann_id = AnnotatedId {- ann_id = (uid, ModuleShape provs reqs),- ann_pid = pid,- ann_cname = compname- },- ci_renaming = incl@(IncludeRenaming prov_rns req_rns),- ci_implicit = implicit- }) = addErrContext (text "In" <+> ci_msg ci) $ do+ :: ComponentInclude (OpenUnitId, ModuleShape) IncludeRenaming+ -> UnifyM+ s+ ( ModuleScopeU s+ , Either+ (ComponentInclude (UnitIdU s) ModuleRenaming {- normal -})+ (ComponentInclude (UnitIdU s) ModuleRenaming {- sig -})+ )+convertInclude+ ci@( ComponentInclude+ { ci_ann_id =+ AnnotatedId+ { ann_id = (uid, ModuleShape provs reqs)+ , ann_pid = pid+ , ann_cname = compname+ }+ , ci_renaming = incl@(IncludeRenaming prov_rns req_rns)+ , ci_implicit = implicit+ }+ ) = addErrContext (text "In" <+> ci_msg ci) $ do let pn = packageName pid- the_source | implicit- = FromBuildDepends pn compname- | otherwise- = FromMixins pn compname incl+ the_source+ | implicit =+ FromBuildDepends pn compname+ | otherwise =+ FromMixins pn compname incl source = WithSource the_source -- Suppose our package has two requirements A and B, and@@ -488,27 +513,31 @@ case req_rns of DefaultRenaming -> return [] HidingRenaming _ -> do- -- Not valid here for requires!- addErr $ text "Unsupported syntax" <+>- quotes (text "requires hiding (...)")- return []+ -- Not valid here for requires!+ addErr $+ text "Unsupported syntax"+ <+> quotes (text "requires hiding (...)")+ return [] ModuleRenaming rns -> return rns let req_rename_listmap :: Map ModuleName [ModuleName] req_rename_listmap =- Map.fromListWith (++) [ (k,[v]) | (k,v) <- req_rename_list ]+ Map.fromListWith (++) [(k, [v]) | (k, v) <- req_rename_list] req_rename <- sequenceA . flip Map.mapWithKey req_rename_listmap $ \k vs0 -> case vs0 of- [] -> error "req_rename"+ [] -> error "req_rename" [v] -> return v- v:vs -> do addErr $- text "Conflicting renamings of requirement" <+> quotes (pretty k) $$- text "Renamed to: " <+> vcat (map pretty (v:vs))- return v+ v : vs -> do+ addErr $+ text "Conflicting renamings of requirement"+ <+> quotes (pretty k)+ $$ text "Renamed to: "+ <+> vcat (map pretty (v : vs))+ return v let req_rename_fn k = case Map.lookup k req_rename of- Nothing -> k- Just v -> v+ Nothing -> k+ Just v -> v -- Requirement substitution. --@@ -523,19 +552,25 @@ -- mappings. -- -- A -> X ==> X -> <X>, B -> <B>- reqs_u <- convertModuleRequires . Map.fromList $- [ (k, [source (OpenModuleVar k)])- | k <- map req_rename_fn (Set.toList reqs)- ]+ reqs_u <-+ convertModuleRequires . Map.fromList $+ [ (k, [source (OpenModuleVar k)])+ | k <- map req_rename_fn (Set.toList reqs)+ ] -- Report errors if there were unused renamings let leftover = Map.keysSet req_rename `Set.difference` reqs unless (Set.null leftover) $- addErr $- hang (text "The" <+> text (showComponentName compname) <+>- text "from package" <+> quotes (pretty pid)- <+> text "does not require:") 4- (vcat (map pretty (Set.toList leftover)))+ addErr $+ hang+ ( text "The"+ <+> text (showComponentName compname)+ <+> text "from package"+ <+> quotes (pretty pid)+ <+> text "does not require:"+ )+ 4+ (vcat (map pretty (Set.toList leftover))) -- Provision computation is more complex. -- For example, if we have:@@ -559,56 +594,72 @@ -- Importantly, overlapping rename targets get accumulated -- together. It's not an (immediate) error. (pre_prov_scope, prov_rns') <-- case prov_rns of- DefaultRenaming -> return (Map.toList provs, prov_rns)- HidingRenaming hides ->- let hides_set = Set.fromList hides- in let r = [ (k,v)- | (k,v) <- Map.toList provs- , not (k `Set.member` hides_set) ]- -- GHC doesn't understand hiding, so expand it out!- in return (r, ModuleRenaming (map ((\x -> (x,x)).fst) r))- ModuleRenaming rns -> do- r <- sequence- [ case Map.lookup from provs of- Just m -> return (to, m)- Nothing -> failWith $- text "Package" <+> quotes (pretty pid) <+>- text "does not expose the module" <+> quotes (pretty from)- | (from, to) <- rns ]- return (r, prov_rns)- let prov_scope = modSubst req_subst- $ Map.fromListWith (++)- [ (k, [source v])- | (k, v) <- pre_prov_scope ]+ case prov_rns of+ DefaultRenaming -> return (Map.toList provs, prov_rns)+ HidingRenaming hides ->+ let hides_set = Set.fromList hides+ in let r =+ [ (k, v)+ | (k, v) <- Map.toList provs+ , not (k `Set.member` hides_set)+ ]+ in -- GHC doesn't understand hiding, so expand it out!+ return (r, ModuleRenaming (map ((\x -> (x, x)) . fst) r))+ ModuleRenaming rns -> do+ r <-+ sequence+ [ case Map.lookup from provs of+ Just m -> return (to, m)+ Nothing ->+ failWith $+ text "Package"+ <+> quotes (pretty pid)+ <+> text "does not expose the module"+ <+> quotes (pretty from)+ | (from, to) <- rns+ ]+ return (r, prov_rns)+ let prov_scope =+ modSubst req_subst $+ Map.fromListWith+ (++)+ [ (k, [source v])+ | (k, v) <- pre_prov_scope+ ] provs_u <- convertModuleProvides prov_scope -- TODO: Assert that provs_u is empty if provs was empty- return ((provs_u, reqs_u),- -- NB: We test that requirements is not null so that- -- users can create packages with zero module exports- -- that cause some C library to linked in, etc.- (if Map.null provs && not (Set.null reqs)- then Right -- is sig- else Left) (ComponentInclude {- ci_ann_id = AnnotatedId {- ann_id = uid_u,- ann_pid = pid,- ann_cname = compname- },- ci_renaming = prov_rns',- ci_implicit = ci_implicit ci- }))+ return+ ( (provs_u, reqs_u)+ , -- NB: We test that requirements is not null so that+ -- users can create packages with zero module exports+ -- that cause some C library to linked in, etc.+ ( if Map.null provs && not (Set.null reqs)+ then Right -- is sig+ else Left+ )+ ( ComponentInclude+ { ci_ann_id =+ AnnotatedId+ { ann_id = uid_u+ , ann_pid = pid+ , ann_cname = compname+ }+ , ci_renaming = prov_rns'+ , ci_implicit = ci_implicit ci+ }+ )+ ) -- | Convert a 'ModuleScopeU' to a 'ModuleScope'. convertModuleScopeU :: ModuleScopeU s -> UnifyM s ModuleScope convertModuleScopeU (provs_u, reqs_u) = do- provs <- convertModuleProvidesU provs_u- reqs <- convertModuleRequiresU reqs_u- -- TODO: Test that the requirements are still free. If they- -- are not, they got unified, and that's dodgy at best.- return (ModuleScope provs reqs)+ provs <- convertModuleProvidesU provs_u+ reqs <- convertModuleRequiresU reqs_u+ -- TODO: Test that the requirements are still free. If they+ -- are not, they got unified, and that's dodgy at best.+ return (ModuleScope provs reqs) -- | Convert a 'ModuleProvides' to a 'ModuleProvidesU' convertModuleProvides :: ModuleProvides -> UnifyM s (ModuleProvidesU s)
− src/Distribution/CabalSpecVersion.hs
@@ -1,150 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.CabalSpecVersion where--import Prelude ()-import Distribution.Compat.Prelude---- | Different Cabal-the-spec versions.------ We branch based on this at least in the parser.----data CabalSpecVersion- = CabalSpecV1_0 -- ^ this is older than 'CabalSpecV1_2'- | CabalSpecV1_2 -- ^ new syntax (sections)- | CabalSpecV1_4- | CabalSpecV1_6- | CabalSpecV1_8- | CabalSpecV1_10- | CabalSpecV1_12- -- 1.16 -- 1.14: no changes- | CabalSpecV1_18- | CabalSpecV1_20- | CabalSpecV1_22- | CabalSpecV1_24- | CabalSpecV2_0- | CabalSpecV2_2- | CabalSpecV2_4- | CabalSpecV3_0- -- 3.2: no changes- | CabalSpecV3_4- | CabalSpecV3_6- deriving (Eq, Ord, Show, Read, Enum, Bounded, Typeable, Data, Generic)--instance Binary CabalSpecVersion-instance Structured CabalSpecVersion-instance NFData CabalSpecVersion where rnf = genericRnf---- | Show cabal spec version, but not the way in the .cabal files------ @since 3.0.0.0-showCabalSpecVersion :: CabalSpecVersion -> String-showCabalSpecVersion CabalSpecV3_6 = "3.6"-showCabalSpecVersion CabalSpecV3_4 = "3.4"-showCabalSpecVersion CabalSpecV3_0 = "3.0"-showCabalSpecVersion CabalSpecV2_4 = "2.4"-showCabalSpecVersion CabalSpecV2_2 = "2.2"-showCabalSpecVersion CabalSpecV2_0 = "2.0"-showCabalSpecVersion CabalSpecV1_24 = "1.24"-showCabalSpecVersion CabalSpecV1_22 = "1.22"-showCabalSpecVersion CabalSpecV1_20 = "1.20"-showCabalSpecVersion CabalSpecV1_18 = "1.18"-showCabalSpecVersion CabalSpecV1_12 = "1.12"-showCabalSpecVersion CabalSpecV1_10 = "1.10"-showCabalSpecVersion CabalSpecV1_8 = "1.8"-showCabalSpecVersion CabalSpecV1_6 = "1.6"-showCabalSpecVersion CabalSpecV1_4 = "1.4"-showCabalSpecVersion CabalSpecV1_2 = "1.2"-showCabalSpecVersion CabalSpecV1_0 = "1.0"--cabalSpecLatest :: CabalSpecVersion-cabalSpecLatest = CabalSpecV3_6---- | Parse 'CabalSpecVersion' from version digits.------ It may fail if for recent versions the version is not exact.----cabalSpecFromVersionDigits :: [Int] -> Maybe CabalSpecVersion-cabalSpecFromVersionDigits v- | v == [3,6] = Just CabalSpecV3_6- | v == [3,4] = Just CabalSpecV3_4- | v == [3,0] = Just CabalSpecV3_0- | v == [2,4] = Just CabalSpecV2_4- | v == [2,2] = Just CabalSpecV2_2- | v == [2,0] = Just CabalSpecV2_0- | v >= [1,25] = Nothing- | v >= [1,23] = Just CabalSpecV1_24- | v >= [1,21] = Just CabalSpecV1_22- | v >= [1,19] = Just CabalSpecV1_20- | v >= [1,17] = Just CabalSpecV1_18- | v >= [1,11] = Just CabalSpecV1_12- | v >= [1,9] = Just CabalSpecV1_10- | v >= [1,7] = Just CabalSpecV1_8- | v >= [1,5] = Just CabalSpecV1_6- | v >= [1,3] = Just CabalSpecV1_4- | v >= [1,1] = Just CabalSpecV1_2- | otherwise = Just CabalSpecV1_0---- | @since 3.4.0.0-cabalSpecToVersionDigits :: CabalSpecVersion -> [Int]-cabalSpecToVersionDigits CabalSpecV3_6 = [3,6]-cabalSpecToVersionDigits CabalSpecV3_4 = [3,4]-cabalSpecToVersionDigits CabalSpecV3_0 = [3,0]-cabalSpecToVersionDigits CabalSpecV2_4 = [2,4]-cabalSpecToVersionDigits CabalSpecV2_2 = [2,2]-cabalSpecToVersionDigits CabalSpecV2_0 = [2,0]-cabalSpecToVersionDigits CabalSpecV1_24 = [1,24]-cabalSpecToVersionDigits CabalSpecV1_22 = [1,22]-cabalSpecToVersionDigits CabalSpecV1_20 = [1,20]-cabalSpecToVersionDigits CabalSpecV1_18 = [1,18]-cabalSpecToVersionDigits CabalSpecV1_12 = [1,12]-cabalSpecToVersionDigits CabalSpecV1_10 = [1,10]-cabalSpecToVersionDigits CabalSpecV1_8 = [1,8]-cabalSpecToVersionDigits CabalSpecV1_6 = [1,6]-cabalSpecToVersionDigits CabalSpecV1_4 = [1,4]-cabalSpecToVersionDigits CabalSpecV1_2 = [1,2]-cabalSpecToVersionDigits CabalSpecV1_0 = [1,0]---- | What is the minimum Cabal library version which knows how handle--- this spec version.------ /Note:/ this is a point where we could decouple cabal-spec and Cabal--- versions, if we ever want that.------ >>> cabalSpecMinimumLibraryVersion CabalSpecV3_0--- [2,5]------ >>> cabalSpecMinimumLibraryVersion CabalSpecV2_4--- [2,3]------ @since 3.4.0.0-cabalSpecMinimumLibraryVersion :: CabalSpecVersion -> [Int]-cabalSpecMinimumLibraryVersion CabalSpecV1_0 = [1,0]-cabalSpecMinimumLibraryVersion csv = case cabalSpecToVersionDigits (pred csv) of- [x,y] -> [x, y+1]- xs -> xs--specHasCommonStanzas :: CabalSpecVersion -> HasCommonStanzas-specHasCommonStanzas v =- if v >= CabalSpecV2_2- then HasCommonStanzas- else NoCommonStanzas--specHasElif :: CabalSpecVersion -> HasElif-specHasElif v = - if v >= CabalSpecV2_2- then HasElif- else NoElif------------------------------------------------------------------------------------ Booleans------------------------------------------------------------------------------------ IDEA: make some kind of tagged booleans?-data HasElif = HasElif | NoElif- deriving (Eq, Show)--data HasCommonStanzas = HasCommonStanzas | NoCommonStanzas- deriving (Eq, Show)--data HasGlobstar = HasGlobstar | NoGlobstar
src/Distribution/Compat/Async.hs view
@@ -1,5 +1,3 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveDataTypeable #-} -- | 'Async', yet using 'MVar's. -- -- Adopted from @async@ library@@ -7,35 +5,46 @@ -- Licensed under BSD-3-Clause -- -- @since 3.2.0.0----module Distribution.Compat.Async (- AsyncM,- withAsync, waitCatch,- wait, asyncThreadId,- cancel, uninterruptibleCancel, AsyncCancelled (..),+module Distribution.Compat.Async+ ( AsyncM+ , withAsync+ , waitCatch+ , wait+ , asyncThreadId+ , cancel+ , uninterruptibleCancel+ , AsyncCancelled (..)+ -- * Cabal extras- withAsyncNF,- ) where+ , withAsyncNF+ ) where -import Control.Concurrent (ThreadId, forkIO)+import Control.Concurrent (ThreadId, forkIO) import Control.Concurrent.MVar (MVar, newEmptyMVar, putMVar, readMVar)-import Control.DeepSeq (NFData, force)+import Control.DeepSeq (NFData, force) import Control.Exception- (BlockedIndefinitelyOnMVar (..), Exception (..), SomeException (..), catch, evaluate, mask, throwIO, throwTo, try, uninterruptibleMask_)-import Control.Monad (void)-import Data.Typeable (Typeable)-import GHC.Exts (inline)--#if MIN_VERSION_base(4,7,0)-import Control.Exception (asyncExceptionFromException, asyncExceptionToException)-#endif+ ( BlockedIndefinitelyOnMVar (..)+ , Exception (..)+ , SomeException (..)+ , asyncExceptionFromException+ , asyncExceptionToException+ , catch+ , evaluate+ , mask+ , throwIO+ , throwTo+ , try+ , uninterruptibleMask_+ )+import Control.Monad (void)+import GHC.Exts (inline) -- | Async, but based on 'MVar', as we don't depend on @stm@. data AsyncM a = Async { asyncThreadId :: {-# UNPACK #-} !ThreadId- -- ^ Returns the 'ThreadId' of the thread running- -- the given 'Async'.- , _asyncMVar :: MVar (Either SomeException a)+ -- ^ Returns the 'ThreadId' of the thread running+ -- the given 'Async'.+ , _asyncMVar :: MVar (Either SomeException a) } -- | Spawn an asynchronous action in a separate thread, and pass its@@ -52,12 +61,12 @@ -- Note: a reference to the child thread is kept alive until the call -- to `withAsync` returns, so nesting many `withAsync` calls requires -- linear memory.--- withAsync :: IO a -> (AsyncM a -> IO b) -> IO b withAsync = inline withAsyncUsing forkIO withAsyncNF :: NFData a => IO a -> (AsyncM a -> IO b) -> IO b-withAsyncNF m = inline withAsyncUsing forkIO (m >>= evaluateNF) where+withAsyncNF m = inline withAsyncUsing forkIO (m >>= evaluateNF)+ where evaluateNF = evaluate . force withAsyncUsing :: (IO () -> IO ThreadId) -> IO a -> (AsyncM a -> IO b) -> IO b@@ -68,7 +77,8 @@ mask $ \restore -> do t <- doFork $ try (restore action) >>= putMVar var let a = Async t var- r <- restore (inner a) `catchAll` \e -> do+ r <-+ restore (inner a) `catchAll` \e -> do uninterruptibleCancel a throwIO e uninterruptibleCancel a@@ -79,21 +89,19 @@ -- exception is re-thrown by 'wait'. -- -- > wait = atomically . waitSTM--- {-# INLINE wait #-} wait :: AsyncM a -> IO a wait a = do- res <- waitCatch a- case res of- Left (SomeException e) -> throwIO e- Right x -> return x+ res <- waitCatch a+ case res of+ Left (SomeException e) -> throwIO e+ Right x -> return x -- | Wait for an asynchronous action to complete, and return either -- @Left e@ if the action raised an exception @e@, or @Right a@ if it -- returned a value @a@. -- -- > waitCatch = atomically . waitCatchSTM--- {-# INLINE waitCatch #-} waitCatch :: AsyncM a -> IO (Either SomeException a) waitCatch (Async _ var) = tryAgain (readMVar var)@@ -123,22 +131,21 @@ {-# INLINE cancel #-} cancel :: AsyncM a -> IO () cancel a@(Async t _) = do- throwTo t AsyncCancelled- void (waitCatch a)+ throwTo t AsyncCancelled+ void (waitCatch a) -- | The exception thrown by `cancel` to terminate a thread. data AsyncCancelled = AsyncCancelled- deriving (Show, Eq- , Typeable+ deriving+ ( Show+ , Eq ) instance Exception AsyncCancelled where-#if MIN_VERSION_base(4,7,0) -- wraps in SomeAsyncException -- See https://github.com/ghc/ghc/commit/756a970eacbb6a19230ee3ba57e24999e4157b09 fromException = asyncExceptionFromException toException = asyncExceptionToException-#endif -- | Cancel an asynchronous action --
− src/Distribution/Compat/Binary.hs
@@ -1,71 +0,0 @@-{-# LANGUAGE CPP #-}-#if __GLASGOW_HASKELL__ >= 711-{-# LANGUAGE PatternSynonyms #-}-#endif--#ifndef MIN_VERSION_binary-#define MIN_VERSION_binary(x, y, z) 0-#endif--module Distribution.Compat.Binary- ( decodeOrFailIO- , decodeFileOrFail'-#if __GLASGOW_HASKELL__ >= 708 || MIN_VERSION_binary(0,7,0)- , module Data.Binary-#else- , Binary(..)- , decode, encode, encodeFile-#endif- ) where--import Control.Exception (ErrorCall (..), catch, evaluate)-import Data.ByteString.Lazy (ByteString)--#if __GLASGOW_HASKELL__ >= 708 || MIN_VERSION_binary(0,7,0)--import Data.Binary---- | Lazily reconstruct a value previously written to a file.-decodeFileOrFail' :: Binary a => FilePath -> IO (Either String a)-decodeFileOrFail' f = either (Left . snd) Right `fmap` decodeFileOrFail f--#else--import Data.Binary.Get-import Data.Binary.Put-import qualified Data.ByteString.Lazy as BSL--import Distribution.Compat.Binary.Class-import Distribution.Compat.Binary.Generic ()---- | Decode a value from a lazy ByteString, reconstructing the--- original structure.----decode :: Binary a => ByteString -> a-decode = runGet get---- | Encode a value using binary serialisation to a lazy ByteString.----encode :: Binary a => a -> ByteString-encode = runPut . put-{-# INLINE encode #-}---- | Lazily reconstruct a value previously written to a file.-decodeFileOrFail' :: Binary a => FilePath -> IO (Either String a)-decodeFileOrFail' f = decodeOrFailIO =<< BSL.readFile f---- | Lazily serialise a value to a file-encodeFile :: Binary a => FilePath -> a -> IO ()-encodeFile f = BSL.writeFile f . encode--#endif--decodeOrFailIO :: Binary a => ByteString -> IO (Either String a)-decodeOrFailIO bs =- catch (evaluate (decode bs) >>= return . Right) handler- where-#if MIN_VERSION_base(4,9,0)- handler (ErrorCallWithLocation str _) = return $ Left str-#else- handler (ErrorCall str) = return $ Left str-#endif
− src/Distribution/Compat/Binary/Class.hs
@@ -1,519 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE Trustworthy #-}-{-# LANGUAGE DefaultSignatures #-}--------------------------------------------------------------------------------- |--- Module : Distribution.Compat.Binary.Class--- Copyright : Lennart Kolmodin--- License : BSD3-style (see LICENSE)------ Maintainer : Lennart Kolmodin <kolmodin@gmail.com>--- Stability : unstable--- Portability : portable to Hugs and GHC. Requires the FFI and some flexible instances------ Typeclass and instances for binary serialization.-----------------------------------------------------------------------------------module Distribution.Compat.Binary.Class (-- -- * The Binary class- Binary(..)-- -- * Support for generics- , GBinary(..)-- ) where--import Data.Word--import Data.Binary.Put-import Data.Binary.Get--import Control.Applicative ((<$>), (<*>), (*>))-import Foreign--import Data.ByteString.Lazy (ByteString)-import qualified Data.ByteString.Lazy as L--import Data.Char (chr,ord)-import Data.List (unfoldr)-import Data.Foldable (traverse_)---- And needed for the instances:-import qualified Data.ByteString as B-import qualified Data.Map as Map-import qualified Data.Set as Set-import qualified Data.IntMap as IntMap-import qualified Data.IntSet as IntSet-import qualified Data.Ratio as R--import qualified Data.Tree as T--import Data.Array.Unboxed--import GHC.Generics--import qualified Data.Sequence as Seq-import qualified Data.Foldable as Fold----------------------------------------------------------------------------class GBinary f where- gput :: f t -> Put- gget :: Get (f t)---- | The 'Binary' class provides 'put' and 'get', methods to encode and--- decode a Haskell value to a lazy 'ByteString'. It mirrors the 'Read' and--- 'Show' classes for textual representation of Haskell types, and is--- suitable for serialising Haskell values to disk, over the network.------ For decoding and generating simple external binary formats (e.g. C--- structures), Binary may be used, but in general is not suitable--- for complex protocols. Instead use the 'Put' and 'Get' primitives--- directly.------ Instances of Binary should satisfy the following property:------ > decode . encode == id------ That is, the 'get' and 'put' methods should be the inverse of each--- other. A range of instances are provided for basic Haskell types.----class Binary t where- -- | Encode a value in the Put monad.- put :: t -> Put- -- | Decode a value in the Get monad- get :: Get t-- default put :: (Generic t, GBinary (Rep t)) => t -> Put- put = gput . from-- default get :: (Generic t, GBinary (Rep t)) => Get t- get = to `fmap` gget----------------------------------------------------------------------------- Simple instances---- The () type need never be written to disk: values of singleton type--- can be reconstructed from the type alone-instance Binary () where- put () = return ()- get = return ()---- Bools are encoded as a byte in the range 0 .. 1-instance Binary Bool where- put = putWord8 . fromIntegral . fromEnum- get = fmap (toEnum . fromIntegral) getWord8---- Values of type 'Ordering' are encoded as a byte in the range 0 .. 2-instance Binary Ordering where- put = putWord8 . fromIntegral . fromEnum- get = fmap (toEnum . fromIntegral) getWord8----------------------------------------------------------------------------- Words and Ints---- Words8s are written as bytes-instance Binary Word8 where- put = putWord8- get = getWord8---- Words16s are written as 2 bytes in big-endian (network) order-instance Binary Word16 where- put = putWord16be- get = getWord16be---- Words32s are written as 4 bytes in big-endian (network) order-instance Binary Word32 where- put = putWord32be- get = getWord32be---- Words64s are written as 8 bytes in big-endian (network) order-instance Binary Word64 where- put = putWord64be- get = getWord64be---- Int8s are written as a single byte.-instance Binary Int8 where- put i = put (fromIntegral i :: Word8)- get = fmap fromIntegral (get :: Get Word8)---- Int16s are written as a 2 bytes in big endian format-instance Binary Int16 where- put i = put (fromIntegral i :: Word16)- get = fmap fromIntegral (get :: Get Word16)---- Int32s are written as a 4 bytes in big endian format-instance Binary Int32 where- put i = put (fromIntegral i :: Word32)- get = fmap fromIntegral (get :: Get Word32)---- Int64s are written as a 4 bytes in big endian format-instance Binary Int64 where- put i = put (fromIntegral i :: Word64)- get = fmap fromIntegral (get :: Get Word64)------------------------------------------------------------------------------ Words are are written as Word64s, that is, 8 bytes in big endian format-instance Binary Word where- put i = put (fromIntegral i :: Word64)- get = fmap fromIntegral (get :: Get Word64)---- Ints are are written as Int64s, that is, 8 bytes in big endian format-instance Binary Int where- put i = put (fromIntegral i :: Int64)- get = fmap fromIntegral (get :: Get Int64)-------------------------------------------------------------------------------- Portable, and pretty efficient, serialisation of Integer------- Fixed-size type for a subset of Integer-type SmallInt = Int32---- Integers are encoded in two ways: if they fit inside a SmallInt,--- they're written as a byte tag, and that value. If the Integer value--- is too large to fit in a SmallInt, it is written as a byte array,--- along with a sign and length field.--instance Binary Integer where-- {-# INLINE put #-}- put n | n >= lo && n <= hi = do- putWord8 0- put (fromIntegral n :: SmallInt) -- fast path- where- lo = fromIntegral (minBound :: SmallInt) :: Integer- hi = fromIntegral (maxBound :: SmallInt) :: Integer-- put n = do- putWord8 1- put sign- put (unroll (abs n)) -- unroll the bytes- where- sign = fromIntegral (signum n) :: Word8-- {-# INLINE get #-}- get = do- tag <- get :: Get Word8- case tag of- 0 -> fmap fromIntegral (get :: Get SmallInt)- _ -> do sign <- get- bytes <- get- let v = roll bytes- return $! if sign == (1 :: Word8) then v else - v------- Fold and unfold an Integer to and from a list of its bytes----unroll :: Integer -> [Word8]-unroll = unfoldr step- where- step 0 = Nothing- step i = Just (fromIntegral i, i `shiftR` 8)--roll :: [Word8] -> Integer-roll = foldr unstep 0- where- unstep b a = a `shiftL` 8 .|. fromIntegral b--{-------- An efficient, raw serialisation for Integer (GHC only)------- TODO This instance is not architecture portable. GMP stores numbers as--- arrays of machine sized words, so the byte format is not portable across--- architectures with different endianness and word size.--import Data.ByteString.Base (toForeignPtr,unsafePackAddress, memcpy)-import GHC.Base hiding (ord, chr)-import GHC.Prim-import GHC.Ptr (Ptr(..))-import GHC.IOBase (IO(..))--instance Binary Integer where- put (S# i) = putWord8 0 *> put (I# i)- put (J# s ba) = do- putWord8 1- put (I# s)- put (BA ba)-- get = do- b <- getWord8- case b of- 0 -> do (I# i#) <- get- return (S# i#)- _ -> do (I# s#) <- get- (BA a#) <- get- return (J# s# a#)--instance Binary ByteArray where-- -- Pretty safe.- put (BA ba) =- let sz = sizeofByteArray# ba -- (primitive) in *bytes*- addr = byteArrayContents# ba- bs = unsafePackAddress (I# sz) addr- in put bs -- write as a ByteString. easy, yay!-- -- Pretty scary. Should be quick though- get = do- (fp, off, n@(I# sz)) <- fmap toForeignPtr get -- so decode a ByteString- assert (off == 0) $ return $ unsafePerformIO $ do- (MBA arr) <- newByteArray sz -- and copy it into a ByteArray#- let to = byteArrayContents# (unsafeCoerce# arr) -- urk, is this safe?- withForeignPtr fp $ \from -> memcpy (Ptr to) from (fromIntegral n)- freezeByteArray arr---- wrapper for ByteArray#-data ByteArray = BA {-# UNPACK #-} !ByteArray#-data MBA = MBA {-# UNPACK #-} !(MutableByteArray# RealWorld)--newByteArray :: Int# -> IO MBA-newByteArray sz = IO $ \s ->- case newPinnedByteArray# sz s of { (# s', arr #) ->- (# s', MBA arr #) }--freezeByteArray :: MutableByteArray# RealWorld -> IO ByteArray-freezeByteArray arr = IO $ \s ->- case unsafeFreezeByteArray# arr s of { (# s', arr' #) ->- (# s', BA arr' #) }---}--instance (Binary a,Integral a) => Binary (R.Ratio a) where- put r = put (R.numerator r) *> put (R.denominator r)- get = (R.%) <$> get <*> get------------------------------------------------------------------------------ Char is serialised as UTF-8-instance Binary Char where- put a | c <= 0x7f = put (fromIntegral c :: Word8)- | c <= 0x7ff = do put (0xc0 .|. y)- put (0x80 .|. z)- | c <= 0xffff = do put (0xe0 .|. x)- put (0x80 .|. y)- put (0x80 .|. z)- | c <= 0x10ffff = do put (0xf0 .|. w)- put (0x80 .|. x)- put (0x80 .|. y)- put (0x80 .|. z)- | otherwise = error "Not a valid Unicode code point"- where- c = ord a- z, y, x, w :: Word8- z = fromIntegral (c .&. 0x3f)- y = fromIntegral (shiftR c 6 .&. 0x3f)- x = fromIntegral (shiftR c 12 .&. 0x3f)- w = fromIntegral (shiftR c 18 .&. 0x7)-- get = do- let getByte = fmap (fromIntegral :: Word8 -> Int) get- shiftL6 = flip shiftL 6 :: Int -> Int- w <- getByte- r <- case () of- _ | w < 0x80 -> return w- | w < 0xe0 -> do- x <- fmap (xor 0x80) getByte- return (x .|. shiftL6 (xor 0xc0 w))- | w < 0xf0 -> do- x <- fmap (xor 0x80) getByte- y <- fmap (xor 0x80) getByte- return (y .|. shiftL6 (x .|. shiftL6- (xor 0xe0 w)))- | otherwise -> do- x <- fmap (xor 0x80) getByte- y <- fmap (xor 0x80) getByte- z <- fmap (xor 0x80) getByte- return (z .|. shiftL6 (y .|. shiftL6- (x .|. shiftL6 (xor 0xf0 w))))- return $! chr r----------------------------------------------------------------------------- Instances for the first few tuples--instance (Binary a, Binary b) => Binary (a,b) where- put (a,b) = put a *> put b- get = (,) <$> get <*> get--instance (Binary a, Binary b, Binary c) => Binary (a,b,c) where- put (a,b,c) = put a *> put b *> put c- get = (,,) <$> get <*> get <*> get--instance (Binary a, Binary b, Binary c, Binary d) => Binary (a,b,c,d) where- put (a,b,c,d) = put a *> put b *> put c *> put d- get = (,,,) <$> get <*> get <*> get <*> get--instance (Binary a, Binary b, Binary c, Binary d, Binary e) => Binary (a,b,c,d,e) where- put (a,b,c,d,e) = put a *> put b *> put c *> put d *> put e- get = (,,,,) <$> get <*> get <*> get <*> get <*> get------- and now just recurse:-----instance (Binary a, Binary b, Binary c, Binary d, Binary e, Binary f)- => Binary (a,b,c,d,e,f) where- put (a,b,c,d,e,f) = put (a,(b,c,d,e,f))- get = do (a,(b,c,d,e,f)) <- get ; return (a,b,c,d,e,f)--instance (Binary a, Binary b, Binary c, Binary d, Binary e, Binary f, Binary g)- => Binary (a,b,c,d,e,f,g) where- put (a,b,c,d,e,f,g) = put (a,(b,c,d,e,f,g))- get = do (a,(b,c,d,e,f,g)) <- get ; return (a,b,c,d,e,f,g)--instance (Binary a, Binary b, Binary c, Binary d, Binary e,- Binary f, Binary g, Binary h)- => Binary (a,b,c,d,e,f,g,h) where- put (a,b,c,d,e,f,g,h) = put (a,(b,c,d,e,f,g,h))- get = do (a,(b,c,d,e,f,g,h)) <- get ; return (a,b,c,d,e,f,g,h)--instance (Binary a, Binary b, Binary c, Binary d, Binary e,- Binary f, Binary g, Binary h, Binary i)- => Binary (a,b,c,d,e,f,g,h,i) where- put (a,b,c,d,e,f,g,h,i) = put (a,(b,c,d,e,f,g,h,i))- get = do (a,(b,c,d,e,f,g,h,i)) <- get ; return (a,b,c,d,e,f,g,h,i)--instance (Binary a, Binary b, Binary c, Binary d, Binary e,- Binary f, Binary g, Binary h, Binary i, Binary j)- => Binary (a,b,c,d,e,f,g,h,i,j) where- put (a,b,c,d,e,f,g,h,i,j) = put (a,(b,c,d,e,f,g,h,i,j))- get = do (a,(b,c,d,e,f,g,h,i,j)) <- get ; return (a,b,c,d,e,f,g,h,i,j)----------------------------------------------------------------------------- Container types--instance Binary a => Binary [a] where- put l = put (length l) *> traverse_ put l- get = do n <- get :: Get Int- getMany n---- | 'getMany n' get 'n' elements in order, without blowing the stack.-getMany :: Binary a => Int -> Get [a]-getMany n = go [] n- where- go xs 0 = return $! reverse xs- go xs i = do x <- get- -- we must seq x to avoid stack overflows due to laziness in- -- (>>=)- x `seq` go (x:xs) (i-1)-{-# INLINE getMany #-}--instance (Binary a) => Binary (Maybe a) where- put Nothing = putWord8 0- put (Just x) = putWord8 1 *> put x- get = do- w <- getWord8- case w of- 0 -> return Nothing- _ -> fmap Just get--instance (Binary a, Binary b) => Binary (Either a b) where- put (Left a) = putWord8 0 *> put a- put (Right b) = putWord8 1 *> put b- get = do- w <- getWord8- case w of- 0 -> fmap Left get- _ -> fmap Right get----------------------------------------------------------------------------- ByteStrings (have specially efficient instances)--instance Binary B.ByteString where- put bs = do put (B.length bs)- putByteString bs- get = get >>= getByteString------- Using old versions of fps, this is a type synonym, and non portable------ Requires 'flexible instances'----instance Binary ByteString where- put bs = do put (fromIntegral (L.length bs) :: Int)- putLazyByteString bs- get = get >>= getLazyByteString----------------------------------------------------------------------------- Maps and Sets--instance (Binary a) => Binary (Set.Set a) where- put s = put (Set.size s) *> traverse_ put (Set.toAscList s)- get = fmap Set.fromDistinctAscList get--instance (Binary k, Binary e) => Binary (Map.Map k e) where- put m = put (Map.size m) *> traverse_ put (Map.toAscList m)- get = fmap Map.fromDistinctAscList get--instance Binary IntSet.IntSet where- put s = put (IntSet.size s) *> traverse_ put (IntSet.toAscList s)- get = fmap IntSet.fromDistinctAscList get--instance (Binary e) => Binary (IntMap.IntMap e) where- put m = put (IntMap.size m) *> traverse_ put (IntMap.toAscList m)- get = fmap IntMap.fromDistinctAscList get----------------------------------------------------------------------------- Queues and Sequences--instance (Binary e) => Binary (Seq.Seq e) where- put s = put (Seq.length s) *> Fold.traverse_ put s- get = do n <- get :: Get Int- rep Seq.empty n get- where rep xs 0 _ = return $! xs- rep xs n g = xs `seq` n `seq` do- x <- g- rep (xs Seq.|> x) (n-1) g----------------------------------------------------------------------------- Floating point--instance Binary Double where- put d = put (decodeFloat d)- get = encodeFloat <$> get <*> get--instance Binary Float where- put f = put (decodeFloat f)- get = encodeFloat <$> get <*> get----------------------------------------------------------------------------- Trees--instance (Binary e) => Binary (T.Tree e) where- put (T.Node r s) = put r *> put s- get = T.Node <$> get <*> get----------------------------------------------------------------------------- Arrays--instance (Binary i, Ix i, Binary e) => Binary (Array i e) where- put a = do- put (bounds a)- put (rangeSize $ bounds a) -- write the length- traverse_ put (elems a) -- now the elems.- get = do- bs <- get- n <- get -- read the length- xs <- getMany n -- now the elems.- return (listArray bs xs)------- The IArray UArray e constraint is non portable. Requires flexible instances----instance (Binary i, Ix i, Binary e, IArray UArray e) => Binary (UArray i e) where- put a = do- put (bounds a)- put (rangeSize $ bounds a) -- now write the length- traverse_ put (elems a)- get = do- bs <- get- n <- get- xs <- getMany n- return (listArray bs xs)
− src/Distribution/Compat/Binary/Generic.hs
@@ -1,128 +0,0 @@-{-# LANGUAGE BangPatterns, CPP, FlexibleInstances, KindSignatures,- ScopedTypeVariables, Trustworthy, TypeOperators, TypeSynonymInstances #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}---------------------------------------------------------------------------------- |--- Module : Distribution.Compat.Binary.Generic--- Copyright : Bryan O'Sullivan--- License : BSD3-style (see LICENSE)------ Maintainer : Bryan O'Sullivan <bos@serpentine.com>--- Stability : unstable--- Portability : Only works with GHC 7.2 and newer------ Instances for supporting GHC generics.----------------------------------------------------------------------------------module Distribution.Compat.Binary.Generic- (- ) where--import Control.Applicative-import Distribution.Compat.Binary.Class-import Data.Binary.Get-import Data.Binary.Put-import Data.Bits-import Data.Word-import GHC.Generics---- Type without constructors-instance GBinary V1 where- gput _ = return ()- gget = return undefined---- Constructor without arguments-instance GBinary U1 where- gput U1 = return ()- gget = return U1---- Product: constructor with parameters-instance (GBinary a, GBinary b) => GBinary (a :*: b) where- gput (x :*: y) = gput x >> gput y- gget = (:*:) <$> gget <*> gget---- Metadata (constructor name, etc)-instance GBinary a => GBinary (M1 i c a) where- gput = gput . unM1- gget = M1 <$> gget---- Constants, additional parameters, and rank-1 recursion-instance Binary a => GBinary (K1 i a) where- gput = put . unK1- gget = K1 <$> get---- Borrowed from the cereal package.---- The following GBinary instance for sums has support for serializing--- types with up to 2^64-1 constructors. It will use the minimal--- number of bytes needed to encode the constructor. For example when--- a type has 2^8 constructors or less it will use a single byte to--- encode the constructor. If it has 2^16 constructors or less it will--- use two bytes, and so on till 2^64-1.--#define GUARD(WORD) (size - 1) <= fromIntegral (maxBound :: WORD)-#define PUTSUM(WORD) GUARD(WORD) = putSum (0 :: WORD) (fromIntegral size)-#define GETSUM(WORD) GUARD(WORD) = (get :: Get WORD) >>= checkGetSum (fromIntegral size)--instance ( GSum a, GSum b- , GBinary a, GBinary b- , SumSize a, SumSize b) => GBinary (a :+: b) where- gput | PUTSUM(Word8) | PUTSUM(Word16) | PUTSUM(Word32) | PUTSUM(Word64)- | otherwise = sizeError "encode" size- where- size = unTagged (sumSize :: Tagged (a :+: b) Word64)-- gget | GETSUM(Word8) | GETSUM(Word16) | GETSUM(Word32) | GETSUM(Word64)- | otherwise = sizeError "decode" size- where- size = unTagged (sumSize :: Tagged (a :+: b) Word64)--sizeError :: Show size => String -> size -> error-sizeError s size =- error $ "Can't " ++ s ++ " a type with " ++ show size ++ " constructors"----------------------------------------------------------------------------checkGetSum :: (Ord word, Num word, Bits word, GSum f)- => word -> word -> Get (f a)-checkGetSum size code | code < size = getSum code size- | otherwise = fail "Unknown encoding for constructor"-{-# INLINE checkGetSum #-}--class GSum f where- getSum :: (Ord word, Num word, Bits word) => word -> word -> Get (f a)- putSum :: (Num w, Bits w, Binary w) => w -> w -> f a -> Put--instance (GSum a, GSum b, GBinary a, GBinary b) => GSum (a :+: b) where- getSum !code !size | code < sizeL = L1 <$> getSum code sizeL- | otherwise = R1 <$> getSum (code - sizeL) sizeR- where- sizeL = size `shiftR` 1- sizeR = size - sizeL-- putSum !code !size s = case s of- L1 x -> putSum code sizeL x- R1 x -> putSum (code + sizeL) sizeR x- where- sizeL = size `shiftR` 1- sizeR = size - sizeL--instance GBinary a => GSum (C1 c a) where- getSum _ _ = gget-- putSum !code _ x = put code *> gput x----------------------------------------------------------------------------class SumSize f where- sumSize :: Tagged f Word64--newtype Tagged (s :: * -> *) b = Tagged {unTagged :: b}--instance (SumSize a, SumSize b) => SumSize (a :+: b) where- sumSize = Tagged $ unTagged (sumSize :: Tagged a Word64) +- unTagged (sumSize :: Tagged b Word64)--instance SumSize (C1 c a) where- sumSize = Tagged 1
− src/Distribution/Compat/CharParsing.hs
@@ -1,357 +0,0 @@-{-# LANGUAGE GADTs #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# OPTIONS_GHC -fspec-constr -fspec-constr-count=8 #-}--------------------------------------------------------------------------------- |--- Module : Distribution.Compat.CharParsing--- Copyright : (c) Edward Kmett 2011--- License : BSD3------ Maintainer : ekmett@gmail.com--- Stability : experimental--- Portability : non-portable------ Parsers for character streams------ Originally in @parsers@ package.----------------------------------------------------------------------------------module Distribution.Compat.CharParsing- (- -- * Combinators- oneOf -- :: CharParsing m => [Char] -> m Char- , noneOf -- :: CharParsing m => [Char] -> m Char- , spaces -- :: CharParsing m => m ()- , space -- :: CharParsing m => m Char- , newline -- :: CharParsing m => m Char- , tab -- :: CharParsing m => m Char- , upper -- :: CharParsing m => m Char- , lower -- :: CharParsing m => m Char- , alphaNum -- :: CharParsing m => m Char- , letter -- :: CharParsing m => m Char- , digit -- :: CharParsing m => m Char- , hexDigit -- :: CharParsing m => m Char- , octDigit -- :: CharParsing m => m Char- , satisfyRange -- :: CharParsing m => Char -> Char -> m Char- -- * Class- , CharParsing(..)- -- * Cabal additions- , integral- , signedIntegral- , munch1- , munch- , skipSpaces1- , module Distribution.Compat.Parsing- ) where--import Prelude ()-import Distribution.Compat.Prelude--import Control.Monad.Trans.Class (lift)-import Control.Monad.Trans.State.Lazy as Lazy-import Control.Monad.Trans.State.Strict as Strict-import Control.Monad.Trans.Writer.Lazy as Lazy-import Control.Monad.Trans.Writer.Strict as Strict-import Control.Monad.Trans.RWS.Lazy as Lazy-import Control.Monad.Trans.RWS.Strict as Strict-import Control.Monad.Trans.Reader (ReaderT (..))-import Control.Monad.Trans.Identity (IdentityT (..))-import Data.Char-import Data.Text (Text, unpack)--import qualified Text.Parsec as Parsec--import Distribution.Compat.Parsing---- | @oneOf cs@ succeeds if the current character is in the supplied--- list of characters @cs@. Returns the parsed character. See also--- 'satisfy'.------ > vowel = oneOf "aeiou"-oneOf :: CharParsing m => [Char] -> m Char-oneOf xs = satisfy (\c -> c `elem` xs)-{-# INLINE oneOf #-}---- | As the dual of 'oneOf', @noneOf cs@ succeeds if the current--- character is /not/ in the supplied list of characters @cs@. Returns the--- parsed character.------ > consonant = noneOf "aeiou"-noneOf :: CharParsing m => [Char] -> m Char-noneOf xs = satisfy (\c -> c `notElem` xs)-{-# INLINE noneOf #-}---- | Skips /zero/ or more white space characters. See also 'skipMany'.-spaces :: CharParsing m => m ()-spaces = skipMany space <?> "white space"-{-# INLINE spaces #-}---- | Parses a white space character (any character which satisfies 'isSpace')--- Returns the parsed character.-space :: CharParsing m => m Char-space = satisfy isSpace <?> "space"-{-# INLINE space #-}---- | Parses a newline character (\'\\n\'). Returns a newline character.-newline :: CharParsing m => m Char-newline = char '\n' <?> "new-line"-{-# INLINE newline #-}---- | Parses a tab character (\'\\t\'). Returns a tab character.-tab :: CharParsing m => m Char-tab = char '\t' <?> "tab"-{-# INLINE tab #-}---- | Parses an upper case letter. Returns the parsed character.-upper :: CharParsing m => m Char-upper = satisfy isUpper <?> "uppercase letter"-{-# INLINE upper #-}---- | Parses a lower case character. Returns the parsed character.-lower :: CharParsing m => m Char-lower = satisfy isLower <?> "lowercase letter"-{-# INLINE lower #-}---- | Parses a letter or digit. Returns the parsed character.-alphaNum :: CharParsing m => m Char-alphaNum = satisfy isAlphaNum <?> "letter or digit"-{-# INLINE alphaNum #-}---- | Parses a letter (an upper case or lower case character). Returns the--- parsed character.-letter :: CharParsing m => m Char-letter = satisfy isAlpha <?> "letter"-{-# INLINE letter #-}---- | Parses a digit. Returns the parsed character.-digit :: CharParsing m => m Char-digit = satisfy isDigit <?> "digit"-{-# INLINE digit #-}---- | Parses a hexadecimal digit (a digit or a letter between \'a\' and--- \'f\' or \'A\' and \'F\'). Returns the parsed character.-hexDigit :: CharParsing m => m Char-hexDigit = satisfy isHexDigit <?> "hexadecimal digit"-{-# INLINE hexDigit #-}---- | Parses an octal digit (a character between \'0\' and \'7\'). Returns--- the parsed character.-octDigit :: CharParsing m => m Char-octDigit = satisfy isOctDigit <?> "octal digit"-{-# INLINE octDigit #-}--satisfyRange :: CharParsing m => Char -> Char -> m Char-satisfyRange a z = satisfy (\c -> c >= a && c <= z)-{-# INLINE satisfyRange #-}---- | Additional functionality needed to parse character streams.-class Parsing m => CharParsing m where- -- | Parse a single character of the input, with UTF-8 decoding- satisfy :: (Char -> Bool) -> m Char-- -- | @char c@ parses a single character @c@. Returns the parsed- -- character (i.e. @c@).- --- -- /e.g./- --- -- @semiColon = 'char' ';'@- char :: Char -> m Char- char c = satisfy (c ==) <?> show [c]- {-# INLINE char #-}-- -- | @notChar c@ parses any single character other than @c@. Returns the parsed- -- character.- notChar :: Char -> m Char- notChar c = satisfy (c /=)- {-# INLINE notChar #-}-- -- | This parser succeeds for any character. Returns the parsed character.- anyChar :: m Char- anyChar = satisfy (const True)- {-# INLINE anyChar #-}-- -- | @string s@ parses a sequence of characters given by @s@. Returns- -- the parsed string (i.e. @s@).- --- -- > divOrMod = string "div"- -- > <|> string "mod"- string :: String -> m String- string s = s <$ try (traverse_ char s) <?> show s- {-# INLINE string #-}-- -- | @text t@ parses a sequence of characters determined by the text @t@ Returns- -- the parsed text fragment (i.e. @t@).- --- -- Using @OverloadedStrings@:- --- -- > divOrMod = text "div"- -- > <|> text "mod"- text :: Text -> m Text- text t = t <$ string (unpack t)- {-# INLINE text #-}--instance (CharParsing m, MonadPlus m) => CharParsing (Lazy.StateT s m) where- satisfy = lift . satisfy- {-# INLINE satisfy #-}- char = lift . char- {-# INLINE char #-}- notChar = lift . notChar- {-# INLINE notChar #-}- anyChar = lift anyChar- {-# INLINE anyChar #-}- string = lift . string- {-# INLINE string #-}- text = lift . text- {-# INLINE text #-}--instance (CharParsing m, MonadPlus m) => CharParsing (Strict.StateT s m) where- satisfy = lift . satisfy- {-# INLINE satisfy #-}- char = lift . char- {-# INLINE char #-}- notChar = lift . notChar- {-# INLINE notChar #-}- anyChar = lift anyChar- {-# INLINE anyChar #-}- string = lift . string- {-# INLINE string #-}- text = lift . text- {-# INLINE text #-}--instance (CharParsing m, MonadPlus m) => CharParsing (ReaderT e m) where- satisfy = lift . satisfy- {-# INLINE satisfy #-}- char = lift . char- {-# INLINE char #-}- notChar = lift . notChar- {-# INLINE notChar #-}- anyChar = lift anyChar- {-# INLINE anyChar #-}- string = lift . string- {-# INLINE string #-}- text = lift . text- {-# INLINE text #-}--instance (CharParsing m, MonadPlus m, Monoid w) => CharParsing (Strict.WriterT w m) where- satisfy = lift . satisfy- {-# INLINE satisfy #-}- char = lift . char- {-# INLINE char #-}- notChar = lift . notChar- {-# INLINE notChar #-}- anyChar = lift anyChar- {-# INLINE anyChar #-}- string = lift . string- {-# INLINE string #-}- text = lift . text- {-# INLINE text #-}--instance (CharParsing m, MonadPlus m, Monoid w) => CharParsing (Lazy.WriterT w m) where- satisfy = lift . satisfy- {-# INLINE satisfy #-}- char = lift . char- {-# INLINE char #-}- notChar = lift . notChar- {-# INLINE notChar #-}- anyChar = lift anyChar- {-# INLINE anyChar #-}- string = lift . string- {-# INLINE string #-}- text = lift . text- {-# INLINE text #-}--instance (CharParsing m, MonadPlus m, Monoid w) => CharParsing (Lazy.RWST r w s m) where- satisfy = lift . satisfy- {-# INLINE satisfy #-}- char = lift . char- {-# INLINE char #-}- notChar = lift . notChar- {-# INLINE notChar #-}- anyChar = lift anyChar- {-# INLINE anyChar #-}- string = lift . string- {-# INLINE string #-}- text = lift . text- {-# INLINE text #-}--instance (CharParsing m, MonadPlus m, Monoid w) => CharParsing (Strict.RWST r w s m) where- satisfy = lift . satisfy- {-# INLINE satisfy #-}- char = lift . char- {-# INLINE char #-}- notChar = lift . notChar- {-# INLINE notChar #-}- anyChar = lift anyChar- {-# INLINE anyChar #-}- string = lift . string- {-# INLINE string #-}- text = lift . text- {-# INLINE text #-}--instance (CharParsing m, MonadPlus m) => CharParsing (IdentityT m) where- satisfy = lift . satisfy- {-# INLINE satisfy #-}- char = lift . char- {-# INLINE char #-}- notChar = lift . notChar- {-# INLINE notChar #-}- anyChar = lift anyChar- {-# INLINE anyChar #-}- string = lift . string- {-# INLINE string #-}- text = lift . text- {-# INLINE text #-}--instance Parsec.Stream s m Char => CharParsing (Parsec.ParsecT s u m) where- satisfy = Parsec.satisfy- char = Parsec.char- notChar c = Parsec.satisfy (/= c)- anyChar = Parsec.anyChar- string = Parsec.string------------------------------------------------------------------------------------ Our additions----------------------------------------------------------------------------------integral :: (CharParsing m, Integral a) => m a-integral = toNumber <$> some d <?> "integral"- where- toNumber = foldl' (\a b -> a * 10 + b) 0- d = f <$> satisfyRange '0' '9'- f '0' = 0- f '1' = 1- f '2' = 2- f '3' = 3- f '4' = 4- f '5' = 5- f '6' = 6- f '7' = 7- f '8' = 8- f '9' = 9- f _ = error "panic! integral"-{-# INLINE integral #-}---- | Accepts negative (starting with @-@) and positive (without sign) integral--- numbers.--- --- @since 3.4.0.0-signedIntegral :: (CharParsing m, Integral a) => m a-signedIntegral = negate <$ char '-' <*> integral <|> integral-{-# INLINE signedIntegral #-}---- | Greedily munch characters while predicate holds.--- Require at least one character.-munch1 :: CharParsing m => (Char -> Bool) -> m String-munch1 = some . satisfy-{-# INLINE munch1 #-}---- | Greedely munch characters while predicate holds.--- Always succeeds.-munch :: CharParsing m => (Char -> Bool) -> m String-munch = many . satisfy-{-# INLINE munch #-}--skipSpaces1 :: CharParsing m => m ()-skipSpaces1 = skipSome space-{-# INLINE skipSpaces1 #-}
src/Distribution/Compat/CopyFile.hs view
@@ -1,18 +1,19 @@ {-# LANGUAGE CPP #-} {-# OPTIONS_HADDOCK hide #-}-module Distribution.Compat.CopyFile (- copyFile,- copyFileChanged,- filesEqual,- copyOrdinaryFile,- copyExecutableFile,- setFileOrdinary,- setFileExecutable,- setDirOrdinary,++module Distribution.Compat.CopyFile+ ( copyFile+ , copyFileChanged+ , filesEqual+ , copyOrdinaryFile+ , copyExecutableFile+ , setFileOrdinary+ , setFileExecutable+ , setDirOrdinary ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () #ifndef mingw32_HOST_OS import Distribution.Compat.Internal.TempFile@@ -20,6 +21,8 @@ import Control.Exception ( bracketOnError ) import qualified Data.ByteString.Lazy as BSL+import Data.Bits+ ( (.|.) ) import System.IO.Error ( ioeSetLocation ) import System.Directory@@ -34,10 +37,8 @@ import System.Posix.Types ( FileMode )-import System.Posix.Internals- ( c_chmod, withFilePath )-import Foreign.C- ( throwErrnoPathIfMinus1_ )+import System.Posix.Files+ ( getFileStatus, fileMode, setFileMode ) #else /* else mingw32_HOST_OS */ @@ -66,18 +67,20 @@ #endif /* mingw32_HOST_OS */ copyOrdinaryFile, copyExecutableFile :: FilePath -> FilePath -> IO ()-copyOrdinaryFile src dest = copyFile src dest >> setFileOrdinary dest+copyOrdinaryFile src dest = copyFile src dest >> setFileOrdinary dest copyExecutableFile src dest = copyFile src dest >> setFileExecutable dest -setFileOrdinary, setFileExecutable, setDirOrdinary :: FilePath -> IO ()+setFileOrdinary, setFileExecutable, setDirOrdinary :: FilePath -> IO () #ifndef mingw32_HOST_OS-setFileOrdinary path = setFileMode path 0o644 -- file perms -rw-r--r---setFileExecutable path = setFileMode path 0o755 -- file perms -rwxr-xr-x+-- When running with a restrictive UMASK such as 0077 we still want to+-- install files and directories that are accessible to other users.+setFileOrdinary path = addFileMode path 0o644 -- file perms -rw-r--r--+setFileExecutable path = addFileMode path 0o755 -- file perms -rwxr-xr-x -setFileMode :: FilePath -> FileMode -> IO ()-setFileMode name m =- withFilePath name $ \s -> do- throwErrnoPathIfMinus1_ "setFileMode" name (c_chmod s m)+addFileMode :: FilePath -> FileMode -> IO ()+addFileMode name m = do+ o <- fileMode <$> getFileStatus name+ setFileMode name (m .|. o) #else setFileOrdinary _ = return () setFileExecutable _ = return ()@@ -91,7 +94,7 @@ copyFile fromFPath toFPath = copy `catchIO` (\ioe -> throwIO (ioeSetLocation ioe "copyFile"))- where+ where #ifndef mingw32_HOST_OS copy = withBinaryFile fromFPath ReadMode $ \hFrom -> bracketOnError openTmp cleanTmp $ \(tmpFPath, hTmp) ->@@ -237,8 +240,9 @@ filesEqual f1 f2 = do ex1 <- doesFileExist f1 ex2 <- doesFileExist f2- if not (ex1 && ex2) then return False else- withBinaryFile f1 ReadMode $ \h1 ->+ if not (ex1 && ex2)+ then return False+ else withBinaryFile f1 ReadMode $ \h1 -> withBinaryFile f2 ReadMode $ \h2 -> do s1 <- hFileSize h1 s2 <- hFileSize h2
src/Distribution/Compat/CreatePipe.hs view
@@ -1,81 +1,5 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}--module Distribution.Compat.CreatePipe (createPipe) where+module Distribution.Compat.CreatePipe+ {-# DEPRECATED "Use System.Process from package process directly" #-}+ (createPipe) where -#if MIN_VERSION_process(1,2,1) import System.Process (createPipe)-#else-import System.IO (Handle, hSetBinaryMode)--import Prelude ()-import Distribution.Compat.Prelude-import Distribution.Compat.Stack---- The mingw32_HOST_OS CPP macro is GHC-specific-#ifdef mingw32_HOST_OS-import qualified Prelude-import Control.Exception (onException)-import Foreign.C.Error (throwErrnoIfMinus1_)-import Foreign.C.Types (CInt(..), CUInt(..))-import Foreign.Ptr (Ptr)-import Foreign.Marshal.Array (allocaArray)-import Foreign.Storable (peek, peekElemOff)-import GHC.IO.FD (mkFD)-import GHC.IO.Device (IODeviceType(Stream))-import GHC.IO.Handle.FD (mkHandleFromFD)-import System.IO (IOMode(ReadMode, WriteMode))-#elif defined ghcjs_HOST_OS-#else-import System.Posix.IO (fdToHandle)-import qualified System.Posix.IO as Posix-#endif--createPipe :: IO (Handle, Handle)--- The mingw32_HOST_OS CPP macro is GHC-specific-#ifdef mingw32_HOST_OS-createPipe = do- (readfd, writefd) <- allocaArray 2 $ \ pfds -> do- throwErrnoIfMinus1_ "_pipe" $ c__pipe pfds 2 ({- _O_BINARY -} 32768)- readfd <- peek pfds- writefd <- peekElemOff pfds 1- return (readfd, writefd)- (do readh <- fdToHandle readfd ReadMode- writeh <- fdToHandle writefd WriteMode- hSetBinaryMode readh True- hSetBinaryMode writeh True- return (readh, writeh)) `onException` (close readfd >> close writefd)- where- fdToHandle :: CInt -> IOMode -> IO Handle- fdToHandle fd mode = do- (fd', deviceType) <- mkFD fd mode (Just (Stream, 0, 0)) False False- mkHandleFromFD fd' deviceType "" mode False Nothing-- close :: CInt -> IO ()- close = throwErrnoIfMinus1_ "_close" . c__close- where _ = callStack -- TODO: attach call stack to exception-- _ = callStack -- TODO: attach call stack to exceptions--foreign import ccall "io.h _pipe" c__pipe ::- Ptr CInt -> CUInt -> CInt -> Prelude.IO CInt--foreign import ccall "io.h _close" c__close ::- CInt -> Prelude.IO CInt-#elif defined ghcjs_HOST_OS-createPipe = error "createPipe"- where- _ = callStack-#else-createPipe = do- (readfd, writefd) <- Posix.createPipe- readh <- fdToHandle readfd- writeh <- fdToHandle writefd- hSetBinaryMode readh True- hSetBinaryMode writeh True- return (readh, writeh)- where- _ = callStack-#endif-#endif
− src/Distribution/Compat/DList.hs
@@ -1,53 +0,0 @@--------------------------------------------------------------------------------- |--- Module : Distribution.Compat.DList--- Copyright : (c) Ben Gamari 2015-2019--- License : BSD3------ Maintainer : cabal-dev@haskell.org--- Stability : experimental--- Portability : portable------ A very simple difference list.-module Distribution.Compat.DList (- DList,- runDList,- empty,- singleton,- fromList,- toList,- snoc,-) where--import Prelude ()-import Distribution.Compat.Prelude hiding (toList, empty)---- | Difference list.-newtype DList a = DList ([a] -> [a])--runDList :: DList a -> [a]-runDList (DList run) = run []---- | Make 'DList' containing single element.-singleton :: a -> DList a-singleton a = DList (a:)---- | @since 3.4.0.0-empty :: DList a-empty = DList id--fromList :: [a] -> DList a-fromList as = DList (as ++)--toList :: DList a -> [a]-toList = runDList--snoc :: DList a -> a -> DList a-snoc xs x = xs <> singleton x--instance Monoid (DList a) where- mempty = empty- mappend = (<>)--instance Semigroup (DList a) where- DList a <> DList b = DList (a . b)
src/Distribution/Compat/Directory.hs view
@@ -1,10 +1,10 @@ {-# LANGUAGE CPP #-} module Distribution.Compat.Directory-( listDirectory-, makeAbsolute-, doesPathExist-) where+ ( listDirectory+ , makeAbsolute+ , doesPathExist+ ) where #if MIN_VERSION_directory(1,2,7) import System.Directory as Dir hiding (doesPathExist)@@ -46,4 +46,3 @@ else doesFileExist path #endif-
src/Distribution/Compat/Environment.hs view
@@ -3,33 +3,20 @@ {-# LANGUAGE RankNTypes #-} {-# OPTIONS_HADDOCK hide #-} -module Distribution.Compat.Environment- ( getEnvironment, lookupEnv, setEnv, unsetEnv )- where+module Distribution.Compat.Environment (getEnvironment, lookupEnv, setEnv, unsetEnv)+where +import Distribution.Compat.Prelude import Prelude () import qualified Prelude-import Distribution.Compat.Prelude -#ifndef mingw32_HOST_OS-#if __GLASGOW_HASKELL__ < 708-import Foreign.C.Error (throwErrnoIf_)-#endif-#endif-+import System.Environment (lookupEnv, unsetEnv) import qualified System.Environment as System-import System.Environment (lookupEnv)-#if __GLASGOW_HASKELL__ >= 708-import System.Environment (unsetEnv)-#endif import Distribution.Compat.Stack #ifdef mingw32_HOST_OS import Foreign.C-#if __GLASGOW_HASKELL__ < 708-import Foreign.Ptr (nullPtr)-#endif import GHC.Windows #else import Foreign.C.Types@@ -73,9 +60,10 @@ where _ = callStack -- TODO: attach CallStack to exception +{- FOURMOLU_DISABLE -} # if defined(i386_HOST_ARCH) # define WINDOWS_CCONV stdcall-# elif defined(x86_64_HOST_ARCH)+# elif defined(x86_64_HOST_ARCH) || defined(aarch64_HOST_ARCH) # define WINDOWS_CCONV ccall # else # error Unknown mingw32 arch@@ -95,40 +83,4 @@ foreign import ccall unsafe "setenv" c_setenv :: CString -> CString -> CInt -> Prelude.IO CInt #endif /* mingw32_HOST_OS */--#if __GLASGOW_HASKELL__ < 708---- | @unsetEnv name@ removes the specified environment variable from the--- environment of the current process.------ Throws `Control.Exception.IOException` if @name@ is the empty string or--- contains an equals sign.------ @since 4.7.0.0-unsetEnv :: String -> IO ()-#ifdef mingw32_HOST_OS-unsetEnv key = withCWString key $ \k -> do- success <- c_SetEnvironmentVariable k nullPtr- unless success $ do- -- We consider unsetting an environment variable that does not exist not as- -- an error, hence we ignore eRROR_ENVVAR_NOT_FOUND.- err <- c_GetLastError- unless (err == eRROR_ENVVAR_NOT_FOUND) $ do- throwGetLastError "unsetEnv"--eRROR_ENVVAR_NOT_FOUND :: DWORD-eRROR_ENVVAR_NOT_FOUND = 203--foreign import WINDOWS_CCONV unsafe "windows.h GetLastError"- c_GetLastError:: IO DWORD-#else-unsetEnv key = withFilePath key (throwErrnoIf_ (/= 0) "unsetEnv" . c_unsetenv)-#if __GLASGOW_HASKELL__ > 706-foreign import ccall unsafe "__hsbase_unsetenv" c_unsetenv :: CString -> Prelude.IO CInt-#else--- HACK: We hope very hard that !UNSETENV_RETURNS_VOID-foreign import ccall unsafe "unsetenv" c_unsetenv :: CString -> Prelude.IO CInt-#endif-#endif--#endif+{- FOURMOLU_ENABLE -}
− src/Distribution/Compat/Exception.hs
@@ -1,37 +0,0 @@-{-# LANGUAGE CPP #-}-module Distribution.Compat.Exception (- catchIO,- catchExit,- tryIO,- displayException,- ) where--#ifdef MIN_VERSION_base-#define MINVER_base_48 MIN_VERSION_base(4,8,0)-#else-#define MINVER_base_48 (__GLASGOW_HASKELL__ >= 710)-#endif--import System.Exit-import qualified Control.Exception as Exception--#if MINVER_base_48-import Control.Exception (displayException)-#endif---- | Try 'IOException'.-tryIO :: IO a -> IO (Either Exception.IOException a)-tryIO = Exception.try---- | Catch 'IOException'.-catchIO :: IO a -> (Exception.IOException -> IO a) -> IO a-catchIO = Exception.catch---- | Catch 'ExitCode'-catchExit :: IO a -> (ExitCode -> IO a) -> IO a-catchExit = Exception.catch--#if !MINVER_base_48-displayException :: Exception.Exception e => e -> String-displayException = show-#endif
src/Distribution/Compat/FilePath.hs view
@@ -1,13 +1,12 @@ {-# LANGUAGE CPP #-}--{-# OPTIONS_GHC -fno-warn-unused-imports #-}+{-# OPTIONS_GHC -Wno-unused-imports #-} module Distribution.Compat.FilePath-( isExtensionOf-, stripExtension-) where+ ( isExtensionOf+ , stripExtension+ ) where -import Data.List ( isSuffixOf, stripPrefix )+import Data.List (isSuffixOf, stripPrefix) import System.FilePath #if !MIN_VERSION_filepath(1,4,2)
src/Distribution/Compat/GetShortPathName.hs view
@@ -1,6 +1,7 @@ {-# LANGUAGE CPP #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Compat.GetShortPathName --@@ -8,12 +9,11 @@ -- Portability : Windows-only -- -- Win32 API 'GetShortPathName' function.--module Distribution.Compat.GetShortPathName ( getShortPathName )- where+module Distribution.Compat.GetShortPathName (getShortPathName)+where -import Prelude () import Distribution.Compat.Prelude+import Prelude () #ifdef mingw32_HOST_OS @@ -22,7 +22,8 @@ import System.Win32 (LPCTSTR, LPTSTR, DWORD) import Foreign.Marshal.Array (allocaArray) -#ifdef x86_64_HOST_ARCH+{- FOURMOLU_DISABLE -}+#if defined(x86_64_HOST_ARCH) || defined(aarch64_HOST_ARCH) #define WINAPI ccall #else #define WINAPI stdcall@@ -57,3 +58,4 @@ getShortPathName path = return path #endif+{- FOURMOLU_ENABLE -}
− src/Distribution/Compat/Graph.hs
@@ -1,408 +0,0 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE UndecidableInstances #-}--------------------------------------------------------------------------------- |--- Module : Distribution.Compat.Graph--- Copyright : (c) Edward Z. Yang 2016--- License : BSD3------ Maintainer : cabal-dev@haskell.org--- Stability : experimental--- Portability : portable------ A data type representing directed graphs, backed by "Data.Graph".--- It is strict in the node type.------ This is an alternative interface to "Data.Graph". In this interface,--- nodes (identified by the 'IsNode' type class) are associated with a--- key and record the keys of their neighbors. This interface is more--- convenient than 'Data.Graph.Graph', which requires vertices to be--- explicitly handled by integer indexes.------ The current implementation has somewhat peculiar performance--- characteristics. The asymptotics of all map-like operations mirror--- their counterparts in "Data.Map". However, to perform a graph--- operation, we first must build the "Data.Graph" representation, an--- operation that takes /O(V + E log V)/. However, this operation can--- be amortized across all queries on that particular graph.------ Some nodes may be broken, i.e., refer to neighbors which are not--- stored in the graph. In our graph algorithms, we transparently--- ignore such edges; however, you can easily query for the broken--- vertices of a graph using 'broken' (and should, e.g., to ensure that--- a closure of a graph is well-formed.) It's possible to take a closed--- subset of a broken graph and get a well-formed graph.-----------------------------------------------------------------------------------module Distribution.Compat.Graph (- -- * Graph type- Graph,- IsNode(..),- -- * Query- null,- size,- member,- lookup,- -- * Construction- empty,- insert,- deleteKey,- deleteLookup,- -- * Combine- unionLeft,- unionRight,- -- * Graph algorithms- stronglyConnComp,- SCC(..),- cycles,- broken,- neighbors,- revNeighbors,- closure,- revClosure,- topSort,- revTopSort,- -- * Conversions- -- ** Maps- toMap,- -- ** Lists- fromDistinctList,- toList,- keys,- -- ** Sets- keysSet,- -- ** Graphs- toGraph,- -- * Node type- Node(..),- nodeValue,-) where--import Distribution.Compat.Prelude hiding (empty, lookup, null, toList)-import Prelude ()--import Data.Array ((!))-import Data.Graph (SCC (..))-import Distribution.Utils.Structured (Structure (..), Structured (..))--import qualified Data.Array as Array-import qualified Data.Foldable as Foldable-import qualified Data.Graph as G-import qualified Data.Map.Strict as Map-import qualified Data.Set as Set-import qualified Data.Tree as Tree-import qualified Distribution.Compat.Prelude as Prelude---- | A graph of nodes @a@. The nodes are expected to have instance--- of class 'IsNode'.-data Graph a- = Graph {- graphMap :: !(Map (Key a) a),- -- Lazily cached graph representation- graphForward :: G.Graph,- graphAdjoint :: G.Graph,- graphVertexToNode :: G.Vertex -> a,- graphKeyToVertex :: Key a -> Maybe G.Vertex,- graphBroken :: [(a, [Key a])]- }- deriving (Typeable)---- NB: Not a Functor! (or Traversable), because you need--- to restrict Key a ~ Key b. We provide our own mapping--- functions.---- General strategy is most operations are deferred to the--- Map representation.--instance Show a => Show (Graph a) where- show = show . toList--instance (IsNode a, Read a, Show (Key a)) => Read (Graph a) where- readsPrec d s = map (\(a,r) -> (fromDistinctList a, r)) (readsPrec d s)--instance (IsNode a, Binary a, Show (Key a)) => Binary (Graph a) where- put x = put (toList x)- get = fmap fromDistinctList get--instance Structured a => Structured (Graph a) where- structure p = Nominal (typeRep p) 0 "Graph" [structure (Proxy :: Proxy a)]--instance (Eq (Key a), Eq a) => Eq (Graph a) where- g1 == g2 = graphMap g1 == graphMap g2--instance Foldable.Foldable Graph where- fold = Foldable.fold . graphMap- foldr f z = Foldable.foldr f z . graphMap- foldl f z = Foldable.foldl f z . graphMap- foldMap f = Foldable.foldMap f . graphMap- foldl' f z = Foldable.foldl' f z . graphMap- foldr' f z = Foldable.foldr' f z . graphMap-#ifdef MIN_VERSION_base-#if MIN_VERSION_base(4,8,0)- length = Foldable.length . graphMap- null = Foldable.null . graphMap- toList = Foldable.toList . graphMap- elem x = Foldable.elem x . graphMap- maximum = Foldable.maximum . graphMap- minimum = Foldable.minimum . graphMap- sum = Foldable.sum . graphMap- product = Foldable.product . graphMap-#endif-#endif--instance (NFData a, NFData (Key a)) => NFData (Graph a) where- rnf Graph {- graphMap = m,- graphForward = gf,- graphAdjoint = ga,- graphVertexToNode = vtn,- graphKeyToVertex = ktv,- graphBroken = b- } = gf `seq` ga `seq` vtn `seq` ktv `seq` b `seq` rnf m---- TODO: Data instance?---- | The 'IsNode' class is used for datatypes which represent directed--- graph nodes. A node of type @a@ is associated with some unique key of--- type @'Key' a@; given a node we can determine its key ('nodeKey')--- and the keys of its neighbors ('nodeNeighbors').-class Ord (Key a) => IsNode a where- type Key a- nodeKey :: a -> Key a- nodeNeighbors :: a -> [Key a]--instance (IsNode a, IsNode b, Key a ~ Key b) => IsNode (Either a b) where- type Key (Either a b) = Key a- nodeKey (Left x) = nodeKey x- nodeKey (Right x) = nodeKey x- nodeNeighbors (Left x) = nodeNeighbors x- nodeNeighbors (Right x) = nodeNeighbors x---- | A simple, trivial data type which admits an 'IsNode' instance.-data Node k a = N a k [k]- deriving (Show, Eq)---- | Get the value from a 'Node'.-nodeValue :: Node k a -> a-nodeValue (N a _ _) = a--instance Functor (Node k) where- fmap f (N a k ks) = N (f a) k ks--instance Ord k => IsNode (Node k a) where- type Key (Node k a) = k- nodeKey (N _ k _) = k- nodeNeighbors (N _ _ ks) = ks---- TODO: Maybe introduce a typeclass for items which just--- keys (so, Key associated type, and nodeKey method). But--- I didn't need it here, so I didn't introduce it.---- Query---- | /O(1)/. Is the graph empty?-null :: Graph a -> Bool-null = Map.null . toMap---- | /O(1)/. The number of nodes in the graph.-size :: Graph a -> Int-size = Map.size . toMap---- | /O(log V)/. Check if the key is in the graph.-member :: IsNode a => Key a -> Graph a -> Bool-member k g = Map.member k (toMap g)---- | /O(log V)/. Lookup the node at a key in the graph.-lookup :: IsNode a => Key a -> Graph a -> Maybe a-lookup k g = Map.lookup k (toMap g)---- Construction---- | /O(1)/. The empty graph.-empty :: IsNode a => Graph a-empty = fromMap Map.empty---- | /O(log V)/. Insert a node into a graph.-insert :: IsNode a => a -> Graph a -> Graph a-insert !n g = fromMap (Map.insert (nodeKey n) n (toMap g))---- | /O(log V)/. Delete the node at a key from the graph.-deleteKey :: IsNode a => Key a -> Graph a -> Graph a-deleteKey k g = fromMap (Map.delete k (toMap g))---- | /O(log V)/. Lookup and delete. This function returns the deleted--- value if it existed.-deleteLookup :: IsNode a => Key a -> Graph a -> (Maybe a, Graph a)-deleteLookup k g =- let (r, m') = Map.updateLookupWithKey (\_ _ -> Nothing) k (toMap g)- in (r, fromMap m')---- Combining---- | /O(V + V')/. Right-biased union, preferring entries--- from the second map when conflicts occur.--- @'nodeKey' x = 'nodeKey' (f x)@.-unionRight :: IsNode a => Graph a -> Graph a -> Graph a-unionRight g g' = fromMap (Map.union (toMap g') (toMap g))---- | /O(V + V')/. Left-biased union, preferring entries from--- the first map when conflicts occur.-unionLeft :: IsNode a => Graph a -> Graph a -> Graph a-unionLeft = flip unionRight---- Graph-like operations---- | /Ω(V + E)/. Compute the strongly connected components of a graph.--- Requires amortized construction of graph.-stronglyConnComp :: Graph a -> [SCC a]-stronglyConnComp g = map decode forest- where- forest = G.scc (graphForward g)- decode (Tree.Node v [])- | mentions_itself v = CyclicSCC [graphVertexToNode g v]- | otherwise = AcyclicSCC (graphVertexToNode g v)- decode other = CyclicSCC (dec other [])- where dec (Tree.Node v ts) vs- = graphVertexToNode g v : foldr dec vs ts- mentions_itself v = v `elem` (graphForward g ! v)--- Implementation copied from 'stronglyConnCompR' in 'Data.Graph'.---- | /Ω(V + E)/. Compute the cycles of a graph.--- Requires amortized construction of graph.-cycles :: Graph a -> [[a]]-cycles g = [ vs | CyclicSCC vs <- stronglyConnComp g ]---- | /O(1)/. Return a list of nodes paired with their broken--- neighbors (i.e., neighbor keys which are not in the graph).--- Requires amortized construction of graph.-broken :: Graph a -> [(a, [Key a])]-broken g = graphBroken g---- | Lookup the immediate neighbors from a key in the graph.--- Requires amortized construction of graph.-neighbors :: Graph a -> Key a -> Maybe [a]-neighbors g k = do- v <- graphKeyToVertex g k- return (map (graphVertexToNode g) (graphForward g ! v))---- | Lookup the immediate reverse neighbors from a key in the graph.--- Requires amortized construction of graph.-revNeighbors :: Graph a -> Key a -> Maybe [a]-revNeighbors g k = do- v <- graphKeyToVertex g k- return (map (graphVertexToNode g) (graphAdjoint g ! v))---- | Compute the subgraph which is the closure of some set of keys.--- Returns @Nothing@ if one (or more) keys are not present in--- the graph.--- Requires amortized construction of graph.-closure :: Graph a -> [Key a] -> Maybe [a]-closure g ks = do- vs <- traverse (graphKeyToVertex g) ks- return (decodeVertexForest g (G.dfs (graphForward g) vs))---- | Compute the reverse closure of a graph from some set--- of keys. Returns @Nothing@ if one (or more) keys are not present in--- the graph.--- Requires amortized construction of graph.-revClosure :: Graph a -> [Key a] -> Maybe [a]-revClosure g ks = do- vs <- traverse (graphKeyToVertex g) ks- return (decodeVertexForest g (G.dfs (graphAdjoint g) vs))--flattenForest :: Tree.Forest a -> [a]-flattenForest = concatMap Tree.flatten--decodeVertexForest :: Graph a -> Tree.Forest G.Vertex -> [a]-decodeVertexForest g = map (graphVertexToNode g) . flattenForest---- | Topologically sort the nodes of a graph.--- Requires amortized construction of graph.-topSort :: Graph a -> [a]-topSort g = map (graphVertexToNode g) $ G.topSort (graphForward g)---- | Reverse topologically sort the nodes of a graph.--- Requires amortized construction of graph.-revTopSort :: Graph a -> [a]-revTopSort g = map (graphVertexToNode g) $ G.topSort (graphAdjoint g)---- Conversions---- | /O(1)/. Convert a map from keys to nodes into a graph.--- The map must satisfy the invariant that--- @'fromMap' m == 'fromList' ('Data.Map.elems' m)@;--- if you can't fulfill this invariant use @'fromList' ('Data.Map.elems' m)@--- instead. The values of the map are assumed to already--- be in WHNF.-fromMap :: IsNode a => Map (Key a) a -> Graph a-fromMap m- = Graph { graphMap = m- -- These are lazily computed!- , graphForward = g- , graphAdjoint = G.transposeG g- , graphVertexToNode = vertex_to_node- , graphKeyToVertex = key_to_vertex- , graphBroken = broke- }- where- try_key_to_vertex k = maybe (Left k) Right (key_to_vertex k)-- (brokenEdges, edges)- = unzip- $ [ partitionEithers (map try_key_to_vertex (nodeNeighbors n))- | n <- ns ]- broke = filter (not . Prelude.null . snd) (zip ns brokenEdges)-- g = Array.listArray bounds edges-- ns = Map.elems m -- sorted ascending- vertices = zip (map nodeKey ns) [0..]- vertex_map = Map.fromAscList vertices- key_to_vertex k = Map.lookup k vertex_map-- vertex_to_node vertex = nodeTable ! vertex-- nodeTable = Array.listArray bounds ns- bounds = (0, Map.size m - 1)---- | /O(V log V)/. Convert a list of nodes (with distinct keys) into a graph.-fromDistinctList :: (IsNode a, Show (Key a)) => [a] -> Graph a-fromDistinctList = fromMap- . Map.fromListWith (\_ -> duplicateError)- . map (\n -> n `seq` (nodeKey n, n))- where- duplicateError n = error $ "Graph.fromDistinctList: duplicate key: "- ++ show (nodeKey n)---- Map-like operations---- | /O(V)/. Convert a graph into a list of nodes.-toList :: Graph a -> [a]-toList g = Map.elems (toMap g)---- | /O(V)/. Convert a graph into a list of keys.-keys :: Graph a -> [Key a]-keys g = Map.keys (toMap g)---- | /O(V)/. Convert a graph into a set of keys.-keysSet :: Graph a -> Set.Set (Key a)-keysSet g = Map.keysSet (toMap g)---- | /O(1)/. Convert a graph into a map from keys to nodes.--- The resulting map @m@ is guaranteed to have the property that--- @'Prelude.all' (\(k,n) -> k == 'nodeKey' n) ('Data.Map.toList' m)@.-toMap :: Graph a -> Map (Key a) a-toMap = graphMap---- Graph-like operations---- | /O(1)/. Convert a graph into a 'Data.Graph.Graph'.--- Requires amortized construction of graph.-toGraph :: Graph a -> (G.Graph, G.Vertex -> a, Key a -> Maybe G.Vertex)-toGraph g = (graphForward g, graphVertexToNode g, graphKeyToVertex g)
src/Distribution/Compat/Internal/TempFile.hs view
@@ -1,30 +1,20 @@ {-# LANGUAGE CPP #-} {-# OPTIONS_HADDOCK hide #-}-module Distribution.Compat.Internal.TempFile (- openTempFile,- openBinaryTempFile,- openNewBinaryFile,- createTempDirectory,++module Distribution.Compat.Internal.TempFile+ ( openTempFile+ , openBinaryTempFile+ , openNewBinaryFile+ , createTempDirectory ) where import Distribution.Compat.Exception -import System.FilePath ((</>))--import System.IO (Handle, openTempFile, openBinaryTempFile)-#if defined(__IO_MANAGER_WINIO__)-import System.IO (openBinaryTempFileWithDefaultPermissions)-#else-import Control.Exception (onException)-import Data.Bits ((.|.))-import Foreign.C (CInt, eEXIST, getErrno, errnoToIOError)-import GHC.IO.Handle.FD (fdToHandle)-import System.Posix.Internals (c_open, c_close, o_EXCL, o_BINARY, withFilePath,- o_CREAT, o_RDWR, o_NONBLOCK, o_NOCTTY)-#endif+import System.FilePath ((</>)) +import System.IO (Handle, openBinaryTempFile, openBinaryTempFileWithDefaultPermissions, openTempFile)+import System.IO.Error (isAlreadyExistsError) import System.Posix.Internals (c_getpid)-import System.IO.Error (isAlreadyExistsError) #if defined(mingw32_HOST_OS) || defined(ghcjs_HOST_OS) import System.Directory ( createDirectory )@@ -32,89 +22,8 @@ import qualified System.Posix #endif --- --------------------------------------------------------------- * temporary files--- ---------------------------------------------------------------- This is here for Haskell implementations that do not come with--- System.IO.openTempFile. This includes nhc-1.20, hugs-2006.9.--- TODO: This file should probably be removed.---- This is a copy/paste of the openBinaryTempFile definition, but--- it uses 666 rather than 600 for the permissions. Newer versions--- of base have a new function with this behavior which we use on--- Windows when the new IO manager is used. openNewBinaryFile :: FilePath -> String -> IO (FilePath, Handle)-openNewBinaryFile dir template = do- -- This method can't be used under WINIO. Also the current implementation has- -- thread safety issues depending on which GHC is used. On newer GHC's let's- -- use the built in one.-#if defined(__IO_MANAGER_WINIO__)- openBinaryTempFileWithDefaultPermissions dir template-#else- pid <- c_getpid- findTempName pid- where- -- We split off the last extension, so we can use .foo.ext files- -- for temporary files (hidden on Unix OSes). Unfortunately we're- -- below file path in the hierarchy here.- (prefix,suffix) =- case break (== '.') $ reverse template of- -- First case: template contains no '.'s. Just re-reverse it.- (rev_suffix, "") -> (reverse rev_suffix, "")- -- Second case: template contains at least one '.'. Strip the- -- dot from the prefix and prepend it to the suffix (if we don't- -- do this, the unique number will get added after the '.' and- -- thus be part of the extension, which is wrong.)- (rev_suffix, '.':rest) -> (reverse rest, '.':reverse rev_suffix)- -- Otherwise, something is wrong, because (break (== '.')) should- -- always return a pair with either the empty string or a string- -- beginning with '.' as the second component.- _ -> error "bug in System.IO.openTempFile"-- oflags = rw_flags .|. o_EXCL .|. o_BINARY-- findTempName x = do- fd <- withFilePath filepath $ \ f ->- c_open f oflags 0o666- if fd < 0- then do- errno <- getErrno- if errno == eEXIST- then findTempName (x+1)- else ioError (errnoToIOError "openNewBinaryFile" errno Nothing (Just dir))- else do- -- TODO: We want to tell fdToHandle what the file path is,- -- as any exceptions etc will only be able to report the- -- FD currently- h <- fdToHandle fd `onException` c_close fd- return (filepath, h)- where- filename = prefix ++ show x ++ suffix- filepath = dir `combine` filename-- -- FIXME: bits copied from System.FilePath- combine a b- | null b = a- | null a = b- | last a == pathSeparator = a ++ b- | otherwise = a ++ [pathSeparator] ++ b---- FIXME: Copied from GHC.Handle-std_flags, output_flags, rw_flags :: CInt-std_flags = o_NONBLOCK .|. o_NOCTTY-output_flags = std_flags .|. o_CREAT-rw_flags = output_flags .|. o_RDWR---- FIXME: Should use System.FilePath library-pathSeparator :: Char-#ifdef mingw32_HOST_OS-pathSeparator = '\\'-#else-pathSeparator = '/'-#endif--- /* __IO_MANAGER_WINIO__ */-#endif+openNewBinaryFile = openBinaryTempFileWithDefaultPermissions createTempDirectory :: FilePath -> String -> IO FilePath createTempDirectory dir template = do@@ -122,12 +31,14 @@ findTempName pid where findTempName x = do- let dirpath = dir </> template ++ "-" ++ show x+ let relpath = template ++ "-" ++ show x+ dirpath = dir </> relpath r <- tryIO $ mkPrivateDir dirpath case r of- Right _ -> return dirpath- Left e | isAlreadyExistsError e -> findTempName (x+1)- | otherwise -> ioError e+ Right _ -> return relpath+ Left e+ | isAlreadyExistsError e -> findTempName (x + 1)+ | otherwise -> ioError e mkPrivateDir :: String -> IO () #if defined(mingw32_HOST_OS) || defined(ghcjs_HOST_OS)
− src/Distribution/Compat/Lens.hs
@@ -1,262 +0,0 @@-{-# LANGUAGE RankNTypes #-}--- | This module provides very basic lens functionality, without extra dependencies.------ For the documentation of the combinators see <http://hackage.haskell.org/package/lens lens> package.--- This module uses the same vocabulary.-module Distribution.Compat.Lens (- -- * Types- Lens,- Lens',- Traversal,- Traversal',- -- ** LensLike- LensLike,- LensLike',- -- ** rank-1 types- Getting,- AGetter,- ASetter,- ALens,- ALens',- -- * Getter- view,- use,- getting,- -- * Setter- set,- over,- -- * Fold- toDListOf,- toListOf,- toSetOf,- -- * Lens- cloneLens,- aview,- -- * Common lenses- _1, _2,- -- * Operators- (&),- (^.),- (.~), (?~), (%~),- (.=), (?=), (%=),- (^#),- (#~), (#%~),- -- * Internal Comonads- Pretext (..),- -- * Cabal developer info- -- $development- ) where--import Prelude()-import Distribution.Compat.Prelude--import Control.Monad.State.Class (MonadState (..), gets, modify)--import qualified Distribution.Compat.DList as DList-import qualified Data.Set as Set------------------------------------------------------------------------------------ Types----------------------------------------------------------------------------------type LensLike f s t a b = (a -> f b) -> s -> f t-type LensLike' f s a = (a -> f a) -> s -> f s--type Lens s t a b = forall f. Functor f => LensLike f s t a b-type Traversal s t a b = forall f. Applicative f => LensLike f s t a b--type Lens' s a = Lens s s a a-type Traversal' s a = Traversal s s a a--type Getting r s a = LensLike (Const r) s s a a--type AGetter s a = LensLike (Const a) s s a a -- this doesn't exist in 'lens'-type ASetter s t a b = LensLike Identity s t a b-type ALens s t a b = LensLike (Pretext a b) s t a b--type ALens' s a = ALens s s a a------------------------------------------------------------------------------------ Getter----------------------------------------------------------------------------------view :: Getting a s a -> s -> a-view l s = getConst (l Const s)-{-# INLINE view #-}--use :: MonadState s m => Getting a s a -> m a-use l = gets (view l)-{-# INLINE use #-}---- | @since 2.4------ >>> (3 :: Int) ^. getting (+2) . getting show--- "5"-getting :: (s -> a) -> Getting r s a-getting k f = Const . getConst . f . k-{-# INLINE getting #-}------------------------------------------------------------------------------------ Setter----------------------------------------------------------------------------------set :: ASetter s t a b -> b -> s -> t-set l x = over l (const x)--over :: ASetter s t a b -> (a -> b) -> s -> t-over l f s = runIdentity (l (\x -> Identity (f x)) s)------------------------------------------------------------------------------------ Fold----------------------------------------------------------------------------------toDListOf :: Getting (DList.DList a) s a -> s -> DList.DList a-toDListOf l s = getConst (l (\x -> Const (DList.singleton x)) s)--toListOf :: Getting (DList.DList a) s a -> s -> [a]-toListOf l = DList.runDList . toDListOf l--toSetOf :: Getting (Set.Set a) s a -> s -> Set.Set a-toSetOf l s = getConst (l (\x -> Const (Set.singleton x)) s)------------------------------------------------------------------------------------ Lens----------------------------------------------------------------------------------aview :: ALens s t a b -> s -> a-aview l = pretextPos . l pretextSell-{-# INLINE aview #-}--{--lens :: (s -> a) -> (s -> a -> s) -> Lens' s a-lens sa sbt afb s = sbt s <$> afb (sa s)--}------------------------------------------------------------------------------------ Common----------------------------------------------------------------------------------_1 :: Lens (a, c) (b, c) a b-_1 f (a, c) = flip (,) c <$> f a--_2 :: Lens (c, a) (c, b) a b-_2 f (c, a) = (,) c <$> f a------------------------------------------------------------------------------------ Operators------------------------------------------------------------------------------------ | '&' is a reverse application operator-(&) :: a -> (a -> b) -> b-(&) = flip ($)-{-# INLINE (&) #-}-infixl 1 &--infixl 8 ^., ^#-infixr 4 .~, %~, ?~-infixr 4 #~, #%~-infixr 4 .=, %=, ?=--(^.) :: s -> Getting a s a -> a-s ^. l = getConst (l Const s)-{-# INLINE (^.) #-}--(.~) :: ASetter s t a b -> b -> s -> t-(.~) = set-{-# INLINE (.~) #-}--(?~) :: ASetter s t a (Maybe b) -> b -> s -> t-l ?~ b = set l (Just b)-{-# INLINE (?~) #-}--(%~) :: ASetter s t a b -> (a -> b) -> s -> t-(%~) = over-{-# INLINE (%~) #-}--(.=) :: MonadState s m => ASetter s s a b -> b -> m ()-l .= b = modify (l .~ b)-{-# INLINE (.=) #-}--(?=) :: MonadState s m => ASetter s s a (Maybe b) -> b -> m ()-l ?= b = modify (l ?~ b)-{-# INLINE (?=) #-}--(%=) :: MonadState s m => ASetter s s a b -> (a -> b) -> m ()-l %= f = modify (l %~ f)-{-# INLINE (%=) #-}--(^#) :: s -> ALens s t a b -> a-s ^# l = aview l s--(#~) :: ALens s t a b -> b -> s -> t-(#~) l b s = pretextPeek b (l pretextSell s)-{-# INLINE (#~) #-}--(#%~) :: ALens s t a b -> (a -> b) -> s -> t-(#%~) l f s = pretextPeeks f (l pretextSell s)-{-# INLINE (#%~) #-}--pretextSell :: a -> Pretext a b b-pretextSell a = Pretext (\afb -> afb a)-{-# INLINE pretextSell #-}--pretextPeeks :: (a -> b) -> Pretext a b t -> t-pretextPeeks f (Pretext m) = runIdentity $ m (\x -> Identity (f x))-{-# INLINE pretextPeeks #-}--pretextPeek :: b -> Pretext a b t -> t-pretextPeek b (Pretext m) = runIdentity $ m (\_ -> Identity b)-{-# INLINE pretextPeek #-}--pretextPos :: Pretext a b t -> a-pretextPos (Pretext m) = getConst (m Const)-{-# INLINE pretextPos #-}--cloneLens :: Functor f => ALens s t a b -> LensLike f s t a b-cloneLens l f s = runPretext (l pretextSell s) f-{-# INLINE cloneLens #-}------------------------------------------------------------------------------------ Comonads------------------------------------------------------------------------------------ | @lens@ variant is also parametrised by profunctor.-data Pretext a b t = Pretext { runPretext :: forall f. Functor f => (a -> f b) -> f t }--instance Functor (Pretext a b) where- fmap f (Pretext pretext) = Pretext (\afb -> fmap f (pretext afb))------------------------------------------------------------------------------------ Documentation------------------------------------------------------------------------------------ $development------ We cannot depend on @template-haskell@, because Cabal is a boot library.--- This fact makes defining optics a manual task. Here is a small recipe to--- make the process less tedious.------ First start a repl------ > cabal new-repl Cabal:hackage-tests------ Because @--extra-package@ isn't yet implemented, we use a test-suite--- with @generics-sop@ dependency.------ In the repl, we load a helper script:------ > :l ../generics-sop-lens.hs------ Now we are set up to derive lenses!------ > :m +Distribution.Types.SourceRepo--- > putStr $ genericLenses (Proxy :: Proxy SourceRepo)------ @--- repoKind :: Lens' SourceRepo RepoKind--- repoKind f s = fmap (\\x -> s { T.repoKind = x }) (f (T.repoKind s))--- \{-# INLINE repoKind #-\}--- ...--- @------ /Note:/ You may need to adjust type-aliases, e.g. `String` to `FilePath`.
− src/Distribution/Compat/MonadFail.hs
@@ -1,5 +0,0 @@-{-# LANGUAGE CPP #-}---- | Compatibility layer for "Control.Monad.Fail"-module Distribution.Compat.MonadFail ( Control.Monad.Fail.MonadFail(fail) ) where-import Control.Monad.Fail
− src/Distribution/Compat/Newtype.hs
@@ -1,91 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DefaultSignatures #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FunctionalDependencies #-}--- | Per Conor McBride, the 'Newtype' typeclass represents the packing and--- unpacking of a newtype, and allows you to operatate under that newtype with--- functions such as 'ala'.-module Distribution.Compat.Newtype (- Newtype (..),- ala,- alaf,- pack',- unpack',- ) where--import Data.Functor.Identity (Identity (..))-import Data.Monoid (Sum (..), Product (..), Endo (..))--#if MIN_VERSION_base(4,7,0)-import Data.Coerce (coerce, Coercible)-#else-import Unsafe.Coerce (unsafeCoerce)-#endif---- | The @FunctionalDependencies@ version of 'Newtype' type-class.------ Since Cabal-3.0 class arguments are in a different order than in @newtype@ package.--- This change is to allow usage with @DeriveAnyClass@ (and @DerivingStrategies@, in GHC-8.2).--- Unfortunately one has to repeat inner type.------ @--- newtype New = New Old--- deriving anyclass (Newtype Old)--- @------ Another approach would be to use @TypeFamilies@ (and possibly--- compute inner type using "GHC.Generics"), but we think @FunctionalDependencies@--- version gives cleaner type signatures.----class Newtype o n | n -> o where- pack :: o -> n-#if MIN_VERSION_base(4,7,0)- default pack :: Coercible o n => o -> n- pack = coerce-#else- default pack :: o -> n- pack = unsafeCoerce-#endif-- unpack :: n -> o-#if MIN_VERSION_base(4,7,0)- default unpack :: Coercible n o => n -> o- unpack = coerce-#else- default unpack :: n -> o- unpack = unsafeCoerce-#endif--instance Newtype a (Identity a)-instance Newtype a (Sum a)-instance Newtype a (Product a)-instance Newtype (a -> a) (Endo a)---- |------ >>> ala Sum foldMap [1, 2, 3, 4 :: Int]--- 10------ /Note:/ the user supplied function for the newtype is /ignored/.------ >>> ala (Sum . (+1)) foldMap [1, 2, 3, 4 :: Int]--- 10-ala :: (Newtype o n, Newtype o' n') => (o -> n) -> ((o -> n) -> b -> n') -> (b -> o')-ala pa hof = alaf pa hof id---- |------ >>> alaf Sum foldMap length ["cabal", "install"]--- 12------ /Note:/ as with 'ala', the user supplied function for the newtype is /ignored/.-alaf :: (Newtype o n, Newtype o' n') => (o -> n) -> ((a -> n) -> b -> n') -> (a -> o) -> (b -> o')-alaf _ hof f = unpack . hof (pack . f)---- | Variant of 'pack', which takes a phantom type.-pack' :: Newtype o n => (o -> n) -> o -> n-pack' _ = pack---- | Variant of 'unpack', which takes a phantom type.-unpack' :: Newtype o n => (o -> n) -> n -> o-unpack' _ = unpack
− src/Distribution/Compat/NonEmptySet.hs
@@ -1,155 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveDataTypeable #-}-module Distribution.Compat.NonEmptySet (- NonEmptySet,- -- * Construction- singleton,- -- * Insertion- insert,- -- * Deletion- delete,- -- * Conversions- toNonEmpty,- fromNonEmpty,- toList,- toSet,- -- * Query- member,- -- * Map- map,-) where--import Prelude (Bool (..), Eq, Maybe (..), Ord (..), Read, Show (..), String, error, otherwise, return, showParen, showString, ($), (++), (.))--import Control.DeepSeq (NFData (..))-import Data.Data (Data)-import Data.List.NonEmpty (NonEmpty (..))-import Data.Semigroup (Semigroup (..))-import Data.Typeable (Typeable)--import qualified Data.Foldable as F-import qualified Data.Set as Set--import Distribution.Compat.Binary (Binary (..))-import Distribution.Utils.Structured--#if MIN_VERSION_binary(0,6,0)-import Control.Applicative (empty)-#else-import Control.Monad (fail)-#endif---- | @since 3.4.0.0-newtype NonEmptySet a = NES (Set.Set a)- deriving (Eq, Ord, Typeable, Data, Read)------------------------------------------------------------------------------------ Instances----------------------------------------------------------------------------------instance Show a => Show (NonEmptySet a) where- showsPrec d s = showParen (d > 10)- $ showString "fromNonEmpty "- . showsPrec 11 (toNonEmpty s)--instance Binary a => Binary (NonEmptySet a) where- put (NES s) = put s- get = do- xs <- get- if Set.null xs-#if MIN_VERSION_binary(0,6,0)- then empty-#else- then fail "NonEmptySet: empty"-#endif- else return (NES xs)--instance Structured a => Structured (NonEmptySet a) where- structure = containerStructure--instance NFData a => NFData (NonEmptySet a) where- rnf (NES x) = rnf x---- | Note: there aren't @Monoid@ instance.-instance Ord a => Semigroup (NonEmptySet a) where- NES x <> NES y = NES (Set.union x y)--instance F.Foldable NonEmptySet where- foldMap f (NES s) = F.foldMap f s- foldr f z (NES s) = F.foldr f z s--#if MIN_VERSION_base(4,8,0)- toList = toList- null _ = False- length (NES s) = F.length s-#endif------------------------------------------------------------------------------------ Constructors----------------------------------------------------------------------------------singleton :: a -> NonEmptySet a-singleton = NES . Set.singleton------------------------------------------------------------------------------------ Insertion----------------------------------------------------------------------------------insert :: Ord a => a -> NonEmptySet a -> NonEmptySet a-insert x (NES xs) = NES (Set.insert x xs)------------------------------------------------------------------------------------ Deletion----------------------------------------------------------------------------------delete :: Ord a => a -> NonEmptySet a -> Maybe (NonEmptySet a)-delete x (NES xs)- | Set.null res = Nothing- | otherwise = Just (NES xs)- where- res = Set.delete x xs------------------------------------------------------------------------------------ Conversions----------------------------------------------------------------------------------fromNonEmpty :: Ord a => NonEmpty a -> NonEmptySet a-fromNonEmpty (x :| xs) = NES (Set.fromList (x : xs))--toNonEmpty :: NonEmptySet a -> NonEmpty a-toNonEmpty (NES s) = case Set.toList s of- [] -> panic "toNonEmpty"- x:xs -> x :| xs--toList :: NonEmptySet a -> [a]-toList (NES s) = Set.toList s--toSet :: NonEmptySet a -> Set.Set a-toSet (NES s) = s------------------------------------------------------------------------------------ Query----------------------------------------------------------------------------------member :: Ord a => a -> NonEmptySet a -> Bool-member x (NES xs) = Set.member x xs------------------------------------------------------------------------------------ Map----------------------------------------------------------------------------------map- :: ( Ord b-#if !MIN_VERSION_containers(0,5,2)- , Ord a-#endif- )- => (a -> b) -> NonEmptySet a -> NonEmptySet b-map f (NES x) = NES (Set.map f x)------------------------------------------------------------------------------------ Internal----------------------------------------------------------------------------------panic :: String -> a-panic msg = error $ "NonEmptySet invariant violated: " ++ msg
− src/Distribution/Compat/Parsing.hs
@@ -1,362 +0,0 @@-{-# LANGUAGE GADTs, UndecidableInstances #-}--------------------------------------------------------------------------------- |--- Module : Distribution.Compat.Parsing--- Copyright : (c) Edward Kmett 2011-2012--- License : BSD3------ Maintainer : ekmett@gmail.com--- Stability : experimental--- Portability : non-portable------ Alternative parser combinators.------ Originally in @parsers@ package.----------------------------------------------------------------------------------module Distribution.Compat.Parsing- (- -- * Parsing Combinators- choice- , option- , optional -- from Control.Applicative, parsec optionMaybe- , skipOptional -- parsec optional- , between- , some -- from Control.Applicative, parsec many1- , many -- from Control.Applicative- , sepBy- , sepByNonEmpty- , sepEndByNonEmpty- , sepEndBy- , endByNonEmpty- , endBy- , count- , chainl- , chainr- , chainl1- , chainr1- , manyTill- -- * Parsing Class- , Parsing(..)- ) where--import Prelude ()-import Distribution.Compat.Prelude--import Control.Applicative ((<**>), optional)-import Control.Monad.Trans.Class (lift)-import Control.Monad.Trans.State.Lazy as Lazy-import Control.Monad.Trans.State.Strict as Strict-import Control.Monad.Trans.Writer.Lazy as Lazy-import Control.Monad.Trans.Writer.Strict as Strict-import Control.Monad.Trans.RWS.Lazy as Lazy-import Control.Monad.Trans.RWS.Strict as Strict-import Control.Monad.Trans.Reader (ReaderT (..))-import Control.Monad.Trans.Identity (IdentityT (..))-import Data.Foldable (asum)--import qualified Data.List.NonEmpty as NE-import qualified Text.Parsec as Parsec---- | @choice ps@ tries to apply the parsers in the list @ps@ in order,--- until one of them succeeds. Returns the value of the succeeding--- parser.-choice :: Alternative m => [m a] -> m a-choice = asum-{-# INLINE choice #-}---- | @option x p@ tries to apply parser @p@. If @p@ fails without--- consuming input, it returns the value @x@, otherwise the value--- returned by @p@.------ > priority = option 0 (digitToInt <$> digit)-option :: Alternative m => a -> m a -> m a-option x p = p <|> pure x-{-# INLINE option #-}---- | @skipOptional p@ tries to apply parser @p@. It will parse @p@ or nothing.--- It only fails if @p@ fails after consuming input. It discards the result--- of @p@. (Plays the role of parsec's optional, which conflicts with Applicative's optional)-skipOptional :: Alternative m => m a -> m ()-skipOptional p = (() <$ p) <|> pure ()-{-# INLINE skipOptional #-}---- | @between open close p@ parses @open@, followed by @p@ and @close@.--- Returns the value returned by @p@.------ > braces = between (symbol "{") (symbol "}")-between :: Applicative m => m bra -> m ket -> m a -> m a-between bra ket p = bra *> p <* ket-{-# INLINE between #-}---- | @sepBy p sep@ parses /zero/ or more occurrences of @p@, separated--- by @sep@. Returns a list of values returned by @p@.------ > commaSep p = p `sepBy` (symbol ",")-sepBy :: Alternative m => m a -> m sep -> m [a]-sepBy p sep = toList <$> sepByNonEmpty p sep <|> pure []-{-# INLINE sepBy #-}---- | @sepByNonEmpty p sep@ parses /one/ or more occurrences of @p@, separated--- by @sep@. Returns a non-empty list of values returned by @p@.-sepByNonEmpty :: Alternative m => m a -> m sep -> m (NonEmpty a)-sepByNonEmpty p sep = (:|) <$> p <*> many (sep *> p)-{-# INLINE sepByNonEmpty #-}---- | @sepEndByNonEmpty p sep@ parses /one/ or more occurrences of @p@,--- separated and optionally ended by @sep@. Returns a non-empty list of values--- returned by @p@.-sepEndByNonEmpty :: Alternative m => m a -> m sep -> m (NonEmpty a)-sepEndByNonEmpty p sep = (:|) <$> p <*> ((sep *> sepEndBy p sep) <|> pure [])---- | @sepEndBy p sep@ parses /zero/ or more occurrences of @p@,--- separated and optionally ended by @sep@, ie. haskell style--- statements. Returns a list of values returned by @p@.------ > haskellStatements = haskellStatement `sepEndBy` semi-sepEndBy :: Alternative m => m a -> m sep -> m [a]-sepEndBy p sep = toList <$> sepEndByNonEmpty p sep <|> pure []-{-# INLINE sepEndBy #-}---- | @endByNonEmpty p sep@ parses /one/ or more occurrences of @p@, separated--- and ended by @sep@. Returns a non-empty list of values returned by @p@.-endByNonEmpty :: Alternative m => m a -> m sep -> m (NonEmpty a)-endByNonEmpty p sep = NE.some1 (p <* sep)-{-# INLINE endByNonEmpty #-}---- | @endBy p sep@ parses /zero/ or more occurrences of @p@, separated--- and ended by @sep@. Returns a list of values returned by @p@.------ > cStatements = cStatement `endBy` semi-endBy :: Alternative m => m a -> m sep -> m [a]-endBy p sep = many (p <* sep)-{-# INLINE endBy #-}---- | @count n p@ parses @n@ occurrences of @p@. If @n@ is smaller or--- equal to zero, the parser equals to @return []@. Returns a list of--- @n@ values returned by @p@.-count :: Applicative m => Int -> m a -> m [a]-count n p | n <= 0 = pure []- | otherwise = sequenceA (replicate n p)-{-# INLINE count #-}---- | @chainr p op x@ parses /zero/ or more occurrences of @p@,--- separated by @op@ Returns a value obtained by a /right/ associative--- application of all functions returned by @op@ to the values returned--- by @p@. If there are no occurrences of @p@, the value @x@ is--- returned.-chainr :: Alternative m => m a -> m (a -> a -> a) -> a -> m a-chainr p op x = chainr1 p op <|> pure x-{-# INLINE chainr #-}---- | @chainl p op x@ parses /zero/ or more occurrences of @p@,--- separated by @op@. Returns a value obtained by a /left/ associative--- application of all functions returned by @op@ to the values returned--- by @p@. If there are zero occurrences of @p@, the value @x@ is--- returned.-chainl :: Alternative m => m a -> m (a -> a -> a) -> a -> m a-chainl p op x = chainl1 p op <|> pure x-{-# INLINE chainl #-}---- | @chainl1 p op x@ parses /one/ or more occurrences of @p@,--- separated by @op@ Returns a value obtained by a /left/ associative--- application of all functions returned by @op@ to the values returned--- by @p@. . This parser can for example be used to eliminate left--- recursion which typically occurs in expression grammars.------ > expr = term `chainl1` addop--- > term = factor `chainl1` mulop--- > factor = parens expr <|> integer--- >--- > mulop = (*) <$ symbol "*"--- > <|> div <$ symbol "/"--- >--- > addop = (+) <$ symbol "+"--- > <|> (-) <$ symbol "-"-chainl1 :: Alternative m => m a -> m (a -> a -> a) -> m a-chainl1 p op = scan where- scan = p <**> rst- rst = (\f y g x -> g (f x y)) <$> op <*> p <*> rst <|> pure id-{-# INLINE chainl1 #-}---- | @chainr1 p op x@ parses /one/ or more occurrences of @p@,--- separated by @op@ Returns a value obtained by a /right/ associative--- application of all functions returned by @op@ to the values returned--- by @p@.-chainr1 :: Alternative m => m a -> m (a -> a -> a) -> m a-chainr1 p op = scan where- scan = p <**> rst- rst = (flip <$> op <*> scan) <|> pure id-{-# INLINE chainr1 #-}---- | @manyTill p end@ applies parser @p@ /zero/ or more times until--- parser @end@ succeeds. Returns the list of values returned by @p@.--- This parser can be used to scan comments:------ > simpleComment = do{ string "<!--"--- > ; manyTill anyChar (try (string "-->"))--- > }------ Note the overlapping parsers @anyChar@ and @string \"-->\"@, and--- therefore the use of the 'try' combinator.-manyTill :: Alternative m => m a -> m end -> m [a]-manyTill p end = go where go = ([] <$ end) <|> ((:) <$> p <*> go)-{-# INLINE manyTill #-}--infixr 0 <?>---- | Additional functionality needed to describe parsers independent of input type.-class Alternative m => Parsing m where- -- | Take a parser that may consume input, and on failure, go back to- -- where we started and fail as if we didn't consume input.- try :: m a -> m a-- -- | Give a parser a name- (<?>) :: m a -> String -> m a-- -- | A version of many that discards its input. Specialized because it- -- can often be implemented more cheaply.- skipMany :: m a -> m ()- skipMany p = () <$ many p- {-# INLINE skipMany #-}-- -- | @skipSome p@ applies the parser @p@ /one/ or more times, skipping- -- its result. (aka skipMany1 in parsec)- skipSome :: m a -> m ()- skipSome p = p *> skipMany p- {-# INLINE skipSome #-}-- -- | Used to emit an error on an unexpected token- unexpected :: String -> m a-- -- | This parser only succeeds at the end of the input. This is not a- -- primitive parser but it is defined using 'notFollowedBy'.- --- -- > eof = notFollowedBy anyChar <?> "end of input"- eof :: m ()-- -- | @notFollowedBy p@ only succeeds when parser @p@ fails. This parser- -- does not consume any input. This parser can be used to implement the- -- \'longest match\' rule. For example, when recognizing keywords (for- -- example @let@), we want to make sure that a keyword is not followed- -- by a legal identifier character, in which case the keyword is- -- actually an identifier (for example @lets@). We can program this- -- behaviour as follows:- --- -- > keywordLet = try $ string "let" <* notFollowedBy alphaNum- notFollowedBy :: Show a => m a -> m ()--instance (Parsing m, MonadPlus m) => Parsing (Lazy.StateT s m) where- try (Lazy.StateT m) = Lazy.StateT $ try . m- {-# INLINE try #-}- Lazy.StateT m <?> l = Lazy.StateT $ \s -> m s <?> l- {-# INLINE (<?>) #-}- unexpected = lift . unexpected- {-# INLINE unexpected #-}- eof = lift eof- {-# INLINE eof #-}- notFollowedBy (Lazy.StateT m) = Lazy.StateT- $ \s -> notFollowedBy (fst <$> m s) >> return ((),s)- {-# INLINE notFollowedBy #-}--instance (Parsing m, MonadPlus m) => Parsing (Strict.StateT s m) where- try (Strict.StateT m) = Strict.StateT $ try . m- {-# INLINE try #-}- Strict.StateT m <?> l = Strict.StateT $ \s -> m s <?> l- {-# INLINE (<?>) #-}- unexpected = lift . unexpected- {-# INLINE unexpected #-}- eof = lift eof- {-# INLINE eof #-}- notFollowedBy (Strict.StateT m) = Strict.StateT- $ \s -> notFollowedBy (fst <$> m s) >> return ((),s)- {-# INLINE notFollowedBy #-}--instance (Parsing m, MonadPlus m) => Parsing (ReaderT e m) where- try (ReaderT m) = ReaderT $ try . m- {-# INLINE try #-}- ReaderT m <?> l = ReaderT $ \e -> m e <?> l- {-# INLINE (<?>) #-}- skipMany (ReaderT m) = ReaderT $ skipMany . m- {-# INLINE skipMany #-}- unexpected = lift . unexpected- {-# INLINE unexpected #-}- eof = lift eof- {-# INLINE eof #-}- notFollowedBy (ReaderT m) = ReaderT $ notFollowedBy . m- {-# INLINE notFollowedBy #-}--instance (Parsing m, MonadPlus m, Monoid w) => Parsing (Strict.WriterT w m) where- try (Strict.WriterT m) = Strict.WriterT $ try m- {-# INLINE try #-}- Strict.WriterT m <?> l = Strict.WriterT (m <?> l)- {-# INLINE (<?>) #-}- unexpected = lift . unexpected- {-# INLINE unexpected #-}- eof = lift eof- {-# INLINE eof #-}- notFollowedBy (Strict.WriterT m) = Strict.WriterT- $ notFollowedBy (fst <$> m) >>= \x -> return (x, mempty)- {-# INLINE notFollowedBy #-}--instance (Parsing m, MonadPlus m, Monoid w) => Parsing (Lazy.WriterT w m) where- try (Lazy.WriterT m) = Lazy.WriterT $ try m- {-# INLINE try #-}- Lazy.WriterT m <?> l = Lazy.WriterT (m <?> l)- {-# INLINE (<?>) #-}- unexpected = lift . unexpected- {-# INLINE unexpected #-}- eof = lift eof- {-# INLINE eof #-}- notFollowedBy (Lazy.WriterT m) = Lazy.WriterT- $ notFollowedBy (fst <$> m) >>= \x -> return (x, mempty)- {-# INLINE notFollowedBy #-}--instance (Parsing m, MonadPlus m, Monoid w) => Parsing (Lazy.RWST r w s m) where- try (Lazy.RWST m) = Lazy.RWST $ \r s -> try (m r s)- {-# INLINE try #-}- Lazy.RWST m <?> l = Lazy.RWST $ \r s -> m r s <?> l- {-# INLINE (<?>) #-}- unexpected = lift . unexpected- {-# INLINE unexpected #-}- eof = lift eof- {-# INLINE eof #-}- notFollowedBy (Lazy.RWST m) = Lazy.RWST- $ \r s -> notFollowedBy ((\(a,_,_) -> a) <$> m r s) >>= \x -> return (x, s, mempty)- {-# INLINE notFollowedBy #-}--instance (Parsing m, MonadPlus m, Monoid w) => Parsing (Strict.RWST r w s m) where- try (Strict.RWST m) = Strict.RWST $ \r s -> try (m r s)- {-# INLINE try #-}- Strict.RWST m <?> l = Strict.RWST $ \r s -> m r s <?> l- {-# INLINE (<?>) #-}- unexpected = lift . unexpected- {-# INLINE unexpected #-}- eof = lift eof- {-# INLINE eof #-}- notFollowedBy (Strict.RWST m) = Strict.RWST- $ \r s -> notFollowedBy ((\(a,_,_) -> a) <$> m r s) >>= \x -> return (x, s, mempty)- {-# INLINE notFollowedBy #-}--instance (Parsing m, Monad m) => Parsing (IdentityT m) where- try = IdentityT . try . runIdentityT- {-# INLINE try #-}- IdentityT m <?> l = IdentityT (m <?> l)- {-# INLINE (<?>) #-}- skipMany = IdentityT . skipMany . runIdentityT- {-# INLINE skipMany #-}- unexpected = lift . unexpected- {-# INLINE unexpected #-}- eof = lift eof- {-# INLINE eof #-}- notFollowedBy (IdentityT m) = IdentityT $ notFollowedBy m- {-# INLINE notFollowedBy #-}--instance (Parsec.Stream s m t, Show t) => Parsing (Parsec.ParsecT s u m) where- try = Parsec.try- (<?>) = (Parsec.<?>)- skipMany = Parsec.skipMany- skipSome = Parsec.skipMany1- unexpected = Parsec.unexpected- eof = Parsec.eof- notFollowedBy = Parsec.notFollowedBy
− src/Distribution/Compat/Prelude.hs
@@ -1,316 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE Trustworthy #-}-{-# LANGUAGE TypeOperators #-}--#ifdef MIN_VERSION_base-#define MINVER_base_411 MIN_VERSION_base(4,11,0)-#define MINVER_base_48 MIN_VERSION_base(4,8,0)-#define MINVER_base_47 MIN_VERSION_base(4,7,0)-#else-#define MINVER_base_411 (__GLASGOW_HASKELL__ >= 804)-#define MINVER_base_48 (__GLASGOW_HASKELL__ >= 710)-#define MINVER_base_47 (__GLASGOW_HASKELL__ >= 708)-#endif---- | This module does two things:------ * Acts as a compatibility layer, like @base-compat@.------ * Provides commonly used imports.-module Distribution.Compat.Prelude (- -- * Prelude- --- -- Prelude is re-exported, following is hidden:- module BasePrelude,--#if !MINVER_base_48- -- * base 4.8 shim- Applicative(..), (<$), (<$>),- Monoid(..),-#endif-- -- * Common type-classes- Semigroup (..),- gmappend, gmempty,- Typeable, TypeRep, typeRep,- Data,- Generic,- NFData (..), genericRnf,- Binary (..),- Structured,- Alternative (..),- MonadPlus (..),- IsString (..),-- -- * Some types- Map,- Set,- NonEmptySet,- Identity (..),- Proxy (..),- Const (..),- Void,-- -- * Data.Either- partitionEithers,-- -- * Data.Maybe- catMaybes, mapMaybe,- fromMaybe,- maybeToList, listToMaybe,- isNothing, isJust,-- -- * Data.List- unfoldr,- isPrefixOf, isSuffixOf,- intercalate, intersperse,- sort, sortBy,- nub, nubBy,- partition,-- -- * Data.List.NonEmpty- NonEmpty((:|)), nonEmpty, foldl1, foldr1,- head, tail, last, init,-- -- * Data.Foldable- Foldable, foldMap, foldr,- null, length,- find, foldl',- traverse_, for_,- any, all,- toList,-- -- * Data.Traversable- Traversable, traverse, sequenceA,- for,-- -- * Data.Function- on,-- -- * Data.Ord- comparing,-- -- * Control.Arrow- first,-- -- * Control.Monad- liftM, liftM2,- unless, when,- ap, void,- foldM, filterM,- join, guard,-- -- * Control.Exception- catch, throwIO, evaluate,- Exception (..), IOException, SomeException (..),-#if !MINVER_base_48- displayException,-#endif- tryIO, catchIO, catchExit,-- -- * Control.DeepSeq- deepseq, force,-- -- * Data.Char- isSpace, isDigit, isUpper, isAlpha, isAlphaNum,- chr, ord,- toLower, toUpper,-- -- * Data.Void- absurd, vacuous,-- -- * Data.Word & Data.Int- Word,- Word8, Word16, Word32, Word64,- Int8, Int16, Int32, Int64,-- -- * Text.PrettyPrint- (<<>>), (Disp.<+>),-- -- * System.Exit- ExitCode (..),- exitWith, exitSuccess, exitFailure,-- -- * Text.Read- readMaybe,-- -- * Debug.Trace (as deprecated functions)- trace, traceShow, traceShowId,- ) where---- We also could hide few partial function-import Prelude as BasePrelude hiding- ( mapM, mapM_, sequence, null, length, foldr, any, all, head, tail, last, init- -- partial functions- , read- , foldr1, foldl1-#if MINVER_base_411- -- As of base 4.11.0.0 Prelude exports part of Semigroup(..).- -- Hide this so we instead rely on Distribution.Compat.Semigroup.- , Semigroup(..)-#endif-#if MINVER_base_48- , Word- -- We hide them, as we import only some members- , Traversable, traverse, sequenceA- , Foldable, foldMap-#endif- )---- AMP-#if !MINVER_base_48-import Control.Applicative (Applicative (..), (<$), (<$>))-import Data.Foldable (toList)-import Distribution.Compat.Semigroup (Monoid (..))-#else-import Data.Foldable (Foldable (toList), length, null)-#endif--import Data.Foldable (Foldable (foldMap, foldr), all, any, find, foldl', for_, traverse_)-import Data.Traversable (Traversable (sequenceA, traverse), for)--import qualified Data.Foldable---- Extra exports-import Control.Applicative (Alternative (..))-import Control.Applicative (Const (..))-import Control.Arrow (first)-import Control.DeepSeq (NFData (..), deepseq, force)-import Control.Exception (Exception (..), IOException, SomeException (..), catch, evaluate, throwIO)-import Control.Monad (MonadPlus (..), ap, filterM, foldM, guard, join, liftM, liftM2, unless, void, when)-import Data.Char (chr, isAlpha, isAlphaNum, isDigit, isSpace, isUpper, ord, toLower, toUpper)-import Data.Data (Data)-import Data.Either (partitionEithers)-import Data.Function (on)-import Data.Functor.Identity (Identity (..))-import Data.Int (Int16, Int32, Int64, Int8)-import Data.List (intercalate, intersperse, isPrefixOf, isSuffixOf, nub, nubBy, partition, sort, sortBy, unfoldr)-import Data.List.NonEmpty (NonEmpty ((:|)), nonEmpty, head, init, last, tail)-import Data.Map (Map)-import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing, listToMaybe, mapMaybe, maybeToList)-import Data.Ord (comparing)-import Data.Proxy (Proxy (..))-import Data.Set (Set)-import Data.String (IsString (..))-import Data.Void (Void, absurd, vacuous)-import Data.Word (Word, Word16, Word32, Word64, Word8)-import Distribution.Compat.Binary (Binary (..))-import Distribution.Compat.Semigroup (Semigroup (..), gmappend, gmempty)-import Distribution.Compat.Typeable (TypeRep, Typeable, typeRep)-import GHC.Generics ((:*:) ((:*:)), (:+:) (L1, R1), Generic, K1 (unK1), M1 (unM1), Rep (..), U1 (U1), V1)-import System.Exit (ExitCode (..), exitFailure, exitSuccess, exitWith)-import Text.Read (readMaybe)--import qualified Text.PrettyPrint as Disp--import Distribution.Compat.Exception-import Distribution.Compat.NonEmptySet (NonEmptySet)-import Distribution.Utils.Structured (Structured)--import qualified Debug.Trace---- | New name for 'Text.PrettyPrint.<>'-(<<>>) :: Disp.Doc -> Disp.Doc -> Disp.Doc-(<<>>) = (Disp.<>)--#if !MINVER_base_48--- | Test whether the structure is empty. The default implementation is--- optimized for structures that are similar to cons-lists, because there--- is no general way to do better.-null :: Foldable t => t a -> Bool-null = foldr (\_ _ -> False) True---- | Returns the size/length of a finite structure as an 'Int'. The--- default implementation is optimized for structures that are similar to--- cons-lists, because there is no general way to do better.-length :: Foldable t => t a -> Int-length = foldl' (\c _ -> c+1) 0-#endif----- | "GHC.Generics"-based 'rnf' implementation------ This is needed in order to support @deepseq < 1.4@ which didn't--- have a 'Generic'-based default 'rnf' implementation yet.------ In order to define instances, use e.g.------ > instance NFData MyType where rnf = genericRnf------ The implementation has been taken from @deepseq-1.4.2@'s default--- 'rnf' implementation.-genericRnf :: (Generic a, GNFData (Rep a)) => a -> ()-genericRnf = grnf . from---- | Hidden internal type-class-class GNFData f where- grnf :: f a -> ()--instance GNFData V1 where- grnf = error "Control.DeepSeq.rnf: uninhabited type"--instance GNFData U1 where- grnf U1 = ()--instance NFData a => GNFData (K1 i a) where- grnf = rnf . unK1- {-# INLINEABLE grnf #-}--instance GNFData a => GNFData (M1 i c a) where- grnf = grnf . unM1- {-# INLINEABLE grnf #-}--instance (GNFData a, GNFData b) => GNFData (a :*: b) where- grnf (x :*: y) = grnf x `seq` grnf y- {-# INLINEABLE grnf #-}--instance (GNFData a, GNFData b) => GNFData (a :+: b) where- grnf (L1 x) = grnf x- grnf (R1 x) = grnf x- {-# INLINEABLE grnf #-}----- TODO: if we want foldr1/foldl1 to work on more than NonEmpty, we--- can define a local typeclass 'Foldable1', e.g.------ @--- class Foldable f => Foldable1 f------ instance Foldable1 NonEmpty------ foldr1 :: Foldable1 t => (a -> a -> a) -> t a -> a--- foldr1 = Data.Foldable.foldr1------ foldl1 :: Foldable1 t => (a -> a -> a) -> t a -> a--- foldl1 = Data.Foldable.foldl1--- @-----{-# INLINE foldr1 #-}-foldr1 :: (a -> a -> a) -> NonEmpty a -> a-foldr1 = Data.Foldable.foldr1--{-# INLINE foldl1 #-}-foldl1 :: (a -> a -> a) -> NonEmpty a -> a-foldl1 = Data.Foldable.foldl1------------------------------------------------------------------------------------ Trace------------------------------------------------------------------------------------ Functions from Debug.Trace--- but with DEPRECATED pragma, so -Werror will scream on them.--trace :: String -> a -> a-trace = Debug.Trace.trace-{-# DEPRECATED trace "Don't leave me in the code" #-}--traceShowId :: Show a => a -> a-traceShowId x = Debug.Trace.traceShow x x-{-# DEPRECATED traceShowId "Don't leave me in the code" #-}--traceShow :: Show a => a -> b -> b-traceShow = Debug.Trace.traceShow-{-# DEPRECATED traceShow "Don't leave me in the code" #-}
src/Distribution/Compat/Prelude/Internal.hs view
@@ -7,8 +7,8 @@ -- for @Setup.hs@ scripts since its API is /not/ -- stable. module Distribution.Compat.Prelude.Internal- {-# WARNING "This modules' API is not stable. Use at your own risk, or better yet, use @base-compat@!" #-}- ( module Distribution.Compat.Prelude- ) where+ {-# WARNING "This modules' API is not stable. Use at your own risk, or better yet, use @base-compat@!" #-}+ ( module Distribution.Compat.Prelude+ ) where import Distribution.Compat.Prelude
src/Distribution/Compat/Process.hs view
@@ -1,22 +1,15 @@ {-# LANGUAGE CPP #-}-module Distribution.Compat.Process (- -- * Redefined functions- createProcess,- runInteractiveProcess,- rawSystem,- -- * Additions- enableProcessJobs,- ) where -import System.Exit (ExitCode (..))-import System.IO (Handle)+module Distribution.Compat.Process+ ( -- * Redefined functions+ proc -import System.Process (CreateProcess, ProcessHandle)-import qualified System.Process as Process+ -- * Additions+ , enableProcessJobs+ ) where -#if MIN_VERSION_process(1,2,0)-import System.Process (waitForProcess)-#endif+import System.Process (CreateProcess)+import qualified System.Process as Process #if defined(mingw32_HOST_OS) && MIN_VERSION_process(1,6,9) import System.IO.Unsafe (unsafePerformIO)@@ -48,7 +41,7 @@ -- On Windows 7 or before the jobs are disabled due to the fact that -- processes on these systems can only have one job. This prevents -- spawned process from assigning jobs to its own children. Suppose--- processs A spawns process B. The B process has a job assigned (call+-- process A spawns process B. The B process has a job assigned (call -- it J1) and when it tries to spawn a new process C the C -- automatically inherits the job. But at it also tries to assign a -- new job J2 to C since it doesn’t have access J1. This fails on@@ -64,40 +57,7 @@ -- process redefinitions ------------------------------------------------------------------------------- --- | 'System.Process.createProcess' with process jobs enabled when appropriate.--- See 'enableProcessJobs'.-createProcess :: CreateProcess- -> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)-createProcess = Process.createProcess . enableProcessJobs---- | 'System.Process.rawSystem' with process jobs enabled when appropriate.--- See 'enableProcessJobs'.-rawSystem :: String -> [String] -> IO ExitCode-rawSystem cmd args = do-#if MIN_VERSION_process(1,2,0)- (_,_,_,p) <- createProcess (Process.proc cmd args) { Process.delegate_ctlc = True }- waitForProcess p-#else- -- With very old 'process', just do its rawSystem- Process.rawSystem cmd args-#endif---- | 'System.Process.runInteractiveProcess' with process jobs enabled when--- appropriate. See 'enableProcessJobs'.-runInteractiveProcess- :: FilePath -- ^ Filename of the executable (see 'RawCommand' for details)- -> [String] -- ^ Arguments to pass to the executable- -> Maybe FilePath -- ^ Optional path to the working directory- -> Maybe [(String,String)] -- ^ Optional environment (otherwise inherit)- -> IO (Handle,Handle,Handle,ProcessHandle)-runInteractiveProcess cmd args mb_cwd mb_env = do- (mb_in, mb_out, mb_err, p) <-- createProcess (Process.proc cmd args)- { Process.std_in = Process.CreatePipe,- Process.std_out = Process.CreatePipe,- Process.std_err = Process.CreatePipe,- Process.env = mb_env,- Process.cwd = mb_cwd }- return (fromJust mb_in, fromJust mb_out, fromJust mb_err, p)- where- fromJust = maybe (error "runInteractiveProcess: fromJust") id+-- | 'System.Process.proc' with process jobs enabled when appropriate,+-- and defaulting 'delegate_ctlc' to 'True'.+proc :: FilePath -> [String] -> CreateProcess+proc path args = enableProcessJobs (Process.proc path args){Process.delegate_ctlc = True}
src/Distribution/Compat/ResponseFile.hs view
@@ -1,54 +1,27 @@-{-# LANGUAGE CPP, RankNTypes, FlexibleContexts #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-} -- Compatibility layer for GHC.ResponseFile -- Implementation from base 4.12.0 is used. -- http://hackage.haskell.org/package/base-4.12.0.0/src/LICENSE-module Distribution.Compat.ResponseFile (expandResponse) where+module Distribution.Compat.ResponseFile (expandResponse, escapeArgs) where import Distribution.Compat.Prelude++import GHC.ResponseFile (escapeArgs, unescapeArgs)+ import Prelude () import System.FilePath import System.IO (hPutStrLn, stderr) import System.IO.Error -#if MIN_VERSION_base(4,12,0)-import GHC.ResponseFile (unescapeArgs)-#else--unescapeArgs :: String -> [String]-unescapeArgs = filter (not . null) . unescape--data Quoting = NoneQ | SngQ | DblQ--unescape :: String -> [String]-unescape args = reverse . map reverse $ go args NoneQ False [] []- where- -- n.b., the order of these cases matters; these are cribbed from gcc- -- case 1: end of input- go [] _q _bs a as = a:as- -- case 2: back-slash escape in progress- go (c:cs) q True a as = go cs q False (c:a) as- -- case 3: no back-slash escape in progress, but got a back-slash- go (c:cs) q False a as- | '\\' == c = go cs q True a as- -- case 4: single-quote escaping in progress- go (c:cs) SngQ False a as- | '\'' == c = go cs NoneQ False a as- | otherwise = go cs SngQ False (c:a) as- -- case 5: double-quote escaping in progress- go (c:cs) DblQ False a as- | '"' == c = go cs NoneQ False a as- | otherwise = go cs DblQ False (c:a) as- -- case 6: no escaping is in progress- go (c:cs) NoneQ False a as- | isSpace c = go cs NoneQ False [] (a:as)- | '\'' == c = go cs SngQ False a as- | '"' == c = go cs DblQ False a as- | otherwise = go cs NoneQ False (c:a) as--#endif-+-- | The arg file / response file parser.+--+-- This is not a well-documented capability, and is a bit eccentric+-- (try @cabal \@foo \@bar@ to see what that does), but is crucial+-- for allowing complex arguments to cabal and cabal-install when+-- using command prompts with strongly-limited argument length. expandResponse :: [String] -> IO [String] expandResponse = go recursionLimit "." where@@ -56,11 +29,11 @@ go :: Int -> FilePath -> [String] -> IO [String] go n dir- | n >= 0 = fmap concat . traverse (expand n dir)+ | n >= 0 = fmap concat . traverse (expand n dir) | otherwise = const $ hPutStrLn stderr "Error: response file recursion limit exceeded." >> exitFailure expand :: Int -> FilePath -> String -> IO [String]- expand n dir arg@('@':f) = readRecursively n (dir </> f) `catchIOError` (const $ print "?" >> return [arg])+ expand n dir arg@('@' : f) = readRecursively n (dir </> f) `catchIOError` const (print "?" >> return [arg]) expand _n _dir x = return [x] readRecursively :: Int -> FilePath -> IO [String]
− src/Distribution/Compat/Semigroup.hs
@@ -1,119 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE TypeOperators #-}---- | Compatibility layer for "Data.Semigroup"-module Distribution.Compat.Semigroup- ( Semigroup((<>))- , Mon.Monoid(..)- , All(..)- , Any(..)-- , First'(..)- , Last'(..)-- , Option'(..)-- , gmappend- , gmempty- ) where--import Distribution.Compat.Binary (Binary)-import Distribution.Utils.Structured (Structured)-import Data.Typeable (Typeable)--import GHC.Generics--- Data.Semigroup is available since GHC 8.0/base-4.9 in `base`--- for older GHC/base, it's provided by `semigroups`-import Data.Semigroup-import qualified Data.Monoid as Mon----- | A copy of 'Data.Semigroup.First'.-newtype First' a = First' { getFirst' :: a }- deriving (Eq, Ord, Show)--instance Semigroup (First' a) where- a <> _ = a---- | A copy of 'Data.Semigroup.Last'.-newtype Last' a = Last' { getLast' :: a }- deriving (Eq, Ord, Read, Show, Generic, Binary, Typeable)--instance Structured a => Structured (Last' a)--instance Semigroup (Last' a) where- _ <> b = b--instance Functor Last' where- fmap f (Last' x) = Last' (f x)---- | A wrapper around 'Maybe', providing the 'Semigroup' and 'Monoid' instances--- implemented for 'Maybe' since @base-4.11@.-newtype Option' a = Option' { getOption' :: Maybe a }- deriving (Eq, Ord, Read, Show, Binary, Generic, Functor, Typeable)--instance Structured a => Structured (Option' a)--instance Semigroup a => Semigroup (Option' a) where- Option' (Just a) <> Option' (Just b) = Option' (Just (a <> b))- Option' Nothing <> b = b- a <> Option' Nothing = a--instance Semigroup a => Monoid (Option' a) where- mempty = Option' Nothing- mappend = (<>)-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Stolen from Edward Kmett's BSD3-licensed `semigroups` package---- | Generically generate a 'Semigroup' ('<>') operation for any type--- implementing 'Generic'. This operation will append two values--- by point-wise appending their component fields. It is only defined--- for product types.------ @--- 'gmappend' a ('gmappend' b c) = 'gmappend' ('gmappend' a b) c--- @-gmappend :: (Generic a, GSemigroup (Rep a)) => a -> a -> a-gmappend x y = to (gmappend' (from x) (from y))--class GSemigroup f where- gmappend' :: f p -> f p -> f p--instance Semigroup a => GSemigroup (K1 i a) where- gmappend' (K1 x) (K1 y) = K1 (x <> y)--instance GSemigroup f => GSemigroup (M1 i c f) where- gmappend' (M1 x) (M1 y) = M1 (gmappend' x y)--instance (GSemigroup f, GSemigroup g) => GSemigroup (f :*: g) where- gmappend' (x1 :*: x2) (y1 :*: y2) = gmappend' x1 y1 :*: gmappend' x2 y2---- | Generically generate a 'Monoid' 'mempty' for any product-like type--- implementing 'Generic'.------ It is only defined for product types.------ @--- 'gmappend' 'gmempty' a = a = 'gmappend' a 'gmempty'--- @--gmempty :: (Generic a, GMonoid (Rep a)) => a-gmempty = to gmempty'--class GSemigroup f => GMonoid f where- gmempty' :: f p--instance (Semigroup a, Monoid a) => GMonoid (K1 i a) where- gmempty' = K1 mempty--instance GMonoid f => GMonoid (M1 i c f) where- gmempty' = M1 gmempty'--instance (GMonoid f, GMonoid g) => GMonoid (f :*: g) where- gmempty' = gmempty' :*: gmempty'
src/Distribution/Compat/SnocList.hs view
@@ -1,4 +1,5 @@ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Compat.SnocList -- License : BSD3@@ -8,14 +9,14 @@ -- Portability : portable -- -- A very reversed list. Has efficient `snoc`-module Distribution.Compat.SnocList (- SnocList,- runSnocList,- snoc,-) where+module Distribution.Compat.SnocList+ ( SnocList+ , runSnocList+ , snoc+ ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () newtype SnocList a = SnocList [a] @@ -26,8 +27,8 @@ runSnocList (SnocList xs) = reverse xs instance Semigroup (SnocList a) where- SnocList xs <> SnocList ys = SnocList (ys <> xs)+ SnocList xs <> SnocList ys = SnocList (ys <> xs) instance Monoid (SnocList a) where- mempty = SnocList []- mappend = (<>)+ mempty = SnocList []+ mappend = (<>)
src/Distribution/Compat/Stack.hs view
@@ -1,102 +1,42 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE RankNTypes #-} {-# LANGUAGE ImplicitParams #-}-module Distribution.Compat.Stack (- WithCallStack,- CallStack,- annotateCallStackIO,- withFrozenCallStack,- withLexicalCallStack,- callStack,- prettyCallStack,- parentSrcLocPrefix-) where--import System.IO.Error+{-# LANGUAGE RankNTypes #-} -#ifdef MIN_VERSION_base-#if MIN_VERSION_base(4,8,1)-#define GHC_STACK_SUPPORTED 1-#endif-#endif+module Distribution.Compat.Stack+ ( WithCallStack+ , CallStack+ , annotateCallStackIO+ , withFrozenCallStack+ , withLexicalCallStack+ , callStack+ , prettyCallStack+ , parentSrcLocPrefix+ ) where -#ifdef GHC_STACK_SUPPORTED import GHC.Stack-#endif--#ifdef GHC_STACK_SUPPORTED+import System.IO.Error -#if MIN_VERSION_base(4,9,0) type WithCallStack a = HasCallStack => a-#elif MIN_VERSION_base(4,8,1)-type WithCallStack a = (?callStack :: CallStack) => a-#endif -#if !MIN_VERSION_base(4,9,0)--- NB: Can't say WithCallStack (WithCallStack a -> a);--- Haskell doesn't support this kind of implicit parameter!--- See https://mail.haskell.org/pipermail/ghc-devs/2016-January/011096.html--- Since this function doesn't do anything, it's OK to--- give it a less good type.-withFrozenCallStack :: WithCallStack (a -> a)-withFrozenCallStack x = x--callStack :: (?callStack :: CallStack) => CallStack-callStack = ?callStack--prettyCallStack :: CallStack -> String-prettyCallStack = showCallStack-#endif- -- | Give the *parent* of the person who invoked this; -- so it's most suitable for being called from a utility function. -- You probably want to call this using 'withFrozenCallStack'; otherwise -- it's not very useful. We didn't implement this for base-4.8.1 -- because we cannot rely on freezing to have taken place.--- parentSrcLocPrefix :: WithCallStack String-#if MIN_VERSION_base(4,9,0) parentSrcLocPrefix = case getCallStack callStack of- (_:(_, loc):_) -> showLoc loc+ (_ : (_, loc) : _) -> showLoc loc [(_, loc)] -> showLoc loc [] -> error "parentSrcLocPrefix: empty call stack"- where- showLoc loc =- srcLocFile loc ++ ":" ++ show (srcLocStartLine loc) ++ ": "-#else-parentSrcLocPrefix = "Call sites not available with base < 4.9.0.0 (GHC 8.0): "-#endif+ where+ showLoc loc =+ srcLocFile loc ++ ":" ++ show (srcLocStartLine loc) ++ ": " -- Yeah, this uses skivvy implementation details. withLexicalCallStack :: (a -> WithCallStack (IO b)) -> WithCallStack (a -> IO b) withLexicalCallStack f =- let stk = ?callStack- in \x -> let ?callStack = stk in f x--#else--data CallStack = CallStack- deriving (Eq, Show)--type WithCallStack a = a--withFrozenCallStack :: a -> a-withFrozenCallStack x = x--callStack :: CallStack-callStack = CallStack--prettyCallStack :: CallStack -> String-prettyCallStack _ = "Call stacks not available with base < 4.8.1.0 (GHC 7.10)"--parentSrcLocPrefix :: String-parentSrcLocPrefix = "Call sites not available with base < 4.9.0.0 (GHC 8.0): "--withLexicalCallStack :: (a -> IO b) -> a -> IO b-withLexicalCallStack f = f--#endif+ let stk = ?callStack+ in \x -> let ?callStack = stk in f x -- | This function is for when you *really* want to add a call -- stack to raised IO, but you don't have a@@ -106,8 +46,9 @@ annotateCallStackIO :: WithCallStack (IO a -> IO a) annotateCallStackIO = modifyIOError f where- f ioe = ioeSetErrorString ioe- . wrapCallStack- $ ioeGetErrorString ioe+ f ioe =+ ioeSetErrorString ioe+ . wrapCallStack+ $ ioeGetErrorString ioe wrapCallStack s =- prettyCallStack callStack ++ "\n" ++ s+ prettyCallStack callStack ++ "\n" ++ s
src/Distribution/Compat/Time.hs view
@@ -1,42 +1,39 @@ {-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE RankNTypes #-}-{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-} module Distribution.Compat.Time- ( ModTime(..) -- Needed for testing- , getModTime, getFileAge, getCurTime- , posixSecondsToModTime- , calibrateMtimeChangeDelay )- where+ ( ModTime (..) -- Needed for testing+ , getModTime+ , getFileAge+ , getCurTime+ , posixSecondsToModTime+ , calibrateMtimeChangeDelay+ )+where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -import System.Directory ( getModificationTime )+import System.Directory (getModificationTime) -import Distribution.Simple.Utils ( withTempDirectory )-import Distribution.Verbosity ( silent )+import Distribution.Simple.Utils (withTempDirectoryCwd)+import Distribution.Utils.Path (getSymbolicPath, sameDirectory)+import Distribution.Verbosity (silent) import System.FilePath -import Data.Time.Clock.POSIX ( POSIXTime, getPOSIXTime )-import Data.Time ( diffUTCTime, getCurrentTime )-import Data.Time.Clock.POSIX ( posixDayLength )-+import Data.Time (diffUTCTime, getCurrentTime)+import Data.Time.Clock.POSIX (POSIXTime, getPOSIXTime, posixDayLength) #if defined mingw32_HOST_OS import qualified Prelude import Data.Bits ((.|.), unsafeShiftL)-#if MIN_VERSION_base(4,7,0) import Data.Bits (finiteBitSize)-#else-import Data.Bits (bitSize)-#endif import Foreign ( allocaBytes, peekByteOff ) import System.IO.Error ( mkIOError, doesNotExistErrorType )@@ -44,20 +41,21 @@ #else -import System.Posix.Files ( FileStatus, getFileStatus )-+import System.Posix.Files+ ( FileStatus, getFileStatus #if MIN_VERSION_unix(2,6,0)-import System.Posix.Files ( modificationTimeHiRes )+ , modificationTimeHiRes #else-import System.Posix.Files ( modificationTime )+ , modificationTime #endif+ ) #endif -- | An opaque type representing a file's modification time, represented -- internally as a 64-bit unsigned integer in the Windows UTC format. newtype ModTime = ModTime Word64- deriving (Binary, Generic, Bounded, Eq, Ord, Typeable)+ deriving (Binary, Generic, Bounded, Eq, Ord) instance Structured ModTime @@ -90,18 +88,13 @@ index_WIN32_FILE_ATTRIBUTE_DATA_ftLastWriteTime_dwLowDateTime dwHigh <- peekByteOff info index_WIN32_FILE_ATTRIBUTE_DATA_ftLastWriteTime_dwHighDateTime-#if MIN_VERSION_base(4,7,0) let qwTime = (fromIntegral (dwHigh :: DWORD) `unsafeShiftL` finiteBitSize dwHigh) .|. (fromIntegral (dwLow :: DWORD))-#else- let qwTime =- (fromIntegral (dwHigh :: DWORD) `unsafeShiftL` bitSize dwHigh)- .|. (fromIntegral (dwLow :: DWORD))-#endif return $! ModTime (qwTime :: Word64) -#ifdef x86_64_HOST_ARCH+{- FOURMOLU_DISABLE -}+#if defined(x86_64_HOST_ARCH) || defined(aarch64_HOST_ARCH) #define CALLCONV ccall #else #define CALLCONV stdcall@@ -138,9 +131,10 @@ extractFileTime x = posixTimeToModTime (modificationTimeHiRes x) #endif+{- FOURMOLU_ENABLE -} windowsTick, secToUnixEpoch :: Word64-windowsTick = 10000000+windowsTick = 10000000 secToUnixEpoch = 11644473600 -- | Convert POSIX seconds to ModTime.@@ -150,8 +144,10 @@ -- | Convert 'POSIXTime' to 'ModTime'. posixTimeToModTime :: POSIXTime -> ModTime-posixTimeToModTime p = ModTime $ (ceiling $ p * 1e7) -- 100 ns precision- + (secToUnixEpoch * windowsTick)+posixTimeToModTime p =+ ModTime $+ ceiling (p * 1e7) -- 100 ns precision+ + (secToUnixEpoch * windowsTick) -- | Return age of given file in days. getFileAge :: FilePath -> IO Double@@ -172,17 +168,17 @@ -- than 10 ms, but never larger than 1 second. calibrateMtimeChangeDelay :: IO (Int, Int) calibrateMtimeChangeDelay =- withTempDirectory silent "." "calibration-" $ \dir -> do- let fileName = dir </> "probe"- mtimes <- for [1..25] $ \(i::Int) -> time $ do+ withTempDirectoryCwd silent Nothing sameDirectory "calibration-" $ \dir -> do+ let fileName = getSymbolicPath dir </> "probe"+ mtimes <- for [1 .. 25] $ \(i :: Int) -> time $ do writeFile fileName $ show i t0 <- getModTime fileName let spin j = do- writeFile fileName $ show (i,j)+ writeFile fileName $ show (i, j) t1 <- getModTime fileName unless (t0 < t1) (spin $ j + 1)- spin (0::Int)- let mtimeChange = maximum mtimes+ spin (0 :: Int)+ let mtimeChange = maximum mtimes mtimeChange' = min 1000000 $ (max 10000 mtimeChange) * 2 return (mtimeChange, mtimeChange') where
− src/Distribution/Compat/Typeable.hs
@@ -1,18 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE ScopedTypeVariables #-}-module Distribution.Compat.Typeable (- Typeable,- TypeRep,- typeRep,- ) where--#if MIN_VERSION_base(4,7,0)-import Data.Typeable (Typeable, TypeRep, typeRep)-#else-import Data.Typeable (Typeable, TypeRep, typeOf)-#endif--#if !MIN_VERSION_base(4,7,0)-typeRep :: forall a proxy. Typeable a => proxy a -> TypeRep-typeRep _ = typeOf (undefined :: a)-#endif
− src/Distribution/Compiler.hs
@@ -1,223 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE DeriveFoldable #-}-{-# LANGUAGE DeriveTraversable #-}---------------------------------------------------------------------------------- |--- Module : Distribution.Compiler--- Copyright : Isaac Jones 2003-2004--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ This has an enumeration of the various compilers that Cabal knows about. It--- also specifies the default compiler. Sadly you'll often see code that does--- case analysis on this compiler flavour enumeration like:------ > case compilerFlavor comp of--- > GHC -> GHC.getInstalledPackages verbosity packageDb progdb------ Obviously it would be better to use the proper 'Compiler' abstraction--- because that would keep all the compiler-specific code together.--- Unfortunately we cannot make this change yet without breaking the--- 'UserHooks' api, which would break all custom @Setup.hs@ files, so for the--- moment we just have to live with this deficiency. If you're interested, see--- ticket #57.--module Distribution.Compiler (- -- * Compiler flavor- CompilerFlavor(..),- buildCompilerId,- buildCompilerFlavor,- defaultCompilerFlavor,- classifyCompilerFlavor,- knownCompilerFlavors,-- -- * Per compiler flavor- PerCompilerFlavor (..),- perCompilerFlavorToList,-- -- * Compiler id- CompilerId(..),-- -- * Compiler info- CompilerInfo(..),- unknownCompilerInfo,- AbiTag(..), abiTagString- ) where--import Prelude ()-import Distribution.Compat.Prelude--import Language.Haskell.Extension--import Distribution.Version (Version, mkVersion', nullVersion)--import qualified System.Info (compilerName, compilerVersion)-import Distribution.Parsec (Parsec (..))-import Distribution.Pretty (Pretty (..), prettyShow)-import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp--data CompilerFlavor =- GHC | GHCJS | NHC | YHC | Hugs | HBC | Helium | JHC | LHC | UHC | Eta- | HaskellSuite String -- string is the id of the actual compiler- | OtherCompiler String- deriving (Generic, Show, Read, Eq, Ord, Typeable, Data)--instance Binary CompilerFlavor-instance Structured CompilerFlavor-instance NFData CompilerFlavor where rnf = genericRnf--knownCompilerFlavors :: [CompilerFlavor]-knownCompilerFlavors =- [GHC, GHCJS, NHC, YHC, Hugs, HBC, Helium, JHC, LHC, UHC, Eta]--instance Pretty CompilerFlavor where- pretty (OtherCompiler name) = Disp.text name- pretty (HaskellSuite name) = Disp.text name- pretty NHC = Disp.text "nhc98"- pretty other = Disp.text (lowercase (show other))--instance Parsec CompilerFlavor where- parsec = classifyCompilerFlavor <$> component- where- component = do- cs <- P.munch1 isAlphaNum- if all isDigit cs then fail "all digits compiler name" else return cs--classifyCompilerFlavor :: String -> CompilerFlavor-classifyCompilerFlavor s =- fromMaybe (OtherCompiler s) $ lookup (lowercase s) compilerMap- where- compilerMap = [ (lowercase (prettyShow compiler), compiler)- | compiler <- knownCompilerFlavors ]--buildCompilerFlavor :: CompilerFlavor-buildCompilerFlavor = classifyCompilerFlavor System.Info.compilerName--buildCompilerVersion :: Version-buildCompilerVersion = mkVersion' System.Info.compilerVersion--buildCompilerId :: CompilerId-buildCompilerId = CompilerId buildCompilerFlavor buildCompilerVersion---- | The default compiler flavour to pick when compiling stuff. This defaults--- to the compiler used to build the Cabal lib.------ However if it's not a recognised compiler then it's 'Nothing' and the user--- will have to specify which compiler they want.----defaultCompilerFlavor :: Maybe CompilerFlavor-defaultCompilerFlavor = case buildCompilerFlavor of- OtherCompiler _ -> Nothing- _ -> Just buildCompilerFlavor------------------------------------------------------------------------------------ Per compiler data------------------------------------------------------------------------------------ | 'PerCompilerFlavor' carries only info per GHC and GHCJS------ Cabal parses only @ghc-options@ and @ghcjs-options@, others are omitted.----data PerCompilerFlavor v = PerCompilerFlavor v v- deriving (Generic, Show, Read, Eq, Typeable, Data, Functor, Foldable- , Traversable)--instance Binary a => Binary (PerCompilerFlavor a)-instance Structured a => Structured (PerCompilerFlavor a)-instance NFData a => NFData (PerCompilerFlavor a)--perCompilerFlavorToList :: PerCompilerFlavor v -> [(CompilerFlavor, v)]-perCompilerFlavorToList (PerCompilerFlavor a b) = [(GHC, a), (GHCJS, b)]--instance Semigroup a => Semigroup (PerCompilerFlavor a) where- PerCompilerFlavor a b <> PerCompilerFlavor a' b' = PerCompilerFlavor- (a <> a') (b <> b')--instance (Semigroup a, Monoid a) => Monoid (PerCompilerFlavor a) where- mempty = PerCompilerFlavor mempty mempty- mappend = (<>)---- --------------------------------------------------------------- * Compiler Id--- --------------------------------------------------------------data CompilerId = CompilerId CompilerFlavor Version- deriving (Eq, Generic, Ord, Read, Show, Typeable)--instance Binary CompilerId-instance Structured CompilerId-instance NFData CompilerId where rnf = genericRnf--instance Pretty CompilerId where- pretty (CompilerId f v)- | v == nullVersion = pretty f- | otherwise = pretty f <<>> Disp.char '-' <<>> pretty v--instance Parsec CompilerId where- parsec = do- flavour <- parsec- version <- (P.char '-' >> parsec) <|> return nullVersion- return (CompilerId flavour version)--lowercase :: String -> String-lowercase = map toLower---- --------------------------------------------------------------- * Compiler Info--- ---------------------------------------------------------------- | Compiler information used for resolving configurations. Some--- fields can be set to Nothing to indicate that the information is--- unknown.--data CompilerInfo = CompilerInfo {- compilerInfoId :: CompilerId,- -- ^ Compiler flavour and version.- compilerInfoAbiTag :: AbiTag,- -- ^ Tag for distinguishing incompatible ABI's on the same- -- architecture/os.- compilerInfoCompat :: Maybe [CompilerId],- -- ^ Other implementations that this compiler claims to be- -- compatible with, if known.- compilerInfoLanguages :: Maybe [Language],- -- ^ Supported language standards, if known.- compilerInfoExtensions :: Maybe [Extension]- -- ^ Supported extensions, if known.- }- deriving (Generic, Show, Read)--instance Binary CompilerInfo--data AbiTag- = NoAbiTag- | AbiTag String- deriving (Eq, Generic, Show, Read, Typeable)--instance Binary AbiTag-instance Structured AbiTag--instance Pretty AbiTag where- pretty NoAbiTag = Disp.empty- pretty (AbiTag tag) = Disp.text tag--instance Parsec AbiTag where- parsec = do- tag <- P.munch (\c -> isAlphaNum c || c == '_')- if null tag then return NoAbiTag else return (AbiTag tag)--abiTagString :: AbiTag -> String-abiTagString NoAbiTag = ""-abiTagString (AbiTag tag) = tag---- | Make a CompilerInfo of which only the known information is its CompilerId,--- its AbiTag and that it does not claim to be compatible with other--- compiler id's.-unknownCompilerInfo :: CompilerId -> AbiTag -> CompilerInfo-unknownCompilerInfo compilerId abiTag =- CompilerInfo compilerId abiTag (Just []) Nothing Nothing
− src/Distribution/FieldGrammar.hs
@@ -1,88 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE ScopedTypeVariables #-}--- | This module provides a way to specify a grammar of @.cabal@ -like files.-module Distribution.FieldGrammar (- -- * Field grammar type- FieldGrammar (..),- uniqueField,- optionalField,- optionalFieldDef,- monoidalField,- -- * Concrete grammar implementations- ParsecFieldGrammar,- ParsecFieldGrammar',- parseFieldGrammar,- fieldGrammarKnownFieldList,- PrettyFieldGrammar,- PrettyFieldGrammar',- prettyFieldGrammar,- -- * Auxlilary- (^^^),- Section(..),- Fields,- partitionFields,- takeFields,- runFieldParser,- runFieldParser',- defaultFreeTextFieldDefST,- -- * Newtypes- module Distribution.FieldGrammar.Newtypes,- ) where--import Distribution.Compat.Prelude-import Prelude ()--import qualified Data.Map.Strict as Map--import Distribution.FieldGrammar.Class-import Distribution.FieldGrammar.Newtypes-import Distribution.FieldGrammar.Parsec-import Distribution.FieldGrammar.Pretty-import Distribution.Fields.Field-import Distribution.Utils.Generic (spanMaybe)--type ParsecFieldGrammar' a = ParsecFieldGrammar a a-type PrettyFieldGrammar' a = PrettyFieldGrammar a a--infixl 5 ^^^---- | Reverse function application which binds tighter than '<$>' and '<*>'.--- Useful for refining grammar specification.------ @--- \<*\> 'monoidalFieldAla' "extensions" (alaList' FSep MQuoted) oldExtensions--- ^^^ 'deprecatedSince' [1,12] "Please use 'default-extensions' or 'other-extensions' fields."--- @-(^^^) :: a -> (a -> b) -> b-x ^^^ f = f x---- | Partitioning state-data PS ann = PS (Fields ann) [Section ann] [[Section ann]]---- | Partition field list into field map and groups of sections.-partitionFields :: [Field ann] -> (Fields ann, [[Section ann]])-partitionFields = finalize . foldl' f (PS mempty mempty mempty)- where- finalize :: PS ann -> (Fields ann, [[Section ann]])- finalize (PS fs s ss)- | null s = (fs, reverse ss)- | otherwise = (fs, reverse (reverse s : ss))-- f :: PS ann -> Field ann -> PS ann- f (PS fs s ss) (Field (Name ann name) fss) =- PS (Map.insertWith (flip (++)) name [MkNamelessField ann fss] fs) [] ss'- where- ss' | null s = ss- | otherwise = reverse s : ss- f (PS fs s ss) (Section name sargs sfields) =- PS fs (MkSection name sargs sfields : s) ss---- | Take all fields from the front.-takeFields :: [Field ann] -> (Fields ann, [Field ann])-takeFields = finalize . spanMaybe match- where- finalize (fs, rest) = (Map.fromListWith (flip (++)) fs, rest)-- match (Field (Name ann name) fs) = Just (name, [MkNamelessField ann fs])- match _ = Nothing
− src/Distribution/FieldGrammar/Class.hs
@@ -1,205 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ScopedTypeVariables #-}-#if __GLASGOW_HASKELL__ >= 800-{-# LANGUAGE UndecidableSuperClasses #-}-#else-{-# LANGUAGE UndecidableInstances #-}-#endif-module Distribution.FieldGrammar.Class (- FieldGrammar (..),- uniqueField,- optionalField,- optionalFieldDef,- monoidalField,- defaultFreeTextFieldDefST,-) where--import Distribution.Compat.Lens-import Distribution.Compat.Prelude-import Prelude ()--import Distribution.CabalSpecVersion (CabalSpecVersion)-import Distribution.Compat.Newtype (Newtype)-import Distribution.FieldGrammar.Newtypes-import Distribution.Fields.Field-import Distribution.Utils.ShortText---- | 'FieldGrammar' is parametrised by------ * @s@ which is a structure we are parsing. We need this to provide prettyprinter--- functionality------ * @a@ type of the field.------ /Note:/ We'd like to have @forall s. Applicative (f s)@ context.----class- ( c SpecVersion, c TestedWith, c SpecLicense, c Token, c Token', c FilePathNT- )- => FieldGrammar c g | g -> c- where- -- | Unfocus, zoom out, /blur/ 'FieldGrammar'.- blurFieldGrammar :: ALens' a b -> g b d -> g a d-- -- | Field which should be defined, exactly once.- uniqueFieldAla- :: (c b, Newtype a b)- => FieldName -- ^ field name- -> (a -> b) -- ^ 'Newtype' pack- -> ALens' s a -- ^ lens into the field- -> g s a-- -- | Boolean field with a default value.- booleanFieldDef- :: FieldName -- ^ field name- -> ALens' s Bool -- ^ lens into the field- -> Bool -- ^ default- -> g s Bool-- -- | Optional field.- optionalFieldAla- :: (c b, Newtype a b)- => FieldName -- ^ field name- -> (a -> b) -- ^ 'pack'- -> ALens' s (Maybe a) -- ^ lens into the field- -> g s (Maybe a)-- -- | Optional field with default value.- optionalFieldDefAla- :: (c b, Newtype a b, Eq a)- => FieldName -- ^ field name- -> (a -> b) -- ^ 'Newtype' pack- -> ALens' s a -- ^ @'Lens'' s a@: lens into the field- -> a -- ^ default value- -> g s a-- -- | Free text field is essentially 'optionalFieldDefAla` with @""@- -- as the default and "accept everything" parser.- --- -- @since 3.0.0.0- freeTextField- :: FieldName- -> ALens' s (Maybe String) -- ^ lens into the field- -> g s (Maybe String)-- -- | Free text field is essentially 'optionalFieldDefAla` with @""@- -- as the default and "accept everything" parser.- --- -- @since 3.0.0.0- freeTextFieldDef- :: FieldName- -> ALens' s String -- ^ lens into the field- -> g s String-- -- | @since 3.2.0.0- freeTextFieldDefST- :: FieldName- -> ALens' s ShortText -- ^ lens into the field- -> g s ShortText-- -- | Monoidal field.- --- -- Values are combined with 'mappend'.- --- -- /Note:/ 'optionalFieldAla' is a @monoidalField@ with 'Last' monoid.- --- monoidalFieldAla- :: (c b, Monoid a, Newtype a b)- => FieldName -- ^ field name- -> (a -> b) -- ^ 'pack'- -> ALens' s a -- ^ lens into the field- -> g s a-- -- | Parser matching all fields with a name starting with a prefix.- prefixedFields- :: FieldName -- ^ field name prefix- -> ALens' s [(String, String)] -- ^ lens into the field- -> g s [(String, String)]-- -- | Known field, which we don't parse, nor pretty print.- knownField :: FieldName -> g s ()-- -- | Field which is parsed but not pretty printed.- hiddenField :: g s a -> g s a-- -- | Deprecated since- deprecatedSince- :: CabalSpecVersion -- ^ version- -> String -- ^ deprecation message- -> g s a- -> g s a-- -- | Removed in. If we encounter removed field, parsing fails.- removedIn- :: CabalSpecVersion -- ^ version- -> String -- ^ removal message- -> g s a- -> g s a-- -- | Annotate field with since spec-version.- availableSince- :: CabalSpecVersion -- ^ spec version- -> a -- ^ default value- -> g s a- -> g s a-- -- | Annotate field with since spec-version.- -- This is used to recognise, but warn about the field.- -- It is used to process @other-extensions@ field.- --- -- Default implementation is to not warn.- --- -- @since 3.4.0.0- availableSinceWarn- :: CabalSpecVersion -- ^ spec version- -> g s a- -> g s a- availableSinceWarn _ = id---- | Field which can be defined at most once.-uniqueField- :: (FieldGrammar c g, c (Identity a))- => FieldName -- ^ field name- -> ALens' s a -- ^ lens into the field- -> g s a-uniqueField fn l = uniqueFieldAla fn Identity l---- | Field which can be defined at most once.-optionalField- :: (FieldGrammar c g, c (Identity a))- => FieldName -- ^ field name- -> ALens' s (Maybe a) -- ^ lens into the field- -> g s (Maybe a)-optionalField fn l = optionalFieldAla fn Identity l---- | Optional field with default value.-optionalFieldDef- :: (FieldGrammar c g, Functor (g s), c (Identity a), Eq a)- => FieldName -- ^ field name- -> ALens' s a -- ^ @'Lens'' s a@: lens into the field- -> a -- ^ default value- -> g s a-optionalFieldDef fn l x = optionalFieldDefAla fn Identity l x---- | Field which can be define multiple times, and the results are @mappend@ed.-monoidalField- :: (FieldGrammar c g, c (Identity a), Monoid a)- => FieldName -- ^ field name- -> ALens' s a -- ^ lens into the field- -> g s a-monoidalField fn l = monoidalFieldAla fn Identity l---- | Default implementation for 'freeTextFieldDefST'.-defaultFreeTextFieldDefST- :: (Functor (g s), FieldGrammar c g)- => FieldName- -> ALens' s ShortText -- ^ lens into the field- -> g s ShortText-defaultFreeTextFieldDefST fn l =- toShortText <$> freeTextFieldDef fn (cloneLens l . st)- where- st :: Lens' ShortText String- st f s = toShortText <$> f (fromShortText s)
− src/Distribution/FieldGrammar/FieldDescrs.hs
@@ -1,121 +0,0 @@-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE UndecidableInstances #-}-module Distribution.FieldGrammar.FieldDescrs (- FieldDescrs,- fieldDescrPretty,- fieldDescrParse,- fieldDescrsToList,- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Data.List (dropWhileEnd)-import Distribution.Compat.Lens (aview, cloneLens)-import Distribution.Compat.Newtype-import Distribution.FieldGrammar-import Distribution.Pretty (Pretty (..), showFreeText)--import qualified Data.Map as Map-import qualified Distribution.Compat.CharParsing as C-import qualified Distribution.Fields.Field as P-import qualified Distribution.Parsec as P-import qualified Text.PrettyPrint as Disp---- strict pair-data SP s = SP- { pPretty :: !(s -> Disp.Doc)- , pParse :: !(forall m. P.CabalParsing m => s -> m s)- }---- | A collection of field parsers and pretty-printers.-newtype FieldDescrs s a = F { runF :: Map P.FieldName (SP s) }- deriving (Functor)--instance Applicative (FieldDescrs s) where- pure _ = F mempty- f <*> x = F (mappend (runF f) (runF x))--singletonF :: P.FieldName -> (s -> Disp.Doc) -> (forall m. P.CabalParsing m => s -> m s) -> FieldDescrs s a-singletonF fn f g = F $ Map.singleton fn (SP f g)---- | Lookup a field value pretty-printer.-fieldDescrPretty :: FieldDescrs s a -> P.FieldName -> Maybe (s -> Disp.Doc)-fieldDescrPretty (F m) fn = pPretty <$> Map.lookup fn m---- | Lookup a field value parser.-fieldDescrParse :: P.CabalParsing m => FieldDescrs s a -> P.FieldName -> Maybe (s -> m s)-fieldDescrParse (F m) fn = (\f -> pParse f) <$> Map.lookup fn m--fieldDescrsToList- :: P.CabalParsing m- => FieldDescrs s a- -> [(P.FieldName, s -> Disp.Doc, s -> m s)]-fieldDescrsToList = map mk . Map.toList . runF where- mk (name, SP ppr parse) = (name, ppr, parse)---- | /Note:/ default values are printed.-instance FieldGrammar ParsecPretty FieldDescrs where- blurFieldGrammar l (F m) = F (fmap blur m) where- blur (SP f g) = SP (f . aview l) (cloneLens l g)-- booleanFieldDef fn l _def = singletonF fn f g where- f s = Disp.text (show (aview l s))- g s = cloneLens l (const P.parsec) s- -- Note: eta expansion is needed for RankNTypes type-checking to work.-- uniqueFieldAla fn _pack l = singletonF fn f g where- f s = pretty (pack' _pack (aview l s))- g s = cloneLens l (const (unpack' _pack <$> P.parsec)) s-- optionalFieldAla fn _pack l = singletonF fn f g where- f s = maybe mempty (pretty . pack' _pack) (aview l s)- g s = cloneLens l (const (Just . unpack' _pack <$> P.parsec)) s-- optionalFieldDefAla fn _pack l _def = singletonF fn f g where- f s = pretty (pack' _pack (aview l s))- g s = cloneLens l (const (unpack' _pack <$> P.parsec)) s-- freeTextField fn l = singletonF fn f g where- f s = maybe mempty showFreeText (aview l s)- g s = cloneLens l (const (Just <$> parsecFreeText)) s-- freeTextFieldDef fn l = singletonF fn f g where- f s = showFreeText (aview l s)- g s = cloneLens l (const parsecFreeText) s-- freeTextFieldDefST = defaultFreeTextFieldDefST-- monoidalFieldAla fn _pack l = singletonF fn f g where- f s = pretty (pack' _pack (aview l s))- g s = cloneLens l (\x -> mappend x . unpack' _pack <$> P.parsec) s-- prefixedFields _fnPfx _l = F mempty- knownField _ = pure ()- deprecatedSince _ _ x = x- removedIn _ _ x = x- availableSince _ _ = id- hiddenField _ = F mempty--parsecFreeText :: P.CabalParsing m => m String-parsecFreeText = dropDotLines <$ C.spaces <*> many C.anyChar- where- -- Example package with dot lines- -- http://hackage.haskell.org/package/copilot-cbmc-0.1/copilot-cbmc.cabal- dropDotLines "." = "."- dropDotLines x = intercalate "\n" . map dotToEmpty . lines $ x-- dotToEmpty x | trim' x == "." = ""- dotToEmpty x = trim x-- trim' :: String -> String- trim' = dropWhileEnd (`elem` (" \t" :: String))-- trim :: String -> String- trim = dropWhile isSpace . dropWhileEnd isSpace--class (P.Parsec a, Pretty a) => ParsecPretty a-instance (P.Parsec a, Pretty a) => ParsecPretty a
− src/Distribution/FieldGrammar/Newtypes.hs
@@ -1,392 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE FunctionalDependencies #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ScopedTypeVariables #-}--- | This module provides @newtype@ wrappers to be used with "Distribution.FieldGrammar".-module Distribution.FieldGrammar.Newtypes (- -- * List- alaList,- alaList',- -- ** Modifiers- CommaVCat (..),- CommaFSep (..),- VCat (..),- FSep (..),- NoCommaFSep (..),- Sep (..),- -- ** Type- List,- -- ** Set- alaSet,- alaSet',- Set',- -- ** NonEmpty- alaNonEmpty,- alaNonEmpty',- NonEmpty',- -- * Version & License- SpecVersion (..),- TestedWith (..),- SpecLicense (..),- -- * Identifiers- Token (..),- Token' (..),- MQuoted (..),- FilePathNT (..),- ) where--import Distribution.Compat.Newtype-import Distribution.Compat.Prelude-import Prelude ()--import Distribution.CabalSpecVersion-import Distribution.Compiler (CompilerFlavor)-import Distribution.License (License)-import Distribution.Parsec-import Distribution.Pretty-import Distribution.Version- (LowerBound (..), Version, VersionInterval (..), VersionRange, VersionRangeF (..), anyVersion, asVersionIntervals, cataVersionRange, mkVersion,- version0, versionNumbers)-import Text.PrettyPrint (Doc, comma, fsep, punctuate, text, vcat)--import qualified Data.List.NonEmpty as NE-import qualified Data.Set as Set-import qualified Distribution.Compat.CharParsing as P-import qualified Distribution.SPDX as SPDX---- | Vertical list with commas. Displayed with 'vcat'-data CommaVCat = CommaVCat---- | Paragraph fill list with commas. Displayed with 'fsep'-data CommaFSep = CommaFSep---- | Vertical list with optional commas. Displayed with 'vcat'.-data VCat = VCat---- | Paragraph fill list with optional commas. Displayed with 'fsep'.-data FSep = FSep---- | Paragraph fill list without commas. Displayed with 'fsep'.-data NoCommaFSep = NoCommaFSep--class Sep sep where- prettySep :: Proxy sep -> [Doc] -> Doc-- parseSep :: CabalParsing m => Proxy sep -> m a -> m [a]- parseSepNE :: CabalParsing m => Proxy sep -> m a -> m (NonEmpty a)--instance Sep CommaVCat where- prettySep _ = vcat . punctuate comma- parseSep _ p = do- v <- askCabalSpecVersion- if v >= CabalSpecV2_2 then parsecLeadingCommaList p else parsecCommaList p- parseSepNE _ p = do- v <- askCabalSpecVersion- if v >= CabalSpecV2_2 then parsecLeadingCommaNonEmpty p else parsecCommaNonEmpty p-instance Sep CommaFSep where- prettySep _ = fsep . punctuate comma- parseSep _ p = do- v <- askCabalSpecVersion- if v >= CabalSpecV2_2 then parsecLeadingCommaList p else parsecCommaList p- parseSepNE _ p = do- v <- askCabalSpecVersion- if v >= CabalSpecV2_2 then parsecLeadingCommaNonEmpty p else parsecCommaNonEmpty p-instance Sep VCat where- prettySep _ = vcat- parseSep _ p = do- v <- askCabalSpecVersion- if v >= CabalSpecV3_0 then parsecLeadingOptCommaList p else parsecOptCommaList p- parseSepNE _ p = NE.some1 (p <* P.spaces)-instance Sep FSep where- prettySep _ = fsep- parseSep _ p = do- v <- askCabalSpecVersion- if v >= CabalSpecV3_0 then parsecLeadingOptCommaList p else parsecOptCommaList p- parseSepNE _ p = NE.some1 (p <* P.spaces)-instance Sep NoCommaFSep where- prettySep _ = fsep- parseSep _ p = many (p <* P.spaces)- parseSepNE _ p = NE.some1 (p <* P.spaces)---- | List separated with optional commas. Displayed with @sep@, arguments of--- type @a@ are parsed and pretty-printed as @b@.-newtype List sep b a = List { _getList :: [a] }---- | 'alaList' and 'alaList'' are simply 'List', with additional phantom--- arguments to constrain the resulting type------ >>> :t alaList VCat--- alaList VCat :: [a] -> List VCat (Identity a) a------ >>> :t alaList' FSep Token--- alaList' FSep Token :: [String] -> List FSep Token String----alaList :: sep -> [a] -> List sep (Identity a) a-alaList _ = List---- | More general version of 'alaList'.-alaList' :: sep -> (a -> b) -> [a] -> List sep b a-alaList' _ _ = List--instance Newtype [a] (List sep wrapper a)--instance (Newtype a b, Sep sep, Parsec b) => Parsec (List sep b a) where- parsec = pack . map (unpack :: b -> a) <$> parseSep (Proxy :: Proxy sep) parsec--instance (Newtype a b, Sep sep, Pretty b) => Pretty (List sep b a) where- pretty = prettySep (Proxy :: Proxy sep) . map (pretty . (pack :: a -> b)) . unpack------- | Like 'List', but for 'Set'.------ @since 3.2.0.0-newtype Set' sep b a = Set' { _getSet :: Set a }---- | 'alaSet' and 'alaSet'' are simply 'Set'' constructor, with additional phantom--- arguments to constrain the resulting type------ >>> :t alaSet VCat--- alaSet VCat :: Set a -> Set' VCat (Identity a) a------ >>> :t alaSet' FSep Token--- alaSet' FSep Token :: Set String -> Set' FSep Token String------ >>> unpack' (alaSet' FSep Token) <$> eitherParsec "foo bar foo"--- Right (fromList ["bar","foo"])------ @since 3.2.0.0-alaSet :: sep -> Set a -> Set' sep (Identity a) a-alaSet _ = Set'---- | More general version of 'alaSet'.------ @since 3.2.0.0-alaSet' :: sep -> (a -> b) -> Set a -> Set' sep b a-alaSet' _ _ = Set'--instance Newtype (Set a) (Set' sep wrapper a)--instance (Newtype a b, Ord a, Sep sep, Parsec b) => Parsec (Set' sep b a) where- parsec = pack . Set.fromList . map (unpack :: b -> a) <$> parseSep (Proxy :: Proxy sep) parsec--instance (Newtype a b, Sep sep, Pretty b) => Pretty (Set' sep b a) where- pretty = prettySep (Proxy :: Proxy sep) . map (pretty . (pack :: a -> b)) . Set.toList . unpack------- | Like 'List', but for 'NonEmpty'.------ @since 3.2.0.0-newtype NonEmpty' sep b a = NonEmpty' { _getNonEmpty :: NonEmpty a }---- | 'alaNonEmpty' and 'alaNonEmpty'' are simply 'NonEmpty'' constructor, with additional phantom--- arguments to constrain the resulting type------ >>> :t alaNonEmpty VCat--- alaNonEmpty VCat :: NonEmpty a -> NonEmpty' VCat (Identity a) a------ >>> unpack' (alaNonEmpty' FSep Token) <$> eitherParsec "foo bar foo"--- Right ("foo" :| ["bar","foo"])------ @since 3.2.0.0-alaNonEmpty :: sep -> NonEmpty a -> NonEmpty' sep (Identity a) a-alaNonEmpty _ = NonEmpty'---- | More general version of 'alaNonEmpty'.------ @since 3.2.0.0-alaNonEmpty' :: sep -> (a -> b) -> NonEmpty a -> NonEmpty' sep b a-alaNonEmpty' _ _ = NonEmpty'--instance Newtype (NonEmpty a) (NonEmpty' sep wrapper a)--instance (Newtype a b, Sep sep, Parsec b) => Parsec (NonEmpty' sep b a) where- parsec = pack . fmap (unpack :: b -> a) <$> parseSepNE (Proxy :: Proxy sep) parsec--instance (Newtype a b, Sep sep, Pretty b) => Pretty (NonEmpty' sep b a) where- pretty = prettySep (Proxy :: Proxy sep) . map (pretty . (pack :: a -> b)) . NE.toList . unpack------------------------------------------------------------------------------------ Identifiers------------------------------------------------------------------------------------ | Haskell string or @[^ ,]+@-newtype Token = Token { getToken :: String }--instance Newtype String Token--instance Parsec Token where- parsec = pack <$> parsecToken--instance Pretty Token where- pretty = showToken . unpack---- | Haskell string or @[^ ]+@-newtype Token' = Token' { getToken' :: String }--instance Newtype String Token'--instance Parsec Token' where- parsec = pack <$> parsecToken'--instance Pretty Token' where- pretty = showToken . unpack---- | Either @"quoted"@ or @un-quoted@.-newtype MQuoted a = MQuoted { getMQuoted :: a }--instance Newtype a (MQuoted a)--instance Parsec a => Parsec (MQuoted a) where- parsec = pack <$> parsecMaybeQuoted parsec--instance Pretty a => Pretty (MQuoted a) where- pretty = pretty . unpack---- | Filepath are parsed as 'Token'.-newtype FilePathNT = FilePathNT { getFilePathNT :: String }--instance Newtype String FilePathNT--instance Parsec FilePathNT where- parsec = do- token <- parsecToken- if null token- then P.unexpected "empty FilePath"- else return (FilePathNT token)--instance Pretty FilePathNT where- pretty = showFilePath . unpack------------------------------------------------------------------------------------ SpecVersion------------------------------------------------------------------------------------ | Version range or just version, i.e. @cabal-version@ field.------ There are few things to consider:------ * Starting with 2.2 the cabal-version field should be the first field in the--- file and only exact version is accepted. Therefore if we get e.g.--- @>= 2.2@, we fail.--- See <https://github.com/haskell/cabal/issues/4899>------ We have this newtype, as writing Parsec and Pretty instances--- for CabalSpecVersion would cause cycle in modules:--- Version -> CabalSpecVersion -> Parsec -> ...----newtype SpecVersion = SpecVersion { getSpecVersion :: CabalSpecVersion }- deriving (Eq, Show) -- instances needed for tests--instance Newtype CabalSpecVersion SpecVersion--instance Parsec SpecVersion where- parsec = do- e <- parsecSpecVersion- let ver :: Version- ver = either id specVersionFromRange e-- digits :: [Int]- digits = versionNumbers ver-- case cabalSpecFromVersionDigits digits of- Nothing -> fail $ "Unknown cabal spec version specified: " ++ prettyShow ver- Just csv -> do- -- Check some warnings:- case e of- -- example: cabal-version: 1.10- -- should be cabal-version: >=1.10- Left _v | csv < CabalSpecV1_12 -> parsecWarning PWTSpecVersion $ concat- [ "With 1.10 or earlier, the 'cabal-version' field must use "- , "range syntax rather than a simple version number. Use "- , "'cabal-version: >= " ++ prettyShow ver ++ "'."- ]-- -- example: cabal-version: >=1.12- -- should be cabal-version: 1.12- Right _vr | csv >= CabalSpecV1_12 -> parsecWarning PWTSpecVersion $ concat- [ "Packages with 'cabal-version: 1.12' or later should specify a "- , "specific version of the Cabal spec of the form "- , "'cabal-version: x.y'. "- , "Use 'cabal-version: " ++ prettyShow ver ++ "'."- ]-- -- example: cabal-version: >=1.10 && <1.12- -- should be cabal-version: >=1.10- Right vr | csv < CabalSpecV1_12- , not (simpleSpecVersionRangeSyntax vr) -> parsecWarning PWTSpecVersion $ concat- [ "It is recommended that the 'cabal-version' field only specify a "- , "version range of the form '>= x.y' for older cabal versions. Use "- , "'cabal-version: >= " ++ prettyShow ver ++ "'. "- , "Tools based on Cabal 1.10 and later will ignore upper bounds."- ]-- -- otherwise no warnings- _ -> pure ()-- return (pack csv)- where- parsecSpecVersion = Left <$> parsec <|> Right <$> range-- range = do- vr <- parsec- if specVersionFromRange vr >= mkVersion [2,1]- then fail "cabal-version higher than 2.2 cannot be specified as a range. See https://github.com/haskell/cabal/issues/4899"- else return vr-- specVersionFromRange :: VersionRange -> Version- specVersionFromRange versionRange = case asVersionIntervals versionRange of- [] -> version0- VersionInterval (LowerBound version _) _ : _ -> version-- simpleSpecVersionRangeSyntax = cataVersionRange alg where- alg (OrLaterVersionF _) = True- alg _ = False---instance Pretty SpecVersion where- pretty (SpecVersion csv)- | csv >= CabalSpecV1_12 = text (showCabalSpecVersion csv)- | otherwise = text ">=" <<>> text (showCabalSpecVersion csv)------------------------------------------------------------------------------------ SpecLicense------------------------------------------------------------------------------------ | SPDX License expression or legacy license-newtype SpecLicense = SpecLicense { getSpecLicense :: Either SPDX.License License }--instance Newtype (Either SPDX.License License) SpecLicense--instance Parsec SpecLicense where- parsec = do- v <- askCabalSpecVersion- if v >= CabalSpecV2_2- then SpecLicense . Left <$> parsec- else SpecLicense . Right <$> parsec--instance Pretty SpecLicense where- pretty = either pretty pretty . unpack------------------------------------------------------------------------------------ TestedWith------------------------------------------------------------------------------------ | Version range or just version-newtype TestedWith = TestedWith { getTestedWith :: (CompilerFlavor, VersionRange) }--instance Newtype (CompilerFlavor, VersionRange) TestedWith--instance Parsec TestedWith where- parsec = pack <$> parsecTestedWith--instance Pretty TestedWith where- pretty x = case unpack x of- (compiler, vr) -> pretty compiler <+> pretty vr--parsecTestedWith :: CabalParsing m => m (CompilerFlavor, VersionRange)-parsecTestedWith = do- name <- lexemeParsec- ver <- parsec <|> pure anyVersion- return (name, ver)
− src/Distribution/FieldGrammar/Parsec.hs
@@ -1,439 +0,0 @@-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}--- | This module provides a 'FieldGrammarParser', one way to parse--- @.cabal@ -like files.------ Fields can be specified multiple times in the .cabal files. The order of--- such entries is important, but the mutual ordering of different fields is--- not.Also conditional sections are considered after non-conditional data.--- The example of this silent-commutation quirk is the fact that------ @--- buildable: True--- if os(linux)--- buildable: False--- @------ and------ @--- if os(linux)--- buildable: False--- buildable: True--- @------ behave the same! This is the limitation of 'GeneralPackageDescription'--- structure.------ So we transform the list of fields @['Field' ann]@ into--- a map of grouped ordinary fields and a list of lists of sections:--- @'Fields' ann = 'Map' 'FieldName' ['NamelessField' ann]@ and @[['Section' ann]]@.------ We need list of list of sections, because we need to distinguish situations--- where there are fields in between. For example------ @--- if flag(bytestring-lt-0_10_4)--- build-depends: bytestring < 0.10.4------ default-language: Haskell2020------ else--- build-depends: bytestring >= 0.10.4------ @------ is obviously invalid specification.------ We can parse 'Fields' like we parse @aeson@ objects, yet we use--- slightly higher-level API, so we can process unspecified fields,--- to report unknown fields and save custom @x-fields@.----module Distribution.FieldGrammar.Parsec (- ParsecFieldGrammar,- parseFieldGrammar,- fieldGrammarKnownFieldList,- -- * Auxiliary- Fields,- NamelessField (..),- namelessFieldAnn,- Section (..),- runFieldParser,- runFieldParser',- fieldLinesToStream,- ) where--import Data.List (dropWhileEnd)-import Distribution.Compat.Newtype-import Distribution.Compat.Prelude-import Distribution.Simple.Utils (fromUTF8BS)-import Prelude ()--import qualified Data.ByteString as BS-import qualified Data.List.NonEmpty as NE-import qualified Data.Map.Strict as Map-import qualified Data.Set as Set-import qualified Distribution.Utils.ShortText as ShortText-import qualified Text.Parsec as P-import qualified Text.Parsec.Error as P--import Distribution.CabalSpecVersion-import Distribution.FieldGrammar.Class-import Distribution.Fields.Field-import Distribution.Fields.ParseResult-import Distribution.Parsec-import Distribution.Parsec.FieldLineStream-import Distribution.Parsec.Position (positionCol, positionRow)------------------------------------------------------------------------------------ Auxiliary types----------------------------------------------------------------------------------type Fields ann = Map FieldName [NamelessField ann]---- | Single field, without name, but with its annotation.-data NamelessField ann = MkNamelessField !ann [FieldLine ann]- deriving (Eq, Show, Functor)--namelessFieldAnn :: NamelessField ann -> ann-namelessFieldAnn (MkNamelessField ann _) = ann---- | The 'Section' constructor of 'Field'.-data Section ann = MkSection !(Name ann) [SectionArg ann] [Field ann]- deriving (Eq, Show, Functor)------------------------------------------------------------------------------------ ParsecFieldGrammar----------------------------------------------------------------------------------data ParsecFieldGrammar s a = ParsecFG- { fieldGrammarKnownFields :: !(Set FieldName)- , fieldGrammarKnownPrefixes :: !(Set FieldName)- , fieldGrammarParser :: !(CabalSpecVersion -> Fields Position -> ParseResult a)- }- deriving (Functor)--parseFieldGrammar :: CabalSpecVersion -> Fields Position -> ParsecFieldGrammar s a -> ParseResult a-parseFieldGrammar v fields grammar = do- for_ (Map.toList (Map.filterWithKey isUnknownField fields)) $ \(name, nfields) ->- for_ nfields $ \(MkNamelessField pos _) ->- parseWarning pos PWTUnknownField $ "Unknown field: " ++ show name- -- TODO: fields allowed in this section-- -- parse- fieldGrammarParser grammar v fields-- where- isUnknownField k _ = not $- k `Set.member` fieldGrammarKnownFields grammar- || any (`BS.isPrefixOf` k) (fieldGrammarKnownPrefixes grammar)--fieldGrammarKnownFieldList :: ParsecFieldGrammar s a -> [FieldName]-fieldGrammarKnownFieldList = Set.toList . fieldGrammarKnownFields--instance Applicative (ParsecFieldGrammar s) where- pure x = ParsecFG mempty mempty (\_ _ -> pure x)- {-# INLINE pure #-}-- ParsecFG f f' f'' <*> ParsecFG x x' x'' = ParsecFG- (mappend f x)- (mappend f' x')- (\v fields -> f'' v fields <*> x'' v fields)- {-# INLINE (<*>) #-}--warnMultipleSingularFields :: FieldName -> [NamelessField Position] -> ParseResult ()-warnMultipleSingularFields _ [] = pure ()-warnMultipleSingularFields fn (x : xs) = do- let pos = namelessFieldAnn x- poss = map namelessFieldAnn xs- parseWarning pos PWTMultipleSingularField $- "The field " <> show fn <> " is specified more than once at positions " ++ intercalate ", " (map showPos (pos : poss))--instance FieldGrammar Parsec ParsecFieldGrammar where- blurFieldGrammar _ (ParsecFG s s' parser) = ParsecFG s s' parser-- uniqueFieldAla fn _pack _extract = ParsecFG (Set.singleton fn) Set.empty parser- where- parser v fields = case Map.lookup fn fields of- Nothing -> parseFatalFailure zeroPos $ show fn ++ " field missing"- Just [] -> parseFatalFailure zeroPos $ show fn ++ " field missing"- Just [x] -> parseOne v x- Just xs@(_:y:ys) -> do- warnMultipleSingularFields fn xs- NE.last <$> traverse (parseOne v) (y:|ys)-- parseOne v (MkNamelessField pos fls) =- unpack' _pack <$> runFieldParser pos parsec v fls-- booleanFieldDef fn _extract def = ParsecFG (Set.singleton fn) Set.empty parser- where- parser v fields = case Map.lookup fn fields of- Nothing -> pure def- Just [] -> pure def- Just [x] -> parseOne v x- Just xs@(_:y:ys) -> do- warnMultipleSingularFields fn xs- NE.last <$> traverse (parseOne v) (y:|ys)-- parseOne v (MkNamelessField pos fls) = runFieldParser pos parsec v fls-- optionalFieldAla fn _pack _extract = ParsecFG (Set.singleton fn) Set.empty parser- where- parser v fields = case Map.lookup fn fields of- Nothing -> pure Nothing- Just [] -> pure Nothing- Just [x] -> parseOne v x- Just xs@(_:y:ys) -> do- warnMultipleSingularFields fn xs- NE.last <$> traverse (parseOne v) (y:|ys)-- parseOne v (MkNamelessField pos fls)- | null fls = pure Nothing- | otherwise = Just . unpack' _pack <$> runFieldParser pos parsec v fls-- optionalFieldDefAla fn _pack _extract def = ParsecFG (Set.singleton fn) Set.empty parser- where- parser v fields = case Map.lookup fn fields of- Nothing -> pure def- Just [] -> pure def- Just [x] -> parseOne v x- Just xs@(_:y:ys) -> do- warnMultipleSingularFields fn xs- NE.last <$> traverse (parseOne v) (y:|ys)-- parseOne v (MkNamelessField pos fls)- | null fls = pure def- | otherwise = unpack' _pack <$> runFieldParser pos parsec v fls-- freeTextField fn _ = ParsecFG (Set.singleton fn) Set.empty parser where- parser v fields = case Map.lookup fn fields of- Nothing -> pure Nothing- Just [] -> pure Nothing- Just [x] -> parseOne v x- Just xs@(_:y:ys) -> do- warnMultipleSingularFields fn xs- NE.last <$> traverse (parseOne v) (y:|ys)-- parseOne v (MkNamelessField pos fls)- | null fls = pure Nothing- | v >= CabalSpecV3_0 = pure (Just (fieldlinesToFreeText3 pos fls))- | otherwise = pure (Just (fieldlinesToFreeText fls))-- freeTextFieldDef fn _ = ParsecFG (Set.singleton fn) Set.empty parser where- parser v fields = case Map.lookup fn fields of- Nothing -> pure ""- Just [] -> pure ""- Just [x] -> parseOne v x- Just xs@(_:y:ys) -> do- warnMultipleSingularFields fn xs- NE.last <$> traverse (parseOne v) (y:|ys)-- parseOne v (MkNamelessField pos fls)- | null fls = pure ""- | v >= CabalSpecV3_0 = pure (fieldlinesToFreeText3 pos fls)- | otherwise = pure (fieldlinesToFreeText fls)-- -- freeTextFieldDefST = defaultFreeTextFieldDefST- freeTextFieldDefST fn _ = ParsecFG (Set.singleton fn) Set.empty parser where- parser v fields = case Map.lookup fn fields of- Nothing -> pure mempty- Just [] -> pure mempty- Just [x] -> parseOne v x- Just xs@(_:y:ys) -> do- warnMultipleSingularFields fn xs- NE.last <$> traverse (parseOne v) (y:|ys)-- parseOne v (MkNamelessField pos fls) = case fls of- [] -> pure mempty- [FieldLine _ bs] -> pure (ShortText.unsafeFromUTF8BS bs)- _ | v >= CabalSpecV3_0 -> pure (ShortText.toShortText $ fieldlinesToFreeText3 pos fls)- | otherwise -> pure (ShortText.toShortText $ fieldlinesToFreeText fls)-- monoidalFieldAla fn _pack _extract = ParsecFG (Set.singleton fn) Set.empty parser- where- parser v fields = case Map.lookup fn fields of- Nothing -> pure mempty- Just xs -> foldMap (unpack' _pack) <$> traverse (parseOne v) xs-- parseOne v (MkNamelessField pos fls) = runFieldParser pos parsec v fls-- prefixedFields fnPfx _extract = ParsecFG mempty (Set.singleton fnPfx) (\_ fs -> pure (parser fs))- where- parser :: Fields Position -> [(String, String)]- parser values = reorder $ concatMap convert $ filter match $ Map.toList values-- match (fn, _) = fnPfx `BS.isPrefixOf` fn- convert (fn, fields) =- [ (pos, (fromUTF8BS fn, trim $ fromUTF8BS $ fieldlinesToBS fls))- | MkNamelessField pos fls <- fields- ]- -- hack: recover the order of prefixed fields- reorder = map snd . sortBy (comparing fst)- trim :: String -> String- trim = dropWhile isSpace . dropWhileEnd isSpace-- availableSince vs def (ParsecFG names prefixes parser) = ParsecFG names prefixes parser'- where- parser' v values- | v >= vs = parser v values- | otherwise = do- let unknownFields = Map.intersection values $ Map.fromSet (const ()) names- for_ (Map.toList unknownFields) $ \(name, fields) ->- for_ fields $ \(MkNamelessField pos _) ->- parseWarning pos PWTUnknownField $- "The field " <> show name <> " is available only since the Cabal specification version " ++ showCabalSpecVersion vs ++ ". This field will be ignored."-- pure def-- availableSinceWarn vs (ParsecFG names prefixes parser) = ParsecFG names prefixes parser'- where- parser' v values- | v >= vs = parser v values- | otherwise = do- let unknownFields = Map.intersection values $ Map.fromSet (const ()) names- for_ (Map.toList unknownFields) $ \(name, fields) ->- for_ fields $ \(MkNamelessField pos _) ->- parseWarning pos PWTUnknownField $- "The field " <> show name <> " is available only since the Cabal specification version " ++ showCabalSpecVersion vs ++ "."-- parser v values--- -- todo we know about this field- deprecatedSince vs msg (ParsecFG names prefixes parser) = ParsecFG names prefixes parser'- where- parser' v values- | v >= vs = do- let deprecatedFields = Map.intersection values $ Map.fromSet (const ()) names- for_ (Map.toList deprecatedFields) $ \(name, fields) ->- for_ fields $ \(MkNamelessField pos _) ->- parseWarning pos PWTDeprecatedField $- "The field " <> show name <> " is deprecated in the Cabal specification version " ++ showCabalSpecVersion vs ++ ". " ++ msg-- parser v values-- | otherwise = parser v values-- removedIn vs msg (ParsecFG names prefixes parser) = ParsecFG names prefixes parser' where- parser' v values- | v >= vs = do- let msg' = if null msg then "" else ' ' : msg- let unknownFields = Map.intersection values $ Map.fromSet (const ()) names- let namePos =- [ (name, pos)- | (name, fields) <- Map.toList unknownFields- , MkNamelessField pos _ <- fields- ]-- let makeMsg name = "The field " <> show name <> " is removed in the Cabal specification version " ++ showCabalSpecVersion vs ++ "." ++ msg'-- case namePos of- -- no fields => proceed (with empty values, to be sure)- [] -> parser v mempty-- -- if there's single field: fail fatally with it- ((name, pos) : rest) -> do- for_ rest $ \(name', pos') -> parseFailure pos' $ makeMsg name'- parseFatalFailure pos $ makeMsg name-- | otherwise = parser v values-- knownField fn = ParsecFG (Set.singleton fn) Set.empty (\_ _ -> pure ())-- hiddenField = id------------------------------------------------------------------------------------ Parsec----------------------------------------------------------------------------------runFieldParser' :: [Position] -> ParsecParser a -> CabalSpecVersion -> FieldLineStream -> ParseResult a-runFieldParser' inputPoss p v str = case P.runParser p' [] "<field>" str of- Right (pok, ws) -> do- traverse_ (\(PWarning t pos w) -> parseWarning (mapPosition pos) t w) ws- pure pok- Left err -> do- let ppos = P.errorPos err- let epos = mapPosition $ Position (P.sourceLine ppos) (P.sourceColumn ppos)-- let msg = P.showErrorMessages- "or" "unknown parse error" "expecting" "unexpected" "end of input"- (P.errorMessages err)- parseFatalFailure epos $ msg ++ "\n"- where- p' = (,) <$ P.spaces <*> unPP p v <* P.spaces <* P.eof <*> P.getState-- -- Positions start from 1:1, not 0:0- mapPosition (Position prow pcol) = go (prow - 1) inputPoss where- go _ [] = zeroPos- go _ [Position row col] = Position row (col + pcol - 1)- go n (Position row col:_) | n <= 0 = Position row (col + pcol - 1)- go n (_:ps) = go (n - 1) ps--runFieldParser :: Position -> ParsecParser a -> CabalSpecVersion -> [FieldLine Position] -> ParseResult a-runFieldParser pp p v ls = runFieldParser' poss p v (fieldLinesToStream ls)- where- poss = map (\(FieldLine pos _) -> pos) ls ++ [pp] -- add "default" position--fieldlinesToBS :: [FieldLine ann] -> BS.ByteString-fieldlinesToBS = BS.intercalate "\n" . map (\(FieldLine _ bs) -> bs)---- Example package with dot lines--- http://hackage.haskell.org/package/copilot-cbmc-0.1/copilot-cbmc.cabal-fieldlinesToFreeText :: [FieldLine ann] -> String-fieldlinesToFreeText [FieldLine _ "."] = "."-fieldlinesToFreeText fls = intercalate "\n" (map go fls)- where- go (FieldLine _ bs)- | s == "." = ""- | otherwise = s- where- s = trim (fromUTF8BS bs)-- trim :: String -> String- trim = dropWhile isSpace . dropWhileEnd isSpace--fieldlinesToFreeText3 :: Position -> [FieldLine Position] -> String-fieldlinesToFreeText3 _ [] = ""-fieldlinesToFreeText3 _ [FieldLine _ bs] = fromUTF8BS bs-fieldlinesToFreeText3 pos (FieldLine pos1 bs1 : fls2@(FieldLine pos2 _ : _))- -- if first line is on the same line with field name:- -- the indentation level is either- -- 1. the indentation of left most line in rest fields- -- 2. the indentation of the first line- -- whichever is leftmost- | positionRow pos == positionRow pos1 = concat- $ fromUTF8BS bs1- : mealy (mk mcol1) pos1 fls2-- -- otherwise, also indent the first line- | otherwise = concat- $ replicate (positionCol pos1 - mcol2) ' '- : fromUTF8BS bs1- : mealy (mk mcol2) pos1 fls2-- where- mcol1 = foldl' (\a b -> min a $ positionCol $ fieldLineAnn b) (min (positionCol pos1) (positionCol pos2)) fls2- mcol2 = foldl' (\a b -> min a $ positionCol $ fieldLineAnn b) (positionCol pos1) fls2-- mk :: Int -> Position -> FieldLine Position -> (Position, String)- mk col p (FieldLine q bs) =- ( q- , replicate newlines '\n'- ++ replicate indent ' '- ++ fromUTF8BS bs- )- where- newlines = positionRow q - positionRow p- indent = positionCol q - col--mealy :: (s -> a -> (s, b)) -> s -> [a] -> [b]-mealy f = go where- go _ [] = []- go s (x : xs) = let ~(s', y) = f s x in y : go s' xs--fieldLinesToStream :: [FieldLine ann] -> FieldLineStream-fieldLinesToStream [] = fieldLineStreamEnd-fieldLinesToStream [FieldLine _ bs] = FLSLast bs-fieldLinesToStream (FieldLine _ bs : fs) = FLSCons bs (fieldLinesToStream fs)
− src/Distribution/FieldGrammar/Pretty.hs
@@ -1,104 +0,0 @@-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE MultiParamTypeClasses #-}-module Distribution.FieldGrammar.Pretty (- PrettyFieldGrammar,- prettyFieldGrammar,- ) where--import Distribution.CabalSpecVersion-import Distribution.Compat.Lens-import Distribution.Compat.Newtype-import Distribution.Compat.Prelude-import Distribution.Fields.Field (FieldName)-import Distribution.Fields.Pretty (PrettyField (..))-import Distribution.Pretty (Pretty (..), showFreeText, showFreeTextV3)-import Distribution.Simple.Utils (toUTF8BS)-import Prelude ()-import Text.PrettyPrint (Doc)-import qualified Text.PrettyPrint as PP--import Distribution.FieldGrammar.Class--newtype PrettyFieldGrammar s a = PrettyFG- { fieldGrammarPretty :: CabalSpecVersion -> s -> [PrettyField ()]- }- deriving (Functor)--instance Applicative (PrettyFieldGrammar s) where- pure _ = PrettyFG (\_ _ -> mempty)- PrettyFG f <*> PrettyFG x = PrettyFG (\v s -> f v s <> x v s)---- | We can use 'PrettyFieldGrammar' to pp print the @s@.------ /Note:/ there is not trailing @($+$ text "")@.-prettyFieldGrammar :: CabalSpecVersion -> PrettyFieldGrammar s a -> s -> [PrettyField ()]-prettyFieldGrammar = flip fieldGrammarPretty--instance FieldGrammar Pretty PrettyFieldGrammar where- blurFieldGrammar f (PrettyFG pp) = PrettyFG (\v -> pp v . aview f)-- uniqueFieldAla fn _pack l = PrettyFG $ \_v s ->- ppField fn (pretty (pack' _pack (aview l s)))-- booleanFieldDef fn l def = PrettyFG pp- where- pp _v s- | b == def = mempty- | otherwise = ppField fn (PP.text (show b))- where- b = aview l s-- optionalFieldAla fn _pack l = PrettyFG pp- where- pp v s = case aview l s of- Nothing -> mempty- Just a -> ppField fn (prettyVersioned v (pack' _pack a))-- optionalFieldDefAla fn _pack l def = PrettyFG pp- where- pp v s- | x == def = mempty- | otherwise = ppField fn (prettyVersioned v (pack' _pack x))- where- x = aview l s-- freeTextField fn l = PrettyFG pp where- pp v s = maybe mempty (ppField fn . showFT) (aview l s) where- showFT | v >= CabalSpecV3_0 = showFreeTextV3- | otherwise = showFreeText-- -- it's ok to just show, as showFreeText of empty string is empty.- freeTextFieldDef fn l = PrettyFG pp where- pp v s = ppField fn (showFT (aview l s)) where- showFT | v >= CabalSpecV3_0 = showFreeTextV3- | otherwise = showFreeText-- freeTextFieldDefST = defaultFreeTextFieldDefST-- monoidalFieldAla fn _pack l = PrettyFG pp- where- pp v s = ppField fn (prettyVersioned v (pack' _pack (aview l s)))-- prefixedFields _fnPfx l = PrettyFG (\_ -> pp . aview l)- where- pp xs =- -- always print the field, even its Doc is empty.- -- i.e. don't use ppField- [ PrettyField () (toUTF8BS n) $ PP.vcat $ map PP.text $ lines s- | (n, s) <- xs- -- fnPfx `isPrefixOf` n- ]-- knownField _ = pure ()- deprecatedSince _ _ x = x- -- TODO: as PrettyFieldGrammar isn't aware of cabal-version: we output the field- -- this doesn't affect roundtrip as `removedIn` fields cannot be parsed- -- so invalid documents can be only manually constructed.- removedIn _ _ x = x- availableSince _ _ = id- hiddenField _ = PrettyFG (\_ -> mempty)--ppField :: FieldName -> Doc -> [PrettyField ()]-ppField name fielddoc- | PP.isEmpty fielddoc = []- | otherwise = [ PrettyField () name fielddoc ]
− src/Distribution/Fields.hs
@@ -1,42 +0,0 @@--- | Utilitiies to work with @.cabal@ like file structure.-module Distribution.Fields (- -- * Types- Field(..),- Name(..),- FieldLine(..),- SectionArg(..),- FieldName,- -- * Grammar and parsing- --- -- See "Distribution.Fields.Parser" for grammar.- readFields,- readFields',- -- ** ParseResult- ParseResult,- runParseResult,- parseString,- parseWarning,- parseWarnings,- parseFailure,- parseFatalFailure,- -- ** Warnings- PWarnType (..),- PWarning (..),- showPWarning,- -- ** Errors- PError (..),- showPError,- -- * Pretty printing- PrettyField (..),- showFields,- -- ** Transformation from Field- genericFromParsecFields,- fromParsecFields,- ) where--import Distribution.Fields.Field-import Distribution.Fields.Parser-import Distribution.Fields.ParseResult-import Distribution.Fields.Pretty-import Distribution.Parsec.Error-import Distribution.Parsec.Warning
− src/Distribution/Fields/ConfVar.hs
@@ -1,126 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-module Distribution.Fields.ConfVar (parseConditionConfVar) where--import Distribution.Compat.CharParsing (char, integral)-import Distribution.Compat.Prelude-import Distribution.Fields.Field (SectionArg (..))-import Distribution.Fields.ParseResult-import Distribution.Parsec (Parsec (..), Position (..), runParsecParser)-import Distribution.Parsec.FieldLineStream (fieldLineStreamFromBS)-import Distribution.Types.Condition-import Distribution.Types.ConfVar (ConfVar (..))-import Distribution.Version- (anyVersion, earlierVersion, intersectVersionRanges, laterVersion, majorBoundVersion,- mkVersion, noVersion, orEarlierVersion, orLaterVersion, thisVersion, unionVersionRanges,- withinVersion)-import Prelude ()--import qualified Text.Parsec as P-import qualified Text.Parsec.Error as P---- | Parse @'Condition' 'ConfVar'@ from section arguments provided by parsec--- based outline parser.-parseConditionConfVar :: [SectionArg Position] -> ParseResult (Condition ConfVar)-parseConditionConfVar args =- -- The name of the input file is irrelevant, as we reformat the error message.- case P.runParser (parser <* P.eof) () "<condition>" args of- Right x -> pure x- Left err -> do- -- Mangle the position to the actual one- let ppos = P.errorPos err- let epos = Position (P.sourceLine ppos) (P.sourceColumn ppos)- let msg = P.showErrorMessages- "or" "unknown parse error" "expecting" "unexpected" "end of input"- (P.errorMessages err)- parseFailure epos msg- pure $ Lit True--type Parser = P.Parsec [SectionArg Position] ()--sepByNonEmpty :: Parser a -> Parser sep -> Parser (NonEmpty a)-sepByNonEmpty p sep = (:|) <$> p <*> many (sep *> p)--parser :: Parser (Condition ConfVar)-parser = condOr- where- condOr = sepByNonEmpty condAnd (oper "||") >>= return . foldl1 COr- condAnd = sepByNonEmpty cond (oper "&&") >>= return . foldl1 CAnd- cond = P.choice- [ boolLiteral, parens condOr, notCond, osCond, archCond, flagCond, implCond ]-- notCond = CNot <$ oper "!" <*> cond-- boolLiteral = Lit <$> boolLiteral'- osCond = Var . OS <$ string "os" <*> parens fromParsec- flagCond = Var . PackageFlag <$ string "flag" <*> parens fromParsec- archCond = Var . Arch <$ string "arch" <*> parens fromParsec- implCond = Var <$ string "impl" <*> parens implCond'-- implCond' = Impl- <$> fromParsec- <*> P.option anyVersion versionRange-- version = fromParsec- versionStar = mkVersion <$> fromParsec' versionStar' <* oper "*"- versionStar' = some (integral <* char '.')-- versionRange = expr- where- expr = foldl1 unionVersionRanges <$> sepByNonEmpty term (oper "||")- term = foldl1 intersectVersionRanges <$> sepByNonEmpty factor (oper "&&")-- factor = P.choice- $ parens expr- : parseAnyVersion- : parseNoVersion- : parseWildcardRange- : map parseRangeOp rangeOps-- parseAnyVersion = anyVersion <$ string "-any"- parseNoVersion = noVersion <$ string "-none"-- parseWildcardRange = P.try $ withinVersion <$ oper "==" <*> versionStar-- parseRangeOp (s,f) = P.try (f <$ oper s <*> version)- rangeOps = [ ("<", earlierVersion),- ("<=", orEarlierVersion),- (">", laterVersion),- (">=", orLaterVersion),- ("^>=", majorBoundVersion),- ("==", thisVersion) ]-- -- Number token can have many dots in it: SecArgNum (Position 65 15) "7.6.1"- identBS = tokenPrim $ \t -> case t of- SecArgName _ s -> Just s- _ -> Nothing-- boolLiteral' = tokenPrim $ \t -> case t of- SecArgName _ s- | s == "True" -> Just True- | s == "true" -> Just True- | s == "False" -> Just False- | s == "false" -> Just False- _ -> Nothing-- string s = tokenPrim $ \t -> case t of- SecArgName _ s' | s == s' -> Just ()- _ -> Nothing-- oper o = tokenPrim $ \t -> case t of- SecArgOther _ o' | o == o' -> Just ()- _ -> Nothing-- parens = P.between (oper "(") (oper ")")-- tokenPrim = P.tokenPrim prettySectionArg updatePosition- -- TODO: check where the errors are reported- updatePosition x _ _ = x- prettySectionArg = show-- fromParsec :: Parsec a => Parser a- fromParsec = fromParsec' parsec-- fromParsec' p = do- bs <- identBS- let fls = fieldLineStreamFromBS bs- either (fail . show) pure (runParsecParser p "<fromParsec'>" fls)
− src/Distribution/Fields/Field.hs
@@ -1,141 +0,0 @@-{-# LANGUAGE DeriveFoldable #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE DeriveTraversable #-}--- | Cabal-like file AST types: 'Field', 'Section' etc------ These types are parametrized by an annotation.-module Distribution.Fields.Field (- -- * Cabal file- Field (..),- fieldName,- fieldAnn,- fieldUniverse,- FieldLine (..),- fieldLineAnn,- fieldLineBS,- SectionArg (..),- sectionArgAnn,- -- * Name- FieldName,- Name (..),- mkName,- getName,- nameAnn,- -- * Conversions to String- sectionArgsToString,- fieldLinesToString,- ) where--import Data.ByteString (ByteString)-import qualified Data.ByteString.Char8 as B-import qualified Data.Char as Char-import Distribution.Compat.Prelude-import Distribution.Pretty (showTokenStr)-import Distribution.Simple.Utils (fromUTF8BS)-import Prelude ()------------------------------------------------------------------------------------- Cabal file------------------------------------------------------------------------------------ | A Cabal-like file consists of a series of fields (@foo: bar@) and sections (@library ...@).-data Field ann- = Field !(Name ann) [FieldLine ann]- | Section !(Name ann) [SectionArg ann] [Field ann]- deriving (Eq, Show, Functor, Foldable, Traversable)---- | Section of field name-fieldName :: Field ann -> Name ann-fieldName (Field n _ ) = n-fieldName (Section n _ _) = n--fieldAnn :: Field ann -> ann-fieldAnn = nameAnn . fieldName---- | All transitive descendants of 'Field', including itself.------ /Note:/ the resulting list is never empty.----fieldUniverse :: Field ann -> [Field ann]-fieldUniverse f@(Section _ _ fs) = f : concatMap fieldUniverse fs-fieldUniverse f@(Field _ _) = [f]---- | A line of text representing the value of a field from a Cabal file.--- A field may contain multiple lines.------ /Invariant:/ 'ByteString' has no newlines.-data FieldLine ann = FieldLine !ann !ByteString- deriving (Eq, Show, Functor, Foldable, Traversable)---- | @since 3.0.0.0-fieldLineAnn :: FieldLine ann -> ann-fieldLineAnn (FieldLine ann _) = ann---- | @since 3.0.0.0-fieldLineBS :: FieldLine ann -> ByteString-fieldLineBS (FieldLine _ bs) = bs---- | Section arguments, e.g. name of the library-data SectionArg ann- = SecArgName !ann !ByteString- -- ^ identifier, or something which looks like number. Also many dot numbers, i.e. "7.6.3"- | SecArgStr !ann !ByteString- -- ^ quoted string- | SecArgOther !ann !ByteString- -- ^ everything else, mm. operators (e.g. in if-section conditionals)- deriving (Eq, Show, Functor, Foldable, Traversable)---- | Extract annotation from 'SectionArg'.-sectionArgAnn :: SectionArg ann -> ann-sectionArgAnn (SecArgName ann _) = ann-sectionArgAnn (SecArgStr ann _) = ann-sectionArgAnn (SecArgOther ann _) = ann------------------------------------------------------------------------------------ Name----------------------------------------------------------------------------------type FieldName = ByteString---- | A field name.------ /Invariant/: 'ByteString' is lower-case ASCII.-data Name ann = Name !ann !FieldName- deriving (Eq, Show, Functor, Foldable, Traversable)--mkName :: ann -> FieldName -> Name ann-mkName ann bs = Name ann (B.map Char.toLower bs)--getName :: Name ann -> FieldName-getName (Name _ bs) = bs--nameAnn :: Name ann -> ann-nameAnn (Name ann _) = ann------------------------------------------------------------------------------------ To Strings------------------------------------------------------------------------------------ |------ @since 3.6.0.0-sectionArgsToString :: [SectionArg ann] -> String-sectionArgsToString = unwords . map toStr where- toStr :: SectionArg ann -> String- toStr (SecArgName _ bs) = showTokenStr (fromUTF8BS bs)- toStr (SecArgStr _ bs) = showTokenStr (fromUTF8BS bs)- toStr (SecArgOther _ bs) = fromUTF8BS bs---- | Convert @['FieldLine']@ into String.------ /Note:/ this doesn't preserve indentation or empty lines,--- as the annotations (e.g. positions) are ignored.------ @since 3.6.0.0-fieldLinesToString :: [FieldLine ann] -> String-fieldLinesToString =- -- intercalate to avoid trailing newline.- intercalate "\n" . map toStr- where- toStr (FieldLine _ bs) = fromUTF8BS bs
− src/Distribution/Fields/Lexer.hs
@@ -1,496 +0,0 @@-{-# OPTIONS_GHC -fno-warn-unused-binds -fno-warn-missing-signatures #-}-{-# LANGUAGE CPP,MagicHash #-}-{-# LINE 1 "templates/Lexer.x" #-}---------------------------------------------------------------------------------- |--- Module : Distribution.Fields.Lexer--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ Lexer for the cabal files.-{-# LANGUAGE CPP #-}-{-# LANGUAGE BangPatterns #-}-#ifdef CABAL_PARSEC_DEBUG-{-# LANGUAGE PatternGuards #-}-#endif-{-# OPTIONS_GHC -fno-warn-unused-imports #-}-module Distribution.Fields.Lexer- (ltest, lexToken, Token(..), LToken(..)- ,bol_section, in_section, in_field_layout, in_field_braces- ,mkLexState) where---- [Note: boostrapping parsec parser]------ We manually produce the `Lexer.hs` file from `boot/Lexer.x` (make lexer)--- because boostrapping cabal-install would be otherwise tricky.--- Alex is (atm) tricky package to build, cabal-install has some magic--- to move bundled generated files in place, so rather we don't depend--- on it before we can build it ourselves.--- Therefore there is one thing less to worry in bootstrap.sh, which is a win.------ See also https://github.com/haskell/cabal/issues/4633-----import Prelude ()-import qualified Prelude as Prelude-import Distribution.Compat.Prelude--import Distribution.Fields.LexerMonad-import Distribution.Parsec.Position (Position (..), incPos, retPos)-import Data.ByteString (ByteString)-import qualified Data.ByteString as B-import qualified Data.ByteString.Char8 as B.Char8-import qualified Data.Word as Word--#ifdef CABAL_PARSEC_DEBUG-import Debug.Trace-import qualified Data.Vector as V-import qualified Data.Text as T-import qualified Data.Text.Encoding as T-import qualified Data.Text.Encoding.Error as T-#endif--#if __GLASGOW_HASKELL__ >= 603-#include "ghcconfig.h"-#elif defined(__GLASGOW_HASKELL__)-#include "config.h"-#endif-#if __GLASGOW_HASKELL__ >= 503-import Data.Array-#else-import Array-#endif-#if __GLASGOW_HASKELL__ >= 503-import Data.Array.Base (unsafeAt)-import GHC.Exts-#else-import GlaExts-#endif-alex_tab_size :: Int-alex_tab_size = 8-alex_base :: AlexAddr-alex_base = AlexA#- "\x12\xff\xff\xff\xf9\xff\xff\xff\xfb\xff\xff\xff\x01\x00\x00\x00\x2f\x00\x00\x00\x50\x00\x00\x00\xd0\x00\x00\x00\x48\xff\xff\xff\xdc\xff\xff\xff\x51\xff\xff\xff\x6d\xff\xff\xff\x6f\xff\xff\xff\x50\x01\x00\x00\x74\x01\x00\x00\x70\xff\xff\xff\x68\x00\x00\x00\x09\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\xa3\x01\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x6a\x00\x00\x00\xd1\x01\x00\x00\xfb\x01\x00\x00\x7b\x02\x00\x00\xfb\x02\x00\x00\x00\x00\x00\x00\x7b\x03\x00\x00\x7d\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\x6d\x00\x00\x00\x6b\x00\x00\x00\xfc\x03\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x6f\x00\x00\x00\x1c\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00\x00\x00"#--alex_table :: AlexAddr-alex_table = AlexA#- "\x00\x00\x09\x00\x0f\x00\x11\x00\x02\x00\x11\x00\x12\x00\x00\x00\x12\x00\x13\x00\x03\x00\x11\x00\x07\x00\x10\x00\x12\x00\x25\x00\x14\x00\x11\x00\x10\x00\x11\x00\x14\x00\x11\x00\x12\x00\x23\x00\x12\x00\x0f\x00\x28\x00\x02\x00\x2e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x08\x00\x10\x00\x00\x00\x14\x00\x00\x00\x00\x00\x08\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x2a\x00\x2e\x00\xff\xff\xff\xff\x2f\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x2a\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x26\x00\x28\x00\xff\xff\xff\xff\x29\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x26\x00\x0f\x00\x11\x00\x17\x00\x26\x00\x12\x00\x25\x00\x11\x00\x2a\x00\x00\x00\x12\x00\x00\x00\x15\x00\x00\x00\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x17\x00\x26\x00\x00\x00\x25\x00\x00\x00\x2a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x2c\x00\x00\x00\x2d\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x00\x00\x0e\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x17\x00\x23\x00\xff\xff\xff\xff\x24\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x17\x00\x1e\x00\x0d\x00\x1e\x00\x1e\x00\x1e\x00\x1e\x00\x00\x00\x1f\x00\x1f\x00\x1e\x00\x1e\x00\x1e\x00\x19\x00\x1a\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x00\x00\x00\x1e\x00\x1e\x00\x1e\x00\x1e\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0a\x00\x1f\x00\x1e\x00\x1f\x00\x1e\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x21\x00\x1e\x00\x22\x00\x1e\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x1d\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x0c\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x0c\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x1e\x00\xff\xff\x1e\x00\x1e\x00\x1e\x00\x1e\x00\xff\xff\xff\xff\xff\xff\x1e\x00\x1e\x00\x1e\x00\x18\x00\x1a\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x1e\x00\x1e\x00\x1e\x00\x1e\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x1e\x00\xff\xff\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x1e\x00\xff\xff\x1e\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x1e\x00\xff\xff\x1e\x00\x1e\x00\x1e\x00\x1e\x00\x00\x00\xff\xff\xff\xff\x1e\x00\x1e\x00\x1e\x00\x1a\x00\x1a\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x1e\x00\x1e\x00\x1e\x00\x1e\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x1e\x00\xff\xff\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x1e\x00\xff\xff\x1e\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x1c\x00\x1e\x00\x00\x00\x1e\x00\x1e\x00\x1e\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x1e\x00\x1e\x00\x1e\x00\x1e\x00\x1e\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x00\x1e\x00\x1e\x00\x1e\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x1e\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x00\xff\xff\x1e\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\xff\xff\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"#--alex_check :: AlexAddr-alex_check = AlexA#- "\xff\xff\xef\x00\x09\x00\x0a\x00\x09\x00\x0a\x00\x0d\x00\xbf\x00\x0d\x00\x2d\x00\x09\x00\x0a\x00\xbb\x00\xa0\x00\x0d\x00\xa0\x00\xa0\x00\x0a\x00\x09\x00\x0a\x00\x09\x00\x0a\x00\x0d\x00\x0a\x00\x0d\x00\x20\x00\x0a\x00\x20\x00\x0a\x00\xff\xff\xff\xff\xff\xff\xff\xff\x20\x00\xff\xff\xff\xff\xff\xff\xff\xff\x2d\x00\xff\xff\x2d\x00\x20\x00\xff\xff\x20\x00\xff\xff\xff\xff\x2d\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\x20\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\x20\x00\x09\x00\x0a\x00\x09\x00\x09\x00\x0d\x00\x09\x00\x0a\x00\x09\x00\xff\xff\x0d\x00\xff\xff\x7b\x00\xff\xff\x7d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x20\x00\xff\xff\x20\x00\x20\x00\xff\xff\x20\x00\xff\xff\x20\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x2d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x7b\x00\xff\xff\x7d\x00\xff\xff\x7f\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc2\x00\xff\xff\xc2\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc2\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc2\x00\xff\xff\xc2\x00\xff\xff\x7f\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\x20\x00\x21\x00\x22\x00\x23\x00\x24\x00\x25\x00\x26\x00\xff\xff\x28\x00\x29\x00\x2a\x00\x2b\x00\x2c\x00\x2d\x00\x2e\x00\x2f\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3a\x00\xff\xff\x3c\x00\x3d\x00\x3e\x00\x3f\x00\x40\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc2\x00\x5b\x00\x5c\x00\x5d\x00\x5e\x00\xc2\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x7b\x00\x7c\x00\x7d\x00\x7e\x00\x7f\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\xff\xff\xff\xff\x22\x00\xff\xff\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\xff\xff\xff\xff\x22\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x5c\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x7f\x00\x5c\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\xff\xff\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\xff\xff\xff\xff\x7f\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\x20\x00\x21\x00\x22\x00\x23\x00\x24\x00\x25\x00\x26\x00\x7f\x00\x28\x00\x29\x00\x2a\x00\x2b\x00\x2c\x00\x2d\x00\x2e\x00\x2f\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3a\x00\xff\xff\x3c\x00\x3d\x00\x3e\x00\x3f\x00\x40\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x7f\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x5b\x00\x5c\x00\x5d\x00\x5e\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x7b\x00\x7c\x00\x7d\x00\x7e\x00\x7f\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\x20\x00\x21\x00\x22\x00\x23\x00\x24\x00\x25\x00\x26\x00\xff\xff\x28\x00\x29\x00\x2a\x00\x2b\x00\x2c\x00\x2d\x00\x2e\x00\x2f\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3a\x00\xff\xff\x3c\x00\x3d\x00\x3e\x00\x3f\x00\x40\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x5b\x00\x5c\x00\x5d\x00\x5e\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x7b\x00\x7c\x00\x7d\x00\x7e\x00\x7f\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\x20\x00\x21\x00\x22\x00\x23\x00\x24\x00\x25\x00\x26\x00\xff\xff\x28\x00\x29\x00\x2a\x00\x2b\x00\x2c\x00\xff\xff\xff\xff\x2f\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3a\x00\xff\xff\x3c\x00\x3d\x00\x3e\x00\x3f\x00\x40\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x5b\x00\x5c\x00\x5d\x00\x5e\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x7b\x00\x7c\x00\x7d\x00\x7e\x00\x7f\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\xff\xff\xff\xff\x22\x00\x21\x00\xff\xff\x23\x00\x24\x00\x25\x00\x26\x00\xff\xff\xff\xff\xff\xff\x2a\x00\x2b\x00\x2c\x00\x2d\x00\x2e\x00\x2f\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x3c\x00\x3d\x00\x3e\x00\x3f\x00\x40\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x5c\x00\xff\xff\x5c\x00\xff\xff\x5e\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x7c\x00\x7f\x00\x7e\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\xff\xff\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\xff\xff\x0a\x00\x0b\x00\x0c\x00\x0d\x00\x0e\x00\x0f\x00\x10\x00\x11\x00\x12\x00\x13\x00\x14\x00\x15\x00\x16\x00\x17\x00\x18\x00\x19\x00\x1a\x00\x1b\x00\x1c\x00\x1d\x00\x1e\x00\x1f\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x7f\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x7b\x00\xff\xff\x7d\x00\xff\xff\x7f\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"#--alex_deflt :: AlexAddr-alex_deflt = AlexA#- "\xff\xff\xff\xff\xff\xff\xff\xff\x2b\x00\x27\x00\x1b\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x0d\x00\x0d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x13\x00\xff\xff\xff\xff\xff\xff\xff\xff\x18\x00\x1b\x00\x1b\x00\x1b\x00\xff\xff\x0d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x27\x00\xff\xff\xff\xff\xff\xff\x2b\x00\xff\xff\xff\xff\xff\xff\xff\xff"#--alex_accept = listArray (0 :: Int, 47)- [ AlexAcc 29- , AlexAcc 28- , AlexAcc 27- , AlexAcc 26- , AlexAccNone- , AlexAccNone- , AlexAccNone- , AlexAccNone- , AlexAccNone- , AlexAccNone- , AlexAccNone- , AlexAccNone- , AlexAccNone- , AlexAccNone- , AlexAccNone- , AlexAccNone- , AlexAccNone- , AlexAcc 25- , AlexAcc 24- , AlexAccSkip- , AlexAcc 23- , AlexAcc 22- , AlexAcc 21- , AlexAccSkip- , AlexAccSkip- , AlexAcc 20- , AlexAcc 19- , AlexAcc 18- , AlexAcc 17- , AlexAcc 16- , AlexAcc 15- , AlexAcc 14- , AlexAcc 13- , AlexAcc 12- , AlexAcc 11- , AlexAcc 10- , AlexAcc 9- , AlexAcc 8- , AlexAccSkip- , AlexAcc 7- , AlexAcc 6- , AlexAcc 5- , AlexAccSkip- , AlexAcc 4- , AlexAcc 3- , AlexAcc 2- , AlexAcc 1- , AlexAcc 0- ]--alex_actions = array (0 :: Int, 30)- [ (29,alex_action_0)- , (28,alex_action_20)- , (27,alex_action_16)- , (26,alex_action_3)- , (25,alex_action_1)- , (24,alex_action_1)- , (23,alex_action_3)- , (22,alex_action_4)- , (21,alex_action_5)- , (20,alex_action_8)- , (19,alex_action_8)- , (18,alex_action_8)- , (17,alex_action_9)- , (16,alex_action_9)- , (15,alex_action_10)- , (14,alex_action_11)- , (13,alex_action_12)- , (12,alex_action_13)- , (11,alex_action_14)- , (10,alex_action_15)- , (9,alex_action_15)- , (8,alex_action_16)- , (7,alex_action_18)- , (6,alex_action_19)- , (5,alex_action_19)- , (4,alex_action_22)- , (3,alex_action_23)- , (2,alex_action_24)- , (1,alex_action_25)- , (0,alex_action_25)- ]--{-# LINE 151 "templates/Lexer.x" #-}---- | Tokens of outer cabal file structure. Field values are treated opaquely.-data Token = TokSym !ByteString -- ^ Haskell-like identifier, number or operator- | TokStr !ByteString -- ^ String in quotes- | TokOther !ByteString -- ^ Operators and parens- | Indent !Int -- ^ Indentation token- | TokFieldLine !ByteString -- ^ Lines after @:@- | Colon- | OpenBrace- | CloseBrace- | EOF- | LexicalError InputStream --TODO: add separate string lexical error- deriving Show--data LToken = L !Position !Token- deriving Show--toki :: (ByteString -> Token) -> Position -> Int -> ByteString -> Lex LToken-toki t pos len input = return $! L pos (t (B.take len input))--tok :: Token -> Position -> Int -> ByteString -> Lex LToken-tok t pos _len _input = return $! L pos t--checkLeadingWhitespace :: Int -> ByteString -> Lex Int-checkLeadingWhitespace len bs- | B.any (== 9) (B.take len bs) = do- addWarning LexWarningTab- checkWhitespace len bs- | otherwise = checkWhitespace len bs--checkWhitespace :: Int -> ByteString -> Lex Int-checkWhitespace len bs- | B.any (== 194) (B.take len bs) = do- addWarning LexWarningNBSP- return $ len - B.count 194 (B.take len bs)- | otherwise = return len---- -------------------------------------------------------------------------------- The input type--type AlexInput = InputStream--alexInputPrevChar :: AlexInput -> Char-alexInputPrevChar _ = error "alexInputPrevChar not used"--alexGetByte :: AlexInput -> Maybe (Word.Word8,AlexInput)-alexGetByte = B.uncons--lexicalError :: Position -> InputStream -> Lex LToken-lexicalError pos inp = do- setInput B.empty- return $! L pos (LexicalError inp)--lexToken :: Lex LToken-lexToken = do- pos <- getPos- inp <- getInput- st <- getStartCode- case alexScan inp st of- AlexEOF -> return (L pos EOF)- AlexError inp' ->- let !len_bytes = B.length inp - B.length inp' in- --FIXME: we want len_chars here really- -- need to decode utf8 up to this point- lexicalError (incPos len_bytes pos) inp'- AlexSkip inp' len_chars -> do- checkPosition pos inp inp' len_chars- adjustPos (incPos len_chars)- setInput inp'- lexToken- AlexToken inp' len_chars action -> do- checkPosition pos inp inp' len_chars- adjustPos (incPos len_chars)- setInput inp'- let !len_bytes = B.length inp - B.length inp'- t <- action pos len_bytes inp- --traceShow t $ return tok- return t--checkPosition :: Position -> ByteString -> ByteString -> Int -> Lex ()-#ifdef CABAL_PARSEC_DEBUG-checkPosition pos@(Position lineno colno) inp inp' len_chars = do- text_lines <- getDbgText- let len_bytes = B.length inp - B.length inp'- pos_txt | lineno-1 < V.length text_lines = T.take len_chars (T.drop (colno-1) (text_lines V.! (lineno-1)))- | otherwise = T.empty- real_txt = B.take len_bytes inp- when (pos_txt /= T.decodeUtf8 real_txt) $- traceShow (pos, pos_txt, T.decodeUtf8 real_txt) $- traceShow (take 3 (V.toList text_lines)) $ return ()- where- getDbgText = Lex $ \s@LexState{ dbgText = txt } -> LexResult s txt-#else-checkPosition _ _ _ _ = return ()-#endif--lexAll :: Lex [LToken]-lexAll = do- t <- lexToken- case t of- L _ EOF -> return [t]- _ -> do ts <- lexAll- return (t : ts)--ltest :: Int -> String -> Prelude.IO ()-ltest code s =- let (ws, xs) = execLexer (setStartCode code >> lexAll) (B.Char8.pack s)- in traverse_ print ws >> traverse_ print xs--mkLexState :: ByteString -> LexState-mkLexState input = LexState- { curPos = Position 1 1- , curInput = input- , curCode = 0- , warnings = []-#ifdef CABAL_PARSEC_DEBUG- , dbgText = V.fromList . lines' . T.decodeUtf8With T.lenientDecode $ input-#endif- }--#ifdef CABAL_PARSEC_DEBUG-lines' :: T.Text -> [T.Text]-lines' s1- | T.null s1 = []- | otherwise = case T.break (\c -> c == '\r' || c == '\n') s1 of- (l, s2) | Just (c,s3) <- T.uncons s2- -> case T.uncons s3 of- Just ('\n', s4) | c == '\r' -> l `T.snoc` '\r' `T.snoc` '\n' : lines' s4- _ -> l `T.snoc` c : lines' s3-- | otherwise- -> [l]-#endif--bol_field_braces,bol_field_layout,bol_section,in_field_braces,in_field_layout,in_section :: Int-bol_field_braces = 1-bol_field_layout = 2-bol_section = 3-in_field_braces = 4-in_field_layout = 5-in_section = 6-alex_action_0 = \_ len _ -> do- when (len /= 0) $ addWarning LexWarningBOM- setStartCode bol_section- lexToken- -alex_action_1 = \_pos len inp -> checkWhitespace len inp >> adjustPos retPos >> lexToken -alex_action_3 = \pos len inp -> checkLeadingWhitespace len inp >>- if B.length inp == len- then return (L pos EOF)- else setStartCode in_section- >> return (L pos (Indent len)) -alex_action_4 = tok OpenBrace -alex_action_5 = tok CloseBrace -alex_action_8 = toki TokSym -alex_action_9 = \pos len inp -> return $! L pos (TokStr (B.take (len - 2) (B.tail inp))) -alex_action_10 = toki TokOther -alex_action_11 = toki TokOther -alex_action_12 = tok Colon -alex_action_13 = tok OpenBrace -alex_action_14 = tok CloseBrace -alex_action_15 = \_ _ _ -> adjustPos retPos >> setStartCode bol_section >> lexToken -alex_action_16 = \pos len inp -> checkLeadingWhitespace len inp >>= \len' ->- if B.length inp == len- then return (L pos EOF)- else setStartCode in_field_layout- >> return (L pos (Indent len')) -alex_action_18 = toki TokFieldLine -alex_action_19 = \_ _ _ -> adjustPos retPos >> setStartCode bol_field_layout >> lexToken -alex_action_20 = \_ _ _ -> setStartCode in_field_braces >> lexToken -alex_action_22 = toki TokFieldLine -alex_action_23 = tok OpenBrace -alex_action_24 = tok CloseBrace -alex_action_25 = \_ _ _ -> adjustPos retPos >> setStartCode bol_field_braces >> lexToken -{-# LINE 1 "templates/GenericTemplate.hs" #-}--- -------------------------------------------------------------------------------- ALEX TEMPLATE------ This code is in the PUBLIC DOMAIN; you may copy it freely and use--- it for any purpose whatsoever.---- -------------------------------------------------------------------------------- INTERNALS and main scanner engine---- Do not remove this comment. Required to fix CPP parsing when using GCC and a clang-compiled alex.-#if __GLASGOW_HASKELL__ > 706-#define GTE(n,m) (tagToEnum# (n >=# m))-#define EQ(n,m) (tagToEnum# (n ==# m))-#else-#define GTE(n,m) (n >=# m)-#define EQ(n,m) (n ==# m)-#endif--data AlexAddr = AlexA# Addr#--- Do not remove this comment. Required to fix CPP parsing when using GCC and a clang-compiled alex.-#if __GLASGOW_HASKELL__ < 503-uncheckedShiftL# = shiftL#-#endif--{-# INLINE alexIndexInt16OffAddr #-}-alexIndexInt16OffAddr :: AlexAddr -> Int# -> Int#-alexIndexInt16OffAddr (AlexA# arr) off =-#ifdef WORDS_BIGENDIAN- narrow16Int# i- where- i = word2Int# ((high `uncheckedShiftL#` 8#) `or#` low)- high = int2Word# (ord# (indexCharOffAddr# arr (off' +# 1#)))- low = int2Word# (ord# (indexCharOffAddr# arr off'))- off' = off *# 2#-#else-#if __GLASGOW_HASKELL__ >= 901- int16ToInt#-#endif- (indexInt16OffAddr# arr off)-#endif--{-# INLINE alexIndexInt32OffAddr #-}-alexIndexInt32OffAddr :: AlexAddr -> Int# -> Int#-alexIndexInt32OffAddr (AlexA# arr) off =-#ifdef WORDS_BIGENDIAN- narrow32Int# i- where- i = word2Int# ((b3 `uncheckedShiftL#` 24#) `or#`- (b2 `uncheckedShiftL#` 16#) `or#`- (b1 `uncheckedShiftL#` 8#) `or#` b0)- b3 = int2Word# (ord# (indexCharOffAddr# arr (off' +# 3#)))- b2 = int2Word# (ord# (indexCharOffAddr# arr (off' +# 2#)))- b1 = int2Word# (ord# (indexCharOffAddr# arr (off' +# 1#)))- b0 = int2Word# (ord# (indexCharOffAddr# arr off'))- off' = off *# 4#-#else-#if __GLASGOW_HASKELL__ >= 901- int32ToInt#-#endif- (indexInt32OffAddr# arr off)-#endif--#if __GLASGOW_HASKELL__ < 503-quickIndex arr i = arr ! i-#else--- GHC >= 503, unsafeAt is available from Data.Array.Base.-quickIndex = unsafeAt-#endif---- -------------------------------------------------------------------------------- Main lexing routines--data AlexReturn a- = AlexEOF- | AlexError !AlexInput- | AlexSkip !AlexInput !Int- | AlexToken !AlexInput !Int a---- alexScan :: AlexInput -> StartCode -> AlexReturn a-alexScan input__ (I# (sc))- = alexScanUser undefined input__ (I# (sc))--alexScanUser user__ input__ (I# (sc))- = case alex_scan_tkn user__ input__ 0# input__ sc AlexNone of- (AlexNone, input__') ->- case alexGetByte input__ of- Nothing ->-- AlexEOF- Just _ ->-- AlexError input__'-- (AlexLastSkip input__'' len, _) ->-- AlexSkip input__'' len-- (AlexLastAcc k input__''' len, _) ->-- AlexToken input__''' len (alex_actions ! k)---- Push the input through the DFA, remembering the most recent accepting--- state it encountered.--alex_scan_tkn user__ orig_input len input__ s last_acc =- input__ `seq` -- strict in the input- let- new_acc = (check_accs (alex_accept `quickIndex` (I# (s))))- in- new_acc `seq`- case alexGetByte input__ of- Nothing -> (new_acc, input__)- Just (c, new_input) ->-- case fromIntegral c of { (I# (ord_c)) ->- let- base = alexIndexInt32OffAddr alex_base s- offset = (base +# ord_c)- check = alexIndexInt16OffAddr alex_check offset-- new_s = if GTE(offset,0#) && EQ(check,ord_c)- then alexIndexInt16OffAddr alex_table offset- else alexIndexInt16OffAddr alex_deflt s- in- case new_s of- -1# -> (new_acc, input__)- -- on an error, we want to keep the input *before* the- -- character that failed, not after.- _ -> alex_scan_tkn user__ orig_input (if c < 0x80 || c >= 0xC0 then (len +# 1#) else len)- -- note that the length is increased ONLY if this is the 1st byte in a char encoding)- new_input new_s new_acc- }- where- check_accs (AlexAccNone) = last_acc- check_accs (AlexAcc a ) = AlexLastAcc a input__ (I# (len))- check_accs (AlexAccSkip) = AlexLastSkip input__ (I# (len))--data AlexLastAcc- = AlexNone- | AlexLastAcc !Int !AlexInput !Int- | AlexLastSkip !AlexInput !Int--data AlexAcc user- = AlexAccNone- | AlexAcc Int- | AlexAccSkip-
− src/Distribution/Fields/LexerMonad.hs
@@ -1,155 +0,0 @@-{-# LANGUAGE CPP #-}--------------------------------------------------------------------------------- |--- Module : Distribution.Fields.LexerMonad--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable-module Distribution.Fields.LexerMonad (- InputStream,- LexState(..),- LexResult(..),-- Lex(..),- execLexer,-- getPos,- setPos,- adjustPos,-- getInput,- setInput,-- getStartCode,- setStartCode,-- LexWarning(..),- LexWarningType(..),- addWarning,- toPWarnings,-- ) where--import qualified Data.ByteString as B-import qualified Data.List.NonEmpty as NE-import Distribution.Compat.Prelude-import Distribution.Parsec.Position (Position (..), showPos)-import Distribution.Parsec.Warning (PWarnType (..), PWarning (..))-import Prelude ()--import qualified Data.Map.Strict as Map--#ifdef CABAL_PARSEC_DEBUG--- testing only:-import qualified Data.Text as T-import qualified Data.Text.Encoding as T-import qualified Data.Vector as V-#endif---- simple state monad-newtype Lex a = Lex { unLex :: LexState -> LexResult a }--instance Functor Lex where- fmap = liftM--instance Applicative Lex where- pure = returnLex- (<*>) = ap--instance Monad Lex where- return = pure- (>>=) = thenLex--data LexResult a = LexResult {-# UNPACK #-} !LexState a--data LexWarningType- = LexWarningNBSP -- ^ Encountered non breaking space- | LexWarningBOM -- ^ BOM at the start of the cabal file- | LexWarningTab -- ^ Leading tags- deriving (Eq, Ord, Show)--data LexWarning = LexWarning !LexWarningType- {-# UNPACK #-} !Position- deriving (Show)--toPWarnings :: [LexWarning] -> [PWarning]-toPWarnings- = map (uncurry toWarning)- . Map.toList- . Map.fromListWith (<>)- . map (\(LexWarning t p) -> (t, pure p))- where- toWarning LexWarningBOM poss =- PWarning PWTLexBOM (NE.head poss) "Byte-order mark found at the beginning of the file"- toWarning LexWarningNBSP poss =- PWarning PWTLexNBSP (NE.head poss) $ "Non breaking spaces at " ++ intercalate ", " (NE.toList $ fmap showPos poss)- toWarning LexWarningTab poss =- PWarning PWTLexTab (NE.head poss) $ "Tabs used as indentation at " ++ intercalate ", " (NE.toList $ fmap showPos poss)--data LexState = LexState {- curPos :: {-# UNPACK #-} !Position, -- ^ position at current input location- curInput :: {-# UNPACK #-} !InputStream, -- ^ the current input- curCode :: {-# UNPACK #-} !StartCode, -- ^ lexer code- warnings :: [LexWarning]-#ifdef CABAL_PARSEC_DEBUG- , dbgText :: V.Vector T.Text -- ^ input lines, to print pretty debug info-#endif- } --TODO: check if we should cache the first token- -- since it looks like parsec's uncons can be called many times on the same input--type StartCode = Int -- ^ An @alex@ lexer start code-type InputStream = B.ByteString------ | Execute the given lexer on the supplied input stream.-execLexer :: Lex a -> InputStream -> ([LexWarning], a)-execLexer (Lex lexer) input =- case lexer initialState of- LexResult LexState{ warnings = ws } result -> (ws, result)- where- initialState = LexState- -- TODO: add 'startPosition'- { curPos = Position 1 1- , curInput = input- , curCode = 0- , warnings = []-#ifdef CABAL_PARSEC_DEBUG- , dbgText = V.fromList . T.lines . T.decodeUtf8 $ input-#endif- }--{-# INLINE returnLex #-}-returnLex :: a -> Lex a-returnLex a = Lex $ \s -> LexResult s a--{-# INLINE thenLex #-}-thenLex :: Lex a -> (a -> Lex b) -> Lex b-(Lex m) `thenLex` k = Lex $ \s -> case m s of LexResult s' a -> (unLex (k a)) s'--setPos :: Position -> Lex ()-setPos pos = Lex $ \s -> LexResult s{ curPos = pos } ()--getPos :: Lex Position-getPos = Lex $ \s@LexState{ curPos = pos } -> LexResult s pos--adjustPos :: (Position -> Position) -> Lex ()-adjustPos f = Lex $ \s@LexState{ curPos = pos } -> LexResult s{ curPos = f pos } ()--getInput :: Lex InputStream-getInput = Lex $ \s@LexState{ curInput = i } -> LexResult s i--setInput :: InputStream -> Lex ()-setInput i = Lex $ \s -> LexResult s{ curInput = i } ()--getStartCode :: Lex Int-getStartCode = Lex $ \s@LexState{ curCode = c } -> LexResult s c--setStartCode :: Int -> Lex ()-setStartCode c = Lex $ \s -> LexResult s{ curCode = c } ()---- | Add warning at the current position-addWarning :: LexWarningType -> Lex ()-addWarning wt = Lex $ \s@LexState{ curPos = pos, warnings = ws } ->- LexResult s{ warnings = LexWarning wt pos : ws } ()
− src/Distribution/Fields/ParseResult.hs
@@ -1,196 +0,0 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}--- | A parse result type for parsers from AST to Haskell types.-module Distribution.Fields.ParseResult (- ParseResult,- runParseResult,- recoverWith,- parseWarning,- parseWarnings,- parseFailure,- parseFatalFailure,- parseFatalFailure',- getCabalSpecVersion,- setCabalSpecVersion,- readAndParseFile,- parseString,- withoutWarnings,- ) where--import qualified Data.ByteString.Char8 as BS-import Distribution.Compat.Prelude-import Distribution.Parsec.Error (PError (..), showPError)-import Distribution.Parsec.Position (Position (..), zeroPos)-import Distribution.Parsec.Warning (PWarnType (..), PWarning (..), showPWarning)-import Distribution.Simple.Utils (die', warn)-import Distribution.Verbosity (Verbosity)-import Distribution.Version (Version)-import Prelude ()-import System.Directory (doesFileExist)--#if MIN_VERSION_base(4,10,0)-import Control.Applicative (Applicative (..))-#endif---- | A monad with failure and accumulating errors and warnings.-newtype ParseResult a = PR- { unPR- :: forall r. PRState- -> (PRState -> r) -- failure, but we were able to recover a new-style spec-version declaration- -> (PRState -> a -> r) -- success- -> r- }---- Note: we have version here, as we could get any version.-data PRState = PRState ![PWarning] ![PError] !(Maybe Version)--emptyPRState :: PRState-emptyPRState = PRState [] [] Nothing---- | Forget 'ParseResult's warnings.------ @since 3.4.0.0-withoutWarnings :: ParseResult a -> ParseResult a-withoutWarnings m = PR $ \s failure success ->- unPR m s failure $ \ !s1 -> success (s1 `withWarningsOf` s)- where- withWarningsOf (PRState _ e v) (PRState w _ _) = PRState w e v---- | Destruct a 'ParseResult' into the emitted warnings and either--- a successful value or--- list of errors and possibly recovered a spec-version declaration.-runParseResult :: ParseResult a -> ([PWarning], Either (Maybe Version, NonEmpty PError) a)-runParseResult pr = unPR pr emptyPRState failure success- where- failure (PRState warns [] v) = (warns, Left (v, PError zeroPos "panic" :| []))- failure (PRState warns (err:errs) v) = (warns, Left (v, err :| errs)) where- success (PRState warns [] _) x = (warns, Right x)- -- If there are any errors, don't return the result- success (PRState warns (err:errs) v) _ = (warns, Left (v, err :| errs))--instance Functor ParseResult where- fmap f (PR pr) = PR $ \ !s failure success ->- pr s failure $ \ !s' a ->- success s' (f a)- {-# INLINE fmap #-}--instance Applicative ParseResult where- pure x = PR $ \ !s _ success -> success s x- {-# INLINE pure #-}-- f <*> x = PR $ \ !s0 failure success ->- unPR f s0 failure $ \ !s1 f' ->- unPR x s1 failure $ \ !s2 x' ->- success s2 (f' x')- {-# INLINE (<*>) #-}-- x *> y = PR $ \ !s0 failure success ->- unPR x s0 failure $ \ !s1 _ ->- unPR y s1 failure success- {-# INLINE (*>) #-}-- x <* y = PR $ \ !s0 failure success ->- unPR x s0 failure $ \ !s1 x' ->- unPR y s1 failure $ \ !s2 _ ->- success s2 x'- {-# INLINE (<*) #-}--#if MIN_VERSION_base(4,10,0)- liftA2 f x y = PR $ \ !s0 failure success ->- unPR x s0 failure $ \ !s1 x' ->- unPR y s1 failure $ \ !s2 y' ->- success s2 (f x' y')- {-# INLINE liftA2 #-}-#endif--instance Monad ParseResult where- return = pure- (>>) = (*>)-- m >>= k = PR $ \ !s failure success ->- unPR m s failure $ \ !s' a ->- unPR (k a) s' failure success- {-# INLINE (>>=) #-}---- | "Recover" the parse result, so we can proceed parsing.--- 'runParseResult' will still result in 'Nothing', if there are recorded errors.-recoverWith :: ParseResult a -> a -> ParseResult a-recoverWith (PR pr) x = PR $ \ !s _failure success ->- pr s (\ !s' -> success s' x) success---- | Set cabal spec version.-setCabalSpecVersion :: Maybe Version -> ParseResult ()-setCabalSpecVersion v = PR $ \(PRState warns errs _) _failure success ->- success (PRState warns errs v) ()---- | Get cabal spec version.-getCabalSpecVersion :: ParseResult (Maybe Version)-getCabalSpecVersion = PR $ \s@(PRState _ _ v) _failure success ->- success s v---- | Add a warning. This doesn't fail the parsing process.-parseWarning :: Position -> PWarnType -> String -> ParseResult ()-parseWarning pos t msg = PR $ \(PRState warns errs v) _failure success ->- success (PRState (PWarning t pos msg : warns) errs v) ()---- | Add multiple warnings at once.-parseWarnings :: [PWarning] -> ParseResult ()-parseWarnings newWarns = PR $ \(PRState warns errs v) _failure success ->- success (PRState (newWarns ++ warns) errs v) ()---- | Add an error, but not fail the parser yet.------ For fatal failure use 'parseFatalFailure'-parseFailure :: Position -> String -> ParseResult ()-parseFailure pos msg = PR $ \(PRState warns errs v) _failure success ->- success (PRState warns (PError pos msg : errs) v) ()---- | Add an fatal error.-parseFatalFailure :: Position -> String -> ParseResult a-parseFatalFailure pos msg = PR $ \(PRState warns errs v) failure _success ->- failure (PRState warns (PError pos msg : errs) v)---- | A 'mzero'.-parseFatalFailure' :: ParseResult a-parseFatalFailure' = PR pr- where- pr (PRState warns [] v) failure _success = failure (PRState warns [err] v)- pr s failure _success = failure s-- err = PError zeroPos "Unknown fatal error"---- | Helper combinator to do parsing plumbing for files.------ Given a parser and a filename, return the parse of the file,--- after checking if the file exists.------ Argument order is chosen to encourage partial application.-readAndParseFile- :: (BS.ByteString -> ParseResult a) -- ^ File contents to final value parser- -> Verbosity -- ^ Verbosity level- -> FilePath -- ^ File to read- -> IO a-readAndParseFile parser verbosity fpath = do- exists <- doesFileExist fpath- unless exists $- die' verbosity $- "Error Parsing: file \"" ++ fpath ++ "\" doesn't exist. Cannot continue."- bs <- BS.readFile fpath- parseString parser verbosity fpath bs--parseString- :: (BS.ByteString -> ParseResult a) -- ^ File contents to final value parser- -> Verbosity -- ^ Verbosity level- -> String -- ^ File name- -> BS.ByteString- -> IO a-parseString parser verbosity name bs = do- let (warnings, result) = runParseResult (parser bs)- traverse_ (warn verbosity . showPWarning name) warnings- case result of- Right x -> return x- Left (_, errors) -> do- traverse_ (warn verbosity . showPError name) errors- die' verbosity $ "Failed parsing \"" ++ name ++ "\"."
− src/Distribution/Fields/Parser.hs
@@ -1,377 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE PatternGuards #-}--------------------------------------------------------------------------------- |--- Module : Distribution.Fields.Parser--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable-module Distribution.Fields.Parser (- -- * Types- Field(..),- Name(..),- FieldLine(..),- SectionArg(..),- -- * Grammar and parsing- -- $grammar- readFields,- readFields',-#ifdef CABAL_PARSEC_DEBUG- -- * Internal- parseFile,- parseStr,- parseBS,-#endif- ) where--import qualified Data.ByteString.Char8 as B8-import Data.Functor.Identity-import Distribution.Compat.Prelude-import Distribution.Fields.Field-import Distribution.Fields.Lexer-import Distribution.Fields.LexerMonad- (LexResult (..), LexState (..), LexWarning (..), unLex)-import Distribution.Parsec.Position (Position (..))-import Prelude ()-import Text.Parsec.Combinator hiding (eof, notFollowedBy)-import Text.Parsec.Error-import Text.Parsec.Pos-import Text.Parsec.Prim hiding (many, (<|>))--#ifdef CABAL_PARSEC_DEBUG-import qualified Data.Text as T-import qualified Data.Text.Encoding as T-import qualified Data.Text.Encoding.Error as T-#endif---- | The 'LexState'' (with a prime) is an instance of parsec's 'Stream'--- wrapped around lexer's 'LexState' (without a prime)-data LexState' = LexState' !LexState (LToken, LexState')--mkLexState' :: LexState -> LexState'-mkLexState' st = LexState' st- (case unLex lexToken st of LexResult st' tok -> (tok, mkLexState' st'))--type Parser a = ParsecT LexState' () Identity a--instance Stream LexState' Identity LToken where- uncons (LexState' _ (tok, st')) =- case tok of- L _ EOF -> return Nothing- _ -> return (Just (tok, st'))---- | Get lexer warnings accumulated so far-getLexerWarnings :: Parser [LexWarning]-getLexerWarnings = do- LexState' (LexState { warnings = ws }) _ <- getInput- return ws---- | Set Alex code i.e. the mode "state" lexer is in.-setLexerMode :: Int -> Parser ()-setLexerMode code = do- LexState' ls _ <- getInput- setInput $! mkLexState' ls { curCode = code }--getToken :: (Token -> Maybe a) -> Parser a-getToken getTok = getTokenWithPos (\(L _ t) -> getTok t)--getTokenWithPos :: (LToken -> Maybe a) -> Parser a-getTokenWithPos getTok = tokenPrim (\(L _ t) -> describeToken t) updatePos getTok- where- updatePos :: SourcePos -> LToken -> LexState' -> SourcePos- updatePos pos (L (Position col line) _) _ = newPos (sourceName pos) col line--describeToken :: Token -> String-describeToken t = case t of- TokSym s -> "symbol " ++ show s- TokStr s -> "string " ++ show s- TokOther s -> "operator " ++ show s- Indent _ -> "new line"- TokFieldLine _ -> "field content"- Colon -> "\":\""- OpenBrace -> "\"{\""- CloseBrace -> "\"}\""--- SemiColon -> "\";\""- EOF -> "end of file"- LexicalError is -> "character in input " ++ show (B8.head is)--tokSym :: Parser (Name Position)-tokSym', tokStr, tokOther :: Parser (SectionArg Position)-tokIndent :: Parser Int-tokColon, tokOpenBrace, tokCloseBrace :: Parser ()-tokFieldLine :: Parser (FieldLine Position)--tokSym = getTokenWithPos $ \t -> case t of L pos (TokSym x) -> Just (mkName pos x); _ -> Nothing-tokSym' = getTokenWithPos $ \t -> case t of L pos (TokSym x) -> Just (SecArgName pos x); _ -> Nothing-tokStr = getTokenWithPos $ \t -> case t of L pos (TokStr x) -> Just (SecArgStr pos x); _ -> Nothing-tokOther = getTokenWithPos $ \t -> case t of L pos (TokOther x) -> Just (SecArgOther pos x); _ -> Nothing-tokIndent = getToken $ \t -> case t of Indent x -> Just x; _ -> Nothing-tokColon = getToken $ \t -> case t of Colon -> Just (); _ -> Nothing-tokOpenBrace = getToken $ \t -> case t of OpenBrace -> Just (); _ -> Nothing-tokCloseBrace = getToken $ \t -> case t of CloseBrace -> Just (); _ -> Nothing-tokFieldLine = getTokenWithPos $ \t -> case t of L pos (TokFieldLine s) -> Just (FieldLine pos s); _ -> Nothing--colon, openBrace, closeBrace :: Parser ()--sectionArg :: Parser (SectionArg Position)-sectionArg = tokSym' <|> tokStr <|> tokOther <?> "section parameter"--fieldSecName :: Parser (Name Position)-fieldSecName = tokSym <?> "field or section name"--colon = tokColon <?> "\":\""-openBrace = tokOpenBrace <?> "\"{\""-closeBrace = tokCloseBrace <?> "\"}\""--fieldContent :: Parser (FieldLine Position)-fieldContent = tokFieldLine <?> "field contents"--newtype IndentLevel = IndentLevel Int--zeroIndentLevel :: IndentLevel-zeroIndentLevel = IndentLevel 0--incIndentLevel :: IndentLevel -> IndentLevel-incIndentLevel (IndentLevel i) = IndentLevel (succ i)--indentOfAtLeast :: IndentLevel -> Parser IndentLevel-indentOfAtLeast (IndentLevel i) = try $ do- j <- tokIndent- guard (j >= i) <?> "indentation of at least " ++ show i- return (IndentLevel j)---newtype LexerMode = LexerMode Int--inLexerMode :: LexerMode -> Parser p -> Parser p-inLexerMode (LexerMode mode) p =- do setLexerMode mode; x <- p; setLexerMode in_section; return x----------------------------- Cabal file grammar------- $grammar------ @--- CabalStyleFile ::= SecElems------ SecElems ::= SecElem* '\\n'?--- SecElem ::= '\\n' SecElemLayout | SecElemBraces--- SecElemLayout ::= FieldLayout | FieldBraces | SectionLayout | SectionBraces--- SecElemBraces ::= FieldInline | FieldBraces | SectionBraces--- FieldLayout ::= name ':' line? ('\\n' line)*--- FieldBraces ::= name ':' '\\n'? '{' content '}'--- FieldInline ::= name ':' content--- SectionLayout ::= name arg* SecElems--- SectionBraces ::= name arg* '\\n'? '{' SecElems '}'--- @------ and the same thing but left factored...------ @--- SecElems ::= SecElem*--- SecElem ::= '\\n' name SecElemLayout--- | name SecElemBraces--- SecElemLayout ::= ':' FieldLayoutOrBraces--- | arg* SectionLayoutOrBraces--- FieldLayoutOrBraces ::= '\\n'? '{' content '}'--- | line? ('\\n' line)*--- SectionLayoutOrBraces ::= '\\n'? '{' SecElems '\\n'? '}'--- | SecElems--- SecElemBraces ::= ':' FieldInlineOrBraces--- | arg* '\\n'? '{' SecElems '\\n'? '}'--- FieldInlineOrBraces ::= '\\n'? '{' content '}'--- | content--- @------ Note how we have several productions with the sequence:------ > '\\n'? '{'------ That is, an optional newline (and indent) followed by a @{@ token.--- In the @SectionLayoutOrBraces@ case you can see that this makes it--- not fully left factored (because @SecElems@ can start with a @\\n@).--- Fully left factoring here would be ugly, and though we could use a--- lookahead of two tokens to resolve the alternatives, we can't--- conveniently use Parsec's 'try' here to get a lookahead of only two.--- So instead we deal with this case in the lexer by making a line--- where the first non-space is @{@ lex as just the @{@ token, without--- the usual indent token. Then in the parser we can resolve everything--- with just one token of lookahead and so without using 'try'.---- Top level of a file using cabal syntax----cabalStyleFile :: Parser [Field Position]-cabalStyleFile = do es <- elements zeroIndentLevel- eof- return es---- Elements that live at the top level or inside a section, ie fields--- and sectionscontent------ elements ::= element*-elements :: IndentLevel -> Parser [Field Position]-elements ilevel = many (element ilevel)---- An individual element, ie a field or a section. These can either use--- layout style or braces style. For layout style then it must start on--- a line on its own (so that we know its indentation level).------ element ::= '\\n' name elementInLayoutContext--- | name elementInNonLayoutContext-element :: IndentLevel -> Parser (Field Position)-element ilevel =- (do ilevel' <- indentOfAtLeast ilevel- name <- fieldSecName- elementInLayoutContext (incIndentLevel ilevel') name)- <|> (do name <- fieldSecName- elementInNonLayoutContext name)---- An element (field or section) that is valid in a layout context.--- In a layout context we can have fields and sections that themselves--- either use layout style or that use braces style.------ elementInLayoutContext ::= ':' fieldLayoutOrBraces--- | arg* sectionLayoutOrBraces-elementInLayoutContext :: IndentLevel -> Name Position -> Parser (Field Position)-elementInLayoutContext ilevel name =- (do colon; fieldLayoutOrBraces ilevel name)- <|> (do args <- many sectionArg- elems <- sectionLayoutOrBraces ilevel- return (Section name args elems))---- An element (field or section) that is valid in a non-layout context.--- In a non-layout context we can have only have fields and sections that--- themselves use braces style, or inline style fields.------ elementInNonLayoutContext ::= ':' FieldInlineOrBraces--- | arg* '\\n'? '{' elements '\\n'? '}'-elementInNonLayoutContext :: Name Position -> Parser (Field Position)-elementInNonLayoutContext name =- (do colon; fieldInlineOrBraces name)- <|> (do args <- many sectionArg- openBrace- elems <- elements zeroIndentLevel- optional tokIndent- closeBrace- return (Section name args elems))---- The body of a field, using either layout style or braces style.------ fieldLayoutOrBraces ::= '\\n'? '{' content '}'--- | line? ('\\n' line)*-fieldLayoutOrBraces :: IndentLevel -> Name Position -> Parser (Field Position)-fieldLayoutOrBraces ilevel name = braces <|> fieldLayout- where- braces = do- openBrace- ls <- inLexerMode (LexerMode in_field_braces) (many fieldContent)- closeBrace- return (Field name ls)- fieldLayout = inLexerMode (LexerMode in_field_layout) $ do- l <- optionMaybe fieldContent- ls <- many (do _ <- indentOfAtLeast ilevel; fieldContent)- return $ case l of- Nothing -> Field name ls- Just l' -> Field name (l' : ls)---- The body of a section, using either layout style or braces style.------ sectionLayoutOrBraces ::= '\\n'? '{' elements \\n? '}'--- | elements-sectionLayoutOrBraces :: IndentLevel -> Parser [Field Position]-sectionLayoutOrBraces ilevel =- (do openBrace- elems <- elements zeroIndentLevel- optional tokIndent- closeBrace- return elems)- <|> (elements ilevel)---- The body of a field, using either inline style or braces.------ fieldInlineOrBraces ::= '\\n'? '{' content '}'--- | content-fieldInlineOrBraces :: Name Position -> Parser (Field Position)-fieldInlineOrBraces name =- (do openBrace- ls <- inLexerMode (LexerMode in_field_braces) (many fieldContent)- closeBrace- return (Field name ls))- <|> (do ls <- inLexerMode (LexerMode in_field_braces) (option [] (fmap (\l -> [l]) fieldContent))- return (Field name ls))----- | Parse cabal style 'B8.ByteString' into list of 'Field's, i.e. the cabal AST.-readFields :: B8.ByteString -> Either ParseError [Field Position]-readFields s = fmap fst (readFields' s)---- | Like 'readFields' but also return lexer warnings-readFields' :: B8.ByteString -> Either ParseError ([Field Position], [LexWarning])-readFields' s = do- parse parser "the input" lexSt- where- parser = do- fields <- cabalStyleFile- ws <- getLexerWarnings- pure (fields, ws)-- lexSt = mkLexState' (mkLexState s)--#ifdef CABAL_PARSEC_DEBUG-parseTest' :: Show a => Parsec LexState' () a -> SourceName -> B8.ByteString -> IO ()-parseTest' p fname s =- case parse p fname (lexSt s) of- Left err -> putStrLn (formatError s err)-- Right x -> print x- where- lexSt = mkLexState' . mkLexState--parseFile :: Show a => Parser a -> FilePath -> IO ()-parseFile p f = B8.readFile f >>= \s -> parseTest' p f s--parseStr :: Show a => Parser a -> String -> IO ()-parseStr p = parseBS p . B8.pack--parseBS :: Show a => Parser a -> B8.ByteString -> IO ()-parseBS p = parseTest' p "<input string>"--formatError :: B8.ByteString -> ParseError -> String-formatError input perr =- unlines- [ "Parse error "++ show (errorPos perr) ++ ":"- , errLine- , indicator ++ errmsg ]- where- pos = errorPos perr- ls = lines' (T.decodeUtf8With T.lenientDecode input)- errLine = T.unpack (ls !! (sourceLine pos - 1))- indicator = replicate (sourceColumn pos) ' ' ++ "^"- errmsg = showErrorMessages "or" "unknown parse error"- "expecting" "unexpected" "end of file"- (errorMessages perr)---- | Handles windows/osx/unix line breaks uniformly-lines' :: T.Text -> [T.Text]-lines' s1- | T.null s1 = []- | otherwise = case T.break (\c -> c == '\r' || c == '\n') s1 of- (l, s2) | Just (c,s3) <- T.uncons s2- -> case T.uncons s3 of- Just ('\n', s4) | c == '\r' -> l : lines' s4- _ -> l : lines' s3- | otherwise -> [l]-#endif--eof :: Parser ()-eof = notFollowedBy anyToken <?> "end of file"- where- notFollowedBy :: Parser LToken -> Parser ()- notFollowedBy p = try ( (do L _ t <- try p; unexpected (describeToken t))- <|> return ())
− src/Distribution/Fields/Pretty.hs
@@ -1,176 +0,0 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE DeriveFoldable #-}-{-# LANGUAGE DeriveTraversable #-}--- | Cabal-like file AST types: 'Field', 'Section' etc,------ This (intermediate) data type is used for pretty-printing.------ @since 3.0.0.0----module Distribution.Fields.Pretty (- -- * Fields- PrettyField (..),- showFields,- showFields',- -- * Transformation from 'P.Field'- fromParsecFields,- genericFromParsecFields,- prettyFieldLines,- prettySectionArgs,- ) where--import Distribution.Compat.Prelude-import Distribution.Pretty (showToken)-import Prelude ()--import Distribution.Fields.Field (FieldName)-import Distribution.Simple.Utils (fromUTF8BS)--import qualified Distribution.Fields.Parser as P--import qualified Data.ByteString as BS-import qualified Text.PrettyPrint as PP--data PrettyField ann- = PrettyField ann FieldName PP.Doc- | PrettySection ann FieldName [PP.Doc] [PrettyField ann]- deriving (Functor, Foldable, Traversable)---- | Prettyprint a list of fields.------ Note: the first argument should return 'String's without newlines--- and properly prefixes (with @--@) to count as comments.--- This unsafety is left in place so one could generate empty lines--- between comment lines.----showFields :: (ann -> [String]) -> [PrettyField ann] -> String-showFields rann = showFields' rann (const id) 4---- | 'showFields' with user specified indentation.-showFields'- :: (ann -> [String])- -- ^ Convert an annotation to lined to preceed the field or section.- -> (ann -> [String] -> [String])- -- ^ Post-process non-annotation produced lines.- -> Int- -- ^ Indentation level.- -> [PrettyField ann]- -- ^ Fields/sections to show.- -> String-showFields' rann post n = unlines . renderFields (Opts rann indent post)- where- -- few hardcoded, "unrolled" variants.- indent | n == 4 = indent4- | n == 2 = indent2- | otherwise = (replicate (max n 1) ' ' ++)-- indent4 :: String -> String- indent4 [] = []- indent4 xs = ' ' : ' ' : ' ' : ' ' : xs-- indent2 :: String -> String- indent2 [] = []- indent2 xs = ' ' : ' ' : xs--data Opts ann = Opts- { _optAnnotation ::(ann -> [String])- , _optIndent ::(String -> String)- , _optPostprocess :: ann -> [String] -> [String]- }--renderFields :: Opts ann -> [PrettyField ann] -> [String]-renderFields opts fields = flattenBlocks $ map (renderField opts len) fields- where- len = maxNameLength 0 fields-- maxNameLength !acc [] = acc- maxNameLength !acc (PrettyField _ name _ : rest) = maxNameLength (max acc (BS.length name)) rest- maxNameLength !acc (PrettySection {} : rest) = maxNameLength acc rest---- | Block of lines,--- Boolean parameter tells whether block should be surrounded by empty lines-data Block = Block Margin Margin [String]--data Margin = Margin | NoMargin- deriving Eq---- | Collapse margins, any margin = margin-instance Semigroup Margin where- NoMargin <> NoMargin = NoMargin- _ <> _ = Margin--flattenBlocks :: [Block] -> [String]-flattenBlocks = go0 where- go0 [] = []- go0 (Block _before after strs : blocks) = strs ++ go after blocks-- go _surr' [] = []- go surr' (Block before after strs : blocks) = ins $ strs ++ go after blocks where- ins | surr' <> before == Margin = ("" :)- | otherwise = id--renderField :: Opts ann -> Int -> PrettyField ann -> Block-renderField (Opts rann indent post) fw (PrettyField ann name doc) =- Block before after $ comments ++ post ann lines'- where- comments = rann ann- before = if null comments then NoMargin else Margin-- (lines', after) = case lines narrow of- [] -> ([ name' ++ ":" ], NoMargin)- [singleLine] | length singleLine < 60- -> ([ name' ++ ": " ++ replicate (fw - length name') ' ' ++ narrow ], NoMargin)- _ -> ((name' ++ ":") : map indent (lines (PP.render doc)), Margin)-- name' = fromUTF8BS name- narrow = PP.renderStyle narrowStyle doc-- narrowStyle :: PP.Style- narrowStyle = PP.style { PP.lineLength = PP.lineLength PP.style - fw }--renderField opts@(Opts rann indent post) _ (PrettySection ann name args fields) = Block Margin Margin $- rann ann- ++- post ann [ PP.render $ PP.hsep $ PP.text (fromUTF8BS name) : args ]- ++- (map indent $ renderFields opts fields)------------------------------------------------------------------------------------ Transform from Parsec.Field----------------------------------------------------------------------------------genericFromParsecFields- :: Applicative f- => (FieldName -> [P.FieldLine ann] -> f PP.Doc) -- ^ transform field contents- -> (FieldName -> [P.SectionArg ann] -> f [PP.Doc]) -- ^ transform section arguments- -> [P.Field ann]- -> f [PrettyField ann]-genericFromParsecFields f g = goMany where- goMany = traverse go-- go (P.Field (P.Name ann name) fls) = PrettyField ann name <$> f name fls- go (P.Section (P.Name ann name) secargs fs) = PrettySection ann name <$> g name secargs <*> goMany fs---- | Used in 'fromParsecFields'.-prettyFieldLines :: FieldName -> [P.FieldLine ann] -> PP.Doc-prettyFieldLines _ fls = PP.vcat- [ PP.text $ fromUTF8BS bs- | P.FieldLine _ bs <- fls- ]---- | Used in 'fromParsecFields'.-prettySectionArgs :: FieldName -> [P.SectionArg ann] -> [PP.Doc]-prettySectionArgs _ = map $ \sa -> case sa of- P.SecArgName _ bs -> showToken $ fromUTF8BS bs- P.SecArgStr _ bs -> showToken $ fromUTF8BS bs- P.SecArgOther _ bs -> PP.text $ fromUTF8BS bs---- | Simple variant of 'genericFromParsecField'-fromParsecFields :: [P.Field ann] -> [PrettyField ann]-fromParsecFields = runIdentity . genericFromParsecFields- (Identity .: prettyFieldLines)- (Identity .: prettySectionArgs)- where- (.:) :: (a -> b) -> (c -> d -> a) -> (c -> d -> b)- (f .: g) x y = f (g x y)
src/Distribution/GetOpt.hs view
@@ -1,4 +1,8 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE TupleSections #-}+ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.GetOpt -- Copyright : (c) Sven Panne 2002-2005@@ -17,54 +21,87 @@ -- * Line wrapping in the 'usageInfo' output, plus a more compact -- rendering of short options, and slightly less padding. ----- If you want to take on the challenge of merging this with the GetOpt--- from the base package then go for it!+-- * Parsing of option arguments is allowed to fail. ---{-# LANGUAGE TupleSections #-}-{-# LANGUAGE NamedFieldPuns #-}-module Distribution.GetOpt (- -- * GetOpt- getOpt, getOpt',- usageInfo,- ArgOrder(..),- OptDescr(..),- ArgDescr(..),+-- * 'ReturnInOrder' argument order is removed.+module Distribution.GetOpt+ ( -- * GetOpt+ getOpt+ , getOpt'+ , usageInfo+ , ArgOrder (..)+ , OptDescr (..)+ , ArgDescr (..) - -- * Example- -- | See "System.Console.GetOpt" for examples-) where+ -- * Example -import Prelude ()+ -- | See "System.Console.GetOpt" for examples+ ) where+ import Distribution.Compat.Prelude-import System.Console.GetOpt- ( ArgOrder(..), OptDescr(..), ArgDescr(..) )+import Prelude () -data OptKind a -- kind of cmd line arg (internal use only):- = Opt a -- an option- | UnreqOpt String -- an un-recognized option- | NonOpt String -- a non-option- | EndOfOpts -- end-of-options marker (i.e. "--")- | OptErr String -- something went wrong...+-- | What to do with options following non-options+data ArgOrder a+ = -- | no option processing after first non-option+ RequireOrder+ | -- | freely intersperse options and non-options+ Permute -data OptHelp a = OptHelp {- optNames :: a,- optHelp :: String- }+data OptDescr a -- description of a single options:+ = Option+ [Char] -- list of short option characters+ [String] -- list of long option strings (without "--")+ (ArgDescr a) -- argument descriptor+ String -- explanation of option for user +instance Functor OptDescr where+ fmap f (Option a b argDescr c) = Option a b (fmap f argDescr) c++-- | Describes whether an option takes an argument or not, and if so+-- how the argument is parsed to a value of type @a@.+--+-- Compared to System.Console.GetOpt, we allow for parse errors.+data ArgDescr a+ = -- | no argument expected+ NoArg a+ | -- | option requires argument+ ReqArg (String -> Either String a) String+ | -- | optional argument+ OptArg String (Maybe String -> Either String a) String++instance Functor ArgDescr where+ fmap f (NoArg a) = NoArg (f a)+ fmap f (ReqArg g s) = ReqArg (fmap f . g) s+ fmap f (OptArg dv g s) = OptArg dv (fmap f . g) s++data OptKind a -- kind of cmd line arg (internal use only):+ = Opt a -- an option+ | UnreqOpt String -- an un-recognized option+ | NonOpt String -- a non-option+ | EndOfOpts -- end-of-options marker (i.e. "--")+ | OptErr String -- something went wrong...++data OptHelp = OptHelp+ { optNames :: String+ , optHelp :: String+ }+ -- | Return a string describing the usage of a command, derived from -- the header (first argument) and the options described by the -- second argument.-usageInfo :: String -- header- -> [OptDescr a] -- option descriptors- -> String -- nicely formatted decription of options+usageInfo+ :: String -- header+ -> [OptDescr a] -- option descriptors+ -> String -- nicely formatted description of options usageInfo header optDescr = unlines (header : table) where options = flip map optDescr $ \(Option sos los ad d) -> OptHelp { optNames =- intercalate ", " $- map (fmtShort ad) sos ++- map (fmtLong ad) (take 1 los)+ intercalate ", " $+ map (fmtShort ad) sos+ ++ map (fmtLong ad) (take 1 los) , optHelp = d } @@ -76,8 +113,9 @@ OptHelp{optNames, optHelp} <- options let wrappedHelp = wrapText descolWidth optHelp if length optNames >= maxOptNameWidth- then [" " ++ optNames] ++- renderColumns [] wrappedHelp+ then+ [" " ++ optNames]+ ++ renderColumns [] wrappedHelp else renderColumns [optNames] wrappedHelp renderColumns :: [String] -> [String] -> [String]@@ -85,141 +123,179 @@ (x, y) <- zipDefault "" "" xs ys return $ " " ++ padTo maxOptNameWidth x ++ " " ++ y - padTo n x = take n (x ++ repeat ' ')+ padTo n x = take n (x ++ repeat ' ') -zipDefault :: a -> b -> [a] -> [b] -> [(a,b)]-zipDefault _ _ [] [] = []-zipDefault _ bd (a:as) [] = (a,bd) : map (,bd) as-zipDefault ad _ [] (b:bs) = (ad,b) : map (ad,) bs-zipDefault ad bd (a:as) (b:bs) = (a,b) : zipDefault ad bd as bs+zipDefault :: a -> b -> [a] -> [b] -> [(a, b)]+zipDefault _ _ [] [] = []+zipDefault _ bd (a : as) [] = (a, bd) : map (,bd) as+zipDefault ad _ [] (b : bs) = (ad, b) : map (ad,) bs+zipDefault ad bd (a : as) (b : bs) = (a, b) : zipDefault ad bd as bs +-- | Pretty printing of short options.+-- * With required arguments can be given as:+-- @-w PATH or -wPATH (but not -w=PATH)@+-- This is displayed as:+-- @-w PATH or -wPATH@+-- * With optional but default arguments can be given as:+-- @-j or -jNUM (but not -j=NUM or -j NUM)@+-- This is displayed as:+-- @-j[NUM]@ fmtShort :: ArgDescr a -> Char -> String-fmtShort (NoArg _ ) so = "-" ++ [so]-fmtShort (ReqArg _ _) so = "-" ++ [so]-fmtShort (OptArg _ _) so = "-" ++ [so]- -- unlike upstream GetOpt we omit the arg name for short options+fmtShort (NoArg _) so = "-" ++ [so]+fmtShort (ReqArg _ ad) so =+ let opt = "-" ++ [so]+ in opt ++ " " ++ ad ++ " or " ++ opt ++ ad+fmtShort (OptArg _ _ ad) so =+ let opt = "-" ++ [so]+ in opt ++ "[" ++ ad ++ "]" +-- | Pretty printing of long options.+-- * With required arguments can be given as:+-- @--with-compiler=PATH (but not --with-compiler PATH)@+-- This is displayed as:+-- @--with-compiler=PATH@+-- * With optional but default arguments can be given as:+-- @--jobs or --jobs=NUM (but not --jobs NUM)@+-- This is displayed as:+-- @--jobs[=NUM]@ fmtLong :: ArgDescr a -> String -> String-fmtLong (NoArg _ ) lo = "--" ++ lo-fmtLong (ReqArg _ ad) lo = "--" ++ lo ++ "=" ++ ad-fmtLong (OptArg _ ad) lo = "--" ++ lo ++ "[=" ++ ad ++ "]"+fmtLong (NoArg _) lo = "--" ++ lo+fmtLong (ReqArg _ ad) lo =+ let opt = "--" ++ lo+ in opt ++ "=" ++ ad+fmtLong (OptArg _ _ ad) lo =+ let opt = "--" ++ lo+ in opt ++ "[=" ++ ad ++ "]" wrapText :: Int -> String -> [String] wrapText width = map unwords . wrap 0 [] . words- where wrap :: Int -> [String] -> [String] -> [[String]]- wrap 0 [] (w:ws)- | length w + 1 > width- = wrap (length w) [w] ws- wrap col line (w:ws)- | col + length w + 1 > width- = reverse line : wrap 0 [] (w:ws)- wrap col line (w:ws)- = let col' = col + length w + 1- in wrap col' (w:line) ws- wrap _ [] [] = []- wrap _ line [] = [reverse line]--{-|-Process the command-line, and return the list of values that matched-(and those that didn\'t). The arguments are:--* The order requirements (see 'ArgOrder')--* The option descriptions (see 'OptDescr')--* The actual command line arguments (presumably got from- 'System.Environment.getArgs').+ where+ wrap :: Int -> [String] -> [String] -> [[String]]+ wrap 0 [] (w : ws)+ | length w + 1 > width =+ wrap (length w) [w] ws+ wrap col line (w : ws)+ | col + length w + 1 > width =+ reverse line : wrap 0 [] (w : ws)+ wrap col line (w : ws) =+ let col' = col + length w + 1+ in wrap col' (w : line) ws+ wrap _ [] [] = []+ wrap _ line [] = [reverse line] -'getOpt' returns a triple consisting of the option arguments, a list-of non-options, and a list of error messages.--}-getOpt :: ArgOrder a -- non-option handling- -> [OptDescr a] -- option descriptors- -> [String] -- the command-line arguments- -> ([a],[String],[String]) -- (options,non-options,error messages)-getOpt ordering optDescr args = (os,xs,es ++ map errUnrec us)- where (os,xs,us,es) = getOpt' ordering optDescr args+-- |+-- Process the command-line, and return the list of values that matched+-- (and those that didn\'t). The arguments are:+--+-- * The order requirements (see 'ArgOrder')+--+-- * The option descriptions (see 'OptDescr')+--+-- * The actual command line arguments (presumably got from+-- 'System.Environment.getArgs').+--+-- 'getOpt' returns a triple consisting of the option arguments, a list+-- of non-options, and a list of error messages.+getOpt+ :: ArgOrder a -- non-option handling+ -> [OptDescr a] -- option descriptors+ -> [String] -- the command-line arguments+ -> ([a], [String], [String]) -- (options,non-options,error messages)+getOpt ordering optDescr args = (os, xs, es ++ map errUnrec us)+ where+ (os, xs, us, es) = getOpt' ordering optDescr args -{-|-This is almost the same as 'getOpt', but returns a quadruple-consisting of the option arguments, a list of non-options, a list of-unrecognized options, and a list of error messages.--}-getOpt' :: ArgOrder a -- non-option handling- -> [OptDescr a] -- option descriptors- -> [String] -- the command-line arguments- -> ([a],[String], [String] ,[String]) -- (options,non-options,unrecognized,error messages)-getOpt' _ _ [] = ([],[],[],[])-getOpt' ordering optDescr (arg:args) = procNextOpt opt ordering- where procNextOpt (Opt o) _ = (o:os,xs,us,es)- procNextOpt (UnreqOpt u) _ = (os,xs,u:us,es)- procNextOpt (NonOpt x) RequireOrder = ([],x:rest,[],[])- procNextOpt (NonOpt x) Permute = (os,x:xs,us,es)- procNextOpt (NonOpt x) (ReturnInOrder f) = (f x :os, xs,us,es)- procNextOpt EndOfOpts RequireOrder = ([],rest,[],[])- procNextOpt EndOfOpts Permute = ([],rest,[],[])- procNextOpt EndOfOpts (ReturnInOrder f) = (map f rest,[],[],[])- procNextOpt (OptErr e) _ = (os,xs,us,e:es)+-- |+-- This is almost the same as 'getOpt', but returns a quadruple+-- consisting of the option arguments, a list of non-options, a list of+-- unrecognized options, and a list of error messages.+getOpt'+ :: ArgOrder a -- non-option handling+ -> [OptDescr a] -- option descriptors+ -> [String] -- the command-line arguments+ -> ([a], [String], [String], [String]) -- (options,non-options,unrecognized,error messages)+getOpt' _ _ [] = ([], [], [], [])+getOpt' ordering optDescr (arg : args) = procNextOpt opt ordering+ where+ procNextOpt (Opt o) _ = (o : os, xs, us, es)+ procNextOpt (UnreqOpt u) _ = (os, xs, u : us, es)+ procNextOpt (NonOpt x) RequireOrder = ([], x : rest, [], [])+ procNextOpt (NonOpt x) Permute = (os, x : xs, us, es)+ procNextOpt EndOfOpts RequireOrder = ([], rest, [], [])+ procNextOpt EndOfOpts Permute = ([], rest, [], [])+ procNextOpt (OptErr e) _ = (os, xs, us, e : es) - (opt,rest) = getNext arg args optDescr- (os,xs,us,es) = getOpt' ordering optDescr rest+ (opt, rest) = getNext arg args optDescr+ (os, xs, us, es) = getOpt' ordering optDescr rest -- take a look at the next cmd line arg and decide what to do with it-getNext :: String -> [String] -> [OptDescr a] -> (OptKind a,[String])-getNext ('-':'-':[]) rest _ = (EndOfOpts,rest)-getNext ('-':'-':xs) rest optDescr = longOpt xs rest optDescr-getNext ('-': x :xs) rest optDescr = shortOpt x xs rest optDescr-getNext a rest _ = (NonOpt a,rest)+getNext :: String -> [String] -> [OptDescr a] -> (OptKind a, [String])+getNext ('-' : '-' : []) rest _ = (EndOfOpts, rest)+getNext ('-' : '-' : xs) rest optDescr = longOpt xs rest optDescr+getNext ('-' : x : xs) rest optDescr = shortOpt x xs rest optDescr+getNext a rest _ = (NonOpt a, rest) -- handle long option-longOpt :: String -> [String] -> [OptDescr a] -> (OptKind a,[String])+longOpt :: String -> [String] -> [OptDescr a] -> (OptKind a, [String]) longOpt ls rs optDescr = long ads arg rs- where (opt,arg) = break (=='=') ls- getWith p = [ o | o@(Option _ xs _ _) <- optDescr- , isJust (find (p opt) xs)]- exact = getWith (==)- options = if null exact then getWith isPrefixOf else exact- ads = [ ad | Option _ _ ad _ <- options ]- optStr = "--" ++ opt+ where+ (opt, arg) = break (== '=') ls+ getWith p =+ [ o | o@(Option _ xs _ _) <- optDescr, isJust (find (p opt) xs)+ ]+ exact = getWith (==)+ options = if null exact then getWith isPrefixOf else exact+ ads = [ad | Option _ _ ad _ <- options]+ optStr = "--" ++ opt+ fromRes = fromParseResult optStr - long (_:_:_) _ rest = (errAmbig options optStr,rest)- long [NoArg a ] [] rest = (Opt a,rest)- long [NoArg _ ] ('=':_) rest = (errNoArg optStr,rest)- long [ReqArg _ d] [] [] = (errReq d optStr,[])- long [ReqArg f _] [] (r:rest) = (Opt (f r),rest)- long [ReqArg f _] ('=':xs) rest = (Opt (f xs),rest)- long [OptArg f _] [] rest = (Opt (f Nothing),rest)- long [OptArg f _] ('=':xs) rest = (Opt (f (Just xs)),rest)- long _ _ rest = (UnreqOpt ("--"++ls),rest)+ long (_ : _ : _) _ rest = (errAmbig options optStr, rest)+ long [NoArg a] [] rest = (Opt a, rest)+ long [NoArg _] ('=' : _) rest = (errNoArg optStr, rest)+ long [ReqArg _ d] [] [] = (errReq d optStr, [])+ long [ReqArg f _] [] (r : rest) = (fromRes (f r), rest)+ long [ReqArg f _] ('=' : xs) rest = (fromRes (f xs), rest)+ long [OptArg _ f _] [] rest = (fromRes (f Nothing), rest)+ long [OptArg _ f _] ('=' : xs) rest = (fromRes (f (Just xs)), rest)+ long _ _ rest = (UnreqOpt ("--" ++ ls), rest) -- handle short option-shortOpt :: Char -> String -> [String] -> [OptDescr a] -> (OptKind a,[String])+shortOpt :: Char -> String -> [String] -> [OptDescr a] -> (OptKind a, [String]) shortOpt y ys rs optDescr = short ads ys rs- where options = [ o | o@(Option ss _ _ _) <- optDescr, s <- ss, y == s ]- ads = [ ad | Option _ _ ad _ <- options ]- optStr = '-':[y]+ where+ options = [o | o@(Option ss _ _ _) <- optDescr, s <- ss, y == s]+ ads = [ad | Option _ _ ad _ <- options]+ optStr = '-' : [y]+ fromRes = fromParseResult optStr - short (_:_:_) _ rest = (errAmbig options optStr,rest)- short (NoArg a :_) [] rest = (Opt a,rest)- short (NoArg a :_) xs rest = (Opt a,('-':xs):rest)- short (ReqArg _ d:_) [] [] = (errReq d optStr,[])- short (ReqArg f _:_) [] (r:rest) = (Opt (f r),rest)- short (ReqArg f _:_) xs rest = (Opt (f xs),rest)- short (OptArg f _:_) [] rest = (Opt (f Nothing),rest)- short (OptArg f _:_) xs rest = (Opt (f (Just xs)),rest)- short [] [] rest = (UnreqOpt optStr,rest)- short [] xs rest = (UnreqOpt (optStr++xs),rest)- -- This is different vs upstream = (UnreqOpt optStr,('-':xs):rest)- -- Apparently this was part of the change so that flags that are- -- not recognised as global flags are passed on to the sub-command.- -- But why was no equivalent change required for longOpt? So could- -- this change go upstream?+ short (_ : _ : _) _ rest = (errAmbig options optStr, rest)+ short (NoArg a : _) [] rest = (Opt a, rest)+ short (NoArg a : _) xs rest = (Opt a, ('-' : xs) : rest)+ short (ReqArg _ d : _) [] [] = (errReq d optStr, [])+ short (ReqArg f _ : _) [] (r : rest) = (fromRes (f r), rest)+ short (ReqArg f _ : _) xs rest = (fromRes (f xs), rest)+ short (OptArg _ f _ : _) [] rest = (fromRes (f Nothing), rest)+ short (OptArg _ f _ : _) xs rest = (fromRes (f (Just xs)), rest)+ short [] [] rest = (UnreqOpt optStr, rest)+ short [] xs rest = (UnreqOpt (optStr ++ xs), rest) +-- This is different vs upstream = (UnreqOpt optStr,('-':xs):rest)+-- Apparently this was part of the change so that flags that are+-- not recognised as global flags are passed on to the sub-command.+-- But why was no equivalent change required for longOpt? So could+-- this change go upstream?++fromParseResult :: String -> Either String a -> OptKind a+fromParseResult optStr res = case res of+ Right x -> Opt x+ Left err -> OptErr ("invalid argument to option `" ++ optStr ++ "': " ++ err ++ "\n")+ -- miscellaneous error formatting -errAmbig :: [OptDescr a] -> String -> OptKind a+errAmbig :: [OptDescr a] -> String -> OptKind b errAmbig ods optStr = OptErr (usageInfo header ods)- where header = "option `" ++ optStr ++ "' is ambiguous; could be one of:"+ where+ header = "option `" ++ optStr ++ "' is ambiguous; could be one of:" errReq :: String -> String -> OptKind a errReq d optStr = OptErr ("option `" ++ optStr ++ "' requires an argument " ++ d ++ "\n")
− src/Distribution/InstalledPackageInfo.hs
@@ -1,145 +0,0 @@--------------------------------------------------------------------------------- |--- Module : Distribution.InstalledPackageInfo--- Copyright : (c) The University of Glasgow 2004------ Maintainer : libraries@haskell.org--- Portability : portable------ This is the information about an /installed/ package that--- is communicated to the @ghc-pkg@ program in order to register--- a package. @ghc-pkg@ now consumes this package format (as of version--- 6.4). This is specific to GHC at the moment.------ The @.cabal@ file format is for describing a package that is not yet--- installed. It has a lot of flexibility, like conditionals and dependency--- ranges. As such, that format is not at all suitable for describing a package--- that has already been built and installed. By the time we get to that stage,--- we have resolved all conditionals and resolved dependency version--- constraints to exact versions of dependent packages. So, this module defines--- the 'InstalledPackageInfo' data structure that contains all the info we keep--- about an installed package. There is a parser and pretty printer. The--- textual format is rather simpler than the @.cabal@ format: there are no--- sections, for example.---- This module is meant to be local-only to Distribution...--module Distribution.InstalledPackageInfo (- InstalledPackageInfo(..),- installedComponentId,- installedOpenUnitId,- sourceComponentName,- requiredSignatures,- ExposedModule(..),- AbiDependency(..),- emptyInstalledPackageInfo,- parseInstalledPackageInfo,- showInstalledPackageInfo,- showFullInstalledPackageInfo,- showInstalledPackageInfoField,- showSimpleInstalledPackageInfoField,- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Backpack-import Distribution.CabalSpecVersion (cabalSpecLatest)-import Distribution.FieldGrammar-import Distribution.FieldGrammar.FieldDescrs-import Distribution.ModuleName-import Distribution.Package hiding (installedUnitId)-import Distribution.Types.ComponentName-import Distribution.Utils.Generic (toUTF8BS)--import Data.ByteString (ByteString)--import qualified Data.Map as Map-import qualified Distribution.Fields as P-import qualified Text.PrettyPrint as Disp--import Distribution.Types.InstalledPackageInfo-import Distribution.Types.InstalledPackageInfo.FieldGrammar---- $setup--- >>> :set -XOverloadedStrings--installedComponentId :: InstalledPackageInfo -> ComponentId-installedComponentId ipi =- case unComponentId (installedComponentId_ ipi) of- "" -> mkComponentId (unUnitId (installedUnitId ipi))- _ -> installedComponentId_ ipi---- | Get the indefinite unit identity representing this package.--- This IS NOT guaranteed to give you a substitution; for--- instantiated packages you will get @DefiniteUnitId (installedUnitId ipi)@.--- For indefinite libraries, however, you will correctly get--- an @OpenUnitId@ with the appropriate 'OpenModuleSubst'.-installedOpenUnitId :: InstalledPackageInfo -> OpenUnitId-installedOpenUnitId ipi- = mkOpenUnitId (installedUnitId ipi) (installedComponentId ipi) (Map.fromList (instantiatedWith ipi))---- | Returns the set of module names which need to be filled for--- an indefinite package, or the empty set if the package is definite.-requiredSignatures :: InstalledPackageInfo -> Set ModuleName-requiredSignatures ipi = openModuleSubstFreeHoles (Map.fromList (instantiatedWith ipi))---- -------------------------------------------------------------------------------- Munging--sourceComponentName :: InstalledPackageInfo -> ComponentName-sourceComponentName = CLibName . sourceLibName---- -------------------------------------------------------------------------------- Parsing---- | Return either errors, or IPI with list of warnings-parseInstalledPackageInfo- :: ByteString- -> Either (NonEmpty String) ([String], InstalledPackageInfo)-parseInstalledPackageInfo s = case P.readFields s of- Left err -> Left (show err :| [])- Right fs -> case partitionFields fs of- (fs', _) -> case P.runParseResult $ parseFieldGrammar cabalSpecLatest fs' ipiFieldGrammar of- (ws, Right x) -> x `deepseq` Right (ws', x) where- ws' = [ P.showPWarning "" w- | w@(P.PWarning wt _ _) <- ws- -- filter out warnings about experimental features- , wt /= P.PWTExperimental- ]- (_, Left (_, errs)) -> Left errs' where- errs' = fmap (P.showPError "") errs---- -------------------------------------------------------------------------------- Pretty-printing---- | Pretty print 'InstalledPackageInfo'.------ @pkgRoot@ isn't printed, as ghc-pkg prints it manually (as GHC-8.4).-showInstalledPackageInfo :: InstalledPackageInfo -> String-showInstalledPackageInfo ipi =- showFullInstalledPackageInfo ipi { pkgRoot = Nothing }---- | The variant of 'showInstalledPackageInfo' which outputs @pkgroot@ field too.-showFullInstalledPackageInfo :: InstalledPackageInfo -> String-showFullInstalledPackageInfo = P.showFields (const []) . prettyFieldGrammar cabalSpecLatest ipiFieldGrammar---- |------ >>> let ipi = emptyInstalledPackageInfo { maintainer = "Tester" }--- >>> fmap ($ ipi) $ showInstalledPackageInfoField "maintainer"--- Just "maintainer: Tester"-showInstalledPackageInfoField :: String -> Maybe (InstalledPackageInfo -> String)-showInstalledPackageInfoField fn =- fmap (\g -> Disp.render . ppField fn . g) $ fieldDescrPretty ipiFieldGrammar (toUTF8BS fn)--showSimpleInstalledPackageInfoField :: String -> Maybe (InstalledPackageInfo -> String)-showSimpleInstalledPackageInfoField fn =- fmap (Disp.renderStyle myStyle .) $ fieldDescrPretty ipiFieldGrammar (toUTF8BS fn)- where- myStyle = Disp.style { Disp.mode = Disp.LeftMode }--ppField :: String -> Disp.Doc -> Disp.Doc-ppField name fielddoc- | Disp.isEmpty fielddoc = mempty- | otherwise = Disp.text name <<>> Disp.colon Disp.<+> fielddoc
src/Distribution/Lex.hs view
@@ -1,4 +1,5 @@ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Lex -- Copyright : Ben Gamari 2015-2019@@ -7,34 +8,43 @@ -- Portability : portable -- -- This module contains a simple lexer supporting quoted strings--module Distribution.Lex (- tokenizeQuotedWords- ) where+module Distribution.Lex+ ( tokenizeQuotedWords+ ) where -import Prelude ()-import Distribution.Compat.Prelude import Distribution.Compat.DList+import Distribution.Compat.Prelude+import Prelude () +-- | A simple parser supporting quoted strings.+--+-- Please be aware that this will only split strings when seeing whitespace+-- outside of quotation marks, i.e, @"foo\"bar baz\"qux quux"@ will be+-- converted to @["foobar bazqux", "quux"]@.+--+-- This behavior can be useful when parsing text like+-- @"ghc-options: -Wl,\"some option with spaces\""@, for instance. tokenizeQuotedWords :: String -> [String] tokenizeQuotedWords = filter (not . null) . go False mempty where- go :: Bool -- ^ in quoted region- -> DList Char -- ^ accumulator- -> String -- ^ string to be parsed- -> [String] -- ^ parse result+ go+ :: Bool+ -- \^ in quoted region+ -> DList Char+ -- \^ accumulator+ -> String+ -- \^ string to be parsed+ -> [String]+ -- \^ parse result go _ accum [] | [] <- accum' = []- | otherwise = [accum']- where accum' = runDList accum-- go False accum (c:cs)+ | otherwise = [accum']+ where+ accum' = runDList accum+ go False accum (c : cs) | isSpace c = runDList accum : go False mempty cs- | c == '"' = go True accum cs-- go True accum (c:cs)- | c == '"' = go False accum cs-- go quoted accum (c:cs)- = go quoted (accum `mappend` singleton c) cs-+ | c == '"' = go True accum cs+ go True accum (c : cs)+ | c == '"' = go False accum cs+ go quoted accum (c : cs) =+ go quoted (accum `mappend` singleton c) cs
− src/Distribution/License.hs
@@ -1,252 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}---------------------------------------------------------------------------------- |--- Module : Distribution.License--- Description : The License data type.--- Copyright : Isaac Jones 2003-2005--- Duncan Coutts 2008--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ Package descriptions contain fields for specifying the name of a software--- license and the name of the file containing the text of that license. While--- package authors may choose any license they like, Cabal provides an--- enumeration of a small set of common free and open source software licenses.--- This is done so that Hackage can recognise licenses, so that tools can detect--- <https://en.wikipedia.org/wiki/License_compatibility licensing conflicts>,--- and to deter--- <https://en.wikipedia.org/wiki/License_proliferation license proliferation>.------ It is recommended that all package authors use the @license-file@ or--- @license-files@ fields in their package descriptions. Further information--- about these fields can be found in the--- <http://www.haskell.org/cabal/users-guide/developing-packages.html#package-descriptions Cabal users guide>.------ = Additional resources------ The following websites provide information about free and open source--- software licenses:------ * <http://www.opensource.org The Open Source Initiative (OSI)>------ * <https://www.fsf.org The Free Software Foundation (FSF)>------ = Disclaimer------ The descriptions of software licenses provided by this documentation are--- intended for informational purposes only and in no way constitute legal--- advice. Please read the text of the licenses and consult a lawyer for any--- advice regarding software licensing.--module Distribution.License (- License(..),- knownLicenses,- licenseToSPDX,- licenseFromSPDX,- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Parsec-import Distribution.Pretty-import Distribution.Version--import qualified Distribution.Compat.CharParsing as P-import qualified Data.Map.Strict as Map-import qualified Distribution.SPDX as SPDX-import qualified Text.PrettyPrint as Disp---- | Indicates the license under which a package's source code is released.--- Versions of the licenses not listed here will be rejected by Hackage and--- cause @cabal check@ to issue a warning.-data License =- -- TODO: * remove BSD4-- -- | GNU General Public License,- -- <https://www.gnu.org/licenses/old-licenses/gpl-2.0.html version 2> or- -- <https://www.gnu.org/licenses/gpl.html version 3>.- GPL (Maybe Version)-- -- | <https://www.gnu.org/licenses/agpl.html GNU Affero General Public License, version 3>.- | AGPL (Maybe Version)-- -- | GNU Lesser General Public License,- -- <https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html version 2.1> or- -- <https://www.gnu.org/licenses/lgpl.html version 3>.- | LGPL (Maybe Version)-- -- | <http://www.opensource.org/licenses/bsd-license 2-clause BSD license>.- | BSD2-- -- | <http://www.opensource.org/licenses/bsd-3-clause 3-clause BSD license>.- | BSD3-- -- | <http://directory.fsf.org/wiki/License:BSD_4Clause 4-clause BSD license>.- -- This license has not been approved by the OSI and is incompatible with- -- the GNU GPL. It is provided for historical reasons and should be avoided.- | BSD4-- -- | <http://www.opensource.org/licenses/MIT MIT license>.- | MIT-- -- | <http://www.isc.org/downloads/software-support-policy/isc-license/ ISC license>- | ISC-- -- | <https://www.mozilla.org/MPL/ Mozilla Public License, version 2.0>.- | MPL Version-- -- | <https://www.apache.org/licenses/ Apache License, version 2.0>.- | Apache (Maybe Version)-- -- | The author of a package disclaims any copyright to its source code and- -- dedicates it to the public domain. This is not a software license. Please- -- note that it is not possible to dedicate works to the public domain in- -- every jurisdiction, nor is a work that is in the public domain in one- -- jurisdiction necessarily in the public domain elsewhere.- | PublicDomain-- -- | Explicitly 'All Rights Reserved', eg for proprietary software. The- -- package may not be legally modified or redistributed by anyone but the- -- rightsholder.- | AllRightsReserved-- -- | No license specified which legally defaults to 'All Rights Reserved'.- -- The package may not be legally modified or redistributed by anyone but- -- the rightsholder.- | UnspecifiedLicense-- -- | Any other software license.- | OtherLicense-- -- | Indicates an erroneous license name.- | UnknownLicense String- deriving (Generic, Read, Show, Eq, Typeable, Data)--instance Binary License-instance Structured License-instance NFData License where rnf = genericRnf---- | The list of all currently recognised licenses.-knownLicenses :: [License]-knownLicenses = [ GPL unversioned, GPL (version [2]), GPL (version [3])- , LGPL unversioned, LGPL (version [2, 1]), LGPL (version [3])- , AGPL unversioned, AGPL (version [3])- , BSD2, BSD3, MIT, ISC- , MPL (mkVersion [2, 0])- , Apache unversioned, Apache (version [2, 0])- , PublicDomain, AllRightsReserved, OtherLicense]- where- unversioned = Nothing- version = Just . mkVersion---- | Convert old 'License' to SPDX 'SPDX.License'.--- Non-SPDX licenses are converted to 'SPDX.LicenseRef'.------ @since 2.2.0.0-licenseToSPDX :: License -> SPDX.License-licenseToSPDX l = case l of- GPL v | v == version [2] -> spdx SPDX.GPL_2_0_only- GPL v | v == version [3] -> spdx SPDX.GPL_3_0_only- LGPL v | v == version [2,1] -> spdx SPDX.LGPL_2_1_only- LGPL v | v == version [3] -> spdx SPDX.LGPL_3_0_only- AGPL v | v == version [3] -> spdx SPDX.AGPL_3_0_only- BSD2 -> spdx SPDX.BSD_2_Clause- BSD3 -> spdx SPDX.BSD_3_Clause- BSD4 -> spdx SPDX.BSD_4_Clause- MIT -> spdx SPDX.MIT- ISC -> spdx SPDX.ISC- MPL v | v == mkVersion [2,0] -> spdx SPDX.MPL_2_0- Apache v | v == version [2,0] -> spdx SPDX.Apache_2_0- AllRightsReserved -> SPDX.NONE- UnspecifiedLicense -> SPDX.NONE- OtherLicense -> ref (SPDX.mkLicenseRef' Nothing "OtherLicense")- PublicDomain -> ref (SPDX.mkLicenseRef' Nothing "PublicDomain")- UnknownLicense str -> ref (SPDX.mkLicenseRef' Nothing str)- _ -> ref (SPDX.mkLicenseRef' Nothing $ prettyShow l)- where- version = Just . mkVersion- spdx = SPDX.License . SPDX.simpleLicenseExpression- ref r = SPDX.License $ SPDX.ELicense (SPDX.ELicenseRef r) Nothing---- | Convert 'SPDX.License' to 'License',------ This is lossy conversion. We try our best.------ >>> licenseFromSPDX . licenseToSPDX $ BSD3--- BSD3------ >>> licenseFromSPDX . licenseToSPDX $ GPL (Just (mkVersion [3]))--- GPL (Just (mkVersion [3]))------ >>> licenseFromSPDX . licenseToSPDX $ PublicDomain--- UnknownLicense "LicenseRefPublicDomain"------ >>> licenseFromSPDX $ SPDX.License $ SPDX.simpleLicenseExpression SPDX.EUPL_1_1--- UnknownLicense "EUPL-1.1"------ >>> licenseFromSPDX . licenseToSPDX $ AllRightsReserved--- AllRightsReserved------ >>> licenseFromSPDX <$> simpleParsec "BSD-3-Clause OR GPL-3.0-only"--- Just (UnknownLicense "BSD3ClauseORGPL30only")------ @since 2.2.0.0-licenseFromSPDX :: SPDX.License -> License-licenseFromSPDX SPDX.NONE = AllRightsReserved-licenseFromSPDX l =- fromMaybe (mungle $ prettyShow l) $ Map.lookup l m- where- m :: Map.Map SPDX.License License- m = Map.fromList $ filter (isSimple . fst ) $- map (\x -> (licenseToSPDX x, x)) knownLicenses-- isSimple (SPDX.License (SPDX.ELicense (SPDX.ELicenseId _) Nothing)) = True- isSimple _ = False-- mungle name = fromMaybe (UnknownLicense (mapMaybe mangle name)) (simpleParsec name)-- mangle c- | isAlphaNum c = Just c- | otherwise = Nothing--instance Pretty License where- pretty (GPL version) = Disp.text "GPL" <<>> dispOptVersion version- pretty (LGPL version) = Disp.text "LGPL" <<>> dispOptVersion version- pretty (AGPL version) = Disp.text "AGPL" <<>> dispOptVersion version- pretty (MPL version) = Disp.text "MPL" <<>> dispVersion version- pretty (Apache version) = Disp.text "Apache" <<>> dispOptVersion version- pretty (UnknownLicense other) = Disp.text other- pretty other = Disp.text (show other)--instance Parsec License where- parsec = do- name <- P.munch1 isAlphaNum- version <- P.optional (P.char '-' *> parsec)- return $! case (name, version :: Maybe Version) of- ("GPL", _ ) -> GPL version- ("LGPL", _ ) -> LGPL version- ("AGPL", _ ) -> AGPL version- ("BSD2", Nothing) -> BSD2- ("BSD3", Nothing) -> BSD3- ("BSD4", Nothing) -> BSD4- ("ISC", Nothing) -> ISC- ("MIT", Nothing) -> MIT- ("MPL", Just version') -> MPL version'- ("Apache", _ ) -> Apache version- ("PublicDomain", Nothing) -> PublicDomain- ("AllRightsReserved", Nothing) -> AllRightsReserved- ("OtherLicense", Nothing) -> OtherLicense- _ -> UnknownLicense $ name ++- maybe "" (('-':) . prettyShow) version--dispOptVersion :: Maybe Version -> Disp.Doc-dispOptVersion Nothing = Disp.empty-dispOptVersion (Just v) = dispVersion v--dispVersion :: Version -> Disp.Doc-dispVersion v = Disp.char '-' <<>> pretty v
src/Distribution/Make.hs view
@@ -2,6 +2,11 @@ {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------++-- copy :+-- $(MAKE) install prefix=$(destdir)/$(prefix) \+-- bindir=$(destdir)/$(bindir) \+ -- | -- Module : Distribution.Make -- Copyright : Martin Sjögren 2004@@ -51,32 +56,26 @@ -- [UnregisterCmd] We assume there is an @unregister@ target. -- -- [HaddockCmd] We assume there is a @docs@ or @doc@ target.----- copy :--- $(MAKE) install prefix=$(destdir)/$(prefix) \--- bindir=$(destdir)/$(bindir) \--module Distribution.Make (- module Distribution.Package,- License(..), Version,- defaultMain, defaultMainArgs+module Distribution.Make+ ( module Distribution.Package+ , License (..)+ , Version+ , defaultMain+ , defaultMainArgs ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -- local+import Distribution.License import Distribution.Package+import Distribution.Pretty+import Distribution.Simple.Command import Distribution.Simple.Program import Distribution.Simple.Setup-import Distribution.Simple.Command- import Distribution.Simple.Utils--import Distribution.License import Distribution.Version-import Distribution.Pretty import System.Environment (getArgs, getProgName) @@ -87,20 +86,21 @@ defaultMainArgs = defaultMainHelper defaultMainHelper :: [String] -> IO ()-defaultMainHelper args =- case commandsRun (globalCommand commands) commands args of- CommandHelp help -> printHelp help- CommandList opts -> printOptionsList opts- CommandErrors errs -> printErrors errs- CommandReadyToGo (flags, commandParse) ->+defaultMainHelper args = do+ command <- commandsRun (globalCommand commands) commands args+ case command of+ CommandHelp help -> printHelp help+ CommandList opts -> printOptionsList opts+ CommandErrors errs -> printErrors errs+ CommandReadyToGo (flags, commandParse) -> case commandParse of- _ | fromFlag (globalVersion flags) -> printVersion+ _+ | fromFlag (globalVersion flags) -> printVersion | fromFlag (globalNumericVersion flags) -> printNumericVersion- CommandHelp help -> printHelp help- CommandList opts -> printOptionsList opts- CommandErrors errs -> printErrors errs- CommandReadyToGo action -> action-+ CommandHelp help -> printHelp help+ CommandList opts -> printOptionsList opts+ CommandErrors errs -> printErrors errs+ CommandReadyToGo action -> action where printHelp help = getProgName >>= putStr . help printOptionsList = putStr . unlines@@ -108,75 +108,94 @@ putStr (intercalate "\n" errs) exitWith (ExitFailure 1) printNumericVersion = putStrLn $ prettyShow cabalVersion- printVersion = putStrLn $ "Cabal library version "- ++ prettyShow cabalVersion-+ printVersion =+ putStrLn $+ "Cabal library version "+ ++ prettyShow cabalVersion progs = defaultProgramDb commands =- [configureCommand progs `commandAddAction` configureAction- ,buildCommand progs `commandAddAction` buildAction- ,installCommand `commandAddAction` installAction- ,copyCommand `commandAddAction` copyAction- ,haddockCommand `commandAddAction` haddockAction- ,cleanCommand `commandAddAction` cleanAction- ,sdistCommand `commandAddAction` sdistAction- ,registerCommand `commandAddAction` registerAction- ,unregisterCommand `commandAddAction` unregisterAction+ [ configureCommand progs `commandAddAction` configureAction+ , buildCommand progs `commandAddAction` buildAction+ , installCommand `commandAddAction` installAction+ , copyCommand `commandAddAction` copyAction+ , haddockCommand `commandAddAction` haddockAction+ , cleanCommand `commandAddAction` cleanAction+ , sdistCommand `commandAddAction` sdistAction+ , registerCommand `commandAddAction` registerAction+ , unregisterCommand `commandAddAction` unregisterAction ] configureAction :: ConfigFlags -> [String] -> IO () configureAction flags args = do noExtraFlags args- let verbosity = fromFlag (configVerbosity flags)- rawSystemExit verbosity "sh" $+ let verbosity = fromFlag $ configVerbosity flags+ mbWorkDir = flagToMaybe $ configWorkingDir flags+ rawSystemExit verbosity mbWorkDir "sh" $ "configure"- : configureArgs backwardsCompatHack flags- where backwardsCompatHack = True+ : configureArgs backwardsCompatHack flags+ where+ backwardsCompatHack = True copyAction :: CopyFlags -> [String] -> IO () copyAction flags args = do noExtraFlags args- let destArgs = case fromFlag $ copyDest flags of- NoCopyDest -> ["install"]- CopyTo path -> ["copy", "destdir=" ++ path]- CopyToDb _ -> error "CopyToDb not supported via Make"+ let verbosity = fromFlag $ copyVerbosity flags+ mbWorkDir = flagToMaybe $ copyWorkingDir flags+ destArgs = case fromFlag $ copyDest flags of+ NoCopyDest -> ["install"]+ CopyTo path -> ["copy", "destdir=" ++ path]+ CopyToDb _ -> error "CopyToDb not supported via Make" - rawSystemExit (fromFlag $ copyVerbosity flags) "make" destArgs+ rawSystemExit verbosity mbWorkDir "make" destArgs installAction :: InstallFlags -> [String] -> IO () installAction flags args = do noExtraFlags args- rawSystemExit (fromFlag $ installVerbosity flags) "make" ["install"]- rawSystemExit (fromFlag $ installVerbosity flags) "make" ["register"]+ let verbosity = fromFlag $ installVerbosity flags+ mbWorkDir = flagToMaybe $ installWorkingDir flags+ rawSystemExit verbosity mbWorkDir "make" ["install"]+ rawSystemExit verbosity mbWorkDir "make" ["register"] haddockAction :: HaddockFlags -> [String] -> IO () haddockAction flags args = do noExtraFlags args- rawSystemExit (fromFlag $ haddockVerbosity flags) "make" ["docs"]+ let verbosity = fromFlag $ haddockVerbosity flags+ mbWorkDir = flagToMaybe $ haddockWorkingDir flags+ rawSystemExit verbosity mbWorkDir "make" ["docs"] `catchIO` \_ ->- rawSystemExit (fromFlag $ haddockVerbosity flags) "make" ["doc"]+ rawSystemExit verbosity mbWorkDir "make" ["doc"] buildAction :: BuildFlags -> [String] -> IO () buildAction flags args = do noExtraFlags args- rawSystemExit (fromFlag $ buildVerbosity flags) "make" []+ let verbosity = fromFlag $ buildVerbosity flags+ mbWorkDir = flagToMaybe $ buildWorkingDir flags+ rawSystemExit verbosity mbWorkDir "make" [] cleanAction :: CleanFlags -> [String] -> IO () cleanAction flags args = do noExtraFlags args- rawSystemExit (fromFlag $ cleanVerbosity flags) "make" ["clean"]+ let verbosity = fromFlag $ cleanVerbosity flags+ mbWorkDir = flagToMaybe $ cleanWorkingDir flags+ rawSystemExit verbosity mbWorkDir "make" ["clean"] sdistAction :: SDistFlags -> [String] -> IO () sdistAction flags args = do noExtraFlags args- rawSystemExit (fromFlag $ sDistVerbosity flags) "make" ["dist"]+ let verbosity = fromFlag $ sDistVerbosity flags+ mbWorkDir = flagToMaybe $ sDistWorkingDir flags+ rawSystemExit verbosity mbWorkDir "make" ["dist"] registerAction :: RegisterFlags -> [String] -> IO ()-registerAction flags args = do+registerAction flags args = do noExtraFlags args- rawSystemExit (fromFlag $ regVerbosity flags) "make" ["register"]+ let verbosity = fromFlag $ registerVerbosity flags+ mbWorkDir = flagToMaybe $ registerWorkingDir flags+ rawSystemExit verbosity mbWorkDir "make" ["register"] unregisterAction :: RegisterFlags -> [String] -> IO () unregisterAction flags args = do noExtraFlags args- rawSystemExit (fromFlag $ regVerbosity flags) "make" ["unregister"]+ let verbosity = fromFlag $ registerVerbosity flags+ mbWorkDir = flagToMaybe $ registerWorkingDir flags+ rawSystemExit verbosity mbWorkDir "make" ["unregister"]
− src/Distribution/ModuleName.hs
@@ -1,131 +0,0 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE ScopedTypeVariables #-}--------------------------------------------------------------------------------- |--- Module : Distribution.ModuleName--- Copyright : Duncan Coutts 2008--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ Data type for Haskell module names.--module Distribution.ModuleName (- ModuleName,- fromString,- fromComponents,- components,- toFilePath,- main,- -- * Internal- validModuleComponent,- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Parsec-import Distribution.Pretty-import Distribution.Utils.ShortText (ShortText, fromShortText, toShortText)-import System.FilePath (pathSeparator)--import qualified Distribution.Compat.CharParsing as P-import qualified Distribution.Compat.DList as DList-import qualified Text.PrettyPrint as Disp---- | A valid Haskell module name.----newtype ModuleName = ModuleName ShortText- deriving (Eq, Generic, Ord, Read, Show, Typeable, Data)--unModuleName :: ModuleName -> String-unModuleName (ModuleName s) = fromShortText s--instance Binary ModuleName-instance Structured ModuleName--instance NFData ModuleName where- rnf (ModuleName ms) = rnf ms--instance Pretty ModuleName where- pretty = Disp.text . unModuleName--instance Parsec ModuleName where- parsec = parsecModuleName--parsecModuleName :: forall m. CabalParsing m => m ModuleName-parsecModuleName = state0 DList.empty where- upper :: m Char- !upper = P.satisfy isUpper-- ch :: m Char- !ch = P.satisfy (\c -> validModuleChar c || c == '.')-- alt :: m ModuleName -> m ModuleName -> m ModuleName- !alt = (<|>)-- state0 :: DList.DList Char -> m ModuleName- state0 acc = do- c <- upper- state1 (DList.snoc acc c)-- state1 :: DList.DList Char -> m ModuleName- state1 acc = state1' acc `alt` return (fromString (DList.toList acc))-- state1' :: DList.DList Char -> m ModuleName- state1' acc = do- c <- ch- case c of- '.' -> state0 (DList.snoc acc c)- _ -> state1 (DList.snoc acc c)--validModuleChar :: Char -> Bool-validModuleChar c = isAlphaNum c || c == '_' || c == '\''--validModuleComponent :: String -> Bool-validModuleComponent [] = False-validModuleComponent (c:cs) = isUpper c && all validModuleChar cs---- | Construct a 'ModuleName' from a valid module name 'String'.------ This is just a convenience function intended for valid module strings. It is--- an error if it is used with a string that is not a valid module name. If you--- are parsing user input then use 'Distribution.Text.simpleParse' instead.----instance IsString ModuleName where- fromString = ModuleName . toShortText---- | Construct a 'ModuleName' from valid module components, i.e. parts--- separated by dots.-fromComponents :: [String] -> ModuleName-fromComponents comps = fromString (intercalate "." comps)-{-# DEPRECATED fromComponents "Exists for cabal-install only" #-}---- | The module name @Main@.----main :: ModuleName-main = ModuleName (fromString "Main")---- | The individual components of a hierarchical module name. For example------ > components (fromString "A.B.C") = ["A", "B", "C"]----components :: ModuleName -> [String]-components mn = split (unModuleName mn)- where- split cs = case break (=='.') cs of- (chunk,[]) -> chunk : []- (chunk,_:rest) -> chunk : split rest---- | Convert a module name to a file path, but without any file extension.--- For example:------ > toFilePath (fromString "A.B.C") = "A/B/C"----toFilePath :: ModuleName -> FilePath-toFilePath = map f . unModuleName where- f '.' = pathSeparator- f c = c
− src/Distribution/Package.hs
@@ -1,96 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}---------------------------------------------------------------------------------- |--- Module : Distribution.Package--- Copyright : Isaac Jones 2003-2004--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ Defines a package identifier along with a parser and pretty printer for it.--- 'PackageIdentifier's consist of a name and an exact version. It also defines--- a 'Dependency' data type. A dependency is a package name and a version--- range, like @\"foo >= 1.2 && < 2\"@.--module Distribution.Package- ( module Distribution.Types.AbiHash- , module Distribution.Types.ComponentId- , module Distribution.Types.PackageId- , module Distribution.Types.UnitId- , module Distribution.Types.Module- , module Distribution.Types.PackageName- , module Distribution.Types.PkgconfigName- , module Distribution.Types.Dependency- , Package(..), packageName, packageVersion- , HasMungedPackageId(..), mungedName', mungedVersion'- , HasUnitId(..)- , PackageInstalled(..)- ) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Version- ( Version )--import Distribution.Types.AbiHash-import Distribution.Types.ComponentId-import Distribution.Types.Dependency-import Distribution.Types.MungedPackageId-import Distribution.Types.PackageId-import Distribution.Types.UnitId-import Distribution.Types.Module-import Distribution.Types.MungedPackageName-import Distribution.Types.PackageName-import Distribution.Types.PkgconfigName---- | Class of things that have a 'PackageIdentifier'------ Types in this class are all notions of a package. This allows us to have--- different types for the different phases that packages go though, from--- simple name\/id, package description, configured or installed packages.------ Not all kinds of packages can be uniquely identified by a--- 'PackageIdentifier'. In particular, installed packages cannot, there may be--- many installed instances of the same source package.----class Package pkg where- packageId :: pkg -> PackageIdentifier--mungedName' :: HasMungedPackageId pkg => pkg -> MungedPackageName-mungedName' = mungedName . mungedId--mungedVersion' :: HasMungedPackageId munged => munged -> Version-mungedVersion' = mungedVersion . mungedId--class HasMungedPackageId pkg where- mungedId :: pkg -> MungedPackageId--instance Package PackageIdentifier where- packageId = id--packageName :: Package pkg => pkg -> PackageName-packageName = pkgName . packageId--packageVersion :: Package pkg => pkg -> Version-packageVersion = pkgVersion . packageId--instance HasMungedPackageId MungedPackageId where- mungedId = id---- | Packages that have an installed unit ID-class Package pkg => HasUnitId pkg where- installedUnitId :: pkg -> UnitId---- | Class of installed packages.------ The primary data type which is an instance of this package is--- 'InstalledPackageInfo', but when we are doing install plans in Cabal install--- we may have other, installed package-like things which contain more metadata.--- Installed packages have exact dependencies 'installedDepends'.-class (HasUnitId pkg) => PackageInstalled pkg where- installedDepends :: pkg -> [UnitId]
− src/Distribution/PackageDescription.hs
@@ -1,113 +0,0 @@--------------------------------------------------------------------------------- |--- Module : Distribution.PackageDescription--- Copyright : Isaac Jones 2003-2005--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ Backwards compatibility reexport of most things you need to know--- about @.cabal@ files.--module Distribution.PackageDescription (- -- * PD and GPD- module Distribution.Types.PackageDescription,- module Distribution.Types.GenericPackageDescription,- -- * Components- module Distribution.Types.ComponentName,- -- ** Library- module Distribution.Types.Library,- module Distribution.Types.LibraryName,- module Distribution.Types.LibraryVisibility,- -- ** Executable- module Distribution.Types.Executable,- module Distribution.Types.ExecutableScope,- -- ** TestSuite- module Distribution.Types.TestSuite,- module Distribution.Types.TestType,- module Distribution.Types.TestSuiteInterface,- -- ** Benchmark- module Distribution.Types.Benchmark,- module Distribution.Types.BenchmarkType,- module Distribution.Types.BenchmarkInterface,- -- ** Foreign library- module Distribution.Types.ForeignLib,- module Distribution.Types.ForeignLibType,- module Distribution.Types.ForeignLibOption,- -- * BuildInfo- module Distribution.Types.BuildType,- module Distribution.Types.BuildInfo,- module Distribution.Types.HookedBuildInfo,- module Distribution.Types.SetupBuildInfo,- -- * Flags- module Distribution.Types.Flag,- -- * Identifiers- module Distribution.Types.PackageId,- module Distribution.Types.PackageName,- module Distribution.Types.UnqualComponentName,- -- * Pkgconfig- module Distribution.Types.PkgconfigName,- module Distribution.Types.PkgconfigVersion,- module Distribution.Types.PkgconfigVersionRange,- -- * Dependencies- module Distribution.Types.Dependency,- module Distribution.Types.ExeDependency,- module Distribution.Types.LegacyExeDependency,- module Distribution.Types.PkgconfigDependency,- -- * Condition trees- module Distribution.Types.CondTree,- module Distribution.Types.Condition,- module Distribution.Types.ConfVar,- -- * Mixin- module Distribution.Types.IncludeRenaming,- module Distribution.Types.Mixin,- module Distribution.Types.ModuleReexport,- module Distribution.Types.ModuleRenaming,- -- * Source repository- module Distribution.Types.SourceRepo,- ) where--import Prelude ()---import Distribution.Compat.Prelude--import Distribution.Types.Benchmark-import Distribution.Types.BenchmarkInterface-import Distribution.Types.BenchmarkType-import Distribution.Types.BuildInfo-import Distribution.Types.BuildType-import Distribution.Types.ComponentName-import Distribution.Types.Condition-import Distribution.Types.CondTree-import Distribution.Types.ConfVar-import Distribution.Types.Dependency-import Distribution.Types.Executable-import Distribution.Types.ExecutableScope-import Distribution.Types.ExeDependency-import Distribution.Types.Flag-import Distribution.Types.ForeignLib-import Distribution.Types.ForeignLibOption-import Distribution.Types.ForeignLibType-import Distribution.Types.GenericPackageDescription-import Distribution.Types.HookedBuildInfo-import Distribution.Types.IncludeRenaming-import Distribution.Types.LegacyExeDependency-import Distribution.Types.Library-import Distribution.Types.LibraryName-import Distribution.Types.LibraryVisibility-import Distribution.Types.Mixin-import Distribution.Types.ModuleReexport-import Distribution.Types.ModuleRenaming-import Distribution.Types.PackageDescription-import Distribution.Types.PackageId-import Distribution.Types.PackageName-import Distribution.Types.PkgconfigDependency-import Distribution.Types.PkgconfigName-import Distribution.Types.PkgconfigVersion-import Distribution.Types.PkgconfigVersionRange-import Distribution.Types.SetupBuildInfo-import Distribution.Types.SourceRepo-import Distribution.Types.TestSuite-import Distribution.Types.TestSuiteInterface-import Distribution.Types.TestType-import Distribution.Types.UnqualComponentName
src/Distribution/PackageDescription/Check.hs view
@@ -1,2392 +1,1111 @@--------------------------------------------------------------------------------- |--- Module : Distribution.PackageDescription.Check--- Copyright : Lennart Kolmodin 2008--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ This has code for checking for various problems in packages. There is one--- set of checks that just looks at a 'PackageDescription' in isolation and--- another set of checks that also looks at files in the package. Some of the--- checks are basic sanity checks, others are portability standards that we'd--- like to encourage. There is a 'PackageCheck' type that distinguishes the--- different kinds of checks so we can see which ones are appropriate to report--- in different situations. This code gets used when configuring a package when--- we consider only basic problems. The higher standard is used when--- preparing a source tarball and by Hackage when uploading new packages. The--- reason for this is that we want to hold packages that are expected to be--- distributed to a higher standard than packages that are only ever expected--- to be used on the author's own environment.--module Distribution.PackageDescription.Check (- -- * Package Checking- PackageCheck(..),- checkPackage,- checkConfiguredPackage,-- -- ** Checking package contents- checkPackageFiles,- checkPackageContent,- CheckPackageContentOps(..),- checkPackageFileNames,- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Data.List (group)-import Distribution.CabalSpecVersion-import Distribution.Compat.Lens-import Distribution.Compiler-import Distribution.License-import Distribution.Package-import Distribution.PackageDescription-import Distribution.PackageDescription.Configuration-import Distribution.Pretty (prettyShow)-import Distribution.Simple.BuildPaths (autogenPathsModuleName)-import Distribution.Simple.BuildToolDepends-import Distribution.Simple.CCompiler-import Distribution.Simple.Glob-import Distribution.Simple.Utils hiding (findPackageDesc, notice)-import Distribution.System-import Distribution.Types.ComponentRequestedSpec-import Distribution.Utils.Generic (isAscii)-import Distribution.Verbosity-import Distribution.Version-import Distribution.Utils.Path-import Language.Haskell.Extension-import System.FilePath- (splitDirectories, splitExtension, splitPath, takeExtension, takeFileName, (<.>), (</>))--import qualified Data.ByteString.Lazy as BS-import qualified Data.Map as Map-import qualified Distribution.Compat.DList as DList-import qualified Distribution.SPDX as SPDX-import qualified System.Directory as System--import qualified System.Directory (getDirectoryContents)-import qualified System.FilePath.Windows as FilePath.Windows (isValid)--import qualified Data.Set as Set-import qualified Distribution.Utils.ShortText as ShortText--import qualified Distribution.Types.BuildInfo.Lens as L-import qualified Distribution.Types.GenericPackageDescription.Lens as L-import qualified Distribution.Types.PackageDescription.Lens as L---- $setup--- >>> import Control.Arrow ((&&&))---- | Results of some kind of failed package check.------ There are a range of severities, from merely dubious to totally insane.--- All of them come with a human readable explanation. In future we may augment--- them with more machine readable explanations, for example to help an IDE--- suggest automatic corrections.----data PackageCheck =-- -- | This package description is no good. There's no way it's going to- -- build sensibly. This should give an error at configure time.- PackageBuildImpossible { explanation :: String }-- -- | A problem that is likely to affect building the package, or an- -- issue that we'd like every package author to be aware of, even if- -- the package is never distributed.- | PackageBuildWarning { explanation :: String }-- -- | An issue that might not be a problem for the package author but- -- might be annoying or detrimental when the package is distributed to- -- users. We should encourage distributed packages to be free from these- -- issues, but occasionally there are justifiable reasons so we cannot- -- ban them entirely.- | PackageDistSuspicious { explanation :: String }-- -- | Like PackageDistSuspicious but will only display warnings- -- rather than causing abnormal exit when you run 'cabal check'.- | PackageDistSuspiciousWarn { explanation :: String }-- -- | An issue that is OK in the author's environment but is almost- -- certain to be a portability problem for other environments. We can- -- quite legitimately refuse to publicly distribute packages with these- -- problems.- | PackageDistInexcusable { explanation :: String }- deriving (Eq, Ord)--instance Show PackageCheck where- show notice = explanation notice--check :: Bool -> PackageCheck -> Maybe PackageCheck-check False _ = Nothing-check True pc = Just pc--checkSpecVersion :: PackageDescription -> CabalSpecVersion -> Bool -> PackageCheck- -> Maybe PackageCheck-checkSpecVersion pkg specver cond pc- | specVersion pkg >= specver = Nothing- | otherwise = check cond pc---- --------------------------------------------------------------- * Standard checks--- ---------------------------------------------------------------- | Check for common mistakes and problems in package descriptions.------ This is the standard collection of checks covering all aspects except--- for checks that require looking at files within the package. For those--- see 'checkPackageFiles'.------ It requires the 'GenericPackageDescription' and optionally a particular--- configuration of that package. If you pass 'Nothing' then we just check--- a version of the generic description using 'flattenPackageDescription'.----checkPackage :: GenericPackageDescription- -> Maybe PackageDescription- -> [PackageCheck]-checkPackage gpkg mpkg =- checkConfiguredPackage pkg- ++ checkConditionals gpkg- ++ checkPackageVersions gpkg- ++ checkDevelopmentOnlyFlags gpkg- ++ checkFlagNames gpkg- ++ checkUnusedFlags gpkg- ++ checkUnicodeXFields gpkg- ++ checkPathsModuleExtensions pkg- where- pkg = fromMaybe (flattenPackageDescription gpkg) mpkg----TODO: make this variant go away--- we should always know the GenericPackageDescription-checkConfiguredPackage :: PackageDescription -> [PackageCheck]-checkConfiguredPackage pkg =- checkSanity pkg- ++ checkFields pkg- ++ checkLicense pkg- ++ checkSourceRepos pkg- ++ checkAllGhcOptions pkg- ++ checkCCOptions pkg- ++ checkCxxOptions pkg- ++ checkCPPOptions pkg- ++ checkPaths pkg- ++ checkCabalVersion pkg----- --------------------------------------------------------------- * Basic sanity checks--- ---------------------------------------------------------------- | Check that this package description is sane.----checkSanity :: PackageDescription -> [PackageCheck]-checkSanity pkg =- catMaybes [-- check (null . unPackageName . packageName $ pkg) $- PackageBuildImpossible "No 'name' field."-- , check (nullVersion == packageVersion pkg) $- PackageBuildImpossible "No 'version' field."-- , check (all ($ pkg) [ null . executables- , null . testSuites- , null . benchmarks- , null . allLibraries- , null . foreignLibs ]) $- PackageBuildImpossible- "No executables, libraries, tests, or benchmarks found. Nothing to do."-- , check (any (== LMainLibName) (map libName $ subLibraries pkg)) $- PackageBuildImpossible $ "Found one or more unnamed internal libraries. "- ++ "Only the non-internal library can have the same name as the package."-- , check (not (null duplicateNames)) $- PackageBuildImpossible $ "Duplicate sections: "- ++ commaSep (map unUnqualComponentName duplicateNames)- ++ ". The name of every library, executable, test suite,"- ++ " and benchmark section in"- ++ " the package must be unique."-- -- NB: but it's OK for executables to have the same name!- -- TODO shouldn't need to compare on the string level- , check (any (== prettyShow (packageName pkg)) (prettyShow <$> subLibNames)) $- PackageBuildImpossible $ "Illegal internal library name "- ++ prettyShow (packageName pkg)- ++ ". Internal libraries cannot have the same name as the package."- ++ " Maybe you wanted a non-internal library?"- ++ " If so, rewrite the section stanza"- ++ " from 'library: '" ++ prettyShow (packageName pkg) ++ "' to 'library'."- ]- --TODO: check for name clashes case insensitively: windows file systems cannot- --cope.-- ++ concatMap (checkLibrary pkg) (allLibraries pkg)- ++ concatMap (checkExecutable pkg) (executables pkg)- ++ concatMap (checkTestSuite pkg) (testSuites pkg)- ++ concatMap (checkBenchmark pkg) (benchmarks pkg)-- where- -- The public 'library' gets special dispensation, because it- -- is common practice to export a library and name the executable- -- the same as the package.- subLibNames = mapMaybe (libraryNameString . libName) $ subLibraries pkg- exeNames = map exeName $ executables pkg- testNames = map testName $ testSuites pkg- bmNames = map benchmarkName $ benchmarks pkg- duplicateNames = dups $ subLibNames ++ exeNames ++ testNames ++ bmNames--checkLibrary :: PackageDescription -> Library -> [PackageCheck]-checkLibrary pkg lib =- catMaybes [-- check (not (null moduleDuplicates)) $- PackageBuildImpossible $- "Duplicate modules in library: "- ++ commaSep (map prettyShow moduleDuplicates)-- -- TODO: This check is bogus if a required-signature was passed through- , check (null (explicitLibModules lib) && null (reexportedModules lib)) $- PackageDistSuspiciousWarn $- showLibraryName (libName lib) ++ " does not expose any modules"-- -- check use of signatures sections- , checkVersion CabalSpecV2_0 (not (null (signatures lib))) $- PackageDistInexcusable $- "To use the 'signatures' field the package needs to specify "- ++ "at least 'cabal-version: 2.0'."-- -- check that all autogen-modules appear on other-modules or exposed-modules- , check- (not $ and $ map (flip elem (explicitLibModules lib)) (libModulesAutogen lib)) $- PackageBuildImpossible $- "An 'autogen-module' is neither on 'exposed-modules' or "- ++ "'other-modules'."-- -- check that all autogen-includes appear on includes or install-includes- , check- (not $ and $ map (flip elem (allExplicitIncludes lib)) (view L.autogenIncludes lib)) $- PackageBuildImpossible $- "An include in 'autogen-includes' is neither in 'includes' or "- ++ "'install-includes'."- ]-- where- checkVersion :: CabalSpecVersion -> Bool -> PackageCheck -> Maybe PackageCheck- checkVersion ver cond pc- | specVersion pkg >= ver = Nothing- | otherwise = check cond pc-- -- TODO: not sure if this check is always right in Backpack- moduleDuplicates = dups (explicitLibModules lib ++- map moduleReexportName (reexportedModules lib))--allExplicitIncludes :: L.HasBuildInfo a => a -> [FilePath]-allExplicitIncludes x = view L.includes x ++ view L.installIncludes x--checkExecutable :: PackageDescription -> Executable -> [PackageCheck]-checkExecutable pkg exe =- catMaybes [-- check (null (modulePath exe)) $- PackageBuildImpossible $- "No 'main-is' field found for executable " ++ prettyShow (exeName exe)-- , check (not (null (modulePath exe))- && (not $ fileExtensionSupportedLanguage $ modulePath exe)) $- PackageBuildImpossible $- "The 'main-is' field must specify a '.hs' or '.lhs' file "- ++ "(even if it is generated by a preprocessor), "- ++ "or it may specify a C/C++/obj-C source file."-- , checkSpecVersion pkg CabalSpecV1_18- (fileExtensionSupportedLanguage (modulePath exe)- && takeExtension (modulePath exe) `notElem` [".hs", ".lhs"]) $- PackageDistInexcusable $- "The package uses a C/C++/obj-C source file for the 'main-is' field. "- ++ "To use this feature you must specify 'cabal-version: >= 1.18'."-- , check (not (null moduleDuplicates)) $- PackageBuildImpossible $- "Duplicate modules in executable '" ++ prettyShow (exeName exe) ++ "': "- ++ commaSep (map prettyShow moduleDuplicates)-- -- check that all autogen-modules appear on other-modules- , check- (not $ and $ map (flip elem (exeModules exe)) (exeModulesAutogen exe)) $- PackageBuildImpossible $- "On executable '" ++ prettyShow (exeName exe) ++ "' an 'autogen-module' is not "- ++ "on 'other-modules'"-- -- check that all autogen-includes appear on includes- , check- (not $ and $ map (flip elem (view L.includes exe)) (view L.autogenIncludes exe)) $- PackageBuildImpossible "An include in 'autogen-includes' is not in 'includes'."- ]- where- moduleDuplicates = dups (exeModules exe)--checkTestSuite :: PackageDescription -> TestSuite -> [PackageCheck]-checkTestSuite pkg test =- catMaybes [-- case testInterface test of- TestSuiteUnsupported tt@(TestTypeUnknown _ _) -> Just $- PackageBuildWarning $- quote (prettyShow tt) ++ " is not a known type of test suite. "- ++ "The known test suite types are: "- ++ commaSep (map prettyShow knownTestTypes)-- TestSuiteUnsupported tt -> Just $- PackageBuildWarning $- quote (prettyShow tt) ++ " is not a supported test suite version. "- ++ "The known test suite types are: "- ++ commaSep (map prettyShow knownTestTypes)- _ -> Nothing-- , check (not $ null moduleDuplicates) $- PackageBuildImpossible $- "Duplicate modules in test suite '" ++ prettyShow (testName test) ++ "': "- ++ commaSep (map prettyShow moduleDuplicates)-- , check mainIsWrongExt $- PackageBuildImpossible $- "The 'main-is' field must specify a '.hs' or '.lhs' file "- ++ "(even if it is generated by a preprocessor), "- ++ "or it may specify a C/C++/obj-C source file."-- , checkSpecVersion pkg CabalSpecV1_18 (mainIsNotHsExt && not mainIsWrongExt) $- PackageDistInexcusable $- "The package uses a C/C++/obj-C source file for the 'main-is' field. "- ++ "To use this feature you must specify 'cabal-version: >= 1.18'."-- -- check that all autogen-modules appear on other-modules- , check- (not $ and $ map (flip elem (testModules test)) (testModulesAutogen test)) $- PackageBuildImpossible $- "On test suite '" ++ prettyShow (testName test) ++ "' an 'autogen-module' is not "- ++ "on 'other-modules'"-- -- check that all autogen-includes appear on includes- , check- (not $ and $ map (flip elem (view L.includes test)) (view L.autogenIncludes test)) $- PackageBuildImpossible "An include in 'autogen-includes' is not in 'includes'."- ]- where- moduleDuplicates = dups $ testModules test-- mainIsWrongExt = case testInterface test of- TestSuiteExeV10 _ f -> not $ fileExtensionSupportedLanguage f- _ -> False-- mainIsNotHsExt = case testInterface test of- TestSuiteExeV10 _ f -> takeExtension f `notElem` [".hs", ".lhs"]- _ -> False--checkBenchmark :: PackageDescription -> Benchmark -> [PackageCheck]-checkBenchmark _pkg bm =- catMaybes [-- case benchmarkInterface bm of- BenchmarkUnsupported tt@(BenchmarkTypeUnknown _ _) -> Just $- PackageBuildWarning $- quote (prettyShow tt) ++ " is not a known type of benchmark. "- ++ "The known benchmark types are: "- ++ commaSep (map prettyShow knownBenchmarkTypes)-- BenchmarkUnsupported tt -> Just $- PackageBuildWarning $- quote (prettyShow tt) ++ " is not a supported benchmark version. "- ++ "The known benchmark types are: "- ++ commaSep (map prettyShow knownBenchmarkTypes)- _ -> Nothing-- , check (not $ null moduleDuplicates) $- PackageBuildImpossible $- "Duplicate modules in benchmark '" ++ prettyShow (benchmarkName bm) ++ "': "- ++ commaSep (map prettyShow moduleDuplicates)-- , check mainIsWrongExt $- PackageBuildImpossible $- "The 'main-is' field must specify a '.hs' or '.lhs' file "- ++ "(even if it is generated by a preprocessor)."-- -- check that all autogen-modules appear on other-modules- , check- (not $ and $ map (flip elem (benchmarkModules bm)) (benchmarkModulesAutogen bm)) $- PackageBuildImpossible $- "On benchmark '" ++ prettyShow (benchmarkName bm) ++ "' an 'autogen-module' is "- ++ "not on 'other-modules'"-- -- check that all autogen-includes appear on includes- , check- (not $ and $ map (flip elem (view L.includes bm)) (view L.autogenIncludes bm)) $- PackageBuildImpossible "An include in 'autogen-includes' is not in 'includes'."- ]- where- moduleDuplicates = dups $ benchmarkModules bm-- mainIsWrongExt = case benchmarkInterface bm of- BenchmarkExeV10 _ f -> takeExtension f `notElem` [".hs", ".lhs"]- _ -> False---- --------------------------------------------------------------- * Additional pure checks--- --------------------------------------------------------------checkFields :: PackageDescription -> [PackageCheck]-checkFields pkg =- catMaybes [-- check (not . FilePath.Windows.isValid . prettyShow . packageName $ pkg) $- PackageDistInexcusable $- "Unfortunately, the package name '" ++ prettyShow (packageName pkg)- ++ "' is one of the reserved system file names on Windows. Many tools "- ++ "need to convert package names to file names so using this name "- ++ "would cause problems."-- , check ((isPrefixOf "z-") . prettyShow . packageName $ pkg) $- PackageDistInexcusable $- "Package names with the prefix 'z-' are reserved by Cabal and "- ++ "cannot be used."-- , check (isNothing (buildTypeRaw pkg) && specVersion pkg < CabalSpecV2_2) $- PackageBuildWarning $- "No 'build-type' specified. If you do not need a custom Setup.hs or "- ++ "./configure script then use 'build-type: Simple'."-- , check (isJust (setupBuildInfo pkg) && buildType pkg /= Custom) $- PackageBuildWarning $- "Ignoring the 'custom-setup' section because the 'build-type' is "- ++ "not 'Custom'. Use 'build-type: Custom' if you need to use a "- ++ "custom Setup.hs script."-- , check (not (null unknownCompilers)) $- PackageBuildWarning $- "Unknown compiler " ++ commaSep (map quote unknownCompilers)- ++ " in 'tested-with' field."-- , check (not (null unknownLanguages)) $- PackageBuildWarning $- "Unknown languages: " ++ commaSep unknownLanguages-- , check (not (null unknownExtensions)) $- PackageBuildWarning $- "Unknown extensions: " ++ commaSep unknownExtensions-- , check (not (null languagesUsedAsExtensions)) $- PackageBuildWarning $- "Languages listed as extensions: "- ++ commaSep languagesUsedAsExtensions- ++ ". Languages must be specified in either the 'default-language' "- ++ " or the 'other-languages' field."-- , check (not (null ourDeprecatedExtensions)) $- PackageDistSuspicious $- "Deprecated extensions: "- ++ commaSep (map (quote . prettyShow . fst) ourDeprecatedExtensions)- ++ ". " ++ unwords- [ "Instead of '" ++ prettyShow ext- ++ "' use '" ++ prettyShow replacement ++ "'."- | (ext, Just replacement) <- ourDeprecatedExtensions ]-- , check (ShortText.null (category pkg)) $- PackageDistSuspicious "No 'category' field."-- , check (ShortText.null (maintainer pkg)) $- PackageDistSuspicious "No 'maintainer' field."-- , check (ShortText.null (synopsis pkg) && ShortText.null (description pkg)) $- PackageDistInexcusable "No 'synopsis' or 'description' field."-- , check (ShortText.null (description pkg) && not (ShortText.null (synopsis pkg))) $- PackageDistSuspicious "No 'description' field."-- , check (ShortText.null (synopsis pkg) && not (ShortText.null (description pkg))) $- PackageDistSuspicious "No 'synopsis' field."-- --TODO: recommend the bug reports URL, author and homepage fields- --TODO: recommend not using the stability field- --TODO: recommend specifying a source repo-- , check (ShortText.length (synopsis pkg) >= 80) $- PackageDistSuspicious- "The 'synopsis' field is rather long (max 80 chars is recommended)."-- -- See also https://github.com/haskell/cabal/pull/3479- , check (not (ShortText.null (description pkg))- && ShortText.length (description pkg) <= ShortText.length (synopsis pkg)) $- PackageDistSuspicious $- "The 'description' field should be longer than the 'synopsis' "- ++ "field. "- ++ "It's useful to provide an informative 'description' to allow "- ++ "Haskell programmers who have never heard about your package to "- ++ "understand the purpose of your package. "- ++ "The 'description' field content is typically shown by tooling "- ++ "(e.g. 'cabal info', Haddock, Hackage) below the 'synopsis' which "- ++ "serves as a headline. "- ++ "Please refer to <https://www.haskell.org/"- ++ "cabal/users-guide/developing-packages.html#package-properties>"- ++ " for more details."-- -- check use of impossible constraints "tested-with: GHC== 6.10 && ==6.12"- , check (not (null testedWithImpossibleRanges)) $- PackageDistInexcusable $- "Invalid 'tested-with' version range: "- ++ commaSep (map prettyShow testedWithImpossibleRanges)- ++ ". To indicate that you have tested a package with multiple "- ++ "different versions of the same compiler use multiple entries, "- ++ "for example 'tested-with: GHC==6.10.4, GHC==6.12.3' and not "- ++ "'tested-with: GHC==6.10.4 && ==6.12.3'."-- -- for more details on why the following was commented out,- -- check https://github.com/haskell/cabal/pull/7470#issuecomment-875878507- -- , check (not (null depInternalLibraryWithExtraVersion)) $- -- PackageBuildWarning $- -- "The package has an extraneous version range for a dependency on an "- -- ++ "internal library: "- -- ++ commaSep (map prettyShow depInternalLibraryWithExtraVersion)- -- ++ ". This version range includes the current package but isn't needed "- -- ++ "as the current package's library will always be used."-- , check (not (null depInternalLibraryWithImpossibleVersion)) $- PackageBuildImpossible $- "The package has an impossible version range for a dependency on an "- ++ "internal library: "- ++ commaSep (map prettyShow depInternalLibraryWithImpossibleVersion)- ++ ". This version range does not include the current package, and must "- ++ "be removed as the current package's library will always be used."-- -- , check (not (null depInternalExecutableWithExtraVersion)) $- -- PackageBuildWarning $- -- "The package has an extraneous version range for a dependency on an "- -- ++ "internal executable: "- -- ++ commaSep (map prettyShow depInternalExecutableWithExtraVersion)- -- ++ ". This version range includes the current package but isn't needed "- -- ++ "as the current package's executable will always be used."-- , check (not (null depInternalExecutableWithImpossibleVersion)) $- PackageBuildImpossible $- "The package has an impossible version range for a dependency on an "- ++ "internal executable: "- ++ commaSep (map prettyShow depInternalExecutableWithImpossibleVersion)- ++ ". This version range does not include the current package, and must "- ++ "be removed as the current package's executable will always be used."-- , check (not (null depMissingInternalExecutable)) $- PackageBuildImpossible $- "The package depends on a missing internal executable: "- ++ commaSep (map prettyShow depInternalExecutableWithImpossibleVersion)- ]- where- unknownCompilers = [ name | (OtherCompiler name, _) <- testedWith pkg ]- unknownLanguages = [ name | bi <- allBuildInfo pkg- , UnknownLanguage name <- allLanguages bi ]- unknownExtensions = [ name | bi <- allBuildInfo pkg- , UnknownExtension name <- allExtensions bi- , name `notElem` map prettyShow knownLanguages ]- ourDeprecatedExtensions = nub $ catMaybes- [ find ((==ext) . fst) deprecatedExtensions- | bi <- allBuildInfo pkg- , ext <- allExtensions bi ]- languagesUsedAsExtensions =- [ name | bi <- allBuildInfo pkg- , UnknownExtension name <- allExtensions bi- , name `elem` map prettyShow knownLanguages ]-- testedWithImpossibleRanges =- [ Dependency (mkPackageName (prettyShow compiler)) vr mainLibSet- | (compiler, vr) <- testedWith pkg- , isNoVersion vr ]-- internalLibraries =- map (maybe (packageName pkg) (unqualComponentNameToPackageName) . libraryNameString . libName)- (allLibraries pkg)-- internalExecutables = map exeName $ executables pkg-- internalLibDeps =- [ dep- | bi <- allBuildInfo pkg- , dep@(Dependency name _ _) <- targetBuildDepends bi- , name `elem` internalLibraries- ]-- internalExeDeps =- [ dep- | bi <- allBuildInfo pkg- , dep <- getAllToolDependencies pkg bi- , isInternal pkg dep- ]-- -- depInternalLibraryWithExtraVersion =- -- [ dep- -- | dep@(Dependency _ versionRange _) <- internalLibDeps- -- , not $ isAnyVersion versionRange- -- , packageVersion pkg `withinRange` versionRange- -- ]-- depInternalLibraryWithImpossibleVersion =- [ dep- | dep@(Dependency _ versionRange _) <- internalLibDeps- , not $ packageVersion pkg `withinRange` versionRange- ]-- -- depInternalExecutableWithExtraVersion =- -- [ dep- -- | dep@(ExeDependency _ _ versionRange) <- internalExeDeps- -- , not $ isAnyVersion versionRange- -- , packageVersion pkg `withinRange` versionRange- -- ]-- depInternalExecutableWithImpossibleVersion =- [ dep- | dep@(ExeDependency _ _ versionRange) <- internalExeDeps- , not $ packageVersion pkg `withinRange` versionRange- ]-- depMissingInternalExecutable =- [ dep- | dep@(ExeDependency _ eName _) <- internalExeDeps- , not $ eName `elem` internalExecutables- ]---checkLicense :: PackageDescription -> [PackageCheck]-checkLicense pkg = case licenseRaw pkg of- Right l -> checkOldLicense pkg l- Left l -> checkNewLicense pkg l--checkNewLicense :: PackageDescription -> SPDX.License -> [PackageCheck]-checkNewLicense _pkg lic = catMaybes- [ check (lic == SPDX.NONE) $- PackageDistInexcusable- "The 'license' field is missing or is NONE."- ]--checkOldLicense :: PackageDescription -> License -> [PackageCheck]-checkOldLicense pkg lic = catMaybes- [ check (lic == UnspecifiedLicense) $- PackageDistInexcusable- "The 'license' field is missing."-- , check (lic == AllRightsReserved) $- PackageDistSuspicious- "The 'license' is AllRightsReserved. Is that really what you want?"-- , checkVersion CabalSpecV1_4 (lic `notElem` compatLicenses) $- PackageDistInexcusable $- "Unfortunately the license " ++ quote (prettyShow (license pkg))- ++ " messes up the parser in earlier Cabal versions so you need to "- ++ "specify 'cabal-version: >= 1.4'. Alternatively if you require "- ++ "compatibility with earlier Cabal versions then use 'OtherLicense'."-- , case lic of- UnknownLicense l -> Just $- PackageBuildWarning $- quote ("license: " ++ l) ++ " is not a recognised license. The "- ++ "known licenses are: "- ++ commaSep (map prettyShow knownLicenses)- _ -> Nothing-- , check (lic == BSD4) $- PackageDistSuspicious $- "Using 'license: BSD4' is almost always a misunderstanding. 'BSD4' "- ++ "refers to the old 4-clause BSD license with the advertising "- ++ "clause. 'BSD3' refers the new 3-clause BSD license."-- , case unknownLicenseVersion (lic) of- Just knownVersions -> Just $- PackageDistSuspicious $- "'license: " ++ prettyShow (lic) ++ "' is not a known "- ++ "version of that license. The known versions are "- ++ commaSep (map prettyShow knownVersions)- ++ ". If this is not a mistake and you think it should be a known "- ++ "version then please file a ticket."- _ -> Nothing-- , check (lic `notElem` [ AllRightsReserved- , UnspecifiedLicense, PublicDomain]- -- AllRightsReserved and PublicDomain are not strictly- -- licenses so don't need license files.- && null (licenseFiles pkg)) $- PackageDistSuspicious "A 'license-file' is not specified."- ]- where- unknownLicenseVersion (GPL (Just v))- | v `notElem` knownVersions = Just knownVersions- where knownVersions = [ v' | GPL (Just v') <- knownLicenses ]- unknownLicenseVersion (LGPL (Just v))- | v `notElem` knownVersions = Just knownVersions- where knownVersions = [ v' | LGPL (Just v') <- knownLicenses ]- unknownLicenseVersion (AGPL (Just v))- | v `notElem` knownVersions = Just knownVersions- where knownVersions = [ v' | AGPL (Just v') <- knownLicenses ]- unknownLicenseVersion (Apache (Just v))- | v `notElem` knownVersions = Just knownVersions- where knownVersions = [ v' | Apache (Just v') <- knownLicenses ]- unknownLicenseVersion _ = Nothing-- checkVersion :: CabalSpecVersion -> Bool -> PackageCheck -> Maybe PackageCheck- checkVersion ver cond pc- | specVersion pkg >= ver = Nothing- | otherwise = check cond pc-- compatLicenses = [ GPL Nothing, LGPL Nothing, AGPL Nothing, BSD3, BSD4- , PublicDomain, AllRightsReserved- , UnspecifiedLicense, OtherLicense ]--checkSourceRepos :: PackageDescription -> [PackageCheck]-checkSourceRepos pkg =- catMaybes $ concat [[-- case repoKind repo of- RepoKindUnknown kind -> Just $ PackageDistInexcusable $- quote kind ++ " is not a recognised kind of source-repository. "- ++ "The repo kind is usually 'head' or 'this'"- _ -> Nothing-- , check (isNothing (repoType repo)) $- PackageDistInexcusable- "The source-repository 'type' is a required field."-- , check (isNothing (repoLocation repo)) $- PackageDistInexcusable- "The source-repository 'location' is a required field."-- , check (repoType repo == Just (KnownRepoType CVS) && isNothing (repoModule repo)) $- PackageDistInexcusable- "For a CVS source-repository, the 'module' is a required field."-- , check (repoKind repo == RepoThis && isNothing (repoTag repo)) $- PackageDistInexcusable $- "For the 'this' kind of source-repository, the 'tag' is a required "- ++ "field. It should specify the tag corresponding to this version "- ++ "or release of the package."-- , check (maybe False isAbsoluteOnAnyPlatform (repoSubdir repo)) $- PackageDistInexcusable- "The 'subdir' field of a source-repository must be a relative path."-- , check (maybe False isAbsoluteOnAnyPlatform (repoSubdir repo)) $- PackageDistInexcusable- "The 'subdir' field of a source-repository must be a relative path."-- , do- subdir <- repoSubdir repo- err <- isGoodRelativeDirectoryPath subdir- return $ PackageDistInexcusable $- "The 'subdir' field of a source-repository is not a good relative path: " ++ show err- ]- | repo <- sourceRepos pkg ]----TODO: check location looks like a URL for some repo types.---- | Checks GHC options from all ghc-*-options fields in the given--- PackageDescription and reports commonly misused or non-portable flags-checkAllGhcOptions :: PackageDescription -> [PackageCheck]-checkAllGhcOptions pkg =- checkGhcOptions "ghc-options" (hcOptions GHC) pkg- ++ checkGhcOptions "ghc-prof-options" (hcProfOptions GHC) pkg- ++ checkGhcOptions "ghc-shared-options" (hcSharedOptions GHC) pkg---- | Extracts GHC options belonging to the given field from the given--- PackageDescription using given function and checks them for commonly misused--- or non-portable flags-checkGhcOptions :: String -> (BuildInfo -> [String]) -> PackageDescription -> [PackageCheck]-checkGhcOptions fieldName getOptions pkg =- catMaybes [-- checkFlags ["-fasm"] $- PackageDistInexcusable $- "'" ++ fieldName ++ ": -fasm' is unnecessary and will not work on CPU "- ++ "architectures other than x86, x86-64, ppc or sparc."-- , checkFlags ["-fvia-C"] $- PackageDistSuspicious $- "'" ++ fieldName ++": -fvia-C' is usually unnecessary. If your package "- ++ "needs -via-C for correctness rather than performance then it "- ++ "is using the FFI incorrectly and will probably not work with GHC "- ++ "6.10 or later."-- , checkFlags ["-fhpc"] $- PackageDistInexcusable $- "'" ++ fieldName ++ ": -fhpc' is not necessary. Use the configure flag "- ++ " --enable-coverage instead."-- , checkFlags ["-prof"] $- PackageBuildWarning $- "'" ++ fieldName ++ ": -prof' is not necessary and will lead to problems "- ++ "when used on a library. Use the configure flag "- ++ "--enable-library-profiling and/or --enable-profiling."-- , checkFlags ["-o"] $- PackageBuildWarning $- "'" ++ fieldName ++ ": -o' is not needed. "- ++ "The output files are named automatically."-- , checkFlags ["-hide-package"] $- PackageBuildWarning $- "'" ++ fieldName ++ ": -hide-package' is never needed. "- ++ "Cabal hides all packages."-- , checkFlags ["--make"] $- PackageBuildWarning $- "'" ++ fieldName ++ ": --make' is never needed. Cabal uses this automatically."-- , checkFlags ["-main-is"] $- PackageDistSuspicious $- "'" ++ fieldName ++ ": -main-is' is not portable."-- , checkNonTestAndBenchmarkFlags ["-O0", "-Onot"] $- PackageDistSuspicious $- "'" ++ fieldName ++ ": -O0' is not needed. "- ++ "Use the --disable-optimization configure flag."-- , checkTestAndBenchmarkFlags ["-O0", "-Onot"] $- PackageDistSuspiciousWarn $- "'" ++ fieldName ++ ": -O0' is not needed. "- ++ "Use the --disable-optimization configure flag."-- , checkFlags [ "-O", "-O1"] $- PackageDistInexcusable $- "'" ++ fieldName ++ ": -O' is not needed. "- ++ "Cabal automatically adds the '-O' flag. "- ++ "Setting it yourself interferes with the --disable-optimization flag."-- , checkFlags ["-O2"] $- PackageDistSuspiciousWarn $- "'" ++ fieldName ++ ": -O2' is rarely needed. "- ++ "Check that it is giving a real benefit "- ++ "and not just imposing longer compile times on your users."-- , checkFlags ["-split-sections"] $- PackageBuildWarning $- "'" ++ fieldName ++ ": -split-sections' is not needed. "- ++ "Use the --enable-split-sections configure flag."-- , checkFlags ["-split-objs"] $- PackageBuildWarning $- "'" ++ fieldName ++ ": -split-objs' is not needed. "- ++ "Use the --enable-split-objs configure flag."-- , checkFlags ["-optl-Wl,-s", "-optl-s"] $- PackageDistInexcusable $- "'" ++ fieldName ++ ": -optl-Wl,-s' is not needed and is not portable to all"- ++ " operating systems. Cabal 1.4 and later automatically strip"- ++ " executables. Cabal also has a flag --disable-executable-stripping"- ++ " which is necessary when building packages for some Linux"- ++ " distributions and using '-optl-Wl,-s' prevents that from working."-- , checkFlags ["-fglasgow-exts"] $- PackageDistSuspicious $- "Instead of '" ++ fieldName ++ ": -fglasgow-exts' it is preferable to use "- ++ "the 'extensions' field."-- , check ("-threaded" `elem` lib_ghc_options) $- PackageBuildWarning $- "'" ++ fieldName ++ ": -threaded' has no effect for libraries. It should "- ++ "only be used for executables."-- , check ("-rtsopts" `elem` lib_ghc_options) $- PackageBuildWarning $- "'" ++ fieldName ++ ": -rtsopts' has no effect for libraries. It should "- ++ "only be used for executables."-- , check (any (\opt -> "-with-rtsopts" `isPrefixOf` opt) lib_ghc_options) $- PackageBuildWarning $- "'" ++ fieldName ++ ": -with-rtsopts' has no effect for libraries. It "- ++ "should only be used for executables."-- , checkAlternatives fieldName "extensions"- [ (flag, prettyShow extension) | flag <- all_ghc_options- , Just extension <- [ghcExtension flag] ]-- , checkAlternatives fieldName "extensions"- [ (flag, extension) | flag@('-':'X':extension) <- all_ghc_options ]-- , checkAlternatives fieldName "cpp-options" $- [ (flag, flag) | flag@('-':'D':_) <- all_ghc_options ]- ++ [ (flag, flag) | flag@('-':'U':_) <- all_ghc_options ]-- , checkAlternatives fieldName "include-dirs"- [ (flag, dir) | flag@('-':'I':dir) <- all_ghc_options ]-- , checkAlternatives fieldName "extra-libraries"- [ (flag, lib) | flag@('-':'l':lib) <- all_ghc_options ]-- , checkAlternatives fieldName "extra-lib-dirs"- [ (flag, dir) | flag@('-':'L':dir) <- all_ghc_options ]-- , checkAlternatives fieldName "frameworks"- [ (flag, fmwk) | (flag@"-framework", fmwk) <-- zip all_ghc_options (safeTail all_ghc_options) ]-- , checkAlternatives fieldName "extra-framework-dirs"- [ (flag, dir) | (flag@"-framework-path", dir) <-- zip all_ghc_options (safeTail all_ghc_options) ]- ]-- where- all_ghc_options = concatMap getOptions (allBuildInfo pkg)- lib_ghc_options = concatMap (getOptions . libBuildInfo)- (allLibraries pkg)- test_ghc_options = concatMap (getOptions . testBuildInfo)- (testSuites pkg)- benchmark_ghc_options = concatMap (getOptions . benchmarkBuildInfo)- (benchmarks pkg)- test_and_benchmark_ghc_options = test_ghc_options ++- benchmark_ghc_options- non_test_and_benchmark_ghc_options = concatMap getOptions- (allBuildInfo (pkg { testSuites = []- , benchmarks = []- }))-- checkFlags :: [String] -> PackageCheck -> Maybe PackageCheck- checkFlags flags = check (any (`elem` flags) all_ghc_options)-- checkTestAndBenchmarkFlags :: [String] -> PackageCheck -> Maybe PackageCheck- checkTestAndBenchmarkFlags flags = check (any (`elem` flags) test_and_benchmark_ghc_options)-- checkNonTestAndBenchmarkFlags :: [String] -> PackageCheck -> Maybe PackageCheck- checkNonTestAndBenchmarkFlags flags = check (any (`elem` flags) non_test_and_benchmark_ghc_options)-- ghcExtension ('-':'f':name) = case name of- "allow-overlapping-instances" -> enable OverlappingInstances- "no-allow-overlapping-instances" -> disable OverlappingInstances- "th" -> enable TemplateHaskell- "no-th" -> disable TemplateHaskell- "ffi" -> enable ForeignFunctionInterface- "no-ffi" -> disable ForeignFunctionInterface- "fi" -> enable ForeignFunctionInterface- "no-fi" -> disable ForeignFunctionInterface- "monomorphism-restriction" -> enable MonomorphismRestriction- "no-monomorphism-restriction" -> disable MonomorphismRestriction- "mono-pat-binds" -> enable MonoPatBinds- "no-mono-pat-binds" -> disable MonoPatBinds- "allow-undecidable-instances" -> enable UndecidableInstances- "no-allow-undecidable-instances" -> disable UndecidableInstances- "allow-incoherent-instances" -> enable IncoherentInstances- "no-allow-incoherent-instances" -> disable IncoherentInstances- "arrows" -> enable Arrows- "no-arrows" -> disable Arrows- "generics" -> enable Generics- "no-generics" -> disable Generics- "implicit-prelude" -> enable ImplicitPrelude- "no-implicit-prelude" -> disable ImplicitPrelude- "implicit-params" -> enable ImplicitParams- "no-implicit-params" -> disable ImplicitParams- "bang-patterns" -> enable BangPatterns- "no-bang-patterns" -> disable BangPatterns- "scoped-type-variables" -> enable ScopedTypeVariables- "no-scoped-type-variables" -> disable ScopedTypeVariables- "extended-default-rules" -> enable ExtendedDefaultRules- "no-extended-default-rules" -> disable ExtendedDefaultRules- _ -> Nothing- ghcExtension "-cpp" = enable CPP- ghcExtension _ = Nothing-- enable e = Just (EnableExtension e)- disable e = Just (DisableExtension e)--checkCCOptions :: PackageDescription -> [PackageCheck]-checkCCOptions = checkCLikeOptions "C" "cc-options" ccOptions--checkCxxOptions :: PackageDescription -> [PackageCheck]-checkCxxOptions = checkCLikeOptions "C++" "cxx-options" cxxOptions--checkCLikeOptions :: String -> String -> (BuildInfo -> [String]) -> PackageDescription -> [PackageCheck]-checkCLikeOptions label prefix accessor pkg =- catMaybes [-- checkAlternatives prefix "include-dirs"- [ (flag, dir) | flag@('-':'I':dir) <- all_cLikeOptions ]-- , checkAlternatives prefix "extra-libraries"- [ (flag, lib) | flag@('-':'l':lib) <- all_cLikeOptions ]-- , checkAlternatives prefix "extra-lib-dirs"- [ (flag, dir) | flag@('-':'L':dir) <- all_cLikeOptions ]-- , checkAlternatives "ld-options" "extra-libraries"- [ (flag, lib) | flag@('-':'l':lib) <- all_ldOptions ]-- , checkAlternatives "ld-options" "extra-lib-dirs"- [ (flag, dir) | flag@('-':'L':dir) <- all_ldOptions ]-- , checkCCFlags [ "-O", "-Os", "-O0", "-O1", "-O2", "-O3" ] $- PackageDistSuspicious $- "'"++prefix++": -O[n]' is generally not needed. When building with "- ++ " optimisations Cabal automatically adds '-O2' for "++label++" code. "- ++ "Setting it yourself interferes with the --disable-optimization flag."- ]-- where all_cLikeOptions = [ opts | bi <- allBuildInfo pkg- , opts <- accessor bi ]- all_ldOptions = [ opts | bi <- allBuildInfo pkg- , opts <- ldOptions bi ]-- checkCCFlags :: [String] -> PackageCheck -> Maybe PackageCheck- checkCCFlags flags = check (any (`elem` flags) all_cLikeOptions)--checkCPPOptions :: PackageDescription -> [PackageCheck]-checkCPPOptions pkg = catMaybes- [ checkAlternatives "cpp-options" "include-dirs"- [ (flag, dir) | flag@('-':'I':dir) <- all_cppOptions ]- ]- ++- [ PackageBuildWarning $ "'cpp-options': " ++ opt ++ " is not portable C-preprocessor flag"- | opt <- all_cppOptions- -- "-I" is handled above, we allow only -DNEWSTUFF and -UOLDSTUFF- , not $ any (`isPrefixOf` opt) ["-D", "-U", "-I" ]- ]- where- all_cppOptions = [ opts | bi <- allBuildInfo pkg, opts <- cppOptions bi ]--checkAlternatives :: String -> String -> [(String, String)]- -> Maybe PackageCheck-checkAlternatives badField goodField flags =- check (not (null badFlags)) $- PackageBuildWarning $- "Instead of " ++ quote (badField ++ ": " ++ unwords badFlags)- ++ " use " ++ quote (goodField ++ ": " ++ unwords goodFlags)-- where (badFlags, goodFlags) = unzip flags--data PathKind- = PathKindFile- | PathKindDirectory- | PathKindGlob--checkPaths :: PackageDescription -> [PackageCheck]-checkPaths pkg =- [ PackageBuildWarning $- quote (field ++ ": " ++ path)- ++ " is a relative path outside of the source tree. "- ++ "This will not work when generating a tarball with 'sdist'."- | (path, field, _) <- relPaths ++ absPaths- , isOutsideTree path ]- ++- [ PackageDistInexcusable $- quote (field ++ ": " ++ path) ++ " is an absolute path."- | (path, field, _) <- relPaths- , isAbsoluteOnAnyPlatform path ]- ++- [ PackageDistInexcusable $- quote (field ++ ": " ++ path) ++ " is not good relative path: " ++ err- | (path, field, kind) <- relPaths- -- these are not paths, but globs...- , err <- maybeToList $ case kind of- PathKindFile -> isGoodRelativeFilePath path- PathKindGlob -> isGoodRelativeGlob path- PathKindDirectory -> isGoodRelativeDirectoryPath path- ]- ++- [ PackageDistInexcusable $- quote (field ++ ": " ++ path) ++ " points inside the 'dist' "- ++ "directory. This is not reliable because the location of this "- ++ "directory is configurable by the user (or package manager). In "- ++ "addition the layout of the 'dist' directory is subject to change "- ++ "in future versions of Cabal."- | (path, field, _) <- relPaths ++ absPaths- , isInsideDist path ]- ++- [ PackageDistInexcusable $- "The 'ghc-options' contains the path '" ++ path ++ "' which points "- ++ "inside the 'dist' directory. This is not reliable because the "- ++ "location of this directory is configurable by the user (or package "- ++ "manager). In addition the layout of the 'dist' directory is subject "- ++ "to change in future versions of Cabal."- | bi <- allBuildInfo pkg- , (GHC, flags) <- perCompilerFlavorToList $ options bi- , path <- flags- , isInsideDist path ]- ++- [ PackageDistInexcusable $- "In the 'data-files' field: " ++ explainGlobSyntaxError pat err- | pat <- dataFiles pkg- , Left err <- [parseFileGlob (specVersion pkg) pat]- ]- ++- [ PackageDistInexcusable $- "In the 'extra-source-files' field: " ++ explainGlobSyntaxError pat err- | pat <- extraSrcFiles pkg- , Left err <- [parseFileGlob (specVersion pkg) pat]- ]- ++- [ PackageDistInexcusable $- "In the 'extra-doc-files' field: " ++ explainGlobSyntaxError pat err- | pat <- extraDocFiles pkg- , Left err <- [parseFileGlob (specVersion pkg) pat]- ]- where- isOutsideTree path = case splitDirectories path of- "..":_ -> True- ".":"..":_ -> True- _ -> False- isInsideDist path = case map lowercase (splitDirectories path) of- "dist" :_ -> True- ".":"dist":_ -> True- _ -> False-- -- paths that must be relative- relPaths :: [(FilePath, String, PathKind)]- relPaths =- [ (path, "extra-source-files", PathKindGlob) | path <- extraSrcFiles pkg ] ++- [ (path, "extra-tmp-files", PathKindFile) | path <- extraTmpFiles pkg ] ++- [ (path, "extra-doc-files", PathKindGlob) | path <- extraDocFiles pkg ] ++- [ (path, "data-files", PathKindGlob) | path <- dataFiles pkg ] ++- [ (path, "data-dir", PathKindDirectory) | path <- [dataDir pkg]] ++- [ (path, "license-file", PathKindFile) | path <- map getSymbolicPath $ licenseFiles pkg ] ++- concat- [ [ (path, "asm-sources", PathKindFile) | path <- asmSources bi ] ++- [ (path, "cmm-sources", PathKindFile) | path <- cmmSources bi ] ++- [ (path, "c-sources", PathKindFile) | path <- cSources bi ] ++- [ (path, "cxx-sources", PathKindFile) | path <- cxxSources bi ] ++- [ (path, "js-sources", PathKindFile) | path <- jsSources bi ] ++- [ (path, "install-includes", PathKindFile) | path <- installIncludes bi ] ++- [ (path, "hs-source-dirs", PathKindDirectory) | path <- map getSymbolicPath $ hsSourceDirs bi ]- | bi <- allBuildInfo pkg- ]-- -- paths that are allowed to be absolute- absPaths :: [(FilePath, String, PathKind)]- absPaths = concat- [ [ (path, "includes", PathKindFile) | path <- includes bi ] ++- [ (path, "include-dirs", PathKindDirectory) | path <- includeDirs bi ] ++- [ (path, "extra-lib-dirs", PathKindDirectory) | path <- extraLibDirs bi ]- | bi <- allBuildInfo pkg- ]----TODO: check sets of paths that would be interpreted differently between Unix--- and windows, ie case-sensitive or insensitive. Things that might clash, or--- conversely be distinguished.----TODO: use the tar path checks on all the above paths---- | Check that the package declares the version in the @\"cabal-version\"@--- field correctly.----checkCabalVersion :: PackageDescription -> [PackageCheck]-checkCabalVersion pkg =- catMaybes [-- -- check use of test suite sections- checkVersion CabalSpecV1_8 (not (null $ testSuites pkg)) $- PackageDistInexcusable $- "The 'test-suite' section is new in Cabal 1.10. "- ++ "Unfortunately it messes up the parser in older Cabal versions "- ++ "so you must specify at least 'cabal-version: >= 1.8', but note "- ++ "that only Cabal 1.10 and later can actually run such test suites."-- -- check use of default-language field- -- note that we do not need to do an equivalent check for the- -- other-language field since that one does not change behaviour- , checkVersion CabalSpecV1_10 (any isJust (buildInfoField defaultLanguage)) $- PackageBuildWarning $- "To use the 'default-language' field the package needs to specify "- ++ "at least 'cabal-version: >= 1.10'."-- , check (specVersion pkg >= CabalSpecV1_10 && specVersion pkg < CabalSpecV3_4- && (any isNothing (buildInfoField defaultLanguage))) $- PackageBuildWarning $- "Packages using 'cabal-version: >= 1.10' and before 'cabal-version: 3.4' must specify the "- ++ "'default-language' field for each component (e.g. Haskell98 or "- ++ "Haskell2010). If a component uses different languages in "- ++ "different modules then list the other ones in the "- ++ "'other-languages' field."-- , checkVersion CabalSpecV1_18- (not . null $ extraDocFiles pkg) $- PackageDistInexcusable $- "To use the 'extra-doc-files' field the package needs to specify "- ++ "at least 'cabal-version: >= 1.18'."-- , checkVersion CabalSpecV2_0- (not (null (subLibraries pkg))) $- PackageDistInexcusable $- "To use multiple 'library' sections or a named library section "- ++ "the package needs to specify at least 'cabal-version: 2.0'."-- -- check use of reexported-modules sections- , checkVersion CabalSpecV1_22- (any (not.null.reexportedModules) (allLibraries pkg)) $- PackageDistInexcusable $- "To use the 'reexported-module' field the package needs to specify "- ++ "at least 'cabal-version: >= 1.22'."-- -- check use of thinning and renaming- , checkVersion CabalSpecV2_0 usesBackpackIncludes $- PackageDistInexcusable $- "To use the 'mixins' field the package needs to specify "- ++ "at least 'cabal-version: 2.0'."-- -- check use of 'extra-framework-dirs' field- , checkVersion CabalSpecV1_24 (any (not . null) (buildInfoField extraFrameworkDirs)) $- -- Just a warning, because this won't break on old Cabal versions.- PackageDistSuspiciousWarn $- "To use the 'extra-framework-dirs' field the package needs to specify"- ++ " at least 'cabal-version: >= 1.24'."-- -- check use of default-extensions field- -- don't need to do the equivalent check for other-extensions- , checkVersion CabalSpecV1_10 (any (not . null) (buildInfoField defaultExtensions)) $- PackageBuildWarning $- "To use the 'default-extensions' field the package needs to specify "- ++ "at least 'cabal-version: >= 1.10'."-- -- check use of extensions field- , check (specVersion pkg >= CabalSpecV1_10- && (any (not . null) (buildInfoField oldExtensions))) $- PackageBuildWarning $- "For packages using 'cabal-version: >= 1.10' the 'extensions' "- ++ "field is deprecated. The new 'default-extensions' field lists "- ++ "extensions that are used in all modules in the component, while "- ++ "the 'other-extensions' field lists extensions that are used in "- ++ "some modules, e.g. via the {-# LANGUAGE #-} pragma."-- , checkVersion CabalSpecV3_0 (any (not . null)- (concatMap buildInfoField- [ asmSources- , cmmSources- , extraBundledLibs- , extraLibFlavours ])) $- PackageDistInexcusable $- "The use of 'asm-sources', 'cmm-sources', 'extra-bundled-libraries' "- ++ " and 'extra-library-flavours' requires the package "- ++ " to specify at least 'cabal-version: 3.0'."-- , checkVersion CabalSpecV3_0 (any (not . null) $ buildInfoField extraDynLibFlavours) $- PackageDistInexcusable $- "The use of 'extra-dynamic-library-flavours' requires the package "- ++ " to specify at least 'cabal-version: 3.0'. The flavours are: "- ++ commaSep [ flav- | flavs <- buildInfoField extraDynLibFlavours- , flav <- flavs ]-- , checkVersion CabalSpecV2_2 (any (not . null)- (buildInfoField virtualModules)) $- PackageDistInexcusable $- "The use of 'virtual-modules' requires the package "- ++ " to specify at least 'cabal-version: 2.2'."-- -- check use of "source-repository" section- , checkVersion CabalSpecV1_6 (not (null (sourceRepos pkg))) $- PackageDistInexcusable $- "The 'source-repository' section is new in Cabal 1.6. "- ++ "Unfortunately it messes up the parser in earlier Cabal versions "- ++ "so you need to specify 'cabal-version: >= 1.6'."-- -- check for new language extensions- , checkVersion CabalSpecV1_4 (not (null mentionedExtensionsThatNeedCabal12)) $- PackageDistInexcusable $- "Unfortunately the language extensions "- ++ commaSep (map (quote . prettyShow) mentionedExtensionsThatNeedCabal12)- ++ " break the parser in earlier Cabal versions so you need to "- ++ "specify 'cabal-version: >= 1.4'. Alternatively if you require "- ++ "compatibility with earlier Cabal versions then you may be able to "- ++ "use an equivalent compiler-specific flag."-- , checkVersion CabalSpecV1_4 (not (null mentionedExtensionsThatNeedCabal14)) $- PackageDistInexcusable $- "Unfortunately the language extensions "- ++ commaSep (map (quote . prettyShow) mentionedExtensionsThatNeedCabal14)- ++ " break the parser in earlier Cabal versions so you need to "- ++ "specify 'cabal-version: >= 1.4'. Alternatively if you require "- ++ "compatibility with earlier Cabal versions then you may be able to "- ++ "use an equivalent compiler-specific flag."-- , check (specVersion pkg >= CabalSpecV1_24- && isNothing (setupBuildInfo pkg)- && buildType pkg == Custom) $- PackageBuildWarning $- "Packages using 'cabal-version: >= 1.24' with 'build-type: Custom' "- ++ "must use a 'custom-setup' section with a 'setup-depends' field "- ++ "that specifies the dependencies of the Setup.hs script itself. "- ++ "The 'setup-depends' field uses the same syntax as 'build-depends', "- ++ "so a simple example would be 'setup-depends: base, Cabal'."-- , check (specVersion pkg < CabalSpecV1_24- && isNothing (setupBuildInfo pkg)- && buildType pkg == Custom) $- PackageDistSuspiciousWarn $- "From version 1.24 cabal supports specifying explicit dependencies "- ++ "for Custom setup scripts. Consider using cabal-version >= 1.24 and "- ++ "adding a 'custom-setup' section with a 'setup-depends' field "- ++ "that specifies the dependencies of the Setup.hs script itself. "- ++ "The 'setup-depends' field uses the same syntax as 'build-depends', "- ++ "so a simple example would be 'setup-depends: base, Cabal'."-- , check (specVersion pkg >= CabalSpecV2_0- && elem (autogenPathsModuleName pkg) allModuleNames- && not (elem (autogenPathsModuleName pkg) allModuleNamesAutogen) ) $- PackageDistInexcusable $- "Packages using 'cabal-version: 2.0' and the autogenerated "- ++ "module Paths_* must include it also on the 'autogen-modules' field "- ++ "besides 'exposed-modules' and 'other-modules'. This specifies that "- ++ "the module does not come with the package and is generated on "- ++ "setup. Modules built with a custom Setup.hs script also go here "- ++ "to ensure that commands like sdist don't fail."-- ]- where- -- Perform a check on packages that use a version of the spec less than- -- the version given. This is for cases where a new Cabal version adds- -- a new feature and we want to check that it is not used prior to that- -- version.- checkVersion :: CabalSpecVersion -> Bool -> PackageCheck -> Maybe PackageCheck- checkVersion ver cond pc- | specVersion pkg >= ver = Nothing- | otherwise = check cond pc-- buildInfoField field = map field (allBuildInfo pkg)-- usesBackpackIncludes = any (not . null . mixins) (allBuildInfo pkg)-- mentionedExtensions = [ ext | bi <- allBuildInfo pkg- , ext <- allExtensions bi ]- mentionedExtensionsThatNeedCabal12 =- nub (filter (`elem` compatExtensionsExtra) mentionedExtensions)-- -- As of Cabal-1.4 we can add new extensions without worrying about- -- breaking old versions of cabal.- mentionedExtensionsThatNeedCabal14 =- nub (filter (`notElem` compatExtensions) mentionedExtensions)-- -- The known extensions in Cabal-1.2.3- compatExtensions =- map EnableExtension- [ OverlappingInstances, UndecidableInstances, IncoherentInstances- , RecursiveDo, ParallelListComp, MultiParamTypeClasses- , FunctionalDependencies, Rank2Types- , RankNTypes, PolymorphicComponents, ExistentialQuantification- , ScopedTypeVariables, ImplicitParams, FlexibleContexts- , FlexibleInstances, EmptyDataDecls, CPP, BangPatterns- , TypeSynonymInstances, TemplateHaskell, ForeignFunctionInterface- , Arrows, Generics, NamedFieldPuns, PatternGuards- , GeneralizedNewtypeDeriving, ExtensibleRecords, RestrictedTypeSynonyms- , HereDocuments] ++- map DisableExtension- [MonomorphismRestriction, ImplicitPrelude] ++- compatExtensionsExtra-- -- The extra known extensions in Cabal-1.2.3 vs Cabal-1.1.6- -- (Cabal-1.1.6 came with ghc-6.6. Cabal-1.2 came with ghc-6.8)- compatExtensionsExtra =- map EnableExtension- [ KindSignatures, MagicHash, TypeFamilies, StandaloneDeriving- , UnicodeSyntax, PatternSignatures, UnliftedFFITypes, LiberalTypeSynonyms- , TypeOperators, RecordWildCards, RecordPuns, DisambiguateRecordFields- , OverloadedStrings, GADTs, RelaxedPolyRec- , ExtendedDefaultRules, UnboxedTuples, DeriveDataTypeable- , ConstrainedClassMethods- ] ++- map DisableExtension- [MonoPatBinds]-- allModuleNames =- (case library pkg of- Nothing -> []- (Just lib) -> explicitLibModules lib- )- ++ concatMap otherModules (allBuildInfo pkg)-- allModuleNamesAutogen = concatMap autogenModules (allBuildInfo pkg)---- --------------------------------------------------------------- * Checks on the GenericPackageDescription--- ---------------------------------------------------------------- | Check the build-depends fields for any weirdness or bad practice.----checkPackageVersions :: GenericPackageDescription -> [PackageCheck]-checkPackageVersions pkg =- catMaybes [-- -- Check that the version of base is bounded above.- -- For example this bans "build-depends: base >= 3".- -- It should probably be "build-depends: base >= 3 && < 4"- -- which is the same as "build-depends: base == 3.*"- check (not (boundedAbove baseDependency)) $- PackageDistInexcusable $- "The dependency 'build-depends: base' does not specify an upper "- ++ "bound on the version number. Each major release of the 'base' "- ++ "package changes the API in various ways and most packages will "- ++ "need some changes to compile with it. The recommended practice "- ++ "is to specify an upper bound on the version of the 'base' "- ++ "package. This ensures your package will continue to build when a "- ++ "new major version of the 'base' package is released. If you are "- ++ "not sure what upper bound to use then use the next major "- ++ "version. For example if you have tested your package with 'base' "- ++ "version 4.5 and 4.6 then use 'build-depends: base >= 4.5 && < 4.7'."-- ]- where- -- TODO: What we really want to do is test if there exists any- -- configuration in which the base version is unbounded above.- -- However that's a bit tricky because there are many possible- -- configurations. As a cheap easy and safe approximation we will- -- pick a single "typical" configuration and check if that has an- -- open upper bound. To get a typical configuration we finalise- -- using no package index and the current platform.- finalised = finalizePD- mempty defaultComponentRequestedSpec (const True)- buildPlatform- (unknownCompilerInfo- (CompilerId buildCompilerFlavor nullVersion)- NoAbiTag)- [] pkg- baseDependency = case finalised of- Right (pkg', _) | not (null baseDeps) ->- foldr intersectVersionRanges anyVersion baseDeps- where- baseDeps =- [ vr | Dependency pname vr _ <- allBuildDepends pkg'- , pname == mkPackageName "base" ]-- -- Just in case finalizePD fails for any reason,- -- or if the package doesn't depend on the base package at all,- -- then we will just skip the check, since boundedAbove noVersion = True- _ -> noVersion-- -- TODO: move to Distribution.Version- boundedAbove :: VersionRange -> Bool- boundedAbove vr = case asVersionIntervals vr of- [] -> True -- this is the inconsistent version range.- (x:xs) -> case last (x:|xs) of- VersionInterval _ UpperBound {} -> True- VersionInterval _ NoUpperBound -> False---checkConditionals :: GenericPackageDescription -> [PackageCheck]-checkConditionals pkg =- catMaybes [-- check (not $ null unknownOSs) $- PackageDistInexcusable $- "Unknown operating system name "- ++ commaSep (map quote unknownOSs)-- , check (not $ null unknownArches) $- PackageDistInexcusable $- "Unknown architecture name "- ++ commaSep (map quote unknownArches)-- , check (not $ null unknownImpls) $- PackageDistInexcusable $- "Unknown compiler name "- ++ commaSep (map quote unknownImpls)- ]- where- unknownOSs = [ os | OS (OtherOS os) <- conditions ]- unknownArches = [ arch | Arch (OtherArch arch) <- conditions ]- unknownImpls = [ impl | Impl (OtherCompiler impl) _ <- conditions ]- conditions = concatMap fvs (maybeToList (condLibrary pkg))- ++ concatMap (fvs . snd) (condSubLibraries pkg)- ++ concatMap (fvs . snd) (condForeignLibs pkg)- ++ concatMap (fvs . snd) (condExecutables pkg)- ++ concatMap (fvs . snd) (condTestSuites pkg)- ++ concatMap (fvs . snd) (condBenchmarks pkg)- fvs (CondNode _ _ ifs) = concatMap compfv ifs -- free variables- compfv (CondBranch c ct mct) = condfv c ++ fvs ct ++ maybe [] fvs mct- condfv c = case c of- Var v -> [v]- Lit _ -> []- CNot c1 -> condfv c1- COr c1 c2 -> condfv c1 ++ condfv c2- CAnd c1 c2 -> condfv c1 ++ condfv c2--checkFlagNames :: GenericPackageDescription -> [PackageCheck]-checkFlagNames gpd- | null invalidFlagNames = []- | otherwise = [ PackageDistInexcusable- $ "Suspicious flag names: " ++ unwords invalidFlagNames ++ ". "- ++ "To avoid ambiguity in command line interfaces, flag shouldn't "- ++ "start with a dash. Also for better compatibility, flag names "- ++ "shouldn't contain non-ascii characters."- ]- where- invalidFlagNames =- [ fn- | flag <- genPackageFlags gpd- , let fn = unFlagName (flagName flag)- , invalidFlagName fn- ]- -- starts with dash- invalidFlagName ('-':_) = True- -- mon ascii letter- invalidFlagName cs = any (not . isAscii) cs--checkUnusedFlags :: GenericPackageDescription -> [PackageCheck]-checkUnusedFlags gpd- | declared == used = []- | otherwise = [ PackageDistSuspicious- $ "Declared and used flag sets differ: "- ++ s declared ++ " /= " ++ s used ++ ". "- ]- where- s :: Set.Set FlagName -> String- s = commaSep . map unFlagName . Set.toList-- declared :: Set.Set FlagName- declared = toSetOf (L.genPackageFlags . traverse . L.flagName) gpd-- used :: Set.Set FlagName- used = mconcat- [ toSetOf (L.condLibrary . traverse . traverseCondTreeV . L._PackageFlag) gpd- , toSetOf (L.condSubLibraries . traverse . _2 . traverseCondTreeV . L._PackageFlag) gpd- , toSetOf (L.condForeignLibs . traverse . _2 . traverseCondTreeV . L._PackageFlag) gpd- , toSetOf (L.condExecutables . traverse . _2 . traverseCondTreeV . L._PackageFlag) gpd- , toSetOf (L.condTestSuites . traverse . _2 . traverseCondTreeV . L._PackageFlag) gpd- , toSetOf (L.condBenchmarks . traverse . _2 . traverseCondTreeV . L._PackageFlag) gpd- ]--checkUnicodeXFields :: GenericPackageDescription -> [PackageCheck]-checkUnicodeXFields gpd- | null nonAsciiXFields = []- | otherwise = [ PackageDistInexcusable- $ "Non ascii custom fields: " ++ unwords nonAsciiXFields ++ ". "- ++ "For better compatibility, custom field names "- ++ "shouldn't contain non-ascii characters."- ]- where- nonAsciiXFields :: [String]- nonAsciiXFields = [ n | (n, _) <- xfields, any (not . isAscii) n ]-- xfields :: [(String,String)]- xfields = DList.runDList $ mconcat- [ toDListOf (L.packageDescription . L.customFieldsPD . traverse) gpd- , toDListOf (L.traverseBuildInfos . L.customFieldsBI . traverse) gpd- ]---- | cabal-version <2.2 + Paths_module + default-extensions: doesn't build.-checkPathsModuleExtensions :: PackageDescription -> [PackageCheck]-checkPathsModuleExtensions pd- | specVersion pd >= CabalSpecV2_2 = []- | any checkBI (allBuildInfo pd) || any checkLib (allLibraries pd)- = return $ PackageBuildImpossible $ unwords- [ "The package uses RebindableSyntax with OverloadedStrings or OverloadedLists"- , "in default-extensions, and also Paths_ autogen module."- , "That configuration is known to cause compile failures with Cabal < 2.2."- , "To use these default-extensions with Paths_ autogen module"- , "specify at least 'cabal-version: 2.2'."- ]- | otherwise = []- where- mn = autogenPathsModuleName pd-- checkLib :: Library -> Bool- checkLib l = mn `elem` exposedModules l && checkExts (l ^. L.defaultExtensions)-- checkBI :: BuildInfo -> Bool- checkBI bi =- (mn `elem` otherModules bi || mn `elem` autogenModules bi) &&- checkExts (bi ^. L.defaultExtensions)-- checkExts exts = rebind `elem` exts && (strings `elem` exts || lists `elem` exts)- where- rebind = EnableExtension RebindableSyntax- strings = EnableExtension OverloadedStrings- lists = EnableExtension OverloadedLists---- | Checks GHC options from all ghc-*-options fields from the given BuildInfo--- and reports flags that are OK during development process, but are--- unacceptable in a distrubuted package-checkDevelopmentOnlyFlagsBuildInfo :: BuildInfo -> [PackageCheck]-checkDevelopmentOnlyFlagsBuildInfo bi =- checkDevelopmentOnlyFlagsOptions "ghc-options" (hcOptions GHC bi)- ++ checkDevelopmentOnlyFlagsOptions "ghc-prof-options" (hcProfOptions GHC bi)- ++ checkDevelopmentOnlyFlagsOptions "ghc-shared-options" (hcSharedOptions GHC bi)---- | Checks the given list of flags belonging to the given field and reports--- flags that are OK during development process, but are unacceptable in a--- distributed package-checkDevelopmentOnlyFlagsOptions :: String -> [String] -> [PackageCheck]-checkDevelopmentOnlyFlagsOptions fieldName ghcOptions =- catMaybes [-- check has_WerrorWall $- PackageDistInexcusable $- "'" ++ fieldName ++ ": -Wall -Werror' makes the package very easy to "- ++ "break with future GHC versions because new GHC versions often "- ++ "add new warnings. Use just '" ++ fieldName ++ ": -Wall' instead."- ++ extraExplanation-- , check (not has_WerrorWall && has_Werror) $- PackageDistInexcusable $- "'" ++ fieldName ++ ": -Werror' makes the package easy to "- ++ "break with future GHC versions because new GHC versions often "- ++ "add new warnings. "- ++ extraExplanation-- , check (has_J) $- PackageDistInexcusable $- "'" ++ fieldName ++ ": -j[N]' can make sense for specific user's setup,"- ++ " but it is not appropriate for a distributed package."- ++ extraExplanation-- , checkFlags ["-fdefer-type-errors"] $- PackageDistInexcusable $- "'" ++ fieldName ++ ": -fdefer-type-errors' is fine during development but "- ++ "is not appropriate for a distributed package. "- ++ extraExplanation-- -- -dynamic is not a debug flag- , check (any (\opt -> "-d" `isPrefixOf` opt && opt /= "-dynamic")- ghcOptions) $- PackageDistInexcusable $- "'" ++ fieldName ++ ": -d*' debug flags are not appropriate "- ++ "for a distributed package. "- ++ extraExplanation-- , checkFlags ["-fprof-auto", "-fprof-auto-top", "-fprof-auto-calls",- "-fprof-cafs", "-fno-prof-count-entries",- "-auto-all", "-auto", "-caf-all"] $- PackageDistSuspicious $- "'" ++ fieldName ++ ": -fprof*' profiling flags are typically not "- ++ "appropriate for a distributed library package. These flags are "- ++ "useful to profile this package, but when profiling other packages "- ++ "that use this one these flags clutter the profile output with "- ++ "excessive detail. If you think other packages really want to see "- ++ "cost centres from this package then use '-fprof-auto-exported' "- ++ "which puts cost centres only on exported functions. "- ++ extraExplanation- ]- where- extraExplanation =- " Alternatively, if you want to use this, make it conditional based "- ++ "on a Cabal configuration flag (with 'manual: True' and 'default: "- ++ "False') and enable that flag during development."-- has_WerrorWall = has_Werror && ( has_Wall || has_W )- has_Werror = "-Werror" `elem` ghcOptions- has_Wall = "-Wall" `elem` ghcOptions- has_W = "-W" `elem` ghcOptions- has_J = any- (\o -> case o of- "-j" -> True- ('-' : 'j' : d : _) -> isDigit d- _ -> False- )- ghcOptions- checkFlags :: [String] -> PackageCheck -> Maybe PackageCheck- checkFlags flags = check (any (`elem` flags) ghcOptions)--checkDevelopmentOnlyFlags :: GenericPackageDescription -> [PackageCheck]-checkDevelopmentOnlyFlags pkg =- concatMap checkDevelopmentOnlyFlagsBuildInfo- [ bi- | (conditions, bi) <- allConditionalBuildInfo- , not (any guardedByManualFlag conditions) ]- where- guardedByManualFlag = definitelyFalse-- -- We've basically got three-values logic here: True, False or unknown- -- hence this pattern to propagate the unknown cases properly.- definitelyFalse (Var (PackageFlag n)) = maybe False not (Map.lookup n manualFlags)- definitelyFalse (Var _) = False- definitelyFalse (Lit b) = not b- definitelyFalse (CNot c) = definitelyTrue c- definitelyFalse (COr c1 c2) = definitelyFalse c1 && definitelyFalse c2- definitelyFalse (CAnd c1 c2) = definitelyFalse c1 || definitelyFalse c2-- definitelyTrue (Var (PackageFlag n)) = fromMaybe False (Map.lookup n manualFlags)- definitelyTrue (Var _) = False- definitelyTrue (Lit b) = b- definitelyTrue (CNot c) = definitelyFalse c- definitelyTrue (COr c1 c2) = definitelyTrue c1 || definitelyTrue c2- definitelyTrue (CAnd c1 c2) = definitelyTrue c1 && definitelyTrue c2-- manualFlags = Map.fromList- [ (flagName flag, flagDefault flag)- | flag <- genPackageFlags pkg- , flagManual flag ]-- allConditionalBuildInfo :: [([Condition ConfVar], BuildInfo)]- allConditionalBuildInfo =- concatMap (collectCondTreePaths libBuildInfo)- (maybeToList (condLibrary pkg))-- ++ concatMap (collectCondTreePaths libBuildInfo . snd)- (condSubLibraries pkg)-- ++ concatMap (collectCondTreePaths buildInfo . snd)- (condExecutables pkg)-- ++ concatMap (collectCondTreePaths testBuildInfo . snd)- (condTestSuites pkg)-- ++ concatMap (collectCondTreePaths benchmarkBuildInfo . snd)- (condBenchmarks pkg)-- -- get all the leaf BuildInfo, paired up with the path (in the tree sense)- -- of if-conditions that guard it- collectCondTreePaths :: (a -> b)- -> CondTree v c a- -> [([Condition v], b)]- collectCondTreePaths mapData = go []- where- go conditions condNode =- -- the data at this level in the tree:- (reverse conditions, mapData (condTreeData condNode))-- : concat- [ go (condition:conditions) ifThen- | (CondBranch condition ifThen _) <- condTreeComponents condNode ]-- ++ concat- [ go (condition:conditions) elseThen- | (CondBranch condition _ (Just elseThen)) <- condTreeComponents condNode ]----- --------------------------------------------------------------- * Checks involving files in the package--- ---------------------------------------------------------------- | Sanity check things that requires IO. It looks at the files in the--- package and expects to find the package unpacked in at the given file path.----checkPackageFiles :: Verbosity -> PackageDescription -> FilePath -> IO [PackageCheck]-checkPackageFiles verbosity pkg root = do- contentChecks <- checkPackageContent checkFilesIO pkg- preDistributionChecks <- checkPackageFilesPreDistribution verbosity pkg root- -- Sort because different platforms will provide files from- -- `getDirectoryContents` in different orders, and we'd like to be- -- stable for test output.- return (sort contentChecks ++ sort preDistributionChecks)- where- checkFilesIO = CheckPackageContentOps {- doesFileExist = System.doesFileExist . relative,- doesDirectoryExist = System.doesDirectoryExist . relative,- getDirectoryContents = System.Directory.getDirectoryContents . relative,- getFileContents = BS.readFile . relative- }- relative path = root </> path---- | A record of operations needed to check the contents of packages.--- Used by 'checkPackageContent'.----data CheckPackageContentOps m = CheckPackageContentOps {- doesFileExist :: FilePath -> m Bool,- doesDirectoryExist :: FilePath -> m Bool,- getDirectoryContents :: FilePath -> m [FilePath],- getFileContents :: FilePath -> m BS.ByteString- }---- | Sanity check things that requires looking at files in the package.--- This is a generalised version of 'checkPackageFiles' that can work in any--- monad for which you can provide 'CheckPackageContentOps' operations.------ The point of this extra generality is to allow doing checks in some virtual--- file system, for example a tarball in memory.----checkPackageContent :: (Monad m, Applicative m)- => CheckPackageContentOps m- -> PackageDescription- -> m [PackageCheck]-checkPackageContent ops pkg = do- cabalBomError <- checkCabalFileBOM ops- cabalNameError <- checkCabalFileName ops pkg- licenseErrors <- checkLicensesExist ops pkg- setupError <- checkSetupExists ops pkg- configureError <- checkConfigureExists ops pkg- localPathErrors <- checkLocalPathsExist ops pkg- vcsLocation <- checkMissingVcsInfo ops pkg-- return $ licenseErrors- ++ catMaybes [cabalBomError, cabalNameError, setupError, configureError]- ++ localPathErrors- ++ vcsLocation--checkCabalFileBOM :: Monad m => CheckPackageContentOps m- -> m (Maybe PackageCheck)-checkCabalFileBOM ops = do- epdfile <- findPackageDesc ops- case epdfile of- -- MASSIVE HACK. If the Cabal file doesn't exist, that is- -- a very strange situation to be in, because the driver code- -- in 'Distribution.Setup' ought to have noticed already!- -- But this can be an issue, see #3552 and also when- -- --cabal-file is specified. So if you can't find the file,- -- just don't bother with this check.- Left _ -> return $ Nothing- Right pdfile -> (flip check pc . BS.isPrefixOf bomUtf8)- `liftM` (getFileContents ops pdfile)- where pc = PackageDistInexcusable $- pdfile ++ " starts with an Unicode byte order mark (BOM)."- ++ " This may cause problems with older cabal versions."-- where- bomUtf8 :: BS.ByteString- bomUtf8 = BS.pack [0xef,0xbb,0xbf] -- U+FEFF encoded as UTF8--checkCabalFileName :: Monad m => CheckPackageContentOps m- -> PackageDescription- -> m (Maybe PackageCheck)-checkCabalFileName ops pkg = do- -- findPackageDesc already takes care to detect missing/multiple- -- .cabal files; we don't include this check in 'findPackageDesc' in- -- order not to short-cut other checks which call 'findPackageDesc'- epdfile <- findPackageDesc ops- case epdfile of- -- see "MASSIVE HACK" note in 'checkCabalFileBOM'- Left _ -> return Nothing- Right pdfile- | takeFileName pdfile == expectedCabalname -> return Nothing- | otherwise -> return $ Just $ PackageDistInexcusable $- "The filename " ++ pdfile ++ " does not match package name " ++- "(expected: " ++ expectedCabalname ++ ")"- where- pkgname = unPackageName . packageName $ pkg- expectedCabalname = pkgname <.> "cabal"----- |Find a package description file in the given directory. Looks for--- @.cabal@ files. Like 'Distribution.Simple.Utils.findPackageDesc',--- but generalized over monads.-findPackageDesc :: Monad m => CheckPackageContentOps m- -> m (Either PackageCheck FilePath) -- ^<pkgname>.cabal-findPackageDesc ops- = do let dir = "."- files <- getDirectoryContents ops dir- -- to make sure we do not mistake a ~/.cabal/ dir for a <pkgname>.cabal- -- file we filter to exclude dirs and null base file names:- cabalFiles <- filterM (doesFileExist ops)- [ dir </> file- | file <- files- , let (name, ext) = splitExtension file- , not (null name) && ext == ".cabal" ]- case cabalFiles of- [] -> return (Left $ PackageBuildImpossible noDesc)- [cabalFile] -> return (Right cabalFile)- multiple -> return (Left $ PackageBuildImpossible- $ multiDesc multiple)-- where- noDesc :: String- noDesc = "No cabal file found.\n"- ++ "Please create a package description file <pkgname>.cabal"-- multiDesc :: [String] -> String- multiDesc l = "Multiple cabal files found while checking.\n"- ++ "Please use only one of: "- ++ intercalate ", " l--checkLicensesExist :: (Monad m, Applicative m)- => CheckPackageContentOps m- -> PackageDescription- -> m [PackageCheck]-checkLicensesExist ops pkg = do- exists <- traverse (doesFileExist ops . getSymbolicPath) (licenseFiles pkg)- return- [ PackageBuildWarning $- "The '" ++ fieldname ++ "' field refers to the file "- ++ quote (getSymbolicPath file) ++ " which does not exist."- | (file, False) <- zip (licenseFiles pkg) exists ]- where- fieldname | length (licenseFiles pkg) == 1 = "license-file"- | otherwise = "license-files"--checkSetupExists :: Monad m => CheckPackageContentOps m- -> PackageDescription- -> m (Maybe PackageCheck)-checkSetupExists ops pkg = do- let simpleBuild = buildType pkg == Simple- hsexists <- doesFileExist ops "Setup.hs"- lhsexists <- doesFileExist ops "Setup.lhs"- return $ check (not simpleBuild && not hsexists && not lhsexists) $- PackageDistInexcusable $- "The package is missing a Setup.hs or Setup.lhs script."--checkConfigureExists :: Monad m => CheckPackageContentOps m- -> PackageDescription- -> m (Maybe PackageCheck)-checkConfigureExists ops pd- | buildType pd == Configure = do- exists <- doesFileExist ops "configure"- return $ check (not exists) $- PackageBuildWarning $- "The 'build-type' is 'Configure' but there is no 'configure' script. "- ++ "You probably need to run 'autoreconf -i' to generate it."- | otherwise = return Nothing--checkLocalPathsExist :: Monad m => CheckPackageContentOps m- -> PackageDescription- -> m [PackageCheck]-checkLocalPathsExist ops pkg = do- let dirs = [ (dir, kind)- | bi <- allBuildInfo pkg- , (dir, kind) <-- [ (dir, "extra-lib-dirs") | dir <- extraLibDirs bi ]- ++ [ (dir, "extra-framework-dirs")- | dir <- extraFrameworkDirs bi ]- ++ [ (dir, "include-dirs") | dir <- includeDirs bi ]- ++ [ (getSymbolicPath dir, "hs-source-dirs") | dir <- hsSourceDirs bi ]- , isRelativeOnAnyPlatform dir ]- missing <- filterM (liftM not . doesDirectoryExist ops . fst) dirs- return [ PackageBuildWarning {- explanation = quote (kind ++ ": " ++ dir)- ++ " directory does not exist."- }- | (dir, kind) <- missing ]--checkMissingVcsInfo :: (Monad m, Applicative m)- => CheckPackageContentOps m- -> PackageDescription- -> m [PackageCheck]-checkMissingVcsInfo ops pkg | null (sourceRepos pkg) = do- vcsInUse <- liftM or $ traverse (doesDirectoryExist ops) repoDirnames- if vcsInUse- then return [ PackageDistSuspicious message ]- else return []- where- repoDirnames = [ dirname | repo <- knownRepoTypes- , dirname <- repoTypeDirname repo]- message = "When distributing packages it is encouraged to specify source "- ++ "control information in the .cabal file using one or more "- ++ "'source-repository' sections. See the Cabal user guide for "- ++ "details."--checkMissingVcsInfo _ _ = return []--repoTypeDirname :: KnownRepoType -> [FilePath]-repoTypeDirname Darcs = ["_darcs"]-repoTypeDirname Git = [".git"]-repoTypeDirname SVN = [".svn"]-repoTypeDirname CVS = ["CVS"]-repoTypeDirname Mercurial = [".hg"]-repoTypeDirname GnuArch = [".arch-params"]-repoTypeDirname Bazaar = [".bzr"]-repoTypeDirname Monotone = ["_MTN"]-repoTypeDirname Pijul = [".pijul"]---- --------------------------------------------------------------- * Checks involving files in the package--- ---------------------------------------------------------------- | Check the names of all files in a package for portability problems. This--- should be done for example when creating or validating a package tarball.----checkPackageFileNames :: [FilePath] -> [PackageCheck]-checkPackageFileNames files =- (take 1 . mapMaybe checkWindowsPath $ files)- ++ (take 1 . mapMaybe checkTarPath $ files)- -- If we get any of these checks triggering then we're likely to get- -- many, and that's probably not helpful, so return at most one.--checkWindowsPath :: FilePath -> Maybe PackageCheck-checkWindowsPath path =- check (not $ FilePath.Windows.isValid path') $- PackageDistInexcusable $- "Unfortunately, the file " ++ quote path ++ " is not a valid file "- ++ "name on Windows which would cause portability problems for this "- ++ "package. Windows file names cannot contain any of the characters "- ++ "\":*?<>|\" and there are a few reserved names including \"aux\", "- ++ "\"nul\", \"con\", \"prn\", \"com1-9\", \"lpt1-9\" and \"clock$\"."- where- path' = ".\\" ++ path- -- force a relative name to catch invalid file names like "f:oo" which- -- otherwise parse as file "oo" in the current directory on the 'f' drive.---- | Check a file name is valid for the portable POSIX tar format.------ The POSIX tar format has a restriction on the length of file names. It is--- unfortunately not a simple restriction like a maximum length. The exact--- restriction is that either the whole path be 100 characters or less, or it--- be possible to split the path on a directory separator such that the first--- part is 155 characters or less and the second part 100 characters or less.----checkTarPath :: FilePath -> Maybe PackageCheck-checkTarPath path- | length path > 255 = Just longPath- | otherwise = case pack nameMax (reverse (splitPath path)) of- Left err -> Just err- Right [] -> Nothing- Right (h:rest) -> case pack prefixMax remainder of- Left err -> Just err- Right [] -> Nothing- Right (_:_) -> Just noSplit- where- -- drop the '/' between the name and prefix:- remainder = safeInit h : rest-- where- nameMax, prefixMax :: Int- nameMax = 100- prefixMax = 155-- pack _ [] = Left emptyName- pack maxLen (c:cs)- | n > maxLen = Left longName- | otherwise = Right (pack' maxLen n cs)- where n = length c-- pack' maxLen n (c:cs)- | n' <= maxLen = pack' maxLen n' cs- where n' = n + length c- pack' _ _ cs = cs-- longPath = PackageDistInexcusable $- "The following file name is too long to store in a portable POSIX "- ++ "format tar archive. The maximum length is 255 ASCII characters.\n"- ++ "The file in question is:\n " ++ path- longName = PackageDistInexcusable $- "The following file name is too long to store in a portable POSIX "- ++ "format tar archive. The maximum length for the name part (including "- ++ "extension) is 100 ASCII characters. The maximum length for any "- ++ "individual directory component is 155.\n"- ++ "The file in question is:\n " ++ path- noSplit = PackageDistInexcusable $- "The following file name is too long to store in a portable POSIX "- ++ "format tar archive. While the total length is less than 255 ASCII "- ++ "characters, there are unfortunately further restrictions. It has to "- ++ "be possible to split the file path on a directory separator into "- ++ "two parts such that the first part fits in 155 characters or less "- ++ "and the second part fits in 100 characters or less. Basically you "- ++ "have to make the file name or directory names shorter, or you could "- ++ "split a long directory name into nested subdirectories with shorter "- ++ "names.\nThe file in question is:\n " ++ path- emptyName = PackageDistInexcusable $- "Encountered a file with an empty name, something is very wrong! "- ++ "Files with an empty name cannot be stored in a tar archive or in "- ++ "standard file systems."---- ----------------------------------------------------------------- * Checks for missing content and other pre-distribution checks--- ------------------------------------------------------------------ | Similar to 'checkPackageContent', 'checkPackageFilesPreDistribution'--- inspects the files included in the package, but is primarily looking for--- files in the working tree that may have been missed or other similar--- problems that can only be detected pre-distribution.------ Because Hackage necessarily checks the uploaded tarball, it is too late to--- check these on the server; these checks only make sense in the development--- and package-creation environment. Hence we can use IO, rather than needing--- to pass a 'CheckPackageContentOps' dictionary around.-checkPackageFilesPreDistribution :: Verbosity -> PackageDescription -> FilePath -> IO [PackageCheck]--- Note: this really shouldn't return any 'Inexcusable' warnings,--- because that will make us say that Hackage would reject the package.--- But, because Hackage doesn't run these tests, that will be a lie!-checkPackageFilesPreDistribution = checkGlobFiles---- | Discover problems with the package's wildcards.-checkGlobFiles :: Verbosity- -> PackageDescription- -> FilePath- -> IO [PackageCheck]-checkGlobFiles verbosity pkg root =- fmap concat $ for allGlobs $ \(field, dir, glob) ->- -- Note: we just skip over parse errors here; they're reported elsewhere.- case parseFileGlob (specVersion pkg) glob of- Left _ -> return []- Right parsedGlob -> do- results <- runDirFileGlob verbosity (root </> dir) parsedGlob- let individualWarnings = results >>= getWarning field glob- noMatchesWarning =- [ PackageDistSuspiciousWarn $- "In '" ++ field ++ "': the pattern '" ++ glob ++ "' does not"- ++ " match any files."- | all (not . suppressesNoMatchesWarning) results- ]- return (noMatchesWarning ++ individualWarnings)- where- adjustedDataDir = if null (dataDir pkg) then "." else dataDir pkg- allGlobs = concat- [ (,,) "extra-source-files" "." <$> extraSrcFiles pkg- , (,,) "extra-doc-files" "." <$> extraDocFiles pkg- , (,,) "data-files" adjustedDataDir <$> dataFiles pkg- ]-- -- If there's a missing directory in play, since our globs don't- -- (currently) support disjunction, that will always mean there are no- -- matches. The no matches error in this case is strictly less informative- -- than the missing directory error, so sit on it.- suppressesNoMatchesWarning (GlobMatch _) = True- suppressesNoMatchesWarning (GlobWarnMultiDot _) = False- suppressesNoMatchesWarning (GlobMissingDirectory _) = True-- getWarning :: String -> FilePath -> GlobResult FilePath -> [PackageCheck]- getWarning _ _ (GlobMatch _) =- []- -- Before Cabal 2.4, the extensions of globs had to match the file- -- exactly. This has been relaxed in 2.4 to allow matching only the- -- suffix. This warning detects when pre-2.4 package descriptions are- -- omitting files purely because of the stricter check.- getWarning field glob (GlobWarnMultiDot file) =- [ PackageDistSuspiciousWarn $- "In '" ++ field ++ "': the pattern '" ++ glob ++ "' does not"- ++ " match the file '" ++ file ++ "' because the extensions do not"- ++ " exactly match (e.g., foo.en.html does not exactly match *.html)."- ++ " To enable looser suffix-only matching, set 'cabal-version: 2.4' or higher."- ]- getWarning field glob (GlobMissingDirectory dir) =- [ PackageDistSuspiciousWarn $- "In '" ++ field ++ "': the pattern '" ++ glob ++ "' attempts to"- ++ " match files in the directory '" ++ dir ++ "', but there is no"- ++ " directory by that name."- ]---- --------------------------------------------------------------- * Utils--- --------------------------------------------------------------quote :: String -> String-quote s = "'" ++ s ++ "'"--commaSep :: [String] -> String-commaSep = intercalate ", "--dups :: Ord a => [a] -> [a]-dups xs = [ x | (x:_:_) <- group (sort xs) ]--fileExtensionSupportedLanguage :: FilePath -> Bool-fileExtensionSupportedLanguage path =- isHaskell || isC- where- extension = takeExtension path- isHaskell = extension `elem` [".hs", ".lhs"]- isC = isJust (filenameCDialect extension)---- | Whether a path is a good relative path.------ >>> let test fp = putStrLn $ show (isGoodRelativeDirectoryPath fp) ++ "; " ++ show (isGoodRelativeFilePath fp)------ >>> test "foo/bar/quu"--- Nothing; Nothing------ Trailing slash is not allowed for files, for directories it is ok.------ >>> test "foo/"--- Nothing; Just "trailing slash"------ Leading @./@ is fine, but @.@ and @./@ are not valid files.------ >>> traverse_ test [".", "./", "./foo/bar"]--- Nothing; Just "trailing dot segment"--- Nothing; Just "trailing slash"--- Nothing; Nothing------ Lastly, not good file nor directory cases:------ >>> traverse_ test ["", "/tmp/src", "foo//bar", "foo/.", "foo/./bar", "foo/../bar", "foo*bar"]--- Just "empty path"; Just "empty path"--- Just "posix absolute path"; Just "posix absolute path"--- Just "empty path segment"; Just "empty path segment"--- Just "trailing same directory segment: ."; Just "trailing same directory segment: ."--- Just "same directory segment: ."; Just "same directory segment: .."--- Just "parent directory segment: .."; Just "parent directory segment: .."--- Just "reserved character '*'"; Just "reserved character '*'"------ For the last case, 'isGoodRelativeGlob' doesn't warn:------ >>> traverse_ (print . isGoodRelativeGlob) ["foo/../bar", "foo*bar"]--- Just "parent directory segment: .."--- Nothing----isGoodRelativeFilePath :: FilePath -> Maybe String-isGoodRelativeFilePath = state0- where- -- Reserved characters- -- https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file- isReserved c = c `elem` "<>:\"\\/|?*"-- -- initial state- state0 [] = Just "empty path"- state0 (c:cs) | c == '.' = state1 cs- | c == '/' = Just "posix absolute path"- | isReserved c = Just ("reserved character " ++ show c)- | otherwise = state3 cs-- -- after .- state1 [] = Just "trailing dot segment"- state1 (c:cs) | c == '.' = state4 cs- | c == '/' = state2 cs- | isReserved c = Just ("reserved character " ++ show c)- | otherwise = state5 cs-- -- after ./- state2 [] = Just "trailing slash"- state2 (c:cs) | c == '.' = state3 cs- | c == '/' = Just "empty path segment"- | isReserved c = Just ("reserved character " ++ show c)- | otherwise = state5 cs-- -- after non-first segment's .- state3 [] = Just "trailing same directory segment: ."- state3 (c:cs) | c == '.' = state4 cs- | c == '/' = Just "same directory segment: .."- | isReserved c = Just ("reserved character " ++ show c)- | otherwise = state5 cs-- -- after non-first segment's ..- state4 [] = Just "trailing parent directory segment: .."- state4 (c:cs) | c == '.' = state5 cs- | c == '/' = Just "parent directory segment: .."- | isReserved c = Just ("reserved character " ++ show c)- | otherwise = state5 cs-- -- in a segment which is ok.- state5 [] = Nothing- state5 (c:cs) | c == '.' = state3 cs- | c == '/' = state2 cs- | isReserved c = Just ("reserved character " ++ show c)- | otherwise = state5 cs---- | See 'isGoodRelativeFilePath'.------ This is barebones function. We check whether the glob is a valid file--- by replacing stars @*@ with @x@ses.-isGoodRelativeGlob :: FilePath -> Maybe String-isGoodRelativeGlob = isGoodRelativeFilePath . map f where- f '*' = 'x'- f c = c---- | See 'isGoodRelativeFilePath'.-isGoodRelativeDirectoryPath :: FilePath -> Maybe String-isGoodRelativeDirectoryPath = state0- where- -- Reserved characters- -- https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file- isReserved c = c `elem` "<>:\"\\/|?*"-- -- initial state- state0 [] = Just "empty path"- state0 (c:cs) | c == '.' = state5 cs- | c == '/' = Just "posix absolute path"- | isReserved c = Just ("reserved character " ++ show c)- | otherwise = state4 cs-- -- after ./- state1 [] = Nothing -- "./"- state1 (c:cs) | c == '.' = state2 cs- | c == '/' = Just "empty path segment"- | isReserved c = Just ("reserved character " ++ show c)- | otherwise = state4 cs-- -- after non-first setgment's .- state2 [] = Just "trailing same directory segment: ."- state2 (c:cs) | c == '.' = state3 cs- | c == '/' = Just "same directory segment: ."- | isReserved c = Just ("reserved character " ++ show c)- | otherwise = state4 cs-- -- after non-first segment's ..- state3 [] = Just "trailing parent directory segment: ."- state3 (c:cs) | c == '.' = state4 cs- | c == '/' = Just "parent directory segment: .."- | isReserved c = Just ("reserved character " ++ show c)- | otherwise = state4 cs-- -- in a segment which is ok.- state4 [] = Nothing- state4 (c:cs) | c == '.' = state4 cs- | c == '/' = state1 cs- | isReserved c = Just ("reserved character " ++ show c)- | otherwise = state4 cs-- -- after .- state5 [] = Nothing -- "."- state5 (c:cs) | c == '.' = state3 cs- | c == '/' = state1 cs- | isReserved c = Just ("reserved character " ++ show c)- | otherwise = state4 cs---- [Note: Good relative paths]------ Using @kleene@ we can define an extended regex:------ @--- import Algebra.Lattice--- import Kleene--- import Kleene.ERE (ERE (..), intersections)------ data C = CDot | CSlash | COtherReserved | CChar--- deriving (Eq, Ord, Enum, Bounded, Show)------ reservedR :: ERE C--- reservedR = notChar CSlash /\ notChar COtherReserved------ pathPieceR :: ERE C--- pathPieceR = intersections--- [ plus reservedR--- , ERENot (string [CDot])--- , ERENot (string [CDot,CDot])--- ]------ filePathR :: ERE C--- filePathR = optional (string [CDot, CSlash]) <> pathPieceR <> star (char CSlash <> pathPieceR)------ dirPathR :: ERE C--- dirPathR = (char CDot \/ filePathR) <> optional (char CSlash)------ plus :: ERE C -> ERE C--- plus r = r <> star r------ optional :: ERE C -> ERE C--- optional r = mempty \/ r--- @------ Results in following state machine for @filePathR@------ @--- 0 -> \x -> if--- | x <= CDot -> 1--- | x <= COtherReserved -> 6--- | otherwise -> 5--- 1 -> \x -> if--- | x <= CDot -> 4--- | x <= CSlash -> 2--- | x <= COtherReserved -> 6--- | otherwise -> 5--- 2 -> \x -> if--- | x <= CDot -> 3--- | x <= COtherReserved -> 6--- | otherwise -> 5--- 3 -> \x -> if--- | x <= CDot -> 4--- | x <= COtherReserved -> 6--- | otherwise -> 5--- 4 -> \x -> if--- | x <= CDot -> 5--- | x <= COtherReserved -> 6--- | otherwise -> 5--- 5+ -> \x -> if--- | x <= CDot -> 5--- | x <= CSlash -> 2--- | x <= COtherReserved -> 6--- | otherwise -> 5--- 6 -> \_ -> 6 -- black hole--- @------ and @dirPathR@:------ @--- 0 -> \x -> if--- | x <= CDot -> 5--- | x <= COtherReserved -> 6--- | otherwise -> 4--- 1+ -> \x -> if--- | x <= CDot -> 2--- | x <= COtherReserved -> 6--- | otherwise -> 4--- 2 -> \x -> if--- | x <= CDot -> 3--- | x <= COtherReserved -> 6--- | otherwise -> 4--- 3 -> \x -> if--- | x <= CDot -> 4--- | x <= COtherReserved -> 6--- | otherwise -> 4--- 4+ -> \x -> if--- | x <= CDot -> 4--- | x <= CSlash -> 1--- | x <= COtherReserved -> 6--- | otherwise -> 4--- 5+ -> \x -> if--- | x <= CDot -> 3--- | x <= CSlash -> 1--- | x <= COtherReserved -> 6--- | otherwise -> 4--- 6 -> \_ -> 6 -- black hole--- @+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- |+-- Module : Distribution.PackageDescription.Check+-- Copyright : Lennart Kolmodin 2008, Francesco Ariis 2022+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- This has code for checking for various problems in packages. There is one+-- set of checks that just looks at a 'PackageDescription' in isolation and+-- another set of checks that also looks at files in the package. Some of the+-- checks are basic sanity checks, others are portability standards that we'd+-- like to encourage. There is a 'PackageCheck' type that distinguishes the+-- different kinds of checks so we can see which ones are appropriate to report+-- in different situations. This code gets used when configuring a package when+-- we consider only basic problems. The higher standard is used when+-- preparing a source tarball and by Hackage when uploading new packages. The+-- reason for this is that we want to hold packages that are expected to be+-- distributed to a higher standard than packages that are only ever expected+-- to be used on the author's own environment.+module Distribution.PackageDescription.Check+ ( -- * Package Checking+ CheckExplanation (..)+ , CheckExplanationID+ , CheckExplanationIDString+ , PackageCheck (..)+ , checkPackage+ , checkConfiguredPackage+ , wrapParseWarning+ , ppPackageCheck+ , ppCheckExplanationId+ , isHackageDistError+ , filterPackageChecksById+ , filterPackageChecksByIdString++ -- ** Checking package contents+ , checkPackageFiles+ , checkPackageFilesGPD+ , checkPackageContent+ , CheckPackageContentOps (..)+ ) where++import Distribution.Compat.Prelude+import Prelude ()++import Data.List (group)+import Distribution.CabalSpecVersion+import Distribution.Compat.Lens+import Distribution.Compiler+import Distribution.License+import Distribution.Package+import Distribution.PackageDescription+import Distribution.PackageDescription.Check.Common+import Distribution.PackageDescription.Check.Conditional+import Distribution.PackageDescription.Check.Monad+import Distribution.PackageDescription.Check.Paths+import Distribution.PackageDescription.Check.Target+import Distribution.PackageDescription.Check.Warning+import Distribution.Parsec.Warning (PWarning)+import Distribution.Pretty (prettyShow)+import Distribution.Simple.Glob+ ( Glob+ , GlobResult (..)+ , globMatches+ , parseFileGlob+ , runDirFileGlob+ )+import Distribution.Simple.Utils hiding (findPackageDesc, notice)+import Distribution.Utils.Generic (isAscii)+import Distribution.Utils.Path+import Distribution.Verbosity+import Distribution.Version+import System.FilePath (splitExtension, takeFileName)++import qualified Data.ByteString.Lazy as BS+import qualified Distribution.SPDX as SPDX+import qualified System.Directory as System++import qualified System.Directory (getDirectoryContents)+import qualified System.FilePath.Windows as FilePath.Windows (isValid)++import qualified Data.Set as Set+import qualified Distribution.Utils.ShortText as ShortText++import qualified Distribution.Types.GenericPackageDescription.Lens as L++import Control.Monad++-- $setup+-- >>> import Control.Arrow ((&&&))++-- ☞ N.B.+--+-- Part of the tools/scaffold used to perform check is found in+-- Distribution.PackageDescription.Check.Types. Summary of that module (for+-- how we use it here):+-- 1. we work inside a 'CheckM m a' monad (where `m` is an abstraction to+-- run non-pure checks);+-- 2. 'checkP', 'checkPre' functions perform checks (respectively pure and+-- non-pure);+-- 3. 'PackageCheck' and 'CheckExplanation' are types for warning severity+-- and description.++-- ------------------------------------------------------------+-- Checking interface+-- ------------------------------------------------------------++-- | 'checkPackagePrim' is the most general way to invoke package checks.+-- We pass to it two interfaces (one to check contents of packages, the+-- other to inspect working tree for orphan files) and before that a+-- Boolean to indicate whether we want pure checks or not. Based on these+-- parameters, some checks will be performed, some omitted.+-- Generality over @m@ means we could do non pure checks in monads other+-- than IO (e.g. a virtual filesystem, like a zip file, a VCS filesystem,+-- etc).+checkPackagePrim+ :: Monad m+ => Bool -- Perform pure checks?+ -> Maybe (CheckPackageContentOps m) -- Package content interface.+ -> Maybe (CheckPreDistributionOps m) -- Predist checks interface.+ -> GenericPackageDescription -- GPD to check.+ -> m [PackageCheck]+checkPackagePrim b mco mpdo gpd = do+ let cm = checkGenericPackageDescription gpd+ ci = CheckInterface b mco mpdo+ ctx = pristineCheckCtx ci gpd+ execCheckM cm ctx++-- | Check for common mistakes and problems in package descriptions.+--+-- This is the standard collection of checks covering all aspects except+-- for checks that require looking at files within the package. For those+-- see 'checkPackageFiles'.+checkPackage :: GenericPackageDescription -> [PackageCheck]+checkPackage gpd = runIdentity $ checkPackagePrim True Nothing Nothing gpd++-- | This function is an oddity due to the historical+-- GenericPackageDescription/PackageDescription split. It is only maintained+-- not to break interface, use `checkPackage` if possible.+checkConfiguredPackage :: PackageDescription -> [PackageCheck]+checkConfiguredPackage pd = checkPackage (pd2gpd pd)++-- | Sanity check things that requires looking at files in the package.+-- This is a generalised version of 'checkPackageFiles' that can work in any+-- monad for which you can provide 'CheckPackageContentOps' operations.+--+-- The point of this extra generality is to allow doing checks in some virtual+-- file system, for example a tarball in memory.+checkPackageContent+ :: Monad m+ => CheckPackageContentOps m+ -> GenericPackageDescription+ -> m [PackageCheck]+checkPackageContent pops gpd = checkPackagePrim False (Just pops) Nothing gpd++-- | Sanity checks that require IO. 'checkPackageFiles' looks at the files+-- in the package and expects to find the package unpacked at the given+-- filepath.+checkPackageFilesGPD+ :: Verbosity -- Glob warn message verbosity.+ -> GenericPackageDescription+ -> FilePath -- Package root.+ -> IO [PackageCheck]+checkPackageFilesGPD verbosity gpd root =+ checkPackagePrim False (Just checkFilesIO) (Just checkPreIO) gpd+ where+ checkFilesIO =+ CheckPackageContentOps+ { doesFileExist = System.doesFileExist . relative+ , doesDirectoryExist = System.doesDirectoryExist . relative+ , getDirectoryContents = System.Directory.getDirectoryContents . relative+ , getFileContents = BS.readFile . relative+ }++ checkPreIO =+ CheckPreDistributionOps+ { runDirFileGlobM = \fp g -> runDirFileGlob verbosity (Just . specVersion $ packageDescription gpd) (root </> fp) g+ , getDirectoryContentsM = System.Directory.getDirectoryContents . relative+ }++ relative :: FilePath -> FilePath+ relative path = root </> path++-- | Same as 'checkPackageFilesGPD', but working with 'PackageDescription'.+--+-- This function is included for legacy reasons, use 'checkPackageFilesGPD'+-- if you are working with 'GenericPackageDescription'.+checkPackageFiles+ :: Verbosity -- Glob warn message verbosity.+ -> PackageDescription+ -> FilePath -- Package root.+ -> IO [PackageCheck]+checkPackageFiles verbosity pd oot =+ checkPackageFilesGPD verbosity (pd2gpd pd) oot++-- ------------------------------------------------------------+-- Package description+-- ------------------------------------------------------------++-- Here lies the meat of the module. Starting from 'GenericPackageDescription',+-- we walk the data while doing a number of checks.+--+-- Where applicable we do a full pattern match (if the data changes, code will+-- break: a gentle reminder to add more checks).+-- Pattern matching variables convention: matching accessor + underscore.+-- This way it is easier to see which one we are missing if we run into+-- an “GPD should have 20 arguments but has been given only 19” error.++-- | 'GenericPackageDescription' checks. Remember that for historical quirks+-- in the cabal codebase we have both `GenericPackageDescription` and+-- `PackageDescription` and that PD is both a *field* of GPD and a concept+-- of its own (i.e. a fully realised GPD).+-- In this case we are checking (correctly) GPD, so for target info/checks+-- you should walk condLibrary_ etc. and *not* the (empty) target info in+-- PD. See 'pd2gpd' for a convenient hack when you only have+-- 'PackageDescription'.+checkGenericPackageDescription+ :: Monad m+ => GenericPackageDescription+ -> CheckM m ()+checkGenericPackageDescription+ gpd@( GenericPackageDescription+ packageDescription_+ _gpdScannedVersion_+ genPackageFlags_+ condLibrary_+ condSubLibraries_+ condForeignLibs_+ condExecutables_+ condTestSuites_+ condBenchmarks_+ ) =+ do+ -- § Description and names.+ checkPackageDescription packageDescription_+ -- Targets should be present...+ let condAllLibraries =+ maybeToList condLibrary_+ ++ (map snd condSubLibraries_)+ checkP+ ( and+ [ null condExecutables_+ , null condTestSuites_+ , null condBenchmarks_+ , null condAllLibraries+ , null condForeignLibs_+ ]+ )+ (PackageBuildImpossible NoTarget)+ -- ... and have unique names (names are not under conditional, it is+ -- appropriate to check here.+ (nsubs, nexes, ntests, nbenchs) <-+ asksCM+ ( ( \n ->+ ( pnSubLibs n+ , pnExecs n+ , pnTests n+ , pnBenchs n+ )+ )+ . ccNames+ )+ let names = concat [nsubs, nexes, ntests, nbenchs]+ dupes = dups names+ checkP+ (not . null $ dups names)+ (PackageBuildImpossible $ DuplicateSections dupes)+ -- Flag names.+ mapM_ checkFlagName genPackageFlags_++ -- § Feature checks.+ checkSpecVer+ CabalSpecV2_0+ (not . null $ condSubLibraries_)+ (PackageDistInexcusable CVMultiLib)+ checkSpecVer+ CabalSpecV1_8+ (not . null $ condTestSuites_)+ (PackageDistInexcusable CVTestSuite)++ -- § Conditional targets++ -- Extract dependencies from libraries, to be passed along for+ -- PVP checks purposes.+ pName <-+ asksCM+ ( packageNameToUnqualComponentName+ . pkgName+ . pnPackageId+ . ccNames+ )+ let ads =+ maybe [] ((: []) . extractAssocDeps pName) condLibrary_+ ++ map (uncurry extractAssocDeps) condSubLibraries_++ case condLibrary_ of+ Just cl ->+ checkCondTarget+ genPackageFlags_+ (checkLibrary False ads)+ (const id)+ (mempty, cl)+ Nothing -> return ()+ mapM_+ ( checkCondTarget+ genPackageFlags_+ (checkLibrary False ads)+ (\u l -> l{libName = maybeToLibraryName (Just u)})+ )+ condSubLibraries_+ mapM_+ ( checkCondTarget+ genPackageFlags_+ checkForeignLib+ (const id)+ )+ condForeignLibs_+ mapM_+ ( checkCondTarget+ genPackageFlags_+ (checkExecutable ads)+ (const id)+ )+ condExecutables_+ mapM_+ ( checkCondTarget+ genPackageFlags_+ (checkTestSuite ads)+ (\u l -> l{testName = u})+ )+ condTestSuites_+ mapM_+ ( checkCondTarget+ genPackageFlags_+ (checkBenchmark ads)+ (\u l -> l{benchmarkName = u})+ )+ condBenchmarks_++ -- For unused flags it is clearer and more convenient to fold the+ -- data rather than walk it, an exception to the rule.+ checkP+ (decFlags /= usedFlags)+ (PackageDistSuspicious $ DeclaredUsedFlags decFlags usedFlags)++ -- Duplicate modules.+ mapM_ tellP (checkDuplicateModules gpd)+ where+ -- todo is this caught at parse time?+ checkFlagName :: Monad m => PackageFlag -> CheckM m ()+ checkFlagName pf =+ let fn = unFlagName . flagName $ pf++ invalidFlagName ('-' : _) = True -- starts with dash+ invalidFlagName cs = any (not . isAscii) cs -- non ASCII+ in checkP+ (invalidFlagName fn)+ (PackageDistInexcusable $ SuspiciousFlagName [fn])++ decFlags :: Set.Set FlagName+ decFlags = toSetOf (L.genPackageFlags . traverse . L.flagName) gpd++ usedFlags :: Set.Set FlagName+ usedFlags =+ mconcat+ [ toSetOf (L.condLibrary . traverse . traverseCondTreeV . L._PackageFlag) gpd+ , toSetOf (L.condSubLibraries . traverse . _2 . traverseCondTreeV . L._PackageFlag) gpd+ , toSetOf (L.condForeignLibs . traverse . _2 . traverseCondTreeV . L._PackageFlag) gpd+ , toSetOf (L.condExecutables . traverse . _2 . traverseCondTreeV . L._PackageFlag) gpd+ , toSetOf (L.condTestSuites . traverse . _2 . traverseCondTreeV . L._PackageFlag) gpd+ , toSetOf (L.condBenchmarks . traverse . _2 . traverseCondTreeV . L._PackageFlag) gpd+ ]++checkPackageDescription :: Monad m => PackageDescription -> CheckM m ()+checkPackageDescription+ pkg@( PackageDescription+ specVersion_+ package_+ licenseRaw_+ licenseFiles_+ _copyright_+ maintainer_+ _author_+ _stability_+ testedWith_+ _homepage_+ _pkgUrl_+ _bugReports_+ sourceRepos_+ synopsis_+ description_+ category_+ customFieldsPD_+ buildTypeRaw_+ setupBuildInfo_+ _library_+ _subLibraries_+ _executables_+ _foreignLibs_+ _testSuites_+ _benchmarks_+ dataFiles_+ dataDir_+ extraSrcFiles_+ extraTmpFiles_+ extraDocFiles_+ extraFiles_+ ) = do+ -- § Sanity checks.+ checkPackageId package_+ -- TODO `name` is caught at parse level, remove this test.+ let pn = packageName package_+ checkP+ (null . unPackageName $ pn)+ (PackageBuildImpossible NoNameField)+ -- TODO `version` is caught at parse level, remove this test.+ checkP+ (nullVersion == packageVersion package_)+ (PackageBuildImpossible NoVersionField)+ -- But it is OK for executables to have the same name.+ nsubs <- asksCM (pnSubLibs . ccNames)+ checkP+ (any (== prettyShow pn) (prettyShow <$> nsubs))+ (PackageBuildImpossible $ IllegalLibraryName pn)++ -- § Fields check.+ checkNull+ category_+ (PackageDistSuspicious MissingFieldCategory)+ checkNull+ maintainer_+ (PackageDistSuspicious MissingFieldMaintainer)+ checkP+ (ShortText.null synopsis_ && not (ShortText.null description_))+ (PackageDistSuspicious MissingFieldSynopsis)+ checkP+ (ShortText.null description_ && not (ShortText.null synopsis_))+ (PackageDistSuspicious MissingFieldDescription)+ checkP+ (all ShortText.null [synopsis_, description_])+ (PackageDistInexcusable MissingFieldSynOrDesc)+ checkP+ (ShortText.length synopsis_ > 80)+ (PackageDistSuspicious SynopsisTooLong)+ checkP+ ( not (ShortText.null description_)+ && ShortText.length description_ <= ShortText.length synopsis_+ )+ (PackageDistSuspicious ShortDesc)++ -- § Paths.+ mapM_ (checkPath False "extra-source-files" PathKindGlob . getSymbolicPath) extraSrcFiles_+ mapM_ (checkPath False "extra-tmp-files" PathKindFile . getSymbolicPath) extraTmpFiles_+ mapM_ (checkPath False "extra-doc-files" PathKindGlob . getSymbolicPath) extraDocFiles_+ mapM_ (checkPath False "extra-files" PathKindGlob . getSymbolicPath) extraFiles_+ mapM_ (checkPath False "data-files" PathKindGlob . getSymbolicPath) dataFiles_+ let rawDataDir = getSymbolicPath dataDir_+ checkPath True "data-dir" PathKindDirectory rawDataDir+ let licPaths = map getSymbolicPath licenseFiles_+ mapM_ (checkPath False "license-file" PathKindFile) licPaths+ mapM_ checkLicFileExist licenseFiles_++ -- § Globs.+ dataGlobs <- mapM (checkGlob "data-files" . getSymbolicPath) dataFiles_+ extraSrcGlobs <- mapM (checkGlob "extra-source-files" . getSymbolicPath) extraSrcFiles_+ docGlobs <- mapM (checkGlob "extra-doc-files" . getSymbolicPath) extraDocFiles_+ extraGlobs <- mapM (checkGlob "extra-files" . getSymbolicPath) extraFiles_+ -- We collect globs to feed them to checkMissingDocs.++ -- § Missing documentation.+ checkMissingDocs+ (catMaybes dataGlobs)+ (catMaybes extraSrcGlobs)+ (catMaybes docGlobs)+ (catMaybes extraGlobs)++ -- § Datafield checks.+ checkSetupBuildInfo setupBuildInfo_+ mapM_ checkTestedWith testedWith_+ either+ checkNewLicense+ (checkOldLicense $ null licenseFiles_)+ licenseRaw_+ checkSourceRepos sourceRepos_+ mapM_ checkCustomField customFieldsPD_++ -- Feature checks.+ checkSpecVer+ CabalSpecV1_18+ (not . null $ extraDocFiles_)+ (PackageDistInexcusable CVExtraDocFiles)+ checkSpecVer+ CabalSpecV1_6+ (not . null $ sourceRepos_)+ (PackageDistInexcusable CVSourceRepository)+ checkP+ ( specVersion_ >= CabalSpecV1_24+ && isNothing setupBuildInfo_+ && buildTypeRaw_ == Just Custom+ )+ (PackageBuildWarning CVCustomSetup)+ checkSpecVer+ CabalSpecV1_24+ ( isNothing setupBuildInfo_+ && buildTypeRaw_ == Just Custom+ )+ (PackageDistSuspiciousWarn CVExpliticDepsCustomSetup)+ checkP+ (isNothing buildTypeRaw_ && specVersion_ < CabalSpecV2_2)+ (PackageBuildWarning NoBuildType)+ checkP+ (isJust setupBuildInfo_ && buildType pkg `notElem` [Custom, Hooks])+ (PackageBuildWarning NoCustomSetup)++ -- Contents.+ checkConfigureExists (buildType pkg)+ checkSetupExists (buildType pkg)+ checkCabalFile (packageName pkg)+ mapM_ (checkGlobFile specVersion_ "." "extra-source-files" . getSymbolicPath) extraSrcFiles_+ mapM_ (checkGlobFile specVersion_ "." "extra-doc-files" . getSymbolicPath) extraDocFiles_+ mapM_ (checkGlobFile specVersion_ "." "extra-files" . getSymbolicPath) extraFiles_+ mapM_ (checkGlobFile specVersion_ rawDataDir "data-files" . getSymbolicPath) dataFiles_+ where+ checkNull+ :: Monad m+ => ShortText.ShortText+ -> PackageCheck+ -> CheckM m ()+ checkNull st c = checkP (ShortText.null st) c++ checkTestedWith+ :: Monad m+ => (CompilerFlavor, VersionRange)+ -> CheckM m ()+ checkTestedWith (OtherCompiler n, _) =+ tellP (PackageBuildWarning $ UnknownCompilers [n])+ checkTestedWith (compiler, versionRange) =+ checkVersionRange compiler versionRange++ checkVersionRange+ :: Monad m+ => CompilerFlavor+ -> VersionRange+ -> CheckM m ()+ checkVersionRange cmp vr =+ when+ (isNoVersion vr)+ ( let dep =+ [ Dependency+ (mkPackageName (prettyShow cmp))+ vr+ mainLibSet+ ]+ in tellP (PackageDistInexcusable (InvalidTestWith dep))+ )++checkSetupBuildInfo :: Monad m => Maybe SetupBuildInfo -> CheckM m ()+checkSetupBuildInfo Nothing = return ()+checkSetupBuildInfo (Just (SetupBuildInfo ds _)) = do+ let uqs = map mkUnqualComponentName ["base", "Cabal"]+ (is, rs) <- partitionDeps [] uqs ds+ let ick = PackageDistInexcusable . UpperBoundSetup+ rck =+ PackageDistSuspiciousWarn+ . MissingUpperBounds CETSetup+ leuck =+ PackageDistSuspiciousWarn+ . LEUpperBounds CETSetup+ tzuck =+ PackageDistSuspiciousWarn+ . TrailingZeroUpperBounds CETSetup+ gtlck =+ PackageDistSuspiciousWarn+ . GTLowerBounds CETSetup+ checkPVP (checkDependencyVersionRange $ not . hasUpperBound) ick is+ checkPVPs (checkDependencyVersionRange $ not . hasUpperBound) rck rs+ checkPVPs (checkDependencyVersionRange hasLEUpperBound) leuck ds+ checkPVPs (checkDependencyVersionRange hasTrailingZeroUpperBound) tzuck ds+ checkPVPs (checkDependencyVersionRange hasGTLowerBound) gtlck ds++checkPackageId :: Monad m => PackageIdentifier -> CheckM m ()+checkPackageId (PackageIdentifier pkgName_ _pkgVersion_) = do+ checkP+ (not . FilePath.Windows.isValid . prettyShow $ pkgName_)+ (PackageDistInexcusable $ InvalidNameWin pkgName_)+ checkP (isPrefixOf "z-" . prettyShow $ pkgName_) $+ (PackageDistInexcusable ZPrefix)++checkNewLicense :: Monad m => SPDX.License -> CheckM m ()+checkNewLicense lic = do+ checkP+ (lic == SPDX.NONE)+ (PackageDistInexcusable NONELicense)++checkOldLicense+ :: Monad m+ => Bool -- Flag: no license file?+ -> License+ -> CheckM m ()+checkOldLicense nullLicFiles lic = do+ checkP+ (lic == UnspecifiedLicense)+ (PackageDistInexcusable NoLicense)+ checkP+ (lic == AllRightsReserved)+ (PackageDistSuspicious AllRightsReservedLicense)+ checkSpecVer+ CabalSpecV1_4+ (lic `notElem` compatLicenses)+ (PackageDistInexcusable (LicenseMessParse lic))+ checkP+ (lic == BSD4)+ (PackageDistSuspicious UncommonBSD4)+ case lic of+ UnknownLicense l ->+ tellP (PackageBuildWarning (UnrecognisedLicense l))+ _ -> return ()+ checkP+ ( lic+ `notElem` [ AllRightsReserved+ , UnspecifiedLicense+ , PublicDomain+ ]+ &&+ -- AllRightsReserved and PublicDomain are not strictly+ -- licenses so don't need license files.+ nullLicFiles+ )+ $ (PackageDistSuspicious NoLicenseFile)+ case unknownLicenseVersion lic of+ Just knownVersions ->+ tellP+ (PackageDistSuspicious $ UnknownLicenseVersion lic knownVersions)+ _ -> return ()+ where+ compatLicenses =+ [ GPL Nothing+ , LGPL Nothing+ , AGPL Nothing+ , BSD3+ , BSD4+ , PublicDomain+ , AllRightsReserved+ , UnspecifiedLicense+ , OtherLicense+ ]++ unknownLicenseVersion (GPL (Just v))+ | v `notElem` knownVersions = Just knownVersions+ where+ knownVersions = [v' | GPL (Just v') <- knownLicenses]+ unknownLicenseVersion (LGPL (Just v))+ | v `notElem` knownVersions = Just knownVersions+ where+ knownVersions = [v' | LGPL (Just v') <- knownLicenses]+ unknownLicenseVersion (AGPL (Just v))+ | v `notElem` knownVersions = Just knownVersions+ where+ knownVersions = [v' | AGPL (Just v') <- knownLicenses]+ unknownLicenseVersion (Apache (Just v))+ | v `notElem` knownVersions = Just knownVersions+ where+ knownVersions = [v' | Apache (Just v') <- knownLicenses]+ unknownLicenseVersion _ = Nothing++checkSourceRepos :: Monad m => [SourceRepo] -> CheckM m ()+checkSourceRepos rs = do+ mapM_ repoCheck rs+ checkMissingVcsInfo rs+ where+ -- Single repository checks.+ repoCheck :: Monad m => SourceRepo -> CheckM m ()+ repoCheck+ ( SourceRepo+ repoKind_+ repoType_+ repoLocation_+ repoModule_+ _repoBranch_+ repoTag_+ repoSubdir_+ ) = do+ case repoKind_ of+ RepoKindUnknown kind ->+ tellP+ (PackageDistInexcusable $ UnrecognisedSourceRepo kind)+ _ -> return ()+ checkP+ (isNothing repoType_)+ (PackageDistInexcusable MissingType)+ checkP+ (isNothing repoLocation_)+ (PackageDistInexcusable MissingLocation)+ checkGitProtocol repoLocation_+ checkP+ ( repoType_ == Just (KnownRepoType CVS)+ && isNothing repoModule_+ )+ (PackageDistInexcusable MissingModule)+ checkP+ (repoKind_ == RepoThis && isNothing repoTag_)+ (PackageDistInexcusable MissingTag)+ checkP+ (any isAbsoluteOnAnyPlatform repoSubdir_)+ (PackageDistInexcusable SubdirRelPath)+ case join . fmap isGoodRelativeDirectoryPath $ repoSubdir_ of+ Just err ->+ tellP+ (PackageDistInexcusable $ SubdirGoodRelPath err)+ Nothing -> return ()++checkMissingVcsInfo :: Monad m => [SourceRepo] -> CheckM m ()+checkMissingVcsInfo rs =+ let rdirs = concatMap repoTypeDirname knownRepoTypes+ in checkPkg+ ( \ops -> do+ us <- or <$> traverse (doesDirectoryExist ops) rdirs+ return (null rs && us)+ )+ (PackageDistSuspicious MissingSourceControl)+ where+ repoTypeDirname :: KnownRepoType -> [FilePath]+ repoTypeDirname Darcs = ["_darcs"]+ repoTypeDirname Git = [".git"]+ repoTypeDirname SVN = [".svn"]+ repoTypeDirname CVS = ["CVS"]+ repoTypeDirname Mercurial = [".hg"]+ repoTypeDirname GnuArch = [".arch-params"]+ repoTypeDirname Bazaar = [".bzr"]+ repoTypeDirname Monotone = ["_MTN"]+ repoTypeDirname Pijul = [".pijul"]++-- git:// lacks TLS or other encryption, see+-- https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols#_the_cons_4+checkGitProtocol+ :: Monad m+ => Maybe String -- Repository location+ -> CheckM m ()+checkGitProtocol mloc =+ checkP+ (fmap (isPrefixOf "git://") mloc == Just True)+ (PackageBuildWarning GitProtocol)++-- ------------------------------------------------------------+-- Package and distribution checks+-- ------------------------------------------------------------++-- | Find a package description file in the given directory. Looks for+-- @.cabal@ files. Like 'Distribution.Simple.Utils.findPackageDesc',+-- but generalized over monads.+findPackageDesc :: Monad m => CheckPackageContentOps m -> m [FilePath]+findPackageDesc ops = do+ let dir = "."+ files <- getDirectoryContents ops dir+ -- to make sure we do not mistake a ~/.cabal/ dir for a <name>.cabal+ -- file we filter to exclude dirs and null base file names:+ cabalFiles <-+ filterM+ (doesFileExist ops)+ [ dir </> file+ | file <- files+ , let (name, ext) = splitExtension file+ , not (null name) && ext == ".cabal"+ ]+ return cabalFiles++checkCabalFile :: Monad m => PackageName -> CheckM m ()+checkCabalFile pn = do+ -- liftInt is a bit more messy than stricter interface, but since+ -- each of the following check is exclusive, we can simplify the+ -- condition flow.+ liftInt+ ciPackageOps+ ( \ops -> do+ -- 1. Get .cabal files.+ ds <- findPackageDesc ops+ case ds of+ [] -> return [PackageBuildImpossible NoDesc]+ -- No .cabal file.+ [d] -> do+ bc <- bomf ops d+ return (catMaybes [bc, noMatch d])+ -- BOM + no matching .cabal checks.+ _ -> return [PackageBuildImpossible $ MultiDesc ds]+ )+ where+ -- Multiple .cabal files.++ bomf+ :: Monad m+ => CheckPackageContentOps m+ -> FilePath+ -> m (Maybe PackageCheck)+ bomf wops wfp = do+ b <- BS.isPrefixOf bomUtf8 <$> getFileContents wops wfp+ if b+ then (return . Just) (PackageDistInexcusable $ BOMStart wfp)+ else return Nothing++ bomUtf8 :: BS.ByteString+ bomUtf8 = BS.pack [0xef, 0xbb, 0xbf] -- U+FEFF encoded as UTF8+ noMatch :: FilePath -> Maybe PackageCheck+ noMatch wd =+ let expd = unPackageName pn <.> "cabal"+ in if takeFileName wd /= expd+ then Just (PackageDistInexcusable $ NotPackageName wd expd)+ else Nothing++checkLicFileExist+ :: Monad m+ => RelativePath Pkg File+ -> CheckM m ()+checkLicFileExist sp = do+ let fp = getSymbolicPath sp+ checkPkg+ (\ops -> not <$> doesFileExist ops fp)+ (PackageBuildWarning $ UnknownFile "license-file" sp)++checkConfigureExists :: Monad m => BuildType -> CheckM m ()+checkConfigureExists Configure =+ checkPkg+ (\ops -> not <$> doesFileExist ops "configure")+ (PackageBuildWarning MissingConfigureScript)+checkConfigureExists _ = return ()++checkSetupExists :: Monad m => BuildType -> CheckM m ()+checkSetupExists Simple = return ()+checkSetupExists _ =+ checkPkg+ ( \ops -> do+ ba <- doesFileExist ops "Setup.hs"+ bb <- doesFileExist ops "Setup.lhs"+ return (not $ ba || bb)+ )+ (PackageDistInexcusable MissingSetupFile)++-- The following functions are similar to 'CheckPackageContentOps m' ones,+-- but, as they inspect the files included in the package, but are primarily+-- looking for files in the working tree that may have been missed or other+-- similar problems that can only be detected pre-distribution.+--+-- Because Hackage necessarily checks the uploaded tarball, it is too late to+-- check these on the server; these checks only make sense in the development+-- and package-creation environment.+-- This most likely means we need to use IO, but a dictionary+-- 'CheckPreDistributionOps m' is provided in case in the future such+-- information can come from somewhere else (e.g. VCS filesystem).+--+-- Note: this really shouldn't return any 'Inexcusable' warnings,+-- because that will make us say that Hackage would reject the package.+-- But, because Hackage doesn't yet run these tests, that will be a lie!++checkGlobFile+ :: Monad m+ => CabalSpecVersion+ -> FilePath -- Glob pattern.+ -> FilePath -- Folder to check.+ -> CabalField -- .cabal field we are checking.+ -> CheckM m ()+checkGlobFile cv ddir title fp = do+ let adjDdir = if null ddir then "." else ddir+ dir+ | title == "data-files" = adjDdir+ | otherwise = "."++ case parseFileGlob cv fp of+ -- We just skip over parse errors here; they're reported elsewhere.+ Left _ -> return ()+ Right parsedGlob -> do+ liftInt ciPreDistOps $ \po -> do+ rs <- runDirFileGlobM po dir parsedGlob+ return $ checkGlobResult title fp rs++-- | Checks for matchless globs and too strict matching (<2.4 spec).+checkGlobResult+ :: CabalField -- .cabal field we are checking+ -> FilePath -- Glob pattern (to show the user+ -- which pattern is the offending+ -- one).+ -> [GlobResult FilePath] -- List of glob results.+ -> [PackageCheck]+checkGlobResult title fp rs = dirCheck ++ catMaybes (map getWarning rs)+ where+ dirCheck+ | all (not . withoutNoMatchesWarning) rs =+ [PackageDistSuspiciousWarn $ GlobNoMatch title fp]+ | otherwise = []++ -- If there's a missing directory in play, since globs in Cabal packages+ -- don't (currently) support disjunction, that will always mean there are+ -- no matches. The no matches error in this case is strictly less+ -- informative than the missing directory error.+ withoutNoMatchesWarning (GlobMatch _) = True+ withoutNoMatchesWarning (GlobWarnMultiDot _) = False+ withoutNoMatchesWarning (GlobMissingDirectory _) = True+ withoutNoMatchesWarning (GlobMatchesDirectory _) = True++ getWarning :: GlobResult FilePath -> Maybe PackageCheck+ getWarning (GlobMatch _) = Nothing+ -- Before Cabal 2.4, the extensions of globs had to match the file+ -- exactly. This has been relaxed in 2.4 to allow matching only the+ -- suffix. This warning detects when pre-2.4 package descriptions+ -- are omitting files purely because of the stricter check.+ getWarning (GlobWarnMultiDot file) =+ Just $ PackageDistSuspiciousWarn (GlobExactMatch title fp file)+ getWarning (GlobMissingDirectory dir) =+ Just $ PackageDistSuspiciousWarn (GlobNoDir title fp dir)+ -- GlobMatchesDirectory is handled elsewhere if relevant;+ -- we can discard it here.+ getWarning (GlobMatchesDirectory _) = Nothing++-- ------------------------------------------------------------+-- Other exports+-- ------------------------------------------------------------++-- | Wraps `ParseWarning` into `PackageCheck`.+wrapParseWarning :: FilePath -> PWarning -> PackageCheck+wrapParseWarning fp pw = PackageDistSuspicious (ParseWarning fp pw)++-- TODO: as Jul 2022 there is no severity indication attached PWarnType.+-- Once that is added, we can output something more appropriate+-- than PackageDistSuspicious for every parse warning.+-- (see: Cabal-syntax/src/Distribution/Parsec/Warning.hs)++-- ------------------------------------------------------------+-- Ancillaries+-- ------------------------------------------------------------++-- Gets a list of dependencies from a Library target to pass to PVP related+-- functions. We are not doing checks here: this is not imprecise, as the+-- library itself *will* be checked for PVP errors.+-- Same for branch merging,+-- each of those branch will be checked one by one.+extractAssocDeps+ :: UnqualComponentName -- Name of the target library+ -> CondTree ConfVar [Dependency] Library+ -> AssocDep+extractAssocDeps n ct =+ let a = ignoreConditions ct+ in -- Merging is fine here, remember the specific+ -- library dependencies will be checked branch+ -- by branch.+ (n, snd a)++-- | August 2022: this function is an oddity due to the historical+-- GenericPackageDescription/PackageDescription split (check+-- Distribution.Types.PackageDescription for a description of the relationship+-- between GPD and PD.+-- It is only maintained not to break interface, should be deprecated in the+-- future in favour of `checkPackage` when PD and GPD are refactored sensibly.+pd2gpd :: PackageDescription -> GenericPackageDescription+pd2gpd pd = gpd+ where+ gpd :: GenericPackageDescription+ gpd =+ emptyGenericPackageDescription+ { packageDescription = pd+ , condLibrary = fmap t2c (library pd)+ , condSubLibraries = map (t2cName ln id) (subLibraries pd)+ , condForeignLibs =+ map+ (t2cName foreignLibName id)+ (foreignLibs pd)+ , condExecutables =+ map+ (t2cName exeName id)+ (executables pd)+ , condTestSuites =+ map+ (t2cName testName remTest)+ (testSuites pd)+ , condBenchmarks =+ map+ (t2cName benchmarkName remBench)+ (benchmarks pd)+ }++ -- From target to simple, unconditional CondTree.+ t2c :: a -> CondTree ConfVar [Dependency] a+ t2c a = CondNode a [] []++ -- From named target to unconditional CondTree. Notice we have+ -- a function to extract the name *and* a function to modify+ -- the target. This is needed for 'initTargetAnnotation' to work+ -- properly and to contain all the quirks inside 'pd2gpd'.+ t2cName+ :: (a -> UnqualComponentName)+ -> (a -> a)+ -> a+ -> (UnqualComponentName, CondTree ConfVar [Dependency] a)+ t2cName nf mf a = (nf a, t2c . mf $ a)++ ln :: Library -> UnqualComponentName+ ln wl = case libName wl of+ (LSubLibName u) -> u+ LMainLibName -> mkUnqualComponentName "main-library"++ remTest :: TestSuite -> TestSuite+ remTest t = t{testName = mempty}++ remBench :: Benchmark -> Benchmark+ remBench b = b{benchmarkName = mempty}++-- checkMissingDocs will check that we don’t have an interesting file+-- (changes.txt, Changelog.md, NEWS, etc.) in our work-tree which is not+-- present in our .cabal file.+checkMissingDocs+ :: Monad m+ => [Glob] -- data-files globs.+ -> [Glob] -- extra-source-files globs.+ -> [Glob] -- extra-doc-files globs.+ -> [Glob] -- extra-files globs.+ -> CheckM m ()+checkMissingDocs dgs esgs edgs efgs = do+ extraDocSupport <- (>= CabalSpecV1_18) <$> asksCM ccSpecVersion++ -- Everything in this block uses CheckPreDistributionOps interface.+ liftInt+ ciPreDistOps+ ( \ops -> do+ -- 1. Get root files, see if they are interesting to us.+ rootContents <- getDirectoryContentsM ops "."+ -- Recall getDirectoryContentsM arg is relative to root path.+ let des = filter isDesirableExtraDocFile rootContents++ -- 2. Realise Globs.+ let realGlob t =+ concatMap globMatches+ <$> mapM (runDirFileGlobM ops "") t+ rgs <- realGlob dgs+ res <- realGlob esgs+ red <- realGlob edgs+ ref <- realGlob efgs++ -- 3. Check if anything in 1. is missing in 2.+ let mcs = checkDoc extraDocSupport des (rgs ++ res ++ red ++ ref)++ -- 4. Check if files are present but in the wrong field.+ let pcsData = checkDocMove extraDocSupport "data-files" des rgs+ pcsSource =+ if extraDocSupport+ then+ checkDocMove+ extraDocSupport+ "extra-source-files"+ des+ res+ else []+ pcs = pcsData ++ pcsSource++ return (mcs ++ pcs)+ )+ where+ checkDoc+ :: Bool -- Cabal spec ≥ 1.18?+ -> [FilePath] -- Desirables.+ -> [FilePath] -- Actuals.+ -> [PackageCheck]+ checkDoc b ds as =+ let fds = map ("." </>) $ filter (flip notElem as) ds+ in if null fds+ then []+ else+ [ PackageDistSuspiciousWarn $+ MissingExpectedDocFiles b fds+ ]++ checkDocMove+ :: Bool -- Cabal spec ≥ 1.18?+ -> CabalField -- Name of the field.+ -> [FilePath] -- Desirables.+ -> [FilePath] -- Actuals.+ -> [PackageCheck]+ checkDocMove b field ds as =+ let fds = filter (flip elem as) ds+ in if null fds+ then []+ else+ [ PackageDistSuspiciousWarn $+ WrongFieldForExpectedDocFiles b field fds+ ]++-- Predicate for desirable documentation file on Hackage server.+isDesirableExtraDocFile :: FilePath -> Bool+isDesirableExtraDocFile path =+ basename `elem` desirableChangeLog+ && ext `elem` desirableChangeLogExtensions+ where+ (basename, ext) = splitExtension (map toLower path)++ -- Changelog patterns (basenames & extensions)+ -- Source: hackage-server/src/Distribution/Server/Packages/ChangeLog.hs+ desirableChangeLog = ["news", "changelog", "change_log", "changes"]+ desirableChangeLogExtensions = ["", ".txt", ".md", ".markdown", ".rst"]++-- [TODO] Check readme. Observations:+-- • Readme is not necessary if package description is good.+-- • Some readmes exists only for repository browsing.+-- • There is currently no reliable way to check what a good+-- description is; there will be complains if the criterion+-- is based on the length or number of words (can of worms).+-- -- Readme patterns+-- -- Source: hackage-server/src/Distribution/Server/Packages/Readme.hs+-- desirableReadme = ["readme"]++-- Remove duplicates from list.+dups :: Ord a => [a] -> [a]+dups xs = [x | (x : _ : _) <- group (sort xs)]
+ src/Distribution/PackageDescription/Check/Common.hs view
@@ -0,0 +1,148 @@+-- |+-- Module : Distribution.PackageDescription.Check.Common+-- Copyright : Francesco Ariis 2022+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Common types/functions to various check modules which are *no* part of+-- Distribution.PackageDescription.Check.Monad.+module Distribution.PackageDescription.Check.Common+ ( AssocDep+ , CabalField+ , PathKind (..)+ , checkCustomField+ , partitionDeps+ , checkPVP+ , checkPVPs+ , checkDependencyVersionRange+ ) where++import Distribution.Compat.Prelude+import Prelude ()++import Distribution.Compat.NonEmptySet (toNonEmpty)+import Distribution.Package+import Distribution.PackageDescription+import Distribution.PackageDescription.Check.Monad+import Distribution.Utils.Generic (isAscii)+import Distribution.Version++import Control.Monad++-- Type of FilePath.+data PathKind+ = PathKindFile+ | PathKindDirectory+ | PathKindGlob+ deriving (Eq)++-- | .cabal field we are referring to. As now it is just a synonym to help+-- reading the code, in the future it might take advantage of typification+-- in Cabal-syntax.+type CabalField = String++checkCustomField :: Monad m => (String, String) -> CheckM m ()+checkCustomField (n, _) =+ checkP+ (any (not . isAscii) n)+ (PackageDistInexcusable $ NonASCIICustomField [n])++-- ------------------------------------------------------------+-- PVP types/functions+-- ------------------------------------------------------------++-- A library name / dependencies association list. Ultimately to be+-- fed to PVP check.+type AssocDep = (UnqualComponentName, [Dependency])++-- Convenience function to partition important dependencies by name. To+-- be used together with checkPVP. Important: usually “base” or “Cabal”,+-- as the error is slightly different.+-- Note that `partitionDeps` will also filter out dependencies which are+-- already present in a inherithed fashion (e.g. an exe which imports the+-- main library will not need to specify upper bounds on shared dependencies,+-- hence we do not return those).+--+partitionDeps+ :: Monad m+ => [AssocDep] -- Possibly inherited dependencies, i.e.+ -- dependencies from internal/main libs.+ -> [UnqualComponentName] -- List of package names ("base", "Cabal"…)+ -> [Dependency] -- Dependencies to check.+ -> CheckM m ([Dependency], [Dependency])+partitionDeps ads ns ds = do+ -- Shared dependencies from “intra .cabal” libraries.+ let+ -- names of our dependencies+ dqs = map unqualName ds+ -- shared targets that match+ fads = filter (flip elem dqs . fst) ads+ -- the names of such targets+ inName = nub $ map fst fads :: [UnqualComponentName]+ -- the dependencies of such targets+ inDep = concatMap snd fads :: [Dependency]++ -- We exclude from checks:+ -- 1. dependencies which are shared with main library / a+ -- sublibrary; and of course+ -- 2. the names of main library / sub libraries themselves.+ --+ -- So in myPackage.cabal+ -- library+ -- build-depends: text < 5+ -- ⁝+ -- build-depends: myPackage, ← no warning, internal+ -- text, ← no warning, inherited+ -- monadacme ← warning!+ let fFun d =+ notElem (unqualName d) inName+ && notElem+ (unqualName d)+ (map unqualName inDep)+ ds' = filter fFun ds++ return $ partition (flip elem ns . unqualName) ds'+ where+ -- Return *sublibrary* name if exists (internal),+ -- otherwise package name.+ unqualName :: Dependency -> UnqualComponentName+ unqualName (Dependency n _ nel) =+ case head (toNonEmpty nel) of+ (LSubLibName ln) -> ln+ _ -> packageNameToUnqualComponentName n++-- PVP dependency check (one warning message per dependency, usually+-- for important dependencies like base).+checkPVP+ :: Monad m+ => (Dependency -> Bool)+ -> (String -> PackageCheck) -- Warn message depends on name+ -- (e.g. "base", "Cabal").+ -> [Dependency]+ -> CheckM m ()+checkPVP p ckf ds = do+ let ods = filter p ds+ mapM_ (tellP . ckf . unPackageName . depPkgName) ods++-- PVP dependency check for a list of dependencies. Some code duplication+-- is sadly needed to provide more ergonimic error messages.+checkPVPs+ :: Monad m+ => (Dependency -> Bool)+ -> ( [String]+ -> PackageCheck -- Grouped error message, depends on a+ -- set of names.+ )+ -> [Dependency] -- Deps to analyse.+ -> CheckM m ()+checkPVPs p cf ds+ | null ns = return ()+ | otherwise = tellP (cf ns)+ where+ ods = filter p ds+ ns = map (unPackageName . depPkgName) ods++checkDependencyVersionRange :: (VersionRange -> Bool) -> Dependency -> Bool+checkDependencyVersionRange p (Dependency _ ver _) = p ver
+ src/Distribution/PackageDescription/Check/Conditional.hs view
@@ -0,0 +1,265 @@+{-# LANGUAGE ScopedTypeVariables #-}++-- |+-- Module : Distribution.PackageDescription.Check.Conditional+-- Copyright : Lennart Kolmodin 2008, Francesco Ariis 2023+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Checks on conditional targets (libraries, executables, etc. that are+-- still inside a CondTree and related checks that can only be performed+-- here (variables, duplicated modules).+module Distribution.PackageDescription.Check.Conditional+ ( checkCondTarget+ , checkDuplicateModules+ ) where++import Distribution.Compat.Prelude+import Prelude ()++import Distribution.Compiler+import Distribution.ModuleName (ModuleName)+import Distribution.Package+import Distribution.PackageDescription+import Distribution.PackageDescription.Check.Monad+import Distribution.System++import qualified Data.Map as Map++import Control.Monad++-- As a prerequisite to some checks, we transform a target CondTree into+-- a CondTree of “target + useful context”.+-- This is slightly clearer, is easier to walk without resorting to+-- list comprehensions, allows us in the future to apply some sensible+-- “optimisations” to checks (exclusive branches, etc.).++-- | @nf@ function is needed to appropriately name some targets which need+-- to be spoonfed (otherwise name appears as "").+initTargetAnnotation+ :: Monoid a+ => (UnqualComponentName -> a -> a) -- Naming function for targets.+ -> UnqualComponentName+ -> TargetAnnotation a+initTargetAnnotation nf n = TargetAnnotation (nf n mempty) False++-- | We “build up” target from various slices.+updateTargetAnnotation+ :: Monoid a+ => a -- A target (lib, exe, test, …)+ -> TargetAnnotation a+ -> TargetAnnotation a+updateTargetAnnotation t ta = ta{taTarget = taTarget ta <> t}++-- | Before walking a target 'CondTree', we need to annotate it with+-- information relevant to the checks (read 'TaraAnn' and 'checkCondTarget'+-- doc for more info).+annotateCondTree+ :: forall a+ . (Eq a, Monoid a)+ => [PackageFlag] -- User flags.+ -> TargetAnnotation a+ -> CondTree ConfVar [Dependency] a+ -> CondTree ConfVar [Dependency] (TargetAnnotation a)+annotateCondTree fs ta (CondNode a c bs) =+ let ta' = updateTargetAnnotation a ta+ bs' = map (annotateBranch ta') bs+ bs'' = crossAnnotateBranches defTrueFlags bs'+ in CondNode ta' c bs''+ where+ annotateBranch+ :: TargetAnnotation a+ -> CondBranch ConfVar [Dependency] a+ -> CondBranch+ ConfVar+ [Dependency]+ (TargetAnnotation a)+ annotateBranch wta (CondBranch k t mf) =+ let uf = isPkgFlagCond k+ wta' = wta{taPackageFlag = taPackageFlag wta || uf}+ atf = annotateCondTree fs+ in CondBranch+ k+ (atf wta' t)+ (atf wta <$> mf)+ -- Note how we are passing the *old* wta+ -- in the `else` branch, since we are not+ -- under that flag.++ -- We only want to pick up variables that are flags and that are+ -- \*off* by default.+ isPkgFlagCond :: Condition ConfVar -> Bool+ isPkgFlagCond (Lit _) = False+ isPkgFlagCond (Var (PackageFlag f)) = elem f defOffFlags+ isPkgFlagCond (Var _) = False+ isPkgFlagCond (CNot cn) = not (isPkgFlagCond cn)+ isPkgFlagCond (CAnd ca cb) = isPkgFlagCond ca || isPkgFlagCond cb+ isPkgFlagCond (COr ca cb) = isPkgFlagCond ca && isPkgFlagCond cb++ -- Package flags that are off by default *and* that are manual.+ defOffFlags =+ map flagName $+ filter+ ( \f ->+ not (flagDefault f)+ && flagManual f+ )+ fs++ defTrueFlags :: [PackageFlag]+ defTrueFlags = filter flagDefault fs++-- Propagate contextual information in CondTree branches. This is+-- needed as CondTree is a rosetree and not a binary tree.+crossAnnotateBranches+ :: forall a+ . (Eq a, Monoid a)+ => [PackageFlag] -- `default: true` flags.+ -> [CondBranch ConfVar [Dependency] (TargetAnnotation a)]+ -> [CondBranch ConfVar [Dependency] (TargetAnnotation a)]+crossAnnotateBranches fs bs = map crossAnnBranch bs+ where+ crossAnnBranch+ :: CondBranch ConfVar [Dependency] (TargetAnnotation a)+ -> CondBranch ConfVar [Dependency] (TargetAnnotation a)+ crossAnnBranch wr =+ let+ rs = filter (/= wr) bs+ ts = mapMaybe realiseBranch rs+ in+ updateTargetAnnBranch (mconcat ts) wr++ realiseBranch :: CondBranch ConfVar [Dependency] (TargetAnnotation a) -> Maybe a+ realiseBranch b =+ let+ -- We are only interested in True by default package flags.+ realiseBranchFunction :: ConfVar -> Either ConfVar Bool+ realiseBranchFunction (PackageFlag n) | elem n (map flagName fs) = Right True+ realiseBranchFunction _ = Right False+ ms = simplifyCondBranch realiseBranchFunction (fmap taTarget b)+ in+ fmap snd ms++ updateTargetAnnBranch+ :: a+ -> CondBranch ConfVar [Dependency] (TargetAnnotation a)+ -> CondBranch ConfVar [Dependency] (TargetAnnotation a)+ updateTargetAnnBranch a (CondBranch k t mt) =+ let updateTargetAnnTree (CondNode ka c wbs) =+ (CondNode (updateTargetAnnotation a ka) c wbs)+ in CondBranch k (updateTargetAnnTree t) (updateTargetAnnTree <$> mt)++-- | A conditional target is a library, exe, benchmark etc., destructured+-- in a CondTree. Traversing method: we render the branches, pass a+-- relevant context, collect checks.+checkCondTarget+ :: forall m a+ . (Monad m, Eq a, Monoid a)+ => [PackageFlag] -- User flags.+ -> (a -> CheckM m ()) -- Check function (a = target).+ -> (UnqualComponentName -> a -> a)+ -- Naming function (some targets+ -- need to have their name+ -- spoonfed to them.+ -> (UnqualComponentName, CondTree ConfVar [Dependency] a)+ -- Target name/condtree.+ -> CheckM m ()+checkCondTarget fs cf nf (unqualName, ct) =+ wTree $ annotateCondTree fs (initTargetAnnotation nf unqualName) ct+ where+ -- Walking the tree. Remember that CondTree is not a binary+ -- tree but a /rose/tree.+ wTree+ :: CondTree ConfVar [Dependency] (TargetAnnotation a)+ -> CheckM m ()+ wTree (CondNode ta _ bs)+ -- There are no branches ([] == True) *or* every branch+ -- is “simple” (i.e. missing a 'condBranchIfFalse' part).+ -- This is convenient but not necessarily correct in all+ -- cases; a more precise way would be to check incompatibility+ -- among simple branches conditions (or introduce a principled+ -- `cond` construct in `.cabal` files.+ | all isSimple bs = do+ localCM (initCheckCtx ta) (cf $ taTarget ta)+ mapM_ wBranch bs+ -- If there are T/F conditions, there is no need to check+ -- the intermediate 'TargetAnnotation' too.+ | otherwise = do+ mapM_ wBranch bs++ isSimple+ :: CondBranch ConfVar [Dependency] (TargetAnnotation a)+ -> Bool+ isSimple (CondBranch _ _ Nothing) = True+ isSimple (CondBranch _ _ (Just _)) = False++ wBranch+ :: CondBranch ConfVar [Dependency] (TargetAnnotation a)+ -> CheckM m ()+ wBranch (CondBranch k t mf) = do+ checkCondVars k+ wTree t+ maybe (return ()) wTree mf++-- | Condvar checking (misspelled OS in if conditions, etc).+checkCondVars :: Monad m => Condition ConfVar -> CheckM m ()+checkCondVars cond =+ let (_, vs) = simplifyCondition cond (\v -> Left v)+ in -- Using simplifyCondition is convenient and correct,+ -- if checks become more complex we can always walk+ -- 'Condition'.+ mapM_ vcheck vs+ where+ vcheck :: Monad m => ConfVar -> CheckM m ()+ vcheck (OS (OtherOS os)) =+ tellP (PackageDistInexcusable $ UnknownOS [os])+ vcheck (Arch (OtherArch arch)) =+ tellP (PackageDistInexcusable $ UnknownArch [arch])+ vcheck (Impl (OtherCompiler os) _) =+ tellP (PackageDistInexcusable $ UnknownCompiler [os])+ vcheck _ = return ()++-- Checking duplicated modules cannot unfortunately be done in the+-- “tree checking”. This is because of the monoidal instance in some targets,+-- where e.g. merged dependencies are `nub`’d, hence losing information for+-- this particular check.+checkDuplicateModules :: GenericPackageDescription -> [PackageCheck]+checkDuplicateModules pkg =+ concatMap checkLib (maybe id (:) (condLibrary pkg) . map snd $ condSubLibraries pkg)+ ++ concatMap checkExe (map snd $ condExecutables pkg)+ ++ concatMap checkTest (map snd $ condTestSuites pkg)+ ++ concatMap checkBench (map snd $ condBenchmarks pkg)+ where+ -- the duplicate modules check is has not been thoroughly vetted for backpack+ checkLib = checkDups "library" (\l -> explicitLibModules l ++ map moduleReexportName (reexportedModules l))+ checkExe = checkDups "executable" exeModules+ checkTest = checkDups "test suite" testModules+ checkBench = checkDups "benchmark" benchmarkModules+ checkDups :: String -> (a -> [ModuleName]) -> CondTree v c a -> [PackageCheck]+ checkDups s getModules t =+ let sumPair (x, x') (y, y') = (x + x' :: Int, y + y' :: Int)+ mergePair (x, x') (y, y') = (x + x', max y y')+ maxPair (x, x') (y, y') = (max x x', max y y')+ libMap =+ foldCondTree+ Map.empty+ (\(_, v) -> Map.fromListWith sumPair . map (\x -> (x, (1, 1))) $ getModules v)+ (Map.unionWith mergePair) -- if a module may occur in nonexclusive branches count it twice strictly and once loosely.+ (Map.unionWith maxPair) -- a module occurs the max of times it might appear in exclusive branches+ t+ dupLibsStrict = Map.keys $ Map.filter ((> 1) . fst) libMap+ dupLibsLax = Map.keys $ Map.filter ((> 1) . snd) libMap+ in if not (null dupLibsLax)+ then+ [ PackageBuildImpossible+ (DuplicateModule s dupLibsLax)+ ]+ else+ if not (null dupLibsStrict)+ then+ [ PackageDistSuspicious+ (PotentialDupModule s dupLibsStrict)+ ]+ else []
+ src/Distribution/PackageDescription/Check/Monad.hs view
@@ -0,0 +1,371 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- |+-- Module : Distribution.PackageDescription.Check.Monad+-- Copyright : Francesco Ariis 2022+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Primitives for package checking: check types and monadic interface.+-- Having these primitives in a different module allows us to appropriately+-- limit/manage the interface to suit checking needs.+module Distribution.PackageDescription.Check.Monad+ ( -- * Types and constructors+ CheckM (..)+ , execCheckM+ , CheckInterface (..)+ , CheckPackageContentOps (..)+ , CheckPreDistributionOps (..)+ , TargetAnnotation (..)+ , PackageCheck (..)+ , CheckExplanation (..)+ , CEType (..)+ , WarnLang (..)+ , CheckCtx (..)+ , pristineCheckCtx+ , initCheckCtx+ , PNames (..)++ -- * Operations+ , ppPackageCheck+ , isHackageDistError+ , asksCM+ , localCM+ , checkP+ , checkPkg+ , liftInt+ , tellP+ , checkSpecVer+ ) where++import Distribution.Compat.Prelude+import Prelude ()++import Distribution.CabalSpecVersion (CabalSpecVersion)+import Distribution.Package (packageName)+import Distribution.PackageDescription.Check.Warning+import Distribution.Simple.BuildToolDepends (desugarBuildToolSimple)+import Distribution.Simple.Glob (Glob, GlobResult)+import Distribution.Types.ExeDependency (ExeDependency)+import Distribution.Types.GenericPackageDescription+import Distribution.Types.LegacyExeDependency (LegacyExeDependency)+import Distribution.Types.PackageDescription (package, specVersion)+import Distribution.Types.PackageId (PackageIdentifier)+import Distribution.Types.UnqualComponentName++import qualified Control.Monad.Reader as Reader+import qualified Control.Monad.Trans.Class as Trans+import qualified Control.Monad.Writer as Writer+import qualified Data.ByteString.Lazy as BS+import qualified Data.Set as Set++import Control.Monad++-- Monadic interface for for Distribution.PackageDescription.Check.+--+-- Monadic checking allows us to have a fine grained control on checks+-- (e.g. omitting warning checks in certain situations).++-- * Interfaces++--++-- | Which interface to we have available/should we use? (to perform: pure+-- checks, package checks, pre-distribution checks.)+data CheckInterface m = CheckInterface+ { ciPureChecks :: Bool+ , -- Perform pure checks?+ ciPackageOps :: Maybe (CheckPackageContentOps m)+ , -- If you want to perform package contents+ -- checks, provide an interface.+ ciPreDistOps :: Maybe (CheckPreDistributionOps m)+ -- If you want to work-tree checks, provide+ -- an interface.+ }++-- | A record of operations needed to check the contents of packages.+-- Abstracted over `m` to provide flexibility (could be IO, a .tar.gz+-- file, etc).+data CheckPackageContentOps m = CheckPackageContentOps+ { doesFileExist :: FilePath -> m Bool+ , doesDirectoryExist :: FilePath -> m Bool+ , getDirectoryContents :: FilePath -> m [FilePath]+ , getFileContents :: FilePath -> m BS.ByteString+ }++-- | A record of operations needed to check contents *of the work tree*+-- (compare it with 'CheckPackageContentOps'). This is still `m` abstracted+-- in case in the future we can obtain the same infos other than from IO+-- (e.g. a VCS work tree).+data CheckPreDistributionOps m = CheckPreDistributionOps+ { runDirFileGlobM :: FilePath -> Glob -> m [GlobResult FilePath]+ , getDirectoryContentsM :: FilePath -> m [FilePath]+ }++-- | Context to perform checks (will be the Reader part in your monad).+data CheckCtx m = CheckCtx+ { ccInterface :: CheckInterface m+ , -- Interface for checks.++ -- Contextual infos for checks.+ ccFlag :: Bool+ , -- Are we under a user flag?++ -- Convenience bits that we prefer to carry+ -- in our Reader monad instead of passing it+ -- via ->, as they are often useful and often+ -- in deeply nested places in the GPD tree.+ ccSpecVersion :: CabalSpecVersion+ , -- Cabal version.+ ccDesugar :: LegacyExeDependency -> Maybe ExeDependency+ , -- A desugaring function from+ -- Distribution.Simple.BuildToolDepends+ -- (desugarBuildToolSimple). Again since it+ -- eats PackageName and a list of executable+ -- names, it is more convenient to pass it+ -- via Reader.+ ccNames :: PNames+ -- Various names (id, libs, execs, tests,+ -- benchs), convenience.+ }++-- | Creates a pristing 'CheckCtx'. With pristine we mean everything that+-- can be deduced by GPD but *not* user flags information.+pristineCheckCtx+ :: Monad m+ => CheckInterface m+ -> GenericPackageDescription+ -> CheckCtx m+pristineCheckCtx ci gpd =+ let ens = map fst (condExecutables gpd)+ in CheckCtx+ ci+ False+ (specVersion . packageDescription $ gpd)+ (desugarBuildToolSimple (packageName gpd) ens)+ (initPNames gpd)++-- | Adds useful bits to 'CheckCtx' (as now, whether we are operating under+-- a user off-by-default flag).+initCheckCtx :: Monad m => TargetAnnotation a -> CheckCtx m -> CheckCtx m+initCheckCtx t c = c{ccFlag = taPackageFlag t}++-- | 'TargetAnnotation' collects contextual information on the target we are+-- realising: a buildup of the various slices of the target (a library,+-- executable, etc. — is a monoid) whether we are under an off-by-default+-- package flag.+data TargetAnnotation a = TargetAnnotation+ { taTarget :: a+ , -- The target we are building (lib, exe, etc.)+ taPackageFlag :: Bool+ -- Whether we are under an off-by-default package flag.+ }+ deriving (Show, Eq, Ord)++-- | A collection os names, shipping tuples around is annoying.+data PNames = PNames+ { pnPackageId :: PackageIdentifier -- Package ID…+ -- … and a bunch of lib, exe, test, bench names.+ , pnSubLibs :: [UnqualComponentName]+ , pnExecs :: [UnqualComponentName]+ , pnTests :: [UnqualComponentName]+ , pnBenchs :: [UnqualComponentName]+ }++-- | Init names from a GPD.+initPNames :: GenericPackageDescription -> PNames+initPNames gpd =+ PNames+ (package . packageDescription $ gpd)+ (map fst $ condSubLibraries gpd)+ (map fst $ condExecutables gpd)+ (map fst $ condTestSuites gpd)+ (map fst $ condBenchmarks gpd)++-- | Check monad, carrying a context, collecting 'PackageCheck's.+-- Using Set for writer (automatic sort) is useful for output stability+-- on different platforms.+-- It is nothing more than a monad stack with Reader+Writer.+-- `m` is the monad that could be used to do package/file checks.+newtype CheckM m a+ = CheckM+ ( Reader.ReaderT+ (CheckCtx m)+ ( Writer.WriterT+ (Set.Set PackageCheck)+ m+ )+ a+ )+ deriving (Functor, Applicative, Monad)++-- Not autoderiving MonadReader and MonadWriter gives us better+-- control on the interface of CheckM.++-- | Execute a CheckM monad, leaving `m [PackageCheck]` which can be+-- run in the appropriate `m` environment (IO, pure, …).+execCheckM :: Monad m => CheckM m () -> CheckCtx m -> m [PackageCheck]+execCheckM (CheckM rwm) ctx =+ let wm = Reader.runReaderT rwm ctx+ m = Writer.execWriterT wm+ in Set.toList <$> m++-- | As 'checkP' but always succeeding.+tellP :: Monad m => PackageCheck -> CheckM m ()+tellP = checkP True++-- | Add a package warning withoutu performing any check.+tellCM :: Monad m => PackageCheck -> CheckM m ()+tellCM ck = do+ cf <- asksCM ccFlag+ unless+ (cf && canSkip ck)+ -- Do not push this message if the warning is not severe *and*+ -- we are under a non-default package flag.+ (CheckM . Writer.tell $ Set.singleton ck)+ where+ -- Check if we can skip this error if we are under a+ -- non-default user flag.+ canSkip :: PackageCheck -> Bool+ canSkip wck = not (isSevereLocal wck) || isErrAllowable wck++ isSevereLocal :: PackageCheck -> Bool+ isSevereLocal (PackageBuildImpossible _) = True+ isSevereLocal (PackageBuildWarning _) = True+ isSevereLocal (PackageDistSuspicious _) = False+ isSevereLocal (PackageDistSuspiciousWarn _) = False+ isSevereLocal (PackageDistInexcusable _) = True++ -- There are some errors which, even though severe, will+ -- be allowed by Hackage *if* under a non-default flag.+ isErrAllowable :: PackageCheck -> Bool+ isErrAllowable c = case extractCheckExplanation c of+ (WErrorUnneeded _) -> True+ (JUnneeded _) -> True+ (FDeferTypeErrorsUnneeded _) -> True+ (DynamicUnneeded _) -> True+ (ProfilingUnneeded _) -> True+ _ -> False++-- | Lift a monadic computation to CM.+liftCM :: Monad m => m a -> CheckM m a+liftCM ma = CheckM . Trans.lift . Trans.lift $ ma++-- | Lift a monadic action via an interface. Missing interface, no action.+liftInt+ :: forall m i+ . Monad m+ => (CheckInterface m -> Maybe (i m))+ -- Check interface, may or may not exist. If it does not,+ -- the check simply will not be performed.+ -> (i m -> m [PackageCheck])+ -- The actual check to perform with the above-mentioned+ -- interface. Note the [] around `PackageCheck`, this is+ -- meant to perform/collect multiple checks.+ -> CheckM m ()+liftInt acc f = do+ ops <- asksCM (acc . ccInterface)+ maybe (return ()) l ops+ where+ l :: i m -> CheckM m ()+ l wi = do+ cks <- liftCM (f wi)+ mapM_ (check True) cks++-- | Most basic check function. You do not want to export this, rather export+-- “smart” functions (checkP, checkPkg) to enforce relevant properties.+check+ :: Monad m+ => Bool -- Is there something to warn about?+ -> PackageCheck -- Warn message.+ -> CheckM m ()+check True ck = tellCM ck+check False _ = return ()++-- | Pure check not requiring IO or other interfaces.+checkP+ :: Monad m+ => Bool -- Is there something to warn about?+ -> PackageCheck -- Warn message.+ -> CheckM m ()+checkP b ck = do+ pb <- asksCM (ciPureChecks . ccInterface)+ when pb (check b ck)++-- Check with 'CheckPackageContentOps' operations (i.e. package file checks).+--+checkPkg+ :: forall m+ . Monad m+ => (CheckPackageContentOps m -> m Bool)+ -- Actual check to perform with CPC interface+ -> PackageCheck+ -- Warn message.+ -> CheckM m ()+checkPkg f ck = checkInt ciPackageOps f ck++-- | Generalised version for checks that need an interface. We pass a Reader+-- accessor to such interface ‘i’, a check function.+checkIntDep+ :: forall m i+ . Monad m+ => (CheckInterface m -> Maybe (i m))+ -- Check interface, may or may not exist. If it does not,+ -- the check simply will not be performed.+ -> (i m -> m (Maybe PackageCheck))+ -- The actual check to perform (single check).+ -> CheckM m ()+checkIntDep acc mck = do+ po <- asksCM (acc . ccInterface)+ maybe (return ()) (lc . mck) po+ where+ lc :: Monad m => m (Maybe PackageCheck) -> CheckM m ()+ lc wmck = do+ b <- liftCM wmck+ maybe (return ()) (check True) b++-- | As 'checkIntDep', but 'PackageCheck' does not depend on the monadic+-- computation.+checkInt+ :: forall m i+ . Monad m+ => (CheckInterface m -> Maybe (i m))+ -- Where to get the interface (if available).+ -> (i m -> m Bool)+ -- Condition to check+ -> PackageCheck+ -- Warning message to add (does not depend on `m`).+ -> CheckM m ()+checkInt acc f ck =+ checkIntDep+ acc+ ( \ops -> do+ b <- f ops+ if b+ then return $ Just ck+ else return Nothing+ )++-- | `local` (from Control.Monad.Reader) for CheckM.+localCM :: Monad m => (CheckCtx m -> CheckCtx m) -> CheckM m () -> CheckM m ()+localCM cf (CheckM im) = CheckM $ Reader.local cf im++-- | `ask` (from Control.Monad.Reader) for CheckM.+asksCM :: Monad m => (CheckCtx m -> a) -> CheckM m a+asksCM f = CheckM $ Reader.asks f++-- As checkP, but with an additional condition: the check will be performed+-- only if our spec version is < `vc`.+checkSpecVer+ :: Monad m+ => CabalSpecVersion -- Perform this check only if our+ -- spec version is < than this.+ -> Bool -- Check condition.+ -> PackageCheck -- Check message.+ -> CheckM m ()+checkSpecVer vc cond c = do+ vp <- asksCM ccSpecVersion+ unless (vp >= vc) (checkP cond c)
+ src/Distribution/PackageDescription/Check/Paths.hs view
@@ -0,0 +1,418 @@+-- |+-- Module : Distribution.PackageDescription.Check.Paths+-- Copyright : Lennart Kolmodin 2008, Francesco Ariis 2023+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Functions to check filepaths, directories, globs, etc.+module Distribution.PackageDescription.Check.Paths+ ( checkGlob+ , checkPath+ , checkPackageFileNamesWithGlob+ , fileExtensionSupportedLanguage+ , isGoodRelativeDirectoryPath+ , isGoodRelativeFilePath+ , isGoodRelativeGlob+ , isInsideDist+ ) where++import Distribution.Compat.Prelude+import Prelude ()++import Distribution.PackageDescription.Check.Common+import Distribution.PackageDescription.Check.Monad+import Distribution.Simple.CCompiler+import Distribution.Simple.Glob+ ( Glob+ , explainGlobSyntaxError+ , isRecursiveInRoot+ , parseFileGlob+ )+import Distribution.Simple.Utils hiding (findPackageDesc, notice)+import System.FilePath (splitDirectories, splitPath, takeExtension)++import qualified System.FilePath.Windows as FilePath.Windows (isValid)++fileExtensionSupportedLanguage :: FilePath -> Bool+fileExtensionSupportedLanguage path =+ isHaskell || isC+ where+ extension = takeExtension path+ isHaskell = extension `elem` [".hs", ".lhs"]+ isC = isJust (filenameCDialect extension)++-- Boolean: are absolute paths allowed?+checkPath+ :: Monad m+ => Bool -- Can be absolute path?+ -> CabalField -- .cabal field that we are checking.+ -> PathKind -- Path type.+ -> FilePath -- Path.+ -> CheckM m ()+checkPath isAbs title kind path = do+ checkP+ (isOutsideTree path)+ (PackageBuildWarning $ RelativeOutside title path)+ checkP+ (isInsideDist path)+ (PackageDistInexcusable $ DistPoint (Just title) path)+ checkPackageFileNamesWithGlob kind path++ -- Skip if "can be absolute path".+ checkP+ (not isAbs && isAbsoluteOnAnyPlatform path)+ (PackageDistInexcusable $ AbsolutePath title path)+ case grl path kind of+ Just e ->+ checkP+ (not isAbs)+ (PackageDistInexcusable $ BadRelativePath title path e)+ Nothing -> return ()+ checkWindowsPath (kind == PathKindGlob) path+ where+ isOutsideTree wpath = case splitDirectories wpath of+ ".." : _ -> True+ "." : ".." : _ -> True+ _ -> False++ -- These are not paths, but globs...+ grl wfp PathKindFile = isGoodRelativeFilePath wfp+ grl wfp PathKindGlob = isGoodRelativeGlob wfp+ grl wfp PathKindDirectory = isGoodRelativeDirectoryPath wfp++-- | Is a 'FilePath' inside `dist`, `dist-newstyle` and friends?+isInsideDist :: FilePath -> Bool+isInsideDist path =+ case map lowercase (splitDirectories path) of+ "dist" : _ -> True+ "." : "dist" : _ -> True+ "dist-newstyle" : _ -> True+ "." : "dist-newstyle" : _ -> True+ _ -> False++checkPackageFileNamesWithGlob+ :: Monad m+ => PathKind+ -> FilePath -- Filepath or possibly a glob pattern.+ -> CheckM m ()+checkPackageFileNamesWithGlob kind fp = do+ checkWindowsPath (kind == PathKindGlob) fp+ checkTarPath fp++checkWindowsPath+ :: Monad m+ => Bool -- Is it a glob pattern?+ -> FilePath -- Path.+ -> CheckM m ()+checkWindowsPath isGlob path =+ checkP+ (not . FilePath.Windows.isValid $ escape isGlob path)+ (PackageDistInexcusable $ InvalidOnWin [path])+ where+ -- Force a relative name to catch invalid file names like "f:oo" which+ -- otherwise parse as file "oo" in the current directory on the 'f' drive.+ escape :: Bool -> String -> String+ escape wisGlob wpath =+ (".\\" ++)+ -- Glob paths will be expanded before being dereferenced, so asterisks+ -- shouldn't count against them.+ $+ map (\c -> if c == '*' && wisGlob then 'x' else c) wpath++-- | Check a file name is valid for the portable POSIX tar format.+--+-- The POSIX tar format has a restriction on the length of file names. It is+-- unfortunately not a simple restriction like a maximum length. The exact+-- restriction is that either the whole path be 100 characters or less, or it+-- be possible to split the path on a directory separator such that the first+-- part is 155 characters or less and the second part 100 characters or less.+checkTarPath :: Monad m => FilePath -> CheckM m ()+checkTarPath path+ | length path > 255 = tellP longPath+ | otherwise = case pack nameMax (reverse (splitPath path)) of+ Left err -> tellP err+ Right [] -> return ()+ Right (h : rest) -> case pack prefixMax remainder of+ Left err -> tellP err+ Right [] -> return ()+ Right (_ : _) -> tellP noSplit+ where+ -- drop the '/' between the name and prefix:+ remainder = safeInit h : rest+ where+ nameMax, prefixMax :: Int+ nameMax = 100+ prefixMax = 155++ pack _ [] = Left emptyName+ pack maxLen (c : cs)+ | n > maxLen = Left longName+ | otherwise = Right (pack' maxLen n cs)+ where+ n = length c++ pack' maxLen n (c : cs)+ | n' <= maxLen = pack' maxLen n' cs+ where+ n' = n + length c+ pack' _ _ cs = cs++ longPath = PackageDistInexcusable (FilePathTooLong path)+ longName = PackageDistInexcusable (FilePathNameTooLong path)+ noSplit = PackageDistInexcusable (FilePathSplitTooLong path)+ emptyName = PackageDistInexcusable FilePathEmpty++-- `checkGlob` checks glob patterns and returns good ones for further+-- processing.+checkGlob+ :: Monad m+ => CabalField -- .cabal field we are checking.+ -> FilePath -- glob filepath pattern+ -> CheckM m (Maybe Glob)+checkGlob title pat = do+ ver <- asksCM ccSpecVersion++ -- Glob sanity check.+ case parseFileGlob ver pat of+ Left e -> do+ tellP+ ( PackageDistInexcusable $+ GlobSyntaxError title (explainGlobSyntaxError pat e)+ )+ return Nothing+ Right wglob -> do+ -- \* Miscellaneous checks on sane glob.+ -- Checks for recursive glob in root.+ checkP+ (isRecursiveInRoot wglob)+ ( PackageDistSuspiciousWarn $+ RecursiveGlobInRoot title pat+ )+ return (Just wglob)++-- | Whether a path is a good relative path. We aren't worried about perfect+-- cross-platform compatibility here; this function just checks the paths in+-- the (local) @.cabal@ file, while only Hackage needs the portability.+--+-- >>> let test fp = putStrLn $ show (isGoodRelativeDirectoryPath fp) ++ "; " ++ show (isGoodRelativeFilePath fp)+--+-- Note that "foo./bar.hs" would be invalid on Windows.+--+-- >>> traverse_ test ["foo/bar/quu", "a/b.hs", "foo./bar.hs"]+-- Nothing; Nothing+-- Nothing; Nothing+-- Nothing; Nothing+--+-- Trailing slash is not allowed for files, for directories it is ok.+--+-- >>> test "foo/"+-- Nothing; Just "trailing slash"+--+-- Leading @./@ is fine, but @.@ and @./@ are not valid files.+--+-- >>> traverse_ test [".", "./", "./foo/bar"]+-- Nothing; Just "trailing dot segment"+-- Nothing; Just "trailing slash"+-- Nothing; Nothing+--+-- Lastly, not good file nor directory cases:+--+-- >>> traverse_ test ["", "/tmp/src", "foo//bar", "foo/.", "foo/./bar", "foo/../bar"]+-- Just "empty path"; Just "empty path"+-- Just "posix absolute path"; Just "posix absolute path"+-- Just "empty path segment"; Just "empty path segment"+-- Just "trailing same directory segment: ."; Just "trailing same directory segment: ."+-- Just "same directory segment: ."; Just "same directory segment: ."+-- Just "parent directory segment: .."; Just "parent directory segment: .."+--+-- For the last case, 'isGoodRelativeGlob' doesn't warn:+--+-- >>> traverse_ (print . isGoodRelativeGlob) ["foo/../bar"]+-- Just "parent directory segment: .."+isGoodRelativeFilePath :: FilePath -> Maybe String+isGoodRelativeFilePath = state0+ where+ -- initial state+ state0 [] = Just "empty path"+ state0 (c : cs)+ | c == '.' = state1 cs+ | c == '/' = Just "posix absolute path"+ | otherwise = state5 cs++ -- after initial .+ state1 [] = Just "trailing dot segment"+ state1 (c : cs)+ | c == '.' = state4 cs+ | c == '/' = state2 cs+ | otherwise = state5 cs++ -- after ./ or after / between segments+ state2 [] = Just "trailing slash"+ state2 (c : cs)+ | c == '.' = state3 cs+ | c == '/' = Just "empty path segment"+ | otherwise = state5 cs++ -- after non-first segment's .+ state3 [] = Just "trailing same directory segment: ."+ state3 (c : cs)+ | c == '.' = state4 cs+ | c == '/' = Just "same directory segment: ."+ | otherwise = state5 cs++ -- after ..+ state4 [] = Just "trailing parent directory segment: .."+ state4 (c : cs)+ | c == '.' = state5 cs+ | c == '/' = Just "parent directory segment: .."+ | otherwise = state5 cs++ -- in a segment which is ok.+ state5 [] = Nothing+ state5 (c : cs)+ | c == '.' = state5 cs+ | c == '/' = state2 cs+ | otherwise = state5 cs++-- | See 'isGoodRelativeFilePath'.+--+-- This is barebones function. We check whether the glob is a valid file+-- by replacing stars @*@ with @x@ses.+isGoodRelativeGlob :: FilePath -> Maybe String+isGoodRelativeGlob = isGoodRelativeFilePath . map f+ where+ f '*' = 'x'+ f c = c++-- | See 'isGoodRelativeFilePath'.+isGoodRelativeDirectoryPath :: FilePath -> Maybe String+isGoodRelativeDirectoryPath = state0+ where+ -- initial state+ state0 [] = Just "empty path"+ state0 (c : cs)+ | c == '.' = state5 cs+ | c == '/' = Just "posix absolute path"+ | otherwise = state4 cs++ -- after initial ./ or after / between segments+ state1 [] = Nothing+ state1 (c : cs)+ | c == '.' = state2 cs+ | c == '/' = Just "empty path segment"+ | otherwise = state4 cs++ -- after non-first setgment's .+ state2 [] = Just "trailing same directory segment: ."+ state2 (c : cs)+ | c == '.' = state3 cs+ | c == '/' = Just "same directory segment: ."+ | otherwise = state4 cs++ -- after ..+ state3 [] = Just "trailing parent directory segment: .."+ state3 (c : cs)+ | c == '.' = state4 cs+ | c == '/' = Just "parent directory segment: .."+ | otherwise = state4 cs++ -- in a segment which is ok.+ state4 [] = Nothing+ state4 (c : cs)+ | c == '.' = state4 cs+ | c == '/' = state1 cs+ | otherwise = state4 cs++ -- after initial .+ state5 [] = Nothing -- "."+ state5 (c : cs)+ | c == '.' = state3 cs+ | c == '/' = state1 cs+ | otherwise = state4 cs++-- [Note: Good relative paths]+--+-- Using @kleene@ we can define an extended regex:+--+-- @+-- import Algebra.Lattice+-- import Kleene+-- import Kleene.ERE (ERE (..), intersections)+--+-- data C = CDot | CSlash | CChar+-- deriving (Eq, Ord, Enum, Bounded, Show)+--+-- reservedR :: ERE C+-- reservedR = notChar CSlash+--+-- pathPieceR :: ERE C+-- pathPieceR = intersections+-- [ plus reservedR+-- , ERENot (string [CDot])+-- , ERENot (string [CDot,CDot])+-- ]+--+-- filePathR :: ERE C+-- filePathR = optional (string [CDot, CSlash]) <> pathPieceR <> star (char CSlash <> pathPieceR)+--+-- dirPathR :: ERE C+-- dirPathR = (char CDot \/ filePathR) <> optional (char CSlash)+--+-- plus :: ERE C -> ERE C+-- plus r = r <> star r+--+-- optional :: ERE C -> ERE C+-- optional r = mempty \/ r+-- @+--+-- Results in following state machine for @filePathR@+--+-- @+-- 0 -> \x -> if+-- | x <= CDot -> 1+-- | otherwise -> 5+-- 1 -> \x -> if+-- | x <= CDot -> 4+-- | x <= CSlash -> 2+-- | otherwise -> 5+-- 2 -> \x -> if+-- | x <= CDot -> 3+-- | otherwise -> 5+-- 3 -> \x -> if+-- | x <= CDot -> 4+-- | otherwise -> 5+-- 4 -> \x -> if+-- | x <= CDot -> 5+-- | otherwise -> 5+-- 5+ -> \x -> if+-- | x <= CDot -> 5+-- | x <= CSlash -> 2+-- | otherwise -> 5+-- @+--+-- and @dirPathR@:+--+-- @+-- 0 -> \x -> if+-- | x <= CDot -> 5+-- | otherwise -> 4+-- 1+ -> \x -> if+-- | x <= CDot -> 2+-- | otherwise -> 4+-- 2 -> \x -> if+-- | x <= CDot -> 3+-- | otherwise -> 4+-- 3 -> \x -> if+-- | x <= CDot -> 4+-- | otherwise -> 4+-- 4+ -> \x -> if+-- | x <= CDot -> 4+-- | x <= CSlash -> 1+-- | otherwise -> 4+-- 5+ -> \x -> if+-- | x <= CDot -> 3+-- | x <= CSlash -> 1+-- | otherwise -> 4+-- @
+ src/Distribution/PackageDescription/Check/Target.hs view
@@ -0,0 +1,1119 @@+-- |+-- Module : Distribution.PackageDescription.Check.Target+-- Copyright : Lennart Kolmodin 2008, Francesco Ariis 2023+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Fully-realised target (library, executable, …) checking functions.+module Distribution.PackageDescription.Check.Target+ ( checkLibrary+ , checkForeignLib+ , checkExecutable+ , checkTestSuite+ , checkBenchmark+ ) where++import Distribution.Compat.Prelude+import Prelude ()++import Distribution.CabalSpecVersion+import Distribution.Compat.Lens+import Distribution.Compiler+import Distribution.ModuleName (ModuleName, toFilePath)+import Distribution.Package+import Distribution.PackageDescription+import Distribution.PackageDescription.Check.Common+import Distribution.PackageDescription.Check.Monad+import Distribution.PackageDescription.Check.Paths+import Distribution.Pretty (prettyShow)+import Distribution.Simple.BuildPaths+ ( autogenPackageInfoModuleName+ , autogenPathsModuleName+ )+import Distribution.Simple.Utils hiding (findPackageDesc, notice)+import Distribution.Types.PackageName.Magic+import Distribution.Utils.Path+import Distribution.Version+import Language.Haskell.Extension+import System.FilePath (takeExtension)++import Control.Monad++import qualified Distribution.Types.BuildInfo.Lens as L++checkLibrary+ :: Monad m+ => Bool -- Is this a sublibrary?+ -> [AssocDep] -- “Inherited” dependencies for PVP checks.+ -> Library+ -> CheckM m ()+checkLibrary+ isSub+ ads+ lib@( Library+ libName_+ _exposedModules_+ reexportedModules_+ signatures_+ _libExposed_+ _libVisibility_+ libBuildInfo_+ ) = do+ mapM_ checkModuleName (explicitLibModules lib)++ checkP+ (libName_ == LMainLibName && isSub)+ (PackageBuildImpossible UnnamedInternal)+ -- TODO: bogus if a required-signature was passed through.+ checkP+ (null (explicitLibModules lib) && null reexportedModules_)+ (PackageDistSuspiciousWarn (NoModulesExposed libName_))+ -- TODO parse-caught check, can safely remove.+ checkSpecVer+ CabalSpecV2_0+ (not . null $ signatures_)+ (PackageDistInexcusable SignaturesCabal2)+ -- autogen/includes checks.+ checkP+ ( not $+ all+ (flip elem (explicitLibModules lib))+ (libModulesAutogen lib)+ )+ (PackageBuildImpossible AutogenNotExposed)+ -- check that all autogen-includes appear on includes or+ -- install-includes.+ checkP+ ( not $+ all+ (flip elem (allExplicitIncludes lib) . getSymbolicPath)+ (view L.autogenIncludes lib)+ )+ $ (PackageBuildImpossible AutogenIncludesNotIncluded)++ -- § Build infos.+ checkBuildInfo+ (CETLibrary libName_)+ (explicitLibModules lib)+ ads+ libBuildInfo_++ -- Feature checks.+ -- check use of reexported-modules sections+ checkSpecVer+ CabalSpecV1_22+ (not . null $ reexportedModules_)+ (PackageDistInexcusable CVReexported)+ where+ allExplicitIncludes :: L.HasBuildInfo a => a -> [FilePath]+ allExplicitIncludes x =+ map getSymbolicPath (view L.includes x)+ ++ map getSymbolicPath (view L.installIncludes x)++checkForeignLib :: Monad m => ForeignLib -> CheckM m ()+checkForeignLib+ ( ForeignLib+ foreignLibName_+ _foreignLibType_+ _foreignLibOptions_+ foreignLibBuildInfo_+ _foreignLibVersionInfo_+ _foreignLibVersionLinux_+ _foreignLibModDefFile_+ ) = do+ checkBuildInfo+ (CETForeignLibrary foreignLibName_)+ []+ []+ foreignLibBuildInfo_++checkExecutable+ :: Monad m+ => [AssocDep] -- “Inherited” dependencies for PVP checks.+ -> Executable+ -> CheckM m ()+checkExecutable+ ads+ exe@( Executable+ exeName_+ symbolicModulePath_+ _exeScope_+ buildInfo_+ ) = do+ -- Target type/name (exe).+ let cet = CETExecutable exeName_+ modulePath_ = getSymbolicPath symbolicModulePath_++ mapM_ checkModuleName (exeModules exe)++ -- § Exe specific checks+ checkP+ (null modulePath_)+ (PackageBuildImpossible (NoMainIs exeName_))+ -- This check does not apply to scripts.+ pid <- asksCM (pnPackageId . ccNames)+ checkP+ ( pid /= fakePackageId+ && not (null modulePath_)+ && not (fileExtensionSupportedLanguage $ modulePath_)+ )+ (PackageBuildImpossible NoHsLhsMain)++ -- § Features check+ checkSpecVer+ CabalSpecV1_18+ ( fileExtensionSupportedLanguage modulePath_+ && takeExtension modulePath_ `notElem` [".hs", ".lhs"]+ )+ (PackageDistInexcusable MainCCabal1_18)++ -- Alas exeModules ad exeModulesAutogen (exported from+ -- Distribution.Types.Executable) take `Executable` as a parameter.+ checkP+ (not $ all (flip elem (exeModules exe)) (exeModulesAutogen exe))+ (PackageBuildImpossible $ AutogenNoOther cet)+ checkP+ ( not $+ all+ (flip elem (view L.includes exe) . relativeSymbolicPath)+ (view L.autogenIncludes exe)+ )+ (PackageBuildImpossible AutogenIncludesNotIncludedExe)++ -- § Build info checks.+ checkBuildInfo cet [] ads buildInfo_++checkTestSuite+ :: Monad m+ => [AssocDep] -- “Inherited” dependencies for PVP checks.+ -> TestSuite+ -> CheckM m ()+checkTestSuite+ ads+ ts@( TestSuite+ testName_+ testInterface_+ testBuildInfo_+ _testCodeGenerators_+ ) = do+ -- Target type/name (test).+ let cet = CETTest testName_++ -- § TS specific checks.+ -- TODO caught by the parser, can remove safely+ case testInterface_ of+ TestSuiteUnsupported tt@(TestTypeUnknown _ _) ->+ tellP (PackageBuildWarning $ TestsuiteTypeNotKnown tt)+ TestSuiteUnsupported tt ->+ tellP (PackageBuildWarning $ TestsuiteNotSupported tt)+ _ -> return ()++ mapM_ checkModuleName (testModules ts)++ checkP+ mainIsWrongExt+ (PackageBuildImpossible NoHsLhsMain)+ checkP+ ( not $+ all+ (flip elem (testModules ts))+ (testModulesAutogen ts)+ )+ (PackageBuildImpossible $ AutogenNoOther cet)+ checkP+ ( not $+ all+ (flip elem (view L.includes ts) . relativeSymbolicPath)+ (view L.autogenIncludes ts)+ )+ (PackageBuildImpossible AutogenIncludesNotIncludedExe)++ -- § Feature checks.+ checkSpecVer+ CabalSpecV1_18+ (mainIsNotHsExt && not mainIsWrongExt)+ (PackageDistInexcusable MainCCabal1_18)++ -- § Build info checks.+ checkBuildInfo cet [] ads testBuildInfo_+ where+ mainIsWrongExt =+ case testInterface_ of+ TestSuiteExeV10 _ f -> not (fileExtensionSupportedLanguage $ getSymbolicPath f)+ _ -> False++ mainIsNotHsExt =+ case testInterface_ of+ TestSuiteExeV10 _ f -> takeExtension (getSymbolicPath f) `notElem` [".hs", ".lhs"]+ _ -> False++checkBenchmark+ :: Monad m+ => [AssocDep] -- “Inherited” dependencies for PVP checks.+ -> Benchmark+ -> CheckM m ()+checkBenchmark+ ads+ bm@( Benchmark+ benchmarkName_+ benchmarkInterface_+ benchmarkBuildInfo_+ ) = do+ -- Target type/name (benchmark).+ let cet = CETBenchmark benchmarkName_++ mapM_ checkModuleName (benchmarkModules bm)++ -- § Interface & bm specific tests.+ case benchmarkInterface_ of+ BenchmarkUnsupported tt@(BenchmarkTypeUnknown _ _) ->+ tellP (PackageBuildWarning $ BenchmarkTypeNotKnown tt)+ BenchmarkUnsupported tt ->+ tellP (PackageBuildWarning $ BenchmarkNotSupported tt)+ _ -> return ()+ checkP+ mainIsWrongExt+ (PackageBuildImpossible NoHsLhsMainBench)++ checkP+ ( not $+ all+ (flip elem (benchmarkModules bm))+ (benchmarkModulesAutogen bm)+ )+ (PackageBuildImpossible $ AutogenNoOther cet)++ checkP+ ( not $+ all+ (flip elem (view L.includes bm) . relativeSymbolicPath)+ (view L.autogenIncludes bm)+ )+ (PackageBuildImpossible AutogenIncludesNotIncludedExe)++ -- § BuildInfo checks.+ checkBuildInfo cet [] ads benchmarkBuildInfo_+ where+ -- Cannot abstract with similar function in checkTestSuite,+ -- they are different.+ mainIsWrongExt =+ case benchmarkInterface_ of+ BenchmarkExeV10 _ f -> takeExtension (getSymbolicPath f) `notElem` [".hs", ".lhs"]+ _ -> False++-- | Check if a module name is valid on both Windows and Posix systems+checkModuleName :: Monad m => ModuleName -> CheckM m ()+checkModuleName moduleName =+ checkPackageFileNamesWithGlob PathKindFile (toFilePath moduleName)++-- ------------------------------------------------------------+-- Build info+-- ------------------------------------------------------------++-- Check a great deal of things in buildInfo.+-- With 'checkBuildInfo' we cannot follow the usual “pattern match+-- everything” method, for the number of BuildInfo fields (almost 50)+-- but more importantly because accessing options, etc. is done+-- with functions from 'Distribution.Types.BuildInfo' (e.g. 'hcOptions').+-- Duplicating the effort here means risk of diverging definitions for+-- little gain (most likely if a field is added to BI, the relevant+-- function will be tweaked in Distribution.Types.BuildInfo too).+checkBuildInfo+ :: Monad m+ => CEType -- Name and type of the target.+ -> [ModuleName] -- Additional module names which cannot be+ -- extracted from BuildInfo (mainly: exposed+ -- library modules).+ -> [AssocDep] -- Inherited “internal” (main lib, named+ -- internal libs) dependencies.+ -> BuildInfo+ -> CheckM m ()+checkBuildInfo cet ams ads bi = do+ -- For the sake of clarity, we split che checks in various+ -- (top level) functions, even if we are not actually going+ -- deeper in the traversal.++ checkBuildInfoOptions (cet2bit cet) bi+ checkBuildInfoPathsContent bi+ checkBuildInfoPathsWellFormedness bi++ sv <- asksCM ccSpecVersion+ checkBuildInfoFeatures bi sv++ checkAutogenModules ams bi++ -- PVP: we check for base and all other deps.+ let ds = mergeDependencies $ targetBuildDepends bi+ (ids, rds) <-+ partitionDeps+ ads+ [mkUnqualComponentName "base"]+ ds+ let ick = const (PackageDistInexcusable BaseNoUpperBounds)+ rck = PackageDistSuspiciousWarn . MissingUpperBounds cet+ leuck = PackageDistSuspiciousWarn . LEUpperBounds cet+ tzuck = PackageDistSuspiciousWarn . TrailingZeroUpperBounds cet+ gtlck = PackageDistSuspiciousWarn . GTLowerBounds cet+ checkPVP (checkDependencyVersionRange $ not . hasUpperBound) ick ids+ unless+ (isInternalTarget cet)+ (checkPVPs (checkDependencyVersionRange $ not . hasUpperBound) rck rds)+ unless+ (isInternalTarget cet)+ (checkPVPs (checkDependencyVersionRange hasLEUpperBound) leuck ds)+ unless+ (isInternalTarget cet)+ (checkPVPs (checkDependencyVersionRange hasTrailingZeroUpperBound) tzuck ds)+ unless+ (isInternalTarget cet)+ (checkPVPs (checkDependencyVersionRange hasGTLowerBound) gtlck ds)++ -- Custom fields well-formedness (ASCII).+ mapM_ checkCustomField (customFieldsBI bi)++ -- Content.+ mapM_ (checkLocalPathExist "extra-lib-dirs" . getSymbolicPath) (extraLibDirs bi)+ mapM_+ (checkLocalPathExist "extra-lib-dirs-static" . getSymbolicPath)+ (extraLibDirsStatic bi)+ mapM_+ (checkLocalPathExist "extra-framework-dirs" . getSymbolicPath)+ (extraFrameworkDirs bi)+ mapM_ (checkLocalPathExist "include-dirs" . getSymbolicPath) (includeDirs bi)+ mapM_+ (checkLocalPathExist "hs-source-dirs" . getSymbolicPath)+ (hsSourceDirs bi)++-- Well formedness of BI contents (no `Haskell2015`, no deprecated+-- extensions etc).+checkBuildInfoPathsContent :: Monad m => BuildInfo -> CheckM m ()+checkBuildInfoPathsContent bi = do+ mapM_ checkLang (allLanguages bi)+ mapM_ checkExt (allExtensions bi)+ mapM_ checkIntDep (targetBuildDepends bi)+ df <- asksCM ccDesugar+ -- This way we can use the same function for legacy&non exedeps.+ let ds = buildToolDepends bi ++ catMaybes (map df $ buildTools bi)+ mapM_ checkBTDep ds+ where+ checkLang :: Monad m => Language -> CheckM m ()+ checkLang (UnknownLanguage n) =+ tellP (PackageBuildWarning (UnknownLanguages [n]))+ checkLang _ = return ()++ checkExt :: Monad m => Extension -> CheckM m ()+ checkExt (UnknownExtension n)+ | n `elem` map prettyShow knownLanguages =+ tellP (PackageBuildWarning (LanguagesAsExtension [n]))+ | otherwise =+ tellP (PackageBuildWarning (UnknownExtensions [n]))+ checkExt n = do+ let dss = filter (\(a, _) -> a == n) deprecatedExtensions+ checkP+ (not . null $ dss)+ (PackageDistSuspicious $ DeprecatedExtensions dss)++ checkIntDep :: Monad m => Dependency -> CheckM m ()+ checkIntDep d@(Dependency name vrange _) = do+ mpn <-+ asksCM+ ( packageNameToUnqualComponentName+ . pkgName+ . pnPackageId+ . ccNames+ )+ lns <- asksCM (pnSubLibs . ccNames)+ pVer <- asksCM (pkgVersion . pnPackageId . ccNames)+ let allLibNs = mpn : lns+ when+ ( mpn == packageNameToUnqualComponentName name+ -- Make sure it is not a library with the+ -- same name from another package.+ && packageNameToUnqualComponentName name `elem` allLibNs+ )+ ( checkP+ (not $ pVer `withinRange` vrange)+ (PackageBuildImpossible $ ImpossibleInternalDep [d])+ )++ checkBTDep :: Monad m => ExeDependency -> CheckM m ()+ checkBTDep ed@(ExeDependency n name vrange) = do+ exns <- asksCM (pnExecs . ccNames)+ pVer <- asksCM (pkgVersion . pnPackageId . ccNames)+ pNam <- asksCM (pkgName . pnPackageId . ccNames)+ checkP+ ( n == pNam+ && name `notElem` exns -- internal+ -- not present+ )+ (PackageBuildImpossible $ MissingInternalExe [ed])+ when+ (name `elem` exns)+ ( checkP+ (not $ pVer `withinRange` vrange)+ (PackageBuildImpossible $ ImpossibleInternalExe [ed])+ )++-- Paths well-formedness check for BuildInfo.+checkBuildInfoPathsWellFormedness :: Monad m => BuildInfo -> CheckM m ()+checkBuildInfoPathsWellFormedness bi = do+ mapM_ (checkPath False "asm-sources" PathKindFile . getSymbolicPath) (asmSources bi)+ mapM_ (checkPath False "cmm-sources" PathKindFile . getSymbolicPath) (cmmSources bi)+ mapM_ (checkPath False "c-sources" PathKindFile . getSymbolicPath) (cSources bi)+ mapM_ (checkPath False "cxx-sources" PathKindFile . getSymbolicPath) (cxxSources bi)+ mapM_ (checkPath False "js-sources" PathKindFile . getSymbolicPath) (jsSources bi)+ mapM_+ (checkPath False "install-includes" PathKindFile . getSymbolicPath)+ (installIncludes bi)+ mapM_+ (checkPath False "hs-source-dirs" PathKindDirectory . getSymbolicPath)+ (hsSourceDirs bi)+ -- Possibly absolute paths.+ mapM_ (checkPath True "includes" PathKindFile . getSymbolicPath) (includes bi)+ mapM_+ (checkPath True "include-dirs" PathKindDirectory . getSymbolicPath)+ (includeDirs bi)+ mapM_+ (checkPath True "extra-lib-dirs" PathKindDirectory . getSymbolicPath)+ (extraLibDirs bi)+ mapM_+ (checkPath True "extra-lib-dirs-static" PathKindDirectory . getSymbolicPath)+ (extraLibDirsStatic bi)+ mapM_ checkOptionPath (perCompilerFlavorToList $ options bi)+ where+ checkOptionPath+ :: Monad m+ => (CompilerFlavor, [FilePath])+ -> CheckM m ()+ checkOptionPath (GHC, paths) =+ mapM_+ ( \path ->+ checkP+ (isInsideDist path)+ (PackageDistInexcusable $ DistPoint Nothing path)+ )+ paths+ checkOptionPath _ = return ()++-- Checks for features that can be present in BuildInfo only with certain+-- CabalSpecVersion.+checkBuildInfoFeatures+ :: Monad m+ => BuildInfo+ -> CabalSpecVersion+ -> CheckM m ()+checkBuildInfoFeatures bi sv = do+ -- Default language can be used only w/ spec ≥ 1.10+ checkSpecVer+ CabalSpecV1_10+ (isJust $ defaultLanguage bi)+ (PackageBuildWarning CVDefaultLanguage)+ -- CheckSpecVer sv.+ checkDefaultLanguage+ -- Check use of 'extra-framework-dirs' field.+ checkSpecVer+ CabalSpecV1_24+ (not . null $ extraFrameworkDirs bi)+ (PackageDistSuspiciousWarn CVExtraFrameworkDirs)+ -- Check use of default-extensions field don't need to do the+ -- equivalent check for other-extensions.+ checkSpecVer+ CabalSpecV1_10+ (not . null $ defaultExtensions bi)+ (PackageBuildWarning CVDefaultExtensions)+ -- Check use of extensions field+ checkP+ (sv >= CabalSpecV1_10 && (not . null $ oldExtensions bi))+ (PackageBuildWarning CVExtensionsDeprecated)++ -- asm-sources, cmm-sources and friends only w/ spec ≥ 1.10+ checkCVSources (map getSymbolicPath $ asmSources bi)+ checkCVSources (map getSymbolicPath $ cmmSources bi)+ checkCVSources (extraBundledLibs bi)+ checkCVSources (extraLibFlavours bi)++ -- extra-dynamic-library-flavours requires ≥ 3.0+ checkSpecVer+ CabalSpecV3_0+ (not . null $ extraDynLibFlavours bi)+ (PackageDistInexcusable $ CVExtraDynamic [extraDynLibFlavours bi])+ -- virtual-modules requires ≥ 2.2+ checkSpecVer CabalSpecV2_2 (not . null $ virtualModules bi) $+ (PackageDistInexcusable CVVirtualModules)+ -- Check use of thinning and renaming.+ checkSpecVer+ CabalSpecV2_0+ (not . null $ mixins bi)+ (PackageDistInexcusable CVMixins)++ checkBuildInfoExtensions bi+ where+ checkCVSources :: Monad m => [FilePath] -> CheckM m ()+ checkCVSources cvs =+ checkSpecVer+ CabalSpecV3_0+ (not . null $ cvs)+ (PackageDistInexcusable CVSources)++ checkDefaultLanguage :: Monad m => CheckM m ()+ checkDefaultLanguage = do+ -- < 1.10 has no `default-language` field.+ when+ (sv >= CabalSpecV1_10 && isNothing (defaultLanguage bi))+ -- < 3.4 mandatory, after just a suggestion.+ ( if sv < CabalSpecV3_4+ then tellP (PackageBuildWarning CVDefaultLanguageComponent)+ else tellP (PackageDistInexcusable CVDefaultLanguageComponentSoft)+ )++-- Tests for extensions usage which can break Cabal < 1.4.+checkBuildInfoExtensions :: Monad m => BuildInfo -> CheckM m ()+checkBuildInfoExtensions bi = do+ let exts = allExtensions bi+ extCabal1_2 = nub $ filter (`elem` compatExtensionsExtra) exts+ extCabal1_4 = nub $ filter (`notElem` compatExtensions) exts+ -- As of Cabal-1.4 we can add new extensions without worrying+ -- about breaking old versions of cabal.+ checkSpecVer+ CabalSpecV1_2+ (not . null $ extCabal1_2)+ ( PackageDistInexcusable $+ CVExtensions CabalSpecV1_2 extCabal1_2+ )+ checkSpecVer+ CabalSpecV1_4+ (not . null $ extCabal1_4)+ ( PackageDistInexcusable $+ CVExtensions CabalSpecV1_4 extCabal1_4+ )+ where+ -- The known extensions in Cabal-1.2.3+ compatExtensions :: [Extension]+ compatExtensions =+ map+ EnableExtension+ [ OverlappingInstances+ , UndecidableInstances+ , IncoherentInstances+ , RecursiveDo+ , ParallelListComp+ , MultiParamTypeClasses+ , FunctionalDependencies+ , Rank2Types+ , RankNTypes+ , PolymorphicComponents+ , ExistentialQuantification+ , ScopedTypeVariables+ , ImplicitParams+ , FlexibleContexts+ , FlexibleInstances+ , EmptyDataDecls+ , CPP+ , BangPatterns+ , TypeSynonymInstances+ , TemplateHaskell+ , ForeignFunctionInterface+ , Arrows+ , Generics+ , NamedFieldPuns+ , PatternGuards+ , GeneralizedNewtypeDeriving+ , ExtensibleRecords+ , RestrictedTypeSynonyms+ , HereDocuments+ ]+ ++ map+ DisableExtension+ [MonomorphismRestriction, ImplicitPrelude]+ ++ compatExtensionsExtra++ -- The extra known extensions in Cabal-1.2.3 vs Cabal-1.1.6+ -- (Cabal-1.1.6 came with ghc-6.6. Cabal-1.2 came with ghc-6.8)+ compatExtensionsExtra :: [Extension]+ compatExtensionsExtra =+ map+ EnableExtension+ [ KindSignatures+ , MagicHash+ , TypeFamilies+ , StandaloneDeriving+ , UnicodeSyntax+ , PatternSignatures+ , UnliftedFFITypes+ , LiberalTypeSynonyms+ , TypeOperators+ , RecordWildCards+ , RecordPuns+ , DisambiguateRecordFields+ , OverloadedStrings+ , GADTs+ , RelaxedPolyRec+ , ExtendedDefaultRules+ , UnboxedTuples+ , DeriveDataTypeable+ , ConstrainedClassMethods+ ]+ ++ map+ DisableExtension+ [MonoPatBinds]++-- Autogenerated modules (Paths_, PackageInfo_) checks. We could pass this+-- function something more specific than the whole BuildInfo, but it would be+-- a tuple of [ModuleName] lists, error prone.+checkAutogenModules+ :: Monad m+ => [ModuleName] -- Additional modules not present+ -- in BuildInfo (e.g. exposed library+ -- modules).+ -> BuildInfo+ -> CheckM m ()+checkAutogenModules ams bi = do+ pkgId <- asksCM (pnPackageId . ccNames)+ let+ -- It is an unfortunate reality that autogenPathsModuleName+ -- and autogenPackageInfoModuleName work on PackageDescription+ -- while not needing it all, but just the `package` bit.+ minimalPD = emptyPackageDescription{package = pkgId}+ autoPathsName = autogenPathsModuleName minimalPD+ autoInfoModuleName = autogenPackageInfoModuleName minimalPD++ -- Autogenerated module + some default extension build failure.+ autogenCheck autoPathsName CVAutogenPaths+ rebindableClashCheck autoPathsName RebindableClashPaths++ -- Paths_* module + some default extension build failure.+ autogenCheck autoInfoModuleName CVAutogenPackageInfo+ rebindableClashCheck autoInfoModuleName RebindableClashPackageInfo++ -- PackageInfo_* module + cabal-version < 3.12+ -- See Mikolaj’s comments on #9481 on why this has to be+ -- PackageBuildImpossible and not merely PackageDistInexcusable.+ checkSpecVer+ CabalSpecV3_12+ (elem autoInfoModuleName allModsForAuto)+ (PackageBuildImpossible CVAutogenPackageInfoGuard)+ where+ allModsForAuto :: [ModuleName]+ allModsForAuto = ams ++ otherModules bi++ autogenCheck+ :: Monad m+ => ModuleName+ -> CheckExplanation+ -> CheckM m ()+ autogenCheck name warning = do+ sv <- asksCM ccSpecVersion+ checkP+ ( sv >= CabalSpecV2_0+ && elem name allModsForAuto+ && notElem name (autogenModules bi)+ )+ (PackageDistInexcusable warning)++ rebindableClashCheck+ :: Monad m+ => ModuleName+ -> CheckExplanation+ -> CheckM m ()+ rebindableClashCheck name warning = do+ checkSpecVer+ CabalSpecV2_2+ ( ( name `elem` otherModules bi+ || name `elem` autogenModules bi+ )+ && checkExts+ )+ (PackageBuildImpossible warning)++ -- Do we have some peculiar extensions active which would interfere+ -- (cabal-version <2.2) with Paths_modules?+ checkExts :: Bool+ checkExts =+ let exts = defaultExtensions bi+ in rebind `elem` exts+ && (strings `elem` exts || lists `elem` exts)+ where+ rebind = EnableExtension RebindableSyntax+ strings = EnableExtension OverloadedStrings+ lists = EnableExtension OverloadedLists++checkLocalPathExist+ :: Monad m+ => String -- .cabal field where we found the error.+ -> FilePath+ -> CheckM m ()+checkLocalPathExist title dir =+ checkPkg+ ( \ops -> do+ dn <- not <$> doesDirectoryExist ops dir+ let rp = not (isAbsoluteOnAnyPlatform dir)+ return (rp && dn)+ )+ (PackageBuildWarning $ UnknownDirectory title dir)++-- PVP --++-- Sometimes we read (or end up with) “straddle” deps declarations+-- like this:+--+-- build-depends: base > 3, base < 4+--+-- `mergeDependencies` reduces that to base > 3 && < 4, _while_ maintaining+-- dependencies order in the list (better UX).+mergeDependencies :: [Dependency] -> [Dependency]+mergeDependencies [] = []+mergeDependencies l@(d : _) =+ let (sames, diffs) = partition ((== depName d) . depName) l+ merged =+ Dependency+ (depPkgName d)+ ( foldl intersectVersionRanges anyVersion $+ map depVerRange sames+ )+ (depLibraries d)+ in merged : mergeDependencies diffs+ where+ depName :: Dependency -> String+ depName wd = unPackageName . depPkgName $ wd++-- Is this an internal target? We do not perform PVP checks on those,+-- see https://github.com/haskell/cabal/pull/8361#issuecomment-1577547091+isInternalTarget :: CEType -> Bool+isInternalTarget (CETLibrary{}) = False+isInternalTarget (CETForeignLibrary{}) = False+isInternalTarget (CETExecutable{}) = False+isInternalTarget (CETTest{}) = True+isInternalTarget (CETBenchmark{}) = True+isInternalTarget (CETSetup{}) = False++-- ------------------------------------------------------------+-- Options+-- ------------------------------------------------------------++-- Target type for option checking.+data BITarget = BITLib | BITTestBench | BITOther+ deriving (Eq, Show)++cet2bit :: CEType -> BITarget+cet2bit (CETLibrary{}) = BITLib+cet2bit (CETForeignLibrary{}) = BITLib+cet2bit (CETExecutable{}) = BITOther+cet2bit (CETTest{}) = BITTestBench+cet2bit (CETBenchmark{}) = BITTestBench+cet2bit CETSetup = BITOther++-- General check on all options (ghc, C, C++, …) for common inaccuracies.+checkBuildInfoOptions :: Monad m => BITarget -> BuildInfo -> CheckM m ()+checkBuildInfoOptions t bi = do+ checkGHCOptions "ghc-options" t (hcOptions GHC bi)+ checkGHCOptions "ghc-prof-options" t (hcProfOptions GHC bi)+ checkGHCOptions "ghc-shared-options" t (hcSharedOptions GHC bi)+ let ldOpts = ldOptions bi+ checkCLikeOptions LangC "cc-options" (ccOptions bi) ldOpts+ checkCLikeOptions LangCPlusPlus "cxx-options" (cxxOptions bi) ldOpts+ checkCPPOptions (cppOptions bi)+ checkJSPOptions (jsppOptions bi)++-- | Checks GHC options for commonly misused or non-portable flags.+checkGHCOptions+ :: Monad m+ => CabalField -- .cabal field name where we found the error.+ -> BITarget -- Target type.+ -> [String] -- Options (alas in String form).+ -> CheckM m ()+checkGHCOptions title t opts = do+ checkGeneral+ case t of+ BITLib -> sequence_ [checkLib, checkNonTestBench]+ BITTestBench -> checkTestBench+ BITOther -> checkNonTestBench+ where+ checkFlags :: Monad m => [String] -> PackageCheck -> CheckM m ()+ checkFlags fs ck = checkP (any (`elem` fs) opts) ck++ checkFlagsP+ :: Monad m+ => (String -> Bool)+ -> (String -> PackageCheck)+ -> CheckM m ()+ checkFlagsP p ckc =+ case filter p opts of+ [] -> return ()+ (_ : _) -> tellP (ckc title)++ checkGeneral = do+ checkFlags+ ["-fasm"]+ (PackageDistInexcusable $ OptFasm title)+ checkFlags+ ["-fhpc"]+ (PackageDistInexcusable $ OptHpc title)+ checkFlags+ ["-prof"]+ (PackageBuildWarning $ OptProf title)+ pid <- asksCM (pnPackageId . ccNames)+ -- Scripts add the -o flag in the fake-package.cabal in order to have the+ -- executable name match the script name even when there are characters+ -- in the script name which are illegal to have as a target name.+ unless (pid == fakePackageId) $+ checkFlags+ ["-o"]+ (PackageBuildWarning $ OptO title)+ checkFlags+ ["-hide-package"]+ (PackageBuildWarning $ OptHide title)+ checkFlags+ ["--make"]+ (PackageBuildWarning $ OptMake title)+ checkFlags+ ["-O", "-O1"]+ (PackageDistInexcusable $ OptOOne title)+ checkFlags+ ["-O2"]+ (PackageDistSuspiciousWarn $ OptOTwo title)+ checkFlags+ ["-split-sections"]+ (PackageBuildWarning $ OptSplitSections title)+ checkFlags+ ["-split-objs"]+ (PackageBuildWarning $ OptSplitObjs title)+ checkFlags+ ["-optl-Wl,-s", "-optl-s"]+ (PackageDistInexcusable $ OptWls title)+ checkFlags+ ["-fglasgow-exts"]+ (PackageDistSuspicious $ OptExts title)+ let ghcNoRts = rmRtsOpts opts+ checkAlternatives+ title+ "default-extensions"+ [ (flag, prettyShow extension)+ | flag <- ghcNoRts+ , Just extension <- [ghcExtension flag]+ ]+ checkAlternatives+ title+ "default-extensions"+ [ (flag, extension)+ | flag@('-' : 'X' : extension) <- ghcNoRts+ ]+ checkAlternatives+ title+ "cpp-options"+ ( [(flag, flag) | flag@('-' : 'D' : _) <- ghcNoRts]+ ++ [(flag, flag) | flag@('-' : 'U' : _) <- ghcNoRts]+ )+ checkAlternatives+ title+ "jspp-options"+ ( [(flag, flag) | flag@('-' : 'D' : _) <- ghcNoRts]+ ++ [(flag, flag) | flag@('-' : 'U' : _) <- ghcNoRts]+ )+ checkAlternatives+ title+ "include-dirs"+ [(flag, dir) | flag@('-' : 'I' : dir) <- ghcNoRts]+ checkAlternatives+ title+ "extra-libraries"+ [(flag, lib) | flag@('-' : 'l' : lib) <- ghcNoRts]+ checkAlternatives+ title+ "extra-libraries-static"+ [(flag, lib) | flag@('-' : 'l' : lib) <- ghcNoRts]+ checkAlternatives+ title+ "extra-lib-dirs"+ [(flag, dir) | flag@('-' : 'L' : dir) <- ghcNoRts]+ checkAlternatives+ title+ "extra-lib-dirs-static"+ [(flag, dir) | flag@('-' : 'L' : dir) <- ghcNoRts]+ checkAlternatives+ title+ "frameworks"+ [ (flag, fmwk)+ | (flag@"-framework", fmwk) <-+ zip ghcNoRts (safeTail ghcNoRts)+ ]+ checkAlternatives+ title+ "extra-framework-dirs"+ [ (flag, dir)+ | (flag@"-framework-path", dir) <-+ zip ghcNoRts (safeTail ghcNoRts)+ ]+ -- Old `checkDevelopmentOnlyFlagsOptions` section+ checkFlags+ ["-Werror"]+ (PackageDistInexcusable $ WErrorUnneeded title)+ checkFlags+ ["-fdefer-type-errors"]+ (PackageDistInexcusable $ FDeferTypeErrorsUnneeded title)+ checkFlags+ [ "-fprof-auto"+ , "-fprof-auto-top"+ , "-fprof-auto-calls"+ , "-fprof-cafs"+ , "-fno-prof-count-entries"+ , "-auto-all"+ , "-auto"+ , "-caf-all"+ ]+ (PackageDistSuspicious $ ProfilingUnneeded title)+ checkFlagsP+ ( \opt ->+ "-d" `isPrefixOf` opt+ && opt /= "-dynamic"+ )+ (PackageDistInexcusable . DynamicUnneeded)+ checkFlagsP+ ( \opt -> case opt of+ "-j" -> True+ ('-' : 'j' : d : _) -> isDigit d+ _ -> False+ )+ (PackageDistInexcusable . JUnneeded)++ checkLib = do+ checkP+ ("-rtsopts" `elem` opts)+ (PackageBuildWarning $ OptRts title)+ checkP+ (any (\opt -> "-with-rtsopts" `isPrefixOf` opt) opts)+ (PackageBuildWarning $ OptWithRts title)++ checkTestBench = do+ checkFlags+ ["-O0", "-Onot"]+ (PackageDistSuspiciousWarn $ OptONot title)++ checkNonTestBench = do+ checkFlags+ ["-O0", "-Onot"]+ (PackageDistSuspicious $ OptONot title)++ ghcExtension ('-' : 'f' : name) = case name of+ "allow-overlapping-instances" -> enable OverlappingInstances+ "no-allow-overlapping-instances" -> disable OverlappingInstances+ "th" -> enable TemplateHaskell+ "no-th" -> disable TemplateHaskell+ "ffi" -> enable ForeignFunctionInterface+ "no-ffi" -> disable ForeignFunctionInterface+ "fi" -> enable ForeignFunctionInterface+ "no-fi" -> disable ForeignFunctionInterface+ "monomorphism-restriction" -> enable MonomorphismRestriction+ "no-monomorphism-restriction" -> disable MonomorphismRestriction+ "mono-pat-binds" -> enable MonoPatBinds+ "no-mono-pat-binds" -> disable MonoPatBinds+ "allow-undecidable-instances" -> enable UndecidableInstances+ "no-allow-undecidable-instances" -> disable UndecidableInstances+ "allow-incoherent-instances" -> enable IncoherentInstances+ "no-allow-incoherent-instances" -> disable IncoherentInstances+ "arrows" -> enable Arrows+ "no-arrows" -> disable Arrows+ "generics" -> enable Generics+ "no-generics" -> disable Generics+ "implicit-prelude" -> enable ImplicitPrelude+ "no-implicit-prelude" -> disable ImplicitPrelude+ "implicit-params" -> enable ImplicitParams+ "no-implicit-params" -> disable ImplicitParams+ "bang-patterns" -> enable BangPatterns+ "no-bang-patterns" -> disable BangPatterns+ "scoped-type-variables" -> enable ScopedTypeVariables+ "no-scoped-type-variables" -> disable ScopedTypeVariables+ "extended-default-rules" -> enable ExtendedDefaultRules+ "no-extended-default-rules" -> disable ExtendedDefaultRules+ _ -> Nothing+ ghcExtension "-cpp" = enable CPP+ ghcExtension _ = Nothing++ enable e = Just (EnableExtension e)+ disable e = Just (DisableExtension e)++ rmRtsOpts :: [String] -> [String]+ rmRtsOpts ("-with-rtsopts" : _ : xs) = rmRtsOpts xs+ rmRtsOpts (x : xs) = x : rmRtsOpts xs+ rmRtsOpts [] = []++checkCLikeOptions+ :: Monad m+ => WarnLang -- Language we are warning about (C or C++).+ -> CabalField -- Field where we found the error.+ -> [String] -- Options in string form.+ -> [String] -- Link options in String form.+ -> CheckM m ()+checkCLikeOptions label prefix opts ldOpts = do+ checkAlternatives+ prefix+ "include-dirs"+ [(flag, dir) | flag@('-' : 'I' : dir) <- opts]+ checkAlternatives+ prefix+ "extra-libraries"+ [(flag, lib) | flag@('-' : 'l' : lib) <- opts]+ checkAlternatives+ prefix+ "extra-lib-dirs"+ [(flag, dir) | flag@('-' : 'L' : dir) <- opts]++ checkAlternatives+ "ld-options"+ "extra-libraries"+ [(flag, lib) | flag@('-' : 'l' : lib) <- ldOpts]+ checkAlternatives+ "ld-options"+ "extra-lib-dirs"+ [(flag, dir) | flag@('-' : 'L' : dir) <- ldOpts]++ checkP+ (any (`elem` ["-O", "-Os", "-O0", "-O1", "-O2", "-O3"]) opts)+ (PackageDistSuspicious $ COptONumber prefix label)++checkAlternatives+ :: Monad m+ => CabalField -- Wrong field.+ -> CabalField -- Appropriate field.+ -> [(String, String)] -- List of good and bad flags.+ -> CheckM m ()+checkAlternatives badField goodField flags = do+ let (badFlags, _) = unzip flags+ checkP+ (not $ null badFlags)+ (PackageBuildWarning $ OptAlternatives badField goodField flags)++checkCPPOptions+ :: Monad m+ => [String] -- Options in String form.+ -> CheckM m ()+checkCPPOptions opts = do+ checkAlternatives+ "cpp-options"+ "include-dirs"+ [(flag, dir) | flag@('-' : 'I' : dir) <- opts]+ mapM_+ ( \opt ->+ checkP+ (not $ any (`isPrefixOf` opt) ["-D", "-U", "-I"])+ (PackageBuildWarning (COptCPP opt))+ )+ opts++checkJSPOptions+ :: Monad m+ => [String] -- Options in String form.+ -> CheckM m ()+checkJSPOptions opts = do+ checkAlternatives+ "jspp-options"+ "include-dirs"+ [(flag, dir) | flag@('-' : 'I' : dir) <- opts]+ mapM_+ ( \opt ->+ checkP+ (not $ any (`isPrefixOf` opt) ["-D", "-U", "-I"])+ (PackageBuildWarning (OptJSPP opt))+ )+ opts
+ src/Distribution/PackageDescription/Check/Warning.hs view
@@ -0,0 +1,1527 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE ScopedTypeVariables #-}++-- |+-- Module : Distribution.PackageDescription.Check.Warning+-- Copyright : Francesco Ariis 2022+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Warning types, messages, severity and associated functions.+module Distribution.PackageDescription.Check.Warning+ ( -- * Types and constructors+ PackageCheck (..)+ , CheckExplanation (..)+ , CheckExplanationID+ , CheckExplanationIDString+ , CEType (..)+ , WarnLang (..)++ -- * Operations+ , ppPackageCheck+ , ppCheckExplanationId+ , isHackageDistError+ , extractCheckExplanation+ , filterPackageChecksById+ , filterPackageChecksByIdString+ ) where++import Distribution.Compat.Prelude+import Prelude ()++import Distribution.CabalSpecVersion (CabalSpecVersion, showCabalSpecVersion)+import Distribution.License (License, knownLicenses)+import Distribution.ModuleName (ModuleName)+import Distribution.Parsec.Warning (PWarning, showPWarning)+import Distribution.Pretty (prettyShow)+import Distribution.Types.BenchmarkType (BenchmarkType, knownBenchmarkTypes)+import Distribution.Types.Dependency (Dependency (..))+import Distribution.Types.ExeDependency (ExeDependency)+import Distribution.Types.Flag (FlagName, unFlagName)+import Distribution.Types.LibraryName (LibraryName (..), showLibraryName)+import Distribution.Types.PackageName (PackageName)+import Distribution.Types.TestType (TestType, knownTestTypes)+import Distribution.Types.UnqualComponentName+import Distribution.Types.Version (Version)+import Distribution.Utils.Path (FileOrDir (..), Pkg, RelativePath, getSymbolicPath)+import Language.Haskell.Extension (Extension)++import qualified Data.Either as Either+import qualified Data.List as List+import qualified Data.Set as Set++-- ------------------------------------------------------------+-- Check types and explanations+-- ------------------------------------------------------------++-- | Results of some kind of failed package check.+--+-- There are a range of severities, from merely dubious to totally insane.+-- All of them come with a human readable explanation. In future we may augment+-- them with more machine readable explanations, for example to help an IDE+-- suggest automatic corrections.+data PackageCheck+ = -- | This package description is no good. There's no way it's going to+ -- build sensibly. This should give an error at configure time.+ PackageBuildImpossible {explanation :: CheckExplanation}+ | -- | A problem that is likely to affect building the package, or an+ -- issue that we'd like every package author to be aware of, even if+ -- the package is never distributed.+ PackageBuildWarning {explanation :: CheckExplanation}+ | -- | An issue that might not be a problem for the package author but+ -- might be annoying or detrimental when the package is distributed to+ -- users. We should encourage distributed packages to be free from these+ -- issues, but occasionally there are justifiable reasons so we cannot+ -- ban them entirely.+ PackageDistSuspicious {explanation :: CheckExplanation}+ | -- | Like PackageDistSuspicious but will only display warnings+ -- rather than causing abnormal exit when you run 'cabal check'.+ PackageDistSuspiciousWarn {explanation :: CheckExplanation}+ | -- | An issue that is OK in the author's environment but is almost+ -- certain to be a portability problem for other environments. We can+ -- quite legitimately refuse to publicly distribute packages with these+ -- problems.+ PackageDistInexcusable {explanation :: CheckExplanation}+ deriving (Eq, Ord)++-- | Pretty printing 'PackageCheck'.+ppPackageCheck :: PackageCheck -> String+ppPackageCheck e =+ let ex = explanation e+ in "["+ ++ (ppCheckExplanationId . checkExplanationId) ex+ ++ "] "+ ++ ppExplanation ex++-- | Broken 'Show' instance (not bijective with Read), alas external packages+-- depend on it.+instance Show PackageCheck where+ show notice = ppPackageCheck notice++-- | Would Hackage refuse a package because of this error?+isHackageDistError :: PackageCheck -> Bool+isHackageDistError = \case+ (PackageBuildImpossible{}) -> True+ (PackageBuildWarning{}) -> True+ (PackageDistInexcusable{}) -> True+ (PackageDistSuspicious{}) -> False+ (PackageDistSuspiciousWarn{}) -> False++-- | Filter Package Check by CheckExplanationID.+filterPackageChecksById+ :: [PackageCheck]+ -- ^ Original checks.+ -> [CheckExplanationID]+ -- ^ IDs to omit.+ -> [PackageCheck]+filterPackageChecksById cs is = filter ff cs+ where+ ff :: PackageCheck -> Bool+ ff c =+ flip notElem is+ . checkExplanationId+ . extractCheckExplanation+ $ c++-- | Filter Package Check by Check explanation /string/.+filterPackageChecksByIdString+ :: [PackageCheck]+ -- ^ Original checks.+ -> [CheckExplanationIDString]+ -- ^ IDs to omit, in @String@ format.+ -> ([PackageCheck], [CheckExplanationIDString])+-- Filtered checks plus unrecognised id strings.+filterPackageChecksByIdString cs ss =+ let (es, is) = Either.partitionEithers $ map readExplanationID ss+ in (filterPackageChecksById cs is, es)++-- | Explanations of 'PackageCheck`'s errors/warnings.+data CheckExplanation+ = ParseWarning FilePath PWarning+ | NoNameField+ | NoVersionField+ | NoTarget+ | UnnamedInternal+ | DuplicateSections [UnqualComponentName]+ | IllegalLibraryName PackageName+ | NoModulesExposed LibraryName+ | SignaturesCabal2+ | AutogenNotExposed+ | AutogenIncludesNotIncluded+ | NoMainIs UnqualComponentName+ | NoHsLhsMain+ | MainCCabal1_18+ | AutogenNoOther CEType+ | AutogenIncludesNotIncludedExe+ | TestsuiteTypeNotKnown TestType+ | TestsuiteNotSupported TestType+ | BenchmarkTypeNotKnown BenchmarkType+ | BenchmarkNotSupported BenchmarkType+ | NoHsLhsMainBench+ | InvalidNameWin PackageName+ | ZPrefix+ | NoBuildType+ | NoCustomSetup+ | UnknownCompilers [String]+ | UnknownLanguages [String]+ | UnknownExtensions [String]+ | LanguagesAsExtension [String]+ | DeprecatedExtensions [(Extension, Maybe Extension)]+ | MissingFieldCategory+ | MissingFieldMaintainer+ | MissingFieldSynopsis+ | MissingFieldDescription+ | MissingFieldSynOrDesc+ | SynopsisTooLong+ | ShortDesc+ | InvalidTestWith [Dependency]+ | ImpossibleInternalDep [Dependency]+ | ImpossibleInternalExe [ExeDependency]+ | MissingInternalExe [ExeDependency]+ | NONELicense+ | NoLicense+ | AllRightsReservedLicense+ | LicenseMessParse License+ | UnrecognisedLicense String+ | UncommonBSD4+ | UnknownLicenseVersion License [Version]+ | NoLicenseFile+ | UnrecognisedSourceRepo String+ | MissingType+ | MissingLocation+ | GitProtocol+ | MissingModule+ | MissingTag+ | SubdirRelPath+ | SubdirGoodRelPath String+ | OptFasm String+ | OptHpc String+ | OptProf String+ | OptO String+ | OptHide String+ | OptMake String+ | OptONot String+ | OptOOne String+ | OptOTwo String+ | OptSplitSections String+ | OptSplitObjs String+ | OptWls String+ | OptExts String+ | OptRts String+ | OptWithRts String+ | COptONumber String WarnLang+ | COptCPP String+ | OptJSPP String+ | OptAlternatives String String [(String, String)]+ | RelativeOutside String FilePath+ | AbsolutePath String FilePath+ | BadRelativePath String FilePath String+ | DistPoint (Maybe String) FilePath+ | GlobSyntaxError String String+ | RecursiveGlobInRoot String FilePath+ | InvalidOnWin [FilePath]+ | FilePathTooLong FilePath+ | FilePathNameTooLong FilePath+ | FilePathSplitTooLong FilePath+ | FilePathEmpty+ | CVTestSuite+ | CVDefaultLanguage+ | CVDefaultLanguageComponent+ | CVDefaultLanguageComponentSoft+ | CVExtraDocFiles+ | CVMultiLib+ | CVReexported+ | CVMixins+ | CVExtraFrameworkDirs+ | CVDefaultExtensions+ | CVExtensionsDeprecated+ | CVSources+ | CVExtraDynamic [[String]]+ | CVVirtualModules+ | CVSourceRepository+ | CVExtensions CabalSpecVersion [Extension]+ | CVCustomSetup+ | CVExpliticDepsCustomSetup+ | CVAutogenPaths+ | CVAutogenPackageInfo+ | CVAutogenPackageInfoGuard+ | GlobNoMatch String String+ | GlobExactMatch String String FilePath+ | GlobNoDir String String FilePath+ | UnknownOS [String]+ | UnknownArch [String]+ | UnknownCompiler [String]+ | BaseNoUpperBounds+ | MissingUpperBounds CEType [String]+ | LEUpperBounds CEType [String]+ | TrailingZeroUpperBounds CEType [String]+ | GTLowerBounds CEType [String]+ | SuspiciousFlagName [String]+ | DeclaredUsedFlags (Set.Set FlagName) (Set.Set FlagName)+ | NonASCIICustomField [String]+ | RebindableClashPaths+ | RebindableClashPackageInfo+ | WErrorUnneeded String+ | JUnneeded String+ | FDeferTypeErrorsUnneeded String+ | DynamicUnneeded String+ | ProfilingUnneeded String+ | UpperBoundSetup String+ | DuplicateModule String [ModuleName]+ | PotentialDupModule String [ModuleName]+ | BOMStart FilePath+ | NotPackageName FilePath String+ | NoDesc+ | MultiDesc [String]+ | UnknownFile String (RelativePath Pkg File)+ | MissingSetupFile+ | MissingConfigureScript+ | UnknownDirectory String FilePath+ | MissingSourceControl+ | MissingExpectedDocFiles Bool [FilePath]+ | WrongFieldForExpectedDocFiles Bool String [FilePath]+ deriving (Eq, Ord, Show)++-- TODO Some checks have a constructor in list form+-- (e.g. `SomeWarn [n]`), CheckM m () correctly catches warnings in+-- different stanzas in different checks (so it is not one soup).+--+-- Ideally [SomeWar [a], SomeWar [b]] would be translated into+-- SomeWar [a,b] in the few cases where it is appropriate for UX+-- and left separated otherwise.+-- To achieve this the Writer part of CheckM could be modified+-- to be a ad hoc monoid.++-- Convenience.+extractCheckExplanation :: PackageCheck -> CheckExplanation+extractCheckExplanation (PackageBuildImpossible e) = e+extractCheckExplanation (PackageBuildWarning e) = e+extractCheckExplanation (PackageDistSuspicious e) = e+extractCheckExplanation (PackageDistSuspiciousWarn e) = e+extractCheckExplanation (PackageDistInexcusable e) = e++-- | Identifier for the specific 'CheckExplanation'. This ensures `--ignore`+-- can output a warning on unrecognised values.+-- ☞ N.B.: should be kept in sync with 'CheckExplanation'.+data CheckExplanationID+ = CIParseWarning+ | CINoNameField+ | CINoVersionField+ | CINoTarget+ | CIUnnamedInternal+ | CIDuplicateSections+ | CIIllegalLibraryName+ | CINoModulesExposed+ | CISignaturesCabal2+ | CIAutogenNotExposed+ | CIAutogenIncludesNotIncluded+ | CINoMainIs+ | CINoHsLhsMain+ | CIMainCCabal1_18+ | CIAutogenNoOther+ | CIAutogenIncludesNotIncludedExe+ | CITestsuiteTypeNotKnown+ | CITestsuiteNotSupported+ | CIBenchmarkTypeNotKnown+ | CIBenchmarkNotSupported+ | CINoHsLhsMainBench+ | CIInvalidNameWin+ | CIZPrefix+ | CINoBuildType+ | CINoCustomSetup+ | CIUnknownCompilers+ | CIUnknownLanguages+ | CIUnknownExtensions+ | CILanguagesAsExtension+ | CIDeprecatedExtensions+ | CIMissingFieldCategory+ | CIMissingFieldMaintainer+ | CIMissingFieldSynopsis+ | CIMissingFieldDescription+ | CIMissingFieldSynOrDesc+ | CISynopsisTooLong+ | CIShortDesc+ | CIInvalidTestWith+ | CIImpossibleInternalDep+ | CIImpossibleInternalExe+ | CIMissingInternalExe+ | CINONELicense+ | CINoLicense+ | CIAllRightsReservedLicense+ | CILicenseMessParse+ | CIUnrecognisedLicense+ | CIUncommonBSD4+ | CIUnknownLicenseVersion+ | CINoLicenseFile+ | CIUnrecognisedSourceRepo+ | CIMissingType+ | CIMissingLocation+ | CIGitProtocol+ | CIMissingModule+ | CIMissingTag+ | CISubdirRelPath+ | CISubdirGoodRelPath+ | CIOptFasm+ | CIOptHpc+ | CIOptProf+ | CIOptO+ | CIOptHide+ | CIOptMake+ | CIOptONot+ | CIOptOOne+ | CIOptOTwo+ | CIOptSplitSections+ | CIOptSplitObjs+ | CIOptWls+ | CIOptExts+ | CIOptRts+ | CIOptWithRts+ | CICOptONumber+ | CICOptCPP+ | CIOptJSPP+ | CIOptAlternatives+ | CIRelativeOutside+ | CIAbsolutePath+ | CIBadRelativePath+ | CIDistPoint+ | CIGlobSyntaxError+ | CIRecursiveGlobInRoot+ | CIInvalidOnWin+ | CIFilePathTooLong+ | CIFilePathNameTooLong+ | CIFilePathSplitTooLong+ | CIFilePathEmpty+ | CICVTestSuite+ | CICVDefaultLanguage+ | CICVDefaultLanguageComponent+ | CICVDefaultLanguageComponentSoft+ | CICVExtraDocFiles+ | CICVMultiLib+ | CICVReexported+ | CICVMixins+ | CICVExtraFrameworkDirs+ | CICVDefaultExtensions+ | CICVExtensionsDeprecated+ | CICVSources+ | CICVExtraDynamic+ | CICVVirtualModules+ | CICVSourceRepository+ | CICVExtensions+ | CICVCustomSetup+ | CICVExpliticDepsCustomSetup+ | CICVAutogenPaths+ | CICVAutogenPackageInfo+ | CICVAutogenPackageInfoGuard+ | CIGlobNoMatch+ | CIGlobExactMatch+ | CIGlobNoDir+ | CIUnknownOS+ | CIUnknownArch+ | CIUnknownCompiler+ | CIBaseNoUpperBounds+ | CIMissingUpperBounds+ | CILEUpperBounds+ | CITrailingZeroUpperBounds+ | CIGTLowerBounds+ | CISuspiciousFlagName+ | CIDeclaredUsedFlags+ | CINonASCIICustomField+ | CIRebindableClashPaths+ | CIRebindableClashPackageInfo+ | CIWErrorUnneeded+ | CIJUnneeded+ | CIFDeferTypeErrorsUnneeded+ | CIDynamicUnneeded+ | CIProfilingUnneeded+ | CIUpperBoundSetup+ | CIDuplicateModule+ | CIPotentialDupModule+ | CIBOMStart+ | CINotPackageName+ | CINoDesc+ | CIMultiDesc+ | CIUnknownFile+ | CIMissingSetupFile+ | CIMissingConfigureScript+ | CIUnknownDirectory+ | CIMissingSourceControl+ | CIMissingExpectedDocFiles+ | CIWrongFieldForExpectedDocFiles+ deriving (Eq, Ord, Show, Enum, Bounded)++checkExplanationId :: CheckExplanation -> CheckExplanationID+checkExplanationId (ParseWarning{}) = CIParseWarning+checkExplanationId (NoNameField{}) = CINoNameField+checkExplanationId (NoVersionField{}) = CINoVersionField+checkExplanationId (NoTarget{}) = CINoTarget+checkExplanationId (UnnamedInternal{}) = CIUnnamedInternal+checkExplanationId (DuplicateSections{}) = CIDuplicateSections+checkExplanationId (IllegalLibraryName{}) = CIIllegalLibraryName+checkExplanationId (NoModulesExposed{}) = CINoModulesExposed+checkExplanationId (SignaturesCabal2{}) = CISignaturesCabal2+checkExplanationId (AutogenNotExposed{}) = CIAutogenNotExposed+checkExplanationId (AutogenIncludesNotIncluded{}) = CIAutogenIncludesNotIncluded+checkExplanationId (NoMainIs{}) = CINoMainIs+checkExplanationId (NoHsLhsMain{}) = CINoHsLhsMain+checkExplanationId (MainCCabal1_18{}) = CIMainCCabal1_18+checkExplanationId (AutogenNoOther{}) = CIAutogenNoOther+checkExplanationId (AutogenIncludesNotIncludedExe{}) = CIAutogenIncludesNotIncludedExe+checkExplanationId (TestsuiteTypeNotKnown{}) = CITestsuiteTypeNotKnown+checkExplanationId (TestsuiteNotSupported{}) = CITestsuiteNotSupported+checkExplanationId (BenchmarkTypeNotKnown{}) = CIBenchmarkTypeNotKnown+checkExplanationId (BenchmarkNotSupported{}) = CIBenchmarkNotSupported+checkExplanationId (NoHsLhsMainBench{}) = CINoHsLhsMainBench+checkExplanationId (InvalidNameWin{}) = CIInvalidNameWin+checkExplanationId (ZPrefix{}) = CIZPrefix+checkExplanationId (NoBuildType{}) = CINoBuildType+checkExplanationId (NoCustomSetup{}) = CINoCustomSetup+checkExplanationId (UnknownCompilers{}) = CIUnknownCompilers+checkExplanationId (UnknownLanguages{}) = CIUnknownLanguages+checkExplanationId (UnknownExtensions{}) = CIUnknownExtensions+checkExplanationId (LanguagesAsExtension{}) = CILanguagesAsExtension+checkExplanationId (DeprecatedExtensions{}) = CIDeprecatedExtensions+checkExplanationId (MissingFieldCategory{}) = CIMissingFieldCategory+checkExplanationId (MissingFieldMaintainer{}) = CIMissingFieldMaintainer+checkExplanationId (MissingFieldSynopsis{}) = CIMissingFieldSynopsis+checkExplanationId (MissingFieldDescription{}) = CIMissingFieldDescription+checkExplanationId (MissingFieldSynOrDesc{}) = CIMissingFieldSynOrDesc+checkExplanationId (SynopsisTooLong{}) = CISynopsisTooLong+checkExplanationId (ShortDesc{}) = CIShortDesc+checkExplanationId (InvalidTestWith{}) = CIInvalidTestWith+checkExplanationId (ImpossibleInternalDep{}) = CIImpossibleInternalDep+checkExplanationId (ImpossibleInternalExe{}) = CIImpossibleInternalExe+checkExplanationId (MissingInternalExe{}) = CIMissingInternalExe+checkExplanationId (NONELicense{}) = CINONELicense+checkExplanationId (NoLicense{}) = CINoLicense+checkExplanationId (AllRightsReservedLicense{}) = CIAllRightsReservedLicense+checkExplanationId (LicenseMessParse{}) = CILicenseMessParse+checkExplanationId (UnrecognisedLicense{}) = CIUnrecognisedLicense+checkExplanationId (UncommonBSD4{}) = CIUncommonBSD4+checkExplanationId (UnknownLicenseVersion{}) = CIUnknownLicenseVersion+checkExplanationId (NoLicenseFile{}) = CINoLicenseFile+checkExplanationId (UnrecognisedSourceRepo{}) = CIUnrecognisedSourceRepo+checkExplanationId (MissingType{}) = CIMissingType+checkExplanationId (MissingLocation{}) = CIMissingLocation+checkExplanationId (GitProtocol{}) = CIGitProtocol+checkExplanationId (MissingModule{}) = CIMissingModule+checkExplanationId (MissingTag{}) = CIMissingTag+checkExplanationId (SubdirRelPath{}) = CISubdirRelPath+checkExplanationId (SubdirGoodRelPath{}) = CISubdirGoodRelPath+checkExplanationId (OptFasm{}) = CIOptFasm+checkExplanationId (OptHpc{}) = CIOptHpc+checkExplanationId (OptProf{}) = CIOptProf+checkExplanationId (OptO{}) = CIOptO+checkExplanationId (OptHide{}) = CIOptHide+checkExplanationId (OptMake{}) = CIOptMake+checkExplanationId (OptONot{}) = CIOptONot+checkExplanationId (OptOOne{}) = CIOptOOne+checkExplanationId (OptOTwo{}) = CIOptOTwo+checkExplanationId (OptSplitSections{}) = CIOptSplitSections+checkExplanationId (OptSplitObjs{}) = CIOptSplitObjs+checkExplanationId (OptWls{}) = CIOptWls+checkExplanationId (OptExts{}) = CIOptExts+checkExplanationId (OptRts{}) = CIOptRts+checkExplanationId (OptWithRts{}) = CIOptWithRts+checkExplanationId (COptONumber{}) = CICOptONumber+checkExplanationId (COptCPP{}) = CICOptCPP+checkExplanationId (OptJSPP{}) = CIOptJSPP+checkExplanationId (OptAlternatives{}) = CIOptAlternatives+checkExplanationId (RelativeOutside{}) = CIRelativeOutside+checkExplanationId (AbsolutePath{}) = CIAbsolutePath+checkExplanationId (BadRelativePath{}) = CIBadRelativePath+checkExplanationId (DistPoint{}) = CIDistPoint+checkExplanationId (GlobSyntaxError{}) = CIGlobSyntaxError+checkExplanationId (RecursiveGlobInRoot{}) = CIRecursiveGlobInRoot+checkExplanationId (InvalidOnWin{}) = CIInvalidOnWin+checkExplanationId (FilePathTooLong{}) = CIFilePathTooLong+checkExplanationId (FilePathNameTooLong{}) = CIFilePathNameTooLong+checkExplanationId (FilePathSplitTooLong{}) = CIFilePathSplitTooLong+checkExplanationId (FilePathEmpty{}) = CIFilePathEmpty+checkExplanationId (CVTestSuite{}) = CICVTestSuite+checkExplanationId (CVDefaultLanguage{}) = CICVDefaultLanguage+checkExplanationId (CVDefaultLanguageComponent{}) = CICVDefaultLanguageComponent+checkExplanationId (CVDefaultLanguageComponentSoft{}) = CICVDefaultLanguageComponentSoft+checkExplanationId (CVExtraDocFiles{}) = CICVExtraDocFiles+checkExplanationId (CVMultiLib{}) = CICVMultiLib+checkExplanationId (CVReexported{}) = CICVReexported+checkExplanationId (CVMixins{}) = CICVMixins+checkExplanationId (CVExtraFrameworkDirs{}) = CICVExtraFrameworkDirs+checkExplanationId (CVDefaultExtensions{}) = CICVDefaultExtensions+checkExplanationId (CVExtensionsDeprecated{}) = CICVExtensionsDeprecated+checkExplanationId (CVSources{}) = CICVSources+checkExplanationId (CVExtraDynamic{}) = CICVExtraDynamic+checkExplanationId (CVVirtualModules{}) = CICVVirtualModules+checkExplanationId (CVSourceRepository{}) = CICVSourceRepository+checkExplanationId (CVExtensions{}) = CICVExtensions+checkExplanationId (CVCustomSetup{}) = CICVCustomSetup+checkExplanationId (CVExpliticDepsCustomSetup{}) = CICVExpliticDepsCustomSetup+checkExplanationId (CVAutogenPaths{}) = CICVAutogenPaths+checkExplanationId (CVAutogenPackageInfo{}) = CICVAutogenPackageInfo+checkExplanationId (CVAutogenPackageInfoGuard{}) = CICVAutogenPackageInfoGuard+checkExplanationId (GlobNoMatch{}) = CIGlobNoMatch+checkExplanationId (GlobExactMatch{}) = CIGlobExactMatch+checkExplanationId (GlobNoDir{}) = CIGlobNoDir+checkExplanationId (UnknownOS{}) = CIUnknownOS+checkExplanationId (UnknownArch{}) = CIUnknownArch+checkExplanationId (UnknownCompiler{}) = CIUnknownCompiler+checkExplanationId (BaseNoUpperBounds{}) = CIBaseNoUpperBounds+checkExplanationId (MissingUpperBounds{}) = CIMissingUpperBounds+checkExplanationId (LEUpperBounds{}) = CILEUpperBounds+checkExplanationId (TrailingZeroUpperBounds{}) = CITrailingZeroUpperBounds+checkExplanationId (GTLowerBounds{}) = CIGTLowerBounds+checkExplanationId (SuspiciousFlagName{}) = CISuspiciousFlagName+checkExplanationId (DeclaredUsedFlags{}) = CIDeclaredUsedFlags+checkExplanationId (NonASCIICustomField{}) = CINonASCIICustomField+checkExplanationId (RebindableClashPaths{}) = CIRebindableClashPaths+checkExplanationId (RebindableClashPackageInfo{}) = CIRebindableClashPackageInfo+checkExplanationId (WErrorUnneeded{}) = CIWErrorUnneeded+checkExplanationId (JUnneeded{}) = CIJUnneeded+checkExplanationId (FDeferTypeErrorsUnneeded{}) = CIFDeferTypeErrorsUnneeded+checkExplanationId (DynamicUnneeded{}) = CIDynamicUnneeded+checkExplanationId (ProfilingUnneeded{}) = CIProfilingUnneeded+checkExplanationId (UpperBoundSetup{}) = CIUpperBoundSetup+checkExplanationId (DuplicateModule{}) = CIDuplicateModule+checkExplanationId (PotentialDupModule{}) = CIPotentialDupModule+checkExplanationId (BOMStart{}) = CIBOMStart+checkExplanationId (NotPackageName{}) = CINotPackageName+checkExplanationId (NoDesc{}) = CINoDesc+checkExplanationId (MultiDesc{}) = CIMultiDesc+checkExplanationId (UnknownFile{}) = CIUnknownFile+checkExplanationId (MissingSetupFile{}) = CIMissingSetupFile+checkExplanationId (MissingConfigureScript{}) = CIMissingConfigureScript+checkExplanationId (UnknownDirectory{}) = CIUnknownDirectory+checkExplanationId (MissingSourceControl{}) = CIMissingSourceControl+checkExplanationId (MissingExpectedDocFiles{}) = CIMissingExpectedDocFiles+checkExplanationId (WrongFieldForExpectedDocFiles{}) = CIWrongFieldForExpectedDocFiles++type CheckExplanationIDString = String++-- | A one-word identifier for each @CheckExplanation@.+ppCheckExplanationId :: CheckExplanationID -> CheckExplanationIDString+-- NOTE: If you modify anything here, remember to change the documentation+-- in @doc/cabal-commands.rst@!+-- NOTE: These strings will have to satisfy a test that these messages don't+-- have too many dashes:+-- $ cabal run Cabal-tests:unit-tests -- --pattern=Parsimonious+ppCheckExplanationId CIParseWarning = "parser-warning"+ppCheckExplanationId CINoNameField = "no-name-field"+ppCheckExplanationId CINoVersionField = "no-version-field"+ppCheckExplanationId CINoTarget = "no-target"+ppCheckExplanationId CIUnnamedInternal = "unnamed-internal-library"+ppCheckExplanationId CIDuplicateSections = "duplicate-sections"+ppCheckExplanationId CIIllegalLibraryName = "illegal-library-name"+ppCheckExplanationId CINoModulesExposed = "no-modules-exposed"+ppCheckExplanationId CISignaturesCabal2 = "signatures"+ppCheckExplanationId CIAutogenNotExposed = "autogen-not-exposed"+ppCheckExplanationId CIAutogenIncludesNotIncluded = "autogen-not-included"+ppCheckExplanationId CINoMainIs = "no-main-is"+ppCheckExplanationId CINoHsLhsMain = "unknown-extension-main"+ppCheckExplanationId CIMainCCabal1_18 = "c-like-main"+ppCheckExplanationId CIAutogenNoOther = "autogen-other-modules"+ppCheckExplanationId CIAutogenIncludesNotIncludedExe = "autogen-exe"+ppCheckExplanationId CITestsuiteTypeNotKnown = "unknown-testsuite-type"+ppCheckExplanationId CITestsuiteNotSupported = "unsupported-testsuite"+ppCheckExplanationId CIBenchmarkTypeNotKnown = "unknown-bench"+ppCheckExplanationId CIBenchmarkNotSupported = "unsupported-bench"+ppCheckExplanationId CINoHsLhsMainBench = "bench-unknown-extension"+ppCheckExplanationId CIInvalidNameWin = "invalid-name-win"+ppCheckExplanationId CIZPrefix = "reserved-z-prefix"+ppCheckExplanationId CINoBuildType = "no-build-type"+ppCheckExplanationId CINoCustomSetup = "undeclared-custom-setup"+ppCheckExplanationId CIUnknownCompilers = "unknown-compiler-tested"+ppCheckExplanationId CIUnknownLanguages = "unknown-languages"+ppCheckExplanationId CIUnknownExtensions = "unknown-extension"+ppCheckExplanationId CILanguagesAsExtension = "languages-as-extensions"+ppCheckExplanationId CIDeprecatedExtensions = "deprecated-extensions"+ppCheckExplanationId CIMissingFieldCategory = "no-category"+ppCheckExplanationId CIMissingFieldMaintainer = "no-maintainer"+ppCheckExplanationId CIMissingFieldSynopsis = "no-synopsis"+ppCheckExplanationId CIMissingFieldDescription = "no-description"+ppCheckExplanationId CIMissingFieldSynOrDesc = "no-syn-desc"+ppCheckExplanationId CISynopsisTooLong = "long-synopsis"+ppCheckExplanationId CIShortDesc = "short-description"+ppCheckExplanationId CIInvalidTestWith = "invalid-range-tested"+ppCheckExplanationId CIImpossibleInternalDep = "impossible-dep"+ppCheckExplanationId CIImpossibleInternalExe = "impossible-dep-exe"+ppCheckExplanationId CIMissingInternalExe = "no-internal-exe"+ppCheckExplanationId CINONELicense = "license-none"+ppCheckExplanationId CINoLicense = "no-license"+ppCheckExplanationId CIAllRightsReservedLicense = "all-rights-reserved"+ppCheckExplanationId CILicenseMessParse = "license-parse"+ppCheckExplanationId CIUnrecognisedLicense = "unknown-license"+ppCheckExplanationId CIUncommonBSD4 = "bsd4-license"+ppCheckExplanationId CIUnknownLicenseVersion = "unknown-license-version"+ppCheckExplanationId CINoLicenseFile = "no-license-file"+ppCheckExplanationId CIUnrecognisedSourceRepo = "unrecognised-repo-type"+ppCheckExplanationId CIMissingType = "repo-no-type"+ppCheckExplanationId CIMissingLocation = "repo-no-location"+ppCheckExplanationId CIGitProtocol = "git-protocol"+ppCheckExplanationId CIMissingModule = "repo-no-module"+ppCheckExplanationId CIMissingTag = "repo-no-tag"+ppCheckExplanationId CISubdirRelPath = "repo-relative-dir"+ppCheckExplanationId CISubdirGoodRelPath = "repo-malformed-subdir"+ppCheckExplanationId CIOptFasm = "option-fasm"+ppCheckExplanationId CIOptHpc = "option-fhpc"+ppCheckExplanationId CIOptProf = "option-prof"+ppCheckExplanationId CIOptO = "option-o"+ppCheckExplanationId CIOptHide = "option-hide-package"+ppCheckExplanationId CIOptMake = "option-make"+ppCheckExplanationId CIOptONot = "option-optimize"+ppCheckExplanationId CIOptOOne = "option-o1"+ppCheckExplanationId CIOptOTwo = "option-o2"+ppCheckExplanationId CIOptSplitSections = "option-split-section"+ppCheckExplanationId CIOptSplitObjs = "option-split-objs"+ppCheckExplanationId CIOptWls = "option-optl-wl"+ppCheckExplanationId CIOptExts = "use-extension"+ppCheckExplanationId CIOptRts = "option-rtsopts"+ppCheckExplanationId CIOptWithRts = "option-with-rtsopts"+ppCheckExplanationId CICOptONumber = "option-opt-c"+ppCheckExplanationId CICOptCPP = "cpp-options"+ppCheckExplanationId CIOptJSPP = "jspp-options"+ppCheckExplanationId CIOptAlternatives = "misplaced-c-opt"+ppCheckExplanationId CIRelativeOutside = "relative-path-outside"+ppCheckExplanationId CIAbsolutePath = "absolute-path"+ppCheckExplanationId CIBadRelativePath = "malformed-relative-path"+ppCheckExplanationId CIDistPoint = "unreliable-dist-path"+ppCheckExplanationId CIGlobSyntaxError = "glob-syntax-error"+ppCheckExplanationId CIRecursiveGlobInRoot = "recursive-glob"+ppCheckExplanationId CIInvalidOnWin = "invalid-path-win"+ppCheckExplanationId CIFilePathTooLong = "long-path"+ppCheckExplanationId CIFilePathNameTooLong = "long-name"+ppCheckExplanationId CIFilePathSplitTooLong = "name-not-portable"+ppCheckExplanationId CIFilePathEmpty = "empty-path"+ppCheckExplanationId CICVTestSuite = "test-cabal-ver"+ppCheckExplanationId CICVDefaultLanguage = "default-language"+ppCheckExplanationId CICVDefaultLanguageComponent = "no-default-language"+ppCheckExplanationId CICVDefaultLanguageComponentSoft = "add-language"+ppCheckExplanationId CICVExtraDocFiles = "extra-doc-files"+ppCheckExplanationId CICVMultiLib = "multilib"+ppCheckExplanationId CICVReexported = "reexported-modules"+ppCheckExplanationId CICVMixins = "mixins"+ppCheckExplanationId CICVExtraFrameworkDirs = "extra-framework-dirs"+ppCheckExplanationId CICVDefaultExtensions = "default-extensions"+ppCheckExplanationId CICVExtensionsDeprecated = "extensions-field"+ppCheckExplanationId CICVSources = "unsupported-sources"+ppCheckExplanationId CICVExtraDynamic = "extra-dynamic"+ppCheckExplanationId CICVVirtualModules = "virtual-modules"+ppCheckExplanationId CICVSourceRepository = "source-repository"+ppCheckExplanationId CICVExtensions = "incompatible-extension"+ppCheckExplanationId CICVCustomSetup = "no-setup-depends"+ppCheckExplanationId CICVExpliticDepsCustomSetup = "dependencies-setup"+ppCheckExplanationId CICVAutogenPaths = "no-autogen-paths"+ppCheckExplanationId CICVAutogenPackageInfo = "no-autogen-pinfo"+ppCheckExplanationId CICVAutogenPackageInfoGuard = "autogen-guard"+ppCheckExplanationId CIGlobNoMatch = "no-glob-match"+ppCheckExplanationId CIGlobExactMatch = "glob-no-extension"+ppCheckExplanationId CIGlobNoDir = "glob-missing-dir"+ppCheckExplanationId CIUnknownOS = "unknown-os"+ppCheckExplanationId CIUnknownArch = "unknown-arch"+ppCheckExplanationId CIUnknownCompiler = "unknown-compiler"+ppCheckExplanationId CIBaseNoUpperBounds = "missing-bounds-important"+ppCheckExplanationId CIMissingUpperBounds = "missing-upper-bounds"+ppCheckExplanationId CILEUpperBounds = "le-upper-bounds"+ppCheckExplanationId CITrailingZeroUpperBounds = "tz-upper-bounds"+ppCheckExplanationId CIGTLowerBounds = "gt-lower-bounds"+ppCheckExplanationId CISuspiciousFlagName = "suspicious-flag"+ppCheckExplanationId CIDeclaredUsedFlags = "unused-flag"+ppCheckExplanationId CINonASCIICustomField = "non-ascii"+ppCheckExplanationId CIRebindableClashPaths = "rebindable-clash-paths"+ppCheckExplanationId CIRebindableClashPackageInfo = "rebindable-clash-info"+ppCheckExplanationId CIWErrorUnneeded = "werror"+ppCheckExplanationId CIJUnneeded = "unneeded-j"+ppCheckExplanationId CIFDeferTypeErrorsUnneeded = "fdefer-type-errors"+ppCheckExplanationId CIDynamicUnneeded = "debug-flag"+ppCheckExplanationId CIProfilingUnneeded = "fprof-flag"+ppCheckExplanationId CIUpperBoundSetup = "missing-bounds-setup"+ppCheckExplanationId CIDuplicateModule = "duplicate-modules"+ppCheckExplanationId CIPotentialDupModule = "maybe-duplicate-modules"+ppCheckExplanationId CIBOMStart = "bom"+ppCheckExplanationId CINotPackageName = "name-no-match"+ppCheckExplanationId CINoDesc = "no-cabal-file"+ppCheckExplanationId CIMultiDesc = "multiple-cabal-file"+ppCheckExplanationId CIUnknownFile = "unknown-file"+ppCheckExplanationId CIMissingSetupFile = "missing-setup"+ppCheckExplanationId CIMissingConfigureScript = "missing-conf-script"+ppCheckExplanationId CIUnknownDirectory = "unknown-directory"+ppCheckExplanationId CIMissingSourceControl = "no-repository"+ppCheckExplanationId CIMissingExpectedDocFiles = "no-docs"+ppCheckExplanationId CIWrongFieldForExpectedDocFiles = "doc-place"++-- String: the unrecognised 'CheckExplanationIDString' itself.+readExplanationID+ :: CheckExplanationIDString+ -> Either String CheckExplanationID+readExplanationID s = maybe (Left s) Right (lookup s idsDict)+ where+ idsDict :: [(CheckExplanationIDString, CheckExplanationID)]+ idsDict = map (\i -> (ppCheckExplanationId i, i)) [minBound .. maxBound]++-- | Which stanza does `CheckExplanation` refer to?+data CEType+ = CETLibrary LibraryName+ | CETForeignLibrary UnqualComponentName+ | CETExecutable UnqualComponentName+ | CETTest UnqualComponentName+ | CETBenchmark UnqualComponentName+ | CETSetup+ deriving (Eq, Ord, Show)++-- | Pretty printing `CEType`.+ppCET :: CEType -> String+ppCET cet = case cet of+ CETLibrary ln -> showLibraryName ln+ CETForeignLibrary n -> "foreign library" ++ qn n+ CETExecutable n -> "executable" ++ qn n+ CETTest n -> "test suite" ++ qn n+ CETBenchmark n -> "benchmark" ++ qn n+ CETSetup -> "custom-setup"+ where+ qn :: UnqualComponentName -> String+ qn wn = (" " ++) . quote . prettyShow $ wn++-- | Which language are we referring to in our warning message?+data WarnLang = LangC | LangCPlusPlus+ deriving (Eq, Ord, Show)++-- | Pretty printing `WarnLang`.+ppWarnLang :: WarnLang -> String+ppWarnLang LangC = "C"+ppWarnLang LangCPlusPlus = "C++"++-- | Pretty printing `CheckExplanation`.+ppExplanation :: CheckExplanation -> String+ppExplanation (ParseWarning fp pp) = showPWarning fp pp+ppExplanation NoNameField = "No 'name' field."+ppExplanation NoVersionField = "No 'version' field."+ppExplanation NoTarget =+ "No executables, libraries, tests, or benchmarks found. Nothing to do."+ppExplanation UnnamedInternal =+ "Found one or more unnamed internal libraries. Only the non-internal"+ ++ " library can have the same name as the package."+ppExplanation (DuplicateSections duplicateNames) =+ "Duplicate sections: "+ ++ commaSep (map unUnqualComponentName duplicateNames)+ ++ ". The name of every library, executable, test suite,"+ ++ " and benchmark section in the package must be unique."+ppExplanation (IllegalLibraryName pname) =+ "Illegal internal library name "+ ++ prettyShow pname+ ++ ". Internal libraries cannot have the same name as the package."+ ++ " Maybe you wanted a non-internal library?"+ ++ " If so, rewrite the section stanza"+ ++ " from 'library: '"+ ++ prettyShow pname+ ++ "' to 'library'."+ppExplanation (NoModulesExposed lName) =+ showLibraryName lName ++ " does not expose any modules"+ppExplanation SignaturesCabal2 =+ "To use the 'signatures' field the package needs to specify "+ ++ "at least 'cabal-version: 2.0'."+ppExplanation AutogenNotExposed =+ "An 'autogen-module' is neither on 'exposed-modules' nor 'other-modules'."+ppExplanation AutogenIncludesNotIncluded =+ "An include in 'autogen-includes' is neither in 'includes' nor "+ ++ "'install-includes'."+ppExplanation (NoMainIs eName) =+ "No 'main-is' field found for executable " ++ prettyShow eName+ppExplanation NoHsLhsMain =+ "The 'main-is' field must specify a '.hs' or '.lhs' file "+ ++ "(even if it is generated by a preprocessor), "+ ++ "or it may specify a C/C++/obj-C source file."+ppExplanation MainCCabal1_18 =+ "The package uses a C/C++/obj-C source file for the 'main-is' field. "+ ++ "To use this feature you need to specify 'cabal-version: 1.18' or"+ ++ " higher."+ppExplanation (AutogenNoOther ct) =+ "On "+ ++ ppCET ct+ ++ " an 'autogen-module'"+ ++ " is not on 'other-modules'"+ppExplanation AutogenIncludesNotIncludedExe =+ "An include in 'autogen-includes' is not in 'includes'."+ppExplanation (TestsuiteTypeNotKnown tt) =+ quote (prettyShow tt)+ ++ " is not a known type of test suite. "+ ++ "Either remove the 'type' field or use a known type. "+ ++ "The known test suite types are: "+ ++ commaSep (map prettyShow knownTestTypes)+ppExplanation (TestsuiteNotSupported tt) =+ quote (prettyShow tt)+ ++ " is not a supported test suite version. "+ ++ "Either remove the 'type' field or use a known type. "+ ++ "The known test suite types are: "+ ++ commaSep (map prettyShow knownTestTypes)+ppExplanation (BenchmarkTypeNotKnown tt) =+ quote (prettyShow tt)+ ++ " is not a known type of benchmark. "+ ++ "Either remove the 'type' field or use a known type. "+ ++ "The known benchmark types are: "+ ++ commaSep (map prettyShow knownBenchmarkTypes)+ppExplanation (BenchmarkNotSupported tt) =+ quote (prettyShow tt)+ ++ " is not a supported benchmark version. "+ ++ "Either remove the 'type' field or use a known type. "+ ++ "The known benchmark types are: "+ ++ commaSep (map prettyShow knownBenchmarkTypes)+ppExplanation NoHsLhsMainBench =+ "The 'main-is' field must specify a '.hs' or '.lhs' file "+ ++ "(even if it is generated by a preprocessor)."+ppExplanation (InvalidNameWin pkg) =+ "The package name '"+ ++ prettyShow pkg+ ++ "' is "+ ++ "invalid on Windows. Many tools need to convert package names to "+ ++ "file names, so using this name would cause problems."+ppExplanation ZPrefix =+ "Package names with the prefix 'z-' are reserved by Cabal and "+ ++ "cannot be used."+ppExplanation NoBuildType =+ "No 'build-type' specified. If you do not need a custom Setup.hs or "+ ++ "./configure script then use 'build-type: Simple'."+ppExplanation NoCustomSetup =+ "Ignoring the 'custom-setup' section because the 'build-type' is "+ ++ "not 'Custom'. Use 'build-type: Custom' if you need to use a "+ ++ "custom Setup.hs script."+ppExplanation (UnknownCompilers unknownCompilers) =+ "Unknown compiler "+ ++ commaSep (map quote unknownCompilers)+ ++ " in 'tested-with' field."+ppExplanation (UnknownLanguages unknownLanguages) =+ "Unknown languages: " ++ commaSep unknownLanguages+ppExplanation (UnknownExtensions unknownExtensions) =+ "Unknown extensions: " ++ commaSep unknownExtensions+ppExplanation (LanguagesAsExtension languagesUsedAsExtensions) =+ "Languages listed as extensions: "+ ++ commaSep languagesUsedAsExtensions+ ++ ". Languages must be specified in either the 'default-language' "+ ++ " or the 'other-languages' field."+ppExplanation (DeprecatedExtensions ourDeprecatedExtensions) =+ "Deprecated extensions: "+ ++ commaSep (map (quote . prettyShow . fst) ourDeprecatedExtensions)+ ++ ". "+ ++ unwords+ [ "Instead of '"+ ++ prettyShow ext+ ++ "' use '"+ ++ prettyShow replacement+ ++ "'."+ | (ext, Just replacement) <- ourDeprecatedExtensions+ ]+ppExplanation MissingFieldCategory = "No 'category' field."+ppExplanation MissingFieldMaintainer = "No 'maintainer' field."+ppExplanation MissingFieldSynopsis = "No 'synopsis' field."+ppExplanation MissingFieldDescription = "No 'description' field."+ppExplanation MissingFieldSynOrDesc = "No 'synopsis' or 'description' field."+ppExplanation SynopsisTooLong =+ "The 'synopsis' field is rather long (max 80 chars is recommended)."+ppExplanation ShortDesc =+ "The 'description' field should be longer than the 'synopsis' field. "+ ++ "It's useful to provide an informative 'description' to allow "+ ++ "Haskell programmers who have never heard about your package to "+ ++ "understand the purpose of your package. "+ ++ "The 'description' field content is typically shown by tooling "+ ++ "(e.g. 'cabal info', Haddock, Hackage) below the 'synopsis' which "+ ++ "serves as a headline. "+ ++ "Please refer to <https://cabal.readthedocs.io/en/stable/"+ ++ "cabal-package.html#package-properties> for more details."+ppExplanation (InvalidTestWith testedWithImpossibleRanges) =+ "Invalid 'tested-with' version range: "+ ++ commaSep (map prettyShow testedWithImpossibleRanges)+ ++ ". To indicate that you have tested a package with multiple "+ ++ "different versions of the same compiler use multiple entries, "+ ++ "for example 'tested-with: GHC==6.10.4, GHC==6.12.3' and not "+ ++ "'tested-with: GHC==6.10.4 && ==6.12.3'."+ppExplanation (ImpossibleInternalDep depInternalLibWithImpossibleVersion) =+ "The package has an impossible version range for a dependency on an "+ ++ "internal library: "+ ++ commaSep (map prettyShow depInternalLibWithImpossibleVersion)+ ++ ". This version range does not include the current package, and must "+ ++ "be removed as the current package's library will always be used."+ppExplanation (ImpossibleInternalExe depInternalExecWithImpossibleVersion) =+ "The package has an impossible version range for a dependency on an "+ ++ "internal executable: "+ ++ commaSep (map prettyShow depInternalExecWithImpossibleVersion)+ ++ ". This version range does not include the current package, and must "+ ++ "be removed as the current package's executable will always be used."+ppExplanation (MissingInternalExe depInternalExeWithImpossibleVersion) =+ "The package depends on a missing internal executable: "+ ++ commaSep (map prettyShow depInternalExeWithImpossibleVersion)+ppExplanation NONELicense = "The 'license' field is missing or is NONE."+ppExplanation NoLicense = "The 'license' field is missing."+ppExplanation AllRightsReservedLicense =+ "The 'license' is AllRightsReserved. Is that really what you want?"+ppExplanation (LicenseMessParse lic) =+ "Unfortunately the license "+ ++ quote (prettyShow lic)+ ++ " messes up the parser in earlier Cabal versions so you need to "+ ++ "specify 'cabal-version: >= 1.4'. Alternatively if you require "+ ++ "compatibility with earlier Cabal versions then use 'OtherLicense'."+ppExplanation (UnrecognisedLicense l) =+ quote ("license: " ++ l)+ ++ " is not a recognised license. The "+ ++ "known licenses are: "+ ++ commaSep (map prettyShow knownLicenses)+ppExplanation UncommonBSD4 =+ "Using 'license: BSD4' is almost always a misunderstanding. 'BSD4' "+ ++ "refers to the old 4-clause BSD license with the advertising "+ ++ "clause. 'BSD3' refers the new 3-clause BSD license."+ppExplanation (UnknownLicenseVersion lic known) =+ "'license: "+ ++ prettyShow lic+ ++ "' is not a known "+ ++ "version of that license. The known versions are "+ ++ commaSep (map prettyShow known)+ ++ ". If this is not a mistake and you think it should be a known "+ ++ "version then please file a ticket."+ppExplanation NoLicenseFile = "A 'license-file' is not specified."+ppExplanation (UnrecognisedSourceRepo kind) =+ quote kind+ ++ " is not a recognised kind of source-repository. "+ ++ "The repo kind is usually 'head' or 'this'"+ppExplanation MissingType =+ "The source-repository 'type' is a required field."+ppExplanation MissingLocation =+ "The source-repository 'location' is a required field."+ppExplanation GitProtocol =+ "Cloning over git:// might lead to an arbitrary code execution "+ ++ "vulnerability. Furthermore, popular forges like GitHub do "+ ++ "not support it. Use https:// or ssh:// instead."+ppExplanation MissingModule =+ "For a CVS source-repository, the 'module' is a required field."+ppExplanation MissingTag =+ "For the 'this' kind of source-repository, the 'tag' is a required "+ ++ "field. It should specify the tag corresponding to this version "+ ++ "or release of the package."+ppExplanation SubdirRelPath =+ "The 'subdir' field of a source-repository must be a relative path."+ppExplanation (SubdirGoodRelPath err) =+ "The 'subdir' field of a source-repository is not a good relative path: "+ ++ show err+ppExplanation (OptFasm fieldName) =+ "'"+ ++ fieldName+ ++ ": -fasm' is unnecessary and will not work on CPU "+ ++ "architectures other than x86, x86-64, ppc or sparc."+ppExplanation (OptHpc fieldName) =+ "'"+ ++ fieldName+ ++ ": -fhpc' is not necessary. Use the configure flag "+ ++ " --enable-coverage instead."+ppExplanation (OptProf fieldName) =+ "'"+ ++ fieldName+ ++ ": -prof' is not necessary and will lead to problems "+ ++ "when used on a library. Use the configure flag "+ ++ "--enable-library-profiling and/or --enable-profiling."+ppExplanation (OptO fieldName) =+ "'"+ ++ fieldName+ ++ ": -o' is not needed. "+ ++ "The output files are named automatically."+ppExplanation (OptHide fieldName) =+ "'"+ ++ fieldName+ ++ ": -hide-package' is never needed. "+ ++ "Cabal hides all packages."+ppExplanation (OptMake fieldName) =+ "'"+ ++ fieldName+ ++ ": --make' is never needed. Cabal uses this automatically."+ppExplanation (OptONot fieldName) =+ "'"+ ++ fieldName+ ++ ": -O0' is not needed. "+ ++ "Use the --disable-optimization configure flag."+ppExplanation (OptOOne fieldName) =+ "'"+ ++ fieldName+ ++ ": -O' is not needed. "+ ++ "Cabal automatically adds the '-O' flag. "+ ++ "Setting it yourself interferes with the --disable-optimization flag."+ppExplanation (OptOTwo fieldName) =+ "'"+ ++ fieldName+ ++ ": -O2' is rarely needed. "+ ++ "Check that it is giving a real benefit "+ ++ "and not just imposing longer compile times on your users."+ppExplanation (OptSplitSections fieldName) =+ "'"+ ++ fieldName+ ++ ": -split-sections' is not needed. "+ ++ "Use the --enable-split-sections configure flag."+ppExplanation (OptSplitObjs fieldName) =+ "'"+ ++ fieldName+ ++ ": -split-objs' is not needed. "+ ++ "Use the --enable-split-objs configure flag."+ppExplanation (OptWls fieldName) =+ "'"+ ++ fieldName+ ++ ": -optl-Wl,-s' is not needed and is not portable to"+ ++ " all operating systems. Cabal 1.4 and later automatically strip"+ ++ " executables. Cabal also has a flag --disable-executable-stripping"+ ++ " which is necessary when building packages for some Linux"+ ++ " distributions and using '-optl-Wl,-s' prevents that from working."+ppExplanation (OptExts fieldName) =+ "Instead of '"+ ++ fieldName+ ++ ": -fglasgow-exts' it is preferable to use "+ ++ "the 'extensions' field."+ppExplanation (OptRts fieldName) =+ "'"+ ++ fieldName+ ++ ": -rtsopts' has no effect for libraries. It should "+ ++ "only be used for executables."+ppExplanation (OptWithRts fieldName) =+ "'"+ ++ fieldName+ ++ ": -with-rtsopts' has no effect for libraries. It "+ ++ "should only be used for executables."+ppExplanation (COptONumber prefix label) =+ "'"+ ++ prefix+ ++ ": -O[n]' is generally not needed. When building with "+ ++ " optimisations Cabal automatically adds '-O2' for "+ ++ ppWarnLang label+ ++ " code. Setting it yourself interferes with the"+ ++ " --disable-optimization flag."+ppExplanation (COptCPP opt) =+ "'cpp-options: " ++ opt ++ "' is not a portable C-preprocessor flag."+ppExplanation (OptJSPP opt) =+ "'jspp-options: " ++ opt ++ "' is not a portable JavaScript-preprocessor flag."+ppExplanation (OptAlternatives badField goodField flags) =+ "Instead of "+ ++ quote (badField ++ ": " ++ unwords badFlags)+ ++ " use "+ ++ quote (goodField ++ ": " ++ unwords goodFlags)+ where+ (badFlags, goodFlags) = unzip flags+ppExplanation (RelativeOutside field path) =+ quote (field ++ ": " ++ path)+ ++ " is a relative path outside of the source tree. "+ ++ "This will not work when generating a tarball with 'sdist'."+ppExplanation (AbsolutePath field path) =+ quote (field ++ ": " ++ path)+ ++ " specifies an absolute path, but the "+ ++ quote field+ ++ " field must use relative paths."+ppExplanation (BadRelativePath field path err) =+ quote (field ++ ": " ++ path)+ ++ " is not a good relative path: "+ ++ show err+ppExplanation (DistPoint mfield path) =+ incipit+ ++ " points inside the 'dist' "+ ++ "directory. This is not reliable because the location of this "+ ++ "directory is configurable by the user (or package manager). In "+ ++ "addition, the layout of the 'dist' directory is subject to change "+ ++ "in future versions of Cabal."+ where+ -- mfiled Nothing -> the path is inside `ghc-options`+ incipit =+ maybe+ ("'ghc-options' path " ++ quote path)+ (\field -> quote (field ++ ": " ++ path))+ mfield+ppExplanation (GlobSyntaxError field expl) =+ "In the '" ++ field ++ "' field: " ++ expl+ppExplanation (RecursiveGlobInRoot field glob) =+ "In the '"+ ++ field+ ++ "': glob '"+ ++ glob+ ++ "' starts at project root directory, this might "+ ++ "include `.git/`, ``dist-newstyle/``, or other large directories!"+ppExplanation (InvalidOnWin paths) =+ "The "+ ++ quotes paths+ ++ " invalid on Windows, which "+ ++ "would cause portability problems for this package. Windows file "+ ++ "names cannot contain any of the characters \":*?<>|\", and there "+ ++ "are a few reserved names including \"aux\", \"nul\", \"con\", "+ ++ "\"prn\", \"com{1-9}\", \"lpt{1-9}\" and \"clock$\"."+ where+ quotes [failed] = "path " ++ quote failed ++ " is"+ quotes failed =+ "paths "+ ++ commaSep (map quote failed)+ ++ " are"+ppExplanation (FilePathTooLong path) =+ "The following file name is too long to store in a portable POSIX "+ ++ "format tar archive. The maximum length is 255 ASCII characters.\n"+ ++ "The file in question is:\n "+ ++ path+ppExplanation (FilePathNameTooLong path) =+ "The following file name is too long to store in a portable POSIX "+ ++ "format tar archive. The maximum length for the name part (including "+ ++ "extension) is 100 ASCII characters. The maximum length for any "+ ++ "individual directory component is 155.\n"+ ++ "The file in question is:\n "+ ++ path+ppExplanation (FilePathSplitTooLong path) =+ "The following file name is too long to store in a portable POSIX "+ ++ "format tar archive. While the total length is less than 255 ASCII "+ ++ "characters, there are unfortunately further restrictions. It has to "+ ++ "be possible to split the file path on a directory separator into "+ ++ "two parts such that the first part fits in 155 characters or less "+ ++ "and the second part fits in 100 characters or less. Basically you "+ ++ "have to make the file name or directory names shorter, or you could "+ ++ "split a long directory name into nested subdirectories with shorter "+ ++ "names.\nThe file in question is:\n "+ ++ path+ppExplanation FilePathEmpty =+ "Encountered a file with an empty name, something is very wrong! "+ ++ "Files with an empty name cannot be stored in a tar archive or in "+ ++ "standard file systems."+ppExplanation CVTestSuite =+ "The 'test-suite' section is new in Cabal 1.10. "+ ++ "Unfortunately it messes up the parser in older Cabal versions "+ ++ "so you must specify at least 'cabal-version: >= 1.8', but note "+ ++ "that only Cabal 1.10 and later can actually run such test suites."+ppExplanation CVDefaultLanguage =+ "To use the 'default-language' field the package needs to specify "+ ++ "at least 'cabal-version: >= 1.10'."+ppExplanation CVDefaultLanguageComponent =+ "Packages using 'cabal-version: >= 1.10' and before 'cabal-version: 3.4' "+ ++ "must specify the 'default-language' field for each component (e.g. "+ ++ "Haskell98 or Haskell2010). If a component uses different languages "+ ++ "in different modules then list the other ones in the "+ ++ "'other-languages' field."+ppExplanation CVDefaultLanguageComponentSoft =+ "Without `default-language`, cabal will default to Haskell98, which is "+ ++ "probably not what you want. Please add `default-language` to all "+ ++ "targets."+ppExplanation CVExtraDocFiles =+ "To use the 'extra-doc-files' field the package needs to specify "+ ++ "'cabal-version: 1.18' or higher."+ppExplanation CVMultiLib =+ "To use multiple 'library' sections or a named library section "+ ++ "the package needs to specify at least 'cabal-version: 2.0'."+ppExplanation CVReexported =+ "To use the 'reexported-module' field the package needs to specify "+ ++ "'cabal-version: 1.22' or higher."+ppExplanation CVMixins =+ "To use the 'mixins' field the package needs to specify "+ ++ "at least 'cabal-version: 2.0'."+ppExplanation CVExtraFrameworkDirs =+ "To use the 'extra-framework-dirs' field the package needs to specify"+ ++ " 'cabal-version: 1.24' or higher."+ppExplanation CVDefaultExtensions =+ "To use the 'default-extensions' field the package needs to specify "+ ++ "at least 'cabal-version: >= 1.10'."+ppExplanation CVExtensionsDeprecated =+ "For packages using 'cabal-version: >= 1.10' the 'extensions' "+ ++ "field is deprecated. The new 'default-extensions' field lists "+ ++ "extensions that are used in all modules in the component, while "+ ++ "the 'other-extensions' field lists extensions that are used in "+ ++ "some modules, e.g. via the {-# LANGUAGE #-} pragma."+ppExplanation CVSources =+ "The use of 'asm-sources', 'cmm-sources', 'extra-bundled-libraries' "+ ++ " and 'extra-library-flavours' requires the package "+ ++ " to specify at least 'cabal-version: 3.0'."+ppExplanation (CVExtraDynamic flavs) =+ "The use of 'extra-dynamic-library-flavours' requires the package "+ ++ " to specify at least 'cabal-version: 3.0'. The flavours are: "+ ++ commaSep (concat flavs)+ppExplanation CVVirtualModules =+ "The use of 'virtual-modules' requires the package "+ ++ " to specify at least 'cabal-version: 2.2'."+ppExplanation CVSourceRepository =+ "The 'source-repository' section is new in Cabal 1.6. "+ ++ "Unfortunately it messes up the parser in earlier Cabal versions "+ ++ "so you need to specify 'cabal-version: >= 1.6'."+ppExplanation (CVExtensions version extCab12) =+ "Unfortunately the language extensions "+ ++ commaSep (map (quote . prettyShow) extCab12)+ ++ " break the parser in earlier Cabal versions so you need to "+ ++ "specify 'cabal-version: >= "+ ++ showCabalSpecVersion version+ ++ "'. Alternatively if you require compatibility with earlier "+ ++ "Cabal versions then you may be able to use an equivalent "+ ++ "compiler-specific flag."+ppExplanation CVCustomSetup =+ "Packages using 'cabal-version: 1.24' or higher with 'build-type: Custom' "+ ++ "must use a 'custom-setup' section with a 'setup-depends' field "+ ++ "that specifies the dependencies of the Setup.hs script itself. "+ ++ "The 'setup-depends' field uses the same syntax as 'build-depends', "+ ++ "so a simple example would be 'setup-depends: base, Cabal'."+ppExplanation CVExpliticDepsCustomSetup =+ "From version 1.24 cabal supports specifying explicit dependencies "+ ++ "for Custom setup scripts. Consider using 'cabal-version: 1.24' or "+ ++ "higher and adding a 'custom-setup' section with a 'setup-depends' "+ ++ "field that specifies the dependencies of the Setup.hs script "+ ++ "itself. The 'setup-depends' field uses the same syntax as "+ ++ "'build-depends', so a simple example would be 'setup-depends: base, "+ ++ "Cabal'."+ppExplanation CVAutogenPaths =+ "Packages using 'cabal-version: 2.0' and the autogenerated "+ ++ "module Paths_* must include it also on the 'autogen-modules' field "+ ++ "besides 'exposed-modules' and 'other-modules'. This specifies that "+ ++ "the module does not come with the package and is generated on "+ ++ "setup. Modules built with a custom Setup.hs script also go here "+ ++ "to ensure that commands like sdist don't fail."+ppExplanation CVAutogenPackageInfo =+ "Packages using 'cabal-version: 2.0' and the autogenerated "+ ++ "module PackageInfo_* must include it in 'autogen-modules' as well as"+ ++ " 'exposed-modules' and 'other-modules'. This specifies that "+ ++ "the module does not come with the package and is generated on "+ ++ "setup. Modules built with a custom Setup.hs script also go here "+ ++ "to ensure that commands like sdist don't fail."+ppExplanation CVAutogenPackageInfoGuard =+ "To use the autogenerated module PackageInfo_* you need to specify "+ ++ "`cabal-version: 3.12` or higher."+ppExplanation (GlobNoMatch field glob) =+ "In '"+ ++ field+ ++ "': the pattern '"+ ++ glob+ ++ "' does not"+ ++ " match any files."+ppExplanation (GlobExactMatch field glob file) =+ "In '"+ ++ field+ ++ "': the pattern '"+ ++ glob+ ++ "' does not"+ ++ " match the file '"+ ++ file+ ++ "' because the extensions do not"+ ++ " exactly match (e.g., foo.en.html does not exactly match *.html)."+ ++ " To enable looser suffix-only matching, set 'cabal-version: 2.4' or"+ ++ " higher."+ppExplanation (GlobNoDir field glob dir) =+ "In '"+ ++ field+ ++ "': the pattern '"+ ++ glob+ ++ "' attempts to"+ ++ " match files in the directory '"+ ++ dir+ ++ "', but there is no"+ ++ " directory by that name."+ppExplanation (UnknownOS unknownOSs) =+ "Unknown operating system name " ++ commaSep (map quote unknownOSs)+ppExplanation (UnknownArch unknownArches) =+ "Unknown architecture name " ++ commaSep (map quote unknownArches)+ppExplanation (UnknownCompiler unknownImpls) =+ "Unknown compiler name " ++ commaSep (map quote unknownImpls)+ppExplanation BaseNoUpperBounds =+ "The dependency 'build-depends: base' does not specify an upper "+ ++ "bound on the version number. Each major release of the 'base' "+ ++ "package changes the API in various ways and most packages will "+ ++ "need some changes to compile with it. The recommended practice "+ ++ "is to specify an upper bound on the version of the 'base' "+ ++ "package. This ensures your package will continue to build when a "+ ++ "new major version of the 'base' package is released. If you are "+ ++ "not sure what upper bound to use then use the next major "+ ++ "version. For example if you have tested your package with 'base' "+ ++ "version 4.5 and 4.6 then use 'build-depends: base >= 4.5 && < 4.7'."+ppExplanation (MissingUpperBounds ct names) =+ "On "+ ++ ppCET ct+ ++ ", "+ ++ "these packages miss upper bounds:"+ ++ listSep names+ ++ "Please add them. There is more information at https://pvp.haskell.org/"+ppExplanation (LEUpperBounds ct names) =+ "On "+ ++ ppCET ct+ ++ ", "+ ++ "these packages have less than or equals (<=) upper bounds:"+ ++ listSep names+ ++ "Please use less than (<) for upper bounds."+ppExplanation (TrailingZeroUpperBounds ct names) =+ "On "+ ++ ppCET ct+ ++ ", "+ ++ "these packages have upper bounds with trailing zeros:"+ ++ listSep names+ ++ "Please avoid trailing zeros for upper bounds."+ppExplanation (GTLowerBounds ct names) =+ "On "+ ++ ppCET ct+ ++ ", "+ ++ "these packages have greater than (>) lower bounds:"+ ++ listSep names+ ++ "Please use greater than or equals (>=) for lower bounds."+ppExplanation (SuspiciousFlagName invalidFlagNames) =+ "Suspicious flag names: "+ ++ unwords invalidFlagNames+ ++ ". "+ ++ "To avoid ambiguity in command line interfaces, a flag shouldn't "+ ++ "start with a dash. Also for better compatibility, flag names "+ ++ "shouldn't contain non-ascii characters."+ppExplanation (DeclaredUsedFlags declared used) =+ "Declared and used flag sets differ: "+ ++ s declared+ ++ " /= "+ ++ s used+ ++ ". "+ where+ s :: Set.Set FlagName -> String+ s = commaSep . map unFlagName . Set.toList+ppExplanation (NonASCIICustomField nonAsciiXFields) =+ "Non ascii custom fields: "+ ++ unwords nonAsciiXFields+ ++ ". "+ ++ "For better compatibility, custom field names "+ ++ "shouldn't contain non-ascii characters."+ppExplanation RebindableClashPaths =+ "Packages using RebindableSyntax with OverloadedStrings or"+ ++ " OverloadedLists in default-extensions, in conjunction with the"+ ++ " autogenerated module Paths_*, are known to cause compile failures"+ ++ " with Cabal < 2.2. To use these default-extensions with a Paths_*"+ ++ " autogen module, specify at least 'cabal-version: 2.2'."+ppExplanation RebindableClashPackageInfo =+ "Packages using RebindableSyntax with OverloadedStrings or"+ ++ " OverloadedLists in default-extensions, in conjunction with the"+ ++ " autogenerated module PackageInfo_*, are known to cause compile failures"+ ++ " with Cabal < 2.2. To use these default-extensions with a PackageInfo_*"+ ++ " autogen module, specify at least 'cabal-version: 2.2'."+ppExplanation (WErrorUnneeded fieldName) =+ addConditionalExp $+ "'"+ ++ fieldName+ ++ ": -Werror' makes the package easy to "+ ++ "break with future GHC versions because new GHC versions often "+ ++ "add new warnings."+ppExplanation (JUnneeded fieldName) =+ addConditionalExp $+ "'"+ ++ fieldName+ ++ ": -j[N]' can make sense for a particular user's setup,"+ ++ " but it is not appropriate for a distributed package."+ppExplanation (FDeferTypeErrorsUnneeded fieldName) =+ addConditionalExp $+ "'"+ ++ fieldName+ ++ ": -fdefer-type-errors' is fine during development "+ ++ "but is not appropriate for a distributed package."+ppExplanation (DynamicUnneeded fieldName) =+ addConditionalExp $+ "'"+ ++ fieldName+ ++ ": -d*' debug flags are not appropriate "+ ++ "for a distributed package."+ppExplanation (ProfilingUnneeded fieldName) =+ addConditionalExp $+ "'"+ ++ fieldName+ ++ ": -fprof*' profiling flags are typically not "+ ++ "appropriate for a distributed library package. These flags are "+ ++ "useful to profile this package, but when profiling other packages "+ ++ "that use this one these flags clutter the profile output with "+ ++ "excessive detail. If you think other packages really want to see "+ ++ "cost centres from this package then use '-fprof-auto-exported' "+ ++ "which puts cost centres only on exported functions."+ppExplanation (UpperBoundSetup nm) =+ "The dependency 'setup-depends: '"+ ++ nm+ ++ "' does not specify an "+ ++ "upper bound on the version number. Each major release of the "+ ++ "'"+ ++ nm+ ++ "' package changes the API in various ways and most "+ ++ "packages will need some changes to compile with it. If you are "+ ++ "not sure what upper bound to use then use the next major "+ ++ "version."+ppExplanation (DuplicateModule s dupLibsLax) =+ "Duplicate modules in "+ ++ s+ ++ ": "+ ++ commaSep (map prettyShow dupLibsLax)+ppExplanation (PotentialDupModule s dupLibsStrict) =+ "Potential duplicate modules (subject to conditionals) in "+ ++ s+ ++ ": "+ ++ commaSep (map prettyShow dupLibsStrict)+ppExplanation (BOMStart pdfile) =+ pdfile+ ++ " starts with an Unicode byte order mark (BOM)."+ ++ " This may cause problems with older cabal versions."+ppExplanation (NotPackageName pdfile expectedCabalname) =+ "The filename "+ ++ quote pdfile+ ++ " does not match package name "+ ++ "(expected: "+ ++ quote expectedCabalname+ ++ ")"+ppExplanation NoDesc =+ "No cabal file found.\n"+ ++ "Please create a package description file <pkgname>.cabal"+ppExplanation (MultiDesc multiple) =+ "Multiple cabal files found while checking.\n"+ ++ "Please use only one of: "+ ++ commaSep multiple+ppExplanation (UnknownFile fieldname file) =+ "The '"+ ++ fieldname+ ++ "' field refers to the file "+ ++ quote (getSymbolicPath file)+ ++ " which does not exist."+ppExplanation MissingSetupFile =+ "The package is missing a Setup.hs or Setup.lhs script."+ppExplanation MissingConfigureScript =+ "The 'build-type' is 'Configure' but there is no 'configure' script. "+ ++ "You probably need to run 'autoreconf -i' to generate it."+ppExplanation (UnknownDirectory kind dir) =+ quote (kind ++ ": " ++ dir)+ ++ " specifies a directory which does not exist."+ppExplanation MissingSourceControl =+ "When distributing packages, it is encouraged to specify source "+ ++ "control information in the .cabal file using one or more "+ ++ "'source-repository' sections. See the Cabal user guide for "+ ++ "details."+ppExplanation (MissingExpectedDocFiles extraDocFileSupport paths) =+ "Please consider including the "+ ++ quotes paths+ ++ " in the '"+ ++ targetField+ ++ "' section of the .cabal file "+ ++ "if it contains useful information for users of the package."+ where+ quotes [p] = "file " ++ quote p+ quotes ps = "files " ++ commaSep (map quote ps)+ targetField =+ if extraDocFileSupport+ then "extra-doc-files"+ else "extra-source-files"+ppExplanation (WrongFieldForExpectedDocFiles extraDocFileSupport field paths) =+ "Please consider moving the "+ ++ quotes paths+ ++ " from the '"+ ++ field+ ++ "' section of the .cabal file "+ ++ "to the section '"+ ++ targetField+ ++ "'."+ where+ quotes [p] = "file " ++ quote p+ quotes ps = "files " ++ commaSep (map quote ps)+ targetField =+ if extraDocFileSupport+ then "extra-doc-files"+ else "extra-source-files"++-- * Formatting utilities++listSep :: [String] -> String+listSep names =+ let separator = "\n - "+ in separator ++ List.intercalate separator names ++ "\n"++commaSep :: [String] -> String+commaSep = List.intercalate ", "++quote :: String -> String+quote s = "'" ++ s ++ "'"++addConditionalExp :: String -> String+addConditionalExp expl =+ expl+ ++ " Alternatively, if you want to use this, make it conditional based "+ ++ "on a Cabal configuration flag (with 'manual: True' and 'default: "+ ++ "False') and enable that flag during development."
− src/Distribution/PackageDescription/Configuration.hs
@@ -1,600 +0,0 @@--- -fno-warn-deprecations for use of Map.foldWithKey-{-# OPTIONS_GHC -fno-warn-deprecations #-}--------------------------------------------------------------------------------- |--- Module : Distribution.PackageDescription.Configuration--- Copyright : Thomas Schilling, 2007--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ This is about the cabal configurations feature. It exports--- 'finalizePD' and 'flattenPackageDescription' which are--- functions for converting 'GenericPackageDescription's down to--- 'PackageDescription's. It has code for working with the tree of conditions--- and resolving or flattening conditions.--module Distribution.PackageDescription.Configuration (- finalizePD,- flattenPackageDescription,-- -- Utils- parseCondition,- freeVars,- extractCondition,- extractConditions,- addBuildableCondition,- mapCondTree,- mapTreeData,- mapTreeConds,- mapTreeConstrs,- transformAllBuildInfos,- transformAllBuildDepends,- transformAllBuildDependsN,- ) where--import Distribution.Compat.Prelude-import Prelude ()---- lens-import qualified Distribution.Types.BuildInfo.Lens as L-import qualified Distribution.Types.GenericPackageDescription.Lens as L-import qualified Distribution.Types.PackageDescription.Lens as L-import qualified Distribution.Types.SetupBuildInfo.Lens as L--import Distribution.Compat.CharParsing hiding (char)-import qualified Distribution.Compat.CharParsing as P-import Distribution.Compat.Lens-import Distribution.Compiler-import Distribution.PackageDescription-import Distribution.PackageDescription.Utils-import Distribution.Parsec-import Distribution.Pretty-import Distribution.Simple.Utils-import Distribution.System-import Distribution.Types.Component-import Distribution.Utils.Path-import Distribution.Types.ComponentRequestedSpec-import Distribution.Types.DependencyMap-import Distribution.Types.PackageVersionConstraint-import Distribution.Version--import qualified Data.Map.Lazy as Map-import Data.Tree (Tree (Node))------------------------------------------------------------------------------------ | Simplify a configuration condition using the OS and arch names. Returns--- the names of all the flags occurring in the condition.-simplifyWithSysParams :: OS -> Arch -> CompilerInfo -> Condition ConfVar- -> (Condition FlagName, [FlagName])-simplifyWithSysParams os arch cinfo cond = (cond', flags)- where- (cond', flags) = simplifyCondition cond interp- interp (OS os') = Right $ os' == os- interp (Arch arch') = Right $ arch' == arch- interp (Impl comp vr)- | matchImpl (compilerInfoId cinfo) = Right True- | otherwise = case compilerInfoCompat cinfo of- -- fixme: treat Nothing as unknown, rather than empty list once we- -- support partial resolution of system parameters- Nothing -> Right False- Just compat -> Right (any matchImpl compat)- where- matchImpl (CompilerId c v) = comp == c && v `withinRange` vr- interp (PackageFlag f) = Left f---- TODO: Add instances and check------ prop_sC_idempotent cond a o = cond' == cond''--- where--- cond' = simplifyCondition cond a o--- cond'' = simplifyCondition cond' a o------ prop_sC_noLits cond a o = isLit res || not (hasLits res)--- where--- res = simplifyCondition cond a o--- hasLits (Lit _) = True--- hasLits (CNot c) = hasLits c--- hasLits (COr l r) = hasLits l || hasLits r--- hasLits (CAnd l r) = hasLits l || hasLits r--- hasLits _ = False------- | Parse a configuration condition from a string.-parseCondition :: CabalParsing m => m (Condition ConfVar)-parseCondition = condOr- where- condOr = sepByNonEmpty condAnd (oper "||") >>= return . foldl1 COr- condAnd = sepByNonEmpty cond (oper "&&")>>= return . foldl1 CAnd- -- TODO: try?- cond = sp >> (boolLiteral <|> inparens condOr <|> notCond <|> osCond- <|> archCond <|> flagCond <|> implCond )- inparens = between (P.char '(' >> sp) (sp >> P.char ')' >> sp)- notCond = P.char '!' >> sp >> cond >>= return . CNot- osCond = string "os" >> sp >> inparens osIdent >>= return . Var- archCond = string "arch" >> sp >> inparens archIdent >>= return . Var- flagCond = string "flag" >> sp >> inparens flagIdent >>= return . Var- implCond = string "impl" >> sp >> inparens implIdent >>= return . Var- boolLiteral = fmap Lit parsec- archIdent = fmap Arch parsec- osIdent = fmap OS parsec- flagIdent = fmap (PackageFlag . mkFlagName . lowercase) (munch1 isIdentChar)- isIdentChar c = isAlphaNum c || c == '_' || c == '-'- oper s = sp >> string s >> sp- sp = spaces- implIdent = do i <- parsec- vr <- sp >> option anyVersion parsec- return $ Impl i vr------------------------------------------------------------------------------------ | Result of dependency test. Isomorphic to @Maybe d@ but renamed for--- clarity.-data DepTestRslt d = DepOk | MissingDeps d--instance Semigroup d => Monoid (DepTestRslt d) where- mempty = DepOk- mappend = (<>)--instance Semigroup d => Semigroup (DepTestRslt d) where- DepOk <> x = x- x <> DepOk = x- (MissingDeps d) <> (MissingDeps d') = MissingDeps (d <> d')----- | Try to find a flag assignment that satisfies the constraints of all trees.------ Returns either the missing dependencies, or a tuple containing the--- resulting data, the associated dependencies, and the chosen flag--- assignments.------ In case of failure, the union of the dependencies that led to backtracking--- on all branches is returned.--- [TODO: Could also be specified with a function argument.]------ TODO: The current algorithm is rather naive. A better approach would be to:------ * Rule out possible paths, by taking a look at the associated dependencies.------ * Infer the required values for the conditions of these paths, and--- calculate the required domains for the variables used in these--- conditions. Then picking a flag assignment would be linear (I guess).------ This would require some sort of SAT solving, though, thus it's not--- implemented unless we really need it.----resolveWithFlags ::- [(FlagName,[Bool])]- -- ^ Domain for each flag name, will be tested in order.- -> ComponentRequestedSpec- -> OS -- ^ OS as returned by Distribution.System.buildOS- -> Arch -- ^ Arch as returned by Distribution.System.buildArch- -> CompilerInfo -- ^ Compiler information- -> [PackageVersionConstraint] -- ^ Additional constraints- -> [CondTree ConfVar [Dependency] PDTagged]- -> ([Dependency] -> DepTestRslt [Dependency]) -- ^ Dependency test function.- -> Either [Dependency] (TargetSet PDTagged, FlagAssignment)- -- ^ Either the missing dependencies (error case), or a pair of- -- (set of build targets with dependencies, chosen flag assignments)-resolveWithFlags dom enabled os arch impl constrs trees checkDeps =- either (Left . fromDepMapUnion) Right $ explore (build mempty dom)- where- -- simplify trees by (partially) evaluating all conditions and converting- -- dependencies to dependency maps.- simplifiedTrees :: [CondTree FlagName DependencyMap PDTagged]- simplifiedTrees = map ( mapTreeConstrs toDepMap -- convert to maps- . addBuildableConditionPDTagged- . mapTreeConds (fst . simplifyWithSysParams os arch impl))- trees-- -- @explore@ searches a tree of assignments, backtracking whenever a flag- -- introduces a dependency that cannot be satisfied. If there is no- -- solution, @explore@ returns the union of all dependencies that caused- -- it to backtrack. Since the tree is constructed lazily, we avoid some- -- computation overhead in the successful case.- explore :: Tree FlagAssignment- -> Either DepMapUnion (TargetSet PDTagged, FlagAssignment)- explore (Node flags ts) =- let targetSet = TargetSet $ flip map simplifiedTrees $- -- apply additional constraints to all dependencies- first (`constrainBy` constrs) .- simplifyCondTree (env flags)- deps = overallDependencies enabled targetSet- in case checkDeps (fromDepMap deps) of- DepOk | null ts -> Right (targetSet, flags)- | otherwise -> tryAll $ map explore ts- MissingDeps mds -> Left (toDepMapUnion mds)-- -- Builds a tree of all possible flag assignments. Internal nodes- -- have only partial assignments.- build :: FlagAssignment -> [(FlagName, [Bool])] -> Tree FlagAssignment- build assigned [] = Node assigned []- build assigned ((fn, vals) : unassigned) =- Node assigned $ map (\v -> build (insertFlagAssignment fn v assigned) unassigned) vals-- tryAll :: [Either DepMapUnion a] -> Either DepMapUnion a- tryAll = foldr mp mz-- -- special version of `mplus' for our local purposes- mp :: Either DepMapUnion a -> Either DepMapUnion a -> Either DepMapUnion a- mp m@(Right _) _ = m- mp _ m@(Right _) = m- mp (Left xs) (Left ys) = Left (xs <> ys)-- -- `mzero'- mz :: Either DepMapUnion a- mz = Left (DepMapUnion Map.empty)-- env :: FlagAssignment -> FlagName -> Either FlagName Bool- env flags flag = (maybe (Left flag) Right . lookupFlagAssignment flag) flags---- | Transforms a 'CondTree' by putting the input under the "then" branch of a--- conditional that is True when Buildable is True. If 'addBuildableCondition'--- can determine that Buildable is always True, it returns the input unchanged.--- If Buildable is always False, it returns the empty 'CondTree'.-addBuildableCondition :: (Eq v, Monoid a, Monoid c) => (a -> BuildInfo)- -> CondTree v c a- -> CondTree v c a-addBuildableCondition getInfo t =- case extractCondition (buildable . getInfo) t of- Lit True -> t- Lit False -> CondNode mempty mempty []- c -> CondNode mempty mempty [condIfThen c t]---- | This is a special version of 'addBuildableCondition' for the 'PDTagged'--- type.------ It is not simply a specialisation. It is more complicated than it--- ought to be because of the way the 'PDTagged' monoid instance works. The--- @mempty = 'PDNull'@ forgets the component type, which has the effect of--- completely deleting components that are not buildable.------ See <https://github.com/haskell/cabal/pull/4094> for more details.----addBuildableConditionPDTagged :: (Eq v, Monoid c) =>- CondTree v c PDTagged- -> CondTree v c PDTagged-addBuildableConditionPDTagged t =- case extractCondition (buildable . getInfo) t of- Lit True -> t- Lit False -> deleteConstraints t- c -> CondNode mempty mempty [condIfThenElse c t (deleteConstraints t)]- where- deleteConstraints = mapTreeConstrs (const mempty)-- getInfo :: PDTagged -> BuildInfo- getInfo (Lib l) = libBuildInfo l- getInfo (SubComp _ c) = componentBuildInfo c- getInfo PDNull = mempty----- Note: extracting buildable conditions.--- -------------------------------------------- If the conditions in a cond tree lead to Buildable being set to False, then--- none of the dependencies for this cond tree should actually be taken into--- account. On the other hand, some of the flags may only be decided in the--- solver, so we cannot necessarily make the decision whether a component is--- Buildable or not prior to solving.------ What we are doing here is to partially evaluate a condition tree in order to--- extract the condition under which Buildable is True. The predicate determines--- whether data under a 'CondTree' is buildable.---- | Extract conditions matched by the given predicate from all cond trees in a--- 'GenericPackageDescription'.-extractConditions :: (BuildInfo -> Bool) -> GenericPackageDescription- -> [Condition ConfVar]-extractConditions f gpkg =- concat [- extractCondition (f . libBuildInfo) <$> maybeToList (condLibrary gpkg)- , extractCondition (f . libBuildInfo) . snd <$> condSubLibraries gpkg- , extractCondition (f . buildInfo) . snd <$> condExecutables gpkg- , extractCondition (f . testBuildInfo) . snd <$> condTestSuites gpkg- , extractCondition (f . benchmarkBuildInfo) . snd <$> condBenchmarks gpkg- ]----- | A map of package constraints that combines version ranges using 'unionVersionRanges'.-newtype DepMapUnion = DepMapUnion { unDepMapUnion :: Map PackageName (VersionRange, NonEmptySet LibraryName) }--instance Semigroup DepMapUnion where- DepMapUnion x <> DepMapUnion y = DepMapUnion $- Map.unionWith unionVersionRanges' x y--unionVersionRanges'- :: (VersionRange, NonEmptySet LibraryName)- -> (VersionRange, NonEmptySet LibraryName)- -> (VersionRange, NonEmptySet LibraryName)-unionVersionRanges' (vr, cs) (vr', cs') = (unionVersionRanges vr vr', cs <> cs')--toDepMapUnion :: [Dependency] -> DepMapUnion-toDepMapUnion ds =- DepMapUnion $ Map.fromListWith unionVersionRanges' [ (p,(vr,cs)) | Dependency p vr cs <- ds ]---fromDepMapUnion :: DepMapUnion -> [Dependency]-fromDepMapUnion m = [ Dependency p vr cs | (p,(vr,cs)) <- Map.toList (unDepMapUnion m) ]--freeVars :: CondTree ConfVar c a -> [FlagName]-freeVars t = [ f | PackageFlag f <- freeVars' t ]- where- freeVars' (CondNode _ _ ifs) = concatMap compfv ifs- compfv (CondBranch c ct mct) = condfv c ++ freeVars' ct ++ maybe [] freeVars' mct- condfv c = case c of- Var v -> [v]- Lit _ -> []- CNot c' -> condfv c'- COr c1 c2 -> condfv c1 ++ condfv c2- CAnd c1 c2 -> condfv c1 ++ condfv c2------------------------------------------------------------------------------------- | A set of targets with their package dependencies-newtype TargetSet a = TargetSet [(DependencyMap, a)]---- | Combine the target-specific dependencies in a TargetSet to give the--- dependencies for the package as a whole.-overallDependencies :: ComponentRequestedSpec -> TargetSet PDTagged -> DependencyMap-overallDependencies enabled (TargetSet targets) = mconcat depss- where- (depss, _) = unzip $ filter (removeDisabledSections . snd) targets- removeDisabledSections :: PDTagged -> Bool- -- UGH. The embedded componentName in the 'Component's here is- -- BLANK. I don't know whose fault this is but I'll use the tag- -- instead. -- ezyang- removeDisabledSections (Lib _) = componentNameRequested- enabled- (CLibName LMainLibName)- removeDisabledSections (SubComp t c)- -- Do NOT use componentName- = componentNameRequested enabled- $ case c of- CLib _ -> CLibName (LSubLibName t)- CFLib _ -> CFLibName t- CExe _ -> CExeName t- CTest _ -> CTestName t- CBench _ -> CBenchName t- removeDisabledSections PDNull = True---- | Collect up the targets in a TargetSet of tagged targets, storing the--- dependencies as we go.-flattenTaggedTargets :: TargetSet PDTagged -> (Maybe Library, [(UnqualComponentName, Component)])-flattenTaggedTargets (TargetSet targets) = foldr untag (Nothing, []) targets where- untag (depMap, pdTagged) accum = case (pdTagged, accum) of- (Lib _, (Just _, _)) -> userBug "Only one library expected"- (Lib l, (Nothing, comps)) -> (Just $ redoBD l, comps)- (SubComp n c, (mb_lib, comps))- | any ((== n) . fst) comps ->- userBug $ "There exist several components with the same name: '" ++ prettyShow n ++ "'"- | otherwise -> (mb_lib, (n, redoBD c) : comps)- (PDNull, x) -> x -- actually this should not happen, but let's be liberal- where- redoBD :: L.HasBuildInfo a => a -> a- redoBD = set L.targetBuildDepends $ fromDepMap depMap----------------------------------------------------------------------------------- Convert GenericPackageDescription to PackageDescription-----data PDTagged = Lib Library- | SubComp UnqualComponentName Component- | PDNull- deriving Show--instance Monoid PDTagged where- mempty = PDNull- mappend = (<>)--instance Semigroup PDTagged where- PDNull <> x = x- x <> PDNull = x- Lib l <> Lib l' = Lib (l <> l')- SubComp n x <> SubComp n' x' | n == n' = SubComp n (x <> x')- _ <> _ = cabalBug "Cannot combine incompatible tags"---- | Create a package description with all configurations resolved.------ This function takes a `GenericPackageDescription` and several environment--- parameters and tries to generate `PackageDescription` by finding a flag--- assignment that result in satisfiable dependencies.------ It takes as inputs a not necessarily complete specifications of flags--- assignments, an optional package index as well as platform parameters. If--- some flags are not assigned explicitly, this function will try to pick an--- assignment that causes this function to succeed. The package index is--- optional since on some platforms we cannot determine which packages have--- been installed before. When no package index is supplied, every dependency--- is assumed to be satisfiable, therefore all not explicitly assigned flags--- will get their default values.------ This function will fail if it cannot find a flag assignment that leads to--- satisfiable dependencies. (It will not try alternative assignments for--- explicitly specified flags.) In case of failure it will return the missing--- dependencies that it encountered when trying different flag assignments.--- On success, it will return the package description and the full flag--- assignment chosen.------ Note that this drops any stanzas which have @buildable: False@. While--- this is arguably the right thing to do, it means we give bad error--- messages in some situations, see #3858.----finalizePD ::- FlagAssignment -- ^ Explicitly specified flag assignments- -> ComponentRequestedSpec- -> (Dependency -> Bool) -- ^ Is a given dependency satisfiable from the set of- -- available packages? If this is unknown then use- -- True.- -> Platform -- ^ The 'Arch' and 'OS'- -> CompilerInfo -- ^ Compiler information- -> [PackageVersionConstraint] -- ^ Additional constraints- -> GenericPackageDescription- -> Either [Dependency]- (PackageDescription, FlagAssignment)- -- ^ Either missing dependencies or the resolved package- -- description along with the flag assignments chosen.-finalizePD userflags enabled satisfyDep- (Platform arch os) impl constraints- (GenericPackageDescription pkg _ver flags mb_lib0 sub_libs0 flibs0 exes0 tests0 bms0) = do- (targetSet, flagVals) <-- resolveWithFlags flagChoices enabled os arch impl constraints condTrees check- let- (mb_lib, comps) = flattenTaggedTargets targetSet- mb_lib' = fmap libFillInDefaults mb_lib- comps' = flip map comps $ \(n,c) -> foldComponent- (\l -> CLib (libFillInDefaults l) { libName = LSubLibName n- , libExposed = False })- (\l -> CFLib (flibFillInDefaults l) { foreignLibName = n })- (\e -> CExe (exeFillInDefaults e) { exeName = n })- (\t -> CTest (testFillInDefaults t) { testName = n })- (\b -> CBench (benchFillInDefaults b) { benchmarkName = n })- c- (sub_libs', flibs', exes', tests', bms') = partitionComponents comps'- return ( pkg { library = mb_lib'- , subLibraries = sub_libs'- , foreignLibs = flibs'- , executables = exes'- , testSuites = tests'- , benchmarks = bms'- }- , flagVals )- where- -- Combine lib, exes, and tests into one list of @CondTree@s with tagged data- condTrees = maybeToList (fmap (mapTreeData Lib) mb_lib0)- ++ map (\(name,tree) -> mapTreeData (SubComp name . CLib) tree) sub_libs0- ++ map (\(name,tree) -> mapTreeData (SubComp name . CFLib) tree) flibs0- ++ map (\(name,tree) -> mapTreeData (SubComp name . CExe) tree) exes0- ++ map (\(name,tree) -> mapTreeData (SubComp name . CTest) tree) tests0- ++ map (\(name,tree) -> mapTreeData (SubComp name . CBench) tree) bms0-- flagChoices = map (\(MkPackageFlag n _ d manual) -> (n, d2c manual n d)) flags- d2c manual n b = case lookupFlagAssignment n userflags of- Just val -> [val]- Nothing- | manual -> [b]- | otherwise -> [b, not b]- --flagDefaults = map (\(n,x:_) -> (n,x)) flagChoices- check ds = let missingDeps = filter (not . satisfyDep) ds- in if null missingDeps- then DepOk- else MissingDeps missingDeps--{--let tst_p = (CondNode [1::Int] [Distribution.Package.Dependency "a" AnyVersion] [])-let tst_p2 = (CondNode [1::Int] [Distribution.Package.Dependency "a" (EarlierVersion (Version [1,0] [])), Distribution.Package.Dependency "a" (LaterVersion (Version [2,0] []))] [])--let p_index = Distribution.Simple.PackageIndex.fromList [Distribution.Package.PackageIdentifier "a" (Version [0,5] []), Distribution.Package.PackageIdentifier "a" (Version [2,5] [])]-let look = not . null . Distribution.Simple.PackageIndex.lookupDependency p_index-let looks ds = mconcat $ map (\d -> if look d then DepOk else MissingDeps [d]) ds-resolveWithFlags [] Distribution.System.Linux Distribution.System.I386 (Distribution.Compiler.GHC,Version [6,8,2] []) [tst_p] looks ===> Right ...-resolveWithFlags [] Distribution.System.Linux Distribution.System.I386 (Distribution.Compiler.GHC,Version [6,8,2] []) [tst_p2] looks ===> Left ...--}---- | Flatten a generic package description by ignoring all conditions and just--- join the field descriptors into on package description. Note, however,--- that this may lead to inconsistent field values, since all values are--- joined into one field, which may not be possible in the original package--- description, due to the use of exclusive choices (if ... else ...).------ TODO: One particularly tricky case is defaulting. In the original package--- description, e.g., the source directory might either be the default or a--- certain, explicitly set path. Since defaults are filled in only after the--- package has been resolved and when no explicit value has been set, the--- default path will be missing from the package description returned by this--- function.-flattenPackageDescription :: GenericPackageDescription -> PackageDescription-flattenPackageDescription- (GenericPackageDescription pkg _ _ mlib0 sub_libs0 flibs0 exes0 tests0 bms0) =- pkg { library = mlib- , subLibraries = reverse sub_libs- , foreignLibs = reverse flibs- , executables = reverse exes- , testSuites = reverse tests- , benchmarks = reverse bms- }- where- mlib = f <$> mlib0- where f lib = (libFillInDefaults . fst . ignoreConditions $ lib) { libName = LMainLibName }- sub_libs = flattenLib <$> sub_libs0- flibs = flattenFLib <$> flibs0- exes = flattenExe <$> exes0- tests = flattenTst <$> tests0- bms = flattenBm <$> bms0- flattenLib (n, t) = libFillInDefaults $ (fst $ ignoreConditions t)- { libName = LSubLibName n, libExposed = False }- flattenFLib (n, t) = flibFillInDefaults $ (fst $ ignoreConditions t)- { foreignLibName = n }- flattenExe (n, t) = exeFillInDefaults $ (fst $ ignoreConditions t)- { exeName = n }- flattenTst (n, t) = testFillInDefaults $ (fst $ ignoreConditions t)- { testName = n }- flattenBm (n, t) = benchFillInDefaults $ (fst $ ignoreConditions t)- { benchmarkName = n }---- This is in fact rather a hack. The original version just overrode the--- default values, however, when adding conditions we had to switch to a--- modifier-based approach. There, nothing is ever overwritten, but only--- joined together.------ This is the cleanest way i could think of, that doesn't require--- changing all field parsing functions to return modifiers instead.-libFillInDefaults :: Library -> Library-libFillInDefaults lib@(Library { libBuildInfo = bi }) =- lib { libBuildInfo = biFillInDefaults bi }--flibFillInDefaults :: ForeignLib -> ForeignLib-flibFillInDefaults flib@(ForeignLib { foreignLibBuildInfo = bi }) =- flib { foreignLibBuildInfo = biFillInDefaults bi }--exeFillInDefaults :: Executable -> Executable-exeFillInDefaults exe@(Executable { buildInfo = bi }) =- exe { buildInfo = biFillInDefaults bi }--testFillInDefaults :: TestSuite -> TestSuite-testFillInDefaults tst@(TestSuite { testBuildInfo = bi }) =- tst { testBuildInfo = biFillInDefaults bi }--benchFillInDefaults :: Benchmark -> Benchmark-benchFillInDefaults bm@(Benchmark { benchmarkBuildInfo = bi }) =- bm { benchmarkBuildInfo = biFillInDefaults bi }--biFillInDefaults :: BuildInfo -> BuildInfo-biFillInDefaults bi =- if null (hsSourceDirs bi)- then bi { hsSourceDirs = [sameDirectory] }- else bi---- Walk a 'GenericPackageDescription' and apply @onBuildInfo@/@onSetupBuildInfo@--- to all nested 'BuildInfo'/'SetupBuildInfo' values.-transformAllBuildInfos :: (BuildInfo -> BuildInfo)- -> (SetupBuildInfo -> SetupBuildInfo)- -> GenericPackageDescription- -> GenericPackageDescription-transformAllBuildInfos onBuildInfo onSetupBuildInfo =- over L.traverseBuildInfos onBuildInfo- . over (L.packageDescription . L.setupBuildInfo . traverse) onSetupBuildInfo---- | Walk a 'GenericPackageDescription' and apply @f@ to all nested--- @build-depends@ fields.-transformAllBuildDepends :: (Dependency -> Dependency)- -> GenericPackageDescription- -> GenericPackageDescription-transformAllBuildDepends f =- over (L.traverseBuildInfos . L.targetBuildDepends . traverse) f- . over (L.packageDescription . L.setupBuildInfo . traverse . L.setupDepends . traverse) f- -- cannot be point-free as normal because of higher rank- . over (\f' -> L.allCondTrees $ traverseCondTreeC f') (map f)---- | Walk a 'GenericPackageDescription' and apply @f@ to all nested--- @build-depends@ fields.-transformAllBuildDependsN :: ([Dependency] -> [Dependency])- -> GenericPackageDescription- -> GenericPackageDescription-transformAllBuildDependsN f =- over (L.traverseBuildInfos . L.targetBuildDepends) f- . over (L.packageDescription . L.setupBuildInfo . traverse . L.setupDepends) f- -- cannot be point-free as normal because of higher rank- . over (\f' -> L.allCondTrees $ traverseCondTreeC f') f
− src/Distribution/PackageDescription/FieldGrammar.hs
@@ -1,802 +0,0 @@-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}--- | 'GenericPackageDescription' Field descriptions-module Distribution.PackageDescription.FieldGrammar (- -- * Package description- packageDescriptionFieldGrammar,- -- * Library- libraryFieldGrammar,- -- * Foreign library- foreignLibFieldGrammar,- -- * Executable- executableFieldGrammar,- -- * Test suite- TestSuiteStanza (..),- testSuiteFieldGrammar,- validateTestSuite,- unvalidateTestSuite,- -- ** Lenses- testStanzaTestType,- testStanzaMainIs,- testStanzaTestModule,- testStanzaBuildInfo,- -- * Benchmark- BenchmarkStanza (..),- benchmarkFieldGrammar,- validateBenchmark,- unvalidateBenchmark,- -- * Field grammars- formatDependencyList,- formatExposedModules,- formatExtraSourceFiles,- formatHsSourceDirs,- formatMixinList,- formatOtherExtensions,- formatOtherModules,- -- ** Lenses- benchmarkStanzaBenchmarkType,- benchmarkStanzaMainIs,- benchmarkStanzaBenchmarkModule,- benchmarkStanzaBuildInfo,- -- * Flag- flagFieldGrammar,- -- * Source repository- sourceRepoFieldGrammar,- -- * Setup build info- setupBInfoFieldGrammar,- -- * Component build info- buildInfoFieldGrammar,- ) where---import Distribution.Compat.Lens-import Distribution.Compat.Prelude-import Language.Haskell.Extension-import Prelude ()--import Distribution.CabalSpecVersion-import Distribution.Compat.Newtype (Newtype, pack', unpack')-import Distribution.Compiler (CompilerFlavor (..), PerCompilerFlavor (..))-import Distribution.FieldGrammar-import Distribution.Fields-import Distribution.ModuleName (ModuleName)-import Distribution.Package-import Distribution.PackageDescription-import Distribution.Parsec-import Distribution.Pretty (Pretty (..), prettyShow, showToken)-import Distribution.Utils.Path-import Distribution.Version (Version, VersionRange)--import qualified Data.ByteString.Char8 as BS8-import qualified Distribution.Compat.CharParsing as P-import qualified Distribution.SPDX as SPDX-import qualified Distribution.Types.Lens as L------------------------------------------------------------------------------------ PackageDescription----------------------------------------------------------------------------------packageDescriptionFieldGrammar- :: ( FieldGrammar c g, Applicative (g PackageDescription), Applicative (g PackageIdentifier)- , c (Identity BuildType)- , c (Identity PackageName)- , c (Identity Version)- , c (List FSep FilePathNT String)- , c (List FSep CompatFilePath String)- , c (List FSep (Identity (SymbolicPath PackageDir LicenseFile)) (SymbolicPath PackageDir LicenseFile))- , c (List FSep TestedWith (CompilerFlavor, VersionRange))- , c (List VCat FilePathNT String)- , c FilePathNT- , c CompatLicenseFile- , c CompatFilePath- , c SpecLicense- , c SpecVersion- )- => g PackageDescription PackageDescription-packageDescriptionFieldGrammar = PackageDescription- <$> optionalFieldDefAla "cabal-version" SpecVersion L.specVersion CabalSpecV1_0- <*> blurFieldGrammar L.package packageIdentifierGrammar- <*> optionalFieldDefAla "license" SpecLicense L.licenseRaw (Left SPDX.NONE)- <*> licenseFilesGrammar- <*> freeTextFieldDefST "copyright" L.copyright- <*> freeTextFieldDefST "maintainer" L.maintainer- <*> freeTextFieldDefST "author" L.author- <*> freeTextFieldDefST "stability" L.stability- <*> monoidalFieldAla "tested-with" (alaList' FSep TestedWith) L.testedWith- <*> freeTextFieldDefST "homepage" L.homepage- <*> freeTextFieldDefST "package-url" L.pkgUrl- <*> freeTextFieldDefST "bug-reports" L.bugReports- <*> pure [] -- source-repos are stanza- <*> freeTextFieldDefST "synopsis" L.synopsis- <*> freeTextFieldDefST "description" L.description- <*> freeTextFieldDefST "category" L.category- <*> prefixedFields "x-" L.customFieldsPD- <*> optionalField "build-type" L.buildTypeRaw- <*> pure Nothing -- custom-setup- -- components- <*> pure Nothing -- lib- <*> pure [] -- sub libs- <*> pure [] -- executables- <*> pure [] -- foreign libs- <*> pure [] -- test suites- <*> pure [] -- benchmarks- -- * Files- <*> monoidalFieldAla "data-files" (alaList' VCat FilePathNT) L.dataFiles- <*> optionalFieldDefAla "data-dir" CompatFilePath L.dataDir "."- ^^^ fmap (\x -> if null x then "." else x) -- map empty directories to "."- <*> monoidalFieldAla "extra-source-files" formatExtraSourceFiles L.extraSrcFiles- <*> monoidalFieldAla "extra-tmp-files" (alaList' VCat FilePathNT) L.extraTmpFiles- <*> monoidalFieldAla "extra-doc-files" (alaList' VCat FilePathNT) L.extraDocFiles- where- packageIdentifierGrammar = PackageIdentifier- <$> uniqueField "name" L.pkgName- <*> uniqueField "version" L.pkgVersion-- licenseFilesGrammar = (++)- -- TODO: neither field is deprecated- -- should we pretty print license-file if there's single license file- -- and license-files when more- <$> monoidalFieldAla "license-file" CompatLicenseFile L.licenseFiles- <*> monoidalFieldAla "license-files" (alaList FSep) L.licenseFiles- ^^^ hiddenField------------------------------------------------------------------------------------ Library----------------------------------------------------------------------------------libraryFieldGrammar- :: ( FieldGrammar c g, Applicative (g Library), Applicative (g BuildInfo)- , c (Identity LibraryVisibility)- , c (List CommaFSep (Identity ExeDependency) ExeDependency)- , c (List CommaFSep (Identity LegacyExeDependency) LegacyExeDependency)- , c (List CommaFSep (Identity PkgconfigDependency) PkgconfigDependency)- , c (List CommaVCat (Identity Dependency) Dependency)- , c (List CommaVCat (Identity Mixin) Mixin)- , c (List CommaVCat (Identity ModuleReexport) ModuleReexport)- , c (List FSep (MQuoted Extension) Extension)- , c (List FSep (MQuoted Language) Language)- , c (List FSep FilePathNT String)- , c (List FSep Token String)- , c (List NoCommaFSep Token' String)- , c (List VCat (MQuoted ModuleName) ModuleName)- , c (List VCat FilePathNT String)- , c (List FSep (Identity (SymbolicPath PackageDir SourceDir)) (SymbolicPath PackageDir SourceDir))- , c (List VCat Token String)- , c (MQuoted Language)- )- => LibraryName- -> g Library Library-libraryFieldGrammar n = Library n- <$> monoidalFieldAla "exposed-modules" formatExposedModules L.exposedModules- <*> monoidalFieldAla "reexported-modules" (alaList CommaVCat) L.reexportedModules- <*> monoidalFieldAla "signatures" (alaList' VCat MQuoted) L.signatures- ^^^ availableSince CabalSpecV2_0 []- <*> booleanFieldDef "exposed" L.libExposed True- <*> visibilityField- <*> blurFieldGrammar L.libBuildInfo buildInfoFieldGrammar- where- visibilityField = case n of- -- nameless/"main" libraries are public- LMainLibName -> pure LibraryVisibilityPublic- -- named libraries have the field- LSubLibName _ ->- optionalFieldDef "visibility" L.libVisibility LibraryVisibilityPrivate- ^^^ availableSince CabalSpecV3_0 LibraryVisibilityPrivate--{-# SPECIALIZE libraryFieldGrammar :: LibraryName -> ParsecFieldGrammar' Library #-}-{-# SPECIALIZE libraryFieldGrammar :: LibraryName -> PrettyFieldGrammar' Library #-}------------------------------------------------------------------------------------ Foreign library----------------------------------------------------------------------------------foreignLibFieldGrammar- :: ( FieldGrammar c g, Applicative (g ForeignLib), Applicative (g BuildInfo)- , c (Identity ForeignLibType)- , c (Identity LibVersionInfo)- , c (Identity Version)- , c (List CommaFSep (Identity ExeDependency) ExeDependency)- , c (List CommaFSep (Identity LegacyExeDependency) LegacyExeDependency)- , c (List CommaFSep (Identity PkgconfigDependency) PkgconfigDependency)- , c (List CommaVCat (Identity Dependency) Dependency)- , c (List CommaVCat (Identity Mixin) Mixin)- , c (List FSep (Identity ForeignLibOption) ForeignLibOption)- , c (List FSep (MQuoted Extension) Extension)- , c (List FSep (MQuoted Language) Language)- , c (List FSep FilePathNT String)- , c (List FSep Token String)- , c (List FSep (Identity (SymbolicPath PackageDir SourceDir)) (SymbolicPath PackageDir SourceDir))- , c (List NoCommaFSep Token' String)- , c (List VCat (MQuoted ModuleName) ModuleName)- , c (List VCat FilePathNT String), c (List VCat Token String)- , c (MQuoted Language)- )- => UnqualComponentName -> g ForeignLib ForeignLib-foreignLibFieldGrammar n = ForeignLib n- <$> optionalFieldDef "type" L.foreignLibType ForeignLibTypeUnknown- <*> monoidalFieldAla "options" (alaList FSep) L.foreignLibOptions- <*> blurFieldGrammar L.foreignLibBuildInfo buildInfoFieldGrammar- <*> optionalField "lib-version-info" L.foreignLibVersionInfo- <*> optionalField "lib-version-linux" L.foreignLibVersionLinux- <*> monoidalFieldAla "mod-def-file" (alaList' FSep FilePathNT) L.foreignLibModDefFile-{-# SPECIALIZE foreignLibFieldGrammar :: UnqualComponentName -> ParsecFieldGrammar' ForeignLib #-}-{-# SPECIALIZE foreignLibFieldGrammar :: UnqualComponentName -> PrettyFieldGrammar' ForeignLib #-}------------------------------------------------------------------------------------ Executable----------------------------------------------------------------------------------executableFieldGrammar- :: ( FieldGrammar c g, Applicative (g Executable), Applicative (g BuildInfo)- , c (Identity ExecutableScope)- , c (List CommaFSep (Identity ExeDependency) ExeDependency)- , c (List CommaFSep (Identity LegacyExeDependency) LegacyExeDependency)- , c (List CommaFSep (Identity PkgconfigDependency) PkgconfigDependency)- , c (List CommaVCat (Identity Dependency) Dependency)- , c (List CommaVCat (Identity Mixin) Mixin)- , c (List FSep (MQuoted Extension) Extension)- , c (List FSep (MQuoted Language) Language)- , c (List FSep FilePathNT String)- , c (List FSep Token String)- , c (List FSep (Identity (SymbolicPath PackageDir SourceDir)) (SymbolicPath PackageDir SourceDir))- , c (List NoCommaFSep Token' String)- , c (List VCat (MQuoted ModuleName) ModuleName)- , c (List VCat FilePathNT String)- , c (List VCat Token String)- , c (MQuoted Language)- )- => UnqualComponentName -> g Executable Executable-executableFieldGrammar n = Executable n- -- main-is is optional as conditional blocks don't have it- <$> optionalFieldDefAla "main-is" FilePathNT L.modulePath ""- <*> optionalFieldDef "scope" L.exeScope ExecutablePublic- ^^^ availableSince CabalSpecV2_0 ExecutablePublic- <*> blurFieldGrammar L.buildInfo buildInfoFieldGrammar-{-# SPECIALIZE executableFieldGrammar :: UnqualComponentName -> ParsecFieldGrammar' Executable #-}-{-# SPECIALIZE executableFieldGrammar :: UnqualComponentName -> PrettyFieldGrammar' Executable #-}------------------------------------------------------------------------------------ TestSuite------------------------------------------------------------------------------------ | An intermediate type just used for parsing the test-suite stanza.--- After validation it is converted into the proper 'TestSuite' type.-data TestSuiteStanza = TestSuiteStanza- { _testStanzaTestType :: Maybe TestType- , _testStanzaMainIs :: Maybe FilePath- , _testStanzaTestModule :: Maybe ModuleName- , _testStanzaBuildInfo :: BuildInfo- }--instance L.HasBuildInfo TestSuiteStanza where- buildInfo = testStanzaBuildInfo--testStanzaTestType :: Lens' TestSuiteStanza (Maybe TestType)-testStanzaTestType f s = fmap (\x -> s { _testStanzaTestType = x }) (f (_testStanzaTestType s))-{-# INLINE testStanzaTestType #-}--testStanzaMainIs :: Lens' TestSuiteStanza (Maybe FilePath)-testStanzaMainIs f s = fmap (\x -> s { _testStanzaMainIs = x }) (f (_testStanzaMainIs s))-{-# INLINE testStanzaMainIs #-}--testStanzaTestModule :: Lens' TestSuiteStanza (Maybe ModuleName)-testStanzaTestModule f s = fmap (\x -> s { _testStanzaTestModule = x }) (f (_testStanzaTestModule s))-{-# INLINE testStanzaTestModule #-}--testStanzaBuildInfo :: Lens' TestSuiteStanza BuildInfo-testStanzaBuildInfo f s = fmap (\x -> s { _testStanzaBuildInfo = x }) (f (_testStanzaBuildInfo s))-{-# INLINE testStanzaBuildInfo #-}--testSuiteFieldGrammar- :: ( FieldGrammar c g, Applicative (g TestSuiteStanza), Applicative (g BuildInfo)- , c (Identity ModuleName)- , c (Identity TestType)- , c (List CommaFSep (Identity ExeDependency) ExeDependency)- , c (List CommaFSep (Identity LegacyExeDependency) LegacyExeDependency)- , c (List CommaFSep (Identity PkgconfigDependency) PkgconfigDependency)- , c (List CommaVCat (Identity Dependency) Dependency)- , c (List CommaVCat (Identity Mixin) Mixin)- , c (List FSep (MQuoted Extension) Extension)- , c (List FSep (MQuoted Language) Language)- , c (List FSep FilePathNT String)- , c (List FSep Token String)- , c (List NoCommaFSep Token' String)- , c (List VCat (MQuoted ModuleName) ModuleName)- , c (List VCat FilePathNT String)- , c (List FSep (Identity (SymbolicPath PackageDir SourceDir)) (SymbolicPath PackageDir SourceDir))- , c (List VCat Token String)- , c (MQuoted Language)- )- => g TestSuiteStanza TestSuiteStanza-testSuiteFieldGrammar = TestSuiteStanza- <$> optionalField "type" testStanzaTestType- <*> optionalFieldAla "main-is" FilePathNT testStanzaMainIs- <*> optionalField "test-module" testStanzaTestModule- <*> blurFieldGrammar testStanzaBuildInfo buildInfoFieldGrammar--validateTestSuite :: Position -> TestSuiteStanza -> ParseResult TestSuite-validateTestSuite pos stanza = case _testStanzaTestType stanza of- Nothing -> return $- emptyTestSuite { testBuildInfo = _testStanzaBuildInfo stanza }-- Just tt@(TestTypeUnknown _ _) ->- pure emptyTestSuite- { testInterface = TestSuiteUnsupported tt- , testBuildInfo = _testStanzaBuildInfo stanza- }-- Just tt | tt `notElem` knownTestTypes ->- pure emptyTestSuite- { testInterface = TestSuiteUnsupported tt- , testBuildInfo = _testStanzaBuildInfo stanza- }-- Just tt@(TestTypeExe ver) -> case _testStanzaMainIs stanza of- Nothing -> do- parseFailure pos (missingField "main-is" tt)- pure emptyTestSuite- Just file -> do- when (isJust (_testStanzaTestModule stanza)) $- parseWarning pos PWTExtraBenchmarkModule (extraField "test-module" tt)- pure emptyTestSuite- { testInterface = TestSuiteExeV10 ver file- , testBuildInfo = _testStanzaBuildInfo stanza- }-- Just tt@(TestTypeLib ver) -> case _testStanzaTestModule stanza of- Nothing -> do- parseFailure pos (missingField "test-module" tt)- pure emptyTestSuite- Just module_ -> do- when (isJust (_testStanzaMainIs stanza)) $- parseWarning pos PWTExtraMainIs (extraField "main-is" tt)- pure emptyTestSuite- { testInterface = TestSuiteLibV09 ver module_- , testBuildInfo = _testStanzaBuildInfo stanza- }-- where- missingField name tt = "The '" ++ name ++ "' field is required for the "- ++ prettyShow tt ++ " test suite type."-- extraField name tt = "The '" ++ name ++ "' field is not used for the '"- ++ prettyShow tt ++ "' test suite type."--unvalidateTestSuite :: TestSuite -> TestSuiteStanza-unvalidateTestSuite t = TestSuiteStanza- { _testStanzaTestType = ty- , _testStanzaMainIs = ma- , _testStanzaTestModule = mo- , _testStanzaBuildInfo = testBuildInfo t- }- where- (ty, ma, mo) = case testInterface t of- TestSuiteExeV10 ver file -> (Just $ TestTypeExe ver, Just file, Nothing)- TestSuiteLibV09 ver modu -> (Just $ TestTypeLib ver, Nothing, Just modu)- _ -> (Nothing, Nothing, Nothing)------------------------------------------------------------------------------------ Benchmark------------------------------------------------------------------------------------ | An intermediate type just used for parsing the benchmark stanza.--- After validation it is converted into the proper 'Benchmark' type.-data BenchmarkStanza = BenchmarkStanza- { _benchmarkStanzaBenchmarkType :: Maybe BenchmarkType- , _benchmarkStanzaMainIs :: Maybe FilePath- , _benchmarkStanzaBenchmarkModule :: Maybe ModuleName- , _benchmarkStanzaBuildInfo :: BuildInfo- }--instance L.HasBuildInfo BenchmarkStanza where- buildInfo = benchmarkStanzaBuildInfo--benchmarkStanzaBenchmarkType :: Lens' BenchmarkStanza (Maybe BenchmarkType)-benchmarkStanzaBenchmarkType f s = fmap (\x -> s { _benchmarkStanzaBenchmarkType = x }) (f (_benchmarkStanzaBenchmarkType s))-{-# INLINE benchmarkStanzaBenchmarkType #-}--benchmarkStanzaMainIs :: Lens' BenchmarkStanza (Maybe FilePath)-benchmarkStanzaMainIs f s = fmap (\x -> s { _benchmarkStanzaMainIs = x }) (f (_benchmarkStanzaMainIs s))-{-# INLINE benchmarkStanzaMainIs #-}--benchmarkStanzaBenchmarkModule :: Lens' BenchmarkStanza (Maybe ModuleName)-benchmarkStanzaBenchmarkModule f s = fmap (\x -> s { _benchmarkStanzaBenchmarkModule = x }) (f (_benchmarkStanzaBenchmarkModule s))-{-# INLINE benchmarkStanzaBenchmarkModule #-}--benchmarkStanzaBuildInfo :: Lens' BenchmarkStanza BuildInfo-benchmarkStanzaBuildInfo f s = fmap (\x -> s { _benchmarkStanzaBuildInfo = x }) (f (_benchmarkStanzaBuildInfo s))-{-# INLINE benchmarkStanzaBuildInfo #-}--benchmarkFieldGrammar- :: ( FieldGrammar c g, Applicative (g BenchmarkStanza), Applicative (g BuildInfo)- , c (Identity BenchmarkType)- , c (Identity ModuleName)- , c (List CommaFSep (Identity ExeDependency) ExeDependency)- , c (List CommaFSep (Identity LegacyExeDependency) LegacyExeDependency)- , c (List CommaFSep (Identity PkgconfigDependency) PkgconfigDependency)- , c (List CommaVCat (Identity Dependency) Dependency)- , c (List CommaVCat (Identity Mixin) Mixin)- , c (List FSep (MQuoted Extension) Extension)- , c (List FSep (MQuoted Language) Language)- , c (List FSep FilePathNT String)- , c (List FSep Token String)- , c (List NoCommaFSep Token' String)- , c (List VCat (MQuoted ModuleName) ModuleName)- , c (List VCat FilePathNT String)- , c (List FSep (Identity (SymbolicPath PackageDir SourceDir)) (SymbolicPath PackageDir SourceDir))- , c (List VCat Token String)- , c (MQuoted Language)- )- => g BenchmarkStanza BenchmarkStanza-benchmarkFieldGrammar = BenchmarkStanza- <$> optionalField "type" benchmarkStanzaBenchmarkType- <*> optionalFieldAla "main-is" FilePathNT benchmarkStanzaMainIs- <*> optionalField "benchmark-module" benchmarkStanzaBenchmarkModule- <*> blurFieldGrammar benchmarkStanzaBuildInfo buildInfoFieldGrammar--validateBenchmark :: Position -> BenchmarkStanza -> ParseResult Benchmark-validateBenchmark pos stanza = case _benchmarkStanzaBenchmarkType stanza of- Nothing -> pure emptyBenchmark- { benchmarkBuildInfo = _benchmarkStanzaBuildInfo stanza }-- Just tt@(BenchmarkTypeUnknown _ _) -> pure emptyBenchmark- { benchmarkInterface = BenchmarkUnsupported tt- , benchmarkBuildInfo = _benchmarkStanzaBuildInfo stanza- }-- Just tt | tt `notElem` knownBenchmarkTypes -> pure emptyBenchmark- { benchmarkInterface = BenchmarkUnsupported tt- , benchmarkBuildInfo = _benchmarkStanzaBuildInfo stanza- }-- Just tt@(BenchmarkTypeExe ver) -> case _benchmarkStanzaMainIs stanza of- Nothing -> do- parseFailure pos (missingField "main-is" tt)- pure emptyBenchmark- Just file -> do- when (isJust (_benchmarkStanzaBenchmarkModule stanza)) $- parseWarning pos PWTExtraBenchmarkModule (extraField "benchmark-module" tt)- pure emptyBenchmark- { benchmarkInterface = BenchmarkExeV10 ver file- , benchmarkBuildInfo = _benchmarkStanzaBuildInfo stanza- }-- where- missingField name tt = "The '" ++ name ++ "' field is required for the "- ++ prettyShow tt ++ " benchmark type."-- extraField name tt = "The '" ++ name ++ "' field is not used for the '"- ++ prettyShow tt ++ "' benchmark type."--unvalidateBenchmark :: Benchmark -> BenchmarkStanza-unvalidateBenchmark b = BenchmarkStanza- { _benchmarkStanzaBenchmarkType = ty- , _benchmarkStanzaMainIs = ma- , _benchmarkStanzaBenchmarkModule = mo- , _benchmarkStanzaBuildInfo = benchmarkBuildInfo b- }- where- (ty, ma, mo) = case benchmarkInterface b of- BenchmarkExeV10 ver "" -> (Just $ BenchmarkTypeExe ver, Nothing, Nothing)- BenchmarkExeV10 ver ma' -> (Just $ BenchmarkTypeExe ver, Just ma', Nothing)- _ -> (Nothing, Nothing, Nothing)------------------------------------------------------------------------------------ Build info----------------------------------------------------------------------------------buildInfoFieldGrammar- :: ( FieldGrammar c g, Applicative (g BuildInfo)- , c (List CommaFSep (Identity ExeDependency) ExeDependency)- , c (List CommaFSep (Identity LegacyExeDependency) LegacyExeDependency)- , c (List CommaFSep (Identity PkgconfigDependency) PkgconfigDependency)- , c (List CommaVCat (Identity Dependency) Dependency)- , c (List CommaVCat (Identity Mixin) Mixin)- , c (List FSep (MQuoted Extension) Extension)- , c (List FSep (MQuoted Language) Language)- , c (List FSep FilePathNT String)- , c (List FSep Token String)- , c (List NoCommaFSep Token' String)- , c (List VCat (MQuoted ModuleName) ModuleName)- , c (List VCat FilePathNT String)- , c (List FSep (Identity (SymbolicPath PackageDir SourceDir)) (SymbolicPath PackageDir SourceDir))- , c (List VCat Token String)- , c (MQuoted Language)- )- => g BuildInfo BuildInfo-buildInfoFieldGrammar = BuildInfo- <$> booleanFieldDef "buildable" L.buildable True- <*> monoidalFieldAla "build-tools" (alaList CommaFSep) L.buildTools- ^^^ deprecatedSince CabalSpecV2_0- "Please use 'build-tool-depends' field"- ^^^ removedIn CabalSpecV3_0- "Please use 'build-tool-depends' field."- <*> monoidalFieldAla "build-tool-depends" (alaList CommaFSep) L.buildToolDepends- -- {- ^^^ availableSince [2,0] [] -}- -- here, we explicitly want to recognise build-tool-depends for all Cabal files- -- as otherwise cabal new-build cannot really work.- --- -- I.e. we don't want trigger unknown field warning- <*> monoidalFieldAla "cpp-options" (alaList' NoCommaFSep Token') L.cppOptions- <*> monoidalFieldAla "asm-options" (alaList' NoCommaFSep Token') L.asmOptions- ^^^ availableSince CabalSpecV3_0 []- <*> monoidalFieldAla "cmm-options" (alaList' NoCommaFSep Token') L.cmmOptions- ^^^ availableSince CabalSpecV3_0 []- <*> monoidalFieldAla "cc-options" (alaList' NoCommaFSep Token') L.ccOptions- <*> monoidalFieldAla "cxx-options" (alaList' NoCommaFSep Token') L.cxxOptions- ^^^ availableSince CabalSpecV2_2 []- <*> monoidalFieldAla "ld-options" (alaList' NoCommaFSep Token') L.ldOptions- <*> monoidalFieldAla "hsc2hs-options" (alaList' NoCommaFSep Token') L.hsc2hsOptions- ^^^ availableSince CabalSpecV3_6 []- <*> monoidalFieldAla "pkgconfig-depends" (alaList CommaFSep) L.pkgconfigDepends- <*> monoidalFieldAla "frameworks" (alaList' FSep Token) L.frameworks- <*> monoidalFieldAla "extra-framework-dirs" (alaList' FSep FilePathNT) L.extraFrameworkDirs- <*> monoidalFieldAla "asm-sources" (alaList' VCat FilePathNT) L.asmSources- ^^^ availableSince CabalSpecV3_0 []- <*> monoidalFieldAla "cmm-sources" (alaList' VCat FilePathNT) L.cmmSources- ^^^ availableSince CabalSpecV3_0 []- <*> monoidalFieldAla "c-sources" (alaList' VCat FilePathNT) L.cSources- <*> monoidalFieldAla "cxx-sources" (alaList' VCat FilePathNT) L.cxxSources- ^^^ availableSince CabalSpecV2_2 []- <*> monoidalFieldAla "js-sources" (alaList' VCat FilePathNT) L.jsSources- <*> hsSourceDirsGrammar- <*> monoidalFieldAla "other-modules" formatOtherModules L.otherModules- <*> monoidalFieldAla "virtual-modules" (alaList' VCat MQuoted) L.virtualModules- ^^^ availableSince CabalSpecV2_2 []- <*> monoidalFieldAla "autogen-modules" (alaList' VCat MQuoted) L.autogenModules- ^^^ availableSince CabalSpecV2_0 []- <*> optionalFieldAla "default-language" MQuoted L.defaultLanguage- ^^^ availableSince CabalSpecV1_10 Nothing- <*> monoidalFieldAla "other-languages" (alaList' FSep MQuoted) L.otherLanguages- ^^^ availableSince CabalSpecV1_10 []- <*> monoidalFieldAla "default-extensions" (alaList' FSep MQuoted) L.defaultExtensions- ^^^ availableSince CabalSpecV1_10 []- <*> monoidalFieldAla "other-extensions" formatOtherExtensions L.otherExtensions- ^^^ availableSinceWarn CabalSpecV1_10- <*> monoidalFieldAla "extensions" (alaList' FSep MQuoted) L.oldExtensions- ^^^ deprecatedSince CabalSpecV1_12- "Please use 'default-extensions' or 'other-extensions' fields."- ^^^ removedIn CabalSpecV3_0- "Please use 'default-extensions' or 'other-extensions' fields."- <*> monoidalFieldAla "extra-libraries" (alaList' VCat Token) L.extraLibs- <*> monoidalFieldAla "extra-ghci-libraries" (alaList' VCat Token) L.extraGHCiLibs- <*> monoidalFieldAla "extra-bundled-libraries" (alaList' VCat Token) L.extraBundledLibs- <*> monoidalFieldAla "extra-library-flavours" (alaList' VCat Token) L.extraLibFlavours- <*> monoidalFieldAla "extra-dynamic-library-flavours" (alaList' VCat Token) L.extraDynLibFlavours- ^^^ availableSince CabalSpecV3_0 []- <*> monoidalFieldAla "extra-lib-dirs" (alaList' FSep FilePathNT) L.extraLibDirs- <*> monoidalFieldAla "include-dirs" (alaList' FSep FilePathNT) L.includeDirs- <*> monoidalFieldAla "includes" (alaList' FSep FilePathNT) L.includes- <*> monoidalFieldAla "autogen-includes" (alaList' FSep FilePathNT) L.autogenIncludes- ^^^ availableSince CabalSpecV3_0 []- <*> monoidalFieldAla "install-includes" (alaList' FSep FilePathNT) L.installIncludes- <*> optionsFieldGrammar- <*> profOptionsFieldGrammar- <*> sharedOptionsFieldGrammar- <*> pure mempty -- static-options ???- <*> prefixedFields "x-" L.customFieldsBI- <*> monoidalFieldAla "build-depends" formatDependencyList L.targetBuildDepends- <*> monoidalFieldAla "mixins" formatMixinList L.mixins- ^^^ availableSince CabalSpecV2_0 []-{-# SPECIALIZE buildInfoFieldGrammar :: ParsecFieldGrammar' BuildInfo #-}-{-# SPECIALIZE buildInfoFieldGrammar :: PrettyFieldGrammar' BuildInfo #-}--hsSourceDirsGrammar- :: ( FieldGrammar c g, Applicative (g BuildInfo)- , c (List FSep (Identity (SymbolicPath PackageDir SourceDir)) (SymbolicPath PackageDir SourceDir))- )- => g BuildInfo [SymbolicPath PackageDir SourceDir]-hsSourceDirsGrammar = (++)- <$> monoidalFieldAla "hs-source-dirs" formatHsSourceDirs L.hsSourceDirs- <*> monoidalFieldAla "hs-source-dir" (alaList FSep) wrongLens- --- https://github.com/haskell/cabal/commit/49e3cdae3bdf21b017ccd42e66670ca402e22b44- ^^^ deprecatedSince CabalSpecV1_2 "Please use 'hs-source-dirs'"- ^^^ removedIn CabalSpecV3_0 "Please use 'hs-source-dirs' field."- where- -- TODO: make pretty printer aware of CabalSpecVersion- wrongLens :: Functor f => LensLike' f BuildInfo [SymbolicPath PackageDir SourceDir]- wrongLens f bi = (\fps -> set L.hsSourceDirs fps bi) <$> f []--optionsFieldGrammar- :: (FieldGrammar c g, Applicative (g BuildInfo), c (List NoCommaFSep Token' String))- => g BuildInfo (PerCompilerFlavor [String])-optionsFieldGrammar = PerCompilerFlavor- <$> monoidalFieldAla "ghc-options" (alaList' NoCommaFSep Token') (extract GHC)- <*> monoidalFieldAla "ghcjs-options" (alaList' NoCommaFSep Token') (extract GHCJS)- -- NOTE: Hugs, NHC and JHC are not supported anymore, but these- -- fields are kept around so that we can still parse legacy .cabal- -- files that have them.- <* knownField "jhc-options"- <* knownField "hugs-options"- <* knownField "nhc98-options"- where- extract :: CompilerFlavor -> ALens' BuildInfo [String]- extract flavor = L.options . lookupLens flavor--profOptionsFieldGrammar- :: (FieldGrammar c g, Applicative (g BuildInfo), c (List NoCommaFSep Token' String))- => g BuildInfo (PerCompilerFlavor [String])-profOptionsFieldGrammar = PerCompilerFlavor- <$> monoidalFieldAla "ghc-prof-options" (alaList' NoCommaFSep Token') (extract GHC)- <*> monoidalFieldAla "ghcjs-prof-options" (alaList' NoCommaFSep Token') (extract GHCJS)- where- extract :: CompilerFlavor -> ALens' BuildInfo [String]- extract flavor = L.profOptions . lookupLens flavor--sharedOptionsFieldGrammar- :: (FieldGrammar c g, Applicative (g BuildInfo), c (List NoCommaFSep Token' String))- => g BuildInfo (PerCompilerFlavor [String])-sharedOptionsFieldGrammar = PerCompilerFlavor- <$> monoidalFieldAla "ghc-shared-options" (alaList' NoCommaFSep Token') (extract GHC)- <*> monoidalFieldAla "ghcjs-shared-options" (alaList' NoCommaFSep Token') (extract GHCJS)- where- extract :: CompilerFlavor -> ALens' BuildInfo [String]- extract flavor = L.sharedOptions . lookupLens flavor--lookupLens :: (Functor f, Monoid v) => CompilerFlavor -> LensLike' f (PerCompilerFlavor v) v-lookupLens k f p@(PerCompilerFlavor ghc ghcjs)- | k == GHC = (\n -> PerCompilerFlavor n ghcjs) <$> f ghc- | k == GHCJS = (\n -> PerCompilerFlavor ghc n) <$> f ghcjs- | otherwise = p <$ f mempty------------------------------------------------------------------------------------ Flag----------------------------------------------------------------------------------flagFieldGrammar- :: (FieldGrammar c g, Applicative (g PackageFlag))- => FlagName -> g PackageFlag PackageFlag-flagFieldGrammar name = MkPackageFlag name- <$> freeTextFieldDef "description" L.flagDescription- <*> booleanFieldDef "default" L.flagDefault True- <*> booleanFieldDef "manual" L.flagManual False-{-# SPECIALIZE flagFieldGrammar :: FlagName -> ParsecFieldGrammar' PackageFlag #-}-{-# SPECIALIZE flagFieldGrammar :: FlagName -> PrettyFieldGrammar' PackageFlag #-}------------------------------------------------------------------------------------ SourceRepo----------------------------------------------------------------------------------sourceRepoFieldGrammar- :: (FieldGrammar c g, Applicative (g SourceRepo), c (Identity RepoType), c Token, c FilePathNT)- => RepoKind -> g SourceRepo SourceRepo-sourceRepoFieldGrammar kind = SourceRepo kind- <$> optionalField "type" L.repoType- <*> freeTextField "location" L.repoLocation- <*> optionalFieldAla "module" Token L.repoModule- <*> optionalFieldAla "branch" Token L.repoBranch- <*> optionalFieldAla "tag" Token L.repoTag- <*> optionalFieldAla "subdir" FilePathNT L.repoSubdir-{-# SPECIALIZE sourceRepoFieldGrammar :: RepoKind -> ParsecFieldGrammar' SourceRepo #-}-{-# SPECIALIZE sourceRepoFieldGrammar :: RepoKind -> PrettyFieldGrammar' SourceRepo #-}------------------------------------------------------------------------------------ SetupBuildInfo----------------------------------------------------------------------------------setupBInfoFieldGrammar- :: (FieldGrammar c g, Functor (g SetupBuildInfo), c (List CommaVCat (Identity Dependency) Dependency))- => Bool -> g SetupBuildInfo SetupBuildInfo-setupBInfoFieldGrammar def = flip SetupBuildInfo def- <$> monoidalFieldAla "setup-depends" (alaList CommaVCat) L.setupDepends-{-# SPECIALIZE setupBInfoFieldGrammar :: Bool -> ParsecFieldGrammar' SetupBuildInfo #-}-{-# SPECIALIZE setupBInfoFieldGrammar :: Bool -> PrettyFieldGrammar' SetupBuildInfo #-}------------------------------------------------------------------------------------ Define how field values should be formatted for 'pretty'.----------------------------------------------------------------------------------formatDependencyList :: [Dependency] -> List CommaVCat (Identity Dependency) Dependency-formatDependencyList = alaList CommaVCat--formatMixinList :: [Mixin] -> List CommaVCat (Identity Mixin) Mixin-formatMixinList = alaList CommaVCat--formatExtraSourceFiles :: [FilePath] -> List VCat FilePathNT FilePath-formatExtraSourceFiles = alaList' VCat FilePathNT--formatExposedModules :: [ModuleName] -> List VCat (MQuoted ModuleName) ModuleName-formatExposedModules = alaList' VCat MQuoted--formatHsSourceDirs :: [SymbolicPath PackageDir SourceDir] -> List FSep (Identity (SymbolicPath PackageDir SourceDir)) (SymbolicPath PackageDir SourceDir)-formatHsSourceDirs = alaList FSep--formatOtherExtensions :: [Extension] -> List FSep (MQuoted Extension) Extension-formatOtherExtensions = alaList' FSep MQuoted--formatOtherModules :: [ModuleName] -> List VCat (MQuoted ModuleName) ModuleName-formatOtherModules = alaList' VCat MQuoted------------------------------------------------------------------------------------ newtypes------------------------------------------------------------------------------------ | Compat FilePath accepts empty file path,--- but issues a warning.------ There are simply too many (~1200) package definition files------ @--- license-file: ""--- @------ and------ @--- data-dir: ""--- @------ across Hackage to outrule them completely.--- I suspect some of them are generated (e.g. formatted) by machine.----newtype CompatFilePath = CompatFilePath { getCompatFilePath :: FilePath } -- TODO: Change to use SymPath--instance Newtype String CompatFilePath--instance Parsec CompatFilePath where- parsec = do- token <- parsecToken- if null token- then do- parsecWarning PWTEmptyFilePath "empty FilePath"- return (CompatFilePath "")- else return (CompatFilePath token)--instance Pretty CompatFilePath where- pretty = showToken . getCompatFilePath--newtype CompatLicenseFile = CompatLicenseFile { getCompatLicenseFile :: [SymbolicPath PackageDir LicenseFile] }--instance Newtype [SymbolicPath PackageDir LicenseFile] CompatLicenseFile---- TODO-instance Parsec CompatLicenseFile where- parsec = emptyToken <|> CompatLicenseFile . unpack' (alaList FSep) <$> parsec- where- emptyToken = P.try $ do- token <- parsecToken- if null token- then return (CompatLicenseFile [])- else P.unexpected "non-empty-token"--instance Pretty CompatLicenseFile where- pretty = pretty . pack' (alaList FSep) . getCompatLicenseFile------------------------------------------------------------------------------------ vim syntax definitions------------------------------------------------------------------------------------ | '_syntaxFieldNames' and '_syntaxExtensions'--- are for generating VIM syntax file definitions.----_syntaxFieldNames :: IO ()-_syntaxFieldNames = sequence_- [ BS8.putStrLn $ " \\ " <> n- | n <- nub $ sort $ mconcat- [ fieldGrammarKnownFieldList packageDescriptionFieldGrammar- , fieldGrammarKnownFieldList $ libraryFieldGrammar LMainLibName- , fieldGrammarKnownFieldList $ executableFieldGrammar "exe"- , fieldGrammarKnownFieldList $ foreignLibFieldGrammar "flib"- , fieldGrammarKnownFieldList testSuiteFieldGrammar- , fieldGrammarKnownFieldList benchmarkFieldGrammar- , fieldGrammarKnownFieldList $ flagFieldGrammar (error "flagname")- , fieldGrammarKnownFieldList $ sourceRepoFieldGrammar (error "repokind")- , fieldGrammarKnownFieldList $ setupBInfoFieldGrammar True- ]- ]--_syntaxExtensions :: IO ()-_syntaxExtensions = sequence_- [ putStrLn $ " \\ " <> e- | e <- ["Safe","Trustworthy","Unsafe"]- ++ es- ++ map ("No"++) es- ]- where- es = nub $ sort- [ prettyShow e- | e <- [ minBound .. maxBound ]- , e `notElem` [Safe,Unsafe,Trustworthy]- ]
− src/Distribution/PackageDescription/Parsec.hs
@@ -1,980 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE Rank2Types #-}-{-# LANGUAGE ScopedTypeVariables #-}--------------------------------------------------------------------------------- |--- Module : Distribution.PackageDescription.Parsec--- Copyright : Isaac Jones 2003-2005--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ This defined parsers and partial pretty printers for the @.cabal@ format.--module Distribution.PackageDescription.Parsec (- -- * Package descriptions- readGenericPackageDescription,- parseGenericPackageDescription,- parseGenericPackageDescriptionMaybe,-- -- ** Parsing- ParseResult,- runParseResult,-- -- * New-style spec-version- scanSpecVersion,-- -- ** Supplementary build information- readHookedBuildInfo,- parseHookedBuildInfo,- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Control.Monad.State.Strict (StateT, execStateT)-import Control.Monad.Trans.Class (lift)-import Distribution.CabalSpecVersion-import Distribution.Compat.Lens-import Distribution.FieldGrammar-import Distribution.FieldGrammar.Parsec (NamelessField (..))-import Distribution.Fields.ConfVar (parseConditionConfVar)-import Distribution.Fields.Field (FieldName, getName)-import Distribution.Fields.LexerMonad (LexWarning, toPWarnings)-import Distribution.Fields.Parser-import Distribution.Fields.ParseResult-import Distribution.PackageDescription-import Distribution.PackageDescription.Configuration (freeVars, transformAllBuildInfos)-import Distribution.PackageDescription.FieldGrammar-import Distribution.PackageDescription.Quirks (patchQuirks)-import Distribution.Parsec (parsec, simpleParsecBS)-import Distribution.Parsec.FieldLineStream (fieldLineStreamFromBS)-import Distribution.Parsec.Position (Position (..), zeroPos)-import Distribution.Parsec.Warning (PWarnType (..))-import Distribution.Pretty (prettyShow)-import Distribution.Simple.Utils (fromUTF8BS, toUTF8BS)-import Distribution.Utils.Generic (breakMaybe, unfoldrM, validateUTF8)-import Distribution.Verbosity (Verbosity)-import Distribution.Version (Version, mkVersion, versionNumbers)--import qualified Data.ByteString as BS-import qualified Data.ByteString.Char8 as BS8-import qualified Data.Map.Strict as Map-import qualified Data.Set as Set-import qualified Distribution.Compat.Newtype as Newtype-import qualified Distribution.Compat.NonEmptySet as NES-import qualified Distribution.Types.BuildInfo.Lens as L-import qualified Distribution.Types.Executable.Lens as L-import qualified Distribution.Types.ForeignLib.Lens as L-import qualified Distribution.Types.GenericPackageDescription.Lens as L-import qualified Distribution.Types.PackageDescription.Lens as L-import qualified Distribution.Types.SetupBuildInfo.Lens as L-import qualified Text.Parsec as P---- ------------------------------------------------------------------ Parsing--- ------------------------------------------------------------------- | Parse the given package file.-readGenericPackageDescription :: Verbosity -> FilePath -> IO GenericPackageDescription-readGenericPackageDescription = readAndParseFile parseGenericPackageDescription----------------------------------------------------------------------------------- | Parses the given file into a 'GenericPackageDescription'.------ In Cabal 1.2 the syntax for package descriptions was changed to a format--- with sections and possibly indented property descriptions.----parseGenericPackageDescription :: BS.ByteString -> ParseResult GenericPackageDescription-parseGenericPackageDescription bs = do- -- set scanned version- setCabalSpecVersion ver-- csv <- case ver of- -- if we get too new version, fail right away- Just v -> case cabalSpecFromVersionDigits (versionNumbers v) of- Just csv -> return (Just csv)- Nothing -> parseFatalFailure zeroPos $- "Unsupported cabal-version " ++ prettyShow v ++ ". See https://github.com/haskell/cabal/issues/4899."- _ -> pure Nothing-- case readFields' bs'' of- Right (fs, lexWarnings) -> do- when patched $- parseWarning zeroPos PWTQuirkyCabalFile "Legacy cabal file"- -- UTF8 is validated in a prepass step, afterwards parsing is lenient.- parseGenericPackageDescription' csv lexWarnings invalidUtf8 fs- -- TODO: better marshalling of errors- Left perr -> parseFatalFailure pos (show perr) where- ppos = P.errorPos perr- pos = Position (P.sourceLine ppos) (P.sourceColumn ppos)- where- (patched, bs') = patchQuirks bs- ver = scanSpecVersion bs'-- invalidUtf8 = validateUTF8 bs'-- -- if there are invalid utf8 characters, we make the bytestring valid.- bs'' = case invalidUtf8 of- Nothing -> bs'- Just _ -> toUTF8BS (fromUTF8BS bs')----- | 'Maybe' variant of 'parseGenericPackageDescription'-parseGenericPackageDescriptionMaybe :: BS.ByteString -> Maybe GenericPackageDescription-parseGenericPackageDescriptionMaybe =- either (const Nothing) Just . snd . runParseResult . parseGenericPackageDescription--fieldlinesToBS :: [FieldLine ann] -> BS.ByteString-fieldlinesToBS = BS.intercalate "\n" . map (\(FieldLine _ bs) -> bs)---- Monad in which sections are parsed-type SectionParser = StateT SectionS ParseResult---- | State of section parser-data SectionS = SectionS- { _stateGpd :: !GenericPackageDescription- , _stateCommonStanzas :: !(Map String CondTreeBuildInfo)- }--stateGpd :: Lens' SectionS GenericPackageDescription-stateGpd f (SectionS gpd cs) = (\x -> SectionS x cs) <$> f gpd-{-# INLINE stateGpd #-}--stateCommonStanzas :: Lens' SectionS (Map String CondTreeBuildInfo)-stateCommonStanzas f (SectionS gpd cs) = SectionS gpd <$> f cs-{-# INLINE stateCommonStanzas #-}---- Note [Accumulating parser]------ This parser has two "states":--- * first we parse fields of PackageDescription--- * then we parse sections (libraries, executables, etc)-parseGenericPackageDescription'- :: Maybe CabalSpecVersion- -> [LexWarning]- -> Maybe Int- -> [Field Position]- -> ParseResult GenericPackageDescription-parseGenericPackageDescription' scannedVer lexWarnings utf8WarnPos fs = do- parseWarnings (toPWarnings lexWarnings)- for_ utf8WarnPos $ \pos ->- parseWarning zeroPos PWTUTF $ "UTF8 encoding problem at byte offset " ++ show pos- let (syntax, fs') = sectionizeFields fs- let (fields, sectionFields) = takeFields fs'-- -- cabal-version- specVer <- case scannedVer of- Just v -> return v- Nothing -> case Map.lookup "cabal-version" fields >>= safeLast of- Nothing -> return CabalSpecV1_0- Just (MkNamelessField pos fls) -> do- -- version will be parsed twice, therefore we parse without warnings.- v <- withoutWarnings $- Newtype.unpack' SpecVersion <$>- -- Use version with || and && but before addition of ^>= and removal of -any- runFieldParser pos parsec CabalSpecV1_24 fls-- -- if it were at the beginning, scanner would found it- when (v >= CabalSpecV2_2) $ parseFailure pos $- "cabal-version should be at the beginning of the file starting with spec version 2.2. " ++- "See https://github.com/haskell/cabal/issues/4899"-- return v-- -- reset cabal version, it might not be set- let specVer' = mkVersion (cabalSpecToVersionDigits specVer)- setCabalSpecVersion (Just specVer')-- -- Package description- pd <- parseFieldGrammar specVer fields packageDescriptionFieldGrammar-- -- Check that scanned and parsed versions match.- unless (specVer == specVersion pd) $ parseFailure zeroPos $- "Scanned and parsed cabal-versions don't match " ++- prettyShow (SpecVersion specVer) ++ " /= " ++ prettyShow (SpecVersion (specVersion pd))-- maybeWarnCabalVersion syntax pd-- -- Sections- let gpd = emptyGenericPackageDescription- & L.packageDescription .~ pd- gpd1 <- view stateGpd <$> execStateT (goSections specVer sectionFields) (SectionS gpd Map.empty)-- let gpd2 = postProcessInternalDeps specVer gpd1- checkForUndefinedFlags gpd2- checkForUndefinedCustomSetup gpd2- -- See nothunks test, without this deepseq we get (at least):- -- Thunk in ThunkInfo {thunkContext = ["PackageIdentifier","PackageDescription","GenericPackageDescription"]}- --- -- TODO: re-benchmark, whether `deepseq` is important (both cabal-benchmarks and solver-benchmarks)- -- TODO: remove the need for deepseq if `deepseq` in fact matters- -- NOTE: IIRC it does affect (maximal) memory usage, which causes less GC pressure- gpd2 `deepseq` return gpd2- where- safeLast :: [a] -> Maybe a- safeLast = listToMaybe . reverse-- newSyntaxVersion :: CabalSpecVersion- newSyntaxVersion = CabalSpecV1_2-- maybeWarnCabalVersion :: Syntax -> PackageDescription -> ParseResult ()- maybeWarnCabalVersion syntax pkg- | syntax == NewSyntax && specVersion pkg < newSyntaxVersion- = parseWarning zeroPos PWTNewSyntax $- "A package using section syntax must specify at least\n"- ++ "'cabal-version: >= 1.2'."-- maybeWarnCabalVersion syntax pkg- | syntax == OldSyntax && specVersion pkg >= newSyntaxVersion- = parseWarning zeroPos PWTOldSyntax $- "A package using 'cabal-version: "- ++ prettyShow (SpecVersion (specVersion pkg))- ++ "' must use section syntax. See the Cabal user guide for details."-- maybeWarnCabalVersion _ _ = return ()--goSections :: CabalSpecVersion -> [Field Position] -> SectionParser ()-goSections specVer = traverse_ process- where- process (Field (Name pos name) _) =- lift $ parseWarning pos PWTTrailingFields $- "Ignoring trailing fields after sections: " ++ show name- process (Section name args secFields) =- parseSection name args secFields-- snoc x xs = xs ++ [x]-- hasCommonStanzas = specHasCommonStanzas specVer-- -- we need signature, because this is polymorphic, but not-closed- parseCondTree'- :: L.HasBuildInfo a- => ParsecFieldGrammar' a -- ^ grammar- -> (BuildInfo -> a)- -> Map String CondTreeBuildInfo -- ^ common stanzas- -> [Field Position]- -> ParseResult (CondTree ConfVar [Dependency] a)- parseCondTree' = parseCondTreeWithCommonStanzas specVer-- parseSection :: Name Position -> [SectionArg Position] -> [Field Position] -> SectionParser ()- parseSection (Name pos name) args fields- | hasCommonStanzas == NoCommonStanzas, name == "common" = lift $ do- parseWarning pos PWTUnknownSection $ "Ignoring section: common. You should set cabal-version: 2.2 or larger to use common stanzas."-- | name == "common" = do- commonStanzas <- use stateCommonStanzas- name' <- lift $ parseCommonName pos args- biTree <- lift $ parseCondTree' buildInfoFieldGrammar id commonStanzas fields-- case Map.lookup name' commonStanzas of- Nothing -> stateCommonStanzas .= Map.insert name' biTree commonStanzas- Just _ -> lift $ parseFailure pos $- "Duplicate common stanza: " ++ name'-- | name == "library" && null args = do- prev <- use $ stateGpd . L.condLibrary- when (isJust prev) $ lift $ parseFailure pos $- "Multiple main libraries; have you forgotten to specify a name for an internal library?"-- commonStanzas <- use stateCommonStanzas- let name'' = LMainLibName- lib <- lift $ parseCondTree' (libraryFieldGrammar name'') (libraryFromBuildInfo name'') commonStanzas fields- --- -- TODO check that not set- stateGpd . L.condLibrary ?= lib-- -- Sublibraries- -- TODO: check cabal-version- | name == "library" = do- commonStanzas <- use stateCommonStanzas- name' <- parseUnqualComponentName pos args- let name'' = LSubLibName name'- lib <- lift $ parseCondTree' (libraryFieldGrammar name'') (libraryFromBuildInfo name'') commonStanzas fields- -- TODO check duplicate name here?- stateGpd . L.condSubLibraries %= snoc (name', lib)-- -- TODO: check cabal-version- | name == "foreign-library" = do- commonStanzas <- use stateCommonStanzas- name' <- parseUnqualComponentName pos args- flib <- lift $ parseCondTree' (foreignLibFieldGrammar name') (fromBuildInfo' name') commonStanzas fields-- let hasType ts = foreignLibType ts /= foreignLibType mempty- unless (onAllBranches hasType flib) $ lift $ parseFailure pos $ concat- [ "Foreign library " ++ show (prettyShow name')- , " is missing required field \"type\" or the field "- , "is not present in all conditional branches. The "- , "available test types are: "- , intercalate ", " (map prettyShow knownForeignLibTypes)- ]-- -- TODO check duplicate name here?- stateGpd . L.condForeignLibs %= snoc (name', flib)-- | name == "executable" = do- commonStanzas <- use stateCommonStanzas- name' <- parseUnqualComponentName pos args- exe <- lift $ parseCondTree' (executableFieldGrammar name') (fromBuildInfo' name') commonStanzas fields- -- TODO check duplicate name here?- stateGpd . L.condExecutables %= snoc (name', exe)-- | name == "test-suite" = do- commonStanzas <- use stateCommonStanzas- name' <- parseUnqualComponentName pos args- testStanza <- lift $ parseCondTree' testSuiteFieldGrammar (fromBuildInfo' name') commonStanzas fields- testSuite <- lift $ traverse (validateTestSuite pos) testStanza-- let hasType ts = testInterface ts /= testInterface mempty- unless (onAllBranches hasType testSuite) $ lift $ parseFailure pos $ concat- [ "Test suite " ++ show (prettyShow name')- , " is missing required field \"type\" or the field "- , "is not present in all conditional branches. The "- , "available test types are: "- , intercalate ", " (map prettyShow knownTestTypes)- ]-- -- TODO check duplicate name here?- stateGpd . L.condTestSuites %= snoc (name', testSuite)-- | name == "benchmark" = do- commonStanzas <- use stateCommonStanzas- name' <- parseUnqualComponentName pos args- benchStanza <- lift $ parseCondTree' benchmarkFieldGrammar (fromBuildInfo' name') commonStanzas fields- bench <- lift $ traverse (validateBenchmark pos) benchStanza-- let hasType ts = benchmarkInterface ts /= benchmarkInterface mempty- unless (onAllBranches hasType bench) $ lift $ parseFailure pos $ concat- [ "Benchmark " ++ show (prettyShow name')- , " is missing required field \"type\" or the field "- , "is not present in all conditional branches. The "- , "available benchmark types are: "- , intercalate ", " (map prettyShow knownBenchmarkTypes)- ]-- -- TODO check duplicate name here?- stateGpd . L.condBenchmarks %= snoc (name', bench)-- | name == "flag" = do- name' <- parseNameBS pos args- name'' <- lift $ runFieldParser' [pos] parsec specVer (fieldLineStreamFromBS name') `recoverWith` mkFlagName ""- flag <- lift $ parseFields specVer fields (flagFieldGrammar name'')- -- Check default flag- stateGpd . L.genPackageFlags %= snoc flag-- | name == "custom-setup" && null args = do- sbi <- lift $ parseFields specVer fields (setupBInfoFieldGrammar False)- stateGpd . L.packageDescription . L.setupBuildInfo ?= sbi-- | name == "source-repository" = do- kind <- lift $ case args of- [SecArgName spos secName] ->- runFieldParser' [spos] parsec specVer (fieldLineStreamFromBS secName) `recoverWith` RepoHead- [] -> do- parseFailure pos "'source-repository' requires exactly one argument"- pure RepoHead- _ -> do- parseFailure pos $ "Invalid source-repository kind " ++ show args- pure RepoHead-- sr <- lift $ parseFields specVer fields (sourceRepoFieldGrammar kind)- stateGpd . L.packageDescription . L.sourceRepos %= snoc sr-- | otherwise = lift $- parseWarning pos PWTUnknownSection $ "Ignoring section: " ++ show name--parseName :: Position -> [SectionArg Position] -> SectionParser String-parseName pos args = fromUTF8BS <$> parseNameBS pos args--parseNameBS :: Position -> [SectionArg Position] -> SectionParser BS.ByteString--- TODO: use strict parser-parseNameBS pos args = case args of- [SecArgName _pos secName] ->- pure secName- [SecArgStr _pos secName] ->- pure secName- [] -> do- lift $ parseFailure pos "name required"- pure ""- _ -> do- -- TODO: pretty print args- lift $ parseFailure pos $ "Invalid name " ++ show args- pure ""--parseCommonName :: Position -> [SectionArg Position] -> ParseResult String-parseCommonName pos args = case args of- [SecArgName _pos secName] ->- pure $ fromUTF8BS secName- [SecArgStr _pos secName] ->- pure $ fromUTF8BS secName- [] -> do- parseFailure pos $ "name required"- pure ""- _ -> do- -- TODO: pretty print args- parseFailure pos $ "Invalid name " ++ show args- pure ""---- TODO: avoid conversion to 'String'.-parseUnqualComponentName :: Position -> [SectionArg Position] -> SectionParser UnqualComponentName-parseUnqualComponentName pos args = mkUnqualComponentName <$> parseName pos args---- | Parse a non-recursive list of fields.-parseFields- :: CabalSpecVersion- -> [Field Position] -- ^ fields to be parsed- -> ParsecFieldGrammar' a- -> ParseResult a-parseFields v fields grammar = do- let (fs0, ss) = partitionFields fields- traverse_ (traverse_ warnInvalidSubsection) ss- parseFieldGrammar v fs0 grammar--warnInvalidSubsection :: Section Position -> ParseResult ()-warnInvalidSubsection (MkSection (Name pos name) _ _) =- void $ parseFailure pos $ "invalid subsection " ++ show name--parseCondTree- :: forall a. L.HasBuildInfo a- => CabalSpecVersion- -> HasElif -- ^ accept @elif@- -> ParsecFieldGrammar' a -- ^ grammar- -> Map String CondTreeBuildInfo -- ^ common stanzas- -> (BuildInfo -> a) -- ^ constructor from buildInfo- -> (a -> [Dependency]) -- ^ condition extractor- -> [Field Position]- -> ParseResult (CondTree ConfVar [Dependency] a)-parseCondTree v hasElif grammar commonStanzas fromBuildInfo cond = go- where- go fields0 = do- (fields, endo) <-- if v >= CabalSpecV3_0- then processImports v fromBuildInfo commonStanzas fields0- else traverse (warnImport v) fields0 >>= \fields1 -> return (catMaybes fields1, id)-- let (fs, ss) = partitionFields fields- x <- parseFieldGrammar v fs grammar- branches <- concat <$> traverse parseIfs ss- return $ endo $ CondNode x (cond x) branches-- parseIfs :: [Section Position] -> ParseResult [CondBranch ConfVar [Dependency] a]- parseIfs [] = return []- parseIfs (MkSection (Name _ name) test fields : sections) | name == "if" = do- test' <- parseConditionConfVar test- fields' <- go fields- (elseFields, sections') <- parseElseIfs sections- return (CondBranch test' fields' elseFields : sections')- parseIfs (MkSection (Name pos name) _ _ : sections) = do- parseWarning pos PWTInvalidSubsection $ "invalid subsection " ++ show name- parseIfs sections-- parseElseIfs- :: [Section Position]- -> ParseResult (Maybe (CondTree ConfVar [Dependency] a), [CondBranch ConfVar [Dependency] a])- parseElseIfs [] = return (Nothing, [])- parseElseIfs (MkSection (Name pos name) args fields : sections) | name == "else" = do- unless (null args) $- parseFailure pos $ "`else` section has section arguments " ++ show args- elseFields <- go fields- sections' <- parseIfs sections- return (Just elseFields, sections')-- parseElseIfs (MkSection (Name _ name) test fields : sections) | hasElif == HasElif, name == "elif" = do- test' <- parseConditionConfVar test- fields' <- go fields- (elseFields, sections') <- parseElseIfs sections- -- we parse an empty 'Fields', to get empty value for a node- a <- parseFieldGrammar v mempty grammar- return (Just $ CondNode a (cond a) [CondBranch test' fields' elseFields], sections')-- parseElseIfs (MkSection (Name pos name) _ _ : sections) | name == "elif" = do- parseWarning pos PWTInvalidSubsection $ "invalid subsection \"elif\". You should set cabal-version: 2.2 or larger to use elif-conditionals."- (,) Nothing <$> parseIfs sections-- parseElseIfs sections = (,) Nothing <$> parseIfs sections--{- Note [Accumulating parser]--Note: Outdated a bit--In there parser, @'FieldDescr' a@ is transformed into @Map FieldName (a ->-FieldParser a)@. The weird value is used because we accumulate structure of-@a@ by folding over the fields. There are various reasons for that:--* Almost all fields are optional--* This is simple approach so declarative bi-directional format (parsing and-printing) of structure could be specified (list of @'FieldDescr' a@)--* There are surface syntax fields corresponding to single field in the file:- @license-file@ and @license-files@--* This is quite safe approach.--When/if we re-implement the parser to support formatting preservging roundtrip-with new AST, this all need to be rewritten.--}------------------------------------------------------------------------------------ Common stanzas------------------------------------------------------------------------------------ $commonStanzas------ [Note: Common stanzas]------ In Cabal 2.2 we support simple common stanzas:------ * Commons stanzas define 'BuildInfo'------ * import "fields" can only occur at top of other stanzas (think: imports)------ In particular __there aren't__------ * implicit stanzas------ * More specific common stanzas (executable, test-suite).--------- The approach uses the fact that 'BuildInfo' is a 'Monoid':------ @--- mergeCommonStanza' :: HasBuildInfo comp => BuildInfo -> comp -> comp--- mergeCommonStanza' bi = over L.BuildInfo (bi <>)--- @------ Real 'mergeCommonStanza' is more complicated as we have to deal with--- conditional trees.------ The approach is simple, and have good properties:------ * Common stanzas are parsed exactly once, even if not-used. Thus we report errors in them.----type CondTreeBuildInfo = CondTree ConfVar [Dependency] BuildInfo---- | Create @a@ from 'BuildInfo'.--- This class is used to implement common stanza parsing.------ Law: @view buildInfo . fromBuildInfo = id@------ This takes name, as 'FieldGrammar's take names too.-class L.HasBuildInfo a => FromBuildInfo a where- fromBuildInfo' :: UnqualComponentName -> BuildInfo -> a--libraryFromBuildInfo :: LibraryName -> BuildInfo -> Library-libraryFromBuildInfo n bi = emptyLibrary- { libName = n- , libVisibility = case n of- LMainLibName -> LibraryVisibilityPublic- LSubLibName _ -> LibraryVisibilityPrivate- , libBuildInfo = bi- }--instance FromBuildInfo BuildInfo where fromBuildInfo' _ = id-instance FromBuildInfo ForeignLib where fromBuildInfo' n bi = set L.foreignLibName n $ set L.buildInfo bi emptyForeignLib-instance FromBuildInfo Executable where fromBuildInfo' n bi = set L.exeName n $ set L.buildInfo bi emptyExecutable--instance FromBuildInfo TestSuiteStanza where- fromBuildInfo' _ bi = TestSuiteStanza Nothing Nothing Nothing bi--instance FromBuildInfo BenchmarkStanza where- fromBuildInfo' _ bi = BenchmarkStanza Nothing Nothing Nothing bi--parseCondTreeWithCommonStanzas- :: forall a. L.HasBuildInfo a- => CabalSpecVersion- -> ParsecFieldGrammar' a -- ^ grammar- -> (BuildInfo -> a) -- ^ construct fromBuildInfo- -> Map String CondTreeBuildInfo -- ^ common stanzas- -> [Field Position]- -> ParseResult (CondTree ConfVar [Dependency] a)-parseCondTreeWithCommonStanzas v grammar fromBuildInfo commonStanzas fields = do- (fields', endo) <- processImports v fromBuildInfo commonStanzas fields- x <- parseCondTree v hasElif grammar commonStanzas fromBuildInfo (view L.targetBuildDepends) fields'- return (endo x)- where- hasElif = specHasElif v--processImports- :: forall a. L.HasBuildInfo a- => CabalSpecVersion- -> (BuildInfo -> a) -- ^ construct fromBuildInfo- -> Map String CondTreeBuildInfo -- ^ common stanzas- -> [Field Position]- -> ParseResult ([Field Position], CondTree ConfVar [Dependency] a -> CondTree ConfVar [Dependency] a)-processImports v fromBuildInfo commonStanzas = go []- where- hasCommonStanzas = specHasCommonStanzas v-- getList' :: List CommaFSep Token String -> [String]- getList' = Newtype.unpack-- go acc (Field (Name pos name) _ : fields) | name == "import", hasCommonStanzas == NoCommonStanzas = do- parseWarning pos PWTUnknownField "Unknown field: import. You should set cabal-version: 2.2 or larger to use common stanzas"- go acc fields- -- supported:- go acc (Field (Name pos name) fls : fields) | name == "import" = do- names <- getList' <$> runFieldParser pos parsec v fls- names' <- for names $ \commonName ->- case Map.lookup commonName commonStanzas of- Nothing -> do- parseFailure pos $ "Undefined common stanza imported: " ++ commonName- pure Nothing- Just commonTree ->- pure (Just commonTree)-- go (acc ++ catMaybes names') fields-- -- parse actual CondTree- go acc fields = do- fields' <- catMaybes <$> traverse (warnImport v) fields- pure $ (fields', \x -> foldr (mergeCommonStanza fromBuildInfo) x acc)---- | Warn on "import" fields, also map to Maybe, so errorneous fields can be filtered-warnImport :: CabalSpecVersion -> Field Position -> ParseResult (Maybe (Field Position))-warnImport v (Field (Name pos name) _) | name == "import" = do- if specHasCommonStanzas v == NoCommonStanzas- then parseWarning pos PWTUnknownField "Unknown field: import. You should set cabal-version: 2.2 or larger to use common stanzas"- else parseWarning pos PWTUnknownField "Unknown field: import. Common stanza imports should be at the top of the enclosing section"- return Nothing-warnImport _ f = pure (Just f)--mergeCommonStanza- :: L.HasBuildInfo a- => (BuildInfo -> a)- -> CondTree ConfVar [Dependency] BuildInfo- -> CondTree ConfVar [Dependency] a- -> CondTree ConfVar [Dependency] a-mergeCommonStanza fromBuildInfo (CondNode bi _ bis) (CondNode x _ cs) =- CondNode x' (x' ^. L.targetBuildDepends) cs'- where- -- new value is old value with buildInfo field _prepended_.- x' = x & L.buildInfo %~ (bi <>)-- -- tree components are appended together.- cs' = map (fmap fromBuildInfo) bis ++ cs------------------------------------------------------------------------------------ Branches------------------------------------------------------------------------------------ Check that a property holds on all branches of a condition tree-onAllBranches :: forall v c a. Monoid a => (a -> Bool) -> CondTree v c a -> Bool-onAllBranches p = go mempty- where- -- If the current level of the tree satisfies the property, then we are- -- done. If not, then one of the conditional branches below the current node- -- must satisfy it. Each node may have multiple immediate children; we only- -- one need one to satisfy the property because the configure step uses- -- 'mappend' to join together the results of flag resolution.- go :: a -> CondTree v c a -> Bool- go acc ct = let acc' = acc `mappend` condTreeData ct- in p acc' || any (goBranch acc') (condTreeComponents ct)-- -- Both the 'true' and the 'false' block must satisfy the property.- goBranch :: a -> CondBranch v c a -> Bool- goBranch _ (CondBranch _ _ Nothing) = False- goBranch acc (CondBranch _ t (Just e)) = go acc t && go acc e------------------------------------------------------------------------------------ Post parsing checks------------------------------------------------------------------------------------ | Check that we ------ * don't use undefined flags (very bad)--- * define flags which are unused (just bad)----checkForUndefinedFlags :: GenericPackageDescription -> ParseResult ()-checkForUndefinedFlags gpd = do- let definedFlags, usedFlags :: Set.Set FlagName- definedFlags = toSetOf (L.genPackageFlags . traverse . getting flagName) gpd- usedFlags = getConst $ L.allCondTrees f gpd-- -- Note: we can check for defined, but unused flags here too.- unless (usedFlags `Set.isSubsetOf` definedFlags) $ parseFailure zeroPos $- "These flags are used without having been defined: " ++- intercalate ", " [ unFlagName fn | fn <- Set.toList $ usedFlags `Set.difference` definedFlags ]- where- f :: CondTree ConfVar c a -> Const (Set.Set FlagName) (CondTree ConfVar c a)- f ct = Const (Set.fromList (freeVars ct))---- | Since @cabal-version: 1.24@ one can specify @custom-setup@.--- Let us require it.----checkForUndefinedCustomSetup :: GenericPackageDescription -> ParseResult ()-checkForUndefinedCustomSetup gpd = do- let pd = packageDescription gpd- let csv = specVersion pd-- when (buildType pd == Custom && isNothing (setupBuildInfo pd)) $- when (csv >= CabalSpecV1_24) $ parseFailure zeroPos $- "Since cabal-version: 1.24 specifying custom-setup section is mandatory"------------------------------------------------------------------------------------ Post processing of internal dependencies------------------------------------------------------------------------------------ Note [Dependencies on sublibraries]--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~------ This is solution to https://github.com/haskell/cabal/issues/6083------ Before 'cabal-version: 3.0' we didn't have a syntax specially--- for referring to internal libraries. Internal library names--- shadowed the outside ones.------ Since 'cabal-version: 3.0' we have ability to write------ build-depends: some-package:its-sub-lib >=1.2.3------ This allows us to refer also to local packages by `this-package:sublib`.--- So since 'cabal-version: 3.4' to refer to *any*--- sublibrary we must use the two part syntax. Here's small table:------ | pre-3.4 | 3.4 and after |--- ------------------|---------------------|-------------------------------|--- pkg-name | may refer to sublib | always refers to external pkg |--- pkg-name:sublib | refers to sublib | refers to sublib |--- pkg-name:pkg-name | may refer to sublib | always refers to external pkg |------ In pre-3.4 case, if a package 'this-pkg' has a sublibrary 'pkg-name',--- all dependency definitions will refer to that sublirary.------ In 3.4 and after case, 'pkg-name' will always refer to external package,--- and to use internal library you have to say 'this-pkg:pkg-name'.------ In summary, In 3.4 and after, the internal names don't shadow,--- as there is an explicit syntax to refer to them,--- i.e. what you write is what you get;--- For pre-3.4 we post-process the file.------ Similarly, we process mixins.--- See https://github.com/haskell/cabal/issues/6281-----postProcessInternalDeps :: CabalSpecVersion -> GenericPackageDescription -> GenericPackageDescription-postProcessInternalDeps specVer gpd- | specVer >= CabalSpecV3_4 = gpd- | otherwise = transformAllBuildInfos transformBI transformSBI gpd- where- transformBI :: BuildInfo -> BuildInfo- transformBI- = over L.targetBuildDepends (concatMap transformD)- . over L.mixins (map transformM)-- transformSBI :: SetupBuildInfo -> SetupBuildInfo- transformSBI = over L.setupDepends (concatMap transformD)-- transformD :: Dependency -> [Dependency]- transformD (Dependency pn vr ln)- | uqn `Set.member` internalLibs- , LMainLibName `NES.member` ln- = case NES.delete LMainLibName ln of- Nothing -> [dep]- Just ln' -> [dep, Dependency pn vr ln']- where- uqn = packageNameToUnqualComponentName pn- dep = Dependency thisPn vr (NES.singleton (LSubLibName uqn))-- transformD d = [d]-- transformM :: Mixin -> Mixin- transformM (Mixin pn LMainLibName incl)- | uqn `Set.member` internalLibs- = mkMixin thisPn (LSubLibName uqn) incl- where- uqn = packageNameToUnqualComponentName pn-- transformM m = m-- thisPn :: PackageName- thisPn = pkgName (package (packageDescription gpd))-- internalLibs :: Set UnqualComponentName- internalLibs = Set.fromList- [ n- | (n, _) <- condSubLibraries gpd- ]------------------------------------------------------------------------------------ Old syntax------------------------------------------------------------------------------------ TODO: move to own module---- | "Sectionize" an old-style Cabal file. A sectionized file has:------ * all global fields at the beginning, followed by------ * all flag declarations, followed by------ * an optional library section, and an arbitrary number of executable--- sections (in any order).------ The current implementation just gathers all library-specific fields--- in a library section and wraps all executable stanzas in an executable--- section.-sectionizeFields :: [Field ann] -> (Syntax, [Field ann])-sectionizeFields fs = case classifyFields fs of- Just fields -> (OldSyntax, convert fields)- Nothing -> (NewSyntax, fs)- where- -- return 'Just' if all fields are simple fields- classifyFields :: [Field ann] -> Maybe [(Name ann, [FieldLine ann])]- classifyFields = traverse f- where- f (Field name fieldlines) = Just (name, fieldlines)- f _ = Nothing-- trim = BS.dropWhile isSpace' . BS.reverse . BS.dropWhile isSpace' . BS.reverse- isSpace' = (== 32)-- convert :: [(Name ann, [FieldLine ann])] -> [Field ann]- convert fields =- let- toField (name, ls) = Field name ls- -- "build-depends" is a local field now. To be backwards- -- compatible, we still allow it as a global field in old-style- -- package description files and translate it to a local field by- -- adding it to every non-empty section- (hdr0, exes0) = break ((=="executable") . getName . fst) fields- (hdr, libfs0) = partition (not . (`elem` libFieldNames) . getName . fst) hdr0-- (deps, libfs) = partition ((== "build-depends") . getName . fst)- libfs0-- exes = unfoldr toExe exes0- toExe [] = Nothing- toExe ((Name pos n, ls) : r)- | n == "executable" =- let (efs, r') = break ((== "executable") . getName . fst) r- in Just (Section (Name pos "executable") [SecArgName pos $ trim $ fieldlinesToBS ls] (map toField $ deps ++ efs), r')- toExe _ = error "unexpected input to 'toExe'"-- lib = case libfs of- [] -> []- ((Name pos _, _) : _) ->- [Section (Name pos "library") [] (map toField $ deps ++ libfs)]-- in map toField hdr ++ lib ++ exes---- | See 'sectionizeFields'.-data Syntax = OldSyntax | NewSyntax- deriving (Eq, Show)---- TODO:-libFieldNames :: [FieldName]-libFieldNames = fieldGrammarKnownFieldList (libraryFieldGrammar LMainLibName)------------------------------------------------------------------------------------ Suplementary build information----------------------------------------------------------------------------------readHookedBuildInfo :: Verbosity -> FilePath -> IO HookedBuildInfo-readHookedBuildInfo = readAndParseFile parseHookedBuildInfo--parseHookedBuildInfo :: BS.ByteString -> ParseResult HookedBuildInfo-parseHookedBuildInfo bs = case readFields' bs of- Right (fs, lexWarnings) -> do- parseHookedBuildInfo' lexWarnings fs- -- TODO: better marshalling of errors- Left perr -> parseFatalFailure zeroPos (show perr)--parseHookedBuildInfo'- :: [LexWarning]- -> [Field Position]- -> ParseResult HookedBuildInfo-parseHookedBuildInfo' lexWarnings fs = do- parseWarnings (toPWarnings lexWarnings)- (mLibFields, exes) <- stanzas fs- mLib <- parseLib mLibFields- biExes <- traverse parseExe exes- return (mLib, biExes)- where- parseLib :: Fields Position -> ParseResult (Maybe BuildInfo)- parseLib fields- | Map.null fields = pure Nothing- | otherwise = Just <$> parseFieldGrammar cabalSpecLatest fields buildInfoFieldGrammar-- parseExe :: (UnqualComponentName, Fields Position) -> ParseResult (UnqualComponentName, BuildInfo)- parseExe (n, fields) = do- bi <- parseFieldGrammar cabalSpecLatest fields buildInfoFieldGrammar- pure (n, bi)-- stanzas :: [Field Position] -> ParseResult (Fields Position, [(UnqualComponentName, Fields Position)])- stanzas fields = do- let (hdr0, exes0) = breakMaybe isExecutableField fields- hdr <- toFields hdr0- exes <- unfoldrM (traverse toExe) exes0- pure (hdr, exes)-- toFields :: [Field Position] -> ParseResult (Fields Position)- toFields fields = do- let (fields', ss) = partitionFields fields- traverse_ (traverse_ warnInvalidSubsection) ss- pure fields'-- toExe- :: ([FieldLine Position], [Field Position])- -> ParseResult ((UnqualComponentName, Fields Position), Maybe ([FieldLine Position], [Field Position]))- toExe (fss, fields) = do- name <- runFieldParser zeroPos parsec cabalSpecLatest fss- let (hdr0, rest) = breakMaybe isExecutableField fields- hdr <- toFields hdr0- pure ((name, hdr), rest)-- isExecutableField (Field (Name _ name) fss)- | name == "executable" = Just fss- | otherwise = Nothing- isExecutableField _ = Nothing------------------------------------------------------------------------------------ Scan of spec version------------------------------------------------------------------------------------ | Quickly scan new-style spec-version------ A new-style spec-version declaration begins the .cabal file and--- follow the following case-insensitive grammar (expressed in--- RFC5234 ABNF):------ @--- newstyle-spec-version-decl = "cabal-version" *WS ":" *WS newstyle-pec-version *WS------ spec-version = NUM "." NUM [ "." NUM ]------ NUM = DIGIT0 / DIGITP 1*DIGIT0--- DIGIT0 = %x30-39--- DIGITP = %x31-39--- WS = %20--- @----scanSpecVersion :: BS.ByteString -> Maybe Version-scanSpecVersion bs = do- fstline':_ <- pure (BS8.lines bs)-- -- parse <newstyle-spec-version-decl>- -- normalise: remove all whitespace, convert to lower-case- let fstline = BS.map toLowerW8 $ BS.filter (/= 0x20) fstline'- ["cabal-version",vers] <- pure (BS8.split ':' fstline)-- -- parse <spec-version>- --- -- This is currently more tolerant regarding leading 0 digits.- --- ver <- simpleParsecBS vers- guard $ case versionNumbers ver of- [_,_] -> True- [_,_,_] -> True- _ -> False-- pure ver- where- -- | Translate ['A'..'Z'] to ['a'..'z']- toLowerW8 :: Word8 -> Word8- toLowerW8 w | 0x40 < w && w < 0x5b = w+0x20- | otherwise = w
− src/Distribution/PackageDescription/PrettyPrint.hs
@@ -1,282 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--------------------------------------------------------------------------------- |--- Module : Distribution.PackageDescription.PrettyPrint--- Copyright : Jürgen Nicklisch-Franken 2010--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Stability : provisional--- Portability : portable------ Pretty printing for cabal files-----------------------------------------------------------------------------------module Distribution.PackageDescription.PrettyPrint (- -- * Generic package descriptions- writeGenericPackageDescription,- showGenericPackageDescription,- ppGenericPackageDescription,-- -- * Package descriptions- writePackageDescription,- showPackageDescription,-- -- ** Supplementary build information- writeHookedBuildInfo,- showHookedBuildInfo,-) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.CabalSpecVersion-import Distribution.Fields.Pretty-import Distribution.Compat.Lens-import Distribution.PackageDescription-import Distribution.Pretty-import Distribution.Simple.Utils (writeFileAtomic, writeUTF8File)-import Distribution.FieldGrammar (PrettyFieldGrammar', prettyFieldGrammar)-import Distribution.PackageDescription.Configuration (transformAllBuildInfos)-import Distribution.PackageDescription.FieldGrammar- (benchmarkFieldGrammar, buildInfoFieldGrammar, executableFieldGrammar, flagFieldGrammar, foreignLibFieldGrammar, libraryFieldGrammar,- packageDescriptionFieldGrammar, setupBInfoFieldGrammar, sourceRepoFieldGrammar, testSuiteFieldGrammar)--import qualified Distribution.PackageDescription.FieldGrammar as FG-import qualified Distribution.Types.BuildInfo.Lens as L-import qualified Distribution.Types.SetupBuildInfo.Lens as L--import Text.PrettyPrint (Doc, char, hsep, parens, text)--import qualified Data.ByteString.Lazy.Char8 as BS.Char8-import qualified Distribution.Compat.NonEmptySet as NES---- | Writes a .cabal file from a generic package description-writeGenericPackageDescription :: FilePath -> GenericPackageDescription -> IO ()-writeGenericPackageDescription fpath pkg = writeUTF8File fpath (showGenericPackageDescription pkg)---- | Writes a generic package description to a string-showGenericPackageDescription :: GenericPackageDescription -> String-showGenericPackageDescription gpd = showFields (const []) $ ppGenericPackageDescription v gpd- where- v = specVersion $ packageDescription gpd---- | Convert a generic package description to 'PrettyField's.-ppGenericPackageDescription :: CabalSpecVersion -> GenericPackageDescription -> [PrettyField ()]-ppGenericPackageDescription v gpd0 = concat- [ ppPackageDescription v (packageDescription gpd)- , ppSetupBInfo v (setupBuildInfo (packageDescription gpd))- , ppGenPackageFlags v (genPackageFlags gpd)- , ppCondLibrary v (condLibrary gpd)- , ppCondSubLibraries v (condSubLibraries gpd)- , ppCondForeignLibs v (condForeignLibs gpd)- , ppCondExecutables v (condExecutables gpd)- , ppCondTestSuites v (condTestSuites gpd)- , ppCondBenchmarks v (condBenchmarks gpd)- ]- where- gpd = preProcessInternalDeps (specVersion (packageDescription gpd0)) gpd0---ppPackageDescription :: CabalSpecVersion -> PackageDescription -> [PrettyField ()]-ppPackageDescription v pd =- prettyFieldGrammar v packageDescriptionFieldGrammar pd- ++ ppSourceRepos v (sourceRepos pd)--ppSourceRepos :: CabalSpecVersion -> [SourceRepo] -> [PrettyField ()]-ppSourceRepos = map . ppSourceRepo--ppSourceRepo :: CabalSpecVersion -> SourceRepo -> PrettyField ()-ppSourceRepo v repo = PrettySection () "source-repository" [pretty kind] $- prettyFieldGrammar v (sourceRepoFieldGrammar kind) repo- where- kind = repoKind repo--ppSetupBInfo :: CabalSpecVersion -> Maybe SetupBuildInfo -> [PrettyField ()]-ppSetupBInfo _ Nothing = mempty-ppSetupBInfo v (Just sbi)- | defaultSetupDepends sbi = mempty- | otherwise = pure $ PrettySection () "custom-setup" [] $- prettyFieldGrammar v (setupBInfoFieldGrammar False) sbi--ppGenPackageFlags :: CabalSpecVersion -> [PackageFlag] -> [PrettyField ()]-ppGenPackageFlags = map . ppFlag--ppFlag :: CabalSpecVersion -> PackageFlag -> PrettyField ()-ppFlag v flag@(MkPackageFlag name _ _ _) = PrettySection () "flag" [ppFlagName name] $- prettyFieldGrammar v (flagFieldGrammar name) flag--ppCondTree2 :: CabalSpecVersion -> PrettyFieldGrammar' s -> CondTree ConfVar [Dependency] s -> [PrettyField ()]-ppCondTree2 v grammar = go- where- -- TODO: recognise elif opportunities- go (CondNode it _ ifs) =- prettyFieldGrammar v grammar it ++- concatMap ppIf ifs-- ppIf (CondBranch c thenTree Nothing)--- | isEmpty thenDoc = mempty- | otherwise = [ppIfCondition c thenDoc]- where- thenDoc = go thenTree-- ppIf (CondBranch c thenTree (Just elseTree)) =- -- See #6193- [ ppIfCondition c (go thenTree)- , PrettySection () "else" [] (go elseTree)- ]--ppCondLibrary :: CabalSpecVersion -> Maybe (CondTree ConfVar [Dependency] Library) -> [PrettyField ()]-ppCondLibrary _ Nothing = mempty-ppCondLibrary v (Just condTree) = pure $ PrettySection () "library" [] $- ppCondTree2 v (libraryFieldGrammar LMainLibName) condTree--ppCondSubLibraries :: CabalSpecVersion -> [(UnqualComponentName, CondTree ConfVar [Dependency] Library)] -> [PrettyField ()]-ppCondSubLibraries v libs =- [ PrettySection () "library" [pretty n]- $ ppCondTree2 v (libraryFieldGrammar $ LSubLibName n) condTree- | (n, condTree) <- libs- ]--ppCondForeignLibs :: CabalSpecVersion -> [(UnqualComponentName, CondTree ConfVar [Dependency] ForeignLib)] -> [PrettyField ()]-ppCondForeignLibs v flibs =- [ PrettySection () "foreign-library" [pretty n]- $ ppCondTree2 v (foreignLibFieldGrammar n) condTree- | (n, condTree) <- flibs- ]--ppCondExecutables :: CabalSpecVersion -> [(UnqualComponentName, CondTree ConfVar [Dependency] Executable)] -> [PrettyField ()]-ppCondExecutables v exes =- [ PrettySection () "executable" [pretty n]- $ ppCondTree2 v (executableFieldGrammar n) condTree- | (n, condTree) <- exes- ]--ppCondTestSuites :: CabalSpecVersion -> [(UnqualComponentName, CondTree ConfVar [Dependency] TestSuite)] -> [PrettyField ()]-ppCondTestSuites v suites =- [ PrettySection () "test-suite" [pretty n]- $ ppCondTree2 v testSuiteFieldGrammar (fmap FG.unvalidateTestSuite condTree)- | (n, condTree) <- suites- ]--ppCondBenchmarks :: CabalSpecVersion -> [(UnqualComponentName, CondTree ConfVar [Dependency] Benchmark)] -> [PrettyField ()]-ppCondBenchmarks v suites =- [ PrettySection () "benchmark" [pretty n]- $ ppCondTree2 v benchmarkFieldGrammar (fmap FG.unvalidateBenchmark condTree)- | (n, condTree) <- suites- ]--ppCondition :: Condition ConfVar -> Doc-ppCondition (Var x) = ppConfVar x-ppCondition (Lit b) = text (show b)-ppCondition (CNot c) = char '!' <<>> (ppCondition c)-ppCondition (COr c1 c2) = parens (hsep [ppCondition c1, text "||"- <+> ppCondition c2])-ppCondition (CAnd c1 c2) = parens (hsep [ppCondition c1, text "&&"- <+> ppCondition c2])-ppConfVar :: ConfVar -> Doc-ppConfVar (OS os) = text "os" <<>> parens (pretty os)-ppConfVar (Arch arch) = text "arch" <<>> parens (pretty arch)-ppConfVar (PackageFlag name) = text "flag" <<>> parens (ppFlagName name)-ppConfVar (Impl c v) = text "impl" <<>> parens (pretty c <+> pretty v)--ppFlagName :: FlagName -> Doc-ppFlagName = text . unFlagName--ppIfCondition :: Condition ConfVar -> [PrettyField ()] -> PrettyField ()-ppIfCondition c = PrettySection () "if" [ppCondition c]---- | @since 2.0.0.2-writePackageDescription :: FilePath -> PackageDescription -> IO ()-writePackageDescription fpath pkg = writeUTF8File fpath (showPackageDescription pkg)----TODO: make this use section syntax--- add equivalent for GenericPackageDescription---- | @since 2.0.0.2-showPackageDescription :: PackageDescription -> String-showPackageDescription = showGenericPackageDescription . pdToGpd--pdToGpd :: PackageDescription -> GenericPackageDescription-pdToGpd pd = GenericPackageDescription- { packageDescription = pd- , gpdScannedVersion = Nothing- , genPackageFlags = []- , condLibrary = mkCondTree <$> library pd- , condSubLibraries = mkCondTreeL <$> subLibraries pd- , condForeignLibs = mkCondTree' foreignLibName <$> foreignLibs pd- , condExecutables = mkCondTree' exeName <$> executables pd- , condTestSuites = mkCondTree' testName <$> testSuites pd- , condBenchmarks = mkCondTree' benchmarkName <$> benchmarks pd- }- where- -- We set CondTree's [Dependency] to an empty list, as it- -- is not pretty printed anyway.- mkCondTree x = CondNode x [] []- mkCondTreeL l = (fromMaybe (mkUnqualComponentName "") (libraryNameString (libName l)), CondNode l [] [])-- mkCondTree'- :: (a -> UnqualComponentName)- -> a -> (UnqualComponentName, CondTree ConfVar [Dependency] a)- mkCondTree' f x = (f x, CondNode x [] [])------------------------------------------------------------------------------------ Internal libs------------------------------------------------------------------------------------ See Note [Dependencies on sublibraries] in Distribution.PackageDescription.Parsec----preProcessInternalDeps :: CabalSpecVersion -> GenericPackageDescription -> GenericPackageDescription-preProcessInternalDeps specVer gpd- | specVer >= CabalSpecV3_4 = gpd- | otherwise = transformAllBuildInfos transformBI transformSBI gpd- where- transformBI :: BuildInfo -> BuildInfo- transformBI- = over L.targetBuildDepends (concatMap transformD)- . over L.mixins (map transformM)-- transformSBI :: SetupBuildInfo -> SetupBuildInfo- transformSBI = over L.setupDepends (concatMap transformD)-- transformD :: Dependency -> [Dependency]- transformD (Dependency pn vr ln)- | pn == thisPn- = if LMainLibName `NES.member` ln- then Dependency thisPn vr mainLibSet : sublibs- else sublibs- where- sublibs =- [ Dependency (unqualComponentNameToPackageName uqn) vr mainLibSet- | LSubLibName uqn <- NES.toList ln- ]-- transformD d = [d]-- transformM :: Mixin -> Mixin- transformM (Mixin pn (LSubLibName uqn) inc)- | pn == thisPn- = mkMixin (unqualComponentNameToPackageName uqn) LMainLibName inc- transformM m = m-- thisPn :: PackageName- thisPn = pkgName (package (packageDescription gpd))------------------------------------------------------------------------------------ HookedBuildInfo------------------------------------------------------------------------------------ | @since 2.0.0.2-writeHookedBuildInfo :: FilePath -> HookedBuildInfo -> IO ()-writeHookedBuildInfo fpath = writeFileAtomic fpath . BS.Char8.pack- . showHookedBuildInfo---- | @since 2.0.0.2-showHookedBuildInfo :: HookedBuildInfo -> String-showHookedBuildInfo (mb_lib_bi, ex_bis) = showFields (const []) $- maybe mempty (prettyFieldGrammar cabalSpecLatest buildInfoFieldGrammar) mb_lib_bi ++- [ PrettySection () "executable:" [pretty name]- $ prettyFieldGrammar cabalSpecLatest buildInfoFieldGrammar bi- | (name, bi) <- ex_bis- ]
− src/Distribution/PackageDescription/Quirks.hs
@@ -1,340 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RankNTypes #-}--- |------ @since 2.2.0.0-module Distribution.PackageDescription.Quirks (patchQuirks) where--import Distribution.Compat.Prelude-import Distribution.Utils.MD5-import GHC.Fingerprint (Fingerprint (..))-import Prelude ()--import qualified Data.ByteString as BS-import qualified Data.Map as Map---- | Patch legacy @.cabal@ file contents to allow parsec parser to accept--- all of Hackage.------ Bool part of the result tells whether the output is modified.------ @since 2.2.0.0-patchQuirks :: BS.ByteString -> (Bool, BS.ByteString)-patchQuirks bs = case Map.lookup (BS.take 256 bs, md5 bs) patches of- Nothing -> (False, bs)- Just (post, f)- | post /= md5 output -> (False, bs)- | otherwise -> (True, output)- where- output = f bs---- | 'patches' contains first 256 bytes, pre- and post-fingerprints and a patch function.-patches :: Map.Map (BS.ByteString, Fingerprint) (Fingerprint, BS.ByteString -> BS.ByteString)-patches = Map.fromList- -- http://hackage.haskell.org/package/unicode-transforms-0.3.3- -- other-modules: .- -- ReadP assumed dot is empty line- [ mk "-- This file has been generated from package.yaml by hpack version 0.17.0.\n--\n-- see: https://github.com/sol/hpack\n\nname: unicode-transforms\nversion: 0.3.3\nsynopsis: Unicode normalization\ndescription: Fast Unic"- (Fingerprint 15958160436627155571 10318709190730872881)- (Fingerprint 11008465475756725834 13815629925116264363)- (bsRemove " other-modules:\n .\n") -- TODO: remove traling \n to test structural-diff- -- http://hackage.haskell.org/package/DSTM-0.1.2- -- http://hackage.haskell.org/package/DSTM-0.1.1- -- http://hackage.haskell.org/package/DSTM-0.1- -- Other Modules: no dash- -- ReadP parsed as section- , mk "Name: DSTM\nVersion: 0.1.2\nCopyright: (c) 2010, Frank Kupke\nLicense: LGPL\nLicense-File: LICENSE\nAuthor: Frank Kupke\nMaintainer: frk@informatik.uni-kiel.de\nCabal-Version: >= 1.2.3\nStability: provisional\nSynopsis: A framework for using STM within distributed "- (Fingerprint 6919263071548559054 9050746360708965827)- (Fingerprint 17015177514298962556 11943164891661867280)- (bsReplace "Other modules:" "-- ")- , mk "Name: DSTM\nVersion: 0.1.1\nCopyright: (c) 2010, Frank Kupke\nLicense: LGPL\nLicense-File: LICENSE\nAuthor: Frank Kupke\nMaintainer: frk@informatik.uni-kiel.de\nCabal-Version: >= 1.2.3\nStability: provisional\nSynopsis: A framework for using STM within distributed "- (Fingerprint 17313105789069667153 9610429408495338584)- (Fingerprint 17250946493484671738 17629939328766863497)- (bsReplace "Other modules:" "-- ")- , mk "Name: DSTM\nVersion: 0.1\nCopyright: (c) 2010, Frank Kupke\nLicense: LGPL\nLicense-File: LICENSE\nAuthor: Frank Kupke\nMaintainer: frk@informatik.uni-kiel.de\nCabal-Version: >= 1.2.3\nStability: provisional\nSynopsis: A framework for using STM within distributed sy"- (Fingerprint 10502599650530614586 16424112934471063115)- (Fingerprint 13562014713536696107 17899511905611879358)- (bsReplace "Other modules:" "-- ")- -- http://hackage.haskell.org/package/control-monad-exception-mtl-0.10.3- , mk "name: control-monad-exception-mtl\nversion: 0.10.3\nCabal-Version: >= 1.10\nbuild-type: Simple\nlicense: PublicDomain\nauthor: Pepe Iborra\nmaintainer: pepeiborra@gmail.com\nhomepage: http://pepeiborra.github.com/control-monad-exception\nsynopsis: MTL instances f"- (Fingerprint 18274748422558568404 4043538769550834851)- (Fingerprint 11395257416101232635 4303318131190196308)- (bsReplace " default- extensions:" "unknown-section")- -- http://hackage.haskell.org/package/vacuum-opengl-0.0- -- \DEL character- , mk "Name: vacuum-opengl\nVersion: 0.0\nSynopsis: Visualize live Haskell data structures using vacuum, graphviz and OpenGL.\nDescription: \DELVisualize live Haskell data structures using vacuum, graphviz and OpenGL.\n "- (Fingerprint 5946760521961682577 16933361639326309422)- (Fingerprint 14034745101467101555 14024175957788447824)- (bsRemove "\DEL")- , mk "Name: vacuum-opengl\nVersion: 0.0.1\nSynopsis: Visualize live Haskell data structures using vacuum, graphviz and OpenGL.\nDescription: \DELVisualize live Haskell data structures using vacuum, graphviz and OpenGL.\n "- (Fingerprint 10790950110330119503 1309560249972452700)- (Fingerprint 1565743557025952928 13645502325715033593)- (bsRemove "\DEL")- -- http://hackage.haskell.org/package/ixset-1.0.4- -- {- comments -}- , mk "Name: ixset\nVersion: 1.0.4\nSynopsis: Efficient relational queries on Haskell sets.\nDescription:\n Create and query sets that are indexed by multiple indices.\nLicense: BSD3\nLicense-file: COPYING\nAut"- (Fingerprint 11886092342440414185 4150518943472101551)- (Fingerprint 5731367240051983879 17473925006273577821)- (bsRemoveStarting "{-")- -- : after section- -- http://hackage.haskell.org/package/ds-kanren- , mk "name: ds-kanren\nversion: 0.2.0.0\nsynopsis: A subset of the miniKanren language\ndescription:\n ds-kanren is an implementation of the <http://minikanren.org miniKanren> language.\n .\n == What's in ds-kanren?\n .\n ['dis"- (Fingerprint 2804006762382336875 9677726932108735838)- (Fingerprint 9830506174094917897 12812107316777006473)- (bsReplace "Test-Suite test-unify:" "Test-Suite \"test-unify:\"" . bsReplace "Test-Suite test-list-ops:" "Test-Suite \"test-list-ops:\"")- , mk "name: ds-kanren\nversion: 0.2.0.1\nsynopsis: A subset of the miniKanren language\ndescription:\n ds-kanren is an implementation of the <http://minikanren.org miniKanren> language.\n\nlicense: MIT\nlicense-file: "- (Fingerprint 9130259649220396193 2155671144384738932)- (Fingerprint 1847988234352024240 4597789823227580457)- (bsReplace "Test-Suite test-unify:" "Test-Suite \"test-unify:\"" . bsReplace "Test-Suite test-list-ops:" "Test-Suite \"test-list-ops:\"")- , mk "name: metric\nversion: 0.1.4\nsynopsis: Metric spaces.\nlicense: MIT\nlicense-file: LICENSE\nauthor: Vikram Verma\nmaintainer: me@vikramverma.com\ncategory: Data\nbuild-type:"- (Fingerprint 6150019278861565482 3066802658031228162)- (Fingerprint 9124826020564520548 15629704249829132420)- (bsReplace "test-suite metric-tests:" "test-suite \"metric-tests:\"")- , mk "name: metric\nversion: 0.2.0\nsynopsis: Metric spaces.\nlicense: MIT\nlicense-file: LICENSE\nauthor: Vikram Verma\nmaintainer: me@vikramverma.com\ncategory: Data\nbuild-type:"- (Fingerprint 4639805967994715694 7859317050376284551)- (Fingerprint 5566222290622325231 873197212916959151)- (bsReplace "test-suite metric-tests:" "test-suite \"metric-tests:\"")- , mk "name: phasechange\ncategory: Data\nversion: 0.1\nauthor: G\195\161bor Lehel\nmaintainer: G\195\161bor Lehel <illissius@gmail.com>\nhomepage: http://github.com/glehel/phasechange\ncopyright: Copyright (C) 2012 G\195\161bor Lehel\nlicense: "- (Fingerprint 10546509771395401582 245508422312751943)- (Fingerprint 5169853482576003304 7247091607933993833)- (bsReplace "impl(ghc >= 7.4):" "erroneous-section" . bsReplace "impl(ghc >= 7.6):" "erroneous-section")- , mk "Name: smartword\nSynopsis: Web based flash card for Word Smart I and II vocabularies\nVersion: 0.0.0.5\nHomepage: http://kyagrd.dyndns.org/~kyagrd/project/smartword/\nCategory: Web,Education\nLicense: "- (Fingerprint 7803544783533485151 10807347873998191750)- (Fingerprint 1665635316718752601 16212378357991151549)- (bsReplace "build depends:" "--")- , mk "name: shelltestrunner\n-- sync with README.md, ANNOUNCE:\nversion: 1.3\ncategory: Testing\nsynopsis: A tool for testing command-line programs.\ndescription:\n shelltestrunner is a cross-platform tool for testing command-line\n program"- (Fingerprint 4403237110790078829 15392625961066653722)- (Fingerprint 10218887328390239431 4644205837817510221)- (bsReplace "other modules:" "--")- -- &&!- -- http://hackage.haskell.org/package/hblas-0.3.0.0- , mk "-- Initial hblas.cabal generated by cabal init. For further \n-- documentation, see http://haskell.org/cabal/users-guide/\n\n-- The name of the package.\nname: hblas\n\n-- The package version. See the Haskell package versioning policy (PVP) \n-- "- (Fingerprint 8570120150072467041 18315524331351505945)- (Fingerprint 10838007242302656005 16026440017674974175)- (bsReplace "&&!" "&& !")- , mk "-- Initial hblas.cabal generated by cabal init. For further \n-- documentation, see http://haskell.org/cabal/users-guide/\n\n-- The name of the package.\nname: hblas\n\n-- The package version. See the Haskell package versioning policy (PVP) \n-- "- (Fingerprint 5262875856214215155 10846626274067555320)- (Fingerprint 3022954285783401045 13395975869915955260)- (bsReplace "&&!" "&& !")- , mk "-- Initial hblas.cabal generated by cabal init. For further \n-- documentation, see http://haskell.org/cabal/users-guide/\n\n-- The name of the package.\nname: hblas\n\n-- The package version. See the Haskell package versioning policy (PVP) \n-- "- (Fingerprint 54222628930951453 5526514916844166577)- (Fingerprint 1749630806887010665 8607076506606977549)- (bsReplace "&&!" "&& !")- , mk "-- Initial hblas.cabal generated by cabal init. For further\n-- documentation, see http://haskell.org/cabal/users-guide/\n\n-- The name of the package.\nname: hblas\n\n-- The package version. See the Haskell package versioning policy (PVP)\n-- fo"- (Fingerprint 6817250511240350300 15278852712000783849)- (Fingerprint 15757717081429529536 15542551865099640223)- (bsReplace "&&!" "&& !")- , mk "-- Initial hblas.cabal generated by cabal init. For further\n-- documentation, see http://haskell.org/cabal/users-guide/\n\n-- The name of the package.\nname: hblas\n\n-- The package version. See the Haskell package versioning policy (PVP)\n-- fo"- (Fingerprint 8310050400349211976 201317952074418615)- (Fingerprint 10283381191257209624 4231947623042413334)- (bsReplace "&&!" "&& !")- , mk "-- Initial hblas.cabal generated by cabal init. For further\n-- documentation, see http://haskell.org/cabal/users-guide/\n\n-- The name of the package.\nname: hblas\n\n-- The package version. See the Haskell package versioning policy (PVP)\n-- fo"- (Fingerprint 7010988292906098371 11591884496857936132)- (Fingerprint 6158672440010710301 6419743768695725095)- (bsReplace "&&!" "&& !")- , mk "-- Initial hblas.cabal generated by cabal init. For further\r\n-- documentation, see http://haskell.org/cabal/users-guide/\r\n\r\n-- The name of the package.\r\nname: hblas\r\n\r\n-- The package version. See the Haskell package versioning policy (PVP)"- (Fingerprint 2076850805659055833 16615160726215879467)- (Fingerprint 10634706281258477722 5285812379517916984)- (bsReplace "&&!" "&& !")- , mk "-- Initial hblas.cabal generated by cabal init. For further\r\n-- documentation, see http://haskell.org/cabal/users-guide/\r\n\r\n-- The name of the package.\r\nname: hblas\r\n\r\n-- The package version. See the Haskell package versioning policy (PVP)"- (Fingerprint 11850020631622781099 11956481969231030830)- (Fingerprint 13702868780337762025 13383526367149067158)- (bsReplace "&&!" "&& !")- , mk "-- Initial hblas.cabal generated by cabal init. For further\n-- documentation, see http://haskell.org/cabal/users-guide/\n\n-- The name of the package.\nname: hblas\n\n-- The package version. See the Haskell package versioning policy (PVP)\n-- fo"- (Fingerprint 13690322768477779172 19704059263540994)- (Fingerprint 11189374824645442376 8363528115442591078)- (bsReplace "&&!" "&& !")- -- flag used, but not defined- , mk "name: brainheck\nversion: 0.1.0.2\nsynopsis: Brainh*ck interpreter in haskell\ndescription: Brainh*ck interpreter written in haskell and taking advantage of many prominent libraries\nhomepage: https://gi"- (Fingerprint 6910727116443152200 15401634478524888973)- (Fingerprint 16551412117098094368 16260377389127603629)- (bsReplace "flag(llvm-fast)" "False")- , mk "name: brainheck\r\nversion: 0.1.0.2\r\nx-revision: 1\r\nsynopsis: Brainh*ck interpreter in haskell\r\ndescription: Brainh*ck interpreter written in haskell and taking advantage of many prominent libraries\r\nhomepage: "- (Fingerprint 14320987921316832277 10031098243571536929)- (Fingerprint 7959395602414037224 13279941216182213050)- (bsReplace "flag(llvm-fast)" "False")- , mk "name: brainheck\r\nversion: 0.1.0.2\r\nx-revision: 2\r\nsynopsis: Brainh*ck interpreter in haskell\r\ndescription: Brainh*ck interpreter written in haskell and taking advantage of many prominent libraries\r\nhomepage: "- (Fingerprint 3809078390223299128 10796026010775813741)- (Fingerprint 1127231189459220796 12088367524333209349)- (bsReplace "flag(llvm-fast)" "False")- , mk "name: brainheck\r\nversion: 0.1.0.2\r\nx-revision: 3\r\nsynopsis: Brainh*ck interpreter in haskell\r\ndescription: Brainh*ck interpreter written in haskell and taking advantage of many prominent libraries\r\nhomepage: "- (Fingerprint 13860013038089410950 12479824176801390651)- (Fingerprint 4687484721703340391 8013395164515771785)- (bsReplace "flag(llvm-fast)" "False")- , mk "name: wordchoice\nversion: 0.1.0.1\nsynopsis: Get word counts and distributions\ndescription: A command line tool to compute the word distribution from various types of document, converting to text with pandoc.\nho"- (Fingerprint 16215911397419608203 15594928482155652475)- (Fingerprint 15120681510314491047 2666192399775157359)- (bsReplace "flag(llvm-fast)" "False")- , mk "name: wordchoice\r\nversion: 0.1.0.1\r\nx-revision: 1\r\nsynopsis: Get word counts and distributions\r\ndescription: A command line tool to compute the word distribution from various types of document, converting to te"- (Fingerprint 16593139224723441188 4052919014346212001)- (Fingerprint 3577381082410411593 11481899387780544641)- (bsReplace "flag(llvm-fast)" "False")- , mk "name: wordchoice\nversion: 0.1.0.2\nsynopsis: Get word counts and distributions\ndescription: A command line tool to compute the word distribution from various types of document, converting to text with pandoc.\nho"- (Fingerprint 9321301260802539374 1316392715016096607)- (Fingerprint 3784628652257760949 12662640594755291035)- (bsReplace "flag(llvm-fast)" "False")- , mk "name: wordchoice\r\nversion: 0.1.0.2\r\nx-revision: 1\r\nsynopsis: Get word counts and distributions\r\ndescription: A command line tool to compute the word distribution from various types of document, converting to te"- (Fingerprint 2546901804824433337 2059732715322561176)- (Fingerprint 8082068680348326500 615008613291421947)- (bsReplace "flag(llvm-fast)" "False")- , mk "name: wordchoice\nversion: 0.1.0.3\nsynopsis: Get word counts and distributions\ndescription: A command line tool to compute the word distribution from various types of document, converting to text with pandoc.\nho"- (Fingerprint 2282380737467965407 12457554753171662424)- (Fingerprint 17324757216926991616 17172911843227482125)- (bsReplace "flag(llvm-fast)" "False")- , mk "name: wordchoice\r\nversion: 0.1.0.3\r\nx-revision: 1\r\nsynopsis: Get word counts and distributions\r\ndescription: A command line tool to compute the word distribution from various types of document, converting to te"- (Fingerprint 12907988890480595481 11078473638628359710)- (Fingerprint 13246185333368731848 4663060731847518614)- (bsReplace "flag(llvm-fast)" "False")- , mk "name: hw-prim-bits\nversion: 0.1.0.0\nsynopsis: Primitive support for bit manipulation\ndescription: Please see README.md\nhomepage: https://github.com/githubuser/hw-prim-bits#readme\nlicense: "- (Fingerprint 12386777729082870356 17414156731912743711)- (Fingerprint 3452290353395041602 14102887112483033720)- (bsReplace "flag(sse42)" "False")- , mk "name: hw-prim-bits\nversion: 0.1.0.1\nsynopsis: Primitive support for bit manipulation\ndescription: Please see README.md\nhomepage: https://github.com/githubuser/hw-prim-bits#readme\nlicen"- (Fingerprint 6870520675313101180 14553457351296240636)- (Fingerprint 12481021059537696455 14711088786769892762)- (bsReplace "flag(sse42)" "False")- -- leading zeros in version digits- -- https://github.com/haskell-infra/hackage-trustees/issues/128- -- https://github.com/haskell/cabal/issues/5092- -- https://github.com/haskell/cabal/issues/5138- , mk "name: Sit\nversion: 0.2017.02.26\nbuild-type: Simple\ncabal-version: >= 1.8\nlicense: OtherLicense\nlicense-file: LICENSE\nauthor: Anonymous\nmaintainer: Anonymous\nhomepage: NONE\ncategory: Dependent"- (Fingerprint 8458530898096910998 3228538743646501413)- (Fingerprint 14470502514907936793 17514354054641875371)- (bsReplace "0.2017.02.26" "0.2017.2.26")- , mk "name: Sit\nversion: 0.2017.05.01\nbuild-type: Simple\ncabal-version: >= 1.8\nlicense: OtherLicense\nlicense-file: LICENSE\nauthor: Andreas Abel <andreas.abel@gu.se>\nmaintainer: Andreas Abel <andreas.abel@gu.se>\n"- (Fingerprint 1450130849535097473 11742099607098860444)- (Fingerprint 16679762943850814021 4253724355613883542)- (bsReplace "0.2017.05.01" "0.2017.5.1")- , mk "name: Sit\nversion: 0.2017.05.02\nbuild-type: Simple\ncabal-version: >= 1.8\nlicense: OtherLicense\nlicense-file: LICENSE\nauthor: Andreas Abel <andreas.abel@gu.se>\nmaintainer: Andreas Abel <andreas.abel@gu.se>\n"- (Fingerprint 297248532398492441 17322625167861324800)- (Fingerprint 634812045126693280 1755581866539318862)- (bsReplace "0.2017.05.02" "0.2017.5.2")- , mk "name: Sit\nversion: 0.2017.5.02\nx-revision: 1\n-- x-revision:1 see https://github.com/haskell-infra/hackage-trustees/issues/128\nbuild-type: Simple\ncabal-version: >= 1.8\nlicense: OtherLicense\nlicense-file: LICENSE\nauthor: "- (Fingerprint 3697869560530373941 3942982281026987312)- (Fingerprint 14344526114710295386 16386400353475114712)- (bsReplace "0.2017.5.02" "0.2017.5.2")- , mk "name: MiniAgda\nversion: 0.2017.02.18\nbuild-type: Simple\ncabal-version: >= 1.22\nlicense: OtherLicense\nlicense-file: LICENSE\nauthor: Andreas Abel and Karl Mehltretter\nmaintainer: Andreas Abel <andreas.abel@i"- (Fingerprint 17167128953451088679 4300350537748753465)- (Fingerprint 12402236925293025673 7715084875284020606)- (bsReplace "0.2017.02.18" "0.2017.2.18")- , mk "cabal-version:\n 2.0\nname:\n fast-downward\nversion:\n 0.1.0.0\nbuild-type:\n Simple\nsynopsis:\n Solve classical planning problems (STRIPS/SAS+) using Haskell & Fast Downward.\ndescription:\n @fast-downward@ is a library for modelling classical planning probl"- (Fingerprint 11256076039027887363 6867903407496243216)- (Fingerprint 12159816716813155434 5278015399212299853)- (bsReplace "1.2.03.0" "1.2.3.0")- , mk "cabal-version:\r\n 2.0\r\nname:\r\n fast-downward\r\nversion:\r\n 0.1.0.0\r\nx-revision: \r\n 1\r\nbuild-type:\r\n Simple\r\nsynopsis:\r\n Solve classical planning problems (STRIPS/SAS+) using Haskell & Fast Downward.\r\ndescription:\r\n @fast-downward@ is a library for mode"- (Fingerprint 9216193973149680231 893446343655828508)- (Fingerprint 10020169545407746427 1828336750379510675)- (bsReplace "1.2.03.0" "1.2.3.0")- , mk "cabal-version:\n 2.0\nname:\n fast-downward\nversion:\n 0.1.0.1\nbuild-type:\n Simple\nsynopsis:\n Solve classical planning problems (STRIPS/SAS+) using Haskell & Fast Downward.\ndescription:\n @fast-downward@ is a library for modelling classical planning probl"- (Fingerprint 9899886602574848632 5980433644983783334)- (Fingerprint 12007469255857289958 8321466548645225439)- (bsReplace "1.2.03.0" "1.2.3.0")- , mk "cabal-version:\n 2.0\nname:\n fast-downward\nversion:\n 0.1.1.0\nbuild-type:\n Simple\nsynopsis:\n Solve classical planning problems (STRIPS/SAS+) using Haskell & Fast Downward.\ndescription:\n @fast-downward@ is a library for modelling classical planning probl"- (Fingerprint 12694656661460787751 1902242956706735615)- (Fingerprint 15433152131513403849 2284712791516353264)- (bsReplace "1.2.03.0" "1.2.3.0")- -- 9 digits limit- , mk "Name: SGplus\nVersion: 1.1\nSynopsis: (updated) Small geometry library for dealing with vectors and collision detection\nLicense: BSD3\nLicense-file: LICENSE\nAuthor: Neil Brown\nMaintainer: "- (Fingerprint 17735649550442248029 11493772714725351354)- (Fingerprint 9565458801063261772 15955773698774721052)- (bsReplace "1000000000" "100000000")- , mk "-- Initial control-dotdotdot.cabal generated by cabal init. For further \n-- documentation, see http://haskell.org/cabal/users-guide/\n\nname: control-dotdotdot\nversion: 0.1.0.1\nsynopsis: Haskell operator\n "- (Fingerprint 1514257173776509942 7756050823377346485)- (Fingerprint 14082092642045505999 18415918653404121035)- (bsReplace "9223372036854775807" "5")- , mk "name: data-foldapp\r\nversion: 0.1.1.0\r\nsynopsis: Fold function applications. Framework for variadic functions.\r\ndescription: Fold function applications. Framework for variadic functions.\r\nhomepage: ht"- (Fingerprint 4511234156311243251 11701153011544112556)- (Fingerprint 11820542702491924189 4902231447612406724)- (bsReplace "9223372036854775807" "999" . bsReplace "9223372036854775807" "999")- , mk "-- Initial data-list-zigzag.cabal generated by cabal init. For further \r\n-- documentation, see http://haskell.org/cabal/users-guide/\r\n\r\nname: data-list-zigzag\r\nversion: 0.1.1.1\r\nsynopsis: A list but with a balanced en"- (Fingerprint 12475837388692175691 18053834261188158945)- (Fingerprint 16279938253437334942 15753349540193002309)- (bsReplace "9223372036854775807" "999")- -- Not UTF8- , mk "Name: nat\nVersion: 0.1\nDescription: Implementation of natural numbers and integers by a binary\n representation. The implementation is supposed to be lazy and\n "- (Fingerprint 9222512268705577108 13085311382746579495)- (Fingerprint 17468921266614378430 13221316288008291892)- (bsReplace "\xf6" "\xc3\xb6")- -- cabal-version: 2- , mk "name: streaming-bracketed\nversion: 0.1.0.0\nsynopsis: A resource management decorator for \"streaming\". \ndescription: This package provides a decorator for the Stream type from\n the \"st"- (Fingerprint 14670044663153191927 1427497586294143829)- (Fingerprint 9233007756654759985 6571998449003682006)- (bsReplace "cabal-version: 2" "cabal-version: 2.0")- , mk "name: streaming-bracketed\nversion: 0.1.0.1\nsynopsis: A resource management decorator for \"streaming\". \ndescription: This package provides a decorator for the Stream type from\n \"stream"- (Fingerprint 7298738862909203815 10141693276062967842)- (Fingerprint 1349949738792220441 3593683359695349293)- (bsReplace "cabal-version: 2" "cabal-version: 2.0")- , mk "name: zsyntax\nversion: 0.2.0.0\ndescription: An automated theorem prover for Zsyntax, a\n logical calculus for molecular biology inspired by linear logic,\n that can be used to automatically verify biological\n"- (Fingerprint 17812331267506881875 3005293725141563863)- (Fingerprint 3445957263137759540 12472369104312474458)- (bsReplace "cabal-version: 2" "cabal-version: 2.0")-- -- Empty filepath in not license-file or data-dir- -- These have hs-source-dirs: ""- , mk "\nname: wai-middleware-hmac-client\nversion: 0.1.0.1\nlicense: BSD3\nlicense-file: LICENSE\nauthor: Christopher Reichert\nmaintainer: creichert07@gmail.com\ncopyright: (c) 2015, Christo"- (Fingerprint 3112606538775065787 11984607507024462091)- (Fingerprint 6916432989977230500 6621389616675138128)- (bsReplace "\"\"" ".")- , mk "\nname: wai-middleware-hmac-client\nversion: 0.1.0.2\nlicense: BSD3\nlicense-file: LICENSE\nauthor: Christopher Reichert\nmaintainer: creichert07@gmail.com\ncopyright: (c) 2015, Christo"- (Fingerprint 12566783342663020458 17562089389615949789)- (Fingerprint 15745683452603944938 10556498036622072844)- (bsReplace "\"\"" ".")-- -- absolute license-file- , mk "name: reheat\nversion: 0.1.4\ncabal-version: >=1.8\nbuild-type: Simple\nlicense: GPL\nlicense-file: /home/palo/dev/haskell-workspace/playground/reheat/gpl-3.0.txt\ncopyright: GPL\nmaintainer: Ingolf Wagner <palipalo9@gmail.com>\nstability: experimental\nhomepage: h"- (Fingerprint 9155400339287317061 14812953666990892802)- (Fingerprint 7687053346032173923 15384472501136606592)- (bsReplace "/home/palo/dev/haskell-workspace/playground/reheat/gpl-3.0.txt" "")- , mk "name: reheat\nversion: 0.1.5\ncabal-version: >=1.8\nbuild-type: Simple\nlicense: GPL\nlicense-file: /home/palo/dev/haskell-workspace/playground/reheat/gpl-3.0.txt\ncopyright: GPL\nmaintainer: Ingolf Wagner <palipalo9@gmail.com>\nstability: experimental\nhomepage: h"- (Fingerprint 2984391146441073709 11728234882049907993)- (Fingerprint 12058479081855347701 14017937756688869826)- (bsReplace "/home/palo/dev/haskell-workspace/playground/reheat/gpl-3.0.txt" "")- ]- where- mk a b c d = ((a, b), (c, d))---- | Helper to create entries in patches-_makePatchKey :: FilePath -> (BS.ByteString -> BS.ByteString) -> IO ()-_makePatchKey fp transform = do- contents <- BS.readFile fp- let output = transform contents- let Fingerprint hi lo = md5 contents- let Fingerprint hi' lo' = md5 output- putStrLn- $ showString " , mk "- . shows (BS.take 256 contents)- . showString "\n (Fingerprint "- . shows hi- . showString " "- . shows lo- . showString ")\n (Fingerprint "- . shows hi'- . showString " "- . shows lo'- . showString ")"- $ ""------------------------------------------------------------------------------------ Patch helpers----------------------------------------------------------------------------------bsRemove- :: BS.ByteString -- ^ needle- -> BS.ByteString -> BS.ByteString-bsRemove needle haystack = case BS.breakSubstring needle haystack of- (h, t) -> BS.append h (BS.drop (BS.length needle) t)--bsReplace- :: BS.ByteString -- ^ needle- -> BS.ByteString -- ^ replacement- -> BS.ByteString -> BS.ByteString-bsReplace needle repl haystack = case BS.breakSubstring needle haystack of- (h, t)- | not (BS.null t) -> BS.append h (BS.append repl (BS.drop (BS.length needle) t))- | otherwise -> haystack--bsRemoveStarting- :: BS.ByteString -- ^ needle- -> BS.ByteString -> BS.ByteString-bsRemoveStarting needle haystack = case BS.breakSubstring needle haystack of- (h, _) -> h
− src/Distribution/PackageDescription/Utils.hs
@@ -1,23 +0,0 @@--------------------------------------------------------------------------------- |--- Module : Distribution.PackageDescription.Utils------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ Common utils used by modules under Distribution.PackageDescription.*.--module Distribution.PackageDescription.Utils (- cabalBug, userBug- ) where---- ------------------------------------------------------------------------------- Exception and logging utils--userBug :: String -> a-userBug msg = error $ msg ++ ". This is a bug in your .cabal file."--cabalBug :: String -> a-cabalBug msg = error $ msg ++ ". This is possibly a bug in Cabal.\n"- ++ "Please report it to the developers: "- ++ "https://github.com/haskell/cabal/issues/new"
− src/Distribution/Parsec.hs
@@ -1,524 +0,0 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE CPP #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE ScopedTypeVariables #-}-module Distribution.Parsec (- Parsec(..),- ParsecParser (..),- runParsecParser,- runParsecParser',- simpleParsec,- simpleParsecBS,- simpleParsec',- simpleParsecW',- lexemeParsec,- eitherParsec,- explicitEitherParsec,- explicitEitherParsec',- -- * CabalParsing and diagnostics- CabalParsing (..),- -- ** Warnings- PWarnType (..),- PWarning (..),- showPWarning,- -- ** Errors- PError (..),- showPError,- -- * Position- Position (..),- incPos,- retPos,- showPos,- zeroPos,- -- * Utilities- parsecToken,- parsecToken',- parsecFilePath,- parsecQuoted,- parsecMaybeQuoted,- parsecCommaList,- parsecCommaNonEmpty,- parsecLeadingCommaList,- parsecLeadingCommaNonEmpty,- parsecOptCommaList,- parsecLeadingOptCommaList,- parsecStandard,- parsecUnqualComponentName,- ) where--import Data.ByteString (ByteString)-import Data.Char (digitToInt, intToDigit)-import Data.List (transpose)-import Distribution.CabalSpecVersion-import Distribution.Compat.Prelude-import Distribution.Parsec.Error (PError (..), showPError)-import Distribution.Parsec.FieldLineStream (FieldLineStream, fieldLineStreamFromBS, fieldLineStreamFromString)-import Distribution.Parsec.Position (Position (..), incPos, retPos, showPos, zeroPos)-import Distribution.Parsec.Warning (PWarnType (..), PWarning (..), showPWarning)-import Numeric (showIntAtBase)-import Prelude ()--import qualified Distribution.Compat.CharParsing as P-import qualified Distribution.Compat.DList as DList-import qualified Distribution.Compat.MonadFail as Fail-import qualified Text.Parsec as Parsec------------------------------------------------------------------------------------ Class------------------------------------------------------------------------------------ | Class for parsing with @parsec@. Mainly used for @.cabal@ file fields.------ For parsing @.cabal@ like file structure, see "Distribution.Fields".----class Parsec a where- parsec :: CabalParsing m => m a---- | Parsing class which------ * can report Cabal parser warnings.------ * knows @cabal-version@ we work with----class (P.CharParsing m, MonadPlus m, Fail.MonadFail m) => CabalParsing m where- parsecWarning :: PWarnType -> String -> m ()-- parsecHaskellString :: m String- parsecHaskellString = stringLiteral-- askCabalSpecVersion :: m CabalSpecVersion---- | 'parsec' /could/ consume trailing spaces, this function /will/ consume.-lexemeParsec :: (CabalParsing m, Parsec a) => m a-lexemeParsec = parsec <* P.spaces--newtype ParsecParser a = PP { unPP- :: CabalSpecVersion -> Parsec.Parsec FieldLineStream [PWarning] a- }--liftParsec :: Parsec.Parsec FieldLineStream [PWarning] a -> ParsecParser a-liftParsec p = PP $ \_ -> p--instance Functor ParsecParser where- fmap f p = PP $ \v -> fmap f (unPP p v)- {-# INLINE fmap #-}-- x <$ p = PP $ \v -> x <$ unPP p v- {-# INLINE (<$) #-}--instance Applicative ParsecParser where- pure = liftParsec . pure- {-# INLINE pure #-}-- f <*> x = PP $ \v -> unPP f v <*> unPP x v- {-# INLINE (<*>) #-}- f *> x = PP $ \v -> unPP f v *> unPP x v- {-# INLINE (*>) #-}- f <* x = PP $ \v -> unPP f v <* unPP x v- {-# INLINE (<*) #-}--instance Alternative ParsecParser where- empty = liftParsec empty-- a <|> b = PP $ \v -> unPP a v <|> unPP b v- {-# INLINE (<|>) #-}-- many p = PP $ \v -> many (unPP p v)- {-# INLINE many #-}-- some p = PP $ \v -> some (unPP p v)- {-# INLINE some #-}--instance Monad ParsecParser where- return = pure-- m >>= k = PP $ \v -> unPP m v >>= \x -> unPP (k x) v- {-# INLINE (>>=) #-}- (>>) = (*>)- {-# INLINE (>>) #-}--#if !(MIN_VERSION_base(4,13,0))- fail = Fail.fail-#endif--instance MonadPlus ParsecParser where- mzero = empty- mplus = (<|>)--instance Fail.MonadFail ParsecParser where- fail = P.unexpected--instance P.Parsing ParsecParser where- try p = PP $ \v -> P.try (unPP p v)- p <?> d = PP $ \v -> unPP p v P.<?> d- skipMany p = PP $ \v -> P.skipMany (unPP p v)- skipSome p = PP $ \v -> P.skipSome (unPP p v)- unexpected = liftParsec . P.unexpected- eof = liftParsec P.eof- notFollowedBy p = PP $ \v -> P.notFollowedBy (unPP p v)--instance P.CharParsing ParsecParser where- satisfy = liftParsec . P.satisfy- char = liftParsec . P.char- notChar = liftParsec . P.notChar- anyChar = liftParsec P.anyChar- string = liftParsec . P.string--instance CabalParsing ParsecParser where- parsecWarning t w = liftParsec $ do- spos <- Parsec.getPosition- Parsec.modifyState- (PWarning t (Position (Parsec.sourceLine spos) (Parsec.sourceColumn spos)) w :)- askCabalSpecVersion = PP pure---- | Parse a 'String' with 'lexemeParsec'.-simpleParsec :: Parsec a => String -> Maybe a-simpleParsec- = either (const Nothing) Just- . runParsecParser lexemeParsec "<simpleParsec>"- . fieldLineStreamFromString---- | Like 'simpleParsec' but for 'ByteString'-simpleParsecBS :: Parsec a => ByteString -> Maybe a-simpleParsecBS- = either (const Nothing) Just- . runParsecParser lexemeParsec "<simpleParsec>"- . fieldLineStreamFromBS---- | Parse a 'String' with 'lexemeParsec' using specific 'CabalSpecVersion'.------ @since 3.4.0.0-simpleParsec' :: Parsec a => CabalSpecVersion -> String -> Maybe a-simpleParsec' spec- = either (const Nothing) Just- . runParsecParser' spec lexemeParsec "<simpleParsec>"- . fieldLineStreamFromString---- | Parse a 'String' with 'lexemeParsec' using specific 'CabalSpecVersion'.--- Fail if there are any warnings.------ @since 3.4.0.0-simpleParsecW' :: Parsec a => CabalSpecVersion -> String -> Maybe a-simpleParsecW' spec- = either (const Nothing) (\(x, ws) -> if null ws then Just x else Nothing)- . runParsecParser' spec ((,) <$> lexemeParsec <*> liftParsec Parsec.getState) "<simpleParsec>"- . fieldLineStreamFromString---- | Parse a 'String' with 'lexemeParsec'.-eitherParsec :: Parsec a => String -> Either String a-eitherParsec = explicitEitherParsec parsec---- | Parse a 'String' with given 'ParsecParser'. Trailing whitespace is accepted.-explicitEitherParsec :: ParsecParser a -> String -> Either String a-explicitEitherParsec parser- = either (Left . show) Right- . runParsecParser (parser <* P.spaces) "<eitherParsec>"- . fieldLineStreamFromString---- | Parse a 'String' with given 'ParsecParser' and 'CabalSpecVersion'. Trailing whitespace is accepted.--- See 'explicitEitherParsec'.------ @since 3.4.0.0----explicitEitherParsec' :: CabalSpecVersion -> ParsecParser a -> String -> Either String a-explicitEitherParsec' spec parser- = either (Left . show) Right- . runParsecParser' spec (parser <* P.spaces) "<eitherParsec>"- . fieldLineStreamFromString---- | Run 'ParsecParser' with 'cabalSpecLatest'.-runParsecParser :: ParsecParser a -> FilePath -> FieldLineStream -> Either Parsec.ParseError a-runParsecParser = runParsecParser' cabalSpecLatest---- | Like 'runParsecParser' but lets specify 'CabalSpecVersion' used.------ @since 3.0.0.0----runParsecParser' :: CabalSpecVersion -> ParsecParser a -> FilePath -> FieldLineStream -> Either Parsec.ParseError a-runParsecParser' v p n = Parsec.runParser (unPP p v <* P.eof) [] n--instance Parsec a => Parsec (Identity a) where- parsec = Identity <$> parsec--instance Parsec Bool where- parsec = P.munch1 isAlpha >>= postprocess- where- postprocess str- | str == "True" = pure True- | str == "False" = pure False- | lstr == "true" = parsecWarning PWTBoolCase caseWarning *> pure True- | lstr == "false" = parsecWarning PWTBoolCase caseWarning *> pure False- | otherwise = fail $ "Not a boolean: " ++ str- where- lstr = map toLower str- caseWarning =- "Boolean values are case sensitive, use 'True' or 'False'."---- | @[^ ,]@-parsecToken :: CabalParsing m => m String-parsecToken = parsecHaskellString <|> ((P.munch1 (\x -> not (isSpace x) && x /= ',') P.<?> "identifier" ) >>= checkNotDoubleDash)---- | @[^ ]@-parsecToken' :: CabalParsing m => m String-parsecToken' = parsecHaskellString <|> ((P.munch1 (not . isSpace) P.<?> "token") >>= checkNotDoubleDash)--checkNotDoubleDash :: CabalParsing m => String -> m String-checkNotDoubleDash s = do- when (s == "--") $ parsecWarning PWTDoubleDash $ unwords- [ "Double-dash token found."- , "Note: there are no end-of-line comments in .cabal files, only whole line comments."- , "Use \"--\" (quoted double dash) to silence this warning, if you actually want -- token"- ]-- return s--parsecFilePath :: CabalParsing m => m FilePath-parsecFilePath = parsecToken---- | Parse a benchmark/test-suite types.-parsecStandard :: (CabalParsing m, Parsec ver) => (ver -> String -> a) -> m a-parsecStandard f = do- cs <- some $ P.try (component <* P.char '-')- ver <- parsec- let name = map toLower (intercalate "-" cs)- return $! f ver name- where- component = do- cs <- P.munch1 isAlphaNum- if all isDigit cs then fail "all digit component" else return cs- -- each component must contain an alphabetic character, to avoid- -- ambiguity in identifiers like foo-1 (the 1 is the version number).--parsecCommaList :: CabalParsing m => m a -> m [a]-parsecCommaList p = P.sepBy (p <* P.spaces) (P.char ',' *> P.spaces P.<?> "comma")--parsecCommaNonEmpty :: CabalParsing m => m a -> m (NonEmpty a)-parsecCommaNonEmpty p = P.sepByNonEmpty (p <* P.spaces) (P.char ',' *> P.spaces P.<?> "comma")---- | Like 'parsecCommaList' but accept leading or trailing comma.------ @--- p (comma p)* -- p `sepBy` comma--- (comma p)* -- leading comma--- (p comma)* -- trailing comma--- @-parsecLeadingCommaList :: CabalParsing m => m a -> m [a]-parsecLeadingCommaList p = do- c <- P.optional comma- case c of- Nothing -> toList <$> P.sepEndByNonEmpty lp comma <|> pure []- Just _ -> toList <$> P.sepByNonEmpty lp comma- where- lp = p <* P.spaces- comma = P.char ',' *> P.spaces P.<?> "comma"---- |------ @since 3.4.0.0-parsecLeadingCommaNonEmpty :: CabalParsing m => m a -> m (NonEmpty a)-parsecLeadingCommaNonEmpty p = do- c <- P.optional comma- case c of- Nothing -> P.sepEndByNonEmpty lp comma- Just _ -> P.sepByNonEmpty lp comma- where- lp = p <* P.spaces- comma = P.char ',' *> P.spaces P.<?> "comma"--parsecOptCommaList :: CabalParsing m => m a -> m [a]-parsecOptCommaList p = P.sepBy (p <* P.spaces) (P.optional comma)- where- comma = P.char ',' *> P.spaces---- | Like 'parsecOptCommaList' but------ * require all or none commas--- * accept leading or trailing comma.------ @--- p (comma p)* -- p `sepBy` comma--- (comma p)* -- leading comma--- (p comma)* -- trailing comma--- p* -- no commas: many p--- @------ @since 3.0.0.0----parsecLeadingOptCommaList :: CabalParsing m => m a -> m [a]-parsecLeadingOptCommaList p = do- c <- P.optional comma- case c of- Nothing -> sepEndBy1Start <|> pure []- Just _ -> toList <$> P.sepByNonEmpty lp comma- where- lp = p <* P.spaces- comma = P.char ',' *> P.spaces P.<?> "comma"-- sepEndBy1Start = do- x <- lp- c <- P.optional comma- case c of- Nothing -> (x :) <$> many lp- Just _ -> (x :) <$> P.sepEndBy lp comma---- | Content isn't unquoted-parsecQuoted :: CabalParsing m => m a -> m a-parsecQuoted = P.between (P.char '"') (P.char '"')---- | @parsecMaybeQuoted p = 'parsecQuoted' p <|> p@.-parsecMaybeQuoted :: CabalParsing m => m a -> m a-parsecMaybeQuoted p = parsecQuoted p <|> p--parsecUnqualComponentName :: forall m. CabalParsing m => m String-parsecUnqualComponentName = state0 DList.empty where- --- -- using @kleene@ package we can easily see that- -- we need only two states to recognize- -- unqual-component-name- --- -- Compare with declarative- -- 'Distribution.FieldGrammar.Described.reUnqualComponent'.- --- -- @- -- import Kleene- -- import Kleene.Internal.Pretty- -- import Algebra.Lattice- -- import Data.Char- --- -- import qualified Data.RangeSet.Map as RSet- --- -- main = do- -- -- this is an approximation, to get an idea.- -- let component :: RE Char- -- component = star alphaNum <> alpha <> star alphaNum- --- -- alphaNum = alpha \/ num- -- alpha = unions $ map char ['a'..'z']- -- num = unions $ map char ['0'..'9']- --- -- re :: RE Char- -- re = component <> star (char '-' <> component)- --- -- putPretty re- -- putPretty $ fromTM re- -- @-- state0 :: DList.DList Char -> m String- state0 acc = do- c <- ch -- <|> fail ("Invalid component, after " ++ DList.toList acc)- case () of- _ | isDigit c -> state0 (DList.snoc acc c)- | isAlphaNum c -> state1 (DList.snoc acc c)- | c == '-' -> fail ("Empty component, after " ++ DList.toList acc)- | otherwise -> fail ("Internal error, after " ++ DList.toList acc)-- state1 :: DList.DList Char -> m String- state1 acc = state1' acc `alt` return (DList.toList acc)-- state1' :: DList.DList Char -> m String- state1' acc = do- c <- ch- case () of- _ | isAlphaNum c -> state1 (DList.snoc acc c)- | c == '-' -> state0 (DList.snoc acc c)- | otherwise -> fail ("Internal error, after " ++ DList.toList acc)-- ch :: m Char- !ch = P.satisfy (\c -> isAlphaNum c || c == '-')-- alt :: m String -> m String -> m String- !alt = (<|>)--stringLiteral :: forall m. P.CharParsing m => m String-stringLiteral = lit where- lit :: m String- lit = foldr (maybe id (:)) ""- <$> P.between (P.char '"') (P.char '"' P.<?> "end of string") (many stringChar)- P.<?> "string"-- stringChar :: m (Maybe Char)- stringChar = Just <$> stringLetter- <|> stringEscape- P.<?> "string character"-- stringLetter :: m Char- stringLetter = P.satisfy (\c -> (c /= '"') && (c /= '\\') && (c > '\026'))-- stringEscape :: m (Maybe Char)- stringEscape = P.char '\\' *> esc where- esc :: m (Maybe Char)- esc = Nothing <$ escapeGap- <|> Nothing <$ escapeEmpty- <|> Just <$> escapeCode-- escapeEmpty, escapeGap :: m Char- escapeEmpty = P.char '&'- escapeGap = P.skipSpaces1 *> (P.char '\\' P.<?> "end of string gap")--escapeCode :: forall m. P.CharParsing m => m Char-escapeCode = (charEsc <|> charNum <|> charAscii <|> charControl) P.<?> "escape code"- where- charControl, charNum :: m Char- charControl = (\c -> toEnum (fromEnum c - fromEnum '@')) <$> (P.char '^' *> (P.upper <|> P.char '@'))- charNum = toEnum <$> num- where- num :: m Int- num = bounded 10 maxchar- <|> (P.char 'o' *> bounded 8 maxchar)- <|> (P.char 'x' *> bounded 16 maxchar)- maxchar = fromEnum (maxBound :: Char)-- bounded :: Int -> Int -> m Int- bounded base bnd = foldl' (\x d -> base * x + digitToInt d) 0- <$> bounded' (take base thedigits) (map digitToInt $ showIntAtBase base intToDigit bnd "")- where- thedigits :: [m Char]- thedigits = map P.char ['0'..'9'] ++ map P.oneOf (transpose [['A'..'F'],['a'..'f']])-- toomuch :: m a- toomuch = P.unexpected "out-of-range numeric escape sequence"-- bounded', bounded'' :: [m Char] -> [Int] -> m [Char]- bounded' dps@(zero:_) bds = P.skipSome zero *> ([] <$ P.notFollowedBy (P.choice dps) <|> bounded'' dps bds)- <|> bounded'' dps bds- bounded' [] _ = error "bounded called with base 0"- bounded'' dps [] = [] <$ P.notFollowedBy (P.choice dps) <|> toomuch- bounded'' dps (bd : bds) = let anyd :: m Char- anyd = P.choice dps-- nomore :: m ()- nomore = P.notFollowedBy anyd <|> toomuch-- (low, ex, high) = case splitAt bd dps of- (low', ex' : high') -> (low', ex', high')- (_, _) -> error "escapeCode: Logic error"- in ((:) <$> P.choice low <*> atMost (length bds) anyd) <* nomore- <|> ((:) <$> ex <*> ([] <$ nomore <|> bounded'' dps bds))- <|> if not (null bds)- then (:) <$> P.choice high <*> atMost (length bds - 1) anyd <* nomore- else empty- atMost n p | n <= 0 = pure []- | otherwise = ((:) <$> p <*> atMost (n - 1) p) <|> pure []-- charEsc :: m Char- charEsc = P.choice $ parseEsc <$> escMap-- parseEsc (c,code) = code <$ P.char c- escMap = zip "abfnrtv\\\"\'" "\a\b\f\n\r\t\v\\\"\'"-- charAscii :: m Char- charAscii = P.choice $ parseAscii <$> asciiMap-- parseAscii (asc,code) = P.try $ code <$ P.string asc- asciiMap = zip (ascii3codes ++ ascii2codes) (ascii3 ++ ascii2)- ascii2codes, ascii3codes :: [String]- ascii2codes = [ "BS","HT","LF","VT","FF","CR","SO"- , "SI","EM","FS","GS","RS","US","SP"]- ascii3codes = ["NUL","SOH","STX","ETX","EOT","ENQ","ACK"- ,"BEL","DLE","DC1","DC2","DC3","DC4","NAK"- ,"SYN","ETB","CAN","SUB","ESC","DEL"]- ascii2, ascii3 :: String- ascii2 = "\BS\HT\LF\VT\FF\CR\SO\SI\EM\FS\GS\RS\US\SP"- ascii3 = "\NUL\SOH\STX\ETX\EOT\ENQ\ACK\BEL\DLE\DC1\DC2\DC3\DC4\NAK\SYN\ETB\CAN\SUB\ESC\DEL"
− src/Distribution/Parsec/Error.hs
@@ -1,21 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Parsec.Error (- PError (..),- showPError,- ) where--import Distribution.Compat.Prelude-import Distribution.Parsec.Position-import Prelude ()-import System.FilePath (normalise)---- | Parser error.-data PError = PError Position String- deriving (Show, Generic)--instance Binary PError-instance NFData PError where rnf = genericRnf--showPError :: FilePath -> PError -> String-showPError fpath (PError pos msg) =- normalise fpath ++ ":" ++ showPos pos ++ ": " ++ msg
− src/Distribution/Parsec/FieldLineStream.hs
@@ -1,91 +0,0 @@-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# OPTIONS_GHC -Wall -Werror #-}-module Distribution.Parsec.FieldLineStream (- FieldLineStream (..),- fieldLineStreamFromString,- fieldLineStreamFromBS,- fieldLineStreamEnd,- ) where--import Data.Bits-import Data.ByteString (ByteString)-import Distribution.Compat.Prelude-import Distribution.Utils.Generic (toUTF8BS)-import Prelude ()--import qualified Data.ByteString as BS-import qualified Text.Parsec as Parsec---- | This is essentially a lazy bytestring, but chunks are glued with newline @\'\\n\'@.-data FieldLineStream- = FLSLast !ByteString- | FLSCons {-# UNPACK #-} !ByteString FieldLineStream- deriving Show--fieldLineStreamEnd :: FieldLineStream-fieldLineStreamEnd = FLSLast mempty---- | Convert 'String' to 'FieldLineStream'.------ /Note:/ inefficient!-fieldLineStreamFromString :: String -> FieldLineStream-fieldLineStreamFromString = FLSLast . toUTF8BS--fieldLineStreamFromBS :: ByteString -> FieldLineStream-fieldLineStreamFromBS = FLSLast--instance Monad m => Parsec.Stream FieldLineStream m Char where- uncons (FLSLast bs) = return $ case BS.uncons bs of- Nothing -> Nothing- Just (c, bs') -> Just (unconsChar c bs' (\bs'' -> FLSLast bs'') fieldLineStreamEnd)-- uncons (FLSCons bs s) = return $ case BS.uncons bs of- -- as lines are glued with '\n', we return '\n' here!- Nothing -> Just ('\n', s)- Just (c, bs') -> Just (unconsChar c bs' (\bs'' -> FLSCons bs'' s) s)---- Based on implementation 'decodeStringUtf8'-unconsChar :: forall a. Word8 -> ByteString -> (ByteString -> a) -> a -> (Char, a)-unconsChar c0 bs0 f next- | c0 <= 0x7F = (chr (fromIntegral c0), f bs0)- | c0 <= 0xBF = (replacementChar, f bs0)- | c0 <= 0xDF = twoBytes- | c0 <= 0xEF = moreBytes 3 0x800 bs0 (fromIntegral $ c0 .&. 0xF)- | c0 <= 0xF7 = moreBytes 4 0x10000 bs0 (fromIntegral $ c0 .&. 0x7)- | c0 <= 0xFB = moreBytes 5 0x200000 bs0 (fromIntegral $ c0 .&. 0x3)- | c0 <= 0xFD = moreBytes 6 0x4000000 bs0 (fromIntegral $ c0 .&. 0x1)- | otherwise = error $ "not implemented " ++ show c0- where- twoBytes = case BS.uncons bs0 of- Nothing -> (replacementChar, next)- Just (c1, bs1)- | c1 .&. 0xC0 == 0x80 ->- if d >= 0x80- then (chr d, f bs1)- else (replacementChar, f bs1)- | otherwise -> (replacementChar, f bs1)- where- d = (fromIntegral (c0 .&. 0x1F) `shiftL` 6) .|. fromIntegral (c1 .&. 0x3F)-- moreBytes :: Int -> Int -> ByteString -> Int -> (Char, a)- moreBytes 1 overlong bs' acc- | overlong <= acc, acc <= 0x10FFFF, acc < 0xD800 || 0xDFFF < acc- = (chr acc, f bs')- | otherwise- = (replacementChar, f bs')-- moreBytes byteCount overlong bs' acc = case BS.uncons bs' of- Nothing -> (replacementChar, f bs')- Just (cn, bs1)- | cn .&. 0xC0 == 0x80 -> moreBytes- (byteCount-1)- overlong- bs1- ((acc `shiftL` 6) .|. fromIntegral cn .&. 0x3F)- | otherwise -> (replacementChar, f bs1)--replacementChar :: Char-replacementChar = '\xfffd'
− src/Distribution/Parsec/Position.hs
@@ -1,44 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Parsec.Position (- Position (..),- incPos,- retPos,- showPos,- zeroPos,- positionCol,- positionRow,- ) where--import Distribution.Compat.Prelude-import Prelude ()---- | 1-indexed row and column positions in a file.-data Position = Position- {-# UNPACK #-} !Int -- row- {-# UNPACK #-} !Int -- column- deriving (Eq, Ord, Show, Generic)--instance Binary Position-instance NFData Position where rnf = genericRnf---- | Shift position by n columns to the right.-incPos :: Int -> Position -> Position-incPos n (Position row col) = Position row (col + n)---- | Shift position to beginning of next row.-retPos :: Position -> Position-retPos (Position row _col) = Position (row + 1) 1--showPos :: Position -> String-showPos (Position row col) = show row ++ ":" ++ show col--zeroPos :: Position-zeroPos = Position 0 0---- | @since 3.0.0.0-positionCol :: Position -> Int-positionCol (Position _ c) = c---- | @since 3.0.0.0-positionRow :: Position -> Int-positionRow (Position r _) = r
− src/Distribution/Parsec/Warning.hs
@@ -1,61 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Parsec.Warning (- PWarning (..),- PWarnType (..),- showPWarning,- ) where--import Distribution.Compat.Prelude-import Distribution.Parsec.Position-import Prelude ()-import System.FilePath (normalise)---- | Type of parser warning. We do classify warnings.------ Different application may decide not to show some, or have fatal behaviour on others-data PWarnType- = PWTOther -- ^ Unclassified warning- | PWTUTF -- ^ Invalid UTF encoding- | PWTBoolCase -- ^ @true@ or @false@, not @True@ or @False@- | PWTVersionTag -- ^ there are version with tags- | PWTNewSyntax -- ^ New syntax used, but no @cabal-version: >= 1.2@ specified- | PWTOldSyntax -- ^ Old syntax used, and @cabal-version >= 1.2@ specified- | PWTDeprecatedField- | PWTInvalidSubsection- | PWTUnknownField- | PWTUnknownSection- | PWTTrailingFields- | PWTExtraMainIs -- ^ extra main-is field- | PWTExtraTestModule -- ^ extra test-module field- | PWTExtraBenchmarkModule -- ^ extra benchmark-module field- | PWTLexNBSP- | PWTLexBOM- | PWTLexTab- | PWTQuirkyCabalFile -- ^ legacy cabal file that we know how to patch- | PWTDoubleDash -- ^ Double dash token, most likely it's a mistake - it's not a comment- | PWTMultipleSingularField -- ^ e.g. name or version should be specified only once.- | PWTBuildTypeDefault -- ^ Workaround for derive-package having build-type: Default. See <https://github.com/haskell/cabal/issues/5020>.-- | PWTVersionOperator -- ^ Version operators used (without cabal-version: 1.8)- | PWTVersionWildcard -- ^ Version wildcard used (without cabal-version: 1.6)-- | PWTSpecVersion -- ^ Warnings about cabal-version format.-- | PWTEmptyFilePath -- ^ Empty filepath, i.e. literally ""-- | PWTExperimental -- ^ Experimental feature- deriving (Eq, Ord, Show, Enum, Bounded, Generic)--instance Binary PWarnType-instance NFData PWarnType where rnf = genericRnf---- | Parser warning.-data PWarning = PWarning !PWarnType !Position String- deriving (Show, Generic)--instance Binary PWarning-instance NFData PWarning where rnf = genericRnf--showPWarning :: FilePath -> PWarning -> String-showPWarning fpath (PWarning _ pos msg) =- normalise fpath ++ ":" ++ showPos pos ++ ": " ++ msg
− src/Distribution/Pretty.hs
@@ -1,110 +0,0 @@-module Distribution.Pretty (- Pretty (..),- prettyShow,- defaultStyle,- flatStyle,- -- * Utilities- showFilePath,- showToken,- showTokenStr,- showFreeText,- showFreeTextV3,- -- * Deprecated- Separator,- ) where--import Distribution.CabalSpecVersion-import Distribution.Compat.Prelude-import Prelude ()--import qualified Text.PrettyPrint as PP--class Pretty a where- pretty :: a -> PP.Doc-- prettyVersioned :: CabalSpecVersion -> a -> PP.Doc- prettyVersioned _ = pretty---- | @since 3.4.0.0-instance Pretty PP.Doc where- pretty = id--instance Pretty Bool where- pretty = PP.text . show--instance Pretty Int where- pretty = PP.text . show--instance Pretty a => Pretty (Identity a) where- pretty = pretty . runIdentity--prettyShow :: Pretty a => a -> String-prettyShow = PP.renderStyle defaultStyle . pretty---- | The default rendering style used in Cabal for console--- output. It has a fixed page width and adds line breaks--- automatically.-defaultStyle :: PP.Style-defaultStyle = PP.Style { PP.mode = PP.PageMode- , PP.lineLength = 79- , PP.ribbonsPerLine = 1.0- }---- | A style for rendering all on one line.-flatStyle :: PP.Style-flatStyle = PP.Style { PP.mode = PP.LeftMode- , PP.lineLength = err "lineLength"- , PP.ribbonsPerLine = err "ribbonsPerLine"- }- where- err x = error ("flatStyle: tried to access " ++ x ++ " in LeftMode. " ++- "This should never happen and indicates a bug in Cabal.")------------------------------------------------------------------------------------ Utilities------------------------------------------------------------------------------------ TODO: remove when ReadP parser is gone.-type Separator = [PP.Doc] -> PP.Doc--showFilePath :: FilePath -> PP.Doc-showFilePath = showToken--showToken :: String -> PP.Doc-showToken = PP.text . showTokenStr--showTokenStr :: String -> String-showTokenStr str- -- if token looks like a comment (starts with --), print it in quotes- | "--" `isPrefixOf` str = show str- -- also if token ends with a colon (e.g. executable name), print it in quotes- | ":" `isSuffixOf` str = show str- | not (any dodgy str) && not (null str) = str- | otherwise = show str- where- dodgy c = isSpace c || c == ','----- | Pretty-print free-format text, ensuring that it is vertically aligned,--- and with blank lines replaced by dots for correct re-parsing.-showFreeText :: String -> PP.Doc-showFreeText "" = mempty-showFreeText s = PP.vcat [ PP.text (if null l then "." else l) | l <- lines_ s ]---- | Pretty-print free-format text.--- Since @cabal-version: 3.0@ we don't replace blank lines with dots.------ @since 3.0.0.0-showFreeTextV3 :: String -> PP.Doc-showFreeTextV3 "" = mempty-showFreeTextV3 s = PP.vcat [ PP.text l | l <- lines_ s ]---- | 'lines_' breaks a string up into a list of strings at newline--- characters. The resulting strings do not contain newlines.-lines_ :: String -> [String]-lines_ [] = [""]-lines_ s =- let (l, s') = break (== '\n') s- in l : case s' of- [] -> []- (_:s'') -> lines_ s''
src/Distribution/ReadE.hs view
@@ -1,4 +1,7 @@+{-# LANGUAGE LambdaCase #-}+ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.ReadE -- Copyright : Jose Iborra 2008@@ -8,29 +11,37 @@ -- Portability : portable -- -- Simple parsing with failure+module Distribution.ReadE+ ( -- * ReadE+ ReadE (..)+ , succeedReadE+ , failReadE -module Distribution.ReadE (- -- * ReadE- ReadE(..), succeedReadE, failReadE,- -- * Projections- readEOrFail,- parsecToReadE,+ -- * Projections+ , parsecToReadE+ , parsecToReadEErr++ -- * Parse Errors+ , unexpectMsgString ) where +import qualified Data.Bifunctor as Bi (first) import Distribution.Compat.Prelude import Prelude () import Distribution.Parsec import Distribution.Parsec.FieldLineStream+import qualified Text.Parsec.Error as Parsec -- | Parser with simple error reporting newtype ReadE a = ReadE {runReadE :: String -> Either ErrorMsg a}-type ErrorMsg = String +type ErrorMsg = String+ instance Functor ReadE where fmap f (ReadE p) = ReadE $ \txt -> case p txt of- Right a -> Right (f a)- Left err -> Left err+ Right a -> Right (f a)+ Left err -> Left err succeedReadE :: (String -> a) -> ReadE a succeedReadE f = ReadE (Right . f)@@ -38,12 +49,23 @@ failReadE :: ErrorMsg -> ReadE a failReadE = ReadE . const . Left -readEOrFail :: ReadE a -> String -> a-readEOrFail r = either error id . runReadE r+runParsecFromString :: ParsecParser a -> String -> Either Parsec.ParseError a+runParsecFromString p txt =+ runParsecParser p "<parsecToReadE>" (fieldLineStreamFromString txt) parsecToReadE :: (String -> ErrorMsg) -> ParsecParser a -> ReadE a parsecToReadE err p = ReadE $ \txt ->- case runParsecParser p "<parsecToReadE>" (fieldLineStreamFromString txt) of- Right x -> Right x- Left _e -> Left (err txt)--- TODO: use parsec error to make 'ErrorMsg'.+ const (err txt) `Bi.first` runParsecFromString p txt++parsecToReadEErr :: (Parsec.ParseError -> ErrorMsg) -> ParsecParser a -> ReadE a+parsecToReadEErr err p =+ ReadE $+ Bi.first err . runParsecFromString p++-- Show only unexpected error messages+unexpectMsgString :: Parsec.ParseError -> String+unexpectMsgString =+ unlines+ . map Parsec.messageString+ . filter (\case Parsec.UnExpect _ -> True; _ -> False)+ . Parsec.errorMessages
− src/Distribution/SPDX.hs
@@ -1,40 +0,0 @@--- | This module implements SPDX specification version 2.1 with a version 3.0 license list.------ Specification is available on <https://spdx.org/specifications>-module Distribution.SPDX (- -- * License- License (..),- -- * License expression- LicenseExpression (..),- SimpleLicenseExpression (..),- simpleLicenseExpression,- -- * License identifier- LicenseId (..),- licenseId,- licenseName,- licenseIsOsiApproved,- mkLicenseId,- licenseIdList,- -- * License exception- LicenseExceptionId (..),- licenseExceptionId,- licenseExceptionName,- mkLicenseExceptionId,- licenseExceptionIdList,- -- * License reference- LicenseRef,- licenseRef,- licenseDocumentRef,- mkLicenseRef,- mkLicenseRef',- -- * License list version- LicenseListVersion (..),- cabalSpecVersionToSPDXListVersion,- ) where--import Distribution.SPDX.LicenseExceptionId-import Distribution.SPDX.License-import Distribution.SPDX.LicenseId-import Distribution.SPDX.LicenseExpression-import Distribution.SPDX.LicenseReference-import Distribution.SPDX.LicenseListVersion
− src/Distribution/SPDX/License.hs
@@ -1,65 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.SPDX.License (- License (..),- ) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Pretty-import Distribution.Parsec-import Distribution.SPDX.LicenseExpression--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp---- | Declared license.--- See [section 3.15 of SPDX Specification 2.1](https://spdx.org/spdx-specification-21-web-version#h.1hmsyys)------ /Note:/ the NOASSERTION case is omitted.------ Old 'License' can be migrated using following rules:------ * @AllRightsReserved@ and @UnspecifiedLicense@ to 'NONE'.--- No license specified which legally defaults to /All Rights Reserved/.--- The package may not be legally modified or redistributed by anyone but--- the rightsholder.------ * @OtherLicense@ can be converted to 'LicenseRef' pointing to the file--- in the package.------ * @UnknownLicense@ i.e. other licenses of the form @name-x.y@, should be--- covered by SPDX license list, otherwise use 'LicenseRef'.------ * @PublicDomain@ isn't covered. Consider using CC0.--- See <https://wiki.spdx.org/view/Legal_Team/Decisions/Dealing_with_Public_Domain_within_SPDX_Files>--- for more information.----data License- = NONE- -- ^ if the package contains no license information whatsoever; or- | License LicenseExpression- -- ^ A valid SPDX License Expression as defined in Appendix IV.- deriving (Show, Read, Eq, Ord, Typeable, Data, Generic)--instance Binary License-instance Structured License--instance NFData License where- rnf NONE = ()- rnf (License l) = rnf l--instance Pretty License where- pretty NONE = Disp.text "NONE"- pretty (License l) = pretty l---- |--- >>> eitherParsec "BSD-3-Clause AND MIT" :: Either String License--- Right (License (EAnd (ELicense (ELicenseId BSD_3_Clause) Nothing) (ELicense (ELicenseId MIT) Nothing)))------ >>> eitherParsec "NONE" :: Either String License--- Right NONE----instance Parsec License where- parsec = NONE <$ P.try (P.string "NONE") <|> License <$> parsec
− src/Distribution/SPDX/LicenseExceptionId.hs
@@ -1,316 +0,0 @@--- This file is generated. See Makefile's spdx rule-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.SPDX.LicenseExceptionId (- LicenseExceptionId (..),- licenseExceptionId,- licenseExceptionName,- mkLicenseExceptionId,- licenseExceptionIdList,- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Compat.Lens (set)-import Distribution.Pretty-import Distribution.Parsec-import Distribution.Utils.Generic (isAsciiAlphaNum)-import Distribution.Utils.Structured (Structured (..), nominalStructure, typeVersion)-import Distribution.SPDX.LicenseListVersion--import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary-import qualified Data.Map.Strict as Map-import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp------------------------------------------------------------------------------------ LicenseExceptionId------------------------------------------------------------------------------------ | SPDX License identifier-data LicenseExceptionId- = DS389_exception -- ^ @389-exception@, 389 Directory Server Exception- | Autoconf_exception_2_0 -- ^ @Autoconf-exception-2.0@, Autoconf exception 2.0- | Autoconf_exception_3_0 -- ^ @Autoconf-exception-3.0@, Autoconf exception 3.0- | Bison_exception_2_2 -- ^ @Bison-exception-2.2@, Bison exception 2.2- | Bootloader_exception -- ^ @Bootloader-exception@, Bootloader Distribution Exception- | Classpath_exception_2_0 -- ^ @Classpath-exception-2.0@, Classpath exception 2.0- | CLISP_exception_2_0 -- ^ @CLISP-exception-2.0@, CLISP exception 2.0- | DigiRule_FOSS_exception -- ^ @DigiRule-FOSS-exception@, DigiRule FOSS License Exception- | ECos_exception_2_0 -- ^ @eCos-exception-2.0@, eCos exception 2.0- | Fawkes_Runtime_exception -- ^ @Fawkes-Runtime-exception@, Fawkes Runtime Exception- | FLTK_exception -- ^ @FLTK-exception@, FLTK exception- | Font_exception_2_0 -- ^ @Font-exception-2.0@, Font exception 2.0- | Freertos_exception_2_0 -- ^ @freertos-exception-2.0@, FreeRTOS Exception 2.0- | GCC_exception_2_0 -- ^ @GCC-exception-2.0@, GCC Runtime Library exception 2.0- | GCC_exception_3_1 -- ^ @GCC-exception-3.1@, GCC Runtime Library exception 3.1- | Gnu_javamail_exception -- ^ @gnu-javamail-exception@, GNU JavaMail exception- | GPL_3_0_linking_exception -- ^ @GPL-3.0-linking-exception@, GPL-3.0 Linking Exception, SPDX License List 3.9, SPDX License List 3.10- | GPL_3_0_linking_source_exception -- ^ @GPL-3.0-linking-source-exception@, GPL-3.0 Linking Exception (with Corresponding Source), SPDX License List 3.9, SPDX License List 3.10- | GPL_CC_1_0 -- ^ @GPL-CC-1.0@, GPL Cooperation Commitment 1.0, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | I2p_gpl_java_exception -- ^ @i2p-gpl-java-exception@, i2p GPL+Java Exception- | LGPL_3_0_linking_exception -- ^ @LGPL-3.0-linking-exception@, LGPL-3.0 Linking Exception, SPDX License List 3.9, SPDX License List 3.10- | Libtool_exception -- ^ @Libtool-exception@, Libtool Exception- | Linux_syscall_note -- ^ @Linux-syscall-note@, Linux Syscall Note- | LLVM_exception -- ^ @LLVM-exception@, LLVM Exception, SPDX License List 3.2, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | LZMA_exception -- ^ @LZMA-exception@, LZMA exception- | Mif_exception -- ^ @mif-exception@, Macros and Inline Functions Exception- | Nokia_Qt_exception_1_1 -- ^ @Nokia-Qt-exception-1.1@, Nokia Qt LGPL exception 1.1, SPDX License List 3.0, SPDX License List 3.2- | OCaml_LGPL_linking_exception -- ^ @OCaml-LGPL-linking-exception@, OCaml LGPL Linking Exception, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | OCCT_exception_1_0 -- ^ @OCCT-exception-1.0@, Open CASCADE Exception 1.0- | OpenJDK_assembly_exception_1_0 -- ^ @OpenJDK-assembly-exception-1.0@, OpenJDK Assembly exception 1.0, SPDX License List 3.2, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | Openvpn_openssl_exception -- ^ @openvpn-openssl-exception@, OpenVPN OpenSSL Exception- | PS_or_PDF_font_exception_20170817 -- ^ @PS-or-PDF-font-exception-20170817@, PS/PDF font exception (2017-08-17), SPDX License List 3.2, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | Qt_GPL_exception_1_0 -- ^ @Qt-GPL-exception-1.0@, Qt GPL exception 1.0, SPDX License List 3.2, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | Qt_LGPL_exception_1_1 -- ^ @Qt-LGPL-exception-1.1@, Qt LGPL exception 1.1, SPDX License List 3.2, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | Qwt_exception_1_0 -- ^ @Qwt-exception-1.0@, Qwt exception 1.0- | SHL_2_0 -- ^ @SHL-2.0@, Solderpad Hardware License v2.0, SPDX License List 3.9, SPDX License List 3.10- | SHL_2_1 -- ^ @SHL-2.1@, Solderpad Hardware License v2.1, SPDX License List 3.9, SPDX License List 3.10- | Swift_exception -- ^ @Swift-exception@, Swift Exception, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | U_boot_exception_2_0 -- ^ @u-boot-exception-2.0@, U-Boot exception 2.0- | Universal_FOSS_exception_1_0 -- ^ @Universal-FOSS-exception-1.0@, Universal FOSS Exception, Version 1.0, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | WxWindows_exception_3_1 -- ^ @WxWindows-exception-3.1@, WxWindows Library Exception 3.1- deriving (Eq, Ord, Enum, Bounded, Show, Read, Typeable, Data, Generic)--instance Binary LicenseExceptionId where- put = Binary.putWord8 . fromIntegral . fromEnum- get = do- i <- Binary.getWord8- if i > fromIntegral (fromEnum (maxBound :: LicenseExceptionId))- then fail "Too large LicenseExceptionId tag"- else return (toEnum (fromIntegral i))---- note: remember to bump version each time the definition changes-instance Structured LicenseExceptionId where- structure p = set typeVersion 306 $ nominalStructure p--instance Pretty LicenseExceptionId where- pretty = Disp.text . licenseExceptionId--instance Parsec LicenseExceptionId where- parsec = do- n <- some $ P.satisfy $ \c -> isAsciiAlphaNum c || c == '-' || c == '.'- v <- askCabalSpecVersion- maybe (fail $ "Unknown SPDX license exception identifier: " ++ n) return $- mkLicenseExceptionId (cabalSpecVersionToSPDXListVersion v) n--instance NFData LicenseExceptionId where- rnf l = l `seq` ()------------------------------------------------------------------------------------ License Data------------------------------------------------------------------------------------ | License SPDX identifier, e.g. @"BSD-3-Clause"@.-licenseExceptionId :: LicenseExceptionId -> String-licenseExceptionId DS389_exception = "389-exception"-licenseExceptionId Autoconf_exception_2_0 = "Autoconf-exception-2.0"-licenseExceptionId Autoconf_exception_3_0 = "Autoconf-exception-3.0"-licenseExceptionId Bison_exception_2_2 = "Bison-exception-2.2"-licenseExceptionId Bootloader_exception = "Bootloader-exception"-licenseExceptionId Classpath_exception_2_0 = "Classpath-exception-2.0"-licenseExceptionId CLISP_exception_2_0 = "CLISP-exception-2.0"-licenseExceptionId DigiRule_FOSS_exception = "DigiRule-FOSS-exception"-licenseExceptionId ECos_exception_2_0 = "eCos-exception-2.0"-licenseExceptionId Fawkes_Runtime_exception = "Fawkes-Runtime-exception"-licenseExceptionId FLTK_exception = "FLTK-exception"-licenseExceptionId Font_exception_2_0 = "Font-exception-2.0"-licenseExceptionId Freertos_exception_2_0 = "freertos-exception-2.0"-licenseExceptionId GCC_exception_2_0 = "GCC-exception-2.0"-licenseExceptionId GCC_exception_3_1 = "GCC-exception-3.1"-licenseExceptionId Gnu_javamail_exception = "gnu-javamail-exception"-licenseExceptionId GPL_3_0_linking_exception = "GPL-3.0-linking-exception"-licenseExceptionId GPL_3_0_linking_source_exception = "GPL-3.0-linking-source-exception"-licenseExceptionId GPL_CC_1_0 = "GPL-CC-1.0"-licenseExceptionId I2p_gpl_java_exception = "i2p-gpl-java-exception"-licenseExceptionId LGPL_3_0_linking_exception = "LGPL-3.0-linking-exception"-licenseExceptionId Libtool_exception = "Libtool-exception"-licenseExceptionId Linux_syscall_note = "Linux-syscall-note"-licenseExceptionId LLVM_exception = "LLVM-exception"-licenseExceptionId LZMA_exception = "LZMA-exception"-licenseExceptionId Mif_exception = "mif-exception"-licenseExceptionId Nokia_Qt_exception_1_1 = "Nokia-Qt-exception-1.1"-licenseExceptionId OCaml_LGPL_linking_exception = "OCaml-LGPL-linking-exception"-licenseExceptionId OCCT_exception_1_0 = "OCCT-exception-1.0"-licenseExceptionId OpenJDK_assembly_exception_1_0 = "OpenJDK-assembly-exception-1.0"-licenseExceptionId Openvpn_openssl_exception = "openvpn-openssl-exception"-licenseExceptionId PS_or_PDF_font_exception_20170817 = "PS-or-PDF-font-exception-20170817"-licenseExceptionId Qt_GPL_exception_1_0 = "Qt-GPL-exception-1.0"-licenseExceptionId Qt_LGPL_exception_1_1 = "Qt-LGPL-exception-1.1"-licenseExceptionId Qwt_exception_1_0 = "Qwt-exception-1.0"-licenseExceptionId SHL_2_0 = "SHL-2.0"-licenseExceptionId SHL_2_1 = "SHL-2.1"-licenseExceptionId Swift_exception = "Swift-exception"-licenseExceptionId U_boot_exception_2_0 = "u-boot-exception-2.0"-licenseExceptionId Universal_FOSS_exception_1_0 = "Universal-FOSS-exception-1.0"-licenseExceptionId WxWindows_exception_3_1 = "WxWindows-exception-3.1"---- | License name, e.g. @"GNU General Public License v2.0 only"@-licenseExceptionName :: LicenseExceptionId -> String-licenseExceptionName DS389_exception = "389 Directory Server Exception"-licenseExceptionName Autoconf_exception_2_0 = "Autoconf exception 2.0"-licenseExceptionName Autoconf_exception_3_0 = "Autoconf exception 3.0"-licenseExceptionName Bison_exception_2_2 = "Bison exception 2.2"-licenseExceptionName Bootloader_exception = "Bootloader Distribution Exception"-licenseExceptionName Classpath_exception_2_0 = "Classpath exception 2.0"-licenseExceptionName CLISP_exception_2_0 = "CLISP exception 2.0"-licenseExceptionName DigiRule_FOSS_exception = "DigiRule FOSS License Exception"-licenseExceptionName ECos_exception_2_0 = "eCos exception 2.0"-licenseExceptionName Fawkes_Runtime_exception = "Fawkes Runtime Exception"-licenseExceptionName FLTK_exception = "FLTK exception"-licenseExceptionName Font_exception_2_0 = "Font exception 2.0"-licenseExceptionName Freertos_exception_2_0 = "FreeRTOS Exception 2.0"-licenseExceptionName GCC_exception_2_0 = "GCC Runtime Library exception 2.0"-licenseExceptionName GCC_exception_3_1 = "GCC Runtime Library exception 3.1"-licenseExceptionName Gnu_javamail_exception = "GNU JavaMail exception"-licenseExceptionName GPL_3_0_linking_exception = "GPL-3.0 Linking Exception"-licenseExceptionName GPL_3_0_linking_source_exception = "GPL-3.0 Linking Exception (with Corresponding Source)"-licenseExceptionName GPL_CC_1_0 = "GPL Cooperation Commitment 1.0"-licenseExceptionName I2p_gpl_java_exception = "i2p GPL+Java Exception"-licenseExceptionName LGPL_3_0_linking_exception = "LGPL-3.0 Linking Exception"-licenseExceptionName Libtool_exception = "Libtool Exception"-licenseExceptionName Linux_syscall_note = "Linux Syscall Note"-licenseExceptionName LLVM_exception = "LLVM Exception"-licenseExceptionName LZMA_exception = "LZMA exception"-licenseExceptionName Mif_exception = "Macros and Inline Functions Exception"-licenseExceptionName Nokia_Qt_exception_1_1 = "Nokia Qt LGPL exception 1.1"-licenseExceptionName OCaml_LGPL_linking_exception = "OCaml LGPL Linking Exception"-licenseExceptionName OCCT_exception_1_0 = "Open CASCADE Exception 1.0"-licenseExceptionName OpenJDK_assembly_exception_1_0 = "OpenJDK Assembly exception 1.0"-licenseExceptionName Openvpn_openssl_exception = "OpenVPN OpenSSL Exception"-licenseExceptionName PS_or_PDF_font_exception_20170817 = "PS/PDF font exception (2017-08-17)"-licenseExceptionName Qt_GPL_exception_1_0 = "Qt GPL exception 1.0"-licenseExceptionName Qt_LGPL_exception_1_1 = "Qt LGPL exception 1.1"-licenseExceptionName Qwt_exception_1_0 = "Qwt exception 1.0"-licenseExceptionName SHL_2_0 = "Solderpad Hardware License v2.0"-licenseExceptionName SHL_2_1 = "Solderpad Hardware License v2.1"-licenseExceptionName Swift_exception = "Swift Exception"-licenseExceptionName U_boot_exception_2_0 = "U-Boot exception 2.0"-licenseExceptionName Universal_FOSS_exception_1_0 = "Universal FOSS Exception, Version 1.0"-licenseExceptionName WxWindows_exception_3_1 = "WxWindows Library Exception 3.1"------------------------------------------------------------------------------------ Creation----------------------------------------------------------------------------------licenseExceptionIdList :: LicenseListVersion -> [LicenseExceptionId]-licenseExceptionIdList LicenseListVersion_3_0 =- [ Nokia_Qt_exception_1_1- ]- ++ bulkOfLicenses-licenseExceptionIdList LicenseListVersion_3_2 =- [ LLVM_exception- , Nokia_Qt_exception_1_1- , OpenJDK_assembly_exception_1_0- , PS_or_PDF_font_exception_20170817- , Qt_GPL_exception_1_0- , Qt_LGPL_exception_1_1- ]- ++ bulkOfLicenses-licenseExceptionIdList LicenseListVersion_3_6 =- [ GPL_CC_1_0- , LLVM_exception- , OCaml_LGPL_linking_exception- , OpenJDK_assembly_exception_1_0- , PS_or_PDF_font_exception_20170817- , Qt_GPL_exception_1_0- , Qt_LGPL_exception_1_1- , Swift_exception- , Universal_FOSS_exception_1_0- ]- ++ bulkOfLicenses-licenseExceptionIdList LicenseListVersion_3_9 =- [ GPL_3_0_linking_exception- , GPL_3_0_linking_source_exception- , GPL_CC_1_0- , LGPL_3_0_linking_exception- , LLVM_exception- , OCaml_LGPL_linking_exception- , OpenJDK_assembly_exception_1_0- , PS_or_PDF_font_exception_20170817- , Qt_GPL_exception_1_0- , Qt_LGPL_exception_1_1- , SHL_2_0- , SHL_2_1- , Swift_exception- , Universal_FOSS_exception_1_0- ]- ++ bulkOfLicenses-licenseExceptionIdList LicenseListVersion_3_10 =- [ GPL_3_0_linking_exception- , GPL_3_0_linking_source_exception- , GPL_CC_1_0- , LGPL_3_0_linking_exception- , LLVM_exception- , OCaml_LGPL_linking_exception- , OpenJDK_assembly_exception_1_0- , PS_or_PDF_font_exception_20170817- , Qt_GPL_exception_1_0- , Qt_LGPL_exception_1_1- , SHL_2_0- , SHL_2_1- , Swift_exception- , Universal_FOSS_exception_1_0- ]- ++ bulkOfLicenses---- | Create a 'LicenseExceptionId' from a 'String'.-mkLicenseExceptionId :: LicenseListVersion -> String -> Maybe LicenseExceptionId-mkLicenseExceptionId LicenseListVersion_3_0 s = Map.lookup s stringLookup_3_0-mkLicenseExceptionId LicenseListVersion_3_2 s = Map.lookup s stringLookup_3_2-mkLicenseExceptionId LicenseListVersion_3_6 s = Map.lookup s stringLookup_3_6-mkLicenseExceptionId LicenseListVersion_3_9 s = Map.lookup s stringLookup_3_9-mkLicenseExceptionId LicenseListVersion_3_10 s = Map.lookup s stringLookup_3_10--stringLookup_3_0 :: Map String LicenseExceptionId-stringLookup_3_0 = Map.fromList $ map (\i -> (licenseExceptionId i, i)) $- licenseExceptionIdList LicenseListVersion_3_0--stringLookup_3_2 :: Map String LicenseExceptionId-stringLookup_3_2 = Map.fromList $ map (\i -> (licenseExceptionId i, i)) $- licenseExceptionIdList LicenseListVersion_3_2--stringLookup_3_6 :: Map String LicenseExceptionId-stringLookup_3_6 = Map.fromList $ map (\i -> (licenseExceptionId i, i)) $- licenseExceptionIdList LicenseListVersion_3_6--stringLookup_3_9 :: Map String LicenseExceptionId-stringLookup_3_9 = Map.fromList $ map (\i -> (licenseExceptionId i, i)) $- licenseExceptionIdList LicenseListVersion_3_9--stringLookup_3_10 :: Map String LicenseExceptionId-stringLookup_3_10 = Map.fromList $ map (\i -> (licenseExceptionId i, i)) $- licenseExceptionIdList LicenseListVersion_3_10---- | License exceptions in all SPDX License lists-bulkOfLicenses :: [LicenseExceptionId]-bulkOfLicenses =- [ DS389_exception- , Autoconf_exception_2_0- , Autoconf_exception_3_0- , Bison_exception_2_2- , Bootloader_exception- , Classpath_exception_2_0- , CLISP_exception_2_0- , DigiRule_FOSS_exception- , ECos_exception_2_0- , Fawkes_Runtime_exception- , FLTK_exception- , Font_exception_2_0- , Freertos_exception_2_0- , GCC_exception_2_0- , GCC_exception_3_1- , Gnu_javamail_exception- , I2p_gpl_java_exception- , Libtool_exception- , Linux_syscall_note- , LZMA_exception- , Mif_exception- , OCCT_exception_1_0- , Openvpn_openssl_exception- , Qwt_exception_1_0- , U_boot_exception_2_0- , WxWindows_exception_3_1- ]
− src/Distribution/SPDX/LicenseExpression.hs
@@ -1,162 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.SPDX.LicenseExpression (- LicenseExpression (..),- SimpleLicenseExpression (..),- simpleLicenseExpression,- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Parsec-import Distribution.Pretty-import Distribution.SPDX.LicenseExceptionId-import Distribution.SPDX.LicenseId-import Distribution.SPDX.LicenseListVersion-import Distribution.SPDX.LicenseReference-import Distribution.Utils.Generic (isAsciiAlphaNum)--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp---- | SPDX License Expression.------ @--- idstring = 1*(ALPHA \/ DIGIT \/ "-" \/ "." )--- license id = \<short form license identifier inAppendix I.1>--- license exception id = \<short form license exception identifier inAppendix I.2>--- license ref = [\"DocumentRef-"1*(idstring)":"]\"LicenseRef-"1*(idstring)------ simple expression = license id \/ license id"+" \/ license ref------ compound expression = 1*1(simple expression \/--- simple expression \"WITH" license exception id \/--- compound expression \"AND" compound expression \/--- compound expression \"OR" compound expression ) \/--- "(" compound expression ")" )------ license expression = 1*1(simple expression / compound expression)--- @-data LicenseExpression- = ELicense !SimpleLicenseExpression !(Maybe LicenseExceptionId)- | EAnd !LicenseExpression !LicenseExpression- | EOr !LicenseExpression !LicenseExpression- deriving (Show, Read, Eq, Ord, Typeable, Data, Generic)---- | Simple License Expressions.-data SimpleLicenseExpression- = ELicenseId LicenseId- -- ^ An SPDX License List Short Form Identifier. For example: @GPL-2.0-only@- | ELicenseIdPlus LicenseId- -- ^ An SPDX License List Short Form Identifier with a unary"+" operator suffix to represent the current version of the license or any later version. For example: @GPL-2.0+@- | ELicenseRef LicenseRef- -- ^ A SPDX user defined license reference: For example: @LicenseRef-23@, @LicenseRef-MIT-Style-1@, or @DocumentRef-spdx-tool-1.2:LicenseRef-MIT-Style-2@- deriving (Show, Read, Eq, Ord, Typeable, Data, Generic)--simpleLicenseExpression :: LicenseId -> LicenseExpression-simpleLicenseExpression i = ELicense (ELicenseId i) Nothing--instance Binary LicenseExpression-instance Binary SimpleLicenseExpression-instance Structured SimpleLicenseExpression-instance Structured LicenseExpression--instance Pretty LicenseExpression where- pretty = go 0- where- go :: Int -> LicenseExpression -> Disp.Doc- go _ (ELicense lic exc) =- let doc = pretty lic- in maybe id (\e d -> d <+> Disp.text "WITH" <+> pretty e) exc doc- go d (EAnd e1 e2) = parens (d < 0) $ go 0 e1 <+> Disp.text "AND" <+> go 0 e2- go d (EOr e1 e2) = parens (d < 1) $ go 1 e1 <+> Disp.text "OR" <+> go 1 e2--- parens False doc = doc- parens True doc = Disp.parens doc--instance Pretty SimpleLicenseExpression where- pretty (ELicenseId i) = pretty i- pretty (ELicenseIdPlus i) = pretty i <<>> Disp.char '+'- pretty (ELicenseRef r) = pretty r--instance Parsec SimpleLicenseExpression where- parsec = idstring >>= simple where- simple n- | Just l <- "LicenseRef-" `isPrefixOfMaybe` n =- maybe (fail $ "Incorrect LicenseRef format: " ++ n) (return . ELicenseRef) $ mkLicenseRef Nothing l- | Just d <- "DocumentRef-" `isPrefixOfMaybe` n = do- _ <- P.string ":LicenseRef-"- l <- idstring- maybe (fail $ "Incorrect LicenseRef format:" ++ n) (return . ELicenseRef) $ mkLicenseRef (Just d) l- | otherwise = do- v <- askCabalSpecVersion- l <- maybe (fail $ "Unknown SPDX license identifier: '" ++ n ++ "' " ++ licenseIdMigrationMessage n) return $- mkLicenseId (cabalSpecVersionToSPDXListVersion v) n- orLater <- isJust <$> P.optional (P.char '+')- if orLater- then return (ELicenseIdPlus l)- else return (ELicenseId l)--idstring :: P.CharParsing m => m String-idstring = P.munch1 $ \c -> isAsciiAlphaNum c || c == '-' || c == '.'---- returns suffix part-isPrefixOfMaybe :: Eq a => [a] -> [a] -> Maybe [a]-isPrefixOfMaybe pfx s- | pfx `isPrefixOf` s = Just (drop (length pfx) s)- | otherwise = Nothing--instance Parsec LicenseExpression where- parsec = expr- where- expr = compoundOr-- simple = do- s <- parsec- exc <- exception- return $ ELicense s exc-- exception = P.optional $ P.try (spaces1 *> P.string "WITH" *> spaces1) *> parsec-- compoundOr = do- x <- compoundAnd- l <- P.optional $ P.try (spaces1 *> P.string "OR" *> spaces1) *> compoundOr- return $ maybe id (flip EOr) l x-- compoundAnd = do- x <- compound- l <- P.optional $ P.try (spaces1 *> P.string "AND" *> spaces1) *> compoundAnd- return $ maybe id (flip EAnd) l x-- compound = braces <|> simple-- -- NOTE: we require that there's a space around AND & OR operators,- -- i.e. @(MIT)AND(MIT)@ will cause parse-error.- braces = do- _ <- P.char '('- _ <- P.spaces- x <- expr- _ <- P.char ')'- return x-- spaces1 = P.space *> P.spaces---- notes:------ There MUST NOT be whitespace between a licenseid and any following "+". This supports easy parsing and--- backwards compatibility. There MUST be whitespace on either side of the operator "WITH". There MUST be--- whitespace and/or parentheses on either side of the operators "AND" and "OR".------ We handle that by having greedy 'idstring' parser, so MITAND would parse as invalid license identifier.--instance NFData LicenseExpression where- rnf (ELicense s e) = rnf s `seq` rnf e- rnf (EAnd x y) = rnf x `seq` rnf y- rnf (EOr x y) = rnf x `seq` rnf y--instance NFData SimpleLicenseExpression where- rnf (ELicenseId i) = rnf i- rnf (ELicenseIdPlus i) = rnf i- rnf (ELicenseRef r) = rnf r
− src/Distribution/SPDX/LicenseId.hs
@@ -1,2152 +0,0 @@--- This file is generated. See Makefile's spdx rule-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.SPDX.LicenseId (- LicenseId (..),- licenseId,- licenseName,- licenseIsOsiApproved,- licenseIsFsfLibre,- mkLicenseId,- licenseIdList,- -- * Helpers- licenseIdMigrationMessage,- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Compat.Lens (set)-import Distribution.Pretty-import Distribution.Parsec-import Distribution.Utils.Generic (isAsciiAlphaNum)-import Distribution.Utils.Structured (Structured (..), nominalStructure, typeVersion)-import Distribution.SPDX.LicenseListVersion--import qualified Data.Binary.Get as Binary-import qualified Data.Binary.Put as Binary-import qualified Data.Map.Strict as Map-import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp------------------------------------------------------------------------------------ LicenseId------------------------------------------------------------------------------------ | SPDX License identifier-data LicenseId- = NullBSD -- ^ @0BSD@, BSD Zero Clause License- | AAL -- ^ @AAL@, Attribution Assurance License- | Abstyles -- ^ @Abstyles@, Abstyles License- | Adobe_2006 -- ^ @Adobe-2006@, Adobe Systems Incorporated Source Code License Agreement- | Adobe_Glyph -- ^ @Adobe-Glyph@, Adobe Glyph List License- | ADSL -- ^ @ADSL@, Amazon Digital Services License- | AFL_1_1 -- ^ @AFL-1.1@, Academic Free License v1.1- | AFL_1_2 -- ^ @AFL-1.2@, Academic Free License v1.2- | AFL_2_0 -- ^ @AFL-2.0@, Academic Free License v2.0- | AFL_2_1 -- ^ @AFL-2.1@, Academic Free License v2.1- | AFL_3_0 -- ^ @AFL-3.0@, Academic Free License v3.0- | Afmparse -- ^ @Afmparse@, Afmparse License- | AGPL_1_0 -- ^ @AGPL-1.0@, Affero General Public License v1.0, SPDX License List 3.0- | AGPL_1_0_only -- ^ @AGPL-1.0-only@, Affero General Public License v1.0 only, SPDX License List 3.2, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | AGPL_1_0_or_later -- ^ @AGPL-1.0-or-later@, Affero General Public License v1.0 or later, SPDX License List 3.2, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | AGPL_3_0_only -- ^ @AGPL-3.0-only@, GNU Affero General Public License v3.0 only- | AGPL_3_0_or_later -- ^ @AGPL-3.0-or-later@, GNU Affero General Public License v3.0 or later- | Aladdin -- ^ @Aladdin@, Aladdin Free Public License- | AMDPLPA -- ^ @AMDPLPA@, AMD's plpa_map.c License- | AML -- ^ @AML@, Apple MIT License- | AMPAS -- ^ @AMPAS@, Academy of Motion Picture Arts and Sciences BSD- | ANTLR_PD -- ^ @ANTLR-PD@, ANTLR Software Rights Notice- | Apache_1_0 -- ^ @Apache-1.0@, Apache License 1.0- | Apache_1_1 -- ^ @Apache-1.1@, Apache License 1.1- | Apache_2_0 -- ^ @Apache-2.0@, Apache License 2.0- | APAFML -- ^ @APAFML@, Adobe Postscript AFM License- | APL_1_0 -- ^ @APL-1.0@, Adaptive Public License 1.0- | APSL_1_0 -- ^ @APSL-1.0@, Apple Public Source License 1.0- | APSL_1_1 -- ^ @APSL-1.1@, Apple Public Source License 1.1- | APSL_1_2 -- ^ @APSL-1.2@, Apple Public Source License 1.2- | APSL_2_0 -- ^ @APSL-2.0@, Apple Public Source License 2.0- | Artistic_1_0_cl8 -- ^ @Artistic-1.0-cl8@, Artistic License 1.0 w/clause 8- | Artistic_1_0_Perl -- ^ @Artistic-1.0-Perl@, Artistic License 1.0 (Perl)- | Artistic_1_0 -- ^ @Artistic-1.0@, Artistic License 1.0- | Artistic_2_0 -- ^ @Artistic-2.0@, Artistic License 2.0- | Bahyph -- ^ @Bahyph@, Bahyph License- | Barr -- ^ @Barr@, Barr License- | Beerware -- ^ @Beerware@, Beerware License- | BitTorrent_1_0 -- ^ @BitTorrent-1.0@, BitTorrent Open Source License v1.0- | BitTorrent_1_1 -- ^ @BitTorrent-1.1@, BitTorrent Open Source License v1.1- | Blessing -- ^ @blessing@, SQLite Blessing, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | BlueOak_1_0_0 -- ^ @BlueOak-1.0.0@, Blue Oak Model License 1.0.0, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | Borceux -- ^ @Borceux@, Borceux license- | BSD_1_Clause -- ^ @BSD-1-Clause@, BSD 1-Clause License- | BSD_2_Clause_FreeBSD -- ^ @BSD-2-Clause-FreeBSD@, BSD 2-Clause FreeBSD License, SPDX License List 3.0, SPDX License List 3.2, SPDX License List 3.6, SPDX License List 3.9- | BSD_2_Clause_NetBSD -- ^ @BSD-2-Clause-NetBSD@, BSD 2-Clause NetBSD License, SPDX License List 3.0, SPDX License List 3.2, SPDX License List 3.6- | BSD_2_Clause_Patent -- ^ @BSD-2-Clause-Patent@, BSD-2-Clause Plus Patent License- | BSD_2_Clause_Views -- ^ @BSD-2-Clause-Views@, BSD 2-Clause with views sentence, SPDX License List 3.10- | BSD_2_Clause -- ^ @BSD-2-Clause@, BSD 2-Clause "Simplified" License- | BSD_3_Clause_Attribution -- ^ @BSD-3-Clause-Attribution@, BSD with attribution- | BSD_3_Clause_Clear -- ^ @BSD-3-Clause-Clear@, BSD 3-Clause Clear License- | BSD_3_Clause_LBNL -- ^ @BSD-3-Clause-LBNL@, Lawrence Berkeley National Labs BSD variant license- | BSD_3_Clause_No_Nuclear_License_2014 -- ^ @BSD-3-Clause-No-Nuclear-License-2014@, BSD 3-Clause No Nuclear License 2014- | BSD_3_Clause_No_Nuclear_License -- ^ @BSD-3-Clause-No-Nuclear-License@, BSD 3-Clause No Nuclear License- | BSD_3_Clause_No_Nuclear_Warranty -- ^ @BSD-3-Clause-No-Nuclear-Warranty@, BSD 3-Clause No Nuclear Warranty- | BSD_3_Clause_Open_MPI -- ^ @BSD-3-Clause-Open-MPI@, BSD 3-Clause Open MPI variant, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | BSD_3_Clause -- ^ @BSD-3-Clause@, BSD 3-Clause "New" or "Revised" License- | BSD_4_Clause_UC -- ^ @BSD-4-Clause-UC@, BSD-4-Clause (University of California-Specific)- | BSD_4_Clause -- ^ @BSD-4-Clause@, BSD 4-Clause "Original" or "Old" License- | BSD_Protection -- ^ @BSD-Protection@, BSD Protection License- | BSD_Source_Code -- ^ @BSD-Source-Code@, BSD Source Code Attribution- | BSL_1_0 -- ^ @BSL-1.0@, Boost Software License 1.0- | Bzip2_1_0_5 -- ^ @bzip2-1.0.5@, bzip2 and libbzip2 License v1.0.5- | Bzip2_1_0_6 -- ^ @bzip2-1.0.6@, bzip2 and libbzip2 License v1.0.6- | CAL_1_0_Combined_Work_Exception -- ^ @CAL-1.0-Combined-Work-Exception@, Cryptographic Autonomy License 1.0 (Combined Work Exception), SPDX License List 3.9, SPDX License List 3.10- | CAL_1_0 -- ^ @CAL-1.0@, Cryptographic Autonomy License 1.0, SPDX License List 3.9, SPDX License List 3.10- | Caldera -- ^ @Caldera@, Caldera License- | CATOSL_1_1 -- ^ @CATOSL-1.1@, Computer Associates Trusted Open Source License 1.1- | CC_BY_1_0 -- ^ @CC-BY-1.0@, Creative Commons Attribution 1.0 Generic- | CC_BY_2_0 -- ^ @CC-BY-2.0@, Creative Commons Attribution 2.0 Generic- | CC_BY_2_5 -- ^ @CC-BY-2.5@, Creative Commons Attribution 2.5 Generic- | CC_BY_3_0_AT -- ^ @CC-BY-3.0-AT@, Creative Commons Attribution 3.0 Austria, SPDX License List 3.10- | CC_BY_3_0 -- ^ @CC-BY-3.0@, Creative Commons Attribution 3.0 Unported- | CC_BY_4_0 -- ^ @CC-BY-4.0@, Creative Commons Attribution 4.0 International- | CC_BY_NC_1_0 -- ^ @CC-BY-NC-1.0@, Creative Commons Attribution Non Commercial 1.0 Generic- | CC_BY_NC_2_0 -- ^ @CC-BY-NC-2.0@, Creative Commons Attribution Non Commercial 2.0 Generic- | CC_BY_NC_2_5 -- ^ @CC-BY-NC-2.5@, Creative Commons Attribution Non Commercial 2.5 Generic- | CC_BY_NC_3_0 -- ^ @CC-BY-NC-3.0@, Creative Commons Attribution Non Commercial 3.0 Unported- | CC_BY_NC_4_0 -- ^ @CC-BY-NC-4.0@, Creative Commons Attribution Non Commercial 4.0 International- | CC_BY_NC_ND_1_0 -- ^ @CC-BY-NC-ND-1.0@, Creative Commons Attribution Non Commercial No Derivatives 1.0 Generic- | CC_BY_NC_ND_2_0 -- ^ @CC-BY-NC-ND-2.0@, Creative Commons Attribution Non Commercial No Derivatives 2.0 Generic- | CC_BY_NC_ND_2_5 -- ^ @CC-BY-NC-ND-2.5@, Creative Commons Attribution Non Commercial No Derivatives 2.5 Generic- | CC_BY_NC_ND_3_0_IGO -- ^ @CC-BY-NC-ND-3.0-IGO@, Creative Commons Attribution Non Commercial No Derivatives 3.0 IGO, SPDX License List 3.10- | CC_BY_NC_ND_3_0 -- ^ @CC-BY-NC-ND-3.0@, Creative Commons Attribution Non Commercial No Derivatives 3.0 Unported- | CC_BY_NC_ND_4_0 -- ^ @CC-BY-NC-ND-4.0@, Creative Commons Attribution Non Commercial No Derivatives 4.0 International- | CC_BY_NC_SA_1_0 -- ^ @CC-BY-NC-SA-1.0@, Creative Commons Attribution Non Commercial Share Alike 1.0 Generic- | CC_BY_NC_SA_2_0 -- ^ @CC-BY-NC-SA-2.0@, Creative Commons Attribution Non Commercial Share Alike 2.0 Generic- | CC_BY_NC_SA_2_5 -- ^ @CC-BY-NC-SA-2.5@, Creative Commons Attribution Non Commercial Share Alike 2.5 Generic- | CC_BY_NC_SA_3_0 -- ^ @CC-BY-NC-SA-3.0@, Creative Commons Attribution Non Commercial Share Alike 3.0 Unported- | CC_BY_NC_SA_4_0 -- ^ @CC-BY-NC-SA-4.0@, Creative Commons Attribution Non Commercial Share Alike 4.0 International- | CC_BY_ND_1_0 -- ^ @CC-BY-ND-1.0@, Creative Commons Attribution No Derivatives 1.0 Generic- | CC_BY_ND_2_0 -- ^ @CC-BY-ND-2.0@, Creative Commons Attribution No Derivatives 2.0 Generic- | CC_BY_ND_2_5 -- ^ @CC-BY-ND-2.5@, Creative Commons Attribution No Derivatives 2.5 Generic- | CC_BY_ND_3_0 -- ^ @CC-BY-ND-3.0@, Creative Commons Attribution No Derivatives 3.0 Unported- | CC_BY_ND_4_0 -- ^ @CC-BY-ND-4.0@, Creative Commons Attribution No Derivatives 4.0 International- | CC_BY_SA_1_0 -- ^ @CC-BY-SA-1.0@, Creative Commons Attribution Share Alike 1.0 Generic- | CC_BY_SA_2_0 -- ^ @CC-BY-SA-2.0@, Creative Commons Attribution Share Alike 2.0 Generic- | CC_BY_SA_2_5 -- ^ @CC-BY-SA-2.5@, Creative Commons Attribution Share Alike 2.5 Generic- | CC_BY_SA_3_0_AT -- ^ @CC-BY-SA-3.0-AT@, Creative Commons Attribution-Share Alike 3.0 Austria, SPDX License List 3.10- | CC_BY_SA_3_0 -- ^ @CC-BY-SA-3.0@, Creative Commons Attribution Share Alike 3.0 Unported- | CC_BY_SA_4_0 -- ^ @CC-BY-SA-4.0@, Creative Commons Attribution Share Alike 4.0 International- | CC_PDDC -- ^ @CC-PDDC@, Creative Commons Public Domain Dedication and Certification, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | CC0_1_0 -- ^ @CC0-1.0@, Creative Commons Zero v1.0 Universal- | CDDL_1_0 -- ^ @CDDL-1.0@, Common Development and Distribution License 1.0- | CDDL_1_1 -- ^ @CDDL-1.1@, Common Development and Distribution License 1.1- | CDLA_Permissive_1_0 -- ^ @CDLA-Permissive-1.0@, Community Data License Agreement Permissive 1.0- | CDLA_Sharing_1_0 -- ^ @CDLA-Sharing-1.0@, Community Data License Agreement Sharing 1.0- | CECILL_1_0 -- ^ @CECILL-1.0@, CeCILL Free Software License Agreement v1.0- | CECILL_1_1 -- ^ @CECILL-1.1@, CeCILL Free Software License Agreement v1.1- | CECILL_2_0 -- ^ @CECILL-2.0@, CeCILL Free Software License Agreement v2.0- | CECILL_2_1 -- ^ @CECILL-2.1@, CeCILL Free Software License Agreement v2.1- | CECILL_B -- ^ @CECILL-B@, CeCILL-B Free Software License Agreement- | CECILL_C -- ^ @CECILL-C@, CeCILL-C Free Software License Agreement- | CERN_OHL_1_1 -- ^ @CERN-OHL-1.1@, CERN Open Hardware Licence v1.1, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | CERN_OHL_1_2 -- ^ @CERN-OHL-1.2@, CERN Open Hardware Licence v1.2, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | CERN_OHL_P_2_0 -- ^ @CERN-OHL-P-2.0@, CERN Open Hardware Licence Version 2 - Permissive, SPDX License List 3.9, SPDX License List 3.10- | CERN_OHL_S_2_0 -- ^ @CERN-OHL-S-2.0@, CERN Open Hardware Licence Version 2 - Strongly Reciprocal, SPDX License List 3.9, SPDX License List 3.10- | CERN_OHL_W_2_0 -- ^ @CERN-OHL-W-2.0@, CERN Open Hardware Licence Version 2 - Weakly Reciprocal, SPDX License List 3.9, SPDX License List 3.10- | ClArtistic -- ^ @ClArtistic@, Clarified Artistic License- | CNRI_Jython -- ^ @CNRI-Jython@, CNRI Jython License- | CNRI_Python_GPL_Compatible -- ^ @CNRI-Python-GPL-Compatible@, CNRI Python Open Source GPL Compatible License Agreement- | CNRI_Python -- ^ @CNRI-Python@, CNRI Python License- | Condor_1_1 -- ^ @Condor-1.1@, Condor Public License v1.1- | Copyleft_next_0_3_0 -- ^ @copyleft-next-0.3.0@, copyleft-next 0.3.0, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | Copyleft_next_0_3_1 -- ^ @copyleft-next-0.3.1@, copyleft-next 0.3.1, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | CPAL_1_0 -- ^ @CPAL-1.0@, Common Public Attribution License 1.0- | CPL_1_0 -- ^ @CPL-1.0@, Common Public License 1.0- | CPOL_1_02 -- ^ @CPOL-1.02@, Code Project Open License 1.02- | Crossword -- ^ @Crossword@, Crossword License- | CrystalStacker -- ^ @CrystalStacker@, CrystalStacker License- | CUA_OPL_1_0 -- ^ @CUA-OPL-1.0@, CUA Office Public License v1.0- | Cube -- ^ @Cube@, Cube License- | Curl -- ^ @curl@, curl License- | D_FSL_1_0 -- ^ @D-FSL-1.0@, Deutsche Freie Software Lizenz- | Diffmark -- ^ @diffmark@, diffmark license- | DOC -- ^ @DOC@, DOC License- | Dotseqn -- ^ @Dotseqn@, Dotseqn License- | DSDP -- ^ @DSDP@, DSDP License- | Dvipdfm -- ^ @dvipdfm@, dvipdfm License- | ECL_1_0 -- ^ @ECL-1.0@, Educational Community License v1.0- | ECL_2_0 -- ^ @ECL-2.0@, Educational Community License v2.0- | EFL_1_0 -- ^ @EFL-1.0@, Eiffel Forum License v1.0- | EFL_2_0 -- ^ @EFL-2.0@, Eiffel Forum License v2.0- | EGenix -- ^ @eGenix@, eGenix.com Public License 1.1.0- | Entessa -- ^ @Entessa@, Entessa Public License v1.0- | EPICS -- ^ @EPICS@, EPICS Open License, SPDX License List 3.10- | EPL_1_0 -- ^ @EPL-1.0@, Eclipse Public License 1.0- | EPL_2_0 -- ^ @EPL-2.0@, Eclipse Public License 2.0- | ErlPL_1_1 -- ^ @ErlPL-1.1@, Erlang Public License v1.1- | Etalab_2_0 -- ^ @etalab-2.0@, Etalab Open License 2.0, SPDX License List 3.9, SPDX License List 3.10- | EUDatagrid -- ^ @EUDatagrid@, EU DataGrid Software License- | EUPL_1_0 -- ^ @EUPL-1.0@, European Union Public License 1.0- | EUPL_1_1 -- ^ @EUPL-1.1@, European Union Public License 1.1- | EUPL_1_2 -- ^ @EUPL-1.2@, European Union Public License 1.2- | Eurosym -- ^ @Eurosym@, Eurosym License- | Fair -- ^ @Fair@, Fair License- | Frameworx_1_0 -- ^ @Frameworx-1.0@, Frameworx Open License 1.0- | FreeImage -- ^ @FreeImage@, FreeImage Public License v1.0- | FSFAP -- ^ @FSFAP@, FSF All Permissive License- | FSFULLR -- ^ @FSFULLR@, FSF Unlimited License (with License Retention)- | FSFUL -- ^ @FSFUL@, FSF Unlimited License- | FTL -- ^ @FTL@, Freetype Project License- | GFDL_1_1_invariants_only -- ^ @GFDL-1.1-invariants-only@, GNU Free Documentation License v1.1 only - invariants, SPDX License List 3.10- | GFDL_1_1_invariants_or_later -- ^ @GFDL-1.1-invariants-or-later@, GNU Free Documentation License v1.1 or later - invariants, SPDX License List 3.10- | GFDL_1_1_no_invariants_only -- ^ @GFDL-1.1-no-invariants-only@, GNU Free Documentation License v1.1 only - no invariants, SPDX License List 3.10- | GFDL_1_1_no_invariants_or_later -- ^ @GFDL-1.1-no-invariants-or-later@, GNU Free Documentation License v1.1 or later - no invariants, SPDX License List 3.10- | GFDL_1_1_only -- ^ @GFDL-1.1-only@, GNU Free Documentation License v1.1 only- | GFDL_1_1_or_later -- ^ @GFDL-1.1-or-later@, GNU Free Documentation License v1.1 or later- | GFDL_1_2_invariants_only -- ^ @GFDL-1.2-invariants-only@, GNU Free Documentation License v1.2 only - invariants, SPDX License List 3.10- | GFDL_1_2_invariants_or_later -- ^ @GFDL-1.2-invariants-or-later@, GNU Free Documentation License v1.2 or later - invariants, SPDX License List 3.10- | GFDL_1_2_no_invariants_only -- ^ @GFDL-1.2-no-invariants-only@, GNU Free Documentation License v1.2 only - no invariants, SPDX License List 3.10- | GFDL_1_2_no_invariants_or_later -- ^ @GFDL-1.2-no-invariants-or-later@, GNU Free Documentation License v1.2 or later - no invariants, SPDX License List 3.10- | GFDL_1_2_only -- ^ @GFDL-1.2-only@, GNU Free Documentation License v1.2 only- | GFDL_1_2_or_later -- ^ @GFDL-1.2-or-later@, GNU Free Documentation License v1.2 or later- | GFDL_1_3_invariants_only -- ^ @GFDL-1.3-invariants-only@, GNU Free Documentation License v1.3 only - invariants, SPDX License List 3.10- | GFDL_1_3_invariants_or_later -- ^ @GFDL-1.3-invariants-or-later@, GNU Free Documentation License v1.3 or later - invariants, SPDX License List 3.10- | GFDL_1_3_no_invariants_only -- ^ @GFDL-1.3-no-invariants-only@, GNU Free Documentation License v1.3 only - no invariants, SPDX License List 3.10- | GFDL_1_3_no_invariants_or_later -- ^ @GFDL-1.3-no-invariants-or-later@, GNU Free Documentation License v1.3 or later - no invariants, SPDX License List 3.10- | GFDL_1_3_only -- ^ @GFDL-1.3-only@, GNU Free Documentation License v1.3 only- | GFDL_1_3_or_later -- ^ @GFDL-1.3-or-later@, GNU Free Documentation License v1.3 or later- | Giftware -- ^ @Giftware@, Giftware License- | GL2PS -- ^ @GL2PS@, GL2PS License- | Glide -- ^ @Glide@, 3dfx Glide License- | Glulxe -- ^ @Glulxe@, Glulxe License- | GLWTPL -- ^ @GLWTPL@, Good Luck With That Public License, SPDX License List 3.10- | Gnuplot -- ^ @gnuplot@, gnuplot License- | GPL_1_0_only -- ^ @GPL-1.0-only@, GNU General Public License v1.0 only- | GPL_1_0_or_later -- ^ @GPL-1.0-or-later@, GNU General Public License v1.0 or later- | GPL_2_0_only -- ^ @GPL-2.0-only@, GNU General Public License v2.0 only- | GPL_2_0_or_later -- ^ @GPL-2.0-or-later@, GNU General Public License v2.0 or later- | GPL_3_0_only -- ^ @GPL-3.0-only@, GNU General Public License v3.0 only- | GPL_3_0_or_later -- ^ @GPL-3.0-or-later@, GNU General Public License v3.0 or later- | GSOAP_1_3b -- ^ @gSOAP-1.3b@, gSOAP Public License v1.3b- | HaskellReport -- ^ @HaskellReport@, Haskell Language Report License- | Hippocratic_2_1 -- ^ @Hippocratic-2.1@, Hippocratic License 2.1, SPDX License List 3.9, SPDX License List 3.10- | HPND_sell_variant -- ^ @HPND-sell-variant@, Historical Permission Notice and Disclaimer - sell variant, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | HPND -- ^ @HPND@, Historical Permission Notice and Disclaimer- | IBM_pibs -- ^ @IBM-pibs@, IBM PowerPC Initialization and Boot Software- | ICU -- ^ @ICU@, ICU License- | IJG -- ^ @IJG@, Independent JPEG Group License- | ImageMagick -- ^ @ImageMagick@, ImageMagick License- | IMatix -- ^ @iMatix@, iMatix Standard Function Library Agreement- | Imlib2 -- ^ @Imlib2@, Imlib2 License- | Info_ZIP -- ^ @Info-ZIP@, Info-ZIP License- | Intel_ACPI -- ^ @Intel-ACPI@, Intel ACPI Software License Agreement- | Intel -- ^ @Intel@, Intel Open Source License- | Interbase_1_0 -- ^ @Interbase-1.0@, Interbase Public License v1.0- | IPA -- ^ @IPA@, IPA Font License- | IPL_1_0 -- ^ @IPL-1.0@, IBM Public License v1.0- | ISC -- ^ @ISC@, ISC License- | JasPer_2_0 -- ^ @JasPer-2.0@, JasPer License- | JPNIC -- ^ @JPNIC@, Japan Network Information Center License, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | JSON -- ^ @JSON@, JSON License- | LAL_1_2 -- ^ @LAL-1.2@, Licence Art Libre 1.2- | LAL_1_3 -- ^ @LAL-1.3@, Licence Art Libre 1.3- | Latex2e -- ^ @Latex2e@, Latex2e License- | Leptonica -- ^ @Leptonica@, Leptonica License- | LGPL_2_0_only -- ^ @LGPL-2.0-only@, GNU Library General Public License v2 only- | LGPL_2_0_or_later -- ^ @LGPL-2.0-or-later@, GNU Library General Public License v2 or later- | LGPL_2_1_only -- ^ @LGPL-2.1-only@, GNU Lesser General Public License v2.1 only- | LGPL_2_1_or_later -- ^ @LGPL-2.1-or-later@, GNU Lesser General Public License v2.1 or later- | LGPL_3_0_only -- ^ @LGPL-3.0-only@, GNU Lesser General Public License v3.0 only- | LGPL_3_0_or_later -- ^ @LGPL-3.0-or-later@, GNU Lesser General Public License v3.0 or later- | LGPLLR -- ^ @LGPLLR@, Lesser General Public License For Linguistic Resources- | Libpng_2_0 -- ^ @libpng-2.0@, PNG Reference Library version 2, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | Libpng -- ^ @Libpng@, libpng License- | Libselinux_1_0 -- ^ @libselinux-1.0@, libselinux public domain notice, SPDX License List 3.9, SPDX License List 3.10- | Libtiff -- ^ @libtiff@, libtiff License- | LiLiQ_P_1_1 -- ^ @LiLiQ-P-1.1@, Licence Libre du Québec – Permissive version 1.1- | LiLiQ_R_1_1 -- ^ @LiLiQ-R-1.1@, Licence Libre du Québec – Réciprocité version 1.1- | LiLiQ_Rplus_1_1 -- ^ @LiLiQ-Rplus-1.1@, Licence Libre du Québec – Réciprocité forte version 1.1- | Linux_OpenIB -- ^ @Linux-OpenIB@, Linux Kernel Variant of OpenIB.org license, SPDX License List 3.2, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | LPL_1_02 -- ^ @LPL-1.02@, Lucent Public License v1.02- | LPL_1_0 -- ^ @LPL-1.0@, Lucent Public License Version 1.0- | LPPL_1_0 -- ^ @LPPL-1.0@, LaTeX Project Public License v1.0- | LPPL_1_1 -- ^ @LPPL-1.1@, LaTeX Project Public License v1.1- | LPPL_1_2 -- ^ @LPPL-1.2@, LaTeX Project Public License v1.2- | LPPL_1_3a -- ^ @LPPL-1.3a@, LaTeX Project Public License v1.3a- | LPPL_1_3c -- ^ @LPPL-1.3c@, LaTeX Project Public License v1.3c- | MakeIndex -- ^ @MakeIndex@, MakeIndex License- | MirOS -- ^ @MirOS@, The MirOS Licence- | MIT_0 -- ^ @MIT-0@, MIT No Attribution, SPDX License List 3.2, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | MIT_advertising -- ^ @MIT-advertising@, Enlightenment License (e16)- | MIT_CMU -- ^ @MIT-CMU@, CMU License- | MIT_enna -- ^ @MIT-enna@, enna License- | MIT_feh -- ^ @MIT-feh@, feh License- | MITNFA -- ^ @MITNFA@, MIT +no-false-attribs license- | MIT -- ^ @MIT@, MIT License- | Motosoto -- ^ @Motosoto@, Motosoto License- | Mpich2 -- ^ @mpich2@, mpich2 License- | MPL_1_0 -- ^ @MPL-1.0@, Mozilla Public License 1.0- | MPL_1_1 -- ^ @MPL-1.1@, Mozilla Public License 1.1- | MPL_2_0_no_copyleft_exception -- ^ @MPL-2.0-no-copyleft-exception@, Mozilla Public License 2.0 (no copyleft exception)- | MPL_2_0 -- ^ @MPL-2.0@, Mozilla Public License 2.0- | MS_PL -- ^ @MS-PL@, Microsoft Public License- | MS_RL -- ^ @MS-RL@, Microsoft Reciprocal License- | MTLL -- ^ @MTLL@, Matrix Template Library License- | MulanPSL_1_0 -- ^ @MulanPSL-1.0@, Mulan Permissive Software License, Version 1, SPDX License List 3.9, SPDX License List 3.10- | MulanPSL_2_0 -- ^ @MulanPSL-2.0@, Mulan Permissive Software License, Version 2, SPDX License List 3.9, SPDX License List 3.10- | Multics -- ^ @Multics@, Multics License- | Mup -- ^ @Mup@, Mup License- | NASA_1_3 -- ^ @NASA-1.3@, NASA Open Source Agreement 1.3- | Naumen -- ^ @Naumen@, Naumen Public License- | NBPL_1_0 -- ^ @NBPL-1.0@, Net Boolean Public License v1- | NCGL_UK_2_0 -- ^ @NCGL-UK-2.0@, Non-Commercial Government Licence, SPDX License List 3.9, SPDX License List 3.10- | NCSA -- ^ @NCSA@, University of Illinois/NCSA Open Source License- | Net_SNMP -- ^ @Net-SNMP@, Net-SNMP License- | NetCDF -- ^ @NetCDF@, NetCDF license- | Newsletr -- ^ @Newsletr@, Newsletr License- | NGPL -- ^ @NGPL@, Nethack General Public License- | NIST_PD_fallback -- ^ @NIST-PD-fallback@, NIST Public Domain Notice with license fallback, SPDX License List 3.10- | NIST_PD -- ^ @NIST-PD@, NIST Public Domain Notice, SPDX License List 3.10- | NLOD_1_0 -- ^ @NLOD-1.0@, Norwegian Licence for Open Government Data- | NLPL -- ^ @NLPL@, No Limit Public License- | Nokia -- ^ @Nokia@, Nokia Open Source License- | NOSL -- ^ @NOSL@, Netizen Open Source License- | Noweb -- ^ @Noweb@, Noweb License- | NPL_1_0 -- ^ @NPL-1.0@, Netscape Public License v1.0- | NPL_1_1 -- ^ @NPL-1.1@, Netscape Public License v1.1- | NPOSL_3_0 -- ^ @NPOSL-3.0@, Non-Profit Open Software License 3.0- | NRL -- ^ @NRL@, NRL License- | NTP_0 -- ^ @NTP-0@, NTP No Attribution, SPDX License List 3.9, SPDX License List 3.10- | NTP -- ^ @NTP@, NTP License- | O_UDA_1_0 -- ^ @O-UDA-1.0@, Open Use of Data Agreement v1.0, SPDX License List 3.9, SPDX License List 3.10- | OCCT_PL -- ^ @OCCT-PL@, Open CASCADE Technology Public License- | OCLC_2_0 -- ^ @OCLC-2.0@, OCLC Research Public License 2.0- | ODbL_1_0 -- ^ @ODbL-1.0@, ODC Open Database License v1.0- | ODC_By_1_0 -- ^ @ODC-By-1.0@, Open Data Commons Attribution License v1.0, SPDX License List 3.2, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | OFL_1_0_no_RFN -- ^ @OFL-1.0-no-RFN@, SIL Open Font License 1.0 with no Reserved Font Name, SPDX License List 3.9, SPDX License List 3.10- | OFL_1_0_RFN -- ^ @OFL-1.0-RFN@, SIL Open Font License 1.0 with Reserved Font Name, SPDX License List 3.9, SPDX License List 3.10- | OFL_1_0 -- ^ @OFL-1.0@, SIL Open Font License 1.0- | OFL_1_1_no_RFN -- ^ @OFL-1.1-no-RFN@, SIL Open Font License 1.1 with no Reserved Font Name, SPDX License List 3.9, SPDX License List 3.10- | OFL_1_1_RFN -- ^ @OFL-1.1-RFN@, SIL Open Font License 1.1 with Reserved Font Name, SPDX License List 3.9, SPDX License List 3.10- | OFL_1_1 -- ^ @OFL-1.1@, SIL Open Font License 1.1- | OGC_1_0 -- ^ @OGC-1.0@, OGC Software License, Version 1.0, SPDX License List 3.9, SPDX License List 3.10- | OGL_Canada_2_0 -- ^ @OGL-Canada-2.0@, Open Government Licence - Canada, SPDX License List 3.9, SPDX License List 3.10- | OGL_UK_1_0 -- ^ @OGL-UK-1.0@, Open Government Licence v1.0, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | OGL_UK_2_0 -- ^ @OGL-UK-2.0@, Open Government Licence v2.0, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | OGL_UK_3_0 -- ^ @OGL-UK-3.0@, Open Government Licence v3.0, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | OGTSL -- ^ @OGTSL@, Open Group Test Suite License- | OLDAP_1_1 -- ^ @OLDAP-1.1@, Open LDAP Public License v1.1- | OLDAP_1_2 -- ^ @OLDAP-1.2@, Open LDAP Public License v1.2- | OLDAP_1_3 -- ^ @OLDAP-1.3@, Open LDAP Public License v1.3- | OLDAP_1_4 -- ^ @OLDAP-1.4@, Open LDAP Public License v1.4- | OLDAP_2_0_1 -- ^ @OLDAP-2.0.1@, Open LDAP Public License v2.0.1- | OLDAP_2_0 -- ^ @OLDAP-2.0@, Open LDAP Public License v2.0 (or possibly 2.0A and 2.0B)- | OLDAP_2_1 -- ^ @OLDAP-2.1@, Open LDAP Public License v2.1- | OLDAP_2_2_1 -- ^ @OLDAP-2.2.1@, Open LDAP Public License v2.2.1- | OLDAP_2_2_2 -- ^ @OLDAP-2.2.2@, Open LDAP Public License 2.2.2- | OLDAP_2_2 -- ^ @OLDAP-2.2@, Open LDAP Public License v2.2- | OLDAP_2_3 -- ^ @OLDAP-2.3@, Open LDAP Public License v2.3- | OLDAP_2_4 -- ^ @OLDAP-2.4@, Open LDAP Public License v2.4- | OLDAP_2_5 -- ^ @OLDAP-2.5@, Open LDAP Public License v2.5- | OLDAP_2_6 -- ^ @OLDAP-2.6@, Open LDAP Public License v2.6- | OLDAP_2_7 -- ^ @OLDAP-2.7@, Open LDAP Public License v2.7- | OLDAP_2_8 -- ^ @OLDAP-2.8@, Open LDAP Public License v2.8- | OML -- ^ @OML@, Open Market License- | OpenSSL -- ^ @OpenSSL@, OpenSSL License- | OPL_1_0 -- ^ @OPL-1.0@, Open Public License v1.0- | OSET_PL_2_1 -- ^ @OSET-PL-2.1@, OSET Public License version 2.1- | OSL_1_0 -- ^ @OSL-1.0@, Open Software License 1.0- | OSL_1_1 -- ^ @OSL-1.1@, Open Software License 1.1- | OSL_2_0 -- ^ @OSL-2.0@, Open Software License 2.0- | OSL_2_1 -- ^ @OSL-2.1@, Open Software License 2.1- | OSL_3_0 -- ^ @OSL-3.0@, Open Software License 3.0- | Parity_6_0_0 -- ^ @Parity-6.0.0@, The Parity Public License 6.0.0, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | Parity_7_0_0 -- ^ @Parity-7.0.0@, The Parity Public License 7.0.0, SPDX License List 3.9, SPDX License List 3.10- | PDDL_1_0 -- ^ @PDDL-1.0@, ODC Public Domain Dedication & License 1.0- | PHP_3_01 -- ^ @PHP-3.01@, PHP License v3.01- | PHP_3_0 -- ^ @PHP-3.0@, PHP License v3.0- | Plexus -- ^ @Plexus@, Plexus Classworlds License- | PolyForm_Noncommercial_1_0_0 -- ^ @PolyForm-Noncommercial-1.0.0@, PolyForm Noncommercial License 1.0.0, SPDX License List 3.9, SPDX License List 3.10- | PolyForm_Small_Business_1_0_0 -- ^ @PolyForm-Small-Business-1.0.0@, PolyForm Small Business License 1.0.0, SPDX License List 3.9, SPDX License List 3.10- | PostgreSQL -- ^ @PostgreSQL@, PostgreSQL License- | PSF_2_0 -- ^ @PSF-2.0@, Python Software Foundation License 2.0, SPDX License List 3.9, SPDX License List 3.10- | Psfrag -- ^ @psfrag@, psfrag License- | Psutils -- ^ @psutils@, psutils License- | Python_2_0 -- ^ @Python-2.0@, Python License 2.0- | Qhull -- ^ @Qhull@, Qhull License- | QPL_1_0 -- ^ @QPL-1.0@, Q Public License 1.0- | Rdisc -- ^ @Rdisc@, Rdisc License- | RHeCos_1_1 -- ^ @RHeCos-1.1@, Red Hat eCos Public License v1.1- | RPL_1_1 -- ^ @RPL-1.1@, Reciprocal Public License 1.1- | RPL_1_5 -- ^ @RPL-1.5@, Reciprocal Public License 1.5- | RPSL_1_0 -- ^ @RPSL-1.0@, RealNetworks Public Source License v1.0- | RSA_MD -- ^ @RSA-MD@, RSA Message-Digest License- | RSCPL -- ^ @RSCPL@, Ricoh Source Code Public License- | Ruby -- ^ @Ruby@, Ruby License- | SAX_PD -- ^ @SAX-PD@, Sax Public Domain Notice- | Saxpath -- ^ @Saxpath@, Saxpath License- | SCEA -- ^ @SCEA@, SCEA Shared Source License- | Sendmail_8_23 -- ^ @Sendmail-8.23@, Sendmail License 8.23, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | Sendmail -- ^ @Sendmail@, Sendmail License- | SGI_B_1_0 -- ^ @SGI-B-1.0@, SGI Free Software License B v1.0- | SGI_B_1_1 -- ^ @SGI-B-1.1@, SGI Free Software License B v1.1- | SGI_B_2_0 -- ^ @SGI-B-2.0@, SGI Free Software License B v2.0- | SHL_0_51 -- ^ @SHL-0.51@, Solderpad Hardware License, Version 0.51, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | SHL_0_5 -- ^ @SHL-0.5@, Solderpad Hardware License v0.5, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | SimPL_2_0 -- ^ @SimPL-2.0@, Simple Public License 2.0- | SISSL_1_2 -- ^ @SISSL-1.2@, Sun Industry Standards Source License v1.2- | SISSL -- ^ @SISSL@, Sun Industry Standards Source License v1.1- | Sleepycat -- ^ @Sleepycat@, Sleepycat License- | SMLNJ -- ^ @SMLNJ@, Standard ML of New Jersey License- | SMPPL -- ^ @SMPPL@, Secure Messaging Protocol Public License- | SNIA -- ^ @SNIA@, SNIA Public License 1.1- | Spencer_86 -- ^ @Spencer-86@, Spencer License 86- | Spencer_94 -- ^ @Spencer-94@, Spencer License 94- | Spencer_99 -- ^ @Spencer-99@, Spencer License 99- | SPL_1_0 -- ^ @SPL-1.0@, Sun Public License v1.0- | SSH_OpenSSH -- ^ @SSH-OpenSSH@, SSH OpenSSH license, SPDX License List 3.9, SPDX License List 3.10- | SSH_short -- ^ @SSH-short@, SSH short notice, SPDX License List 3.9, SPDX License List 3.10- | SSPL_1_0 -- ^ @SSPL-1.0@, Server Side Public License, v 1, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | SugarCRM_1_1_3 -- ^ @SugarCRM-1.1.3@, SugarCRM Public License v1.1.3- | SWL -- ^ @SWL@, Scheme Widget Library (SWL) Software License Agreement- | TAPR_OHL_1_0 -- ^ @TAPR-OHL-1.0@, TAPR Open Hardware License v1.0, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | TCL -- ^ @TCL@, TCL/TK License- | TCP_wrappers -- ^ @TCP-wrappers@, TCP Wrappers License- | TMate -- ^ @TMate@, TMate Open Source License- | TORQUE_1_1 -- ^ @TORQUE-1.1@, TORQUE v2.5+ Software License v1.1- | TOSL -- ^ @TOSL@, Trusster Open Source License- | TU_Berlin_1_0 -- ^ @TU-Berlin-1.0@, Technische Universitaet Berlin License 1.0, SPDX License List 3.2, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | TU_Berlin_2_0 -- ^ @TU-Berlin-2.0@, Technische Universitaet Berlin License 2.0, SPDX License List 3.2, SPDX License List 3.6, SPDX License List 3.9, SPDX License List 3.10- | UCL_1_0 -- ^ @UCL-1.0@, Upstream Compatibility License v1.0, SPDX License List 3.9, SPDX License List 3.10- | Unicode_DFS_2015 -- ^ @Unicode-DFS-2015@, Unicode License Agreement - Data Files and Software (2015)- | Unicode_DFS_2016 -- ^ @Unicode-DFS-2016@, Unicode License Agreement - Data Files and Software (2016)- | Unicode_TOU -- ^ @Unicode-TOU@, Unicode Terms of Use- | Unlicense -- ^ @Unlicense@, The Unlicense- | UPL_1_0 -- ^ @UPL-1.0@, Universal Permissive License v1.0- | Vim -- ^ @Vim@, Vim License- | VOSTROM -- ^ @VOSTROM@, VOSTROM Public License for Open Source- | VSL_1_0 -- ^ @VSL-1.0@, Vovida Software License v1.0- | W3C_19980720 -- ^ @W3C-19980720@, W3C Software Notice and License (1998-07-20)- | W3C_20150513 -- ^ @W3C-20150513@, W3C Software Notice and Document License (2015-05-13)- | W3C -- ^ @W3C@, W3C Software Notice and License (2002-12-31)- | Watcom_1_0 -- ^ @Watcom-1.0@, Sybase Open Watcom Public License 1.0- | Wsuipa -- ^ @Wsuipa@, Wsuipa License- | WTFPL -- ^ @WTFPL@, Do What The F*ck You Want To Public License- | X11 -- ^ @X11@, X11 License- | Xerox -- ^ @Xerox@, Xerox License- | XFree86_1_1 -- ^ @XFree86-1.1@, XFree86 License 1.1- | Xinetd -- ^ @xinetd@, xinetd License- | Xnet -- ^ @Xnet@, X.Net License- | Xpp -- ^ @xpp@, XPP License- | XSkat -- ^ @XSkat@, XSkat License- | YPL_1_0 -- ^ @YPL-1.0@, Yahoo! Public License v1.0- | YPL_1_1 -- ^ @YPL-1.1@, Yahoo! Public License v1.1- | Zed -- ^ @Zed@, Zed License- | Zend_2_0 -- ^ @Zend-2.0@, Zend License v2.0- | Zimbra_1_3 -- ^ @Zimbra-1.3@, Zimbra Public License v1.3- | Zimbra_1_4 -- ^ @Zimbra-1.4@, Zimbra Public License v1.4- | Zlib_acknowledgement -- ^ @zlib-acknowledgement@, zlib/libpng License with Acknowledgement- | Zlib -- ^ @Zlib@, zlib License- | ZPL_1_1 -- ^ @ZPL-1.1@, Zope Public License 1.1- | ZPL_2_0 -- ^ @ZPL-2.0@, Zope Public License 2.0- | ZPL_2_1 -- ^ @ZPL-2.1@, Zope Public License 2.1- deriving (Eq, Ord, Enum, Bounded, Show, Read, Typeable, Data, Generic)--instance Binary LicenseId where- -- Word16 is encoded in big endianess- -- https://github.com/kolmodin/binary/blob/master/src/Data/Binary/Class.hs#L220-LL227- put = Binary.putWord16be . fromIntegral . fromEnum- get = do- i <- Binary.getWord16be- if i > fromIntegral (fromEnum (maxBound :: LicenseId))- then fail "Too large LicenseId tag"- else return (toEnum (fromIntegral i))---- note: remember to bump version each time the definition changes-instance Structured LicenseId where- structure p = set typeVersion 306 $ nominalStructure p--instance Pretty LicenseId where- pretty = Disp.text . licenseId---- |--- >>> eitherParsec "BSD-3-Clause" :: Either String LicenseId--- Right BSD_3_Clause------ >>> eitherParsec "BSD3" :: Either String LicenseId--- Left "...Unknown SPDX license identifier: 'BSD3' Do you mean BSD-3-Clause?"----instance Parsec LicenseId where- parsec = do- n <- some $ P.satisfy $ \c -> isAsciiAlphaNum c || c == '-' || c == '.'- v <- askCabalSpecVersion- maybe (fail $ "Unknown SPDX license identifier: '" ++ n ++ "' " ++ licenseIdMigrationMessage n) return $- mkLicenseId (cabalSpecVersionToSPDXListVersion v) n--instance NFData LicenseId where- rnf l = l `seq` ()---- | Help message for migrating from non-SPDX license identifiers.------ Old 'License' is almost SPDX, except for 'BSD2', 'BSD3'. This function--- suggests SPDX variant:------ >>> licenseIdMigrationMessage "BSD3"--- "Do you mean BSD-3-Clause?"------ Also 'OtherLicense', 'AllRightsReserved', and 'PublicDomain' aren't--- valid SPDX identifiers------ >>> traverse_ (print . licenseIdMigrationMessage) [ "OtherLicense", "AllRightsReserved", "PublicDomain" ]--- "SPDX license list contains plenty of licenses. See https://spdx.org/licenses/. Also they can be combined into complex expressions with AND and OR."--- "You can use NONE as a value of license field."--- "Public Domain is a complex matter. See https://wiki.spdx.org/view/Legal_Team/Decisions/Dealing_with_Public_Domain_within_SPDX_Files. Consider using a proper license."------ SPDX License list version 3.0 introduced "-only" and "-or-later" variants for GNU family of licenses.--- See <https://spdx.org/news/news/2018/01/license-list-30-released>--- >>> licenseIdMigrationMessage "GPL-2.0"--- "SPDX license list 3.0 deprecated suffixless variants of GNU family of licenses. Use GPL-2.0-only or GPL-2.0-or-later."------ For other common licenses their old license format coincides with the SPDX identifiers:------ >>> traverse eitherParsec ["GPL-2.0-only", "GPL-3.0-only", "LGPL-2.1-only", "MIT", "ISC", "MPL-2.0", "Apache-2.0"] :: Either String [LicenseId]--- Right [GPL_2_0_only,GPL_3_0_only,LGPL_2_1_only,MIT,ISC,MPL_2_0,Apache_2_0]----licenseIdMigrationMessage :: String -> String-licenseIdMigrationMessage = go where- go l | gnuVariant l = "SPDX license list 3.0 deprecated suffixless variants of GNU family of licenses. Use " ++ l ++ "-only or " ++ l ++ "-or-later."- go "BSD3" = "Do you mean BSD-3-Clause?"- go "BSD2" = "Do you mean BSD-2-Clause?"- go "AllRightsReserved" = "You can use NONE as a value of license field."- go "OtherLicense" = "SPDX license list contains plenty of licenses. See https://spdx.org/licenses/. Also they can be combined into complex expressions with AND and OR."- go "PublicDomain" = "Public Domain is a complex matter. See https://wiki.spdx.org/view/Legal_Team/Decisions/Dealing_with_Public_Domain_within_SPDX_Files. Consider using a proper license."-- -- otherwise, we don't know- go _ = ""-- gnuVariant = flip elem ["GPL-2.0", "GPL-3.0", "LGPL-2.1", "LGPL-3.0", "AGPL-3.0" ]------------------------------------------------------------------------------------ License Data------------------------------------------------------------------------------------ | License SPDX identifier, e.g. @"BSD-3-Clause"@.-licenseId :: LicenseId -> String-licenseId NullBSD = "0BSD"-licenseId AAL = "AAL"-licenseId Abstyles = "Abstyles"-licenseId Adobe_2006 = "Adobe-2006"-licenseId Adobe_Glyph = "Adobe-Glyph"-licenseId ADSL = "ADSL"-licenseId AFL_1_1 = "AFL-1.1"-licenseId AFL_1_2 = "AFL-1.2"-licenseId AFL_2_0 = "AFL-2.0"-licenseId AFL_2_1 = "AFL-2.1"-licenseId AFL_3_0 = "AFL-3.0"-licenseId Afmparse = "Afmparse"-licenseId AGPL_1_0 = "AGPL-1.0"-licenseId AGPL_1_0_only = "AGPL-1.0-only"-licenseId AGPL_1_0_or_later = "AGPL-1.0-or-later"-licenseId AGPL_3_0_only = "AGPL-3.0-only"-licenseId AGPL_3_0_or_later = "AGPL-3.0-or-later"-licenseId Aladdin = "Aladdin"-licenseId AMDPLPA = "AMDPLPA"-licenseId AML = "AML"-licenseId AMPAS = "AMPAS"-licenseId ANTLR_PD = "ANTLR-PD"-licenseId Apache_1_0 = "Apache-1.0"-licenseId Apache_1_1 = "Apache-1.1"-licenseId Apache_2_0 = "Apache-2.0"-licenseId APAFML = "APAFML"-licenseId APL_1_0 = "APL-1.0"-licenseId APSL_1_0 = "APSL-1.0"-licenseId APSL_1_1 = "APSL-1.1"-licenseId APSL_1_2 = "APSL-1.2"-licenseId APSL_2_0 = "APSL-2.0"-licenseId Artistic_1_0_cl8 = "Artistic-1.0-cl8"-licenseId Artistic_1_0_Perl = "Artistic-1.0-Perl"-licenseId Artistic_1_0 = "Artistic-1.0"-licenseId Artistic_2_0 = "Artistic-2.0"-licenseId Bahyph = "Bahyph"-licenseId Barr = "Barr"-licenseId Beerware = "Beerware"-licenseId BitTorrent_1_0 = "BitTorrent-1.0"-licenseId BitTorrent_1_1 = "BitTorrent-1.1"-licenseId Blessing = "blessing"-licenseId BlueOak_1_0_0 = "BlueOak-1.0.0"-licenseId Borceux = "Borceux"-licenseId BSD_1_Clause = "BSD-1-Clause"-licenseId BSD_2_Clause_FreeBSD = "BSD-2-Clause-FreeBSD"-licenseId BSD_2_Clause_NetBSD = "BSD-2-Clause-NetBSD"-licenseId BSD_2_Clause_Patent = "BSD-2-Clause-Patent"-licenseId BSD_2_Clause_Views = "BSD-2-Clause-Views"-licenseId BSD_2_Clause = "BSD-2-Clause"-licenseId BSD_3_Clause_Attribution = "BSD-3-Clause-Attribution"-licenseId BSD_3_Clause_Clear = "BSD-3-Clause-Clear"-licenseId BSD_3_Clause_LBNL = "BSD-3-Clause-LBNL"-licenseId BSD_3_Clause_No_Nuclear_License_2014 = "BSD-3-Clause-No-Nuclear-License-2014"-licenseId BSD_3_Clause_No_Nuclear_License = "BSD-3-Clause-No-Nuclear-License"-licenseId BSD_3_Clause_No_Nuclear_Warranty = "BSD-3-Clause-No-Nuclear-Warranty"-licenseId BSD_3_Clause_Open_MPI = "BSD-3-Clause-Open-MPI"-licenseId BSD_3_Clause = "BSD-3-Clause"-licenseId BSD_4_Clause_UC = "BSD-4-Clause-UC"-licenseId BSD_4_Clause = "BSD-4-Clause"-licenseId BSD_Protection = "BSD-Protection"-licenseId BSD_Source_Code = "BSD-Source-Code"-licenseId BSL_1_0 = "BSL-1.0"-licenseId Bzip2_1_0_5 = "bzip2-1.0.5"-licenseId Bzip2_1_0_6 = "bzip2-1.0.6"-licenseId CAL_1_0_Combined_Work_Exception = "CAL-1.0-Combined-Work-Exception"-licenseId CAL_1_0 = "CAL-1.0"-licenseId Caldera = "Caldera"-licenseId CATOSL_1_1 = "CATOSL-1.1"-licenseId CC_BY_1_0 = "CC-BY-1.0"-licenseId CC_BY_2_0 = "CC-BY-2.0"-licenseId CC_BY_2_5 = "CC-BY-2.5"-licenseId CC_BY_3_0_AT = "CC-BY-3.0-AT"-licenseId CC_BY_3_0 = "CC-BY-3.0"-licenseId CC_BY_4_0 = "CC-BY-4.0"-licenseId CC_BY_NC_1_0 = "CC-BY-NC-1.0"-licenseId CC_BY_NC_2_0 = "CC-BY-NC-2.0"-licenseId CC_BY_NC_2_5 = "CC-BY-NC-2.5"-licenseId CC_BY_NC_3_0 = "CC-BY-NC-3.0"-licenseId CC_BY_NC_4_0 = "CC-BY-NC-4.0"-licenseId CC_BY_NC_ND_1_0 = "CC-BY-NC-ND-1.0"-licenseId CC_BY_NC_ND_2_0 = "CC-BY-NC-ND-2.0"-licenseId CC_BY_NC_ND_2_5 = "CC-BY-NC-ND-2.5"-licenseId CC_BY_NC_ND_3_0_IGO = "CC-BY-NC-ND-3.0-IGO"-licenseId CC_BY_NC_ND_3_0 = "CC-BY-NC-ND-3.0"-licenseId CC_BY_NC_ND_4_0 = "CC-BY-NC-ND-4.0"-licenseId CC_BY_NC_SA_1_0 = "CC-BY-NC-SA-1.0"-licenseId CC_BY_NC_SA_2_0 = "CC-BY-NC-SA-2.0"-licenseId CC_BY_NC_SA_2_5 = "CC-BY-NC-SA-2.5"-licenseId CC_BY_NC_SA_3_0 = "CC-BY-NC-SA-3.0"-licenseId CC_BY_NC_SA_4_0 = "CC-BY-NC-SA-4.0"-licenseId CC_BY_ND_1_0 = "CC-BY-ND-1.0"-licenseId CC_BY_ND_2_0 = "CC-BY-ND-2.0"-licenseId CC_BY_ND_2_5 = "CC-BY-ND-2.5"-licenseId CC_BY_ND_3_0 = "CC-BY-ND-3.0"-licenseId CC_BY_ND_4_0 = "CC-BY-ND-4.0"-licenseId CC_BY_SA_1_0 = "CC-BY-SA-1.0"-licenseId CC_BY_SA_2_0 = "CC-BY-SA-2.0"-licenseId CC_BY_SA_2_5 = "CC-BY-SA-2.5"-licenseId CC_BY_SA_3_0_AT = "CC-BY-SA-3.0-AT"-licenseId CC_BY_SA_3_0 = "CC-BY-SA-3.0"-licenseId CC_BY_SA_4_0 = "CC-BY-SA-4.0"-licenseId CC_PDDC = "CC-PDDC"-licenseId CC0_1_0 = "CC0-1.0"-licenseId CDDL_1_0 = "CDDL-1.0"-licenseId CDDL_1_1 = "CDDL-1.1"-licenseId CDLA_Permissive_1_0 = "CDLA-Permissive-1.0"-licenseId CDLA_Sharing_1_0 = "CDLA-Sharing-1.0"-licenseId CECILL_1_0 = "CECILL-1.0"-licenseId CECILL_1_1 = "CECILL-1.1"-licenseId CECILL_2_0 = "CECILL-2.0"-licenseId CECILL_2_1 = "CECILL-2.1"-licenseId CECILL_B = "CECILL-B"-licenseId CECILL_C = "CECILL-C"-licenseId CERN_OHL_1_1 = "CERN-OHL-1.1"-licenseId CERN_OHL_1_2 = "CERN-OHL-1.2"-licenseId CERN_OHL_P_2_0 = "CERN-OHL-P-2.0"-licenseId CERN_OHL_S_2_0 = "CERN-OHL-S-2.0"-licenseId CERN_OHL_W_2_0 = "CERN-OHL-W-2.0"-licenseId ClArtistic = "ClArtistic"-licenseId CNRI_Jython = "CNRI-Jython"-licenseId CNRI_Python_GPL_Compatible = "CNRI-Python-GPL-Compatible"-licenseId CNRI_Python = "CNRI-Python"-licenseId Condor_1_1 = "Condor-1.1"-licenseId Copyleft_next_0_3_0 = "copyleft-next-0.3.0"-licenseId Copyleft_next_0_3_1 = "copyleft-next-0.3.1"-licenseId CPAL_1_0 = "CPAL-1.0"-licenseId CPL_1_0 = "CPL-1.0"-licenseId CPOL_1_02 = "CPOL-1.02"-licenseId Crossword = "Crossword"-licenseId CrystalStacker = "CrystalStacker"-licenseId CUA_OPL_1_0 = "CUA-OPL-1.0"-licenseId Cube = "Cube"-licenseId Curl = "curl"-licenseId D_FSL_1_0 = "D-FSL-1.0"-licenseId Diffmark = "diffmark"-licenseId DOC = "DOC"-licenseId Dotseqn = "Dotseqn"-licenseId DSDP = "DSDP"-licenseId Dvipdfm = "dvipdfm"-licenseId ECL_1_0 = "ECL-1.0"-licenseId ECL_2_0 = "ECL-2.0"-licenseId EFL_1_0 = "EFL-1.0"-licenseId EFL_2_0 = "EFL-2.0"-licenseId EGenix = "eGenix"-licenseId Entessa = "Entessa"-licenseId EPICS = "EPICS"-licenseId EPL_1_0 = "EPL-1.0"-licenseId EPL_2_0 = "EPL-2.0"-licenseId ErlPL_1_1 = "ErlPL-1.1"-licenseId Etalab_2_0 = "etalab-2.0"-licenseId EUDatagrid = "EUDatagrid"-licenseId EUPL_1_0 = "EUPL-1.0"-licenseId EUPL_1_1 = "EUPL-1.1"-licenseId EUPL_1_2 = "EUPL-1.2"-licenseId Eurosym = "Eurosym"-licenseId Fair = "Fair"-licenseId Frameworx_1_0 = "Frameworx-1.0"-licenseId FreeImage = "FreeImage"-licenseId FSFAP = "FSFAP"-licenseId FSFULLR = "FSFULLR"-licenseId FSFUL = "FSFUL"-licenseId FTL = "FTL"-licenseId GFDL_1_1_invariants_only = "GFDL-1.1-invariants-only"-licenseId GFDL_1_1_invariants_or_later = "GFDL-1.1-invariants-or-later"-licenseId GFDL_1_1_no_invariants_only = "GFDL-1.1-no-invariants-only"-licenseId GFDL_1_1_no_invariants_or_later = "GFDL-1.1-no-invariants-or-later"-licenseId GFDL_1_1_only = "GFDL-1.1-only"-licenseId GFDL_1_1_or_later = "GFDL-1.1-or-later"-licenseId GFDL_1_2_invariants_only = "GFDL-1.2-invariants-only"-licenseId GFDL_1_2_invariants_or_later = "GFDL-1.2-invariants-or-later"-licenseId GFDL_1_2_no_invariants_only = "GFDL-1.2-no-invariants-only"-licenseId GFDL_1_2_no_invariants_or_later = "GFDL-1.2-no-invariants-or-later"-licenseId GFDL_1_2_only = "GFDL-1.2-only"-licenseId GFDL_1_2_or_later = "GFDL-1.2-or-later"-licenseId GFDL_1_3_invariants_only = "GFDL-1.3-invariants-only"-licenseId GFDL_1_3_invariants_or_later = "GFDL-1.3-invariants-or-later"-licenseId GFDL_1_3_no_invariants_only = "GFDL-1.3-no-invariants-only"-licenseId GFDL_1_3_no_invariants_or_later = "GFDL-1.3-no-invariants-or-later"-licenseId GFDL_1_3_only = "GFDL-1.3-only"-licenseId GFDL_1_3_or_later = "GFDL-1.3-or-later"-licenseId Giftware = "Giftware"-licenseId GL2PS = "GL2PS"-licenseId Glide = "Glide"-licenseId Glulxe = "Glulxe"-licenseId GLWTPL = "GLWTPL"-licenseId Gnuplot = "gnuplot"-licenseId GPL_1_0_only = "GPL-1.0-only"-licenseId GPL_1_0_or_later = "GPL-1.0-or-later"-licenseId GPL_2_0_only = "GPL-2.0-only"-licenseId GPL_2_0_or_later = "GPL-2.0-or-later"-licenseId GPL_3_0_only = "GPL-3.0-only"-licenseId GPL_3_0_or_later = "GPL-3.0-or-later"-licenseId GSOAP_1_3b = "gSOAP-1.3b"-licenseId HaskellReport = "HaskellReport"-licenseId Hippocratic_2_1 = "Hippocratic-2.1"-licenseId HPND_sell_variant = "HPND-sell-variant"-licenseId HPND = "HPND"-licenseId IBM_pibs = "IBM-pibs"-licenseId ICU = "ICU"-licenseId IJG = "IJG"-licenseId ImageMagick = "ImageMagick"-licenseId IMatix = "iMatix"-licenseId Imlib2 = "Imlib2"-licenseId Info_ZIP = "Info-ZIP"-licenseId Intel_ACPI = "Intel-ACPI"-licenseId Intel = "Intel"-licenseId Interbase_1_0 = "Interbase-1.0"-licenseId IPA = "IPA"-licenseId IPL_1_0 = "IPL-1.0"-licenseId ISC = "ISC"-licenseId JasPer_2_0 = "JasPer-2.0"-licenseId JPNIC = "JPNIC"-licenseId JSON = "JSON"-licenseId LAL_1_2 = "LAL-1.2"-licenseId LAL_1_3 = "LAL-1.3"-licenseId Latex2e = "Latex2e"-licenseId Leptonica = "Leptonica"-licenseId LGPL_2_0_only = "LGPL-2.0-only"-licenseId LGPL_2_0_or_later = "LGPL-2.0-or-later"-licenseId LGPL_2_1_only = "LGPL-2.1-only"-licenseId LGPL_2_1_or_later = "LGPL-2.1-or-later"-licenseId LGPL_3_0_only = "LGPL-3.0-only"-licenseId LGPL_3_0_or_later = "LGPL-3.0-or-later"-licenseId LGPLLR = "LGPLLR"-licenseId Libpng_2_0 = "libpng-2.0"-licenseId Libpng = "Libpng"-licenseId Libselinux_1_0 = "libselinux-1.0"-licenseId Libtiff = "libtiff"-licenseId LiLiQ_P_1_1 = "LiLiQ-P-1.1"-licenseId LiLiQ_R_1_1 = "LiLiQ-R-1.1"-licenseId LiLiQ_Rplus_1_1 = "LiLiQ-Rplus-1.1"-licenseId Linux_OpenIB = "Linux-OpenIB"-licenseId LPL_1_02 = "LPL-1.02"-licenseId LPL_1_0 = "LPL-1.0"-licenseId LPPL_1_0 = "LPPL-1.0"-licenseId LPPL_1_1 = "LPPL-1.1"-licenseId LPPL_1_2 = "LPPL-1.2"-licenseId LPPL_1_3a = "LPPL-1.3a"-licenseId LPPL_1_3c = "LPPL-1.3c"-licenseId MakeIndex = "MakeIndex"-licenseId MirOS = "MirOS"-licenseId MIT_0 = "MIT-0"-licenseId MIT_advertising = "MIT-advertising"-licenseId MIT_CMU = "MIT-CMU"-licenseId MIT_enna = "MIT-enna"-licenseId MIT_feh = "MIT-feh"-licenseId MITNFA = "MITNFA"-licenseId MIT = "MIT"-licenseId Motosoto = "Motosoto"-licenseId Mpich2 = "mpich2"-licenseId MPL_1_0 = "MPL-1.0"-licenseId MPL_1_1 = "MPL-1.1"-licenseId MPL_2_0_no_copyleft_exception = "MPL-2.0-no-copyleft-exception"-licenseId MPL_2_0 = "MPL-2.0"-licenseId MS_PL = "MS-PL"-licenseId MS_RL = "MS-RL"-licenseId MTLL = "MTLL"-licenseId MulanPSL_1_0 = "MulanPSL-1.0"-licenseId MulanPSL_2_0 = "MulanPSL-2.0"-licenseId Multics = "Multics"-licenseId Mup = "Mup"-licenseId NASA_1_3 = "NASA-1.3"-licenseId Naumen = "Naumen"-licenseId NBPL_1_0 = "NBPL-1.0"-licenseId NCGL_UK_2_0 = "NCGL-UK-2.0"-licenseId NCSA = "NCSA"-licenseId Net_SNMP = "Net-SNMP"-licenseId NetCDF = "NetCDF"-licenseId Newsletr = "Newsletr"-licenseId NGPL = "NGPL"-licenseId NIST_PD_fallback = "NIST-PD-fallback"-licenseId NIST_PD = "NIST-PD"-licenseId NLOD_1_0 = "NLOD-1.0"-licenseId NLPL = "NLPL"-licenseId Nokia = "Nokia"-licenseId NOSL = "NOSL"-licenseId Noweb = "Noweb"-licenseId NPL_1_0 = "NPL-1.0"-licenseId NPL_1_1 = "NPL-1.1"-licenseId NPOSL_3_0 = "NPOSL-3.0"-licenseId NRL = "NRL"-licenseId NTP_0 = "NTP-0"-licenseId NTP = "NTP"-licenseId O_UDA_1_0 = "O-UDA-1.0"-licenseId OCCT_PL = "OCCT-PL"-licenseId OCLC_2_0 = "OCLC-2.0"-licenseId ODbL_1_0 = "ODbL-1.0"-licenseId ODC_By_1_0 = "ODC-By-1.0"-licenseId OFL_1_0_no_RFN = "OFL-1.0-no-RFN"-licenseId OFL_1_0_RFN = "OFL-1.0-RFN"-licenseId OFL_1_0 = "OFL-1.0"-licenseId OFL_1_1_no_RFN = "OFL-1.1-no-RFN"-licenseId OFL_1_1_RFN = "OFL-1.1-RFN"-licenseId OFL_1_1 = "OFL-1.1"-licenseId OGC_1_0 = "OGC-1.0"-licenseId OGL_Canada_2_0 = "OGL-Canada-2.0"-licenseId OGL_UK_1_0 = "OGL-UK-1.0"-licenseId OGL_UK_2_0 = "OGL-UK-2.0"-licenseId OGL_UK_3_0 = "OGL-UK-3.0"-licenseId OGTSL = "OGTSL"-licenseId OLDAP_1_1 = "OLDAP-1.1"-licenseId OLDAP_1_2 = "OLDAP-1.2"-licenseId OLDAP_1_3 = "OLDAP-1.3"-licenseId OLDAP_1_4 = "OLDAP-1.4"-licenseId OLDAP_2_0_1 = "OLDAP-2.0.1"-licenseId OLDAP_2_0 = "OLDAP-2.0"-licenseId OLDAP_2_1 = "OLDAP-2.1"-licenseId OLDAP_2_2_1 = "OLDAP-2.2.1"-licenseId OLDAP_2_2_2 = "OLDAP-2.2.2"-licenseId OLDAP_2_2 = "OLDAP-2.2"-licenseId OLDAP_2_3 = "OLDAP-2.3"-licenseId OLDAP_2_4 = "OLDAP-2.4"-licenseId OLDAP_2_5 = "OLDAP-2.5"-licenseId OLDAP_2_6 = "OLDAP-2.6"-licenseId OLDAP_2_7 = "OLDAP-2.7"-licenseId OLDAP_2_8 = "OLDAP-2.8"-licenseId OML = "OML"-licenseId OpenSSL = "OpenSSL"-licenseId OPL_1_0 = "OPL-1.0"-licenseId OSET_PL_2_1 = "OSET-PL-2.1"-licenseId OSL_1_0 = "OSL-1.0"-licenseId OSL_1_1 = "OSL-1.1"-licenseId OSL_2_0 = "OSL-2.0"-licenseId OSL_2_1 = "OSL-2.1"-licenseId OSL_3_0 = "OSL-3.0"-licenseId Parity_6_0_0 = "Parity-6.0.0"-licenseId Parity_7_0_0 = "Parity-7.0.0"-licenseId PDDL_1_0 = "PDDL-1.0"-licenseId PHP_3_01 = "PHP-3.01"-licenseId PHP_3_0 = "PHP-3.0"-licenseId Plexus = "Plexus"-licenseId PolyForm_Noncommercial_1_0_0 = "PolyForm-Noncommercial-1.0.0"-licenseId PolyForm_Small_Business_1_0_0 = "PolyForm-Small-Business-1.0.0"-licenseId PostgreSQL = "PostgreSQL"-licenseId PSF_2_0 = "PSF-2.0"-licenseId Psfrag = "psfrag"-licenseId Psutils = "psutils"-licenseId Python_2_0 = "Python-2.0"-licenseId Qhull = "Qhull"-licenseId QPL_1_0 = "QPL-1.0"-licenseId Rdisc = "Rdisc"-licenseId RHeCos_1_1 = "RHeCos-1.1"-licenseId RPL_1_1 = "RPL-1.1"-licenseId RPL_1_5 = "RPL-1.5"-licenseId RPSL_1_0 = "RPSL-1.0"-licenseId RSA_MD = "RSA-MD"-licenseId RSCPL = "RSCPL"-licenseId Ruby = "Ruby"-licenseId SAX_PD = "SAX-PD"-licenseId Saxpath = "Saxpath"-licenseId SCEA = "SCEA"-licenseId Sendmail_8_23 = "Sendmail-8.23"-licenseId Sendmail = "Sendmail"-licenseId SGI_B_1_0 = "SGI-B-1.0"-licenseId SGI_B_1_1 = "SGI-B-1.1"-licenseId SGI_B_2_0 = "SGI-B-2.0"-licenseId SHL_0_51 = "SHL-0.51"-licenseId SHL_0_5 = "SHL-0.5"-licenseId SimPL_2_0 = "SimPL-2.0"-licenseId SISSL_1_2 = "SISSL-1.2"-licenseId SISSL = "SISSL"-licenseId Sleepycat = "Sleepycat"-licenseId SMLNJ = "SMLNJ"-licenseId SMPPL = "SMPPL"-licenseId SNIA = "SNIA"-licenseId Spencer_86 = "Spencer-86"-licenseId Spencer_94 = "Spencer-94"-licenseId Spencer_99 = "Spencer-99"-licenseId SPL_1_0 = "SPL-1.0"-licenseId SSH_OpenSSH = "SSH-OpenSSH"-licenseId SSH_short = "SSH-short"-licenseId SSPL_1_0 = "SSPL-1.0"-licenseId SugarCRM_1_1_3 = "SugarCRM-1.1.3"-licenseId SWL = "SWL"-licenseId TAPR_OHL_1_0 = "TAPR-OHL-1.0"-licenseId TCL = "TCL"-licenseId TCP_wrappers = "TCP-wrappers"-licenseId TMate = "TMate"-licenseId TORQUE_1_1 = "TORQUE-1.1"-licenseId TOSL = "TOSL"-licenseId TU_Berlin_1_0 = "TU-Berlin-1.0"-licenseId TU_Berlin_2_0 = "TU-Berlin-2.0"-licenseId UCL_1_0 = "UCL-1.0"-licenseId Unicode_DFS_2015 = "Unicode-DFS-2015"-licenseId Unicode_DFS_2016 = "Unicode-DFS-2016"-licenseId Unicode_TOU = "Unicode-TOU"-licenseId Unlicense = "Unlicense"-licenseId UPL_1_0 = "UPL-1.0"-licenseId Vim = "Vim"-licenseId VOSTROM = "VOSTROM"-licenseId VSL_1_0 = "VSL-1.0"-licenseId W3C_19980720 = "W3C-19980720"-licenseId W3C_20150513 = "W3C-20150513"-licenseId W3C = "W3C"-licenseId Watcom_1_0 = "Watcom-1.0"-licenseId Wsuipa = "Wsuipa"-licenseId WTFPL = "WTFPL"-licenseId X11 = "X11"-licenseId Xerox = "Xerox"-licenseId XFree86_1_1 = "XFree86-1.1"-licenseId Xinetd = "xinetd"-licenseId Xnet = "Xnet"-licenseId Xpp = "xpp"-licenseId XSkat = "XSkat"-licenseId YPL_1_0 = "YPL-1.0"-licenseId YPL_1_1 = "YPL-1.1"-licenseId Zed = "Zed"-licenseId Zend_2_0 = "Zend-2.0"-licenseId Zimbra_1_3 = "Zimbra-1.3"-licenseId Zimbra_1_4 = "Zimbra-1.4"-licenseId Zlib_acknowledgement = "zlib-acknowledgement"-licenseId Zlib = "Zlib"-licenseId ZPL_1_1 = "ZPL-1.1"-licenseId ZPL_2_0 = "ZPL-2.0"-licenseId ZPL_2_1 = "ZPL-2.1"---- | License name, e.g. @"GNU General Public License v2.0 only"@-licenseName :: LicenseId -> String-licenseName NullBSD = "BSD Zero Clause License"-licenseName AAL = "Attribution Assurance License"-licenseName Abstyles = "Abstyles License"-licenseName Adobe_2006 = "Adobe Systems Incorporated Source Code License Agreement"-licenseName Adobe_Glyph = "Adobe Glyph List License"-licenseName ADSL = "Amazon Digital Services License"-licenseName AFL_1_1 = "Academic Free License v1.1"-licenseName AFL_1_2 = "Academic Free License v1.2"-licenseName AFL_2_0 = "Academic Free License v2.0"-licenseName AFL_2_1 = "Academic Free License v2.1"-licenseName AFL_3_0 = "Academic Free License v3.0"-licenseName Afmparse = "Afmparse License"-licenseName AGPL_1_0 = "Affero General Public License v1.0"-licenseName AGPL_1_0_only = "Affero General Public License v1.0 only"-licenseName AGPL_1_0_or_later = "Affero General Public License v1.0 or later"-licenseName AGPL_3_0_only = "GNU Affero General Public License v3.0 only"-licenseName AGPL_3_0_or_later = "GNU Affero General Public License v3.0 or later"-licenseName Aladdin = "Aladdin Free Public License"-licenseName AMDPLPA = "AMD's plpa_map.c License"-licenseName AML = "Apple MIT License"-licenseName AMPAS = "Academy of Motion Picture Arts and Sciences BSD"-licenseName ANTLR_PD = "ANTLR Software Rights Notice"-licenseName Apache_1_0 = "Apache License 1.0"-licenseName Apache_1_1 = "Apache License 1.1"-licenseName Apache_2_0 = "Apache License 2.0"-licenseName APAFML = "Adobe Postscript AFM License"-licenseName APL_1_0 = "Adaptive Public License 1.0"-licenseName APSL_1_0 = "Apple Public Source License 1.0"-licenseName APSL_1_1 = "Apple Public Source License 1.1"-licenseName APSL_1_2 = "Apple Public Source License 1.2"-licenseName APSL_2_0 = "Apple Public Source License 2.0"-licenseName Artistic_1_0_cl8 = "Artistic License 1.0 w/clause 8"-licenseName Artistic_1_0_Perl = "Artistic License 1.0 (Perl)"-licenseName Artistic_1_0 = "Artistic License 1.0"-licenseName Artistic_2_0 = "Artistic License 2.0"-licenseName Bahyph = "Bahyph License"-licenseName Barr = "Barr License"-licenseName Beerware = "Beerware License"-licenseName BitTorrent_1_0 = "BitTorrent Open Source License v1.0"-licenseName BitTorrent_1_1 = "BitTorrent Open Source License v1.1"-licenseName Blessing = "SQLite Blessing"-licenseName BlueOak_1_0_0 = "Blue Oak Model License 1.0.0"-licenseName Borceux = "Borceux license"-licenseName BSD_1_Clause = "BSD 1-Clause License"-licenseName BSD_2_Clause_FreeBSD = "BSD 2-Clause FreeBSD License"-licenseName BSD_2_Clause_NetBSD = "BSD 2-Clause NetBSD License"-licenseName BSD_2_Clause_Patent = "BSD-2-Clause Plus Patent License"-licenseName BSD_2_Clause_Views = "BSD 2-Clause with views sentence"-licenseName BSD_2_Clause = "BSD 2-Clause \"Simplified\" License"-licenseName BSD_3_Clause_Attribution = "BSD with attribution"-licenseName BSD_3_Clause_Clear = "BSD 3-Clause Clear License"-licenseName BSD_3_Clause_LBNL = "Lawrence Berkeley National Labs BSD variant license"-licenseName BSD_3_Clause_No_Nuclear_License_2014 = "BSD 3-Clause No Nuclear License 2014"-licenseName BSD_3_Clause_No_Nuclear_License = "BSD 3-Clause No Nuclear License"-licenseName BSD_3_Clause_No_Nuclear_Warranty = "BSD 3-Clause No Nuclear Warranty"-licenseName BSD_3_Clause_Open_MPI = "BSD 3-Clause Open MPI variant"-licenseName BSD_3_Clause = "BSD 3-Clause \"New\" or \"Revised\" License"-licenseName BSD_4_Clause_UC = "BSD-4-Clause (University of California-Specific)"-licenseName BSD_4_Clause = "BSD 4-Clause \"Original\" or \"Old\" License"-licenseName BSD_Protection = "BSD Protection License"-licenseName BSD_Source_Code = "BSD Source Code Attribution"-licenseName BSL_1_0 = "Boost Software License 1.0"-licenseName Bzip2_1_0_5 = "bzip2 and libbzip2 License v1.0.5"-licenseName Bzip2_1_0_6 = "bzip2 and libbzip2 License v1.0.6"-licenseName CAL_1_0_Combined_Work_Exception = "Cryptographic Autonomy License 1.0 (Combined Work Exception)"-licenseName CAL_1_0 = "Cryptographic Autonomy License 1.0"-licenseName Caldera = "Caldera License"-licenseName CATOSL_1_1 = "Computer Associates Trusted Open Source License 1.1"-licenseName CC_BY_1_0 = "Creative Commons Attribution 1.0 Generic"-licenseName CC_BY_2_0 = "Creative Commons Attribution 2.0 Generic"-licenseName CC_BY_2_5 = "Creative Commons Attribution 2.5 Generic"-licenseName CC_BY_3_0_AT = "Creative Commons Attribution 3.0 Austria"-licenseName CC_BY_3_0 = "Creative Commons Attribution 3.0 Unported"-licenseName CC_BY_4_0 = "Creative Commons Attribution 4.0 International"-licenseName CC_BY_NC_1_0 = "Creative Commons Attribution Non Commercial 1.0 Generic"-licenseName CC_BY_NC_2_0 = "Creative Commons Attribution Non Commercial 2.0 Generic"-licenseName CC_BY_NC_2_5 = "Creative Commons Attribution Non Commercial 2.5 Generic"-licenseName CC_BY_NC_3_0 = "Creative Commons Attribution Non Commercial 3.0 Unported"-licenseName CC_BY_NC_4_0 = "Creative Commons Attribution Non Commercial 4.0 International"-licenseName CC_BY_NC_ND_1_0 = "Creative Commons Attribution Non Commercial No Derivatives 1.0 Generic"-licenseName CC_BY_NC_ND_2_0 = "Creative Commons Attribution Non Commercial No Derivatives 2.0 Generic"-licenseName CC_BY_NC_ND_2_5 = "Creative Commons Attribution Non Commercial No Derivatives 2.5 Generic"-licenseName CC_BY_NC_ND_3_0_IGO = "Creative Commons Attribution Non Commercial No Derivatives 3.0 IGO"-licenseName CC_BY_NC_ND_3_0 = "Creative Commons Attribution Non Commercial No Derivatives 3.0 Unported"-licenseName CC_BY_NC_ND_4_0 = "Creative Commons Attribution Non Commercial No Derivatives 4.0 International"-licenseName CC_BY_NC_SA_1_0 = "Creative Commons Attribution Non Commercial Share Alike 1.0 Generic"-licenseName CC_BY_NC_SA_2_0 = "Creative Commons Attribution Non Commercial Share Alike 2.0 Generic"-licenseName CC_BY_NC_SA_2_5 = "Creative Commons Attribution Non Commercial Share Alike 2.5 Generic"-licenseName CC_BY_NC_SA_3_0 = "Creative Commons Attribution Non Commercial Share Alike 3.0 Unported"-licenseName CC_BY_NC_SA_4_0 = "Creative Commons Attribution Non Commercial Share Alike 4.0 International"-licenseName CC_BY_ND_1_0 = "Creative Commons Attribution No Derivatives 1.0 Generic"-licenseName CC_BY_ND_2_0 = "Creative Commons Attribution No Derivatives 2.0 Generic"-licenseName CC_BY_ND_2_5 = "Creative Commons Attribution No Derivatives 2.5 Generic"-licenseName CC_BY_ND_3_0 = "Creative Commons Attribution No Derivatives 3.0 Unported"-licenseName CC_BY_ND_4_0 = "Creative Commons Attribution No Derivatives 4.0 International"-licenseName CC_BY_SA_1_0 = "Creative Commons Attribution Share Alike 1.0 Generic"-licenseName CC_BY_SA_2_0 = "Creative Commons Attribution Share Alike 2.0 Generic"-licenseName CC_BY_SA_2_5 = "Creative Commons Attribution Share Alike 2.5 Generic"-licenseName CC_BY_SA_3_0_AT = "Creative Commons Attribution-Share Alike 3.0 Austria"-licenseName CC_BY_SA_3_0 = "Creative Commons Attribution Share Alike 3.0 Unported"-licenseName CC_BY_SA_4_0 = "Creative Commons Attribution Share Alike 4.0 International"-licenseName CC_PDDC = "Creative Commons Public Domain Dedication and Certification"-licenseName CC0_1_0 = "Creative Commons Zero v1.0 Universal"-licenseName CDDL_1_0 = "Common Development and Distribution License 1.0"-licenseName CDDL_1_1 = "Common Development and Distribution License 1.1"-licenseName CDLA_Permissive_1_0 = "Community Data License Agreement Permissive 1.0"-licenseName CDLA_Sharing_1_0 = "Community Data License Agreement Sharing 1.0"-licenseName CECILL_1_0 = "CeCILL Free Software License Agreement v1.0"-licenseName CECILL_1_1 = "CeCILL Free Software License Agreement v1.1"-licenseName CECILL_2_0 = "CeCILL Free Software License Agreement v2.0"-licenseName CECILL_2_1 = "CeCILL Free Software License Agreement v2.1"-licenseName CECILL_B = "CeCILL-B Free Software License Agreement"-licenseName CECILL_C = "CeCILL-C Free Software License Agreement"-licenseName CERN_OHL_1_1 = "CERN Open Hardware Licence v1.1"-licenseName CERN_OHL_1_2 = "CERN Open Hardware Licence v1.2"-licenseName CERN_OHL_P_2_0 = "CERN Open Hardware Licence Version 2 - Permissive"-licenseName CERN_OHL_S_2_0 = "CERN Open Hardware Licence Version 2 - Strongly Reciprocal"-licenseName CERN_OHL_W_2_0 = "CERN Open Hardware Licence Version 2 - Weakly Reciprocal"-licenseName ClArtistic = "Clarified Artistic License"-licenseName CNRI_Jython = "CNRI Jython License"-licenseName CNRI_Python_GPL_Compatible = "CNRI Python Open Source GPL Compatible License Agreement"-licenseName CNRI_Python = "CNRI Python License"-licenseName Condor_1_1 = "Condor Public License v1.1"-licenseName Copyleft_next_0_3_0 = "copyleft-next 0.3.0"-licenseName Copyleft_next_0_3_1 = "copyleft-next 0.3.1"-licenseName CPAL_1_0 = "Common Public Attribution License 1.0"-licenseName CPL_1_0 = "Common Public License 1.0"-licenseName CPOL_1_02 = "Code Project Open License 1.02"-licenseName Crossword = "Crossword License"-licenseName CrystalStacker = "CrystalStacker License"-licenseName CUA_OPL_1_0 = "CUA Office Public License v1.0"-licenseName Cube = "Cube License"-licenseName Curl = "curl License"-licenseName D_FSL_1_0 = "Deutsche Freie Software Lizenz"-licenseName Diffmark = "diffmark license"-licenseName DOC = "DOC License"-licenseName Dotseqn = "Dotseqn License"-licenseName DSDP = "DSDP License"-licenseName Dvipdfm = "dvipdfm License"-licenseName ECL_1_0 = "Educational Community License v1.0"-licenseName ECL_2_0 = "Educational Community License v2.0"-licenseName EFL_1_0 = "Eiffel Forum License v1.0"-licenseName EFL_2_0 = "Eiffel Forum License v2.0"-licenseName EGenix = "eGenix.com Public License 1.1.0"-licenseName Entessa = "Entessa Public License v1.0"-licenseName EPICS = "EPICS Open License"-licenseName EPL_1_0 = "Eclipse Public License 1.0"-licenseName EPL_2_0 = "Eclipse Public License 2.0"-licenseName ErlPL_1_1 = "Erlang Public License v1.1"-licenseName Etalab_2_0 = "Etalab Open License 2.0"-licenseName EUDatagrid = "EU DataGrid Software License"-licenseName EUPL_1_0 = "European Union Public License 1.0"-licenseName EUPL_1_1 = "European Union Public License 1.1"-licenseName EUPL_1_2 = "European Union Public License 1.2"-licenseName Eurosym = "Eurosym License"-licenseName Fair = "Fair License"-licenseName Frameworx_1_0 = "Frameworx Open License 1.0"-licenseName FreeImage = "FreeImage Public License v1.0"-licenseName FSFAP = "FSF All Permissive License"-licenseName FSFULLR = "FSF Unlimited License (with License Retention)"-licenseName FSFUL = "FSF Unlimited License"-licenseName FTL = "Freetype Project License"-licenseName GFDL_1_1_invariants_only = "GNU Free Documentation License v1.1 only - invariants"-licenseName GFDL_1_1_invariants_or_later = "GNU Free Documentation License v1.1 or later - invariants"-licenseName GFDL_1_1_no_invariants_only = "GNU Free Documentation License v1.1 only - no invariants"-licenseName GFDL_1_1_no_invariants_or_later = "GNU Free Documentation License v1.1 or later - no invariants"-licenseName GFDL_1_1_only = "GNU Free Documentation License v1.1 only"-licenseName GFDL_1_1_or_later = "GNU Free Documentation License v1.1 or later"-licenseName GFDL_1_2_invariants_only = "GNU Free Documentation License v1.2 only - invariants"-licenseName GFDL_1_2_invariants_or_later = "GNU Free Documentation License v1.2 or later - invariants"-licenseName GFDL_1_2_no_invariants_only = "GNU Free Documentation License v1.2 only - no invariants"-licenseName GFDL_1_2_no_invariants_or_later = "GNU Free Documentation License v1.2 or later - no invariants"-licenseName GFDL_1_2_only = "GNU Free Documentation License v1.2 only"-licenseName GFDL_1_2_or_later = "GNU Free Documentation License v1.2 or later"-licenseName GFDL_1_3_invariants_only = "GNU Free Documentation License v1.3 only - invariants"-licenseName GFDL_1_3_invariants_or_later = "GNU Free Documentation License v1.3 or later - invariants"-licenseName GFDL_1_3_no_invariants_only = "GNU Free Documentation License v1.3 only - no invariants"-licenseName GFDL_1_3_no_invariants_or_later = "GNU Free Documentation License v1.3 or later - no invariants"-licenseName GFDL_1_3_only = "GNU Free Documentation License v1.3 only"-licenseName GFDL_1_3_or_later = "GNU Free Documentation License v1.3 or later"-licenseName Giftware = "Giftware License"-licenseName GL2PS = "GL2PS License"-licenseName Glide = "3dfx Glide License"-licenseName Glulxe = "Glulxe License"-licenseName GLWTPL = "Good Luck With That Public License"-licenseName Gnuplot = "gnuplot License"-licenseName GPL_1_0_only = "GNU General Public License v1.0 only"-licenseName GPL_1_0_or_later = "GNU General Public License v1.0 or later"-licenseName GPL_2_0_only = "GNU General Public License v2.0 only"-licenseName GPL_2_0_or_later = "GNU General Public License v2.0 or later"-licenseName GPL_3_0_only = "GNU General Public License v3.0 only"-licenseName GPL_3_0_or_later = "GNU General Public License v3.0 or later"-licenseName GSOAP_1_3b = "gSOAP Public License v1.3b"-licenseName HaskellReport = "Haskell Language Report License"-licenseName Hippocratic_2_1 = "Hippocratic License 2.1"-licenseName HPND_sell_variant = "Historical Permission Notice and Disclaimer - sell variant"-licenseName HPND = "Historical Permission Notice and Disclaimer"-licenseName IBM_pibs = "IBM PowerPC Initialization and Boot Software"-licenseName ICU = "ICU License"-licenseName IJG = "Independent JPEG Group License"-licenseName ImageMagick = "ImageMagick License"-licenseName IMatix = "iMatix Standard Function Library Agreement"-licenseName Imlib2 = "Imlib2 License"-licenseName Info_ZIP = "Info-ZIP License"-licenseName Intel_ACPI = "Intel ACPI Software License Agreement"-licenseName Intel = "Intel Open Source License"-licenseName Interbase_1_0 = "Interbase Public License v1.0"-licenseName IPA = "IPA Font License"-licenseName IPL_1_0 = "IBM Public License v1.0"-licenseName ISC = "ISC License"-licenseName JasPer_2_0 = "JasPer License"-licenseName JPNIC = "Japan Network Information Center License"-licenseName JSON = "JSON License"-licenseName LAL_1_2 = "Licence Art Libre 1.2"-licenseName LAL_1_3 = "Licence Art Libre 1.3"-licenseName Latex2e = "Latex2e License"-licenseName Leptonica = "Leptonica License"-licenseName LGPL_2_0_only = "GNU Library General Public License v2 only"-licenseName LGPL_2_0_or_later = "GNU Library General Public License v2 or later"-licenseName LGPL_2_1_only = "GNU Lesser General Public License v2.1 only"-licenseName LGPL_2_1_or_later = "GNU Lesser General Public License v2.1 or later"-licenseName LGPL_3_0_only = "GNU Lesser General Public License v3.0 only"-licenseName LGPL_3_0_or_later = "GNU Lesser General Public License v3.0 or later"-licenseName LGPLLR = "Lesser General Public License For Linguistic Resources"-licenseName Libpng_2_0 = "PNG Reference Library version 2"-licenseName Libpng = "libpng License"-licenseName Libselinux_1_0 = "libselinux public domain notice"-licenseName Libtiff = "libtiff License"-licenseName LiLiQ_P_1_1 = "Licence Libre du Qu\233bec \8211 Permissive version 1.1"-licenseName LiLiQ_R_1_1 = "Licence Libre du Qu\233bec \8211 R\233ciprocit\233 version 1.1"-licenseName LiLiQ_Rplus_1_1 = "Licence Libre du Qu\233bec \8211 R\233ciprocit\233 forte version 1.1"-licenseName Linux_OpenIB = "Linux Kernel Variant of OpenIB.org license"-licenseName LPL_1_02 = "Lucent Public License v1.02"-licenseName LPL_1_0 = "Lucent Public License Version 1.0"-licenseName LPPL_1_0 = "LaTeX Project Public License v1.0"-licenseName LPPL_1_1 = "LaTeX Project Public License v1.1"-licenseName LPPL_1_2 = "LaTeX Project Public License v1.2"-licenseName LPPL_1_3a = "LaTeX Project Public License v1.3a"-licenseName LPPL_1_3c = "LaTeX Project Public License v1.3c"-licenseName MakeIndex = "MakeIndex License"-licenseName MirOS = "The MirOS Licence"-licenseName MIT_0 = "MIT No Attribution"-licenseName MIT_advertising = "Enlightenment License (e16)"-licenseName MIT_CMU = "CMU License"-licenseName MIT_enna = "enna License"-licenseName MIT_feh = "feh License"-licenseName MITNFA = "MIT +no-false-attribs license"-licenseName MIT = "MIT License"-licenseName Motosoto = "Motosoto License"-licenseName Mpich2 = "mpich2 License"-licenseName MPL_1_0 = "Mozilla Public License 1.0"-licenseName MPL_1_1 = "Mozilla Public License 1.1"-licenseName MPL_2_0_no_copyleft_exception = "Mozilla Public License 2.0 (no copyleft exception)"-licenseName MPL_2_0 = "Mozilla Public License 2.0"-licenseName MS_PL = "Microsoft Public License"-licenseName MS_RL = "Microsoft Reciprocal License"-licenseName MTLL = "Matrix Template Library License"-licenseName MulanPSL_1_0 = "Mulan Permissive Software License, Version 1"-licenseName MulanPSL_2_0 = "Mulan Permissive Software License, Version 2"-licenseName Multics = "Multics License"-licenseName Mup = "Mup License"-licenseName NASA_1_3 = "NASA Open Source Agreement 1.3"-licenseName Naumen = "Naumen Public License"-licenseName NBPL_1_0 = "Net Boolean Public License v1"-licenseName NCGL_UK_2_0 = "Non-Commercial Government Licence"-licenseName NCSA = "University of Illinois/NCSA Open Source License"-licenseName Net_SNMP = "Net-SNMP License"-licenseName NetCDF = "NetCDF license"-licenseName Newsletr = "Newsletr License"-licenseName NGPL = "Nethack General Public License"-licenseName NIST_PD_fallback = "NIST Public Domain Notice with license fallback"-licenseName NIST_PD = "NIST Public Domain Notice"-licenseName NLOD_1_0 = "Norwegian Licence for Open Government Data"-licenseName NLPL = "No Limit Public License"-licenseName Nokia = "Nokia Open Source License"-licenseName NOSL = "Netizen Open Source License"-licenseName Noweb = "Noweb License"-licenseName NPL_1_0 = "Netscape Public License v1.0"-licenseName NPL_1_1 = "Netscape Public License v1.1"-licenseName NPOSL_3_0 = "Non-Profit Open Software License 3.0"-licenseName NRL = "NRL License"-licenseName NTP_0 = "NTP No Attribution"-licenseName NTP = "NTP License"-licenseName O_UDA_1_0 = "Open Use of Data Agreement v1.0"-licenseName OCCT_PL = "Open CASCADE Technology Public License"-licenseName OCLC_2_0 = "OCLC Research Public License 2.0"-licenseName ODbL_1_0 = "ODC Open Database License v1.0"-licenseName ODC_By_1_0 = "Open Data Commons Attribution License v1.0"-licenseName OFL_1_0_no_RFN = "SIL Open Font License 1.0 with no Reserved Font Name"-licenseName OFL_1_0_RFN = "SIL Open Font License 1.0 with Reserved Font Name"-licenseName OFL_1_0 = "SIL Open Font License 1.0"-licenseName OFL_1_1_no_RFN = "SIL Open Font License 1.1 with no Reserved Font Name"-licenseName OFL_1_1_RFN = "SIL Open Font License 1.1 with Reserved Font Name"-licenseName OFL_1_1 = "SIL Open Font License 1.1"-licenseName OGC_1_0 = "OGC Software License, Version 1.0"-licenseName OGL_Canada_2_0 = "Open Government Licence - Canada"-licenseName OGL_UK_1_0 = "Open Government Licence v1.0"-licenseName OGL_UK_2_0 = "Open Government Licence v2.0"-licenseName OGL_UK_3_0 = "Open Government Licence v3.0"-licenseName OGTSL = "Open Group Test Suite License"-licenseName OLDAP_1_1 = "Open LDAP Public License v1.1"-licenseName OLDAP_1_2 = "Open LDAP Public License v1.2"-licenseName OLDAP_1_3 = "Open LDAP Public License v1.3"-licenseName OLDAP_1_4 = "Open LDAP Public License v1.4"-licenseName OLDAP_2_0_1 = "Open LDAP Public License v2.0.1"-licenseName OLDAP_2_0 = "Open LDAP Public License v2.0 (or possibly 2.0A and 2.0B)"-licenseName OLDAP_2_1 = "Open LDAP Public License v2.1"-licenseName OLDAP_2_2_1 = "Open LDAP Public License v2.2.1"-licenseName OLDAP_2_2_2 = "Open LDAP Public License 2.2.2"-licenseName OLDAP_2_2 = "Open LDAP Public License v2.2"-licenseName OLDAP_2_3 = "Open LDAP Public License v2.3"-licenseName OLDAP_2_4 = "Open LDAP Public License v2.4"-licenseName OLDAP_2_5 = "Open LDAP Public License v2.5"-licenseName OLDAP_2_6 = "Open LDAP Public License v2.6"-licenseName OLDAP_2_7 = "Open LDAP Public License v2.7"-licenseName OLDAP_2_8 = "Open LDAP Public License v2.8"-licenseName OML = "Open Market License"-licenseName OpenSSL = "OpenSSL License"-licenseName OPL_1_0 = "Open Public License v1.0"-licenseName OSET_PL_2_1 = "OSET Public License version 2.1"-licenseName OSL_1_0 = "Open Software License 1.0"-licenseName OSL_1_1 = "Open Software License 1.1"-licenseName OSL_2_0 = "Open Software License 2.0"-licenseName OSL_2_1 = "Open Software License 2.1"-licenseName OSL_3_0 = "Open Software License 3.0"-licenseName Parity_6_0_0 = "The Parity Public License 6.0.0"-licenseName Parity_7_0_0 = "The Parity Public License 7.0.0"-licenseName PDDL_1_0 = "ODC Public Domain Dedication & License 1.0"-licenseName PHP_3_01 = "PHP License v3.01"-licenseName PHP_3_0 = "PHP License v3.0"-licenseName Plexus = "Plexus Classworlds License"-licenseName PolyForm_Noncommercial_1_0_0 = "PolyForm Noncommercial License 1.0.0"-licenseName PolyForm_Small_Business_1_0_0 = "PolyForm Small Business License 1.0.0"-licenseName PostgreSQL = "PostgreSQL License"-licenseName PSF_2_0 = "Python Software Foundation License 2.0"-licenseName Psfrag = "psfrag License"-licenseName Psutils = "psutils License"-licenseName Python_2_0 = "Python License 2.0"-licenseName Qhull = "Qhull License"-licenseName QPL_1_0 = "Q Public License 1.0"-licenseName Rdisc = "Rdisc License"-licenseName RHeCos_1_1 = "Red Hat eCos Public License v1.1"-licenseName RPL_1_1 = "Reciprocal Public License 1.1"-licenseName RPL_1_5 = "Reciprocal Public License 1.5"-licenseName RPSL_1_0 = "RealNetworks Public Source License v1.0"-licenseName RSA_MD = "RSA Message-Digest License"-licenseName RSCPL = "Ricoh Source Code Public License"-licenseName Ruby = "Ruby License"-licenseName SAX_PD = "Sax Public Domain Notice"-licenseName Saxpath = "Saxpath License"-licenseName SCEA = "SCEA Shared Source License"-licenseName Sendmail_8_23 = "Sendmail License 8.23"-licenseName Sendmail = "Sendmail License"-licenseName SGI_B_1_0 = "SGI Free Software License B v1.0"-licenseName SGI_B_1_1 = "SGI Free Software License B v1.1"-licenseName SGI_B_2_0 = "SGI Free Software License B v2.0"-licenseName SHL_0_51 = "Solderpad Hardware License, Version 0.51"-licenseName SHL_0_5 = "Solderpad Hardware License v0.5"-licenseName SimPL_2_0 = "Simple Public License 2.0"-licenseName SISSL_1_2 = "Sun Industry Standards Source License v1.2"-licenseName SISSL = "Sun Industry Standards Source License v1.1"-licenseName Sleepycat = "Sleepycat License"-licenseName SMLNJ = "Standard ML of New Jersey License"-licenseName SMPPL = "Secure Messaging Protocol Public License"-licenseName SNIA = "SNIA Public License 1.1"-licenseName Spencer_86 = "Spencer License 86"-licenseName Spencer_94 = "Spencer License 94"-licenseName Spencer_99 = "Spencer License 99"-licenseName SPL_1_0 = "Sun Public License v1.0"-licenseName SSH_OpenSSH = "SSH OpenSSH license"-licenseName SSH_short = "SSH short notice"-licenseName SSPL_1_0 = "Server Side Public License, v 1"-licenseName SugarCRM_1_1_3 = "SugarCRM Public License v1.1.3"-licenseName SWL = "Scheme Widget Library (SWL) Software License Agreement"-licenseName TAPR_OHL_1_0 = "TAPR Open Hardware License v1.0"-licenseName TCL = "TCL/TK License"-licenseName TCP_wrappers = "TCP Wrappers License"-licenseName TMate = "TMate Open Source License"-licenseName TORQUE_1_1 = "TORQUE v2.5+ Software License v1.1"-licenseName TOSL = "Trusster Open Source License"-licenseName TU_Berlin_1_0 = "Technische Universitaet Berlin License 1.0"-licenseName TU_Berlin_2_0 = "Technische Universitaet Berlin License 2.0"-licenseName UCL_1_0 = "Upstream Compatibility License v1.0"-licenseName Unicode_DFS_2015 = "Unicode License Agreement - Data Files and Software (2015)"-licenseName Unicode_DFS_2016 = "Unicode License Agreement - Data Files and Software (2016)"-licenseName Unicode_TOU = "Unicode Terms of Use"-licenseName Unlicense = "The Unlicense"-licenseName UPL_1_0 = "Universal Permissive License v1.0"-licenseName Vim = "Vim License"-licenseName VOSTROM = "VOSTROM Public License for Open Source"-licenseName VSL_1_0 = "Vovida Software License v1.0"-licenseName W3C_19980720 = "W3C Software Notice and License (1998-07-20)"-licenseName W3C_20150513 = "W3C Software Notice and Document License (2015-05-13)"-licenseName W3C = "W3C Software Notice and License (2002-12-31)"-licenseName Watcom_1_0 = "Sybase Open Watcom Public License 1.0"-licenseName Wsuipa = "Wsuipa License"-licenseName WTFPL = "Do What The F*ck You Want To Public License"-licenseName X11 = "X11 License"-licenseName Xerox = "Xerox License"-licenseName XFree86_1_1 = "XFree86 License 1.1"-licenseName Xinetd = "xinetd License"-licenseName Xnet = "X.Net License"-licenseName Xpp = "XPP License"-licenseName XSkat = "XSkat License"-licenseName YPL_1_0 = "Yahoo! Public License v1.0"-licenseName YPL_1_1 = "Yahoo! Public License v1.1"-licenseName Zed = "Zed License"-licenseName Zend_2_0 = "Zend License v2.0"-licenseName Zimbra_1_3 = "Zimbra Public License v1.3"-licenseName Zimbra_1_4 = "Zimbra Public License v1.4"-licenseName Zlib_acknowledgement = "zlib/libpng License with Acknowledgement"-licenseName Zlib = "zlib License"-licenseName ZPL_1_1 = "Zope Public License 1.1"-licenseName ZPL_2_0 = "Zope Public License 2.0"-licenseName ZPL_2_1 = "Zope Public License 2.1"---- | Whether the license is approved by Open Source Initiative (OSI).------ See <https://opensource.org/licenses/alphabetical>.-licenseIsOsiApproved :: LicenseId -> Bool-licenseIsOsiApproved NullBSD = True-licenseIsOsiApproved AAL = True-licenseIsOsiApproved AFL_1_1 = True-licenseIsOsiApproved AFL_1_2 = True-licenseIsOsiApproved AFL_2_0 = True-licenseIsOsiApproved AFL_2_1 = True-licenseIsOsiApproved AFL_3_0 = True-licenseIsOsiApproved AGPL_3_0_only = True-licenseIsOsiApproved AGPL_3_0_or_later = True-licenseIsOsiApproved Apache_1_1 = True-licenseIsOsiApproved Apache_2_0 = True-licenseIsOsiApproved APL_1_0 = True-licenseIsOsiApproved APSL_1_0 = True-licenseIsOsiApproved APSL_1_1 = True-licenseIsOsiApproved APSL_1_2 = True-licenseIsOsiApproved APSL_2_0 = True-licenseIsOsiApproved Artistic_1_0_cl8 = True-licenseIsOsiApproved Artistic_1_0_Perl = True-licenseIsOsiApproved Artistic_1_0 = True-licenseIsOsiApproved Artistic_2_0 = True-licenseIsOsiApproved BSD_1_Clause = True-licenseIsOsiApproved BSD_2_Clause_Patent = True-licenseIsOsiApproved BSD_2_Clause = True-licenseIsOsiApproved BSD_3_Clause_LBNL = True-licenseIsOsiApproved BSD_3_Clause = True-licenseIsOsiApproved BSL_1_0 = True-licenseIsOsiApproved CAL_1_0_Combined_Work_Exception = True-licenseIsOsiApproved CAL_1_0 = True-licenseIsOsiApproved CATOSL_1_1 = True-licenseIsOsiApproved CDDL_1_0 = True-licenseIsOsiApproved CECILL_2_1 = True-licenseIsOsiApproved CNRI_Python = True-licenseIsOsiApproved CPAL_1_0 = True-licenseIsOsiApproved CPL_1_0 = True-licenseIsOsiApproved CUA_OPL_1_0 = True-licenseIsOsiApproved ECL_1_0 = True-licenseIsOsiApproved ECL_2_0 = True-licenseIsOsiApproved EFL_1_0 = True-licenseIsOsiApproved EFL_2_0 = True-licenseIsOsiApproved Entessa = True-licenseIsOsiApproved EPL_1_0 = True-licenseIsOsiApproved EPL_2_0 = True-licenseIsOsiApproved EUDatagrid = True-licenseIsOsiApproved EUPL_1_1 = True-licenseIsOsiApproved EUPL_1_2 = True-licenseIsOsiApproved Fair = True-licenseIsOsiApproved Frameworx_1_0 = True-licenseIsOsiApproved GPL_2_0_only = True-licenseIsOsiApproved GPL_2_0_or_later = True-licenseIsOsiApproved GPL_3_0_only = True-licenseIsOsiApproved GPL_3_0_or_later = True-licenseIsOsiApproved HPND = True-licenseIsOsiApproved Intel = True-licenseIsOsiApproved IPA = True-licenseIsOsiApproved IPL_1_0 = True-licenseIsOsiApproved ISC = True-licenseIsOsiApproved LGPL_2_0_only = True-licenseIsOsiApproved LGPL_2_0_or_later = True-licenseIsOsiApproved LGPL_2_1_only = True-licenseIsOsiApproved LGPL_2_1_or_later = True-licenseIsOsiApproved LGPL_3_0_only = True-licenseIsOsiApproved LGPL_3_0_or_later = True-licenseIsOsiApproved LiLiQ_P_1_1 = True-licenseIsOsiApproved LiLiQ_R_1_1 = True-licenseIsOsiApproved LiLiQ_Rplus_1_1 = True-licenseIsOsiApproved LPL_1_02 = True-licenseIsOsiApproved LPL_1_0 = True-licenseIsOsiApproved LPPL_1_3c = True-licenseIsOsiApproved MirOS = True-licenseIsOsiApproved MIT = True-licenseIsOsiApproved Motosoto = True-licenseIsOsiApproved MPL_1_0 = True-licenseIsOsiApproved MPL_1_1 = True-licenseIsOsiApproved MPL_2_0_no_copyleft_exception = True-licenseIsOsiApproved MPL_2_0 = True-licenseIsOsiApproved MS_PL = True-licenseIsOsiApproved MS_RL = True-licenseIsOsiApproved MulanPSL_2_0 = True-licenseIsOsiApproved Multics = True-licenseIsOsiApproved NASA_1_3 = True-licenseIsOsiApproved Naumen = True-licenseIsOsiApproved NCSA = True-licenseIsOsiApproved NGPL = True-licenseIsOsiApproved Nokia = True-licenseIsOsiApproved NPOSL_3_0 = True-licenseIsOsiApproved NTP = True-licenseIsOsiApproved OCLC_2_0 = True-licenseIsOsiApproved OFL_1_1_no_RFN = True-licenseIsOsiApproved OFL_1_1_RFN = True-licenseIsOsiApproved OFL_1_1 = True-licenseIsOsiApproved OGTSL = True-licenseIsOsiApproved OSET_PL_2_1 = True-licenseIsOsiApproved OSL_1_0 = True-licenseIsOsiApproved OSL_2_0 = True-licenseIsOsiApproved OSL_2_1 = True-licenseIsOsiApproved OSL_3_0 = True-licenseIsOsiApproved PHP_3_01 = True-licenseIsOsiApproved PHP_3_0 = True-licenseIsOsiApproved PostgreSQL = True-licenseIsOsiApproved Python_2_0 = True-licenseIsOsiApproved QPL_1_0 = True-licenseIsOsiApproved RPL_1_1 = True-licenseIsOsiApproved RPL_1_5 = True-licenseIsOsiApproved RPSL_1_0 = True-licenseIsOsiApproved RSCPL = True-licenseIsOsiApproved SimPL_2_0 = True-licenseIsOsiApproved SISSL = True-licenseIsOsiApproved Sleepycat = True-licenseIsOsiApproved SPL_1_0 = True-licenseIsOsiApproved UCL_1_0 = True-licenseIsOsiApproved Unlicense = True-licenseIsOsiApproved UPL_1_0 = True-licenseIsOsiApproved VSL_1_0 = True-licenseIsOsiApproved W3C = True-licenseIsOsiApproved Watcom_1_0 = True-licenseIsOsiApproved Xnet = True-licenseIsOsiApproved Zlib = True-licenseIsOsiApproved ZPL_2_0 = True-licenseIsOsiApproved _ = False---- | Whether the license is considered libre by Free Software Foundation (FSF).------ See <https://www.gnu.org/licenses/license-list.en.html>------ @since 3.4.0.0----licenseIsFsfLibre :: LicenseId -> Bool-licenseIsFsfLibre AFL_1_1 = True-licenseIsFsfLibre AFL_1_2 = True-licenseIsFsfLibre AFL_2_0 = True-licenseIsFsfLibre AFL_2_1 = True-licenseIsFsfLibre AFL_3_0 = True-licenseIsFsfLibre AGPL_1_0 = True-licenseIsFsfLibre AGPL_3_0_only = True-licenseIsFsfLibre AGPL_3_0_or_later = True-licenseIsFsfLibre Apache_1_0 = True-licenseIsFsfLibre Apache_1_1 = True-licenseIsFsfLibre Apache_2_0 = True-licenseIsFsfLibre APSL_2_0 = True-licenseIsFsfLibre Artistic_2_0 = True-licenseIsFsfLibre BitTorrent_1_1 = True-licenseIsFsfLibre BSD_2_Clause_FreeBSD = True-licenseIsFsfLibre BSD_3_Clause_Clear = True-licenseIsFsfLibre BSD_3_Clause = True-licenseIsFsfLibre BSD_4_Clause = True-licenseIsFsfLibre BSL_1_0 = True-licenseIsFsfLibre CC_BY_4_0 = True-licenseIsFsfLibre CC_BY_SA_4_0 = True-licenseIsFsfLibre CC0_1_0 = True-licenseIsFsfLibre CDDL_1_0 = True-licenseIsFsfLibre CECILL_2_0 = True-licenseIsFsfLibre CECILL_B = True-licenseIsFsfLibre CECILL_C = True-licenseIsFsfLibre ClArtistic = True-licenseIsFsfLibre Condor_1_1 = True-licenseIsFsfLibre CPAL_1_0 = True-licenseIsFsfLibre CPL_1_0 = True-licenseIsFsfLibre ECL_2_0 = True-licenseIsFsfLibre EFL_2_0 = True-licenseIsFsfLibre EPL_1_0 = True-licenseIsFsfLibre EPL_2_0 = True-licenseIsFsfLibre EUDatagrid = True-licenseIsFsfLibre EUPL_1_1 = True-licenseIsFsfLibre EUPL_1_2 = True-licenseIsFsfLibre FSFAP = True-licenseIsFsfLibre FTL = True-licenseIsFsfLibre GFDL_1_1_only = True-licenseIsFsfLibre GFDL_1_1_or_later = True-licenseIsFsfLibre GFDL_1_2_only = True-licenseIsFsfLibre GFDL_1_2_or_later = True-licenseIsFsfLibre GFDL_1_3_only = True-licenseIsFsfLibre GFDL_1_3_or_later = True-licenseIsFsfLibre Gnuplot = True-licenseIsFsfLibre GPL_2_0_only = True-licenseIsFsfLibre GPL_2_0_or_later = True-licenseIsFsfLibre GPL_3_0_only = True-licenseIsFsfLibre GPL_3_0_or_later = True-licenseIsFsfLibre HPND = True-licenseIsFsfLibre IJG = True-licenseIsFsfLibre IMatix = True-licenseIsFsfLibre Imlib2 = True-licenseIsFsfLibre Intel = True-licenseIsFsfLibre IPA = True-licenseIsFsfLibre IPL_1_0 = True-licenseIsFsfLibre ISC = True-licenseIsFsfLibre LGPL_2_1_only = True-licenseIsFsfLibre LGPL_2_1_or_later = True-licenseIsFsfLibre LGPL_3_0_only = True-licenseIsFsfLibre LGPL_3_0_or_later = True-licenseIsFsfLibre LPL_1_02 = True-licenseIsFsfLibre LPPL_1_2 = True-licenseIsFsfLibre LPPL_1_3a = True-licenseIsFsfLibre MIT = True-licenseIsFsfLibre MPL_1_1 = True-licenseIsFsfLibre MPL_2_0 = True-licenseIsFsfLibre MS_PL = True-licenseIsFsfLibre MS_RL = True-licenseIsFsfLibre NCSA = True-licenseIsFsfLibre Nokia = True-licenseIsFsfLibre NOSL = True-licenseIsFsfLibre NPL_1_0 = True-licenseIsFsfLibre NPL_1_1 = True-licenseIsFsfLibre ODbL_1_0 = True-licenseIsFsfLibre OFL_1_0 = True-licenseIsFsfLibre OFL_1_1 = True-licenseIsFsfLibre OLDAP_2_3 = True-licenseIsFsfLibre OLDAP_2_7 = True-licenseIsFsfLibre OpenSSL = True-licenseIsFsfLibre OSL_1_0 = True-licenseIsFsfLibre OSL_1_1 = True-licenseIsFsfLibre OSL_2_0 = True-licenseIsFsfLibre OSL_2_1 = True-licenseIsFsfLibre OSL_3_0 = True-licenseIsFsfLibre PHP_3_01 = True-licenseIsFsfLibre Python_2_0 = True-licenseIsFsfLibre QPL_1_0 = True-licenseIsFsfLibre RPSL_1_0 = True-licenseIsFsfLibre Ruby = True-licenseIsFsfLibre SGI_B_2_0 = True-licenseIsFsfLibre SISSL = True-licenseIsFsfLibre Sleepycat = True-licenseIsFsfLibre SMLNJ = True-licenseIsFsfLibre SPL_1_0 = True-licenseIsFsfLibre Unlicense = True-licenseIsFsfLibre UPL_1_0 = True-licenseIsFsfLibre Vim = True-licenseIsFsfLibre W3C = True-licenseIsFsfLibre WTFPL = True-licenseIsFsfLibre X11 = True-licenseIsFsfLibre XFree86_1_1 = True-licenseIsFsfLibre Xinetd = True-licenseIsFsfLibre YPL_1_1 = True-licenseIsFsfLibre Zend_2_0 = True-licenseIsFsfLibre Zimbra_1_3 = True-licenseIsFsfLibre Zlib = True-licenseIsFsfLibre ZPL_2_0 = True-licenseIsFsfLibre ZPL_2_1 = True-licenseIsFsfLibre _ = False------------------------------------------------------------------------------------ Creation----------------------------------------------------------------------------------licenseIdList :: LicenseListVersion -> [LicenseId]-licenseIdList LicenseListVersion_3_0 =- [ AGPL_1_0- , BSD_2_Clause_FreeBSD- , BSD_2_Clause_NetBSD- ]- ++ bulkOfLicenses-licenseIdList LicenseListVersion_3_2 =- [ AGPL_1_0_only- , AGPL_1_0_or_later- , BSD_2_Clause_FreeBSD- , BSD_2_Clause_NetBSD- , Linux_OpenIB- , MIT_0- , ODC_By_1_0- , TU_Berlin_1_0- , TU_Berlin_2_0- ]- ++ bulkOfLicenses-licenseIdList LicenseListVersion_3_6 =- [ AGPL_1_0_only- , AGPL_1_0_or_later- , Blessing- , BlueOak_1_0_0- , BSD_2_Clause_FreeBSD- , BSD_2_Clause_NetBSD- , BSD_3_Clause_Open_MPI- , CC_PDDC- , CERN_OHL_1_1- , CERN_OHL_1_2- , Copyleft_next_0_3_0- , Copyleft_next_0_3_1- , HPND_sell_variant- , JPNIC- , Libpng_2_0- , Linux_OpenIB- , MIT_0- , ODC_By_1_0- , OGL_UK_1_0- , OGL_UK_2_0- , OGL_UK_3_0- , Parity_6_0_0- , Sendmail_8_23- , SHL_0_51- , SHL_0_5- , SSPL_1_0- , TAPR_OHL_1_0- , TU_Berlin_1_0- , TU_Berlin_2_0- ]- ++ bulkOfLicenses-licenseIdList LicenseListVersion_3_9 =- [ AGPL_1_0_only- , AGPL_1_0_or_later- , Blessing- , BlueOak_1_0_0- , BSD_2_Clause_FreeBSD- , BSD_3_Clause_Open_MPI- , CAL_1_0_Combined_Work_Exception- , CAL_1_0- , CC_PDDC- , CERN_OHL_1_1- , CERN_OHL_1_2- , CERN_OHL_P_2_0- , CERN_OHL_S_2_0- , CERN_OHL_W_2_0- , Copyleft_next_0_3_0- , Copyleft_next_0_3_1- , Etalab_2_0- , Hippocratic_2_1- , HPND_sell_variant- , JPNIC- , Libpng_2_0- , Libselinux_1_0- , Linux_OpenIB- , MIT_0- , MulanPSL_1_0- , MulanPSL_2_0- , NCGL_UK_2_0- , NTP_0- , O_UDA_1_0- , ODC_By_1_0- , OFL_1_0_no_RFN- , OFL_1_0_RFN- , OFL_1_1_no_RFN- , OFL_1_1_RFN- , OGC_1_0- , OGL_Canada_2_0- , OGL_UK_1_0- , OGL_UK_2_0- , OGL_UK_3_0- , Parity_6_0_0- , Parity_7_0_0- , PolyForm_Noncommercial_1_0_0- , PolyForm_Small_Business_1_0_0- , PSF_2_0- , Sendmail_8_23- , SHL_0_51- , SHL_0_5- , SSH_OpenSSH- , SSH_short- , SSPL_1_0- , TAPR_OHL_1_0- , TU_Berlin_1_0- , TU_Berlin_2_0- , UCL_1_0- ]- ++ bulkOfLicenses-licenseIdList LicenseListVersion_3_10 =- [ AGPL_1_0_only- , AGPL_1_0_or_later- , Blessing- , BlueOak_1_0_0- , BSD_2_Clause_FreeBSD- , BSD_3_Clause_Open_MPI- , CAL_1_0_Combined_Work_Exception- , CAL_1_0- , CC_PDDC- , CERN_OHL_1_1- , CERN_OHL_1_2- , CERN_OHL_P_2_0- , CERN_OHL_S_2_0- , CERN_OHL_W_2_0- , Copyleft_next_0_3_0- , Copyleft_next_0_3_1- , Etalab_2_0- , Hippocratic_2_1- , HPND_sell_variant- , JPNIC- , Libpng_2_0- , Libselinux_1_0- , Linux_OpenIB- , MIT_0- , MulanPSL_1_0- , MulanPSL_2_0- , NCGL_UK_2_0- , NTP_0- , O_UDA_1_0- , ODC_By_1_0- , OFL_1_0_no_RFN- , OFL_1_0_RFN- , OFL_1_1_no_RFN- , OFL_1_1_RFN- , OGC_1_0- , OGL_Canada_2_0- , OGL_UK_1_0- , OGL_UK_2_0- , OGL_UK_3_0- , Parity_6_0_0- , Parity_7_0_0- , PolyForm_Noncommercial_1_0_0- , PolyForm_Small_Business_1_0_0- , PSF_2_0- , Sendmail_8_23- , SHL_0_51- , SHL_0_5- , SSH_OpenSSH- , SSH_short- , SSPL_1_0- , TAPR_OHL_1_0- , TU_Berlin_1_0- , TU_Berlin_2_0- , UCL_1_0- ]- ++ bulkOfLicenses---- | Create a 'LicenseId' from a 'String'.-mkLicenseId :: LicenseListVersion -> String -> Maybe LicenseId-mkLicenseId LicenseListVersion_3_0 s = Map.lookup s stringLookup_3_0-mkLicenseId LicenseListVersion_3_2 s = Map.lookup s stringLookup_3_2-mkLicenseId LicenseListVersion_3_6 s = Map.lookup s stringLookup_3_6-mkLicenseId LicenseListVersion_3_9 s = Map.lookup s stringLookup_3_9-mkLicenseId LicenseListVersion_3_10 s = Map.lookup s stringLookup_3_10--stringLookup_3_0 :: Map String LicenseId-stringLookup_3_0 = Map.fromList $ map (\i -> (licenseId i, i)) $- licenseIdList LicenseListVersion_3_0--stringLookup_3_2 :: Map String LicenseId-stringLookup_3_2 = Map.fromList $ map (\i -> (licenseId i, i)) $- licenseIdList LicenseListVersion_3_2--stringLookup_3_6 :: Map String LicenseId-stringLookup_3_6 = Map.fromList $ map (\i -> (licenseId i, i)) $- licenseIdList LicenseListVersion_3_6--stringLookup_3_9 :: Map String LicenseId-stringLookup_3_9 = Map.fromList $ map (\i -> (licenseId i, i)) $- licenseIdList LicenseListVersion_3_9--stringLookup_3_10 :: Map String LicenseId-stringLookup_3_10 = Map.fromList $ map (\i -> (licenseId i, i)) $- licenseIdList LicenseListVersion_3_10---- | Licenses in all SPDX License lists-bulkOfLicenses :: [LicenseId]-bulkOfLicenses =- [ NullBSD- , AAL- , Abstyles- , Adobe_2006- , Adobe_Glyph- , ADSL- , AFL_1_1- , AFL_1_2- , AFL_2_0- , AFL_2_1- , AFL_3_0- , Afmparse- , AGPL_3_0_only- , AGPL_3_0_or_later- , Aladdin- , AMDPLPA- , AML- , AMPAS- , ANTLR_PD- , Apache_1_0- , Apache_1_1- , Apache_2_0- , APAFML- , APL_1_0- , APSL_1_0- , APSL_1_1- , APSL_1_2- , APSL_2_0- , Artistic_1_0_cl8- , Artistic_1_0_Perl- , Artistic_1_0- , Artistic_2_0- , Bahyph- , Barr- , Beerware- , BitTorrent_1_0- , BitTorrent_1_1- , Borceux- , BSD_1_Clause- , BSD_2_Clause_Patent- , BSD_2_Clause- , BSD_3_Clause_Attribution- , BSD_3_Clause_Clear- , BSD_3_Clause_LBNL- , BSD_3_Clause_No_Nuclear_License_2014- , BSD_3_Clause_No_Nuclear_License- , BSD_3_Clause_No_Nuclear_Warranty- , BSD_3_Clause- , BSD_4_Clause_UC- , BSD_4_Clause- , BSD_Protection- , BSD_Source_Code- , BSL_1_0- , Bzip2_1_0_5- , Bzip2_1_0_6- , Caldera- , CATOSL_1_1- , CC_BY_1_0- , CC_BY_2_0- , CC_BY_2_5- , CC_BY_3_0- , CC_BY_4_0- , CC_BY_NC_1_0- , CC_BY_NC_2_0- , CC_BY_NC_2_5- , CC_BY_NC_3_0- , CC_BY_NC_4_0- , CC_BY_NC_ND_1_0- , CC_BY_NC_ND_2_0- , CC_BY_NC_ND_2_5- , CC_BY_NC_ND_3_0- , CC_BY_NC_ND_4_0- , CC_BY_NC_SA_1_0- , CC_BY_NC_SA_2_0- , CC_BY_NC_SA_2_5- , CC_BY_NC_SA_3_0- , CC_BY_NC_SA_4_0- , CC_BY_ND_1_0- , CC_BY_ND_2_0- , CC_BY_ND_2_5- , CC_BY_ND_3_0- , CC_BY_ND_4_0- , CC_BY_SA_1_0- , CC_BY_SA_2_0- , CC_BY_SA_2_5- , CC_BY_SA_3_0- , CC_BY_SA_4_0- , CC0_1_0- , CDDL_1_0- , CDDL_1_1- , CDLA_Permissive_1_0- , CDLA_Sharing_1_0- , CECILL_1_0- , CECILL_1_1- , CECILL_2_0- , CECILL_2_1- , CECILL_B- , CECILL_C- , ClArtistic- , CNRI_Jython- , CNRI_Python_GPL_Compatible- , CNRI_Python- , Condor_1_1- , CPAL_1_0- , CPL_1_0- , CPOL_1_02- , Crossword- , CrystalStacker- , CUA_OPL_1_0- , Cube- , Curl- , D_FSL_1_0- , Diffmark- , DOC- , Dotseqn- , DSDP- , Dvipdfm- , ECL_1_0- , ECL_2_0- , EFL_1_0- , EFL_2_0- , EGenix- , Entessa- , EPL_1_0- , EPL_2_0- , ErlPL_1_1- , EUDatagrid- , EUPL_1_0- , EUPL_1_1- , EUPL_1_2- , Eurosym- , Fair- , Frameworx_1_0- , FreeImage- , FSFAP- , FSFULLR- , FSFUL- , FTL- , GFDL_1_1_only- , GFDL_1_1_or_later- , GFDL_1_2_only- , GFDL_1_2_or_later- , GFDL_1_3_only- , GFDL_1_3_or_later- , Giftware- , GL2PS- , Glide- , Glulxe- , Gnuplot- , GPL_1_0_only- , GPL_1_0_or_later- , GPL_2_0_only- , GPL_2_0_or_later- , GPL_3_0_only- , GPL_3_0_or_later- , GSOAP_1_3b- , HaskellReport- , HPND- , IBM_pibs- , ICU- , IJG- , ImageMagick- , IMatix- , Imlib2- , Info_ZIP- , Intel_ACPI- , Intel- , Interbase_1_0- , IPA- , IPL_1_0- , ISC- , JasPer_2_0- , JSON- , LAL_1_2- , LAL_1_3- , Latex2e- , Leptonica- , LGPL_2_0_only- , LGPL_2_0_or_later- , LGPL_2_1_only- , LGPL_2_1_or_later- , LGPL_3_0_only- , LGPL_3_0_or_later- , LGPLLR- , Libpng- , Libtiff- , LiLiQ_P_1_1- , LiLiQ_R_1_1- , LiLiQ_Rplus_1_1- , LPL_1_02- , LPL_1_0- , LPPL_1_0- , LPPL_1_1- , LPPL_1_2- , LPPL_1_3a- , LPPL_1_3c- , MakeIndex- , MirOS- , MIT_advertising- , MIT_CMU- , MIT_enna- , MIT_feh- , MITNFA- , MIT- , Motosoto- , Mpich2- , MPL_1_0- , MPL_1_1- , MPL_2_0_no_copyleft_exception- , MPL_2_0- , MS_PL- , MS_RL- , MTLL- , Multics- , Mup- , NASA_1_3- , Naumen- , NBPL_1_0- , NCSA- , Net_SNMP- , NetCDF- , Newsletr- , NGPL- , NLOD_1_0- , NLPL- , Nokia- , NOSL- , Noweb- , NPL_1_0- , NPL_1_1- , NPOSL_3_0- , NRL- , NTP- , OCCT_PL- , OCLC_2_0- , ODbL_1_0- , OFL_1_0- , OFL_1_1- , OGTSL- , OLDAP_1_1- , OLDAP_1_2- , OLDAP_1_3- , OLDAP_1_4- , OLDAP_2_0_1- , OLDAP_2_0- , OLDAP_2_1- , OLDAP_2_2_1- , OLDAP_2_2_2- , OLDAP_2_2- , OLDAP_2_3- , OLDAP_2_4- , OLDAP_2_5- , OLDAP_2_6- , OLDAP_2_7- , OLDAP_2_8- , OML- , OpenSSL- , OPL_1_0- , OSET_PL_2_1- , OSL_1_0- , OSL_1_1- , OSL_2_0- , OSL_2_1- , OSL_3_0- , PDDL_1_0- , PHP_3_01- , PHP_3_0- , Plexus- , PostgreSQL- , Psfrag- , Psutils- , Python_2_0- , Qhull- , QPL_1_0- , Rdisc- , RHeCos_1_1- , RPL_1_1- , RPL_1_5- , RPSL_1_0- , RSA_MD- , RSCPL- , Ruby- , SAX_PD- , Saxpath- , SCEA- , Sendmail- , SGI_B_1_0- , SGI_B_1_1- , SGI_B_2_0- , SimPL_2_0- , SISSL_1_2- , SISSL- , Sleepycat- , SMLNJ- , SMPPL- , SNIA- , Spencer_86- , Spencer_94- , Spencer_99- , SPL_1_0- , SugarCRM_1_1_3- , SWL- , TCL- , TCP_wrappers- , TMate- , TORQUE_1_1- , TOSL- , Unicode_DFS_2015- , Unicode_DFS_2016- , Unicode_TOU- , Unlicense- , UPL_1_0- , Vim- , VOSTROM- , VSL_1_0- , W3C_19980720- , W3C_20150513- , W3C- , Watcom_1_0- , Wsuipa- , WTFPL- , X11- , Xerox- , XFree86_1_1- , Xinetd- , Xnet- , Xpp- , XSkat- , YPL_1_0- , YPL_1_1- , Zed- , Zend_2_0- , Zimbra_1_3- , Zimbra_1_4- , Zlib_acknowledgement- , Zlib- , ZPL_1_1- , ZPL_2_0- , ZPL_2_1- ]
− src/Distribution/SPDX/LicenseListVersion.hs
@@ -1,22 +0,0 @@-module Distribution.SPDX.LicenseListVersion (- LicenseListVersion (..),- cabalSpecVersionToSPDXListVersion,- ) where--import Distribution.CabalSpecVersion---- | SPDX License List version @Cabal@ is aware of.-data LicenseListVersion- = LicenseListVersion_3_0- | LicenseListVersion_3_2- | LicenseListVersion_3_6- | LicenseListVersion_3_9- | LicenseListVersion_3_10- deriving (Eq, Ord, Show, Enum, Bounded)--cabalSpecVersionToSPDXListVersion :: CabalSpecVersion -> LicenseListVersion-cabalSpecVersionToSPDXListVersion CabalSpecV3_6 = LicenseListVersion_3_10-cabalSpecVersionToSPDXListVersion CabalSpecV3_4 = LicenseListVersion_3_9-cabalSpecVersionToSPDXListVersion CabalSpecV3_0 = LicenseListVersion_3_6-cabalSpecVersionToSPDXListVersion CabalSpecV2_4 = LicenseListVersion_3_2-cabalSpecVersionToSPDXListVersion _ = LicenseListVersion_3_0
− src/Distribution/SPDX/LicenseReference.hs
@@ -1,80 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.SPDX.LicenseReference (- LicenseRef,- licenseRef,- licenseDocumentRef,- mkLicenseRef,- mkLicenseRef',- ) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Utils.Generic (isAsciiAlphaNum)-import Distribution.Pretty-import Distribution.Parsec--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp---- | A user defined license reference denoted by @LicenseRef-[idstring]@ (for a license not on the SPDX License List);-data LicenseRef = LicenseRef- { _lrDocument :: !(Maybe String)- , _lrLicense :: !String- }- deriving (Show, Read, Eq, Ord, Typeable, Data, Generic)---- | License reference.-licenseRef :: LicenseRef -> String-licenseRef = _lrLicense---- | Document reference.-licenseDocumentRef :: LicenseRef -> Maybe String-licenseDocumentRef = _lrDocument--instance Binary LicenseRef-instance Structured LicenseRef--instance NFData LicenseRef where- rnf (LicenseRef d l) = rnf d `seq` rnf l--instance Pretty LicenseRef where- pretty (LicenseRef Nothing l) = Disp.text "LicenseRef-" <<>> Disp.text l- pretty (LicenseRef (Just d) l) =- Disp.text "DocumentRef-" <<>> Disp.text d <<>> Disp.char ':' <<>> Disp.text "LicenseRef-" <<>> Disp.text l--instance Parsec LicenseRef where- parsec = name <|> doc- where- name = do- _ <- P.string "LicenseRef-"- n <- some $ P.satisfy $ \c -> isAsciiAlphaNum c || c == '-' || c == '.'- pure (LicenseRef Nothing n)-- doc = do- _ <- P.string "DocumentRef-"- d <- some $ P.satisfy $ \c -> isAsciiAlphaNum c || c == '-' || c == '.'- _ <- P.char ':'- _ <- P.string "LicenseRef-"- n <- some $ P.satisfy $ \c -> isAsciiAlphaNum c || c == '-' || c == '.'- pure (LicenseRef (Just d) n)---- | Create 'LicenseRef' from optional document ref and name.-mkLicenseRef :: Maybe String -> String -> Maybe LicenseRef-mkLicenseRef d l = do- d' <- traverse checkIdString d- l' <- checkIdString l- pure (LicenseRef d' l')- where- checkIdString s- | all (\c -> isAsciiAlphaNum c || c == '-' || c == '.') s = Just s- | otherwise = Nothing---- | Like 'mkLicenseRef' but convert invalid characters into @-@.-mkLicenseRef' :: Maybe String -> String -> LicenseRef-mkLicenseRef' d l = LicenseRef (fmap f d) (f l)- where- f = map g- g c | isAsciiAlphaNum c || c == '-' || c == '.' = c- | otherwise = '-'
src/Distribution/Simple.hs view
@@ -1,836 +1,1132 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE LambdaCase #-}-{-# LANGUAGE CPP #-}--------------------------------------------------------------------------------- |--- Module : Distribution.Simple--- Copyright : Isaac Jones 2003-2005--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ This is the command line front end to the Simple build system. When given--- the parsed command-line args and package information, is able to perform--- basic commands like configure, build, install, register, etc.------ This module exports the main functions that Setup.hs scripts use. It--- re-exports the 'UserHooks' type, the standard entry points like--- 'defaultMain' and 'defaultMainWithHooks' and the predefined sets of--- 'UserHooks' that custom @Setup.hs@ scripts can extend to add their own--- behaviour.------ This module isn't called \"Simple\" because it's simple. Far from--- it. It's called \"Simple\" because it does complicated things to--- simple software.------ The original idea was that there could be different build systems that all--- presented the same compatible command line interfaces. There is still a--- "Distribution.Make" system but in practice no packages use it.--{--Work around this warning:-libraries/Cabal/Distribution/Simple.hs:78:0:- Warning: In the use of `runTests'- (imported from Distribution.Simple.UserHooks):- Deprecated: "Please use the new testing interface instead!"--}-{-# OPTIONS_GHC -fno-warn-deprecations #-}--module Distribution.Simple (- module Distribution.Package,- module Distribution.Version,- module Distribution.License,- module Distribution.Simple.Compiler,- module Language.Haskell.Extension,- -- * Simple interface- defaultMain, defaultMainNoRead, defaultMainArgs,- -- * Customization- UserHooks(..), Args,- defaultMainWithHooks, defaultMainWithHooksArgs,- defaultMainWithHooksNoRead, defaultMainWithHooksNoReadArgs,- -- ** Standard sets of hooks- simpleUserHooks,- autoconfUserHooks,- emptyUserHooks,- ) where--import Control.Exception (try)--import Prelude ()-import Distribution.Compat.Prelude---- local-import Distribution.Simple.Compiler-import Distribution.Simple.UserHooks-import Distribution.Package-import Distribution.PackageDescription-import Distribution.PackageDescription.Configuration-import Distribution.Simple.Program-import Distribution.Simple.Program.Db-import Distribution.Simple.PreProcess-import Distribution.Simple.Setup-import Distribution.Simple.Command--import Distribution.Simple.Build-import Distribution.Simple.SrcDist-import Distribution.Simple.Register--import Distribution.Simple.Configure--import Distribution.Simple.LocalBuildInfo-import Distribution.Simple.Bench-import Distribution.Simple.BuildPaths-import Distribution.Simple.Test-import Distribution.Simple.Install-import Distribution.Simple.Haddock-import Distribution.Simple.Utils-import Distribution.Utils.NubList-import Distribution.Verbosity-import Language.Haskell.Extension-import Distribution.Version-import Distribution.License-import Distribution.Pretty-import Distribution.System (buildPlatform)---- Base-import System.Environment (getArgs, getProgName)-import System.Directory (removeFile, doesFileExist- ,doesDirectoryExist, removeDirectoryRecursive)-import System.FilePath (searchPathSeparator, takeDirectory, (</>),- splitDirectories, dropDrive)-#ifdef mingw32_HOST_OS-import System.FilePath (normalise, splitDrive)-#endif-import Distribution.Compat.ResponseFile (expandResponse)-import Distribution.Compat.Directory (makeAbsolute)-import Distribution.Compat.Environment (getEnvironment)-import Distribution.Compat.GetShortPathName (getShortPathName)--import Data.List (unionBy, (\\))--import Distribution.PackageDescription.Parsec---- | A simple implementation of @main@ for a Cabal setup script.--- It reads the package description file using IO, and performs the--- action specified on the command line.-defaultMain :: IO ()-defaultMain = getArgs >>= defaultMainHelper simpleUserHooks---- | A version of 'defaultMain' that is passed the command line--- arguments, rather than getting them from the environment.-defaultMainArgs :: [String] -> IO ()-defaultMainArgs = defaultMainHelper simpleUserHooks---- | A customizable version of 'defaultMain'.-defaultMainWithHooks :: UserHooks -> IO ()-defaultMainWithHooks hooks = getArgs >>= defaultMainHelper hooks---- | A customizable version of 'defaultMain' that also takes the command--- line arguments.-defaultMainWithHooksArgs :: UserHooks -> [String] -> IO ()-defaultMainWithHooksArgs = defaultMainHelper---- | Like 'defaultMain', but accepts the package description as input--- rather than using IO to read it.-defaultMainNoRead :: GenericPackageDescription -> IO ()-defaultMainNoRead = defaultMainWithHooksNoRead simpleUserHooks---- | A customizable version of 'defaultMainNoRead'.-defaultMainWithHooksNoRead :: UserHooks -> GenericPackageDescription -> IO ()-defaultMainWithHooksNoRead hooks pkg_descr =- getArgs >>=- defaultMainHelper hooks { readDesc = return (Just pkg_descr) }---- | A customizable version of 'defaultMainNoRead' that also takes the--- command line arguments.------ @since 2.2.0.0-defaultMainWithHooksNoReadArgs :: UserHooks -> GenericPackageDescription -> [String] -> IO ()-defaultMainWithHooksNoReadArgs hooks pkg_descr =- defaultMainHelper hooks { readDesc = return (Just pkg_descr) }--defaultMainHelper :: UserHooks -> Args -> IO ()-defaultMainHelper hooks args = topHandler $ do- args' <- expandResponse args- case commandsRun (globalCommand commands) commands args' of- CommandHelp help -> printHelp help- CommandList opts -> printOptionsList opts- CommandErrors errs -> printErrors errs- CommandReadyToGo (flags, commandParse) ->- case commandParse of- _ | fromFlag (globalVersion flags) -> printVersion- | fromFlag (globalNumericVersion flags) -> printNumericVersion- CommandHelp help -> printHelp help- CommandList opts -> printOptionsList opts- CommandErrors errs -> printErrors errs- CommandReadyToGo action -> action-- where- printHelp help = getProgName >>= putStr . help- printOptionsList = putStr . unlines- printErrors errs = do- putStr (intercalate "\n" errs)- exitWith (ExitFailure 1)- printNumericVersion = putStrLn $ prettyShow cabalVersion- printVersion = putStrLn $ "Cabal library version "- ++ prettyShow cabalVersion-- progs = addKnownPrograms (hookedPrograms hooks) defaultProgramDb- commands =- [configureCommand progs `commandAddAction`- \fs as -> configureAction hooks fs as >> return ()- ,buildCommand progs `commandAddAction` buildAction hooks- ,showBuildInfoCommand progs `commandAddAction` showBuildInfoAction hooks- ,replCommand progs `commandAddAction` replAction hooks- ,installCommand `commandAddAction` installAction hooks- ,copyCommand `commandAddAction` copyAction hooks- ,haddockCommand `commandAddAction` haddockAction hooks- ,cleanCommand `commandAddAction` cleanAction hooks- ,sdistCommand `commandAddAction` sdistAction hooks- ,hscolourCommand `commandAddAction` hscolourAction hooks- ,registerCommand `commandAddAction` registerAction hooks- ,unregisterCommand `commandAddAction` unregisterAction hooks- ,testCommand `commandAddAction` testAction hooks- ,benchmarkCommand `commandAddAction` benchAction hooks- ]---- | Combine the preprocessors in the given hooks with the--- preprocessors built into cabal.-allSuffixHandlers :: UserHooks- -> [PPSuffixHandler]-allSuffixHandlers hooks- = overridesPP (hookedPreProcessors hooks) knownSuffixHandlers- where- overridesPP :: [PPSuffixHandler] -> [PPSuffixHandler] -> [PPSuffixHandler]- overridesPP = unionBy (\x y -> fst x == fst y)--configureAction :: UserHooks -> ConfigFlags -> Args -> IO LocalBuildInfo-configureAction hooks flags args = do- distPref <- findDistPrefOrDefault (configDistPref flags)- let flags' = flags { configDistPref = toFlag distPref- , configArgs = args }-- -- See docs for 'HookedBuildInfo'- pbi <- preConf hooks args flags'-- (mb_pd_file, pkg_descr0) <- confPkgDescr hooks verbosity- (flagToMaybe (configCabalFilePath flags))-- let epkg_descr = (pkg_descr0, pbi)-- localbuildinfo0 <- confHook hooks epkg_descr flags'-- -- remember the .cabal filename if we know it- -- and all the extra command line args- let localbuildinfo = localbuildinfo0 {- pkgDescrFile = mb_pd_file,- extraConfigArgs = args- }- writePersistBuildConfig distPref localbuildinfo-- let pkg_descr = localPkgDescr localbuildinfo- postConf hooks args flags' pkg_descr localbuildinfo- return localbuildinfo- where- verbosity = fromFlag (configVerbosity flags)--confPkgDescr :: UserHooks -> Verbosity -> Maybe FilePath- -> IO (Maybe FilePath, GenericPackageDescription)-confPkgDescr hooks verbosity mb_path = do- mdescr <- readDesc hooks- case mdescr of- Just descr -> return (Nothing, descr)- Nothing -> do- pdfile <- case mb_path of- Nothing -> defaultPackageDesc verbosity- Just path -> return path- info verbosity "Using Parsec parser"- descr <- readGenericPackageDescription verbosity pdfile- return (Just pdfile, descr)--buildAction :: UserHooks -> BuildFlags -> Args -> IO ()-buildAction hooks flags args = do- distPref <- findDistPrefOrDefault (buildDistPref flags)- let verbosity = fromFlag $ buildVerbosity flags- lbi <- getBuildConfig hooks verbosity distPref- let flags' = flags { buildDistPref = toFlag distPref- , buildCabalFilePath = maybeToFlag (cabalFilePath lbi)}-- progs <- reconfigurePrograms verbosity- (buildProgramPaths flags')- (buildProgramArgs flags')- (withPrograms lbi)-- hookedAction verbosity preBuild buildHook postBuild- (return lbi { withPrograms = progs })- hooks flags' { buildArgs = args } args--showBuildInfoAction :: UserHooks -> ShowBuildInfoFlags -> Args -> IO ()-showBuildInfoAction hooks (ShowBuildInfoFlags flags fileOutput) args = do- distPref <- findDistPrefOrDefault (buildDistPref flags)- let verbosity = fromFlag $ buildVerbosity flags- lbi <- getBuildConfig hooks verbosity distPref- let flags' = flags { buildDistPref = toFlag distPref- , buildCabalFilePath = maybeToFlag (cabalFilePath lbi)- }-- progs <- reconfigurePrograms verbosity- (buildProgramPaths flags')- (buildProgramArgs flags')- (withPrograms lbi)-- pbi <- preBuild hooks args flags'- let lbi' = lbi { withPrograms = progs }- pkg_descr0 = localPkgDescr lbi'- pkg_descr = updatePackageDescription pbi pkg_descr0- -- TODO: Somehow don't ignore build hook?- buildInfoString <- showBuildInfo pkg_descr lbi' flags-- case fileOutput of- Nothing -> putStr buildInfoString- Just fp -> writeFile fp buildInfoString-- postBuild hooks args flags' pkg_descr lbi'--replAction :: UserHooks -> ReplFlags -> Args -> IO ()-replAction hooks flags args = do- distPref <- findDistPrefOrDefault (replDistPref flags)- let verbosity = fromFlag $ replVerbosity flags- flags' = flags { replDistPref = toFlag distPref }-- lbi <- getBuildConfig hooks verbosity distPref- progs <- reconfigurePrograms verbosity- (replProgramPaths flags')- (replProgramArgs flags')- (withPrograms lbi)-- -- As far as I can tell, the only reason this doesn't use- -- 'hookedActionWithArgs' is because the arguments of 'replHook'- -- takes the args explicitly. UGH. -- ezyang- pbi <- preRepl hooks args flags'- let pkg_descr0 = localPkgDescr lbi- sanityCheckHookedBuildInfo verbosity pkg_descr0 pbi- let pkg_descr = updatePackageDescription pbi pkg_descr0- lbi' = lbi { withPrograms = progs- , localPkgDescr = pkg_descr }- replHook hooks pkg_descr lbi' hooks flags' args- postRepl hooks args flags' pkg_descr lbi'--hscolourAction :: UserHooks -> HscolourFlags -> Args -> IO ()-hscolourAction hooks flags args = do- distPref <- findDistPrefOrDefault (hscolourDistPref flags)- let verbosity = fromFlag $ hscolourVerbosity flags- lbi <- getBuildConfig hooks verbosity distPref- let flags' = flags { hscolourDistPref = toFlag distPref- , hscolourCabalFilePath = maybeToFlag (cabalFilePath lbi)}-- hookedAction verbosity preHscolour hscolourHook postHscolour- (getBuildConfig hooks verbosity distPref)- hooks flags' args--haddockAction :: UserHooks -> HaddockFlags -> Args -> IO ()-haddockAction hooks flags args = do- distPref <- findDistPrefOrDefault (haddockDistPref flags)- let verbosity = fromFlag $ haddockVerbosity flags- lbi <- getBuildConfig hooks verbosity distPref- let flags' = flags { haddockDistPref = toFlag distPref- , haddockCabalFilePath = maybeToFlag (cabalFilePath lbi)}-- progs <- reconfigurePrograms verbosity- (haddockProgramPaths flags')- (haddockProgramArgs flags')- (withPrograms lbi)-- hookedAction verbosity preHaddock haddockHook postHaddock- (return lbi { withPrograms = progs })- hooks flags' { haddockArgs = args } args--cleanAction :: UserHooks -> CleanFlags -> Args -> IO ()-cleanAction hooks flags args = do- distPref <- findDistPrefOrDefault (cleanDistPref flags)-- elbi <- tryGetBuildConfig hooks verbosity distPref- let flags' = flags { cleanDistPref = toFlag distPref- , cleanCabalFilePath = case elbi of- Left _ -> mempty- Right lbi -> maybeToFlag (cabalFilePath lbi)}-- pbi <- preClean hooks args flags'-- (_, ppd) <- confPkgDescr hooks verbosity Nothing- -- It might seem like we are doing something clever here- -- but we're really not: if you look at the implementation- -- of 'clean' in the end all the package description is- -- used for is to clear out @extra-tmp-files@. IMO,- -- the configure script goo should go into @dist@ too!- -- -- ezyang- let pkg_descr0 = flattenPackageDescription ppd- -- We don't sanity check for clean as an error- -- here would prevent cleaning:- --sanityCheckHookedBuildInfo verbosity pkg_descr0 pbi- let pkg_descr = updatePackageDescription pbi pkg_descr0-- cleanHook hooks pkg_descr () hooks flags'- postClean hooks args flags' pkg_descr ()- where- verbosity = fromFlag (cleanVerbosity flags)--copyAction :: UserHooks -> CopyFlags -> Args -> IO ()-copyAction hooks flags args = do- distPref <- findDistPrefOrDefault (copyDistPref flags)- let verbosity = fromFlag $ copyVerbosity flags- lbi <- getBuildConfig hooks verbosity distPref- let flags' = flags { copyDistPref = toFlag distPref- , copyCabalFilePath = maybeToFlag (cabalFilePath lbi)}- hookedAction verbosity preCopy copyHook postCopy- (getBuildConfig hooks verbosity distPref)- hooks flags' { copyArgs = args } args--installAction :: UserHooks -> InstallFlags -> Args -> IO ()-installAction hooks flags args = do- distPref <- findDistPrefOrDefault (installDistPref flags)- let verbosity = fromFlag $ installVerbosity flags- lbi <- getBuildConfig hooks verbosity distPref- let flags' = flags { installDistPref = toFlag distPref- , installCabalFilePath = maybeToFlag (cabalFilePath lbi)}- hookedAction verbosity preInst instHook postInst- (getBuildConfig hooks verbosity distPref)- hooks flags' args---- Since Cabal-3.4 UserHooks are completely ignored-sdistAction :: UserHooks -> SDistFlags -> Args -> IO ()-sdistAction _hooks flags _args = do- (_, ppd) <- confPkgDescr emptyUserHooks verbosity Nothing- let pkg_descr = flattenPackageDescription ppd- sdist pkg_descr flags srcPref knownSuffixHandlers- where- verbosity = fromFlag (sDistVerbosity flags)--testAction :: UserHooks -> TestFlags -> Args -> IO ()-testAction hooks flags args = do- distPref <- findDistPrefOrDefault (testDistPref flags)- let verbosity = fromFlag $ testVerbosity flags- flags' = flags { testDistPref = toFlag distPref }-- hookedActionWithArgs verbosity preTest testHook postTest- (getBuildConfig hooks verbosity distPref)- hooks flags' args--benchAction :: UserHooks -> BenchmarkFlags -> Args -> IO ()-benchAction hooks flags args = do- distPref <- findDistPrefOrDefault (benchmarkDistPref flags)- let verbosity = fromFlag $ benchmarkVerbosity flags- flags' = flags { benchmarkDistPref = toFlag distPref }- hookedActionWithArgs verbosity preBench benchHook postBench- (getBuildConfig hooks verbosity distPref)- hooks flags' args--registerAction :: UserHooks -> RegisterFlags -> Args -> IO ()-registerAction hooks flags args = do- distPref <- findDistPrefOrDefault (regDistPref flags)- let verbosity = fromFlag $ regVerbosity flags- lbi <- getBuildConfig hooks verbosity distPref- let flags' = flags { regDistPref = toFlag distPref- , regCabalFilePath = maybeToFlag (cabalFilePath lbi)}- hookedAction verbosity preReg regHook postReg- (getBuildConfig hooks verbosity distPref)- hooks flags' { regArgs = args } args--unregisterAction :: UserHooks -> RegisterFlags -> Args -> IO ()-unregisterAction hooks flags args = do- distPref <- findDistPrefOrDefault (regDistPref flags)- let verbosity = fromFlag $ regVerbosity flags- lbi <- getBuildConfig hooks verbosity distPref- let flags' = flags { regDistPref = toFlag distPref- , regCabalFilePath = maybeToFlag (cabalFilePath lbi)}- hookedAction verbosity preUnreg unregHook postUnreg- (getBuildConfig hooks verbosity distPref)- hooks flags' args--hookedAction- :: Verbosity- -> (UserHooks -> Args -> flags -> IO HookedBuildInfo)- -> (UserHooks -> PackageDescription -> LocalBuildInfo- -> UserHooks -> flags -> IO ())- -> (UserHooks -> Args -> flags -> PackageDescription- -> LocalBuildInfo -> IO ())- -> IO LocalBuildInfo- -> UserHooks -> flags -> Args -> IO ()-hookedAction verbosity pre_hook cmd_hook =- hookedActionWithArgs verbosity pre_hook- (\h _ pd lbi uh flags ->- cmd_hook h pd lbi uh flags)--hookedActionWithArgs- :: Verbosity- -> (UserHooks -> Args -> flags -> IO HookedBuildInfo)- -> (UserHooks -> Args -> PackageDescription -> LocalBuildInfo- -> UserHooks -> flags -> IO ())- -> (UserHooks -> Args -> flags -> PackageDescription- -> LocalBuildInfo -> IO ())- -> IO LocalBuildInfo- -> UserHooks -> flags -> Args -> IO ()-hookedActionWithArgs verbosity pre_hook cmd_hook post_hook- get_build_config hooks flags args = do- pbi <- pre_hook hooks args flags- lbi0 <- get_build_config- let pkg_descr0 = localPkgDescr lbi0- sanityCheckHookedBuildInfo verbosity pkg_descr0 pbi- let pkg_descr = updatePackageDescription pbi pkg_descr0- lbi = lbi0 { localPkgDescr = pkg_descr }- cmd_hook hooks args pkg_descr lbi hooks flags- post_hook hooks args flags pkg_descr lbi--sanityCheckHookedBuildInfo- :: Verbosity -> PackageDescription -> HookedBuildInfo -> IO ()-sanityCheckHookedBuildInfo verbosity- (PackageDescription { library = Nothing }) (Just _,_)- = die' verbosity $ "The buildinfo contains info for a library, "- ++ "but the package does not have a library."--sanityCheckHookedBuildInfo verbosity pkg_descr (_, hookExes)- | exe1 : _ <- nonExistant- = die' verbosity $ "The buildinfo contains info for an executable called '"- ++ prettyShow exe1 ++ "' but the package does not have a "- ++ "executable with that name."- where- pkgExeNames = nub (map exeName (executables pkg_descr))- hookExeNames = nub (map fst hookExes)- nonExistant = hookExeNames \\ pkgExeNames--sanityCheckHookedBuildInfo _ _ _ = return ()---- | Try to read the 'localBuildInfoFile'-tryGetBuildConfig :: UserHooks -> Verbosity -> FilePath- -> IO (Either ConfigStateFileError LocalBuildInfo)-tryGetBuildConfig u v = try . getBuildConfig u v----- | Read the 'localBuildInfoFile' or throw an exception.-getBuildConfig :: UserHooks -> Verbosity -> FilePath -> IO LocalBuildInfo-getBuildConfig hooks verbosity distPref = do- lbi_wo_programs <- getPersistBuildConfig distPref- -- Restore info about unconfigured programs, since it is not serialized- let lbi = lbi_wo_programs {- withPrograms = restoreProgramDb- (builtinPrograms ++ hookedPrograms hooks)- (withPrograms lbi_wo_programs)- }-- case pkgDescrFile lbi of- Nothing -> return lbi- Just pkg_descr_file -> do- outdated <- checkPersistBuildConfigOutdated distPref pkg_descr_file- if outdated- then reconfigure pkg_descr_file lbi- else return lbi-- where- reconfigure :: FilePath -> LocalBuildInfo -> IO LocalBuildInfo- reconfigure pkg_descr_file lbi = do- notice verbosity $ pkg_descr_file ++ " has been changed. "- ++ "Re-configuring with most recently used options. "- ++ "If this fails, please run configure manually.\n"- let cFlags = configFlags lbi- let cFlags' = cFlags {- -- Since the list of unconfigured programs is not serialized,- -- restore it to the same value as normally used at the beginning- -- of a configure run:- configPrograms_ = fmap (restoreProgramDb- (builtinPrograms ++ hookedPrograms hooks))- `fmap` configPrograms_ cFlags,-- -- Use the current, not saved verbosity level:- configVerbosity = Flag verbosity- }- configureAction hooks cFlags' (extraConfigArgs lbi)----- ----------------------------------------------------------------------------- Cleaning--clean :: PackageDescription -> CleanFlags -> IO ()-clean pkg_descr flags = do- let distPref = fromFlagOrDefault defaultDistPref $ cleanDistPref flags- notice verbosity "cleaning..."-- maybeConfig <- if fromFlag (cleanSaveConf flags)- then maybeGetPersistBuildConfig distPref- else return Nothing-- -- remove the whole dist/ directory rather than tracking exactly what files- -- we created in there.- chattyTry "removing dist/" $ do- exists <- doesDirectoryExist distPref- when exists (removeDirectoryRecursive distPref)-- -- Any extra files the user wants to remove- traverse_ removeFileOrDirectory (extraTmpFiles pkg_descr)-- -- If the user wanted to save the config, write it back- traverse_ (writePersistBuildConfig distPref) maybeConfig-- where- removeFileOrDirectory :: FilePath -> IO ()- removeFileOrDirectory fname = do- isDir <- doesDirectoryExist fname- isFile <- doesFileExist fname- if isDir then removeDirectoryRecursive fname- else when isFile $ removeFile fname- verbosity = fromFlag (cleanVerbosity flags)---- ----------------------------------------------------------------------------- Default hooks---- | Hooks that correspond to a plain instantiation of the--- \"simple\" build system-simpleUserHooks :: UserHooks-simpleUserHooks =- emptyUserHooks {- confHook = configure,- postConf = finalChecks,- buildHook = defaultBuildHook,- replHook = defaultReplHook,- copyHook = \desc lbi _ f -> install desc lbi f,- -- 'install' has correct 'copy' behavior with params- testHook = defaultTestHook,- benchHook = defaultBenchHook,- instHook = defaultInstallHook,- cleanHook = \p _ _ f -> clean p f,- hscolourHook = \p l h f -> hscolour p l (allSuffixHandlers h) f,- haddockHook = \p l h f -> haddock p l (allSuffixHandlers h) f,- regHook = defaultRegHook,- unregHook = \p l _ f -> unregister p l f- }- where- finalChecks _args flags pkg_descr lbi =- checkForeignDeps pkg_descr lbi (lessVerbose verbosity)- where- verbosity = fromFlag (configVerbosity flags)---- | Basic autoconf 'UserHooks':------ * 'postConf' runs @.\/configure@, if present.------ * the pre-hooks 'preBuild', 'preClean', 'preCopy', 'preInst',--- 'preReg' and 'preUnreg' read additional build information from--- /package/@.buildinfo@, if present.------ Thus @configure@ can use local system information to generate--- /package/@.buildinfo@ and possibly other files.--autoconfUserHooks :: UserHooks-autoconfUserHooks- = simpleUserHooks- {- postConf = defaultPostConf,- preBuild = readHookWithArgs buildVerbosity buildDistPref, -- buildCabalFilePath,- preCopy = readHookWithArgs copyVerbosity copyDistPref,- preClean = readHook cleanVerbosity cleanDistPref,- preInst = readHook installVerbosity installDistPref,- preHscolour = readHook hscolourVerbosity hscolourDistPref,- preHaddock = readHookWithArgs haddockVerbosity haddockDistPref,- preReg = readHook regVerbosity regDistPref,- preUnreg = readHook regVerbosity regDistPref- }- where defaultPostConf :: Args -> ConfigFlags -> PackageDescription- -> LocalBuildInfo -> IO ()- defaultPostConf args flags pkg_descr lbi- = do let verbosity = fromFlag (configVerbosity flags)- baseDir lbi' = fromMaybe ""- (takeDirectory <$> cabalFilePath lbi')- confExists <- doesFileExist $ (baseDir lbi) </> "configure"- if confExists- then runConfigureScript verbosity- backwardsCompatHack flags lbi- else die' verbosity "configure script not found."-- pbi <- getHookedBuildInfo verbosity (buildDir lbi)- sanityCheckHookedBuildInfo verbosity pkg_descr pbi- let pkg_descr' = updatePackageDescription pbi pkg_descr- lbi' = lbi { localPkgDescr = pkg_descr' }- postConf simpleUserHooks args flags pkg_descr' lbi'-- backwardsCompatHack = False-- readHookWithArgs :: (a -> Flag Verbosity)- -> (a -> Flag FilePath)- -> Args -> a- -> IO HookedBuildInfo- readHookWithArgs get_verbosity get_dist_pref _ flags = do- dist_dir <- findDistPrefOrDefault (get_dist_pref flags)- getHookedBuildInfo verbosity (dist_dir </> "build")- where- verbosity = fromFlag (get_verbosity flags)-- readHook :: (a -> Flag Verbosity)- -> (a -> Flag FilePath)- -> Args -> a -> IO HookedBuildInfo- readHook get_verbosity get_dist_pref a flags = do- noExtraFlags a- dist_dir <- findDistPrefOrDefault (get_dist_pref flags)- getHookedBuildInfo verbosity (dist_dir </> "build")- where- verbosity = fromFlag (get_verbosity flags)--runConfigureScript :: Verbosity -> Bool -> ConfigFlags -> LocalBuildInfo- -> IO ()-runConfigureScript verbosity backwardsCompatHack flags lbi = do- env <- getEnvironment- let programDb = withPrograms lbi- (ccProg, ccFlags) <- configureCCompiler verbosity programDb- ccProgShort <- getShortPathName ccProg- -- The C compiler's compilation and linker flags (e.g.- -- "C compiler flags" and "Gcc Linker flags" from GHC) have already- -- been merged into ccFlags, so we set both CFLAGS and LDFLAGS- -- to ccFlags- -- We don't try and tell configure which ld to use, as we don't have- -- a way to pass its flags too- configureFile <- makeAbsolute $- fromMaybe "." (takeDirectory <$> cabalFilePath lbi) </> "configure"- -- autoconf is fussy about filenames, and has a set of forbidden- -- characters that can't appear in the build directory, etc:- -- https://www.gnu.org/software/autoconf/manual/autoconf.html#File-System-Conventions- --- -- This has caused hard-to-debug failures in the past (#5368), so we- -- detect some cases early and warn with a clear message. Windows's- -- use of backslashes is problematic here, so we'll switch to- -- slashes, but we do still want to fail on backslashes in POSIX- -- paths.- --- -- TODO: We don't check for colons, tildes or leading dashes. We- -- also should check the builddir's path, destdir, and all other- -- paths as well.- let configureFile' = toUnix configureFile- for_ badAutoconfCharacters $ \(c, cname) ->- when (c `elem` dropDrive configureFile') $- warn verbosity $ concat- [ "The path to the './configure' script, '", configureFile'- , "', contains the character '", [c], "' (", cname, ")."- , " This may cause the script to fail with an obscure error, or for"- , " building the package to fail later."- ]-- let extraPath = fromNubList $ configProgramPathExtra flags- let cflagsEnv = maybe (unwords ccFlags) (++ (" " ++ unwords ccFlags))- $ lookup "CFLAGS" env- spSep = [searchPathSeparator]- pathEnv = maybe (intercalate spSep extraPath)- ((intercalate spSep extraPath ++ spSep)++) $ lookup "PATH" env- overEnv = ("CFLAGS", Just cflagsEnv) :- [("PATH", Just pathEnv) | not (null extraPath)]- hp = hostPlatform lbi- maybeHostFlag = if hp == buildPlatform then [] else ["--host=" ++ show (pretty hp)]- args' = configureFile':args ++ ["CC=" ++ ccProgShort] ++ maybeHostFlag- shProg = simpleProgram "sh"- progDb = modifyProgramSearchPath- (\p -> map ProgramSearchPathDir extraPath ++ p) emptyProgramDb- shConfiguredProg <- lookupProgram shProg- `fmap` configureProgram verbosity shProg progDb- case shConfiguredProg of- Just sh -> runProgramInvocation verbosity $- (programInvocation (sh {programOverrideEnv = overEnv}) args')- { progInvokeCwd = Just (buildDir lbi) }- Nothing -> die' verbosity notFoundMsg- where- args = configureArgs backwardsCompatHack flags-- notFoundMsg = "The package has a './configure' script. "- ++ "If you are on Windows, This requires a "- ++ "Unix compatibility toolchain such as MinGW+MSYS or Cygwin. "- ++ "If you are not on Windows, ensure that an 'sh' command "- ++ "is discoverable in your path."---- | Convert Windows path to Unix ones-toUnix :: String -> String-#ifdef mingw32_HOST_OS-toUnix s = let tmp = normalise s- (l, rest) = case splitDrive tmp of- ([], x) -> ("/" , x)- (h:_, x) -> ('/':h:"/", x)- parts = splitDirectories rest- in l ++ intercalate "/" parts-#else-toUnix s = intercalate "/" $ splitDirectories s-#endif--badAutoconfCharacters :: [(Char, String)]-badAutoconfCharacters =- [ (' ', "space")- , ('\t', "tab")- , ('\n', "newline")- , ('\0', "null")- , ('"', "double quote")- , ('#', "hash")- , ('$', "dollar sign")- , ('&', "ampersand")- , ('\'', "single quote")- , ('(', "left bracket")- , (')', "right bracket")- , ('*', "star")- , (';', "semicolon")- , ('<', "less-than sign")- , ('=', "equals sign")- , ('>', "greater-than sign")- , ('?', "question mark")- , ('[', "left square bracket")- , ('\\', "backslash")- , ('`', "backtick")- , ('|', "pipe")- ]--getHookedBuildInfo :: Verbosity -> FilePath -> IO HookedBuildInfo-getHookedBuildInfo verbosity build_dir = do- maybe_infoFile <- findHookedPackageDesc verbosity build_dir- case maybe_infoFile of- Nothing -> return emptyHookedBuildInfo- Just infoFile -> do- info verbosity $ "Reading parameters from " ++ infoFile- readHookedBuildInfo verbosity infoFile--defaultTestHook :: Args -> PackageDescription -> LocalBuildInfo- -> UserHooks -> TestFlags -> IO ()-defaultTestHook args pkg_descr localbuildinfo _ flags =- test args pkg_descr localbuildinfo flags--defaultBenchHook :: Args -> PackageDescription -> LocalBuildInfo- -> UserHooks -> BenchmarkFlags -> IO ()-defaultBenchHook args pkg_descr localbuildinfo _ flags =- bench args pkg_descr localbuildinfo flags--defaultInstallHook :: PackageDescription -> LocalBuildInfo- -> UserHooks -> InstallFlags -> IO ()-defaultInstallHook pkg_descr localbuildinfo _ flags = do- let copyFlags = defaultCopyFlags {- copyDistPref = installDistPref flags,- copyDest = installDest flags,- copyVerbosity = installVerbosity flags- }- install pkg_descr localbuildinfo copyFlags- let registerFlags = defaultRegisterFlags {- regDistPref = installDistPref flags,- regInPlace = installInPlace flags,- regPackageDB = installPackageDB flags,- regVerbosity = installVerbosity flags- }- when (hasLibs pkg_descr) $ register pkg_descr localbuildinfo registerFlags--defaultBuildHook :: PackageDescription -> LocalBuildInfo- -> UserHooks -> BuildFlags -> IO ()-defaultBuildHook pkg_descr localbuildinfo hooks flags =- build pkg_descr localbuildinfo flags (allSuffixHandlers hooks)--defaultReplHook :: PackageDescription -> LocalBuildInfo- -> UserHooks -> ReplFlags -> [String] -> IO ()-defaultReplHook pkg_descr localbuildinfo hooks flags args =- repl pkg_descr localbuildinfo flags (allSuffixHandlers hooks) args--defaultRegHook :: PackageDescription -> LocalBuildInfo- -> UserHooks -> RegisterFlags -> IO ()-defaultRegHook pkg_descr localbuildinfo _ flags =- if hasLibs pkg_descr- then register pkg_descr localbuildinfo flags- else setupMessage (fromFlag (regVerbosity flags))- "Package contains no library to register:" (packageId pkg_descr)+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+-----------------------------------------------------------------------------+{-+Work around this warning:+libraries/Cabal/Distribution/Simple.hs:78:0:+ Warning: In the use of `runTests'+ (imported from Distribution.Simple.UserHooks):+ Deprecated: "Please use the new testing interface instead!"+-}+{-# OPTIONS_GHC -Wno-deprecations #-}+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}++-- |+-- Module : Distribution.Simple+-- Copyright : Isaac Jones 2003-2005+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- This is the command line front end to the Simple build system. When given+-- the parsed command-line args and package information, is able to perform+-- basic commands like configure, build, install, register, etc.+--+-- This module exports the main functions that Setup.hs scripts use. It+-- re-exports the 'UserHooks' type, the standard entry points like+-- 'defaultMain' and 'defaultMainWithHooks' and the predefined sets of+-- 'UserHooks' that custom @Setup.hs@ scripts can extend to add their own+-- behaviour.+--+-- This module isn't called \"Simple\" because it's simple. Far from+-- it. It's called \"Simple\" because it does complicated things to+-- simple software.+--+-- The original idea was that there could be different build systems that all+-- presented the same compatible command line interfaces. There is still a+-- "Distribution.Make" system but in practice no packages use it.+module Distribution.Simple+ ( module Distribution.Package+ , module Distribution.Version+ , module Distribution.License+ , module Distribution.Simple.Compiler+ , module Language.Haskell.Extension++ -- * Simple interface+ , defaultMain+ , defaultMainNoRead+ , defaultMainArgs++ -- * Customization+ , UserHooks (..)+ , Args+ , defaultMainWithHooks+ , defaultMainWithSetupHooks+ , defaultMainWithSetupHooksArgs+ , defaultMainWithHooksArgs+ , defaultMainWithHooksNoRead+ , defaultMainWithHooksNoReadArgs++ -- ** Standard sets of hooks+ , simpleUserHooks+ , autoconfUserHooks+ , autoconfSetupHooks+ , emptyUserHooks+ ) where++import Control.Exception (try)++import Distribution.Compat.Prelude+import Distribution.Compat.ResponseFile (expandResponse)+import Prelude ()++-- local++import Distribution.Package+import Distribution.PackageDescription+import Distribution.PackageDescription.Configuration+import Distribution.Simple.Command+import Distribution.Simple.Compiler+import Distribution.Simple.PackageDescription+import Distribution.Simple.PreProcess+import Distribution.Simple.Program+import Distribution.Simple.Setup+import qualified Distribution.Simple.SetupHooks.Internal as SetupHooks+import Distribution.Simple.UserHooks++import Distribution.Simple.Build+import Distribution.Simple.Register+import Distribution.Simple.SrcDist++import Distribution.Simple.Configure++import Distribution.License+import Distribution.Pretty+import Distribution.Simple.Bench+import Distribution.Simple.BuildPaths+import Distribution.Simple.ConfigureScript (runConfigureScript)+import Distribution.Simple.Errors+import Distribution.Simple.Haddock+import Distribution.Simple.Install+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.SetupHooks.Internal+ ( SetupHooks+ )+import Distribution.Simple.Test+import Distribution.Simple.Utils+import qualified Distribution.Types.LocalBuildConfig as LBC+import Distribution.Utils.Path+import Distribution.Verbosity+import Distribution.Version+import Language.Haskell.Extension++-- Base+import Data.List (unionBy, (\\))+import System.Directory+ ( doesDirectoryExist+ , doesFileExist+ , removeDirectoryRecursive+ , removeFile+ )+import System.Environment (getArgs, getProgName)++-- | A simple implementation of @main@ for a Cabal setup script.+-- It reads the package description file using IO, and performs the+-- action specified on the command line.+defaultMain :: IO ()+defaultMain = getArgs >>= defaultMainHelper simpleUserHooks++-- | A version of 'defaultMain' that is passed the command line+-- arguments, rather than getting them from the environment.+defaultMainArgs :: [String] -> IO ()+defaultMainArgs = defaultMainHelper simpleUserHooks++defaultMainWithSetupHooks :: SetupHooks -> IO ()+defaultMainWithSetupHooks setup_hooks =+ getArgs >>= defaultMainWithSetupHooksArgs setup_hooks++defaultMainWithSetupHooksArgs :: SetupHooks -> [String] -> IO ()+defaultMainWithSetupHooksArgs setupHooks =+ defaultMainHelper $+ simpleUserHooks+ { confHook = setup_confHook+ , buildHook = setup_buildHook+ , copyHook = setup_copyHook+ , instHook = setup_installHook+ , replHook = setup_replHook+ , haddockHook = setup_haddockHook+ , hscolourHook = setup_hscolourHook+ }+ where+ setup_confHook+ :: (GenericPackageDescription, HookedBuildInfo)+ -> ConfigFlags+ -> IO LocalBuildInfo+ setup_confHook =+ configure_setupHooks+ (SetupHooks.configureHooks setupHooks)++ setup_buildHook+ :: PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> BuildFlags+ -> IO ()+ setup_buildHook pkg_descr lbi hooks flags =+ build_setupHooks+ (SetupHooks.buildHooks setupHooks)+ pkg_descr+ lbi+ flags+ (allSuffixHandlers hooks)++ setup_copyHook+ :: PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> CopyFlags+ -> IO ()+ setup_copyHook pkg_descr lbi _hooks flags =+ install_setupHooks+ (SetupHooks.installHooks setupHooks)+ pkg_descr+ lbi+ flags++ setup_installHook+ :: PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> InstallFlags+ -> IO ()+ setup_installHook =+ defaultInstallHook_setupHooks+ (SetupHooks.installHooks setupHooks)++ setup_replHook+ :: PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> ReplFlags+ -> [String]+ -> IO ()+ setup_replHook pkg_descr lbi hooks flags args =+ repl_setupHooks+ (SetupHooks.buildHooks setupHooks)+ pkg_descr+ lbi+ flags+ (allSuffixHandlers hooks)+ args++ setup_haddockHook+ :: PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> HaddockFlags+ -> IO ()+ setup_haddockHook pkg_descr lbi hooks flags =+ haddock_setupHooks+ (SetupHooks.buildHooks setupHooks)+ pkg_descr+ lbi+ (allSuffixHandlers hooks)+ flags++ setup_hscolourHook+ :: PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> HscolourFlags+ -> IO ()+ setup_hscolourHook pkg_descr lbi hooks flags =+ hscolour_setupHooks+ (SetupHooks.buildHooks setupHooks)+ pkg_descr+ lbi+ (allSuffixHandlers hooks)+ flags++-- | A customizable version of 'defaultMain'.+defaultMainWithHooks :: UserHooks -> IO ()+defaultMainWithHooks hooks = getArgs >>= defaultMainHelper hooks++-- | A customizable version of 'defaultMain' that also takes the command+-- line arguments.+defaultMainWithHooksArgs :: UserHooks -> [String] -> IO ()+defaultMainWithHooksArgs = defaultMainHelper++-- | Like 'defaultMain', but accepts the package description as input+-- rather than using IO to read it.+defaultMainNoRead :: GenericPackageDescription -> IO ()+defaultMainNoRead = defaultMainWithHooksNoRead simpleUserHooks++-- | A customizable version of 'defaultMainNoRead'.+defaultMainWithHooksNoRead :: UserHooks -> GenericPackageDescription -> IO ()+defaultMainWithHooksNoRead hooks pkg_descr =+ getArgs+ >>= defaultMainHelper hooks{readDesc = return (Just pkg_descr)}++-- | A customizable version of 'defaultMainNoRead' that also takes the+-- command line arguments.+--+-- @since 2.2.0.0+defaultMainWithHooksNoReadArgs :: UserHooks -> GenericPackageDescription -> [String] -> IO ()+defaultMainWithHooksNoReadArgs hooks pkg_descr =+ defaultMainHelper hooks{readDesc = return (Just pkg_descr)}++-- | The central command chooser of the Simple build system,+-- with other defaultMain functions acting as exposed callers,+-- and with 'topHandler' operating as an exceptions handler.+--+-- This uses 'expandResponse' to read response files, preprocessing+-- response files given by "@" prefixes.+--+-- Given hooks and args, this runs 'commandsRun' onto the args,+-- getting 'CommandParse' data back, which is then pattern-matched into+-- IO actions for execution, with arguments applied by the parser.+defaultMainHelper :: UserHooks -> Args -> IO ()+defaultMainHelper hooks args = topHandler $ do+ args' <- expandResponse args+ command <- commandsRun (globalCommand commands) commands args'+ case command of+ CommandHelp help -> printHelp help+ CommandList opts -> printOptionsList opts+ CommandErrors errs -> printErrors errs+ CommandReadyToGo (globalFlags, commandParse) ->+ case commandParse of+ _+ | fromFlag (globalVersion globalFlags) -> printVersion+ | fromFlag (globalNumericVersion globalFlags) -> printNumericVersion+ CommandHelp help -> printHelp help+ CommandList opts -> printOptionsList opts+ CommandErrors errs -> printErrors errs+ CommandReadyToGo action -> action globalFlags+ where+ printHelp help = getProgName >>= putStr . help+ printOptionsList = putStr . unlines+ printErrors errs = do+ putStr (intercalate "\n" errs)+ exitWith (ExitFailure 1)+ printNumericVersion = putStrLn $ prettyShow cabalVersion+ printVersion =+ putStrLn $+ "Cabal library version "+ ++ prettyShow cabalVersion++ progs = addKnownPrograms (hookedPrograms hooks) defaultProgramDb+ addAction :: CommandUI flags -> (GlobalFlags -> UserHooks -> flags -> [String] -> IO res) -> Command (GlobalFlags -> IO ())+ addAction cmd action =+ cmd `commandAddAction` \flags as globalFlags -> void $ action globalFlags hooks flags as+ commands :: [Command (GlobalFlags -> IO ())]+ commands =+ [ configureCommand progs `addAction` configureAction+ , buildCommand progs `addAction` buildAction+ , replCommand progs `addAction` replAction+ , installCommand `addAction` installAction+ , copyCommand `addAction` copyAction+ , haddockCommand `addAction` haddockAction+ , cleanCommand `addAction` cleanAction+ , sdistCommand `addAction` sdistAction+ , hscolourCommand `addAction` hscolourAction+ , registerCommand `addAction` registerAction+ , unregisterCommand `addAction` unregisterAction+ , testCommand `addAction` testAction+ , benchmarkCommand `addAction` benchAction+ ]++-- | Combine the preprocessors in the given hooks with the+-- preprocessors built into cabal.+allSuffixHandlers+ :: UserHooks+ -> [PPSuffixHandler]+allSuffixHandlers hooks =+ overridesPP (hookedPreProcessors hooks) knownSuffixHandlers+ where+ overridesPP :: [PPSuffixHandler] -> [PPSuffixHandler] -> [PPSuffixHandler]+ overridesPP = unionBy (\x y -> fst x == fst y)++configureAction :: GlobalFlags -> UserHooks -> ConfigFlags -> Args -> IO LocalBuildInfo+configureAction globalFlags hooks flags args = do+ distPref <- findDistPrefOrDefault (setupDistPref $ configCommonFlags flags)+ let commonFlags = configCommonFlags flags+ commonFlags' =+ commonFlags+ { setupDistPref = toFlag distPref+ , setupWorkingDir = globalWorkingDir globalFlags <> setupWorkingDir commonFlags+ , setupTargets = args+ }+ flags' =+ flags+ { configCommonFlags = commonFlags'+ }+ mbWorkDir = flagToMaybe $ setupWorkingDir commonFlags'+ verbosity = fromFlag $ setupVerbosity commonFlags'++ -- See docs for 'HookedBuildInfo'+ pbi <- preConf hooks args flags'++ (mb_pd_file, pkg_descr0) <-+ confPkgDescr+ hooks+ verbosity+ mbWorkDir+ (flagToMaybe (setupCabalFilePath commonFlags'))++ let epkg_descr = (pkg_descr0, pbi)++ lbi1 <- confHook hooks epkg_descr flags'++ -- remember the .cabal filename if we know it+ -- and all the extra command line args+ let localbuildinfo =+ lbi1+ { pkgDescrFile = mb_pd_file+ , extraConfigArgs = args+ }+ writePersistBuildConfig mbWorkDir distPref localbuildinfo++ let pkg_descr = localPkgDescr localbuildinfo+ postConf hooks args flags' pkg_descr localbuildinfo+ return localbuildinfo++confPkgDescr+ :: UserHooks+ -> Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> Maybe (SymbolicPath Pkg File)+ -> IO (Maybe (SymbolicPath Pkg File), GenericPackageDescription)+confPkgDescr hooks verbosity cwd mb_path = do+ mdescr <- readDesc hooks+ case mdescr of+ Just descr -> return (Nothing, descr)+ Nothing -> do+ pdfile <- case mb_path of+ Nothing -> relativeSymbolicPath <$> tryFindPackageDesc verbosity cwd+ Just path -> return path+ info verbosity "Using Parsec parser"+ descr <- readGenericPackageDescription verbosity cwd pdfile+ return (Just pdfile, descr)++getCommonFlags+ :: GlobalFlags+ -> UserHooks+ -> CommonSetupFlags+ -> Args+ -> IO (LocalBuildInfo, CommonSetupFlags)+getCommonFlags globalFlags hooks commonFlags args = do+ distPref <- findDistPrefOrDefault (setupDistPref commonFlags)+ let verbosity = fromFlag $ setupVerbosity commonFlags+ lbi <- getBuildConfig globalFlags hooks verbosity distPref+ let common' = configCommonFlags $ configFlags lbi+ return $+ ( lbi+ , commonFlags+ { setupDistPref = toFlag distPref+ , setupCabalFilePath = setupCabalFilePath common' <> setupCabalFilePath commonFlags+ , setupWorkingDir =+ globalWorkingDir globalFlags+ <> setupWorkingDir common'+ <> setupWorkingDir commonFlags+ , setupTargets = args+ }+ )++buildAction :: GlobalFlags -> UserHooks -> BuildFlags -> Args -> IO ()+buildAction globalFlags hooks flags args = do+ let common = buildCommonFlags flags+ verbosity = fromFlag $ setupVerbosity common+ (lbi, common') <- getCommonFlags globalFlags hooks common args+ let flags' = flags{buildCommonFlags = common'}++ progs <-+ reconfigurePrograms+ verbosity+ (buildProgramPaths flags')+ (buildProgramArgs flags')+ (withPrograms lbi)++ hookedAction+ verbosity+ preBuild+ buildHook+ postBuild+ (return lbi{withPrograms = progs})+ hooks+ flags'+ args++replAction :: GlobalFlags -> UserHooks -> ReplFlags -> Args -> IO ()+replAction globalFlags hooks flags args = do+ let common = replCommonFlags flags+ verbosity = fromFlag $ setupVerbosity common+ (lbi, common') <- getCommonFlags globalFlags hooks common args+ let flags' = flags{replCommonFlags = common'}+ progs <-+ reconfigurePrograms+ verbosity+ (replProgramPaths flags')+ (replProgramArgs flags')+ (withPrograms lbi)++ -- As far as I can tell, the only reason this doesn't use+ -- 'hookedActionWithArgs' is because the arguments of 'replHook'+ -- takes the args explicitly. UGH. -- ezyang+ pbi <- preRepl hooks args flags'+ let pkg_descr0 = localPkgDescr lbi+ sanityCheckHookedBuildInfo verbosity pkg_descr0 pbi+ let pkg_descr = updatePackageDescription pbi pkg_descr0+ lbi' =+ lbi+ { withPrograms = progs+ , localPkgDescr = pkg_descr+ }+ replHook hooks pkg_descr lbi' hooks flags' args+ postRepl hooks args flags' pkg_descr lbi'++hscolourAction :: GlobalFlags -> UserHooks -> HscolourFlags -> Args -> IO ()+hscolourAction globalFlags hooks flags args = do+ let common = hscolourCommonFlags flags+ verbosity = fromFlag $ setupVerbosity common+ (_lbi, common') <- getCommonFlags globalFlags hooks common args+ let flags' = flags{hscolourCommonFlags = common'}+ distPref = fromFlag $ setupDistPref common'++ hookedAction+ verbosity+ preHscolour+ hscolourHook+ postHscolour+ (getBuildConfig globalFlags hooks verbosity distPref)+ hooks+ flags'+ args++haddockAction :: GlobalFlags -> UserHooks -> HaddockFlags -> Args -> IO ()+haddockAction globalFlags hooks flags args = do+ let common = haddockCommonFlags flags+ verbosity = fromFlag $ setupVerbosity common+ (lbi, common') <- getCommonFlags globalFlags hooks common args+ let flags' = flags{haddockCommonFlags = common'}++ progs <-+ reconfigurePrograms+ verbosity+ (haddockProgramPaths flags')+ (haddockProgramArgs flags')+ (withPrograms lbi)++ hookedAction+ verbosity+ preHaddock+ haddockHook+ postHaddock+ (return lbi{withPrograms = progs})+ hooks+ flags'+ args++cleanAction :: GlobalFlags -> UserHooks -> CleanFlags -> Args -> IO ()+cleanAction globalFlags hooks flags args = do+ let common = cleanCommonFlags flags+ verbosity = fromFlag $ setupVerbosity common+ distPref <- findDistPrefOrDefault (setupDistPref common)+ elbi <- tryGetBuildConfig globalFlags hooks verbosity distPref+ let common' =+ common+ { setupDistPref = toFlag distPref+ , setupWorkingDir = case elbi of+ Left _ ->+ globalWorkingDir globalFlags+ <> setupWorkingDir common+ Right lbi ->+ globalWorkingDir globalFlags+ <> setupWorkingDir (configCommonFlags $ configFlags lbi)+ <> setupWorkingDir common+ , setupCabalFilePath = case elbi of+ Left _ -> setupCabalFilePath common+ Right lbi ->+ setupCabalFilePath common+ <> setupCabalFilePath (configCommonFlags $ configFlags lbi)+ , setupTargets = args+ }+ flags' =+ flags{cleanCommonFlags = common'}++ mbWorkDirFlag = cleanWorkingDir flags'+ mbWorkDir = flagToMaybe mbWorkDirFlag++ pbi <- preClean hooks args flags'++ (_, ppd) <- confPkgDescr hooks verbosity mbWorkDir Nothing+ -- It might seem like we are doing something clever here+ -- but we're really not: if you look at the implementation+ -- of 'clean' in the end all the package description is+ -- used for is to clear out @extra-tmp-files@. IMO,+ -- the configure script goo should go into @dist@ too!+ -- -- ezyang+ let pkg_descr0 = flattenPackageDescription ppd+ -- We don't sanity check for clean as an error+ -- here would prevent cleaning:+ -- sanityCheckHookedBuildInfo verbosity pkg_descr0 pbi+ let pkg_descr = updatePackageDescription pbi pkg_descr0++ cleanHook hooks pkg_descr () hooks flags'+ postClean hooks args flags' pkg_descr ()++copyAction :: GlobalFlags -> UserHooks -> CopyFlags -> Args -> IO ()+copyAction globalFlags hooks flags args = do+ let common = copyCommonFlags flags+ verbosity = fromFlag $ setupVerbosity common+ (_lbi, common') <- getCommonFlags globalFlags hooks common args+ let flags' = flags{copyCommonFlags = common'}+ distPref = fromFlag $ setupDistPref common'+ hookedAction+ verbosity+ preCopy+ copyHook+ postCopy+ (getBuildConfig globalFlags hooks verbosity distPref)+ hooks+ flags'+ args++installAction :: GlobalFlags -> UserHooks -> InstallFlags -> Args -> IO ()+installAction globalFlags hooks flags args = do+ let common = installCommonFlags flags+ verbosity = fromFlag $ setupVerbosity common+ (_lbi, common') <- getCommonFlags globalFlags hooks common args+ let flags' = flags{installCommonFlags = common'}+ distPref = fromFlag $ setupDistPref common'+ hookedAction+ verbosity+ preInst+ instHook+ postInst+ (getBuildConfig globalFlags hooks verbosity distPref)+ hooks+ flags'+ args++-- Since Cabal-3.4 UserHooks are completely ignored+sdistAction :: GlobalFlags -> UserHooks -> SDistFlags -> Args -> IO ()+sdistAction _globalFlags _hooks flags _args = do+ let mbWorkDir = flagToMaybe $ sDistWorkingDir flags+ (_, ppd) <- confPkgDescr emptyUserHooks verbosity mbWorkDir Nothing+ let pkg_descr = flattenPackageDescription ppd+ sdist pkg_descr flags srcPref knownSuffixHandlers+ where+ verbosity = fromFlag (setupVerbosity $ sDistCommonFlags flags)++testAction :: GlobalFlags -> UserHooks -> TestFlags -> Args -> IO ()+testAction globalFlags hooks flags args = do+ let common = testCommonFlags flags+ verbosity = fromFlag $ setupVerbosity common+ (_lbi, common') <- getCommonFlags globalFlags hooks common args+ let flags' = flags{testCommonFlags = common'}+ distPref = fromFlag $ setupDistPref common'+ hookedActionWithArgs+ verbosity+ preTest+ testHook+ postTest+ (getBuildConfig globalFlags hooks verbosity distPref)+ hooks+ flags'+ args++benchAction :: GlobalFlags -> UserHooks -> BenchmarkFlags -> Args -> IO ()+benchAction globalFlags hooks flags args = do+ let common = benchmarkCommonFlags flags+ verbosity = fromFlag $ setupVerbosity common+ (_lbi, common') <- getCommonFlags globalFlags hooks common args+ let flags' = flags{benchmarkCommonFlags = common'}+ distPref = fromFlag $ setupDistPref common'+ hookedActionWithArgs+ verbosity+ preBench+ benchHook+ postBench+ (getBuildConfig globalFlags hooks verbosity distPref)+ hooks+ flags'+ args++registerAction :: GlobalFlags -> UserHooks -> RegisterFlags -> Args -> IO ()+registerAction globalFlags hooks flags args = do+ let common = registerCommonFlags flags+ verbosity = fromFlag $ setupVerbosity common+ (_lbi, common') <- getCommonFlags globalFlags hooks common args+ let flags' = flags{registerCommonFlags = common'}+ distPref = fromFlag $ setupDistPref common'+ hookedAction+ verbosity+ preReg+ regHook+ postReg+ (getBuildConfig globalFlags hooks verbosity distPref)+ hooks+ flags'+ args++unregisterAction :: GlobalFlags -> UserHooks -> RegisterFlags -> Args -> IO ()+unregisterAction globalFlags hooks flags args = do+ let common = registerCommonFlags flags+ verbosity = fromFlag $ setupVerbosity common+ (_lbi, common') <- getCommonFlags globalFlags hooks common args+ let flags' = flags{registerCommonFlags = common'}+ distPref = fromFlag $ setupDistPref common'+ hookedAction+ verbosity+ preUnreg+ unregHook+ postUnreg+ (getBuildConfig globalFlags hooks verbosity distPref)+ hooks+ flags'+ args++hookedAction+ :: Verbosity+ -> (UserHooks -> Args -> flags -> IO HookedBuildInfo)+ -> ( UserHooks+ -> PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> flags+ -> IO ()+ )+ -> ( UserHooks+ -> Args+ -> flags+ -> PackageDescription+ -> LocalBuildInfo+ -> IO ()+ )+ -> IO LocalBuildInfo+ -> UserHooks+ -> flags+ -> Args+ -> IO ()+hookedAction verbosity pre_hook cmd_hook =+ hookedActionWithArgs+ verbosity+ pre_hook+ ( \h _ pd lbi uh flags ->+ cmd_hook h pd lbi uh flags+ )++hookedActionWithArgs+ :: Verbosity+ -> (UserHooks -> Args -> flags -> IO HookedBuildInfo)+ -> ( UserHooks+ -> Args+ -> PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> flags+ -> IO ()+ )+ -> ( UserHooks+ -> Args+ -> flags+ -> PackageDescription+ -> LocalBuildInfo+ -> IO ()+ )+ -> IO LocalBuildInfo+ -> UserHooks+ -> flags+ -> Args+ -> IO ()+hookedActionWithArgs+ verbosity+ pre_hook+ cmd_hook+ post_hook+ get_build_config+ hooks+ flags+ args = do+ pbi <- pre_hook hooks args flags+ lbi0 <- get_build_config+ let pkg_descr0 = localPkgDescr lbi0+ sanityCheckHookedBuildInfo verbosity pkg_descr0 pbi+ let pkg_descr = updatePackageDescription pbi pkg_descr0+ lbi = lbi0{localPkgDescr = pkg_descr}+ cmd_hook hooks args pkg_descr lbi hooks flags+ post_hook hooks args flags pkg_descr lbi++sanityCheckHookedBuildInfo+ :: Verbosity -> PackageDescription -> HookedBuildInfo -> IO ()+sanityCheckHookedBuildInfo+ verbosity+ (PackageDescription{library = Nothing})+ (Just _, _) =+ dieWithException verbosity $ NoLibraryForPackage+sanityCheckHookedBuildInfo verbosity pkg_descr (_, hookExes)+ | exe1 : _ <- nonExistant =+ dieWithException verbosity $ SanityCheckHookedBuildInfo exe1+ where+ pkgExeNames = nub (map exeName (executables pkg_descr))+ hookExeNames = nub (map fst hookExes)+ nonExistant = hookExeNames \\ pkgExeNames+sanityCheckHookedBuildInfo _ _ _ = return ()++-- | Try to read the 'localBuildInfoFile'+tryGetBuildConfig+ :: GlobalFlags+ -> UserHooks+ -> Verbosity+ -> SymbolicPath Pkg (Dir Dist)+ -> IO (Either ConfigStateFileError LocalBuildInfo)+tryGetBuildConfig g u v = try . getBuildConfig g u v++-- | Read the 'localBuildInfoFile' or throw an exception.+getBuildConfig+ :: GlobalFlags+ -> UserHooks+ -> Verbosity+ -> SymbolicPath Pkg (Dir Dist)+ -> IO LocalBuildInfo+getBuildConfig globalFlags hooks verbosity distPref = do+ lbi_wo_programs <- getPersistBuildConfig mbWorkDir distPref+ -- Restore info about unconfigured programs, since it is not serialized+ let lbi =+ lbi_wo_programs+ { withPrograms =+ restoreProgramDb+ (builtinPrograms ++ hookedPrograms hooks)+ (withPrograms lbi_wo_programs)+ }++ case pkgDescrFile lbi of+ Nothing -> return lbi+ Just pkg_descr_file -> do+ outdated <- checkPersistBuildConfigOutdated mbWorkDir distPref pkg_descr_file+ if outdated+ then reconfigure pkg_descr_file lbi+ else return lbi+ where+ mbWorkDir = flagToMaybe $ globalWorkingDir globalFlags+ reconfigure :: SymbolicPath Pkg File -> LocalBuildInfo -> IO LocalBuildInfo+ reconfigure pkg_descr_file lbi = do+ notice verbosity $+ getSymbolicPath pkg_descr_file+ ++ " has been changed. "+ ++ "Re-configuring with most recently used options. "+ ++ "If this fails, please run configure manually.\n"+ let cFlags = configFlags lbi+ let cFlags' =+ cFlags+ { -- Since the list of unconfigured programs is not serialized,+ -- restore it to the same value as normally used at the beginning+ -- of a configure run:+ configPrograms_ =+ fmap+ ( restoreProgramDb+ (builtinPrograms ++ hookedPrograms hooks)+ )+ `fmap` configPrograms_ cFlags+ , configCommonFlags =+ (configCommonFlags cFlags)+ { -- Use the current, not saved verbosity level:+ setupVerbosity = Flag verbosity+ }+ }+ configureAction globalFlags hooks cFlags' (extraConfigArgs lbi)++-- --------------------------------------------------------------------------+-- Cleaning++clean :: PackageDescription -> CleanFlags -> IO ()+clean pkg_descr flags = do+ let common = cleanCommonFlags flags+ verbosity = fromFlag (setupVerbosity common)+ distPref = fromFlagOrDefault defaultDistPref $ setupDistPref common+ mbWorkDir = flagToMaybe $ setupWorkingDir common+ i = interpretSymbolicPath mbWorkDir -- See Note [Symbolic paths] in Distribution.Utils.Path+ distPath = i distPref+ notice verbosity "cleaning..."++ maybeConfig <-+ if fromFlag (cleanSaveConf flags)+ then maybeGetPersistBuildConfig mbWorkDir distPref+ else return Nothing++ -- remove the whole dist/ directory rather than tracking exactly what files+ -- we created in there.+ chattyTry "removing dist/" $ do+ exists <- doesDirectoryExist distPath+ when exists (removeDirectoryRecursive distPath)++ -- Any extra files the user wants to remove+ traverse_ (removeFileOrDirectory . i) (extraTmpFiles pkg_descr)++ -- If the user wanted to save the config, write it back+ traverse_ (writePersistBuildConfig mbWorkDir distPref) maybeConfig+ where+ removeFileOrDirectory :: FilePath -> IO ()+ removeFileOrDirectory fname = do+ isDir <- doesDirectoryExist fname+ isFile <- doesFileExist fname+ if isDir+ then removeDirectoryRecursive fname+ else when isFile $ removeFile fname++-- --------------------------------------------------------------------------+-- Default hooks++-- | Hooks that correspond to a plain instantiation of the+-- \"simple\" build system+simpleUserHooks :: UserHooks+simpleUserHooks =+ emptyUserHooks+ { confHook = configure+ , postConf = finalChecks+ , buildHook = defaultBuildHook+ , replHook = defaultReplHook+ , copyHook = \desc lbi _ f -> install desc lbi f+ , -- 'install' has correct 'copy' behavior with params+ instHook = defaultInstallHook+ , testHook = defaultTestHook+ , benchHook = defaultBenchHook+ , cleanHook = \p _ _ f -> clean p f+ , hscolourHook = \p l h f -> hscolour p l (allSuffixHandlers h) f+ , haddockHook = \p l h f -> haddock p l (allSuffixHandlers h) f+ , regHook = defaultRegHook+ , unregHook = \p l _ f -> unregister p l f+ }+ where+ finalChecks _args flags pkg_descr lbi =+ checkForeignDeps pkg_descr lbi (lessVerbose verbosity)+ where+ verbosity = fromFlag (setupVerbosity $ configCommonFlags flags)++-- | Basic autoconf 'UserHooks':+--+-- * 'postConf' runs @.\/configure@, if present.+--+-- * the pre-hooks, except for pre-conf, read additional build information from+-- /package/@.buildinfo@, if present.+--+-- Thus @configure@ can use local system information to generate+-- /package/@.buildinfo@ and possibly other files.+autoconfUserHooks :: UserHooks+autoconfUserHooks =+ simpleUserHooks+ { postConf = defaultPostConf+ , preBuild = readHookWithArgs buildCommonFlags+ , preRepl = readHookWithArgs replCommonFlags+ , preCopy = readHookWithArgs copyCommonFlags+ , preClean = readHook cleanCommonFlags+ , preInst = readHook installCommonFlags+ , preHscolour = readHook hscolourCommonFlags+ , preHaddock = readHookWithArgs haddockCommonFlags+ , preReg = readHook registerCommonFlags+ , preUnreg = readHook registerCommonFlags+ , preTest = readHookWithArgs testCommonFlags+ , preBench = readHookWithArgs benchmarkCommonFlags+ }+ where+ defaultPostConf+ :: Args+ -> ConfigFlags+ -> PackageDescription+ -> LocalBuildInfo+ -> IO ()+ defaultPostConf args flags pkg_descr lbi =+ do+ let common = configCommonFlags flags+ verbosity = fromFlag $ setupVerbosity common+ mbWorkDir = flagToMaybe $ setupWorkingDir common+ runConfigureScript+ flags+ (flagAssignment lbi)+ (withPrograms lbi)+ (hostPlatform lbi)+ pbi <- getHookedBuildInfo verbosity mbWorkDir (buildDir lbi)+ sanityCheckHookedBuildInfo verbosity pkg_descr pbi+ let pkg_descr' = updatePackageDescription pbi pkg_descr+ lbi' = lbi{localPkgDescr = pkg_descr'}+ postConf simpleUserHooks args flags pkg_descr' lbi'++ readHookWithArgs+ :: (flags -> CommonSetupFlags)+ -> Args+ -> flags+ -> IO HookedBuildInfo+ readHookWithArgs get_common_flags _args flags = do+ let common = get_common_flags flags+ verbosity = fromFlag (setupVerbosity common)+ mbWorkDir = flagToMaybe $ setupWorkingDir common+ distPref = setupDistPref common+ dist_dir <- findDistPrefOrDefault distPref+ getHookedBuildInfo verbosity mbWorkDir (dist_dir </> makeRelativePathEx "build")++ readHook+ :: (flags -> CommonSetupFlags)+ -> Args+ -> flags+ -> IO HookedBuildInfo+ readHook get_common_flags args flags = do+ let common = get_common_flags flags+ verbosity = fromFlag (setupVerbosity common)+ mbWorkDir = flagToMaybe $ setupWorkingDir common+ distPref = setupDistPref common+ noExtraFlags args+ dist_dir <- findDistPrefOrDefault distPref+ getHookedBuildInfo verbosity mbWorkDir (dist_dir </> makeRelativePathEx "build")++getHookedBuildInfo+ :: Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> SymbolicPath Pkg (Dir Build)+ -> IO HookedBuildInfo+getHookedBuildInfo verbosity mbWorkDir build_dir = do+ maybe_infoFile <- findHookedPackageDesc verbosity mbWorkDir build_dir+ case maybe_infoFile of+ Nothing -> return emptyHookedBuildInfo+ Just infoFile -> do+ info verbosity $ "Reading parameters from " ++ getSymbolicPath infoFile+ readHookedBuildInfo verbosity mbWorkDir infoFile++autoconfSetupHooks :: SetupHooks+autoconfSetupHooks =+ SetupHooks.noSetupHooks+ { SetupHooks.configureHooks =+ SetupHooks.noConfigureHooks+ { SetupHooks.postConfPackageHook = Just post_conf_pkg+ , SetupHooks.preConfComponentHook = Just pre_conf_comp+ }+ }+ where+ post_conf_pkg+ :: SetupHooks.PostConfPackageInputs+ -> IO ()+ post_conf_pkg+ ( SetupHooks.PostConfPackageInputs+ { SetupHooks.localBuildConfig =+ LBC.LocalBuildConfig{LBC.withPrograms = progs}+ , SetupHooks.packageBuildDescr =+ LBC.PackageBuildDescr+ { LBC.configFlags = cfg+ , LBC.flagAssignment = flags+ , LBC.hostPlatform = plat+ }+ }+ ) = runConfigureScript cfg flags progs plat++ pre_conf_comp+ :: SetupHooks.PreConfComponentInputs+ -> IO SetupHooks.PreConfComponentOutputs+ pre_conf_comp+ ( SetupHooks.PreConfComponentInputs+ { SetupHooks.packageBuildDescr =+ LBC.PackageBuildDescr+ { LBC.configFlags = cfg+ , localPkgDescr = pkg_descr+ }+ , SetupHooks.component = component+ }+ ) = do+ let verbosity = fromFlag $ configVerbosity cfg+ mbWorkDir = flagToMaybe $ configWorkingDir cfg+ distPref = configDistPref cfg+ dist_dir <- findDistPrefOrDefault distPref+ -- Read the ".buildinfo" file and use that to update+ -- the components (main library + executables only).+ hbi <- getHookedBuildInfo verbosity mbWorkDir (dist_dir </> makeRelativePathEx "build")+ sanityCheckHookedBuildInfo verbosity pkg_descr hbi+ -- SetupHooks TODO: we are reading getHookedBuildInfo once+ -- for each component. I think this is inherent to the SetupHooks+ -- approach.+ let comp_name = componentName component+ diff <- case SetupHooks.hookedBuildInfoComponentDiff_maybe hbi comp_name of+ Nothing -> return $ SetupHooks.emptyComponentDiff comp_name+ Just do_diff -> do_diff+ return $+ SetupHooks.PreConfComponentOutputs+ { SetupHooks.componentDiff = diff+ }++defaultTestHook+ :: Args+ -> PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> TestFlags+ -> IO ()+defaultTestHook args pkg_descr localbuildinfo _ flags =+ test args pkg_descr localbuildinfo flags++defaultBenchHook+ :: Args+ -> PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> BenchmarkFlags+ -> IO ()+defaultBenchHook args pkg_descr localbuildinfo _ flags =+ bench args pkg_descr localbuildinfo flags++defaultInstallHook+ :: PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> InstallFlags+ -> IO ()+defaultInstallHook =+ defaultInstallHook_setupHooks SetupHooks.noInstallHooks++defaultInstallHook_setupHooks+ :: SetupHooks.InstallHooks+ -> PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> InstallFlags+ -> IO ()+defaultInstallHook_setupHooks inst_hooks pkg_descr localbuildinfo _ flags = do+ let copyFlags =+ defaultCopyFlags+ { copyDest = installDest flags+ , copyCommonFlags = installCommonFlags flags+ }+ install_setupHooks inst_hooks pkg_descr localbuildinfo copyFlags+ let registerFlags =+ defaultRegisterFlags+ { regInPlace = installInPlace flags+ , regPackageDB = installPackageDB flags+ , registerCommonFlags = installCommonFlags flags+ }+ when (hasLibs pkg_descr) $+ register pkg_descr localbuildinfo registerFlags++defaultBuildHook+ :: PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> BuildFlags+ -> IO ()+defaultBuildHook pkg_descr localbuildinfo hooks flags =+ build pkg_descr localbuildinfo flags (allSuffixHandlers hooks)++defaultReplHook+ :: PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> ReplFlags+ -> [String]+ -> IO ()+defaultReplHook pkg_descr localbuildinfo hooks flags args =+ repl pkg_descr localbuildinfo flags (allSuffixHandlers hooks) args++defaultRegHook+ :: PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> RegisterFlags+ -> IO ()+defaultRegHook pkg_descr localbuildinfo _ flags =+ if hasLibs pkg_descr+ then register pkg_descr localbuildinfo flags+ else+ setupMessage+ (fromFlag (setupVerbosity $ registerCommonFlags flags))+ "Package contains no library to register:"+ (packageId pkg_descr)
src/Distribution/Simple/Bench.hs view
@@ -1,7 +1,9 @@+{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Bench -- Copyright : Johan Tibell 2011@@ -13,113 +15,166 @@ -- This is the entry point into running the benchmarks in a built -- package. It performs the \"@.\/setup bench@\" action. It runs -- benchmarks designated in the package description.- module Distribution.Simple.Bench- ( bench- ) where+ ( bench+ ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -import Distribution.Types.UnqualComponentName import qualified Distribution.PackageDescription as PD+import Distribution.Pretty+import Distribution.Simple.Build (addInternalBuildTools) import Distribution.Simple.BuildPaths import Distribution.Simple.Compiler+import Distribution.Simple.Errors import Distribution.Simple.InstallDirs import qualified Distribution.Simple.LocalBuildInfo as LBI-import Distribution.Simple.Setup+import Distribution.Simple.Program.Db+import Distribution.Simple.Program.Find+import Distribution.Simple.Program.Run+import Distribution.Simple.Setup.Benchmark+import Distribution.Simple.Setup.Common import Distribution.Simple.UserHooks import Distribution.Simple.Utils-import Distribution.Pretty+import Distribution.System (Platform (Platform))+import Distribution.Types.Benchmark (Benchmark (benchmarkBuildInfo))+import Distribution.Types.UnqualComponentName+import Distribution.Utils.Path -import System.Directory ( doesFileExist )-import System.FilePath ( (</>), (<.>) )+import System.Directory (doesFileExist) -- | Perform the \"@.\/setup bench@\" action.-bench :: Args -- ^positional command-line arguments- -> PD.PackageDescription -- ^information from the .cabal file- -> LBI.LocalBuildInfo -- ^information from the configure step- -> BenchmarkFlags -- ^flags sent to benchmark- -> IO ()+bench+ :: Args+ -- ^ positional command-line arguments+ -> PD.PackageDescription+ -- ^ information from the .cabal file+ -> LBI.LocalBuildInfo+ -- ^ information from the configure step+ -> BenchmarkFlags+ -- ^ flags sent to benchmark+ -> IO () bench args pkg_descr lbi flags = do- let verbosity = fromFlag $ benchmarkVerbosity flags- benchmarkNames = args- pkgBenchmarks = PD.benchmarks pkg_descr- enabledBenchmarks = map fst (LBI.enabledBenchLBIs pkg_descr lbi)+ curDir <- LBI.absoluteWorkingDirLBI lbi+ let verbosity = fromFlag $ benchmarkVerbosity flags+ benchmarkNames = args+ pkgBenchmarks = PD.benchmarks pkg_descr+ enabledBenchmarks = LBI.enabledBenchLBIs pkg_descr lbi+ mbWorkDir = flagToMaybe $ benchmarkWorkingDir flags+ i = interpretSymbolicPath mbWorkDir -- See Note [Symbolic paths] in Distribution.Utils.Path - -- Run the benchmark- doBench :: PD.Benchmark -> IO ExitCode- doBench bm =- case PD.benchmarkInterface bm of- PD.BenchmarkExeV10 _ _ -> do- let cmd = LBI.buildDir lbi </> name </> name <.> exeExtension (LBI.hostPlatform lbi)- options = map (benchOption pkg_descr lbi bm) $- benchmarkOptions flags- -- Check that the benchmark executable exists.- exists <- doesFileExist cmd- unless exists $ die' verbosity $- "Error: Could not find benchmark program \""- ++ cmd ++ "\". Did you build the package first?"+ -- Run the benchmark+ doBench :: (PD.Benchmark, LBI.ComponentLocalBuildInfo) -> IO ExitCode+ doBench (bm, clbi) = do+ let lbiForBench =+ lbi+ { -- Include any build-tool-depends on build tools internal to the current package.+ LBI.withPrograms =+ addInternalBuildTools+ curDir+ pkg_descr+ lbi+ (benchmarkBuildInfo bm)+ (LBI.withPrograms lbi)+ }+ case PD.benchmarkInterface bm of+ PD.BenchmarkExeV10 _ _ -> do+ let cmd = i $ LBI.buildDir lbiForBench </> makeRelativePathEx (name </> name <.> exeExtension (LBI.hostPlatform lbi))+ options =+ map (benchOption pkg_descr lbiForBench bm) $+ benchmarkOptions flags+ -- Check that the benchmark executable exists.+ exists <- doesFileExist cmd+ unless exists $+ dieWithException verbosity $+ NoBenchMarkProgram cmd - notice verbosity $ startMessage name- -- This will redirect the child process- -- stdout/stderr to the parent process.- exitcode <- rawSystemExitCode verbosity cmd options- notice verbosity $ finishMessage name exitcode- return exitcode+ -- Compute the appropriate environment for running the benchmark+ let progDb = LBI.withPrograms lbiForBench+ pathVar = progSearchPath progDb+ envOverrides = progOverrideEnv progDb+ newPath <- programSearchPathAsPATHVar pathVar+ shellEnv <- getFullEnvironment ([("PATH", Just newPath)] ++ envOverrides) - _ -> do- notice verbosity $ "No support for running "- ++ "benchmark " ++ name ++ " of type: "- ++ prettyShow (PD.benchmarkType bm)- exitFailure- where name = unUnqualComponentName $ PD.benchmarkName bm+ -- Add (DY)LD_LIBRARY_PATH if needed+ shellEnv' <-+ if LBI.withDynExe lbiForBench+ then do+ let (Platform _ os) = LBI.hostPlatform lbiForBench+ paths <- LBI.depLibraryPaths True False lbiForBench clbi+ return (addLibraryPath os paths shellEnv)+ else return shellEnv - unless (PD.hasBenchmarks pkg_descr) $ do- notice verbosity "Package has no benchmarks."- exitSuccess+ notice verbosity $ startMessage name+ -- This will redirect the child process+ -- stdout/stderr to the parent process.+ exitcode <- rawSystemExitCode verbosity mbWorkDir cmd options (Just shellEnv')+ notice verbosity $ finishMessage name exitcode+ return exitcode+ _ -> do+ notice verbosity $+ "No support for running "+ ++ "benchmark "+ ++ name+ ++ " of type: "+ ++ prettyShow (PD.benchmarkType bm)+ exitFailure+ where+ name = unUnqualComponentName $ PD.benchmarkName bm - when (PD.hasBenchmarks pkg_descr && null enabledBenchmarks) $- die' verbosity $ "No benchmarks enabled. Did you remember to configure with "- ++ "\'--enable-benchmarks\'?"+ unless (PD.hasBenchmarks pkg_descr) $ do+ notice verbosity "Package has no benchmarks."+ exitSuccess - bmsToRun <- case benchmarkNames of- [] -> return enabledBenchmarks- names -> for names $ \bmName ->- let benchmarkMap = zip enabledNames enabledBenchmarks- enabledNames = map PD.benchmarkName enabledBenchmarks- allNames = map PD.benchmarkName pkgBenchmarks- in case lookup (mkUnqualComponentName bmName) benchmarkMap of- Just t -> return t- _ | mkUnqualComponentName bmName `elem` allNames ->- die' verbosity $ "Package configured with benchmark "- ++ bmName ++ " disabled."- | otherwise -> die' verbosity $ "no such benchmark: " ++ bmName+ when (PD.hasBenchmarks pkg_descr && null enabledBenchmarks) $+ dieWithException verbosity EnableBenchMark - let totalBenchmarks = length bmsToRun- notice verbosity $ "Running " ++ show totalBenchmarks ++ " benchmarks..."- exitcodes <- traverse doBench bmsToRun- let allOk = totalBenchmarks == length (filter (== ExitSuccess) exitcodes)- unless allOk exitFailure+ bmsToRun <- case benchmarkNames of+ [] -> return enabledBenchmarks+ names -> for names $ \bmName ->+ let benchmarkMap = zip enabledNames enabledBenchmarks+ enabledNames = map (PD.benchmarkName . fst) enabledBenchmarks+ allNames = map PD.benchmarkName pkgBenchmarks+ in case lookup (mkUnqualComponentName bmName) benchmarkMap of+ Just t -> return t+ _+ | mkUnqualComponentName bmName `elem` allNames ->+ dieWithException verbosity $ BenchMarkNameDisabled bmName+ | otherwise -> dieWithException verbosity $ NoBenchMark bmName++ let totalBenchmarks = length bmsToRun+ notice verbosity $ "Running " ++ show totalBenchmarks ++ " benchmarks..."+ exitcodes <- traverse doBench bmsToRun++ let allOk = totalBenchmarks == length (filter (== ExitSuccess) exitcodes)+ unless allOk exitFailure where startMessage name = "Benchmark " ++ name ++ ": RUNNING...\n"- finishMessage name exitcode = "Benchmark " ++ name ++ ": "- ++ (case exitcode of- ExitSuccess -> "FINISH"- ExitFailure _ -> "ERROR")-+ finishMessage name exitcode =+ "Benchmark "+ ++ name+ ++ ": "+ ++ ( case exitcode of+ ExitSuccess -> "FINISH"+ ExitFailure _ -> "ERROR"+ ) -- TODO: This is abusing the notion of a 'PathTemplate'. The result isn't -- necessarily a path.-benchOption :: PD.PackageDescription- -> LBI.LocalBuildInfo- -> PD.Benchmark- -> PathTemplate- -> String+benchOption+ :: PD.PackageDescription+ -> LBI.LocalBuildInfo+ -> PD.Benchmark+ -> PathTemplate+ -> String benchOption pkg_descr lbi bm template =- fromPathTemplate $ substPathTemplate env template+ fromPathTemplate $ substPathTemplate env template where- env = initialPathTemplateEnv- (PD.package pkg_descr) (LBI.localUnitId lbi)- (compilerInfo $ LBI.compiler lbi) (LBI.hostPlatform lbi) ++- [(BenchmarkNameVar, toPathTemplate $ unUnqualComponentName $ PD.benchmarkName bm)]+ env =+ initialPathTemplateEnv+ (PD.package pkg_descr)+ (LBI.localUnitId lbi)+ (compilerInfo $ LBI.compiler lbi)+ (LBI.hostPlatform lbi)+ ++ [(BenchmarkNameVar, toPathTemplate $ unUnqualComponentName $ PD.benchmarkName bm)]
src/Distribution/Simple/Build.hs view
@@ -1,734 +1,1240 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}---------------------------------------------------------------------------------- |--- Module : Distribution.Simple.Build--- Copyright : Isaac Jones 2003-2005,--- Ross Paterson 2006,--- Duncan Coutts 2007-2008, 2012--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ This is the entry point to actually building the modules in a package. It--- doesn't actually do much itself, most of the work is delegated to--- compiler-specific actions. It does do some non-compiler specific bits like--- running pre-processors.-----module Distribution.Simple.Build (- build, showBuildInfo, repl,- startInterpreter,-- initialBuildSteps,- createInternalPackageDB,- componentInitialBuildSteps,- writeAutogenFiles,- ) where--import Prelude ()-import Distribution.Compat.Prelude-import Distribution.Utils.Generic--import Distribution.Types.ComponentLocalBuildInfo-import Distribution.Types.ComponentRequestedSpec-import Distribution.Types.Dependency-import Distribution.Types.ExecutableScope-import Distribution.Types.ForeignLib-import Distribution.Types.LibraryVisibility-import Distribution.Types.LocalBuildInfo-import Distribution.Types.MungedPackageId-import Distribution.Types.MungedPackageName-import Distribution.Types.ModuleRenaming-import Distribution.Types.TargetInfo-import Distribution.Utils.Path--import Distribution.Package-import Distribution.Backpack-import Distribution.Backpack.DescribeUnitId-import qualified Distribution.Simple.GHC as GHC-import qualified Distribution.Simple.GHCJS as GHCJS-import qualified Distribution.Simple.UHC as UHC-import qualified Distribution.Simple.HaskellSuite as HaskellSuite-import qualified Distribution.Simple.PackageIndex as Index--import Distribution.Simple.Build.Macros (generateCabalMacrosHeader)-import Distribution.Simple.Build.PathsModule (generatePathsModule)-import qualified Distribution.Simple.Program.HcPkg as HcPkg--import Distribution.Simple.Compiler-import Distribution.PackageDescription-import qualified Distribution.InstalledPackageInfo as IPI-import Distribution.InstalledPackageInfo (InstalledPackageInfo)-import qualified Distribution.ModuleName as ModuleName--import Distribution.Simple.Setup-import Distribution.Simple.BuildTarget-import Distribution.Simple.BuildToolDepends-import Distribution.Simple.PreProcess-import Distribution.Simple.LocalBuildInfo-import Distribution.Simple.Program.Types-import Distribution.Simple.Program.Db-import Distribution.Simple.ShowBuildInfo-import Distribution.Simple.BuildPaths-import Distribution.Simple.Configure-import Distribution.Simple.Register-import Distribution.Simple.Test.LibV09-import Distribution.Simple.Utils-import Distribution.Simple.Utils.Json--import Distribution.System-import Distribution.Pretty-import Distribution.Verbosity-import Distribution.Version (thisVersion)--import Distribution.Compat.Graph (IsNode(..))--import Control.Monad-import qualified Data.Set as Set-import System.FilePath ( (</>), (<.>), takeDirectory )-import System.Directory ( getCurrentDirectory )---- -------------------------------------------------------------------------------- |Build the libraries and executables in this package.--build :: PackageDescription -- ^ Mostly information from the .cabal file- -> LocalBuildInfo -- ^ Configuration information- -> BuildFlags -- ^ Flags that the user passed to build- -> [ PPSuffixHandler ] -- ^ preprocessors to run before compiling- -> IO ()-build pkg_descr lbi flags suffixes = do- targets <- readTargetInfos verbosity pkg_descr lbi (buildArgs flags)- let componentsToBuild = neededTargetsInBuildOrder' pkg_descr lbi (map nodeKey targets)- info verbosity $ "Component build order: "- ++ intercalate ", "- (map (showComponentName . componentLocalName . targetCLBI)- componentsToBuild)-- when (null targets) $- -- Only bother with this message if we're building the whole package- setupMessage verbosity "Building" (packageId pkg_descr)-- internalPackageDB <- createInternalPackageDB verbosity lbi distPref-- (\f -> foldM_ f (installedPkgs lbi) componentsToBuild) $ \index target -> do- let comp = targetComponent target- clbi = targetCLBI target- componentInitialBuildSteps distPref pkg_descr lbi clbi verbosity- let bi = componentBuildInfo comp- progs' = addInternalBuildTools pkg_descr lbi bi (withPrograms lbi)- lbi' = lbi {- withPrograms = progs',- withPackageDB = withPackageDB lbi ++ [internalPackageDB],- installedPkgs = index- }- mb_ipi <- buildComponent verbosity (buildNumJobs flags) pkg_descr- lbi' suffixes comp clbi distPref- return (maybe index (Index.insert `flip` index) mb_ipi)- return ()- where- distPref = fromFlag (buildDistPref flags)- verbosity = fromFlag (buildVerbosity flags)---showBuildInfo :: PackageDescription -- ^ Mostly information from the .cabal file- -> LocalBuildInfo -- ^ Configuration information- -> BuildFlags -- ^ Flags that the user passed to build- -> IO String-showBuildInfo pkg_descr lbi flags = do- let verbosity = fromFlag (buildVerbosity flags)- targets <- readTargetInfos verbosity pkg_descr lbi (buildArgs flags)- let targetsToBuild = neededTargetsInBuildOrder' pkg_descr lbi (map nodeKey targets)- doc = mkBuildInfo pkg_descr lbi flags targetsToBuild- return $ renderJson doc ""---repl :: PackageDescription -- ^ Mostly information from the .cabal file- -> LocalBuildInfo -- ^ Configuration information- -> ReplFlags -- ^ Flags that the user passed to build- -> [ PPSuffixHandler ] -- ^ preprocessors to run before compiling- -> [String]- -> IO ()-repl pkg_descr lbi flags suffixes args = do- let distPref = fromFlag (replDistPref flags)- verbosity = fromFlag (replVerbosity flags)-- target <- readTargetInfos verbosity pkg_descr lbi args >>= \r -> case r of- -- This seems DEEPLY questionable.- [] -> case allTargetsInBuildOrder' pkg_descr lbi of- (target:_) -> return target- [] -> die' verbosity $ "Failed to determine target."- [target] -> return target- _ -> die' verbosity $ "The 'repl' command does not support multiple targets at once."- let componentsToBuild = neededTargetsInBuildOrder' pkg_descr lbi [nodeKey target]- debug verbosity $ "Component build order: "- ++ intercalate ", "- (map (showComponentName . componentLocalName . targetCLBI)- componentsToBuild)-- internalPackageDB <- createInternalPackageDB verbosity lbi distPref-- let lbiForComponent comp lbi' =- lbi' {- withPackageDB = withPackageDB lbi ++ [internalPackageDB],- withPrograms = addInternalBuildTools pkg_descr lbi'- (componentBuildInfo comp) (withPrograms lbi')- }-- -- build any dependent components- sequence_- [ do let clbi = targetCLBI subtarget- comp = targetComponent subtarget- lbi' = lbiForComponent comp lbi- componentInitialBuildSteps distPref pkg_descr lbi clbi verbosity- buildComponent verbosity NoFlag- pkg_descr lbi' suffixes comp clbi distPref- | subtarget <- safeInit componentsToBuild ]-- -- REPL for target components- let clbi = targetCLBI target- comp = targetComponent target- lbi' = lbiForComponent comp lbi- replFlags = replReplOptions flags- componentInitialBuildSteps distPref pkg_descr lbi clbi verbosity- replComponent replFlags verbosity pkg_descr lbi' suffixes comp clbi distPref----- | Start an interpreter without loading any package files.-startInterpreter :: Verbosity -> ProgramDb -> Compiler -> Platform- -> PackageDBStack -> IO ()-startInterpreter verbosity programDb comp platform packageDBs =- case compilerFlavor comp of- GHC -> GHC.startInterpreter verbosity programDb comp platform packageDBs- GHCJS -> GHCJS.startInterpreter verbosity programDb comp platform packageDBs- _ -> die' verbosity "A REPL is not supported with this compiler."--buildComponent :: Verbosity- -> Flag (Maybe Int)- -> PackageDescription- -> LocalBuildInfo- -> [PPSuffixHandler]- -> Component- -> ComponentLocalBuildInfo- -> FilePath- -> IO (Maybe InstalledPackageInfo)-buildComponent verbosity numJobs pkg_descr lbi suffixes- comp@(CLib lib) clbi distPref = do- preprocessComponent pkg_descr comp lbi clbi False verbosity suffixes- extras <- preprocessExtras verbosity comp lbi- setupMessage' verbosity "Building" (packageId pkg_descr)- (componentLocalName clbi) (maybeComponentInstantiatedWith clbi)- let libbi = libBuildInfo lib- lib' = lib { libBuildInfo = flip addExtraAsmSources extras- $ flip addExtraCmmSources extras- $ flip addExtraCxxSources extras- $ flip addExtraCSources extras- $ libbi- }-- buildLib verbosity numJobs pkg_descr lbi lib' clbi-- let oneComponentRequested (OneComponentRequestedSpec _) = True- oneComponentRequested _ = False- -- Don't register inplace if we're only building a single component;- -- it's not necessary because there won't be any subsequent builds- -- that need to tag us- if (not (oneComponentRequested (componentEnabledSpec lbi)))- then do- -- Register the library in-place, so exes can depend- -- on internally defined libraries.- pwd <- getCurrentDirectory- let -- The in place registration uses the "-inplace" suffix, not an ABI hash- installedPkgInfo = inplaceInstalledPackageInfo pwd distPref pkg_descr- -- NB: Use a fake ABI hash to avoid- -- needing to recompute it every build.- (mkAbiHash "inplace") lib' lbi clbi-- debug verbosity $ "Registering inplace:\n" ++ (IPI.showInstalledPackageInfo installedPkgInfo)- registerPackage verbosity (compiler lbi) (withPrograms lbi)- (withPackageDB lbi) installedPkgInfo- HcPkg.defaultRegisterOptions {- HcPkg.registerMultiInstance = True- }- return (Just installedPkgInfo)- else return Nothing--buildComponent verbosity numJobs pkg_descr lbi suffixes- comp@(CFLib flib) clbi _distPref = do- preprocessComponent pkg_descr comp lbi clbi False verbosity suffixes- setupMessage' verbosity "Building" (packageId pkg_descr)- (componentLocalName clbi) (maybeComponentInstantiatedWith clbi)- buildFLib verbosity numJobs pkg_descr lbi flib clbi- return Nothing--buildComponent verbosity numJobs pkg_descr lbi suffixes- comp@(CExe exe) clbi _ = do- preprocessComponent pkg_descr comp lbi clbi False verbosity suffixes- extras <- preprocessExtras verbosity comp lbi- setupMessage' verbosity "Building" (packageId pkg_descr)- (componentLocalName clbi) (maybeComponentInstantiatedWith clbi)- let ebi = buildInfo exe- exe' = exe { buildInfo = addExtraCSources ebi extras }- buildExe verbosity numJobs pkg_descr lbi exe' clbi- return Nothing---buildComponent verbosity numJobs pkg_descr lbi suffixes- comp@(CTest test@TestSuite { testInterface = TestSuiteExeV10{} })- clbi _distPref = do- let exe = testSuiteExeV10AsExe test- preprocessComponent pkg_descr comp lbi clbi False verbosity suffixes- extras <- preprocessExtras verbosity comp lbi- setupMessage' verbosity "Building" (packageId pkg_descr)- (componentLocalName clbi) (maybeComponentInstantiatedWith clbi)- let ebi = buildInfo exe- exe' = exe { buildInfo = addExtraCSources ebi extras }- buildExe verbosity numJobs pkg_descr lbi exe' clbi- return Nothing---buildComponent verbosity numJobs pkg_descr lbi0 suffixes- comp@(CTest- test@TestSuite { testInterface = TestSuiteLibV09{} })- clbi -- This ComponentLocalBuildInfo corresponds to a detailed- -- test suite and not a real component. It should not- -- be used, except to construct the CLBIs for the- -- library and stub executable that will actually be- -- built.- distPref = do- pwd <- getCurrentDirectory- let (pkg, lib, libClbi, lbi, ipi, exe, exeClbi) =- testSuiteLibV09AsLibAndExe pkg_descr test clbi lbi0 distPref pwd- preprocessComponent pkg_descr comp lbi clbi False verbosity suffixes- extras <- preprocessExtras verbosity comp lbi- setupMessage' verbosity "Building" (packageId pkg_descr)- (componentLocalName clbi) (maybeComponentInstantiatedWith clbi)- buildLib verbosity numJobs pkg lbi lib libClbi- -- NB: need to enable multiple instances here, because on 7.10+- -- the package name is the same as the library, and we still- -- want the registration to go through.- registerPackage verbosity (compiler lbi) (withPrograms lbi)- (withPackageDB lbi) ipi- HcPkg.defaultRegisterOptions {- HcPkg.registerMultiInstance = True- }- let ebi = buildInfo exe- -- NB: The stub executable is linked against the test-library- -- which already contains all `other-modules`, so we need- -- to remove those from the stub-exe's build-info- exe' = exe { buildInfo = (addExtraCSources ebi extras) { otherModules = [] } }- buildExe verbosity numJobs pkg_descr lbi exe' exeClbi- return Nothing -- Can't depend on test suite---buildComponent verbosity _ _ _ _- (CTest TestSuite { testInterface = TestSuiteUnsupported tt })- _ _ =- die' verbosity $ "No support for building test suite type " ++ prettyShow tt---buildComponent verbosity numJobs pkg_descr lbi suffixes- comp@(CBench bm@Benchmark { benchmarkInterface = BenchmarkExeV10 {} })- clbi _distPref = do- let exe = benchmarkExeV10asExe bm- preprocessComponent pkg_descr comp lbi clbi False verbosity suffixes- extras <- preprocessExtras verbosity comp lbi- setupMessage' verbosity "Building" (packageId pkg_descr)- (componentLocalName clbi) (maybeComponentInstantiatedWith clbi)- let ebi = buildInfo exe- exe' = exe { buildInfo = addExtraCSources ebi extras }- buildExe verbosity numJobs pkg_descr lbi exe' clbi- return Nothing---buildComponent verbosity _ _ _ _- (CBench Benchmark { benchmarkInterface = BenchmarkUnsupported tt })- _ _ =- die' verbosity $ "No support for building benchmark type " ++ prettyShow tt----- | Add extra C sources generated by preprocessing to build--- information.-addExtraCSources :: BuildInfo -> [FilePath] -> BuildInfo-addExtraCSources bi extras = bi { cSources = new }- where new = Set.toList $ old `Set.union` exs- old = Set.fromList $ cSources bi- exs = Set.fromList extras----- | Add extra C++ sources generated by preprocessing to build--- information.-addExtraCxxSources :: BuildInfo -> [FilePath] -> BuildInfo-addExtraCxxSources bi extras = bi { cxxSources = new }- where new = Set.toList $ old `Set.union` exs- old = Set.fromList $ cxxSources bi- exs = Set.fromList extras----- | Add extra C-- sources generated by preprocessing to build--- information.-addExtraCmmSources :: BuildInfo -> [FilePath] -> BuildInfo-addExtraCmmSources bi extras = bi { cmmSources = new }- where new = Set.toList $ old `Set.union` exs- old = Set.fromList $ cmmSources bi- exs = Set.fromList extras----- | Add extra ASM sources generated by preprocessing to build--- information.-addExtraAsmSources :: BuildInfo -> [FilePath] -> BuildInfo-addExtraAsmSources bi extras = bi { asmSources = new }- where new = Set.toList $ old `Set.union` exs- old = Set.fromList $ asmSources bi- exs = Set.fromList extras---replComponent :: [String]- -> Verbosity- -> PackageDescription- -> LocalBuildInfo- -> [PPSuffixHandler]- -> Component- -> ComponentLocalBuildInfo- -> FilePath- -> IO ()-replComponent replFlags verbosity pkg_descr lbi suffixes- comp@(CLib lib) clbi _ = do- preprocessComponent pkg_descr comp lbi clbi False verbosity suffixes- extras <- preprocessExtras verbosity comp lbi- let libbi = libBuildInfo lib- lib' = lib { libBuildInfo = libbi { cSources = cSources libbi ++ extras } }- replLib replFlags verbosity pkg_descr lbi lib' clbi--replComponent replFlags verbosity pkg_descr lbi suffixes- comp@(CFLib flib) clbi _ = do- preprocessComponent pkg_descr comp lbi clbi False verbosity suffixes- replFLib replFlags verbosity pkg_descr lbi flib clbi--replComponent replFlags verbosity pkg_descr lbi suffixes- comp@(CExe exe) clbi _ = do- preprocessComponent pkg_descr comp lbi clbi False verbosity suffixes- extras <- preprocessExtras verbosity comp lbi- let ebi = buildInfo exe- exe' = exe { buildInfo = ebi { cSources = cSources ebi ++ extras } }- replExe replFlags verbosity pkg_descr lbi exe' clbi---replComponent replFlags verbosity pkg_descr lbi suffixes- comp@(CTest test@TestSuite { testInterface = TestSuiteExeV10{} })- clbi _distPref = do- let exe = testSuiteExeV10AsExe test- preprocessComponent pkg_descr comp lbi clbi False verbosity suffixes- extras <- preprocessExtras verbosity comp lbi- let ebi = buildInfo exe- exe' = exe { buildInfo = ebi { cSources = cSources ebi ++ extras } }- replExe replFlags verbosity pkg_descr lbi exe' clbi---replComponent replFlags verbosity pkg_descr lbi0 suffixes- comp@(CTest- test@TestSuite { testInterface = TestSuiteLibV09{} })- clbi distPref = do- pwd <- getCurrentDirectory- let (pkg, lib, libClbi, lbi, _, _, _) =- testSuiteLibV09AsLibAndExe pkg_descr test clbi lbi0 distPref pwd- preprocessComponent pkg_descr comp lbi clbi False verbosity suffixes- extras <- preprocessExtras verbosity comp lbi- let libbi = libBuildInfo lib- lib' = lib { libBuildInfo = libbi { cSources = cSources libbi ++ extras } }- replLib replFlags verbosity pkg lbi lib' libClbi---replComponent _ verbosity _ _ _- (CTest TestSuite { testInterface = TestSuiteUnsupported tt })- _ _ =- die' verbosity $ "No support for building test suite type " ++ prettyShow tt---replComponent replFlags verbosity pkg_descr lbi suffixes- comp@(CBench bm@Benchmark { benchmarkInterface = BenchmarkExeV10 {} })- clbi _distPref = do- let exe = benchmarkExeV10asExe bm- preprocessComponent pkg_descr comp lbi clbi False verbosity suffixes- extras <- preprocessExtras verbosity comp lbi- let ebi = buildInfo exe- exe' = exe { buildInfo = ebi { cSources = cSources ebi ++ extras } }- replExe replFlags verbosity pkg_descr lbi exe' clbi---replComponent _ verbosity _ _ _- (CBench Benchmark { benchmarkInterface = BenchmarkUnsupported tt })- _ _ =- die' verbosity $ "No support for building benchmark type " ++ prettyShow tt--------------------------------------------------------- Shared code for buildComponent and replComponent------- | Translate a exe-style 'TestSuite' component into an exe for building-testSuiteExeV10AsExe :: TestSuite -> Executable-testSuiteExeV10AsExe test@TestSuite { testInterface = TestSuiteExeV10 _ mainFile } =- Executable {- exeName = testName test,- modulePath = mainFile,- exeScope = ExecutablePublic,- buildInfo = testBuildInfo test- }-testSuiteExeV10AsExe TestSuite{} = error "testSuiteExeV10AsExe: wrong kind"---- | Translate a exe-style 'Benchmark' component into an exe for building-benchmarkExeV10asExe :: Benchmark -> Executable-benchmarkExeV10asExe bm@Benchmark { benchmarkInterface = BenchmarkExeV10 _ mainFile } =- Executable {- exeName = benchmarkName bm,- modulePath = mainFile,- exeScope = ExecutablePublic,- buildInfo = benchmarkBuildInfo bm- }-benchmarkExeV10asExe Benchmark{} = error "benchmarkExeV10asExe: wrong kind"---- | Translate a lib-style 'TestSuite' component into a lib + exe for building-testSuiteLibV09AsLibAndExe :: PackageDescription- -> TestSuite- -> ComponentLocalBuildInfo- -> LocalBuildInfo- -> FilePath- -> FilePath- -> (PackageDescription,- Library, ComponentLocalBuildInfo,- LocalBuildInfo,- IPI.InstalledPackageInfo,- Executable, ComponentLocalBuildInfo)-testSuiteLibV09AsLibAndExe pkg_descr- test@TestSuite { testInterface = TestSuiteLibV09 _ m }- clbi lbi distPref pwd =- (pkg, lib, libClbi, lbi, ipi, exe, exeClbi)- where- bi = testBuildInfo test- lib = Library {- libName = LMainLibName,- exposedModules = [ m ],- reexportedModules = [],- signatures = [],- libExposed = True,- libVisibility = LibraryVisibilityPrivate,- libBuildInfo = bi- }- -- This is, like, the one place where we use a CTestName for a library.- -- Should NOT use library name, since that could conflict!- PackageIdentifier pkg_name pkg_ver = package pkg_descr- -- Note: we do make internal library from the test!- compat_name = MungedPackageName pkg_name (LSubLibName (testName test))- compat_key = computeCompatPackageKey (compiler lbi) compat_name pkg_ver (componentUnitId clbi)- libClbi = LibComponentLocalBuildInfo- { componentPackageDeps = componentPackageDeps clbi- , componentInternalDeps = componentInternalDeps clbi- , componentIsIndefinite_ = False- , componentExeDeps = componentExeDeps clbi- , componentLocalName = CLibName $ LSubLibName $ testName test- , componentIsPublic = False- , componentIncludes = componentIncludes clbi- , componentUnitId = componentUnitId clbi- , componentComponentId = componentComponentId clbi- , componentInstantiatedWith = []- , componentCompatPackageName = compat_name- , componentCompatPackageKey = compat_key- , componentExposedModules = [IPI.ExposedModule m Nothing]- }- pkgName' = mkPackageName $ prettyShow compat_name- pkg = pkg_descr {- package = (package pkg_descr) { pkgName = pkgName' }- , executables = []- , testSuites = []- , subLibraries = [lib]- }- ipi = inplaceInstalledPackageInfo pwd distPref pkg (mkAbiHash "") lib lbi libClbi- testDir = buildDir lbi </> stubName test- </> stubName test ++ "-tmp"- testLibDep = Dependency- pkgName'- (thisVersion $ pkgVersion $ package pkg_descr)- mainLibSet- exe = Executable {- exeName = mkUnqualComponentName $ stubName test,- modulePath = stubFilePath test,- exeScope = ExecutablePublic,- buildInfo = (testBuildInfo test) {- hsSourceDirs = [ unsafeMakeSymbolicPath testDir ],- targetBuildDepends = testLibDep- : (targetBuildDepends $ testBuildInfo test)- }- }- -- | The stub executable needs a new 'ComponentLocalBuildInfo'- -- that exposes the relevant test suite library.- deps = (IPI.installedUnitId ipi, mungedId ipi)- : (filter (\(_, x) -> let name = prettyShow $ mungedName x- in name == "Cabal" || name == "base")- (componentPackageDeps clbi))- exeClbi = ExeComponentLocalBuildInfo {- -- TODO: this is a hack, but as long as this is unique- -- (doesn't clobber something) we won't run into trouble- componentUnitId = mkUnitId (stubName test),- componentComponentId = mkComponentId (stubName test),- componentInternalDeps = [componentUnitId clbi],- componentExeDeps = [],- componentLocalName = CExeName $ mkUnqualComponentName $ stubName test,- componentPackageDeps = deps,- -- Assert DefUnitId invariant!- -- Executable can't be indefinite, so dependencies must- -- be definite packages.- componentIncludes = zip (map (DefiniteUnitId . unsafeMkDefUnitId . fst) deps)- (repeat defaultRenaming)- }-testSuiteLibV09AsLibAndExe _ TestSuite{} _ _ _ _ = error "testSuiteLibV09AsLibAndExe: wrong kind"----- | Initialize a new package db file for libraries defined--- internally to the package.-createInternalPackageDB :: Verbosity -> LocalBuildInfo -> FilePath- -> IO PackageDB-createInternalPackageDB verbosity lbi distPref = do- existsAlready <- doesPackageDBExist dbPath- when existsAlready $ deletePackageDB dbPath- createPackageDB verbosity (compiler lbi) (withPrograms lbi) False dbPath- return (SpecificPackageDB dbPath)- where- dbPath = internalPackageDBPath lbi distPref--addInternalBuildTools :: PackageDescription -> LocalBuildInfo -> BuildInfo- -> ProgramDb -> ProgramDb-addInternalBuildTools pkg lbi bi progs =- foldr updateProgram progs internalBuildTools- where- internalBuildTools =- [ simpleConfiguredProgram toolName' (FoundOnSystem toolLocation)- | toolName <- getAllInternalToolDependencies pkg bi- , let toolName' = unUnqualComponentName toolName- , let toolLocation = buildDir lbi </> toolName' </> toolName' <.> exeExtension (hostPlatform lbi) ]----- TODO: build separate libs in separate dirs so that we can build--- multiple libs, e.g. for 'LibTest' library-style test suites-buildLib :: Verbosity -> Flag (Maybe Int)- -> PackageDescription -> LocalBuildInfo- -> Library -> ComponentLocalBuildInfo -> IO ()-buildLib verbosity numJobs pkg_descr lbi lib clbi =- case compilerFlavor (compiler lbi) of- GHC -> GHC.buildLib verbosity numJobs pkg_descr lbi lib clbi- GHCJS -> GHCJS.buildLib verbosity numJobs pkg_descr lbi lib clbi- UHC -> UHC.buildLib verbosity pkg_descr lbi lib clbi- HaskellSuite {} -> HaskellSuite.buildLib verbosity pkg_descr lbi lib clbi- _ -> die' verbosity "Building is not supported with this compiler."---- | Build a foreign library------ NOTE: We assume that we already checked that we can actually build the--- foreign library in configure.-buildFLib :: Verbosity -> Flag (Maybe Int)- -> PackageDescription -> LocalBuildInfo- -> ForeignLib -> ComponentLocalBuildInfo -> IO ()-buildFLib verbosity numJobs pkg_descr lbi flib clbi =- case compilerFlavor (compiler lbi) of- GHC -> GHC.buildFLib verbosity numJobs pkg_descr lbi flib clbi- _ -> die' verbosity "Building is not supported with this compiler."--buildExe :: Verbosity -> Flag (Maybe Int)- -> PackageDescription -> LocalBuildInfo- -> Executable -> ComponentLocalBuildInfo -> IO ()-buildExe verbosity numJobs pkg_descr lbi exe clbi =- case compilerFlavor (compiler lbi) of- GHC -> GHC.buildExe verbosity numJobs pkg_descr lbi exe clbi- GHCJS -> GHCJS.buildExe verbosity numJobs pkg_descr lbi exe clbi- UHC -> UHC.buildExe verbosity pkg_descr lbi exe clbi- _ -> die' verbosity "Building is not supported with this compiler."--replLib :: [String] -> Verbosity -> PackageDescription- -> LocalBuildInfo -> Library -> ComponentLocalBuildInfo- -> IO ()-replLib replFlags verbosity pkg_descr lbi lib clbi =- case compilerFlavor (compiler lbi) of- -- 'cabal repl' doesn't need to support 'ghc --make -j', so we just pass- -- NoFlag as the numJobs parameter.- GHC -> GHC.replLib replFlags verbosity NoFlag pkg_descr lbi lib clbi- GHCJS -> GHCJS.replLib replFlags verbosity NoFlag pkg_descr lbi lib clbi- _ -> die' verbosity "A REPL is not supported for this compiler."--replExe :: [String] -> Verbosity -> PackageDescription- -> LocalBuildInfo -> Executable -> ComponentLocalBuildInfo- -> IO ()-replExe replFlags verbosity pkg_descr lbi exe clbi =- case compilerFlavor (compiler lbi) of- GHC -> GHC.replExe replFlags verbosity NoFlag pkg_descr lbi exe clbi- GHCJS -> GHCJS.replExe replFlags verbosity NoFlag pkg_descr lbi exe clbi- _ -> die' verbosity "A REPL is not supported for this compiler."--replFLib :: [String] -> Verbosity -> PackageDescription- -> LocalBuildInfo -> ForeignLib -> ComponentLocalBuildInfo- -> IO ()-replFLib replFlags verbosity pkg_descr lbi exe clbi =- case compilerFlavor (compiler lbi) of- GHC -> GHC.replFLib replFlags verbosity NoFlag pkg_descr lbi exe clbi- _ -> die' verbosity "A REPL is not supported for this compiler."---- | Runs 'componentInitialBuildSteps' on every configured component.-initialBuildSteps :: FilePath -- ^"dist" prefix- -> PackageDescription -- ^mostly information from the .cabal file- -> LocalBuildInfo -- ^Configuration information- -> Verbosity -- ^The verbosity to use- -> IO ()-initialBuildSteps distPref pkg_descr lbi verbosity =- withAllComponentsInBuildOrder pkg_descr lbi $ \_comp clbi ->- componentInitialBuildSteps distPref pkg_descr lbi clbi verbosity---- | Creates the autogenerated files for a particular configured component.-componentInitialBuildSteps :: FilePath -- ^"dist" prefix- -> PackageDescription -- ^mostly information from the .cabal file- -> LocalBuildInfo -- ^Configuration information- -> ComponentLocalBuildInfo- -> Verbosity -- ^The verbosity to use- -> IO ()-componentInitialBuildSteps _distPref pkg_descr lbi clbi verbosity = do- createDirectoryIfMissingVerbose verbosity True (componentBuildDir lbi clbi)-- writeAutogenFiles verbosity pkg_descr lbi clbi---- | Generate and write out the Paths_<pkg>.hs and cabal_macros.h files----writeAutogenFiles :: Verbosity- -> PackageDescription- -> LocalBuildInfo- -> ComponentLocalBuildInfo- -> IO ()-writeAutogenFiles verbosity pkg lbi clbi = do- createDirectoryIfMissingVerbose verbosity True (autogenComponentModulesDir lbi clbi)-- let pathsModulePath = autogenComponentModulesDir lbi clbi- </> ModuleName.toFilePath (autogenPathsModuleName pkg) <.> "hs"- pathsModuleDir = takeDirectory pathsModulePath- -- Ensure that the directory exists!- createDirectoryIfMissingVerbose verbosity True pathsModuleDir- rewriteFileEx verbosity pathsModulePath (generatePathsModule pkg lbi clbi)-- --TODO: document what we're doing here, and move it to its own function- case clbi of- LibComponentLocalBuildInfo { componentInstantiatedWith = insts } ->- -- Write out empty hsig files for all requirements, so that GHC- -- has a source file to look at it when it needs to typecheck- -- a signature. It's harmless to write these out even when- -- there is a real hsig file written by the user, since- -- include path ordering ensures that the real hsig file- -- will always be picked up before the autogenerated one.- for_ (map fst insts) $ \mod_name -> do- let sigPath = autogenComponentModulesDir lbi clbi- </> ModuleName.toFilePath mod_name <.> "hsig"- createDirectoryIfMissingVerbose verbosity True (takeDirectory sigPath)- rewriteFileEx verbosity sigPath $- "{-# OPTIONS_GHC -w #-}\n" ++- "{-# LANGUAGE NoImplicitPrelude #-}\n" ++- "signature " ++ prettyShow mod_name ++ " where"- _ -> return ()-- let cppHeaderPath = autogenComponentModulesDir lbi clbi </> cppHeaderName- rewriteFileEx verbosity cppHeaderPath (generateCabalMacrosHeader pkg lbi clbi)+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TupleSections #-}++-----------------------------------------------------------------------------++-- |+-- Module : Distribution.Simple.Build+-- Copyright : Isaac Jones 2003-2005,+-- Ross Paterson 2006,+-- Duncan Coutts 2007-2008, 2012+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- This is the entry point to actually building the modules in a package. It+-- doesn't actually do much itself, most of the work is delegated to+-- compiler-specific actions. It does do some non-compiler specific bits like+-- running pre-processors.+module Distribution.Simple.Build+ ( -- * Build+ build+ , build_setupHooks++ -- * Repl+ , repl+ , repl_setupHooks+ , startInterpreter++ -- * Build preparation+ , preBuildComponent+ , AutogenFile (..)+ , AutogenFileContents+ , writeBuiltinAutogenFiles+ , writeAutogenFiles++ -- ** Legacy functions+ , componentInitialBuildSteps+ , initialBuildSteps++ -- * Internal package database creation+ , createInternalPackageDB++ -- * Handling of internal build tools+ , addInternalBuildTools+ ) where++import Distribution.Compat.Prelude+import Distribution.Utils.Generic+import Prelude ()++import Distribution.Types.ComponentLocalBuildInfo+import Distribution.Types.ComponentRequestedSpec+import Distribution.Types.Dependency+import Distribution.Types.ExecutableScope+import Distribution.Types.ForeignLib+import Distribution.Types.LibraryVisibility+import Distribution.Types.LocalBuildInfo+import Distribution.Types.ModuleRenaming+import Distribution.Types.MungedPackageId+import Distribution.Types.MungedPackageName+import Distribution.Types.ParStrat+import Distribution.Types.TargetInfo+import Distribution.Utils.Path++import Distribution.Backpack+import Distribution.Backpack.DescribeUnitId+import Distribution.Package+import qualified Distribution.Simple.GHC as GHC+import qualified Distribution.Simple.GHCJS as GHCJS+import qualified Distribution.Simple.PackageIndex as Index+import qualified Distribution.Simple.UHC as UHC++import Distribution.Simple.Build.Macros (generateCabalMacrosHeader)+import Distribution.Simple.Build.PackageInfoModule (generatePackageInfoModule)+import Distribution.Simple.Build.PathsModule (generatePathsModule, pkgPathEnvVar)+import qualified Distribution.Simple.Program.HcPkg as HcPkg++import Distribution.InstalledPackageInfo (InstalledPackageInfo)+import qualified Distribution.InstalledPackageInfo as IPI+import Distribution.ModuleName (ModuleName)+import qualified Distribution.ModuleName as ModuleName+import Distribution.PackageDescription+import Distribution.Simple.Compiler++import Distribution.Simple.BuildPaths+import Distribution.Simple.BuildTarget+import Distribution.Simple.BuildToolDepends+import Distribution.Simple.Configure+import Distribution.Simple.Flag+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.PreProcess+import Distribution.Simple.Program+import Distribution.Simple.Program.Db+import qualified Distribution.Simple.Program.GHC as GHC+import Distribution.Simple.Program.Types+import Distribution.Simple.Register+import Distribution.Simple.Setup.Build+import Distribution.Simple.Setup.Common+import Distribution.Simple.Setup.Config+import Distribution.Simple.Setup.Repl+import Distribution.Simple.SetupHooks.Internal+ ( BuildHooks (..)+ , BuildingWhat (..)+ , noBuildHooks+ )+import qualified Distribution.Simple.SetupHooks.Internal as SetupHooks+import qualified Distribution.Simple.SetupHooks.Rule as SetupHooks+import Distribution.Simple.ShowBuildInfo+import Distribution.Simple.Test.LibV09+import Distribution.Simple.Utils+import Distribution.Utils.Json+import Distribution.Utils.ShortText (ShortText, fromShortText, toShortText)++import Distribution.Pretty+import Distribution.System+import Distribution.Verbosity+import Distribution.Version (thisVersion)++import Distribution.Compat.Graph (IsNode (..))++import Control.Monad+import qualified Data.ByteString.Lazy as LBS+import qualified Data.Map as Map+import Distribution.Simple.Errors+import System.Directory (doesFileExist, removeFile)+import System.FilePath (takeDirectory)++-- -----------------------------------------------------------------------------++-- | Build the libraries and executables in this package.+build+ :: PackageDescription+ -- ^ Mostly information from the .cabal file+ -> LocalBuildInfo+ -- ^ Configuration information+ -> BuildFlags+ -- ^ Flags that the user passed to build+ -> [PPSuffixHandler]+ -- ^ preprocessors to run before compiling+ -> IO ()+build = build_setupHooks noBuildHooks++build_setupHooks+ :: BuildHooks+ -> PackageDescription+ -- ^ Mostly information from the .cabal file+ -> LocalBuildInfo+ -- ^ Configuration information+ -> BuildFlags+ -- ^ Flags that the user passed to build+ -> [PPSuffixHandler]+ -- ^ preprocessors to run before compiling+ -> IO ()+build_setupHooks+ (BuildHooks{preBuildComponentRules = mbPbcRules, postBuildComponentHook = mbPostBuild})+ pkg_descr+ lbi+ flags+ suffixHandlers = do+ checkSemaphoreSupport verbosity (compiler lbi) flags+ targets <- readTargetInfos verbosity pkg_descr lbi (buildTargets flags)+ let componentsToBuild = neededTargetsInBuildOrder' pkg_descr lbi (map nodeKey targets)+ info verbosity $+ "Component build order: "+ ++ intercalate+ ", "+ ( map+ (showComponentName . componentLocalName . targetCLBI)+ componentsToBuild+ )++ when (null targets) $+ -- Only bother with this message if we're building the whole package+ setupMessage verbosity "Building" (packageId pkg_descr)++ internalPackageDB <- createInternalPackageDB verbosity lbi distPref++ -- Before the actual building, dump out build-information.+ -- This way, if the actual compilation failed, the options have still been+ -- dumped.+ dumpBuildInfo verbosity distPref (configDumpBuildInfo (configFlags lbi)) pkg_descr lbi flags++ curDir <- absoluteWorkingDirLBI lbi++ -- Now do the actual building+ (\f -> foldM_ f (installedPkgs lbi) componentsToBuild) $ \index target -> do+ let comp = targetComponent target+ clbi = targetCLBI target+ bi = componentBuildInfo comp+ -- Include any build-tool-depends on build tools internal to the current package.+ progs' = addInternalBuildTools curDir pkg_descr lbi bi (withPrograms lbi)+ lbi' =+ lbi+ { withPrograms = progs'+ , withPackageDB = withPackageDB lbi ++ [internalPackageDB]+ , installedPkgs = index+ }+ runPreBuildHooks :: LocalBuildInfo -> TargetInfo -> IO ()+ runPreBuildHooks lbi2 tgt =+ let inputs =+ SetupHooks.PreBuildComponentInputs+ { SetupHooks.buildingWhat = BuildNormal flags+ , SetupHooks.localBuildInfo = lbi2+ , SetupHooks.targetInfo = tgt+ }+ in for_ mbPbcRules $ \pbcRules -> do+ (ruleFromId, _mons) <- SetupHooks.computeRules verbosity inputs pbcRules+ SetupHooks.executeRules verbosity lbi2 tgt ruleFromId+ preBuildComponent runPreBuildHooks verbosity lbi' target+ let numJobs = buildNumJobs flags+ par_strat <-+ toFlag <$> case buildUseSemaphore flags of+ Flag sem_name -> case numJobs of+ Flag{} -> do+ warn verbosity $ "Ignoring -j due to --semaphore"+ return $ UseSem sem_name+ NoFlag -> return $ UseSem sem_name+ NoFlag -> return $ case numJobs of+ Flag n -> NumJobs n+ NoFlag -> Serial+ mb_ipi <-+ buildComponent+ flags+ par_strat+ pkg_descr+ lbi'+ suffixHandlers+ comp+ clbi+ distPref+ let postBuildInputs =+ SetupHooks.PostBuildComponentInputs+ { SetupHooks.buildFlags = flags+ , SetupHooks.localBuildInfo = lbi'+ , SetupHooks.targetInfo = target+ }+ for_ mbPostBuild ($ postBuildInputs)+ return (maybe index (Index.insert `flip` index) mb_ipi)++ return ()+ where+ distPref = fromFlag (buildDistPref flags)+ verbosity = fromFlag (buildVerbosity flags)++-- | Check for conditions that would prevent the build from succeeding.+checkSemaphoreSupport+ :: Verbosity -> Compiler -> BuildFlags -> IO ()+checkSemaphoreSupport verbosity comp flags = do+ unless (jsemSupported comp || (isNothing (flagToMaybe (buildUseSemaphore flags)))) $+ dieWithException verbosity CheckSemaphoreSupport++-- | Write available build information for 'LocalBuildInfo' to disk.+--+-- Dumps detailed build information 'build-info.json' to the given directory.+-- Build information contains basics such as compiler details, but also+-- lists what modules a component contains and how to compile the component, assuming+-- lib:Cabal made sure that dependencies are up-to-date.+dumpBuildInfo+ :: Verbosity+ -> SymbolicPath Pkg (Dir Dist)+ -- ^ To which directory should the build-info be dumped?+ -> Flag DumpBuildInfo+ -- ^ Should we dump detailed build information for this component?+ -> PackageDescription+ -- ^ Mostly information from the .cabal file+ -> LocalBuildInfo+ -- ^ Configuration information+ -> BuildFlags+ -- ^ Flags that the user passed to build+ -> IO ()+dumpBuildInfo verbosity distPref dumpBuildInfoFlag pkg_descr lbi flags = do+ when shouldDumpBuildInfo $ do+ -- Changing this line might break consumers of the dumped build info.+ -- Announce changes on mailing lists!+ let activeTargets = allTargetsInBuildOrder' pkg_descr lbi+ info verbosity $+ "Dump build information for: "+ ++ intercalate+ ", "+ ( map+ (showComponentName . componentLocalName . targetCLBI)+ activeTargets+ )++ (compilerProg, _) <- case flavorToProgram (compilerFlavor (compiler lbi)) of+ Nothing ->+ dieWithException verbosity $ UnknownCompilerFlavor (compilerFlavor (compiler lbi))+ Just program -> requireProgram verbosity program (withPrograms lbi)++ wdir <- absoluteWorkingDirLBI lbi+ let (warns, json) = mkBuildInfo wdir pkg_descr lbi flags (compilerProg, compiler lbi) activeTargets+ buildInfoText = renderJson json+ unless (null warns) $+ warn verbosity $+ "Encountered warnings while dumping build-info:\n"+ ++ unlines warns+ LBS.writeFile buildInfoFile buildInfoText++ when (not shouldDumpBuildInfo) $ do+ -- Remove existing build-info.json as it might be outdated now.+ exists <- doesFileExist buildInfoFile+ when exists $ removeFile buildInfoFile+ where+ buildInfoFile = interpretSymbolicPathLBI lbi $ buildInfoPref distPref+ shouldDumpBuildInfo = fromFlagOrDefault NoDumpBuildInfo dumpBuildInfoFlag == DumpBuildInfo++ -- \| Given the flavor of the compiler, try to find out+ -- which program we need.+ flavorToProgram :: CompilerFlavor -> Maybe Program+ flavorToProgram GHC = Just ghcProgram+ flavorToProgram GHCJS = Just ghcjsProgram+ flavorToProgram UHC = Just uhcProgram+ flavorToProgram JHC = Just jhcProgram+ flavorToProgram _ = Nothing++repl+ :: PackageDescription+ -- ^ Mostly information from the .cabal file+ -> LocalBuildInfo+ -- ^ Configuration information+ -> ReplFlags+ -- ^ Flags that the user passed to build+ -> [PPSuffixHandler]+ -- ^ preprocessors to run before compiling+ -> [String]+ -> IO ()+repl = repl_setupHooks noBuildHooks++repl_setupHooks+ :: BuildHooks+ -- ^ build hook+ -> PackageDescription+ -- ^ Mostly information from the .cabal file+ -> LocalBuildInfo+ -- ^ Configuration information+ -> ReplFlags+ -- ^ Flags that the user passed to build+ -> [PPSuffixHandler]+ -- ^ preprocessors to run before compiling+ -> [String]+ -> IO ()+repl_setupHooks+ (BuildHooks{preBuildComponentRules = mbPbcRules})+ pkg_descr+ lbi+ flags+ suffixHandlers+ args = do+ let distPref = fromFlag (replDistPref flags)+ verbosity = fromFlag (replVerbosity flags)++ target <-+ readTargetInfos verbosity pkg_descr lbi args >>= \r -> case r of+ -- This seems DEEPLY questionable.+ [] -> case allTargetsInBuildOrder' pkg_descr lbi of+ (target : _) -> return target+ [] -> dieWithException verbosity $ FailedToDetermineTarget+ [target] -> return target+ _ -> dieWithException verbosity $ NoMultipleTargets+ let componentsToBuild = neededTargetsInBuildOrder' pkg_descr lbi [nodeKey target]+ debug verbosity $+ "Component build order: "+ ++ intercalate+ ", "+ ( map+ (showComponentName . componentLocalName . targetCLBI)+ componentsToBuild+ )++ internalPackageDB <- createInternalPackageDB verbosity lbi distPref++ let lbiForComponent comp lbi' = do+ curDir <- absoluteWorkingDirLBI lbi'+ return $+ lbi'+ { withPackageDB = withPackageDB lbi' ++ [internalPackageDB]+ , withPrograms =+ -- Include any build-tool-depends on build tools internal to the current package.+ addInternalBuildTools+ curDir+ pkg_descr+ lbi'+ (componentBuildInfo comp)+ (withPrograms lbi')+ }+ runPreBuildHooks :: LocalBuildInfo -> TargetInfo -> IO ()+ runPreBuildHooks lbi2 tgt =+ let inputs =+ SetupHooks.PreBuildComponentInputs+ { SetupHooks.buildingWhat = BuildRepl flags+ , SetupHooks.localBuildInfo = lbi2+ , SetupHooks.targetInfo = tgt+ }+ in for_ mbPbcRules $ \pbcRules -> do+ (ruleFromId, _mons) <- SetupHooks.computeRules verbosity inputs pbcRules+ SetupHooks.executeRules verbosity lbi2 tgt ruleFromId++ -- build any dependent components+ sequence_+ [ do+ let clbi = targetCLBI subtarget+ comp = targetComponent subtarget+ lbi' <- lbiForComponent comp lbi+ preBuildComponent runPreBuildHooks verbosity lbi' subtarget+ buildComponent+ (mempty{buildCommonFlags = mempty{setupVerbosity = toFlag verbosity}})+ NoFlag+ pkg_descr+ lbi'+ suffixHandlers+ comp+ clbi+ distPref+ | subtarget <- safeInit componentsToBuild+ ]++ -- REPL for target components+ let clbi = targetCLBI target+ comp = targetComponent target+ lbi' <- lbiForComponent comp lbi+ preBuildComponent runPreBuildHooks verbosity lbi' target+ replComponent flags verbosity pkg_descr lbi' suffixHandlers comp clbi distPref++-- | Start an interpreter without loading any package files.+startInterpreter+ :: Verbosity+ -> ProgramDb+ -> Compiler+ -> Platform+ -> PackageDBStack+ -> IO ()+startInterpreter verbosity programDb comp platform packageDBs =+ case compilerFlavor comp of+ GHC -> GHC.startInterpreter verbosity programDb comp platform packageDBs+ GHCJS -> GHCJS.startInterpreter verbosity programDb comp platform packageDBs+ _ -> dieWithException verbosity REPLNotSupported++buildComponent+ :: BuildFlags+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> [PPSuffixHandler]+ -> Component+ -> ComponentLocalBuildInfo+ -> SymbolicPath Pkg (Dir Dist)+ -> IO (Maybe InstalledPackageInfo)+buildComponent flags _ _ _ _ (CTest TestSuite{testInterface = TestSuiteUnsupported tt}) _ _ =+ dieWithException (fromFlag $ buildVerbosity flags) $+ NoSupportBuildingTestSuite tt+buildComponent flags _ _ _ _ (CBench Benchmark{benchmarkInterface = BenchmarkUnsupported tt}) _ _ =+ dieWithException (fromFlag $ buildVerbosity flags) $+ NoSupportBuildingBenchMark tt+buildComponent+ flags+ numJobs+ pkg_descr+ lbi0+ suffixHandlers+ comp@( CTest+ test@TestSuite{testInterface = TestSuiteLibV09{}}+ )+ clbi -- This ComponentLocalBuildInfo corresponds to a detailed+ -- test suite and not a real component. It should not+ -- be used, except to construct the CLBIs for the+ -- library and stub executable that will actually be+ -- built.+ distPref =+ do+ inplaceDir <- absoluteWorkingDirLBI lbi0+ let verbosity = fromFlag $ buildVerbosity flags+ let (pkg, lib, libClbi, lbi, ipi, exe, exeClbi) =+ testSuiteLibV09AsLibAndExe pkg_descr test clbi lbi0 inplaceDir distPref+ preprocessComponent pkg_descr comp lbi clbi False verbosity suffixHandlers+ extras <- preprocessExtras verbosity comp lbi -- TODO find cpphs processed files+ (genDir, generatedExtras) <- generateCode (testCodeGenerators test) (testName test) pkg_descr (testBuildInfo test) lbi clbi verbosity+ setupMessage'+ verbosity+ "Building"+ (packageId pkg_descr)+ (componentLocalName clbi)+ (maybeComponentInstantiatedWith clbi)+ let libbi = libBuildInfo lib+ lib' = lib{libBuildInfo = addSrcDir (addExtraOtherModules libbi generatedExtras) genDir}+ buildLib flags numJobs pkg lbi lib' libClbi+ -- NB: need to enable multiple instances here, because on 7.10++ -- the package name is the same as the library, and we still+ -- want the registration to go through.+ registerPackage+ verbosity+ (compiler lbi)+ (withPrograms lbi)+ (mbWorkDirLBI lbi)+ (withPackageDB lbi)+ ipi+ HcPkg.defaultRegisterOptions+ { HcPkg.registerMultiInstance = True+ }+ let ebi = buildInfo exe+ -- NB: The stub executable is linked against the test-library+ -- which already contains all `other-modules`, so we need+ -- to remove those from the stub-exe's build-info+ exe' = exe{buildInfo = (addExtraCSources ebi extras){otherModules = []}}+ buildExe verbosity numJobs pkg_descr lbi exe' exeClbi+ return Nothing -- Can't depend on test suite+buildComponent+ flags+ numJobs+ pkg_descr+ lbi+ suffixHandlers+ comp+ clbi+ distPref =+ do+ let verbosity = fromFlag $ buildVerbosity flags+ preprocessComponent pkg_descr comp lbi clbi False verbosity suffixHandlers+ extras <- preprocessExtras verbosity comp lbi+ setupMessage'+ verbosity+ "Building"+ (packageId pkg_descr)+ (componentLocalName clbi)+ (maybeComponentInstantiatedWith clbi)+ case comp of+ CLib lib -> do+ let libbi = libBuildInfo lib+ lib' =+ lib+ { libBuildInfo =+ flip addExtraAsmSources extras $+ flip addExtraCmmSources extras $+ flip addExtraCxxSources extras $+ flip addExtraCSources extras $+ flip addExtraJsSources extras $+ libbi+ }++ buildLib flags numJobs pkg_descr lbi lib' clbi++ let oneComponentRequested (OneComponentRequestedSpec _) = True+ oneComponentRequested _ = False+ -- Don't register inplace if we're only building a single component;+ -- it's not necessary because there won't be any subsequent builds+ -- that need to tag us+ if (not (oneComponentRequested (componentEnabledSpec lbi)))+ then do+ -- Register the library in-place, so exes can depend+ -- on internally defined libraries.+ inplaceDir <- absoluteWorkingDirLBI lbi+ let+ -- The in place registration uses the "-inplace" suffix, not an ABI hash+ installedPkgInfo =+ inplaceInstalledPackageInfo+ inplaceDir+ distPref+ pkg_descr+ -- NB: Use a fake ABI hash to avoid+ -- needing to recompute it every build.+ (mkAbiHash "inplace")+ lib'+ lbi+ clbi+ debug verbosity $ "Registering inplace:\n" ++ (IPI.showInstalledPackageInfo installedPkgInfo)+ registerPackage+ verbosity+ (compiler lbi)+ (withPrograms lbi)+ (flagToMaybe $ buildWorkingDir flags)+ (withPackageDB lbi)+ installedPkgInfo+ HcPkg.defaultRegisterOptions+ { HcPkg.registerMultiInstance = True+ }+ return (Just installedPkgInfo)+ else return Nothing+ CFLib flib -> do+ buildFLib verbosity numJobs pkg_descr lbi flib clbi+ return Nothing+ CExe exe -> do+ let ebi = buildInfo exe+ exe' = exe{buildInfo = addExtraCSources ebi extras}+ buildExe verbosity numJobs pkg_descr lbi exe' clbi+ return Nothing+ CTest test@TestSuite{testInterface = TestSuiteExeV10{}} -> do+ let exe = testSuiteExeV10AsExe test+ (genDir, generatedExtras) <- generateCode (testCodeGenerators test) (testName test) pkg_descr (testBuildInfo test) lbi clbi verbosity+ let ebi = buildInfo exe+ exe' = exe{buildInfo = addSrcDir (addExtraOtherModules (addExtraCSources ebi extras) generatedExtras) genDir} -- todo extend hssrcdirs+ buildExe verbosity numJobs pkg_descr lbi exe' clbi+ return Nothing+ CBench bm@Benchmark{benchmarkInterface = BenchmarkExeV10{}} -> do+ let exe = benchmarkExeV10asExe bm+ let ebi = buildInfo exe+ exe' = exe{buildInfo = addExtraCSources ebi extras}+ buildExe verbosity numJobs pkg_descr lbi exe' clbi+ return Nothing+#if __GLASGOW_HASKELL__ < 811+-- silence pattern-match warnings prior to GHC 9.0+ _ -> error "impossible"+#endif++generateCode+ :: [String]+ -> UnqualComponentName+ -> PackageDescription+ -> BuildInfo+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> Verbosity+ -> IO (SymbolicPath Pkg (Dir Source), [ModuleName.ModuleName])+generateCode codeGens nm pdesc bi lbi clbi verbosity = do+ when (not . null $ codeGens) $ createDirectoryIfMissingVerbose verbosity True $ i tgtDir+ (\x -> (tgtDir, x)) . concat <$> mapM go codeGens+ where+ allLibs = (maybe id (:) $ library pdesc) (subLibraries pdesc)+ dependencyLibs = filter (const True) allLibs -- intersect with componentPackageDeps of clbi+ srcDirs = concatMap (hsSourceDirs . libBuildInfo) dependencyLibs+ nm' = unUnqualComponentName nm+ mbWorkDir = mbWorkDirLBI lbi+ i = interpretSymbolicPath mbWorkDir -- See Note [Symbolic paths] in Distribution.Utils.Path+ tgtDir = buildDir lbi </> makeRelativePathEx (nm' </> nm' ++ "-gen")+ go :: String -> IO [ModuleName.ModuleName]+ go codeGenProg =+ fmap fromString . lines+ <$> getDbProgramOutputCwd+ verbosity+ mbWorkDir+ (simpleProgram codeGenProg)+ (withPrograms lbi)+ ( map interpretSymbolicPathCWD (tgtDir : srcDirs)+ ++ ( "--"+ : GHC.renderGhcOptions (compiler lbi) (hostPlatform lbi) (GHC.componentGhcOptions verbosity lbi bi clbi tgtDir)+ )+ )++-- | Add extra C sources generated by preprocessing to build+-- information.+addExtraCSources :: BuildInfo -> [SymbolicPath Pkg File] -> BuildInfo+addExtraCSources bi extras = bi{cSources = new}+ where+ new = ordNub (extras ++ cSources bi)++-- | Add extra C++ sources generated by preprocessing to build+-- information.+addExtraCxxSources :: BuildInfo -> [SymbolicPath Pkg File] -> BuildInfo+addExtraCxxSources bi extras = bi{cxxSources = new}+ where+ new = ordNub (extras ++ cxxSources bi)++-- | Add extra C-- sources generated by preprocessing to build+-- information.+addExtraCmmSources :: BuildInfo -> [SymbolicPath Pkg File] -> BuildInfo+addExtraCmmSources bi extras = bi{cmmSources = new}+ where+ new = ordNub (extras ++ cmmSources bi)++-- | Add extra ASM sources generated by preprocessing to build+-- information.+addExtraAsmSources :: BuildInfo -> [SymbolicPath Pkg File] -> BuildInfo+addExtraAsmSources bi extras = bi{asmSources = new}+ where+ new = ordNub (extras ++ asmSources bi)++-- | Add extra JS sources generated by preprocessing to build+-- information.+addExtraJsSources :: BuildInfo -> [SymbolicPath Pkg File] -> BuildInfo+addExtraJsSources bi extras = bi{jsSources = new}+ where+ new = ordNub (extras ++ jsSources bi)++-- | Add extra HS modules generated by preprocessing to build+-- information.+addExtraOtherModules :: BuildInfo -> [ModuleName.ModuleName] -> BuildInfo+addExtraOtherModules bi extras = bi{otherModules = new}+ where+ new = ordNub (extras ++ otherModules bi)++-- | Add extra source dir for generated modules.+addSrcDir :: BuildInfo -> SymbolicPath Pkg (Dir Source) -> BuildInfo+addSrcDir bi extra = bi{hsSourceDirs = new}+ where+ new = ordNub (extra : hsSourceDirs bi)++replComponent+ :: ReplFlags+ -> Verbosity+ -> PackageDescription+ -> LocalBuildInfo+ -> [PPSuffixHandler]+ -> Component+ -> ComponentLocalBuildInfo+ -> SymbolicPath Pkg (Dir Dist)+ -> IO ()+replComponent _ verbosity _ _ _ (CTest TestSuite{testInterface = TestSuiteUnsupported tt}) _ _ =+ dieWithException verbosity $ NoSupportBuildingTestSuite tt+replComponent _ verbosity _ _ _ (CBench Benchmark{benchmarkInterface = BenchmarkUnsupported tt}) _ _ =+ dieWithException verbosity $ NoSupportBuildingBenchMark tt+replComponent+ replFlags+ verbosity+ pkg_descr+ lbi0+ suffixHandlers+ comp@( CTest+ test@TestSuite{testInterface = TestSuiteLibV09{}}+ )+ clbi+ distPref = do+ inplaceDir <- absoluteWorkingDirLBI lbi0+ let (pkg, lib, libClbi, lbi, _, _, _) =+ testSuiteLibV09AsLibAndExe pkg_descr test clbi lbi0 inplaceDir distPref+ preprocessComponent pkg_descr comp lbi clbi False verbosity suffixHandlers+ extras <- preprocessExtras verbosity comp lbi+ let libbi = libBuildInfo lib+ lib' = lib{libBuildInfo = libbi{cSources = cSources libbi ++ extras}}+ replLib replFlags pkg lbi lib' libClbi+replComponent+ replFlags+ verbosity+ pkg_descr+ lbi+ suffixHandlers+ comp+ clbi+ _ =+ do+ preprocessComponent pkg_descr comp lbi clbi False verbosity suffixHandlers+ extras <- preprocessExtras verbosity comp lbi+ case comp of+ CLib lib -> do+ let libbi = libBuildInfo lib+ lib' = lib{libBuildInfo = libbi{cSources = cSources libbi ++ extras}}+ replLib replFlags pkg_descr lbi lib' clbi+ CFLib flib ->+ replFLib replFlags pkg_descr lbi flib clbi+ CExe exe -> do+ let ebi = buildInfo exe+ exe' = exe{buildInfo = ebi{cSources = cSources ebi ++ extras}}+ replExe replFlags pkg_descr lbi exe' clbi+ CTest test@TestSuite{testInterface = TestSuiteExeV10{}} -> do+ let exe = testSuiteExeV10AsExe test+ let ebi = buildInfo exe+ exe' = exe{buildInfo = ebi{cSources = cSources ebi ++ extras}}+ replExe replFlags pkg_descr lbi exe' clbi+ CBench bm@Benchmark{benchmarkInterface = BenchmarkExeV10{}} -> do+ let exe = benchmarkExeV10asExe bm+ let ebi = buildInfo exe+ exe' = exe{buildInfo = ebi{cSources = cSources ebi ++ extras}}+ replExe replFlags pkg_descr lbi exe' clbi+#if __GLASGOW_HASKELL__ < 811+-- silence pattern-match warnings prior to GHC 9.0+ _ -> error "impossible"+#endif++----------------------------------------------------+-- Shared code for buildComponent and replComponent+--++-- | Translate a exe-style 'TestSuite' component into an exe for building+testSuiteExeV10AsExe :: TestSuite -> Executable+testSuiteExeV10AsExe test@TestSuite{testInterface = TestSuiteExeV10 _ mainFile} =+ Executable+ { exeName = testName test+ , modulePath = mainFile+ , exeScope = ExecutablePublic+ , buildInfo = testBuildInfo test+ }+testSuiteExeV10AsExe TestSuite{} = error "testSuiteExeV10AsExe: wrong kind"++-- | Translate a exe-style 'Benchmark' component into an exe for building+benchmarkExeV10asExe :: Benchmark -> Executable+benchmarkExeV10asExe bm@Benchmark{benchmarkInterface = BenchmarkExeV10 _ mainFile} =+ Executable+ { exeName = benchmarkName bm+ , modulePath = mainFile+ , exeScope = ExecutablePublic+ , buildInfo = benchmarkBuildInfo bm+ }+benchmarkExeV10asExe Benchmark{} = error "benchmarkExeV10asExe: wrong kind"++-- | Translate a lib-style 'TestSuite' component into a lib + exe for building+testSuiteLibV09AsLibAndExe+ :: PackageDescription+ -> TestSuite+ -> ComponentLocalBuildInfo+ -> LocalBuildInfo+ -> AbsolutePath (Dir Pkg)+ -- ^ absolute inplace dir+ -> SymbolicPath Pkg (Dir Dist)+ -> ( PackageDescription+ , Library+ , ComponentLocalBuildInfo+ , LocalBuildInfo+ , IPI.InstalledPackageInfo+ , Executable+ , ComponentLocalBuildInfo+ )+testSuiteLibV09AsLibAndExe+ pkg_descr+ test@TestSuite{testInterface = TestSuiteLibV09 _ m}+ clbi+ lbi+ inplaceDir+ distPref =+ (pkg, lib, libClbi, lbi, ipi, exe, exeClbi)+ where+ bi = testBuildInfo test+ lib =+ Library+ { libName = LMainLibName+ , exposedModules = [m]+ , reexportedModules = []+ , signatures = []+ , libExposed = True+ , libVisibility = LibraryVisibilityPrivate+ , libBuildInfo = bi+ }+ -- This is, like, the one place where we use a CTestName for a library.+ -- Should NOT use library name, since that could conflict!+ PackageIdentifier pkg_name pkg_ver = package pkg_descr+ -- Note: we do make internal library from the test!+ compat_name = MungedPackageName pkg_name (LSubLibName (testName test))+ compat_key = computeCompatPackageKey (compiler lbi) compat_name pkg_ver (componentUnitId clbi)+ libClbi =+ LibComponentLocalBuildInfo+ { componentPackageDeps = componentPackageDeps clbi+ , componentInternalDeps = componentInternalDeps clbi+ , componentIsIndefinite_ = False+ , componentExeDeps = componentExeDeps clbi+ , componentLocalName = CLibName $ LSubLibName $ testName test+ , componentIsPublic = False+ , componentIncludes = componentIncludes clbi+ , componentUnitId = componentUnitId clbi+ , componentComponentId = componentComponentId clbi+ , componentInstantiatedWith = []+ , componentCompatPackageName = compat_name+ , componentCompatPackageKey = compat_key+ , componentExposedModules = [IPI.ExposedModule m Nothing]+ }+ pkgName' = mkPackageName $ prettyShow compat_name+ pkg =+ pkg_descr+ { package = (package pkg_descr){pkgName = pkgName'}+ , executables = []+ , testSuites = []+ , subLibraries = [lib]+ }+ ipi = inplaceInstalledPackageInfo inplaceDir distPref pkg (mkAbiHash "") lib lbi libClbi+ testLibDep =+ Dependency+ pkgName'+ (thisVersion $ pkgVersion $ package pkg_descr)+ mainLibSet+ exe =+ Executable+ { exeName = mkUnqualComponentName $ stubName test+ , modulePath = makeRelativePathEx $ stubFilePath test+ , exeScope = ExecutablePublic+ , buildInfo =+ (testBuildInfo test)+ { hsSourceDirs = [coerceSymbolicPath $ testBuildDir lbi test]+ , targetBuildDepends =+ testLibDep+ : targetBuildDepends (testBuildInfo test)+ }+ }+ -- \| The stub executable needs a new 'ComponentLocalBuildInfo'+ -- that exposes the relevant test suite library.+ deps =+ (IPI.installedUnitId ipi, mungedId ipi)+ : ( filter+ ( \(_, x) ->+ let name = prettyShow $ mungedName x+ in name == "Cabal" || name == "base"+ )+ (componentPackageDeps clbi)+ )+ exeClbi =+ ExeComponentLocalBuildInfo+ { -- TODO: this is a hack, but as long as this is unique+ -- (doesn't clobber something) we won't run into trouble+ componentUnitId = mkUnitId (stubName test)+ , componentComponentId = mkComponentId (stubName test)+ , componentInternalDeps = [componentUnitId clbi]+ , componentExeDeps = []+ , componentLocalName = CExeName $ mkUnqualComponentName $ stubName test+ , componentPackageDeps = deps+ , -- Assert DefUnitId invariant!+ -- Executable can't be indefinite, so dependencies must+ -- be definite packages.+ componentIncludes =+ map ((,defaultRenaming) . DefiniteUnitId . unsafeMkDefUnitId . fst) deps+ }+testSuiteLibV09AsLibAndExe _ TestSuite{} _ _ _ _ = error "testSuiteLibV09AsLibAndExe: wrong kind"++-- | Initialize a new package db file for libraries defined+-- internally to the package.+createInternalPackageDB+ :: Verbosity+ -> LocalBuildInfo+ -> SymbolicPath Pkg (Dir Dist)+ -> IO PackageDB+createInternalPackageDB verbosity lbi distPref = do+ existsAlready <- doesPackageDBExist dbPath+ when existsAlready $ deletePackageDB dbPath+ createPackageDB verbosity (compiler lbi) (withPrograms lbi) False dbPath+ return (SpecificPackageDB dbRelPath)+ where+ dbRelPath = internalPackageDBPath lbi distPref+ dbPath = interpretSymbolicPathLBI lbi dbRelPath++-- | Update the program database to include any build-tool-depends specified+-- in the given 'BuildInfo' on build tools internal to the current package.+--+-- This function:+--+-- - adds these internal build tools to the 'ProgramDb', including+-- paths to their respective data directories,+-- - adds their paths to the current 'progSearchPath', and adds the data+-- directory environment variable for the current package to the current+-- 'progOverrideEnv', so that any programs configured from now on will be+-- able to invoke these build tools.+addInternalBuildTools+ :: AbsolutePath (Dir Pkg)+ -> PackageDescription+ -> LocalBuildInfo+ -> BuildInfo+ -> ProgramDb+ -> ProgramDb+addInternalBuildTools pwd pkg lbi bi progs =+ prependProgramSearchPathNoLogging+ internalToolPaths+ [pkgDataDirVar]+ $ foldr updateProgram progs internalBuildTools+ where+ internalToolPaths = map (takeDirectory . programPath) internalBuildTools+ pkgDataDirVar = (pkgPathEnvVar pkg "datadir", Just dataDirPath)+ internalBuildTools =+ [ (simpleConfiguredProgram toolName' (FoundOnSystem toolLocation))+ { programOverrideEnv = [pkgDataDirVar]+ }+ | toolName <- getAllInternalToolDependencies pkg bi+ , let toolName' = unUnqualComponentName toolName+ , let toolLocation =+ interpretSymbolicPathLBI lbi $+ buildDir lbi+ </> makeRelativePathEx (toolName' </> toolName' <.> exeExtension (hostPlatform lbi))+ ]++ -- This is an absolute path, so if a process changes directory, it can still+ -- find the datadir (#10717)+ dataDirPath :: FilePath+ dataDirPath = interpretSymbolicPathAbsolute pwd (dataDir pkg)++-- TODO: build separate libs in separate dirs so that we can build+-- multiple libs, e.g. for 'LibTest' library-style test suites+buildLib+ :: BuildFlags+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> Library+ -> ComponentLocalBuildInfo+ -> IO ()+buildLib flags numJobs pkg_descr lbi lib clbi =+ let verbosity = fromFlag $ buildVerbosity flags+ in case compilerFlavor (compiler lbi) of+ GHC -> GHC.buildLib flags numJobs pkg_descr lbi lib clbi+ GHCJS -> GHCJS.buildLib verbosity numJobs pkg_descr lbi lib clbi+ UHC -> UHC.buildLib verbosity pkg_descr lbi lib clbi+ _ -> dieWithException verbosity BuildingNotSupportedWithCompiler++-- | Build a foreign library+--+-- NOTE: We assume that we already checked that we can actually build the+-- foreign library in configure.+buildFLib+ :: Verbosity+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> ForeignLib+ -> ComponentLocalBuildInfo+ -> IO ()+buildFLib verbosity numJobs pkg_descr lbi flib clbi =+ case compilerFlavor (compiler lbi) of+ GHC -> GHC.buildFLib verbosity numJobs pkg_descr lbi flib clbi+ _ -> dieWithException verbosity BuildingNotSupportedWithCompiler++buildExe+ :: Verbosity+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> Executable+ -> ComponentLocalBuildInfo+ -> IO ()+buildExe verbosity numJobs pkg_descr lbi exe clbi =+ case compilerFlavor (compiler lbi) of+ GHC -> GHC.buildExe verbosity numJobs pkg_descr lbi exe clbi+ GHCJS -> GHCJS.buildExe verbosity numJobs pkg_descr lbi exe clbi+ UHC -> UHC.buildExe verbosity pkg_descr lbi exe clbi+ _ -> dieWithException verbosity BuildingNotSupportedWithCompiler++replLib+ :: ReplFlags+ -> PackageDescription+ -> LocalBuildInfo+ -> Library+ -> ComponentLocalBuildInfo+ -> IO ()+replLib replFlags pkg_descr lbi lib clbi =+ let verbosity = fromFlag $ replVerbosity replFlags+ opts = replReplOptions replFlags+ in case compilerFlavor (compiler lbi) of+ -- 'cabal repl' doesn't need to support 'ghc --make -j', so we just pass+ -- NoFlag as the numJobs parameter.+ GHC -> GHC.replLib replFlags NoFlag pkg_descr lbi lib clbi+ GHCJS -> GHCJS.replLib (replOptionsFlags opts) verbosity NoFlag pkg_descr lbi lib clbi+ _ -> dieWithException verbosity REPLNotSupported++replExe+ :: ReplFlags+ -> PackageDescription+ -> LocalBuildInfo+ -> Executable+ -> ComponentLocalBuildInfo+ -> IO ()+replExe flags pkg_descr lbi exe clbi =+ let verbosity = fromFlag $ replVerbosity flags+ in case compilerFlavor (compiler lbi) of+ GHC -> GHC.replExe flags NoFlag pkg_descr lbi exe clbi+ GHCJS ->+ GHCJS.replExe+ (replOptionsFlags $ replReplOptions flags)+ verbosity+ NoFlag+ pkg_descr+ lbi+ exe+ clbi+ _ -> dieWithException verbosity REPLNotSupported++replFLib+ :: ReplFlags+ -> PackageDescription+ -> LocalBuildInfo+ -> ForeignLib+ -> ComponentLocalBuildInfo+ -> IO ()+replFLib flags pkg_descr lbi exe clbi =+ let verbosity = fromFlag $ replVerbosity flags+ in case compilerFlavor (compiler lbi) of+ GHC -> GHC.replFLib flags NoFlag pkg_descr lbi exe clbi+ _ -> dieWithException verbosity REPLNotSupported++-- | Runs 'componentInitialBuildSteps' on every configured component.+--+-- Legacy function: does not run pre-build hooks or pre-processors. This function+-- is insufficient on its own to prepare the build for a package.+--+-- Consumers wanting to prepare the sources of a package, e.g. in order to+-- launch a REPL session, are advised to run @Setup repl --repl-multi-file=<fn>@+-- instead.+initialBuildSteps+ :: FilePath+ -- ^ "dist" prefix+ -> PackageDescription+ -- ^ mostly information from the .cabal file+ -> LocalBuildInfo+ -- ^ Configuration information+ -> Verbosity+ -- ^ The verbosity to use+ -> IO ()+initialBuildSteps distPref pkg_descr lbi verbosity =+ withAllComponentsInBuildOrder pkg_descr lbi $ \_comp clbi ->+ componentInitialBuildSteps distPref pkg_descr lbi clbi verbosity+{-# DEPRECATED+ initialBuildSteps+ "This function does not prepare all source files for a package. Suggestion: use 'Setup repl --repl-multi-file=<fn>'."+ #-}++-- | Creates the autogenerated files for a particular configured component.+--+-- Legacy function: does not run pre-build hooks or pre-processors. This function+-- is insufficient on its own to prepare the build for a component.+--+-- Consumers wanting to prepare the sources of a component, e.g. in order to+-- launch a REPL session, are advised to run+-- @Setup repl <compName> --repl-multi-file=<fn>@ instead.+componentInitialBuildSteps+ :: FilePath+ -- ^ "dist" prefix+ -> PackageDescription+ -- ^ mostly information from the .cabal file+ -> LocalBuildInfo+ -- ^ Configuration information+ -> ComponentLocalBuildInfo+ -- ^ Build info about the component+ -> Verbosity+ -- ^ The verbosity to use+ -> IO ()+componentInitialBuildSteps _distPref pkg_descr lbi clbi verbosity = do+ let compBuildDir = interpretSymbolicPathLBI lbi $ componentBuildDir lbi clbi+ createDirectoryIfMissingVerbose verbosity True compBuildDir+ writeBuiltinAutogenFiles verbosity pkg_descr lbi clbi+{-# DEPRECATED+ componentInitialBuildSteps+ "This function does not prepare all source files for a component. Suggestion: use 'Setup repl <compName> --repl-multi-file=<fn>'."+ #-}++-- | Creates the autogenerated files for a particular configured component,+-- and runs the pre-build hook.+preBuildComponent+ :: (LocalBuildInfo -> TargetInfo -> IO ())+ -- ^ pre-build hook+ -> Verbosity+ -> LocalBuildInfo+ -- ^ Configuration information+ -> TargetInfo+ -> IO ()+preBuildComponent preBuildHook verbosity lbi tgt = do+ let pkg_descr = localPkgDescr lbi+ clbi = targetCLBI tgt+ compBuildDir = interpretSymbolicPathLBI lbi $ componentBuildDir lbi clbi+ createDirectoryIfMissingVerbose verbosity True compBuildDir+ writeBuiltinAutogenFiles verbosity pkg_descr lbi clbi+ preBuildHook lbi tgt++-- | Generate and write to disk all built-in autogenerated files+-- for the specified component. These files will be put in the+-- autogenerated module directory for this component+-- (see 'autogenComponentsModuleDir').+--+-- This includes:+--+-- - @Paths_<pkg>.hs@,+-- - @PackageInfo_<pkg>.hs@,+-- - Backpack signature files for components that are not fully instantiated,+-- - @cabal_macros.h@.+writeBuiltinAutogenFiles+ :: Verbosity+ -> PackageDescription+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> IO ()+writeBuiltinAutogenFiles verbosity pkg lbi clbi =+ writeAutogenFiles verbosity lbi clbi $ builtinAutogenFiles pkg lbi clbi++-- | Built-in autogenerated files and their contents. This includes:+--+-- - @Paths_<pkg>.hs@,+-- - @PackageInfo_<pkg>.hs@,+-- - Backpack signature files for components that are not fully instantiated,+-- - @cabal_macros.h@.+builtinAutogenFiles+ :: PackageDescription+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> Map AutogenFile AutogenFileContents+builtinAutogenFiles pkg lbi clbi =+ Map.insert pathsFile pathsContents $+ Map.insert packageInfoFile packageInfoContents $+ Map.insert cppHeaderFile cppHeaderContents $+ emptySignatureModules clbi+ where+ pathsFile = AutogenModule (autogenPathsModuleName pkg) (Suffix "hs")+ pathsContents = toUTF8LBS $ generatePathsModule pkg lbi clbi+ packageInfoFile = AutogenModule (autogenPackageInfoModuleName pkg) (Suffix "hs")+ packageInfoContents = toUTF8LBS $ generatePackageInfoModule pkg lbi+ cppHeaderFile = AutogenFile $ toShortText cppHeaderName+ cppHeaderContents = toUTF8LBS $ generateCabalMacrosHeader pkg lbi clbi++-- | An empty @".hsig"@ Backpack signature module for each requirement, so that+-- GHC has a source file to look at it when it needs to typecheck+-- a signature. It's harmless to generate these modules, even when+-- there is a real @hsig@ file written by the user, since+-- include path ordering ensures that the real @hsig@ file+-- will always be picked up before the autogenerated one.+emptySignatureModules+ :: ComponentLocalBuildInfo+ -> Map AutogenFile AutogenFileContents+emptySignatureModules clbi =+ case clbi of+ LibComponentLocalBuildInfo{componentInstantiatedWith = insts} ->+ Map.fromList+ [ ( AutogenModule modName (Suffix "hsig")+ , emptyHsigFile modName+ )+ | (modName, _) <- insts+ ]+ _ -> Map.empty+ where+ emptyHsigFile :: ModuleName -> AutogenFileContents+ emptyHsigFile modName =+ toUTF8LBS $+ "{-# OPTIONS_GHC -w #-}\n"+ ++ "{-# LANGUAGE NoImplicitPrelude #-}\n"+ ++ "signature "+ ++ prettyShow modName+ ++ " where"++data AutogenFile+ = AutogenModule !ModuleName !Suffix+ | AutogenFile !ShortText+ deriving (Show, Eq, Ord)++-- | A representation of the contents of an autogenerated file.+type AutogenFileContents = LBS.ByteString++-- | Write the given autogenerated files in the autogenerated modules+-- directory for the component.+writeAutogenFiles+ :: Verbosity+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> Map AutogenFile AutogenFileContents+ -> IO ()+writeAutogenFiles verbosity lbi clbi autogenFiles = do+ -- Ensure that the overall autogenerated files directory exists.+ createDirectoryIfMissingVerbose verbosity True autogenDir+ for_ (Map.assocs autogenFiles) $ \(file, contents) -> do+ let path = case file of+ AutogenModule modName (Suffix ext) ->+ autogenDir </> ModuleName.toFilePath modName <.> ext+ AutogenFile fileName ->+ autogenDir </> fromShortText fileName+ dir = takeDirectory path+ -- Ensure that the directory subtree for this autogenerated file exists.+ createDirectoryIfMissingVerbose verbosity True dir+ -- Write the contents of the file.+ rewriteFileLBS verbosity path contents+ where+ autogenDir = interpretSymbolicPathLBI lbi $ autogenComponentModulesDir lbi clbi
+ src/Distribution/Simple/Build/Inputs.hs view
@@ -0,0 +1,74 @@+module Distribution.Simple.Build.Inputs+ ( -- * Inputs of actions for building components+ PreBuildComponentInputs (..)++ -- * Queries over the component being built+ , buildVerbosity+ , buildComponent+ , buildIsLib+ , buildCLBI+ , buildBI+ , buildCompiler++ -- * Re-exports+ , BuildingWhat (..)+ , LocalBuildInfo (..)+ , TargetInfo (..)+ , buildingWhatCommonFlags+ , buildingWhatVerbosity+ , buildingWhatWorkingDir+ , buildingWhatDistPref+ )+where++import Distribution.Simple.Compiler+import Distribution.Simple.Setup hiding+ ( BuildFlags (buildVerbosity)+ )+import Distribution.Types.BuildInfo+import Distribution.Types.Component+import Distribution.Types.ComponentLocalBuildInfo+import Distribution.Types.LocalBuildInfo+import Distribution.Types.TargetInfo+import Distribution.Verbosity++-- | The information required for a build computation which is available right+-- before building each component, i.e. the pre-build component inputs.+data PreBuildComponentInputs = PreBuildComponentInputs+ { buildingWhat :: BuildingWhat+ -- ^ What kind of build are we doing?+ , localBuildInfo :: LocalBuildInfo+ -- ^ Information about the package+ , targetInfo :: TargetInfo+ -- ^ Information about an individual component+ }++-- | Get the @'Verbosity'@ from the context the component being built is in.+buildVerbosity :: PreBuildComponentInputs -> Verbosity+buildVerbosity = buildingWhatVerbosity . buildingWhat++-- | Get the @'Component'@ being built.+buildComponent :: PreBuildComponentInputs -> Component+buildComponent = targetComponent . targetInfo++-- | Is the @'Component'@ being built a @'Library'@?+buildIsLib :: PreBuildComponentInputs -> Bool+buildIsLib = do+ component <- buildComponent+ let isLib+ | CLib{} <- component = True+ | otherwise = False+ return isLib+{-# INLINE buildIsLib #-}++-- | Get the @'ComponentLocalBuildInfo'@ for the component being built.+buildCLBI :: PreBuildComponentInputs -> ComponentLocalBuildInfo+buildCLBI = targetCLBI . targetInfo++-- | Get the @'BuildInfo'@ of the component being built.+buildBI :: PreBuildComponentInputs -> BuildInfo+buildBI = componentBuildInfo . buildComponent++-- | Get the @'Compiler'@ being used to build the component.+buildCompiler :: PreBuildComponentInputs -> Compiler+buildCompiler = compiler . localBuildInfo
src/Distribution/Simple/Build/Macros.hs view
@@ -1,4 +1,5 @@ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Build.Macros -- Copyright : Simon Marlow 2008@@ -18,93 +19,96 @@ -- numbers. -- -- TODO Figure out what to do about backpack and internal libraries. It is very--- suspecious that this stuff works with munged package identifiers-module Distribution.Simple.Build.Macros (- generateCabalMacrosHeader,- generatePackageVersionMacros,+-- suspicious that this stuff works with munged package identifiers+module Distribution.Simple.Build.Macros+ ( generateCabalMacrosHeader+ , generatePackageVersionMacros ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -import Distribution.Version import Distribution.PackageDescription+import Distribution.Pretty import Distribution.Simple.LocalBuildInfo import Distribution.Simple.Program.Db import Distribution.Simple.Program.Types import Distribution.Types.MungedPackageId import Distribution.Types.MungedPackageName-import Distribution.Pretty+import Distribution.Version import qualified Distribution.Simple.Build.Macros.Z as Z -- | The contents of the @cabal_macros.h@ for the given configured package.--- generateCabalMacrosHeader :: PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> String-generateCabalMacrosHeader pkg_descr lbi clbi = Z.render Z.Z- { Z.zPackages = map mkZPackage $ package pkg_descr : map getPid (componentPackageDeps clbi)- , Z.zTools =- [ Z.ZTool- { Z.ztoolName = programId prog+generateCabalMacrosHeader pkg_descr lbi clbi =+ Z.render+ Z.Z+ { Z.zPackages = map mkZPackage $ package pkg_descr : map getPid (componentPackageDeps clbi)+ , Z.zTools =+ [ Z.ZTool+ { Z.ztoolName = programId prog , Z.ztoolVersion = ver- , Z.ztoolX = major1- , Z.ztoolY = major2- , Z.ztoolZ = minor+ , Z.ztoolX = major1+ , Z.ztoolY = major2+ , Z.ztoolZ = minor }- | prog <- configuredPrograms $ withPrograms lbi- , ver <- maybeToList (programVersion prog)- , let (major1,major2,minor) = majorMinor ver- ]- , Z.zPackageKey = case clbi of- LibComponentLocalBuildInfo{} -> componentCompatPackageKey clbi- _ -> ""- , Z.zComponentId = prettyShow (componentComponentId clbi)- , Z.zPackageVersion = pkgVersion (package pkg_descr)- , Z.zNotNull = not . null- , Z.zManglePkgName = map fixchar . unPackageName- , Z.zMangleStr = map fixchar- }+ | prog <- configuredPrograms $ withPrograms lbi+ , ver <- maybeToList (programVersion prog)+ , let (major1, major2, minor) = majorMinor ver+ ]+ , Z.zPackageKey = case clbi of+ LibComponentLocalBuildInfo{componentCompatPackageKey = compatPackageKey} -> compatPackageKey+ _ -> ""+ , Z.zComponentId = prettyShow (componentComponentId clbi)+ , Z.zPackageVersion = pkgVersion (package pkg_descr)+ , Z.zNotNull = not . null+ , Z.zManglePkgName = map fixchar . unPackageName+ , Z.zMangleStr = map fixchar+ } where getPid (_, MungedPackageId (MungedPackageName pn _) v) =- -- NB: Drop the library name! We're just reporting package versions.- -- This would have to be revisited if you are allowed to depend- -- on different versions of the same package- PackageIdentifier pn v+ -- NB: Drop the library name! We're just reporting package versions.+ -- This would have to be revisited if you are allowed to depend+ -- on different versions of the same package+ PackageIdentifier pn v -- | Helper function that generates just the @VERSION_pkg@ and @MIN_VERSION_pkg@ -- macros for a list of package ids (usually used with the specific deps of -- a configured package).--- generatePackageVersionMacros :: Version -> [PackageId] -> String-generatePackageVersionMacros ver pkgids = Z.render Z.Z- { Z.zPackages = map mkZPackage pkgids- , Z.zTools = []- , Z.zPackageKey = ""- , Z.zComponentId = ""- , Z.zPackageVersion = ver- , Z.zNotNull = not . null- , Z.zManglePkgName = map fixchar . unPackageName- , Z.zMangleStr = map fixchar- }+generatePackageVersionMacros ver pkgids =+ Z.render+ Z.Z+ { Z.zPackages = map mkZPackage pkgids+ , Z.zTools = []+ , Z.zPackageKey = ""+ , Z.zComponentId = ""+ , Z.zPackageVersion = ver+ , Z.zNotNull = not . null+ , Z.zManglePkgName = map fixchar . unPackageName+ , Z.zMangleStr = map fixchar+ } mkZPackage :: PackageId -> Z.ZPackage-mkZPackage (PackageIdentifier name ver) = Z.ZPackage- { Z.zpkgName = name+mkZPackage (PackageIdentifier name ver) =+ Z.ZPackage+ { Z.zpkgName = name , Z.zpkgVersion = ver- , Z.zpkgX = major1- , Z.zpkgY = major2- , Z.zpkgZ = minor+ , Z.zpkgX = major1+ , Z.zpkgY = major2+ , Z.zpkgZ = minor } where- (major1,major2,minor) = majorMinor ver+ (major1, major2, minor) = majorMinor ver majorMinor :: Version -> (String, String, String) majorMinor ver = case map show (versionNumbers ver) of- [] -> ("0", "0", "0")- [x] -> (x, "0", "0")- [x,y] -> (x, y, "0")- (x:y:z:_) -> (x, y, z)+ [] -> ("0", "0", "0")+ [x] -> (x, "0", "0")+ [x, y] -> (x, y, "0")+ (x : y : z : _) -> (x, y, z) fixchar :: Char -> Char fixchar '-' = '_'-fixchar c = c+fixchar c = c
src/Distribution/Simple/Build/Macros/Z.hs view
@@ -1,9 +1,10 @@+{- FOURMOLU_DISABLE -} {-# LANGUAGE DeriveGeneric #-} module Distribution.Simple.Build.Macros.Z (render, Z(..), ZPackage (..), ZTool (..)) where import Distribution.ZinzaPrelude data Z- = Z {zPackages :: ([ZPackage]),- zTools :: ([ZTool]),+ = Z {zPackages :: [ZPackage],+ zTools :: [ZTool], zPackageKey :: String, zComponentId :: String, zPackageVersion :: Version,
+ src/Distribution/Simple/Build/PackageInfoModule.hs view
@@ -0,0 +1,60 @@+-----------------------------------------------------------------------------++-- |+-- Module : Distribution.Simple.Build.PackageInfoModule+-- Copyright :+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Generating the PackageInfo_pkgname module.+--+-- This is a module that Cabal generates for the benefit of packages. It+-- enables them to find their package information.+module Distribution.Simple.Build.PackageInfoModule+ ( generatePackageInfoModule+ ) where++import Distribution.Compat.Prelude+import Prelude ()++import Distribution.Package+import Distribution.PackageDescription+import Distribution.Simple.Compiler+import Distribution.Simple.LocalBuildInfo+import Distribution.Utils.ShortText+import Distribution.Version++import qualified Distribution.Simple.Build.PackageInfoModule.Z as Z++-- ------------------------------------------------------------++-- * Building Paths_<pkg>.hs++-- ------------------------------------------------------------++generatePackageInfoModule :: PackageDescription -> LocalBuildInfo -> String+generatePackageInfoModule pkg_descr lbi =+ Z.render+ Z.Z+ { Z.zPackageName = showPkgName $ packageName pkg_descr+ , Z.zVersionDigits = show $ versionNumbers $ packageVersion pkg_descr+ , Z.zSynopsis = fromShortText $ synopsis pkg_descr+ , Z.zCopyright = fromShortText $ copyright pkg_descr+ , Z.zHomepage = fromShortText $ homepage pkg_descr+ , Z.zSupportsNoRebindableSyntax = supports_rebindable_syntax+ }+ where+ supports_rebindable_syntax = ghc_newer_than (mkVersion [7, 0, 1])++ ghc_newer_than minVersion =+ case compilerCompatVersion GHC (compiler lbi) of+ Nothing -> False+ Just version -> version `withinRange` orLaterVersion minVersion++showPkgName :: PackageName -> String+showPkgName = map fixchar . unPackageName++fixchar :: Char -> Char+fixchar '-' = '_'+fixchar c = c
+ src/Distribution/Simple/Build/PackageInfoModule/Z.hs view
@@ -0,0 +1,83 @@+{-# LANGUAGE DeriveGeneric #-}++module Distribution.Simple.Build.PackageInfoModule.Z (render, Z (..)) where++import Distribution.ZinzaPrelude++data Z = Z+ { zPackageName :: String+ , zVersionDigits :: String+ , zSynopsis :: String+ , zCopyright :: String+ , zHomepage :: String+ , zSupportsNoRebindableSyntax :: Bool+ }+ deriving (Generic)++render :: Z -> String+render z_root = execWriter $ do+ if (zSupportsNoRebindableSyntax z_root)+ then do+ tell "{-# LANGUAGE NoRebindableSyntax #-}\n"+ return ()+ else do+ return ()+ tell "{-# OPTIONS_GHC -Wno-missing-import-lists #-}\n"+ tell "{-# OPTIONS_GHC -w #-}\n"+ tell "\n"+ tell "{-|\n"+ tell "Module : PackageInfo_"+ tell (zPackageName z_root)+ tell "\n"+ tell "Description : Contents of some of the package's Cabal file's fields.\n"+ tell "\n"+ tell "WARNING: This module was generated by Cabal. Any modifications will be\n"+ tell "overwritten if the module is regenerated.\n"+ tell "\n"+ tell "This module exports values that record information from some of the fields of\n"+ tell "the package's Cabal package description file (Cabal file).\n"+ tell "\n"+ tell "For further information about the fields in a Cabal file, see the Cabal User\n"+ tell "Guide.\n"+ tell "-}\n"+ tell "\n"+ tell "module PackageInfo_"+ tell (zPackageName z_root)+ tell " (\n"+ tell " name,\n"+ tell " version,\n"+ tell " synopsis,\n"+ tell " copyright,\n"+ tell " homepage,\n"+ tell " ) where\n"+ tell "\n"+ tell "import Data.Version (Version(..))\n"+ tell "import Prelude\n"+ tell "\n"+ tell "-- |The content of the @name@ field of the package's Cabal file, but with any\n"+ tell "-- hyphen characters replaced by underscore characters.\n"+ tell "name :: String\n"+ tell "name = "+ tell (show $ zPackageName z_root)+ tell "\n"+ tell "-- |The content of the @version@ field of the package's Cabal file.\n"+ tell "version :: Version\n"+ tell "version = Version "+ tell (zVersionDigits z_root)+ tell " []\n"+ tell "\n"+ tell "-- |The content of the @synopsis@ field of the package's Cabal file.\n"+ tell "synopsis :: String\n"+ tell "synopsis = "+ tell (show $ zSynopsis z_root)+ tell "\n"+ tell "-- |The content of the @copyright@ field of the package's Cabal file.\n"+ tell "copyright :: String\n"+ tell "copyright = "+ tell (show $ zCopyright z_root)+ tell "\n"+ tell "-- |The content of the @homepage@ field of the package's Cabal file.\n"+ tell "homepage :: String\n"+ tell "homepage = "+ tell (show $ zHomepage z_root)+ tell "\n"
src/Distribution/Simple/Build/PathsModule.hs view
@@ -1,4 +1,5 @@ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Build.Macros -- Copyright : Isaac Jones 2003-2005,@@ -13,9 +14,9 @@ -- This is a module that Cabal generates for the benefit of packages. It -- enables them to find their version number and find any installed data files -- at runtime. This code should probably be split off into another module.----module Distribution.Simple.Build.PathsModule (- generatePathsModule, pkgPathEnvVar+module Distribution.Simple.Build.PathsModule+ ( generatePathsModule+ , pkgPathEnvVar ) where import Distribution.Compat.Prelude@@ -25,121 +26,125 @@ import Distribution.PackageDescription import Distribution.Simple.Compiler import Distribution.Simple.LocalBuildInfo-import Distribution.Simple.Utils (shortRelativePath)+import Distribution.Simple.Utils (shortRelativePath) import Distribution.System import Distribution.Version import qualified Distribution.Simple.Build.PathsModule.Z as Z -- ------------------------------------------------------------+ -- * Building Paths_<pkg>.hs+ -- ------------------------------------------------------------ generatePathsModule :: PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> String-generatePathsModule pkg_descr lbi clbi = Z.render Z.Z- { Z.zPackageName = packageName pkg_descr- , Z.zVersionDigits = show $ versionNumbers $ packageVersion pkg_descr- , Z.zSupportsCpp = supports_cpp- , Z.zSupportsNoRebindableSyntax = supports_rebindable_syntax- , Z.zAbsolute = absolute- , Z.zRelocatable = relocatable lbi- , Z.zIsWindows = isWindows- , Z.zIsI386 = buildArch == I386- , Z.zIsX8664 = buildArch == X86_64- , Z.zNot = not- , Z.zManglePkgName = showPkgName-- , Z.zPrefix = show flat_prefix- , Z.zBindir = zBindir- , Z.zLibdir = zLibdir- , Z.zDynlibdir = zDynlibdir- , Z.zDatadir = zDatadir- , Z.zLibexecdir = zLibexecdir- , Z.zSysconfdir = zSysconfdir- }+generatePathsModule pkg_descr lbi clbi =+ Z.render+ Z.Z+ { Z.zPackageName = packageName pkg_descr+ , Z.zVersionDigits = show $ versionNumbers $ packageVersion pkg_descr+ , Z.zSupportsCpp = supports_cpp+ , Z.zSupportsNoRebindableSyntax = supports_rebindable_syntax+ , Z.zAbsolute = absolute+ , Z.zRelocatable = relocatable lbi+ , Z.zIsWindows = isWindows+ , Z.zIsI386 = buildArch == I386+ , Z.zIsX8664 = buildArch == X86_64+ , Z.zIsAArch64 = buildArch == AArch64+ , Z.zNot = not+ , Z.zManglePkgName = showPkgName+ , Z.zPrefix = show flat_prefix+ , Z.zBindir = zBindir+ , Z.zLibdir = zLibdir+ , Z.zDynlibdir = zDynlibdir+ , Z.zDatadir = zDatadir+ , Z.zLibexecdir = zLibexecdir+ , Z.zSysconfdir = zSysconfdir+ } where- supports_cpp = supports_language_pragma- supports_rebindable_syntax = ghc_newer_than (mkVersion [7,0,1])- supports_language_pragma = ghc_newer_than (mkVersion [6,6,1])+ supports_cpp = supports_language_pragma+ supports_rebindable_syntax = ghc_newer_than (mkVersion [7, 0, 1])+ supports_language_pragma = ghc_newer_than (mkVersion [6, 6, 1]) ghc_newer_than minVersion =- case compilerCompatVersion GHC (compiler lbi) of- Nothing -> False- Just version -> version `withinRange` orLaterVersion minVersion+ case compilerCompatVersion GHC (compiler lbi) of+ Nothing -> False+ Just version -> version `withinRange` orLaterVersion minVersion -- In several cases we cannot make relocatable installations absolute =- hasLibs pkg_descr -- we can only make progs relocatable- || isNothing flat_bindirrel -- if the bin dir is an absolute path- || not (supportsRelocatableProgs (compilerFlavor (compiler lbi)))+ hasLibs pkg_descr -- we can only make progs relocatable+ || isNothing flat_bindirrel -- if the bin dir is an absolute path+ || not (supportsRelocatableProgs (compilerFlavor (compiler lbi))) -- TODO: Here, and with zIsI386 & zIs8664 we should use TARGET platform isWindows = case buildOS of- Windows -> True- _ -> False+ Windows -> True+ _ -> False - supportsRelocatableProgs GHC = isWindows+ supportsRelocatableProgs GHC = isWindows supportsRelocatableProgs GHCJS = isWindows- supportsRelocatableProgs _ = False+ supportsRelocatableProgs _ = False cid = componentUnitId clbi InstallDirs- { bindir = flat_bindir- , libdir = flat_libdir- , dynlibdir = flat_dynlibdir- , datadir = flat_datadir- , libexecdir = flat_libexecdir- , sysconfdir = flat_sysconfdir- , prefix = flat_prefix- } = absoluteInstallCommandDirs pkg_descr lbi cid NoCopyDest+ { bindir = flat_bindir+ , libdir = flat_libdir+ , dynlibdir = flat_dynlibdir+ , datadir = flat_datadir+ , libexecdir = flat_libexecdir+ , sysconfdir = flat_sysconfdir+ , prefix = flat_prefix+ } = absoluteInstallCommandDirs pkg_descr lbi cid NoCopyDest InstallDirs- { bindir = flat_bindirrel- , libdir = flat_libdirrel- , dynlibdir = flat_dynlibdirrel- , datadir = flat_datadirrel- , libexecdir = flat_libexecdirrel- , sysconfdir = flat_sysconfdirrel- } = prefixRelativeComponentInstallDirs (packageId pkg_descr) lbi cid+ { bindir = flat_bindirrel+ , libdir = flat_libdirrel+ , dynlibdir = flat_dynlibdirrel+ , datadir = flat_datadirrel+ , libexecdir = flat_libexecdirrel+ , sysconfdir = flat_sysconfdirrel+ } = prefixRelativeComponentInstallDirs (packageId pkg_descr) lbi cid zBindir, zLibdir, zDynlibdir, zDatadir, zLibexecdir, zSysconfdir :: String (zBindir, zLibdir, zDynlibdir, zDatadir, zLibexecdir, zSysconfdir)- | relocatable lbi =- ( show flat_bindir_reloc- , show flat_libdir_reloc- , show flat_dynlibdir_reloc- , show flat_datadir_reloc- , show flat_libexecdir_reloc- , show flat_sysconfdir_reloc- )- | absolute =- ( show flat_bindir- , show flat_libdir- , show flat_dynlibdir- , show flat_datadir- , show flat_libexecdir- , show flat_sysconfdir- )- | isWindows =- ( "maybe (error \"PathsModule.generate\") id (" ++ show flat_bindirrel ++ ")"- , mkGetDir flat_libdir flat_libdirrel- , mkGetDir flat_dynlibdir flat_dynlibdirrel- , mkGetDir flat_datadir flat_datadirrel- , mkGetDir flat_libexecdir flat_libexecdirrel- , mkGetDir flat_sysconfdir flat_sysconfdirrel- )- | otherwise =- error "panic! generatePathsModule: should never happen"+ | relocatable lbi =+ ( show flat_bindir_reloc+ , show flat_libdir_reloc+ , show flat_dynlibdir_reloc+ , show flat_datadir_reloc+ , show flat_libexecdir_reloc+ , show flat_sysconfdir_reloc+ )+ | absolute =+ ( show flat_bindir+ , show flat_libdir+ , show flat_dynlibdir+ , show flat_datadir+ , show flat_libexecdir+ , show flat_sysconfdir+ )+ | isWindows =+ ( "maybe (error \"PathsModule.generate\") id (" ++ show flat_bindirrel ++ ")"+ , mkGetDir flat_libdir flat_libdirrel+ , mkGetDir flat_dynlibdir flat_dynlibdirrel+ , mkGetDir flat_datadir flat_datadirrel+ , mkGetDir flat_libexecdir flat_libexecdirrel+ , mkGetDir flat_sysconfdir flat_sysconfdirrel+ )+ | otherwise =+ error "panic! generatePathsModule: should never happen" mkGetDir :: FilePath -> Maybe FilePath -> String- mkGetDir _ (Just dirrel) = "getPrefixDirRel " ++ show dirrel- mkGetDir dir Nothing = "return " ++ show dir+ mkGetDir _ (Just dirrel) = "getPrefixDirRel " ++ show dirrel+ mkGetDir dir Nothing = "return " ++ show dir - flat_bindir_reloc = shortRelativePath flat_prefix flat_bindir- flat_libdir_reloc = shortRelativePath flat_prefix flat_libdir- flat_dynlibdir_reloc = shortRelativePath flat_prefix flat_dynlibdir- flat_datadir_reloc = shortRelativePath flat_prefix flat_datadir+ flat_bindir_reloc = shortRelativePath flat_prefix flat_bindir+ flat_libdir_reloc = shortRelativePath flat_prefix flat_libdir+ flat_dynlibdir_reloc = shortRelativePath flat_prefix flat_dynlibdir+ flat_datadir_reloc = shortRelativePath flat_prefix flat_datadir flat_libexecdir_reloc = shortRelativePath flat_prefix flat_libexecdir flat_sysconfdir_reloc = shortRelativePath flat_prefix flat_sysconfdir @@ -149,15 +154,17 @@ -- Note: The format of these strings is part of Cabal's public API; -- changing this function constitutes a *backwards-compatibility* break. pkgPathEnvVar- :: PackageDescription- -> String -- ^ path component; one of \"bindir\", \"libdir\", -- \"datadir\", \"libexecdir\", or \"sysconfdir\"- -> String -- ^ environment variable name+ :: PackageDescription+ -> String+ -- ^ path component; one of \"bindir\", \"libdir\", -- \"datadir\", \"libexecdir\", or \"sysconfdir\"+ -> String+ -- ^ environment variable name pkgPathEnvVar pkg_descr var =- showPkgName (packageName pkg_descr) ++ "_" ++ var+ showPkgName (packageName pkg_descr) ++ "_" ++ var showPkgName :: PackageName -> String showPkgName = map fixchar . unPackageName fixchar :: Char -> Char fixchar '-' = '_'-fixchar c = c+fixchar c = c
src/Distribution/Simple/Build/PathsModule/Z.hs view
@@ -1,3 +1,4 @@+{- FOURMOLU_DISABLE -} {-# LANGUAGE DeriveGeneric #-} module Distribution.Simple.Build.PathsModule.Z (render, Z(..)) where import Distribution.ZinzaPrelude@@ -11,6 +12,7 @@ zIsWindows :: Bool, zIsI386 :: Bool, zIsX8664 :: Bool,+ zIsAArch64 :: Bool, zPrefix :: FilePath, zBindir :: FilePath, zLibdir :: FilePath,@@ -41,8 +43,35 @@ return () else do return ()- tell "{-# OPTIONS_GHC -fno-warn-missing-import-lists #-}\n"+ if (zSupportsCpp z_root)+ then do+ tell "#if __GLASGOW_HASKELL__ >= 810\n"+ tell "{-# OPTIONS_GHC -Wno-prepositive-qualified-module #-}\n"+ tell "#endif\n"+ return ()+ else do+ return ()+ tell "{-# OPTIONS_GHC -Wno-missing-import-lists #-}\n" tell "{-# OPTIONS_GHC -w #-}\n"+ tell "\n"+ tell "{-|\n"+ tell "Module : Paths_"+ tell (zManglePkgName z_root (zPackageName z_root))+ tell "\n"+ tell "Description : Data file location, and package version and installation\n"+ tell " directories.\n"+ tell "\n"+ tell "WARNING: This module was generated by Cabal. Any modifications will be\n"+ tell "overwritten if the module is regenerated.\n"+ tell "\n"+ tell "This module exports a function to locate data files, and values that record\n"+ tell "the version of the package and some directories which the package has been\n"+ tell "configured to be installed into.\n"+ tell "\n"+ tell "For further information about Cabal's options for its configuration step, and\n"+ tell "their default values, see the Cabal User Guide.\n"+ tell "-}\n"+ tell "\n" tell "module Paths_" tell (zManglePkgName z_root (zPackageName z_root)) tell " (\n"@@ -60,7 +89,6 @@ return () tell "\n" tell "import qualified Control.Exception as Exception\n"- tell "import qualified Data.List as List\n" tell "import Data.Version (Version(..))\n" tell "import System.Environment (getEnv)\n" tell "import Prelude\n"@@ -92,19 +120,88 @@ tell "catchIO = Exception.catch\n" return () tell "\n"+ tell "-- |The package version.\n" tell "version :: Version\n" tell "version = Version " tell (zVersionDigits z_root) tell " []\n" tell "\n"+ tell "-- |If the argument is a filename, the result is the name of a corresponding\n"+ tell "-- file on the system on which the program is running, if the file were listed\n"+ tell "-- in the @data-files@ field of the package's Cabal package description file.\n"+ tell "-- No check is performed that the given filename is listed in that field.\n" tell "getDataFileName :: FilePath -> IO FilePath\n" tell "getDataFileName name = do\n" tell " dir <- getDataDir\n" tell " return (dir `joinFileName` name)\n" tell "\n"- tell "getBinDir, getLibDir, getDynLibDir, getDataDir, getLibexecDir, getSysconfDir :: IO FilePath\n"+ tell "-- |The location of the directory specified by Cabal's @--bindir@ option (where\n"+ tell "-- executables that the user might invoke are installed). This can be overridden\n"+ tell "-- at runtime using the environment variable "+ tell (zManglePkgName z_root (zPackageName z_root))+ tell "_bindir.\n"+ tell "getBinDir :: IO FilePath\n" tell "\n"+ tell "-- |The location of the directory specified by Cabal's @--libdir@ option (where\n"+ tell "-- object libraries are installed). This can be overridden at runtime using the\n"+ tell "-- environment variable "+ tell (zManglePkgName z_root (zPackageName z_root))+ tell "_libdir.\n"+ tell "getLibDir :: IO FilePath\n" tell "\n"+ tell "-- |The location of the directory specified by Cabal's @--dynlibdir@ option\n"+ tell "-- (where dynamic libraries are installed). This can be overridden at runtime\n"+ tell "-- using the environment variable "+ tell (zManglePkgName z_root (zPackageName z_root))+ tell "_dynlibdir.\n"+ tell "getDynLibDir :: IO FilePath\n"+ tell "\n"+ tell "-- |The location of the directory specified by Cabal's @--datadir@ option (where\n"+ tell "-- architecture-independent data files are installed). This can be overridden at\n"+ tell "-- runtime using the environment variable "+ tell (zManglePkgName z_root (zPackageName z_root))+ tell "_datadir.\n"+ tell "getDataDir :: IO FilePath\n"+ tell "\n"+ tell "-- |The location of the directory specified by Cabal's @--libexedir@ option\n"+ tell "-- (where executables that are not expected to be invoked directly by the user\n"+ tell "-- are installed). This can be overridden at runtime using the environment\n"+ tell "-- variable "+ tell (zManglePkgName z_root (zPackageName z_root))+ tell "_libexedir.\n"+ tell "getLibexecDir :: IO FilePath\n"+ tell "\n"+ tell "-- |The location of the directory specified by Cabal's @--sysconfdir@ option\n"+ tell "-- (where configuration files are installed). This can be overridden at runtime\n"+ tell "-- using the environment variable "+ tell (zManglePkgName z_root (zPackageName z_root))+ tell "_sysconfdir.\n"+ tell "getSysconfDir :: IO FilePath\n"+ tell "\n"+ let+ z_var0_function_defs = do+ tell "minusFileName :: FilePath -> String -> FilePath\n"+ tell "minusFileName dir \"\" = dir\n"+ tell "minusFileName dir \".\" = dir\n"+ tell "minusFileName dir suffix =\n"+ tell " minusFileName (fst (splitFileName dir)) (fst (splitFileName suffix))\n"+ tell "\n"+ tell "splitFileName :: FilePath -> (String, String)\n"+ tell "splitFileName p = (reverse (path2++drive), reverse fname)\n"+ tell " where\n"+ tell " (path,drive) = case p of\n"+ tell " (c:':':p') -> (reverse p',[':',c])\n"+ tell " _ -> (reverse p ,\"\")\n"+ tell " (fname,path1) = break isPathSeparator path\n"+ tell " path2 = case path1 of\n"+ tell " [] -> \".\"\n"+ tell " [_] -> path1 -- don't remove the trailing slash if\n"+ tell " -- there is only one character\n"+ tell " (c:path') | isPathSeparator c -> path'\n"+ tell " _ -> path1\n"+ return ()+ tell "\n"+ tell "\n" if (zRelocatable z_root) then do tell "\n"@@ -147,6 +244,8 @@ tell (zSysconfdir z_root) tell ")\n" tell "\n"+ z_var0_function_defs+ tell "\n" return () else do if (zAbsolute z_root)@@ -237,6 +336,8 @@ tell ") `joinFileName` dirRel)\n" tell " | otherwise -> try_size (size * 2)\n" tell "\n"+ z_var0_function_defs+ tell "\n" if (zIsI386 z_root) then do tell "foreign import stdcall unsafe \"windows.h GetModuleFileNameW\"\n"@@ -249,9 +350,16 @@ tell " c_GetModuleFileName :: Ptr () -> CWString -> Int32 -> IO Int32\n" return () else do- tell "-- win32 supported only with I386, X86_64\n"- tell "c_GetModuleFileName :: Ptr () -> CWString -> Int32 -> IO Int32\n"- tell "c_GetModuleFileName = _\n"+ if (zIsAArch64 z_root)+ then do+ tell "foreign import ccall unsafe \"windows.h GetModuleFileNameW\"\n"+ tell " c_GetModuleFileName :: Ptr () -> CWString -> Int32 -> IO Int32\n"+ return ()+ else do+ tell "-- win32 supported only with I386, X86_64, AArch64\n"+ tell "c_GetModuleFileName :: Ptr () -> CWString -> Int32 -> IO Int32\n"+ tell "c_GetModuleFileName = _\n"+ return () return () return () tell "\n"@@ -266,38 +374,18 @@ return () tell "\n" tell "\n"- if (zNot z_root (zAbsolute z_root))- then do- tell "minusFileName :: FilePath -> String -> FilePath\n"- tell "minusFileName dir \"\" = dir\n"- tell "minusFileName dir \".\" = dir\n"- tell "minusFileName dir suffix =\n"- tell " minusFileName (fst (splitFileName dir)) (fst (splitFileName suffix))\n"- tell "\n"- tell "splitFileName :: FilePath -> (String, String)\n"- tell "splitFileName p = (reverse (path2++drive), reverse fname)\n"- tell " where\n"- tell " (path,drive) = case p of\n"- tell " (c:':':p') -> (reverse p',[':',c])\n"- tell " _ -> (reverse p ,\"\")\n"- tell " (fname,path1) = break isPathSeparator path\n"- tell " path2 = case path1 of\n"- tell " [] -> \".\"\n"- tell " [_] -> path1 -- don't remove the trailing slash if\n"- tell " -- there is only one character\n"- tell " (c:path') | isPathSeparator c -> path'\n"- tell " _ -> path1\n"- return ()- else do- return ()- tell "\n" tell "joinFileName :: String -> String -> FilePath\n" tell "joinFileName \"\" fname = fname\n" tell "joinFileName \".\" fname = fname\n" tell "joinFileName dir \"\" = dir\n"- tell "joinFileName dir fname\n"- tell " | isPathSeparator (List.last dir) = dir ++ fname\n"+ tell "joinFileName dir@(c:cs) fname\n"+ tell " | isPathSeparator (lastChar c cs) = dir ++ fname\n" tell " | otherwise = dir ++ pathSeparator : fname\n"+ tell " where\n"+ tell " -- We do not use Data.List.NonEmpty.last, as that would limit the module to\n"+ tell " -- base >= 4.9.0.0 (GHC >= 8.0.1).\n"+ tell " lastChar x [] = x\n"+ tell " lastChar _ (x:xs) = lastChar x xs\n" tell "\n" tell "pathSeparator :: Char\n" if (zIsWindows z_root)
src/Distribution/Simple/BuildPaths.hs view
@@ -1,6 +1,9 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-}+ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.BuildPaths -- Copyright : Isaac Jones 2003-2004,@@ -11,52 +14,71 @@ -- Portability : portable -- -- A bunch of dirs, paths and file names used for intermediate build steps.-----module Distribution.Simple.BuildPaths (- defaultDistPref, srcPref,- haddockDirName, hscolourPref, haddockPref,- autogenPackageModulesDir,- autogenComponentModulesDir,-- autogenPathsModuleName,- cppHeaderName,- haddockName,-- mkGenericStaticLibName,- mkLibName,- mkProfLibName,- mkGenericSharedLibName,- mkSharedLibName,- mkStaticLibName,- mkGenericSharedBundledLibName,+module Distribution.Simple.BuildPaths+ ( defaultDistPref+ , srcPref+ , buildInfoPref+ , haddockDirName+ , haddockLibraryDirPath+ , haddockTestDirPath+ , haddockBenchmarkDirPath+ , hscolourPref+ , haddockPref+ , autogenPackageModulesDir+ , autogenComponentModulesDir+ , autogenPathsModuleName+ , autogenPackageInfoModuleName+ , cppHeaderName+ , haddockPath+ , haddockPackageLibraryName+ , haddockPackageLibraryName'+ , haddockLibraryName+ , haddockLibraryPath+ , mkGenericStaticLibName+ , mkLibName+ , mkProfLibName+ , mkGenericSharedLibName+ , mkSharedLibName+ , mkProfSharedLibName+ , mkStaticLibName+ , mkGenericSharedBundledLibName+ , exeExtension+ , objExtension+ , dllExtension+ , staticLibExtension - exeExtension,- objExtension,- dllExtension,- staticLibExtension, -- * Source files & build directories- getSourceFiles, getLibSourceFiles, getExeSourceFiles,- getFLibSourceFiles, exeBuildDir, flibBuildDir,+ , getSourceFiles+ , getLibSourceFiles+ , getExeSourceFiles+ , getTestSourceFiles+ , getBenchmarkSourceFiles+ , getFLibSourceFiles+ , exeBuildDir+ , flibBuildDir+ , stubName+ , testBuildDir+ , benchmarkBuildDir ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -import Distribution.Package-import Distribution.ModuleName as ModuleName+import Data.List (stripPrefix) import Distribution.Compiler+import Distribution.ModuleName as ModuleName+import Distribution.Package import Distribution.PackageDescription-import Distribution.Simple.LocalBuildInfo-import Distribution.Simple.Setup import Distribution.Pretty-import Distribution.System-import Distribution.Verbosity+import Distribution.Simple.Errors+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.PreProcess.Types (builtinHaskellSuffixes)+import Distribution.Simple.Setup.Common+import Distribution.Simple.Setup.Haddock (HaddockTarget (..)) import Distribution.Simple.Utils+import Distribution.System import Distribution.Utils.Path--import Data.List (stripPrefix)-import System.FilePath ((</>), (<.>), normalise)+import Distribution.Verbosity -- --------------------------------------------------------------------------- -- Build directories and files@@ -64,29 +86,78 @@ srcPref :: FilePath -> FilePath srcPref distPref = distPref </> "src" -hscolourPref :: HaddockTarget -> FilePath -> PackageDescription -> FilePath+hscolourPref+ :: HaddockTarget+ -> SymbolicPath root (Dir Dist)+ -> PackageDescription+ -> SymbolicPath root (Dir Artifacts) hscolourPref = haddockPref +-- | Build info json file, generated in every build+buildInfoPref+ :: SymbolicPath root (Dir Dist)+ -> SymbolicPath root File+buildInfoPref distPref = distPref </> makeRelativePathEx "build-info.json"+ -- | This is the name of the directory in which the generated haddocks -- should be stored. It does not include the @<dist>/doc/html@ prefix.+--+-- It is also used by `haddock-project` when constructing its output directory. haddockDirName :: HaddockTarget -> PackageDescription -> FilePath haddockDirName ForDevelopment = prettyShow . packageName haddockDirName ForHackage = (++ "-docs") . prettyShow . packageId +-- | This is the name of the directory in which the generated haddocks for+-- a (sub)library should be stored. It does not include the @<dist>/doc/html@+-- prefix.+--+-- It is also used by `haddock-project` when constructing its output directory.+haddockLibraryDirPath+ :: HaddockTarget+ -> PackageDescription+ -> Library+ -> FilePath+haddockLibraryDirPath haddockTarget pkg_descr lib =+ case libName lib of+ LSubLibName sublib_name ->+ haddockDirName haddockTarget pkg_descr </> prettyShow sublib_name+ _ -> haddockDirName haddockTarget pkg_descr++haddockTestDirPath+ :: HaddockTarget+ -> PackageDescription+ -> TestSuite+ -> FilePath+haddockTestDirPath haddockTarget pkg_descr test =+ haddockDirName haddockTarget pkg_descr </> prettyShow (testName test)++haddockBenchmarkDirPath+ :: HaddockTarget+ -> PackageDescription+ -> Benchmark+ -> FilePath+haddockBenchmarkDirPath haddockTarget pkg_descr bench =+ haddockDirName haddockTarget pkg_descr </> prettyShow (benchmarkName bench)+ -- | The directory to which generated haddock documentation should be written.-haddockPref :: HaddockTarget -> FilePath -> PackageDescription -> FilePath-haddockPref haddockTarget distPref pkg_descr- = distPref </> "doc" </> "html" </> haddockDirName haddockTarget pkg_descr+haddockPref+ :: HaddockTarget+ -> SymbolicPath root (Dir Dist)+ -> PackageDescription+ -> SymbolicPath root (Dir Artifacts)+haddockPref haddockTarget distPref pkg_descr =+ distPref </> makeRelativePathEx ("doc" </> "html" </> haddockDirName haddockTarget pkg_descr) -- | The directory in which we put auto-generated modules for EVERY -- component in the package.-autogenPackageModulesDir :: LocalBuildInfo -> String-autogenPackageModulesDir lbi = buildDir lbi </> "global-autogen"+autogenPackageModulesDir :: LocalBuildInfo -> SymbolicPath Pkg (Dir Source)+autogenPackageModulesDir lbi = buildDir lbi </> makeRelativePathEx "global-autogen" -- | The directory in which we put auto-generated modules for a -- particular component.-autogenComponentModulesDir :: LocalBuildInfo -> ComponentLocalBuildInfo -> String-autogenComponentModulesDir lbi clbi = componentBuildDir lbi clbi </> "autogen"+autogenComponentModulesDir :: LocalBuildInfo -> ComponentLocalBuildInfo -> SymbolicPath Pkg (Dir Source)+autogenComponentModulesDir lbi clbi = componentBuildDir lbi clbi </> makeRelativePathEx "autogen"+ -- NB: Look at 'checkForeignDeps' for where a simplified version of this -- has been copy-pasted. @@ -98,78 +169,203 @@ autogenPathsModuleName pkg_descr = ModuleName.fromString $ "Paths_" ++ map fixchar (prettyShow (packageName pkg_descr))- where fixchar '-' = '_'- fixchar c = c+ where+ fixchar '-' = '_'+ fixchar c = c -haddockName :: PackageDescription -> FilePath-haddockName pkg_descr = prettyShow (packageName pkg_descr) <.> "haddock"+-- | The name of the auto-generated PackageInfo_* module associated with a package+autogenPackageInfoModuleName :: PackageDescription -> ModuleName+autogenPackageInfoModuleName pkg_descr =+ ModuleName.fromString $+ "PackageInfo_" ++ map fixchar (prettyShow (packageName pkg_descr))+ where+ fixchar '-' = '_'+ fixchar c = c +haddockPath :: PackageDescription -> FilePath+haddockPath pkg_descr = prettyShow (packageName pkg_descr) <.> "haddock"++-- | A name of a (sub)library used by haddock, in the form+-- `<package>:<library>` if it is a sublibrary, or `<package>` if it is the+-- main library.+--+-- Used by `haddock-project` and `Distribution.Simple.Haddock`.+haddockPackageLibraryName :: PackageDescription -> Library -> String+haddockPackageLibraryName pkg_descr lib =+ haddockPackageLibraryName' (packageName pkg_descr) (libName lib)++haddockPackageLibraryName' :: PackageName -> LibraryName -> String+haddockPackageLibraryName' pkg_name lib_name =+ case lib_name of+ LSubLibName sublib_name ->+ prettyShow pkg_name ++ ":" ++ prettyShow sublib_name+ LMainLibName -> prettyShow pkg_name++-- | A name of a (sub)library used by haddock.+haddockLibraryName :: PackageDescription -> Library -> String+haddockLibraryName pkg_descr lib =+ case libName lib of+ LSubLibName sublib_name -> prettyShow sublib_name+ LMainLibName -> prettyShow (packageName pkg_descr)++-- | File path of the ".haddock" file.+haddockLibraryPath :: PackageDescription -> Library -> FilePath+haddockLibraryPath pkg_descr lib = haddockLibraryName pkg_descr lib <.> "haddock"+ -- ----------------------------------------------------------------------------- -- Source File helper -getLibSourceFiles :: Verbosity- -> LocalBuildInfo- -> Library- -> ComponentLocalBuildInfo- -> IO [(ModuleName.ModuleName, FilePath)]-getLibSourceFiles verbosity lbi lib clbi = getSourceFiles verbosity searchpaths modules+getLibSourceFiles+ :: Verbosity+ -> LocalBuildInfo+ -> Library+ -> ComponentLocalBuildInfo+ -> IO [(ModuleName.ModuleName, SymbolicPath Pkg File)]+getLibSourceFiles verbosity lbi lib clbi =+ getSourceFiles verbosity mbWorkDir searchpaths modules where- bi = libBuildInfo lib- modules = allLibModules lib clbi- searchpaths = componentBuildDir lbi clbi : map getSymbolicPath (hsSourceDirs bi) ++- [ autogenComponentModulesDir lbi clbi- , autogenPackageModulesDir lbi ]+ bi = libBuildInfo lib+ modules = allLibModules lib clbi+ mbWorkDir = mbWorkDirLBI lbi+ searchpaths =+ coerceSymbolicPath (componentBuildDir lbi clbi)+ : hsSourceDirs bi+ ++ [ autogenComponentModulesDir lbi clbi+ , autogenPackageModulesDir lbi+ ] -getExeSourceFiles :: Verbosity- -> LocalBuildInfo- -> Executable- -> ComponentLocalBuildInfo- -> IO [(ModuleName.ModuleName, FilePath)]+getExeSourceFiles+ :: Verbosity+ -> LocalBuildInfo+ -> Executable+ -> ComponentLocalBuildInfo+ -> IO [(ModuleName.ModuleName, SymbolicPath Pkg 'File)] getExeSourceFiles verbosity lbi exe clbi = do- moduleFiles <- getSourceFiles verbosity searchpaths modules- srcMainPath <- findFileEx verbosity (map getSymbolicPath $ hsSourceDirs bi) (modulePath exe)- return ((ModuleName.main, srcMainPath) : moduleFiles)+ moduleFiles <- getSourceFiles verbosity mbWorkDir searchpaths modules+ srcMainPath <- findFileCwd verbosity mbWorkDir (hsSourceDirs bi) (modulePath exe)+ return ((ModuleName.main, srcMainPath) : moduleFiles) where- bi = buildInfo exe- modules = otherModules bi- searchpaths = autogenComponentModulesDir lbi clbi- : autogenPackageModulesDir lbi- : exeBuildDir lbi exe : map getSymbolicPath (hsSourceDirs bi)+ mbWorkDir = mbWorkDirLBI lbi+ bi = buildInfo exe+ modules = otherModules bi+ searchpaths =+ autogenComponentModulesDir lbi clbi+ : autogenPackageModulesDir lbi+ : coerceSymbolicPath (exeBuildDir lbi exe)+ : hsSourceDirs bi -getFLibSourceFiles :: Verbosity- -> LocalBuildInfo- -> ForeignLib- -> ComponentLocalBuildInfo- -> IO [(ModuleName.ModuleName, FilePath)]-getFLibSourceFiles verbosity lbi flib clbi = getSourceFiles verbosity searchpaths modules+getTestSourceFiles+ :: Verbosity+ -> LocalBuildInfo+ -> TestSuite+ -> ComponentLocalBuildInfo+ -> IO [(ModuleName.ModuleName, SymbolicPath Pkg 'File)]+getTestSourceFiles verbosity lbi test@TestSuite{testInterface = TestSuiteExeV10 _ path} clbi = do+ moduleFiles <- getSourceFiles verbosity mbWorkDir searchpaths modules+ srcMainPath <- findFileCwd verbosity mbWorkDir (hsSourceDirs bi) path+ return ((ModuleName.main, srcMainPath) : moduleFiles) where- bi = foreignLibBuildInfo flib- modules = otherModules bi- searchpaths = autogenComponentModulesDir lbi clbi- : autogenPackageModulesDir lbi- : flibBuildDir lbi flib : map getSymbolicPath (hsSourceDirs bi)+ mbWorkDir = mbWorkDirLBI lbi+ bi = testBuildInfo test+ modules = otherModules bi+ searchpaths =+ autogenComponentModulesDir lbi clbi+ : autogenPackageModulesDir lbi+ : coerceSymbolicPath (testBuildDir lbi test)+ : hsSourceDirs bi+getTestSourceFiles _ _ _ _ = return [] -getSourceFiles :: Verbosity -> [FilePath]- -> [ModuleName.ModuleName]- -> IO [(ModuleName.ModuleName, FilePath)]-getSourceFiles verbosity dirs modules = flip traverse modules $ \m -> fmap ((,) m) $- findFileWithExtension ["hs", "lhs", "hsig", "lhsig"] dirs (ModuleName.toFilePath m)- >>= maybe (notFound m) (return . normalise)+getBenchmarkSourceFiles+ :: Verbosity+ -> LocalBuildInfo+ -> Benchmark+ -> ComponentLocalBuildInfo+ -> IO [(ModuleName.ModuleName, SymbolicPath Pkg 'File)]+getBenchmarkSourceFiles verbosity lbi bench@Benchmark{benchmarkInterface = BenchmarkExeV10 _ path} clbi = do+ moduleFiles <- getSourceFiles verbosity mbWorkDir searchpaths modules+ srcMainPath <- findFileCwd verbosity mbWorkDir (hsSourceDirs bi) path+ return ((ModuleName.main, srcMainPath) : moduleFiles) where- notFound module_ = die' verbosity $ "can't find source for module " ++ prettyShow module_+ mbWorkDir = mbWorkDirLBI lbi+ bi = benchmarkBuildInfo bench+ modules = otherModules bi+ searchpaths =+ autogenComponentModulesDir lbi clbi+ : autogenPackageModulesDir lbi+ : coerceSymbolicPath (benchmarkBuildDir lbi bench)+ : hsSourceDirs bi+getBenchmarkSourceFiles _ _ _ _ = return [] +getFLibSourceFiles+ :: Verbosity+ -> LocalBuildInfo+ -> ForeignLib+ -> ComponentLocalBuildInfo+ -> IO [(ModuleName.ModuleName, SymbolicPath Pkg File)]+getFLibSourceFiles verbosity lbi flib clbi =+ getSourceFiles verbosity mbWorkDir searchpaths modules+ where+ bi = foreignLibBuildInfo flib+ modules = otherModules bi+ mbWorkDir = mbWorkDirLBI lbi+ searchpaths =+ autogenComponentModulesDir lbi clbi+ : autogenPackageModulesDir lbi+ : coerceSymbolicPath (flibBuildDir lbi flib)+ : hsSourceDirs bi++getSourceFiles+ :: Verbosity+ -> Maybe (SymbolicPath CWD ('Dir Pkg))+ -> [SymbolicPathX allowAbsolute Pkg (Dir Source)]+ -> [ModuleName.ModuleName]+ -> IO [(ModuleName.ModuleName, SymbolicPathX allowAbsolute Pkg File)]+getSourceFiles verbosity mbWorkDir dirs modules = flip traverse modules $ \m ->+ fmap ((,) m) $+ findFileCwdWithExtension+ mbWorkDir+ builtinHaskellSuffixes+ dirs+ (moduleNameSymbolicPath m)+ >>= maybe (notFound m) (return . normaliseSymbolicPath)+ where+ notFound module_ =+ dieWithException verbosity $ CantFindSourceModule module_+ -- | The directory where we put build results for an executable-exeBuildDir :: LocalBuildInfo -> Executable -> FilePath-exeBuildDir lbi exe = buildDir lbi </> nm </> nm ++ "-tmp"+exeBuildDir :: LocalBuildInfo -> Executable -> SymbolicPath Pkg (Dir Build)+exeBuildDir lbi exe = buildDir lbi </> makeRelativePathEx (nm </> nm ++ "-tmp") where nm = unUnqualComponentName $ exeName exe -- | The directory where we put build results for a foreign library-flibBuildDir :: LocalBuildInfo -> ForeignLib -> FilePath-flibBuildDir lbi flib = buildDir lbi </> nm </> nm ++ "-tmp"+flibBuildDir :: LocalBuildInfo -> ForeignLib -> SymbolicPath Pkg (Dir Build)+flibBuildDir lbi flib = buildDir lbi </> makeRelativePathEx (nm </> nm ++ "-tmp") where nm = unUnqualComponentName $ foreignLibName flib +-- | The name of the stub executable associated with a library 'TestSuite'.+stubName :: TestSuite -> FilePath+stubName t = unUnqualComponentName (testName t) ++ "Stub"++-- | The directory where we put build results for a test suite+testBuildDir :: LocalBuildInfo -> TestSuite -> SymbolicPath Pkg (Dir Build)+testBuildDir lbi tst =+ buildDir lbi </> makeRelativePathEx testDir+ where+ testDir = case testInterface tst of+ TestSuiteLibV09{} ->+ stubName tst </> stubName tst ++ "-tmp"+ _ -> nm </> nm ++ "-tmp"+ nm = unUnqualComponentName $ testName tst++-- | The directory where we put build results for a benchmark suite+benchmarkBuildDir :: LocalBuildInfo -> Benchmark -> SymbolicPath Pkg (Dir Build)+benchmarkBuildDir lbi bm =+ buildDir lbi </> makeRelativePathEx (nm </> nm ++ "-tmp")+ where+ nm = unUnqualComponentName $ benchmarkName bm+ -- --------------------------------------------------------------------------- -- Library file names @@ -182,29 +378,35 @@ mkLibName lib = mkGenericStaticLibName (getHSLibraryName lib) mkProfLibName :: UnitId -> String-mkProfLibName lib = mkGenericStaticLibName (getHSLibraryName lib ++ "_p")+mkProfLibName lib = mkGenericStaticLibName (getHSLibraryName lib ++ "_p") -- | Create a library name for a shared library from a given name. -- Prepends @lib@ and appends the @-\<compilerFlavour\>\<compilerVersion\>@ -- as well as the shared library extension. mkGenericSharedLibName :: Platform -> CompilerId -> String -> String-mkGenericSharedLibName platform (CompilerId compilerFlavor compilerVersion) lib- = mconcat [ "lib", lib, "-", comp <.> dllExtension platform ]- where comp = prettyShow compilerFlavor ++ prettyShow compilerVersion+mkGenericSharedLibName platform (CompilerId compilerFlavor compilerVersion) lib =+ mconcat ["lib", lib, "-", comp <.> dllExtension platform]+ where+ comp = prettyShow compilerFlavor ++ prettyShow compilerVersion -- Implement proper name mangling for dynamical shared objects -- @libHS\<packagename\>-\<compilerFlavour\>\<compilerVersion\>@ -- e.g. @libHSbase-2.1-ghc6.6.1.so@ mkSharedLibName :: Platform -> CompilerId -> UnitId -> String-mkSharedLibName platform comp lib- = mkGenericSharedLibName platform comp (getHSLibraryName lib)+mkSharedLibName platform comp lib =+ mkGenericSharedLibName platform comp (getHSLibraryName lib) +mkProfSharedLibName :: Platform -> CompilerId -> UnitId -> String+mkProfSharedLibName platform comp lib =+ mkGenericSharedLibName platform comp (getHSLibraryName lib ++ "_p")+ -- Static libs are named the same as shared libraries, only with -- a different extension. mkStaticLibName :: Platform -> CompilerId -> UnitId -> String-mkStaticLibName platform (CompilerId compilerFlavor compilerVersion) lib- = "lib" ++ getHSLibraryName lib ++ "-" ++ comp <.> staticLibExtension platform- where comp = prettyShow compilerFlavor ++ prettyShow compilerVersion+mkStaticLibName platform (CompilerId compilerFlavor compilerVersion) lib =+ "lib" ++ getHSLibraryName lib ++ "-" ++ comp <.> staticLibExtension platform+ where+ comp = prettyShow compilerFlavor ++ prettyShow compilerVersion -- | Create a library name for a bundled shared library from a given name. -- This matches the naming convention for shared libraries as implemented in@@ -217,23 +419,26 @@ -- "Cffi" -> "libffi.so" mkGenericSharedBundledLibName :: Platform -> CompilerId -> String -> String mkGenericSharedBundledLibName platform comp lib- | "HS" `isPrefixOf` lib- = mkGenericSharedLibName platform comp lib- | Just lib' <- stripPrefix "C" lib- = "lib" ++ lib' <.> dllExtension platform- | otherwise- = error ("Don't understand library name " ++ lib)+ | "HS" `isPrefixOf` lib =+ mkGenericSharedLibName platform comp lib+ | Just lib' <- stripPrefix "C" lib =+ "lib" ++ lib' <.> dllExtension platform+ | otherwise =+ error ("Don't understand library name " ++ lib) -- ------------------------------------------------------------+ -- * Platform file extensions+ -- ------------------------------------------------------------ -- | Default extension for executable files on the current platform. -- (typically @\"\"@ on Unix and @\"exe\"@ on Windows or OS\/2) exeExtension :: Platform -> String-exeExtension (Platform _arch os) = case os of- Windows -> "exe"- _ -> ""+exeExtension platform = case platform of+ Platform _ Windows -> "exe"+ Platform Wasm32 _ -> "wasm"+ _ -> "" -- | Extension for object files. For GHC the extension is @\"o\"@. objExtension :: String@@ -242,10 +447,10 @@ -- | Extension for dynamically linked (or shared) libraries -- (typically @\"so\"@ on Unix and @\"dll\"@ on Windows) dllExtension :: Platform -> String-dllExtension (Platform _arch os)= case os of- Windows -> "dll"- OSX -> "dylib"- _ -> "so"+dllExtension (Platform _arch os) = case os of+ Windows -> "dll"+ OSX -> "dylib"+ _ -> "so" -- | Extension for static libraries --@@ -253,5 +458,5 @@ -- interested in, not the build OS. staticLibExtension :: Platform -> String staticLibExtension (Platform _arch os) = case os of- Windows -> "lib"- _ -> "a"+ Windows -> "lib"+ _ -> "a"
src/Distribution/Simple/BuildTarget.hs view
@@ -1,1064 +1,1110 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}---------------------------------------------------------------------------------- |--- Module : Distribution.Client.BuildTargets--- Copyright : (c) Duncan Coutts 2012--- License : BSD-like------ Maintainer : duncan@community.haskell.org------ Handling for user-specified build targets-------------------------------------------------------------------------------module Distribution.Simple.BuildTarget (- -- * Main interface- readTargetInfos,- readBuildTargets, -- in case you don't have LocalBuildInfo-- -- * Build targets- BuildTarget(..),- showBuildTarget,- QualLevel(..),- buildTargetComponentName,-- -- * Parsing user build targets- UserBuildTarget,- readUserBuildTargets,- showUserBuildTarget,- UserBuildTargetProblem(..),- reportUserBuildTargetProblems,-- -- * Resolving build targets- resolveBuildTargets,- BuildTargetProblem(..),- reportBuildTargetProblems,- ) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Types.TargetInfo-import Distribution.Types.LocalBuildInfo-import Distribution.Types.ComponentRequestedSpec-import Distribution.Types.ForeignLib-import Distribution.Types.UnqualComponentName--import Distribution.Package-import Distribution.PackageDescription-import Distribution.ModuleName-import Distribution.Simple.LocalBuildInfo-import Distribution.Pretty-import Distribution.Parsec-import Distribution.Simple.Utils-import Distribution.Verbosity-import Distribution.Utils.Path--import qualified Distribution.Compat.CharParsing as P--import Control.Monad ( msum )-import Data.List ( stripPrefix, groupBy )-import qualified Data.List.NonEmpty as NE-import System.FilePath as FilePath- ( dropExtension, normalise, splitDirectories, joinPath, splitPath- , hasTrailingPathSeparator )-import System.Directory ( doesFileExist, doesDirectoryExist )-import qualified Data.Map as Map---- | Take a list of 'String' build targets, and parse and validate them--- into actual 'TargetInfo's to be built/registered/whatever.-readTargetInfos :: Verbosity -> PackageDescription -> LocalBuildInfo -> [String] -> IO [TargetInfo]-readTargetInfos verbosity pkg_descr lbi args = do- build_targets <- readBuildTargets verbosity pkg_descr args- checkBuildTargets verbosity pkg_descr lbi build_targets---- --------------------------------------------------------------- * User build targets--- ---------------------------------------------------------------- | Various ways that a user may specify a build target.----data UserBuildTarget =-- -- | A target specified by a single name. This could be a component- -- module or file.- --- -- > cabal build foo- -- > cabal build Data.Foo- -- > cabal build Data/Foo.hs Data/Foo.hsc- --- UserBuildTargetSingle String-- -- | A target specified by a qualifier and name. This could be a component- -- name qualified by the component namespace kind, or a module or file- -- qualified by the component name.- --- -- > cabal build lib:foo exe:foo- -- > cabal build foo:Data.Foo- -- > cabal build foo:Data/Foo.hs- --- | UserBuildTargetDouble String String-- -- | A fully qualified target, either a module or file qualified by a- -- component name with the component namespace kind.- --- -- > cabal build lib:foo:Data/Foo.hs exe:foo:Data/Foo.hs- -- > cabal build lib:foo:Data.Foo exe:foo:Data.Foo- --- | UserBuildTargetTriple String String String- deriving (Show, Eq, Ord)----- --------------------------------------------------------------- * Resolved build targets--- ---------------------------------------------------------------- | A fully resolved build target.----data BuildTarget =-- -- | A specific component- --- BuildTargetComponent ComponentName-- -- | A specific module within a specific component.- --- | BuildTargetModule ComponentName ModuleName-- -- | A specific file within a specific component.- --- | BuildTargetFile ComponentName FilePath- deriving (Eq, Show, Generic)--instance Binary BuildTarget--buildTargetComponentName :: BuildTarget -> ComponentName-buildTargetComponentName (BuildTargetComponent cn) = cn-buildTargetComponentName (BuildTargetModule cn _) = cn-buildTargetComponentName (BuildTargetFile cn _) = cn---- | Read a list of user-supplied build target strings and resolve them to--- 'BuildTarget's according to a 'PackageDescription'. If there are problems--- with any of the targets e.g. they don't exist or are misformatted, throw an--- 'IOException'.-readBuildTargets :: Verbosity -> PackageDescription -> [String] -> IO [BuildTarget]-readBuildTargets verbosity pkg targetStrs = do- let (uproblems, utargets) = readUserBuildTargets targetStrs- reportUserBuildTargetProblems verbosity uproblems-- utargets' <- traverse checkTargetExistsAsFile utargets-- let (bproblems, btargets) = resolveBuildTargets pkg utargets'- reportBuildTargetProblems verbosity bproblems-- return btargets--checkTargetExistsAsFile :: UserBuildTarget -> IO (UserBuildTarget, Bool)-checkTargetExistsAsFile t = do- fexists <- existsAsFile (fileComponentOfTarget t)- return (t, fexists)-- where- existsAsFile f = do- exists <- doesFileExist f- case splitPath f of- (d:_) | hasTrailingPathSeparator d -> doesDirectoryExist d- (d:_:_) | not exists -> doesDirectoryExist d- _ -> return exists-- fileComponentOfTarget (UserBuildTargetSingle s1) = s1- fileComponentOfTarget (UserBuildTargetDouble _ s2) = s2- fileComponentOfTarget (UserBuildTargetTriple _ _ s3) = s3----- --------------------------------------------------------------- * Parsing user targets--- --------------------------------------------------------------readUserBuildTargets :: [String] -> ([UserBuildTargetProblem]- ,[UserBuildTarget])-readUserBuildTargets = partitionEithers . map readUserBuildTarget---- |------ >>> readUserBuildTarget "comp"--- Right (UserBuildTargetSingle "comp")------ >>> readUserBuildTarget "lib:comp"--- Right (UserBuildTargetDouble "lib" "comp")------ >>> readUserBuildTarget "pkg:lib:comp"--- Right (UserBuildTargetTriple "pkg" "lib" "comp")------ >>> readUserBuildTarget "\"comp\""--- Right (UserBuildTargetSingle "comp")------ >>> readUserBuildTarget "lib:\"comp\""--- Right (UserBuildTargetDouble "lib" "comp")------ >>> readUserBuildTarget "pkg:lib:\"comp\""--- Right (UserBuildTargetTriple "pkg" "lib" "comp")------ >>> readUserBuildTarget "pkg:lib:comp:more"--- Left (UserBuildTargetUnrecognised "pkg:lib:comp:more")------ >>> readUserBuildTarget "pkg:\"lib\":comp"--- Left (UserBuildTargetUnrecognised "pkg:\"lib\":comp")----readUserBuildTarget :: String -> Either UserBuildTargetProblem- UserBuildTarget-readUserBuildTarget targetstr =- case explicitEitherParsec parseTargetApprox targetstr of- Left _ -> Left (UserBuildTargetUnrecognised targetstr)- Right tgt -> Right tgt-- where- parseTargetApprox :: CabalParsing m => m UserBuildTarget- parseTargetApprox = do- -- read one, two, or three tokens, where last could be "hs-string"- ts <- tokens- return $ case ts of- (a, Nothing) -> UserBuildTargetSingle a- (a, Just (b, Nothing)) -> UserBuildTargetDouble a b- (a, Just (b, Just c)) -> UserBuildTargetTriple a b c-- tokens :: CabalParsing m => m (String, Maybe (String, Maybe String))- tokens = (\s -> (s, Nothing)) <$> parsecHaskellString- <|> (,) <$> token <*> P.optional (P.char ':' *> tokens2)-- tokens2 :: CabalParsing m => m (String, Maybe String)- tokens2 = (\s -> (s, Nothing)) <$> parsecHaskellString- <|> (,) <$> token <*> P.optional (P.char ':' *> (parsecHaskellString <|> token))-- token :: CabalParsing m => m String- token = P.munch1 (\x -> not (isSpace x) && x /= ':')--data UserBuildTargetProblem- = UserBuildTargetUnrecognised String- deriving Show--reportUserBuildTargetProblems :: Verbosity -> [UserBuildTargetProblem] -> IO ()-reportUserBuildTargetProblems verbosity problems = do- case [ target | UserBuildTargetUnrecognised target <- problems ] of- [] -> return ()- target ->- die' verbosity $ unlines- [ "Unrecognised build target '" ++ name ++ "'."- | name <- target ]- ++ "Examples:\n"- ++ " - build foo -- component name "- ++ "(library, executable, test-suite or benchmark)\n"- ++ " - build Data.Foo -- module name\n"- ++ " - build Data/Foo.hsc -- file name\n"- ++ " - build lib:foo exe:foo -- component qualified by kind\n"- ++ " - build foo:Data.Foo -- module qualified by component\n"- ++ " - build foo:Data/Foo.hsc -- file qualified by component"--showUserBuildTarget :: UserBuildTarget -> String-showUserBuildTarget = intercalate ":" . getComponents- where- getComponents (UserBuildTargetSingle s1) = [s1]- getComponents (UserBuildTargetDouble s1 s2) = [s1,s2]- getComponents (UserBuildTargetTriple s1 s2 s3) = [s1,s2,s3]---- | Unless you use 'QL1', this function is PARTIAL;--- use 'showBuildTarget' instead.-showBuildTarget' :: QualLevel -> PackageId -> BuildTarget -> String-showBuildTarget' ql pkgid bt =- showUserBuildTarget (renderBuildTarget ql bt pkgid)---- | Unambiguously render a 'BuildTarget', so that it can--- be parsed in all situations.-showBuildTarget :: PackageId -> BuildTarget -> String-showBuildTarget pkgid t =- showBuildTarget' (qlBuildTarget t) pkgid t- where- qlBuildTarget BuildTargetComponent{} = QL2- qlBuildTarget _ = QL3----- --------------------------------------------------------------- * Resolving user targets to build targets--- --------------------------------------------------------------{--stargets =- [ BuildTargetComponent (CExeName "foo")- , BuildTargetModule (CExeName "foo") (mkMn "Foo")- , BuildTargetModule (CExeName "tst") (mkMn "Foo")- ]- where- mkMn :: String -> ModuleName- mkMn = fromJust . simpleParse--ex_pkgid :: PackageIdentifier-Just ex_pkgid = simpleParse "thelib"--}---- | Given a bunch of user-specified targets, try to resolve what it is they--- refer to.----resolveBuildTargets :: PackageDescription- -> [(UserBuildTarget, Bool)]- -> ([BuildTargetProblem], [BuildTarget])-resolveBuildTargets pkg = partitionEithers- . map (uncurry (resolveBuildTarget pkg))--resolveBuildTarget :: PackageDescription -> UserBuildTarget -> Bool- -> Either BuildTargetProblem BuildTarget-resolveBuildTarget pkg userTarget fexists =- case findMatch (matchBuildTarget pkg userTarget fexists) of- Unambiguous target -> Right target- Ambiguous targets -> Left (BuildTargetAmbiguous userTarget targets')- where targets' = disambiguateBuildTargets- (packageId pkg)- userTarget- targets- None errs -> Left (classifyMatchErrors errs)-- where- classifyMatchErrors errs- | Just expected' <- NE.nonEmpty expected- = let (things, got:|_) = NE.unzip expected' in- BuildTargetExpected userTarget (NE.toList things) got- | not (null nosuch) = BuildTargetNoSuch userTarget nosuch- | otherwise = error $ "resolveBuildTarget: internal error in matching"- where- expected = [ (thing, got) | MatchErrorExpected thing got <- errs ]- nosuch = [ (thing, got) | MatchErrorNoSuch thing got <- errs ]---data BuildTargetProblem- = BuildTargetExpected UserBuildTarget [String] String- -- ^ [expected thing] (actually got)- | BuildTargetNoSuch UserBuildTarget [(String, String)]- -- ^ [(no such thing, actually got)]- | BuildTargetAmbiguous UserBuildTarget [(UserBuildTarget, BuildTarget)]- deriving Show---disambiguateBuildTargets :: PackageId -> UserBuildTarget -> [BuildTarget]- -> [(UserBuildTarget, BuildTarget)]-disambiguateBuildTargets pkgid original =- disambiguate (userTargetQualLevel original)- where- disambiguate ql ts- | null amb = unamb- | otherwise = unamb ++ disambiguate (succ ql) amb- where- (amb, unamb) = step ql ts-- userTargetQualLevel (UserBuildTargetSingle _ ) = QL1- userTargetQualLevel (UserBuildTargetDouble _ _ ) = QL2- userTargetQualLevel (UserBuildTargetTriple _ _ _) = QL3-- step :: QualLevel -> [BuildTarget]- -> ([BuildTarget], [(UserBuildTarget, BuildTarget)])- step ql = (\(amb, unamb) -> (map snd $ concat amb, concat unamb))- . partition (\g -> length g > 1)- . groupBy (equating fst)- . sortBy (comparing fst)- . map (\t -> (renderBuildTarget ql t pkgid, t))--data QualLevel = QL1 | QL2 | QL3- deriving (Enum, Show)--renderBuildTarget :: QualLevel -> BuildTarget -> PackageId -> UserBuildTarget-renderBuildTarget ql target pkgid =- case ql of- QL1 -> UserBuildTargetSingle s1 where s1 = single target- QL2 -> UserBuildTargetDouble s1 s2 where (s1, s2) = double target- QL3 -> UserBuildTargetTriple s1 s2 s3 where (s1, s2, s3) = triple target-- where- single (BuildTargetComponent cn ) = dispCName cn- single (BuildTargetModule _ m) = prettyShow m- single (BuildTargetFile _ f) = f-- double (BuildTargetComponent cn ) = (dispKind cn, dispCName cn)- double (BuildTargetModule cn m) = (dispCName cn, prettyShow m)- double (BuildTargetFile cn f) = (dispCName cn, f)-- triple (BuildTargetComponent _ ) = error "triple BuildTargetComponent"- triple (BuildTargetModule cn m) = (dispKind cn, dispCName cn, prettyShow m)- triple (BuildTargetFile cn f) = (dispKind cn, dispCName cn, f)-- dispCName = componentStringName pkgid- dispKind = showComponentKindShort . componentKind--reportBuildTargetProblems :: Verbosity -> [BuildTargetProblem] -> IO ()-reportBuildTargetProblems verbosity problems = do-- case [ (t, e, g) | BuildTargetExpected t e g <- problems ] of- [] -> return ()- targets ->- die' verbosity $ unlines- [ "Unrecognised build target '" ++ showUserBuildTarget target- ++ "'.\n"- ++ "Expected a " ++ intercalate " or " expected- ++ ", rather than '" ++ got ++ "'."- | (target, expected, got) <- targets ]-- case [ (t, e) | BuildTargetNoSuch t e <- problems ] of- [] -> return ()- targets ->- die' verbosity $ unlines- [ "Unknown build target '" ++ showUserBuildTarget target- ++ "'.\nThere is no "- ++ intercalate " or " [ mungeThing thing ++ " '" ++ got ++ "'"- | (thing, got) <- nosuch ] ++ "."- | (target, nosuch) <- targets ]- where- mungeThing "file" = "file target"- mungeThing thing = thing-- case [ (t, ts) | BuildTargetAmbiguous t ts <- problems ] of- [] -> return ()- targets ->- die' verbosity $ unlines- [ "Ambiguous build target '" ++ showUserBuildTarget target- ++ "'. It could be:\n "- ++ unlines [ " "++ showUserBuildTarget ut ++- " (" ++ showBuildTargetKind bt ++ ")"- | (ut, bt) <- amb ]- | (target, amb) <- targets ]-- where- showBuildTargetKind (BuildTargetComponent _ ) = "component"- showBuildTargetKind (BuildTargetModule _ _) = "module"- showBuildTargetKind (BuildTargetFile _ _) = "file"---------------------------------------- Top level BuildTarget matcher-----matchBuildTarget :: PackageDescription- -> UserBuildTarget -> Bool -> Match BuildTarget-matchBuildTarget pkg = \utarget fexists ->- case utarget of- UserBuildTargetSingle str1 ->- matchBuildTarget1 cinfo str1 fexists-- UserBuildTargetDouble str1 str2 ->- matchBuildTarget2 cinfo str1 str2 fexists-- UserBuildTargetTriple str1 str2 str3 ->- matchBuildTarget3 cinfo str1 str2 str3 fexists- where- cinfo = pkgComponentInfo pkg--matchBuildTarget1 :: [ComponentInfo] -> String -> Bool -> Match BuildTarget-matchBuildTarget1 cinfo str1 fexists =- matchComponent1 cinfo str1- `matchPlusShadowing` matchModule1 cinfo str1- `matchPlusShadowing` matchFile1 cinfo str1 fexists---matchBuildTarget2 :: [ComponentInfo] -> String -> String -> Bool- -> Match BuildTarget-matchBuildTarget2 cinfo str1 str2 fexists =- matchComponent2 cinfo str1 str2- `matchPlusShadowing` matchModule2 cinfo str1 str2- `matchPlusShadowing` matchFile2 cinfo str1 str2 fexists---matchBuildTarget3 :: [ComponentInfo] -> String -> String -> String -> Bool- -> Match BuildTarget-matchBuildTarget3 cinfo str1 str2 str3 fexists =- matchModule3 cinfo str1 str2 str3- `matchPlusShadowing` matchFile3 cinfo str1 str2 str3 fexists---data ComponentInfo = ComponentInfo {- cinfoName :: ComponentName,- cinfoStrName :: ComponentStringName,- cinfoSrcDirs :: [FilePath],- cinfoModules :: [ModuleName],- cinfoHsFiles :: [FilePath], -- other hs files (like main.hs)- cinfoAsmFiles:: [FilePath],- cinfoCmmFiles:: [FilePath],- cinfoCFiles :: [FilePath],- cinfoCxxFiles:: [FilePath],- cinfoJsFiles :: [FilePath]- }--type ComponentStringName = String--pkgComponentInfo :: PackageDescription -> [ComponentInfo]-pkgComponentInfo pkg =- [ ComponentInfo {- cinfoName = componentName c,- cinfoStrName = componentStringName pkg (componentName c),- cinfoSrcDirs = map getSymbolicPath $ hsSourceDirs bi,- cinfoModules = componentModules c,- cinfoHsFiles = componentHsFiles c,- cinfoAsmFiles= asmSources bi,- cinfoCmmFiles= cmmSources bi,- cinfoCFiles = cSources bi,- cinfoCxxFiles= cxxSources bi,- cinfoJsFiles = jsSources bi- }- | c <- pkgComponents pkg- , let bi = componentBuildInfo c ]--componentStringName :: Package pkg => pkg -> ComponentName -> ComponentStringName-componentStringName pkg (CLibName LMainLibName ) = prettyShow (packageName pkg)-componentStringName _ (CLibName (LSubLibName name)) = unUnqualComponentName name-componentStringName _ (CFLibName name) = unUnqualComponentName name-componentStringName _ (CExeName name) = unUnqualComponentName name-componentStringName _ (CTestName name) = unUnqualComponentName name-componentStringName _ (CBenchName name) = unUnqualComponentName name--componentModules :: Component -> [ModuleName]--- TODO: Use of 'explicitLibModules' here is a bit wrong:--- a user could very well ask to build a specific signature--- that was inherited from other packages. To fix this--- we have to plumb 'LocalBuildInfo' through this code.--- Fortunately, this is only used by 'pkgComponentInfo'--- Please don't export this function unless you plan on fixing--- this.-componentModules (CLib lib) = explicitLibModules lib-componentModules (CFLib flib) = foreignLibModules flib-componentModules (CExe exe) = exeModules exe-componentModules (CTest test) = testModules test-componentModules (CBench bench) = benchmarkModules bench--componentHsFiles :: Component -> [FilePath]-componentHsFiles (CExe exe) = [modulePath exe]-componentHsFiles (CTest TestSuite {- testInterface = TestSuiteExeV10 _ mainfile- }) = [mainfile]-componentHsFiles (CBench Benchmark {- benchmarkInterface = BenchmarkExeV10 _ mainfile- }) = [mainfile]-componentHsFiles _ = []--{--ex_cs :: [ComponentInfo]-ex_cs =- [ (mkC (CExeName "foo") ["src1", "src1/src2"] ["Foo", "Src2.Bar", "Bar"])- , (mkC (CExeName "tst") ["src1", "test"] ["Foo"])- ]- where- mkC n ds ms = ComponentInfo n (componentStringName pkgid n) ds (map mkMn ms)- mkMn :: String -> ModuleName- mkMn = fromJust . simpleParse- pkgid :: PackageIdentifier- Just pkgid = simpleParse "thelib"--}----------------------------------- Matching component kinds-----data ComponentKind = LibKind | FLibKind | ExeKind | TestKind | BenchKind- deriving (Eq, Ord, Show, Enum, Bounded)--componentKind :: ComponentName -> ComponentKind-componentKind (CLibName _) = LibKind-componentKind (CFLibName _) = FLibKind-componentKind (CExeName _) = ExeKind-componentKind (CTestName _) = TestKind-componentKind (CBenchName _) = BenchKind--cinfoKind :: ComponentInfo -> ComponentKind-cinfoKind = componentKind . cinfoName--matchComponentKind :: String -> Match ComponentKind-matchComponentKind s- | s `elem` ["lib", "library"] = return' LibKind- | s `elem` ["flib", "foreign-lib", "foreign-library"] = return' FLibKind- | s `elem` ["exe", "executable"] = return' ExeKind- | s `elem` ["tst", "test", "test-suite"] = return' TestKind- | s `elem` ["bench", "benchmark"] = return' BenchKind- | otherwise = matchErrorExpected "component kind" s- where- return' ck = increaseConfidence >> return ck--showComponentKind :: ComponentKind -> String-showComponentKind LibKind = "library"-showComponentKind FLibKind = "foreign-library"-showComponentKind ExeKind = "executable"-showComponentKind TestKind = "test-suite"-showComponentKind BenchKind = "benchmark"--showComponentKindShort :: ComponentKind -> String-showComponentKindShort LibKind = "lib"-showComponentKindShort FLibKind = "flib"-showComponentKindShort ExeKind = "exe"-showComponentKindShort TestKind = "test"-showComponentKindShort BenchKind = "bench"----------------------------------- Matching component targets-----matchComponent1 :: [ComponentInfo] -> String -> Match BuildTarget-matchComponent1 cs = \str1 -> do- guardComponentName str1- c <- matchComponentName cs str1- return (BuildTargetComponent (cinfoName c))--matchComponent2 :: [ComponentInfo] -> String -> String -> Match BuildTarget-matchComponent2 cs = \str1 str2 -> do- ckind <- matchComponentKind str1- guardComponentName str2- c <- matchComponentKindAndName cs ckind str2- return (BuildTargetComponent (cinfoName c))---- utils:--guardComponentName :: String -> Match ()-guardComponentName s- | all validComponentChar s- && not (null s) = increaseConfidence- | otherwise = matchErrorExpected "component name" s- where- validComponentChar c = isAlphaNum c || c == '.'- || c == '_' || c == '-' || c == '\''--matchComponentName :: [ComponentInfo] -> String -> Match ComponentInfo-matchComponentName cs str =- orNoSuchThing "component" str- $ increaseConfidenceFor- $ matchInexactly caseFold- [ (cinfoStrName c, c) | c <- cs ]- str--matchComponentKindAndName :: [ComponentInfo] -> ComponentKind -> String- -> Match ComponentInfo-matchComponentKindAndName cs ckind str =- orNoSuchThing (showComponentKind ckind ++ " component") str- $ increaseConfidenceFor- $ matchInexactly (\(ck, cn) -> (ck, caseFold cn))- [ ((cinfoKind c, cinfoStrName c), c) | c <- cs ]- (ckind, str)------------------------------------ Matching module targets-----matchModule1 :: [ComponentInfo] -> String -> Match BuildTarget-matchModule1 cs = \str1 -> do- guardModuleName str1- nubMatchErrors $ do- c <- tryEach cs- let ms = cinfoModules c- m <- matchModuleName ms str1- return (BuildTargetModule (cinfoName c) m)--matchModule2 :: [ComponentInfo] -> String -> String -> Match BuildTarget-matchModule2 cs = \str1 str2 -> do- guardComponentName str1- guardModuleName str2- c <- matchComponentName cs str1- let ms = cinfoModules c- m <- matchModuleName ms str2- return (BuildTargetModule (cinfoName c) m)--matchModule3 :: [ComponentInfo] -> String -> String -> String- -> Match BuildTarget-matchModule3 cs str1 str2 str3 = do- ckind <- matchComponentKind str1- guardComponentName str2- c <- matchComponentKindAndName cs ckind str2- guardModuleName str3- let ms = cinfoModules c- m <- matchModuleName ms str3- return (BuildTargetModule (cinfoName c) m)---- utils:--guardModuleName :: String -> Match ()-guardModuleName s- | all validModuleChar s- && not (null s) = increaseConfidence- | otherwise = matchErrorExpected "module name" s- where- validModuleChar c = isAlphaNum c || c == '.' || c == '_' || c == '\''--matchModuleName :: [ModuleName] -> String -> Match ModuleName-matchModuleName ms str =- orNoSuchThing "module" str- $ increaseConfidenceFor- $ matchInexactly caseFold- [ (prettyShow m, m)- | m <- ms ]- str------------------------------------ Matching file targets-----matchFile1 :: [ComponentInfo] -> String -> Bool -> Match BuildTarget-matchFile1 cs str1 exists =- nubMatchErrors $ do- c <- tryEach cs- filepath <- matchComponentFile c str1 exists- return (BuildTargetFile (cinfoName c) filepath)---matchFile2 :: [ComponentInfo] -> String -> String -> Bool -> Match BuildTarget-matchFile2 cs str1 str2 exists = do- guardComponentName str1- c <- matchComponentName cs str1- filepath <- matchComponentFile c str2 exists- return (BuildTargetFile (cinfoName c) filepath)---matchFile3 :: [ComponentInfo] -> String -> String -> String -> Bool- -> Match BuildTarget-matchFile3 cs str1 str2 str3 exists = do- ckind <- matchComponentKind str1- guardComponentName str2- c <- matchComponentKindAndName cs ckind str2- filepath <- matchComponentFile c str3 exists- return (BuildTargetFile (cinfoName c) filepath)---matchComponentFile :: ComponentInfo -> String -> Bool -> Match FilePath-matchComponentFile c str fexists =- expecting "file" str $- matchPlus- (matchFileExists str fexists)- (matchPlusShadowing- (msum [ matchModuleFileRooted dirs ms str- , matchOtherFileRooted dirs hsFiles str ])- (msum [ matchModuleFileUnrooted ms str- , matchOtherFileUnrooted hsFiles str- , matchOtherFileUnrooted cFiles str- , matchOtherFileUnrooted jsFiles str ]))- where- dirs = cinfoSrcDirs c- ms = cinfoModules c- hsFiles = cinfoHsFiles c- cFiles = cinfoCFiles c- jsFiles = cinfoJsFiles c----- utils--matchFileExists :: FilePath -> Bool -> Match a-matchFileExists _ False = mzero-matchFileExists fname True = do increaseConfidence- matchErrorNoSuch "file" fname--matchModuleFileUnrooted :: [ModuleName] -> String -> Match FilePath-matchModuleFileUnrooted ms str = do- let filepath = normalise str- _ <- matchModuleFileStem ms filepath- return filepath--matchModuleFileRooted :: [FilePath] -> [ModuleName] -> String -> Match FilePath-matchModuleFileRooted dirs ms str = nubMatches $ do- let filepath = normalise str- filepath' <- matchDirectoryPrefix dirs filepath- _ <- matchModuleFileStem ms filepath'- return filepath--matchModuleFileStem :: [ModuleName] -> FilePath -> Match ModuleName-matchModuleFileStem ms =- increaseConfidenceFor- . matchInexactly caseFold- [ (toFilePath m, m) | m <- ms ]- . dropExtension--matchOtherFileRooted :: [FilePath] -> [FilePath] -> FilePath -> Match FilePath-matchOtherFileRooted dirs fs str = do- let filepath = normalise str- filepath' <- matchDirectoryPrefix dirs filepath- _ <- matchFile fs filepath'- return filepath--matchOtherFileUnrooted :: [FilePath] -> FilePath -> Match FilePath-matchOtherFileUnrooted fs str = do- let filepath = normalise str- _ <- matchFile fs filepath- return filepath--matchFile :: [FilePath] -> FilePath -> Match FilePath-matchFile fs = increaseConfidenceFor- . matchInexactly caseFold [ (f, f) | f <- fs ]--matchDirectoryPrefix :: [FilePath] -> FilePath -> Match FilePath-matchDirectoryPrefix dirs filepath =- exactMatches $- catMaybes- [ stripDirectory (normalise dir) filepath | dir <- dirs ]- where- stripDirectory :: FilePath -> FilePath -> Maybe FilePath- stripDirectory dir fp =- joinPath `fmap` stripPrefix (splitDirectories dir) (splitDirectories fp)------------------------------------ Matching monad------- | A matcher embodies a way to match some input as being some recognised--- value. In particular it deals with multiple and ambiguous matches.------ There are various matcher primitives ('matchExactly', 'matchInexactly'),--- ways to combine matchers ('ambiguousWith', 'shadows') and finally we can--- run a matcher against an input using 'findMatch'.-----data Match a = NoMatch Confidence [MatchError]- | ExactMatch Confidence [a]- | InexactMatch Confidence [a]- deriving Show--type Confidence = Int--data MatchError = MatchErrorExpected String String- | MatchErrorNoSuch String String- deriving (Show, Eq)---instance Alternative Match where- empty = mzero- (<|>) = mplus--instance MonadPlus Match where- mzero = matchZero- mplus = matchPlus--matchZero :: Match a-matchZero = NoMatch 0 []---- | Combine two matchers. Exact matches are used over inexact matches--- but if we have multiple exact, or inexact then the we collect all the--- ambiguous matches.----matchPlus :: Match a -> Match a -> Match a-matchPlus (ExactMatch d1 xs) (ExactMatch d2 xs') =- ExactMatch (max d1 d2) (xs ++ xs')-matchPlus a@(ExactMatch _ _ ) (InexactMatch _ _ ) = a-matchPlus a@(ExactMatch _ _ ) (NoMatch _ _ ) = a-matchPlus (InexactMatch _ _ ) b@(ExactMatch _ _ ) = b-matchPlus (InexactMatch d1 xs) (InexactMatch d2 xs') =- InexactMatch (max d1 d2) (xs ++ xs')-matchPlus a@(InexactMatch _ _ ) (NoMatch _ _ ) = a-matchPlus (NoMatch _ _ ) b@(ExactMatch _ _ ) = b-matchPlus (NoMatch _ _ ) b@(InexactMatch _ _ ) = b-matchPlus a@(NoMatch d1 ms) b@(NoMatch d2 ms')- | d1 > d2 = a- | d1 < d2 = b- | otherwise = NoMatch d1 (ms ++ ms')---- | Combine two matchers. This is similar to 'ambiguousWith' with the--- difference that an exact match from the left matcher shadows any exact--- match on the right. Inexact matches are still collected however.----matchPlusShadowing :: Match a -> Match a -> Match a-matchPlusShadowing a@(ExactMatch _ _) (ExactMatch _ _) = a-matchPlusShadowing a b = matchPlus a b--instance Functor Match where- fmap _ (NoMatch d ms) = NoMatch d ms- fmap f (ExactMatch d xs) = ExactMatch d (fmap f xs)- fmap f (InexactMatch d xs) = InexactMatch d (fmap f xs)--instance Applicative Match where- pure a = ExactMatch 0 [a]- (<*>) = ap--instance Monad Match where- return = pure-- NoMatch d ms >>= _ = NoMatch d ms- ExactMatch d xs >>= f = addDepth d- $ foldr matchPlus matchZero (map f xs)- InexactMatch d xs >>= f = addDepth d . forceInexact- $ foldr matchPlus matchZero (map f xs)--addDepth :: Confidence -> Match a -> Match a-addDepth d' (NoMatch d msgs) = NoMatch (d'+d) msgs-addDepth d' (ExactMatch d xs) = ExactMatch (d'+d) xs-addDepth d' (InexactMatch d xs) = InexactMatch (d'+d) xs--forceInexact :: Match a -> Match a-forceInexact (ExactMatch d ys) = InexactMatch d ys-forceInexact m = m----------------------------------- Various match primitives-----matchErrorExpected, matchErrorNoSuch :: String -> String -> Match a-matchErrorExpected thing got = NoMatch 0 [MatchErrorExpected thing got]-matchErrorNoSuch thing got = NoMatch 0 [MatchErrorNoSuch thing got]--expecting :: String -> String -> Match a -> Match a-expecting thing got (NoMatch 0 _) = matchErrorExpected thing got-expecting _ _ m = m--orNoSuchThing :: String -> String -> Match a -> Match a-orNoSuchThing thing got (NoMatch 0 _) = matchErrorNoSuch thing got-orNoSuchThing _ _ m = m--increaseConfidence :: Match ()-increaseConfidence = ExactMatch 1 [()]--increaseConfidenceFor :: Match a -> Match a-increaseConfidenceFor m = m >>= \r -> increaseConfidence >> return r--nubMatches :: Eq a => Match a -> Match a-nubMatches (NoMatch d msgs) = NoMatch d msgs-nubMatches (ExactMatch d xs) = ExactMatch d (nub xs)-nubMatches (InexactMatch d xs) = InexactMatch d (nub xs)--nubMatchErrors :: Match a -> Match a-nubMatchErrors (NoMatch d msgs) = NoMatch d (nub msgs)-nubMatchErrors (ExactMatch d xs) = ExactMatch d xs-nubMatchErrors (InexactMatch d xs) = InexactMatch d xs---- | Lift a list of matches to an exact match.----exactMatches, inexactMatches :: [a] -> Match a--exactMatches [] = matchZero-exactMatches xs = ExactMatch 0 xs--inexactMatches [] = matchZero-inexactMatches xs = InexactMatch 0 xs--tryEach :: [a] -> Match a-tryEach = exactMatches------------------------------------ Top level match runner------- | Given a matcher and a key to look up, use the matcher to find all the--- possible matches. There may be 'None', a single 'Unambiguous' match or--- you may have an 'Ambiguous' match with several possibilities.----findMatch :: Eq b => Match b -> MaybeAmbiguous b-findMatch match =- case match of- NoMatch _ msgs -> None (nub msgs)- ExactMatch _ xs -> checkAmbiguous xs- InexactMatch _ xs -> checkAmbiguous xs- where- checkAmbiguous xs = case nub xs of- [x] -> Unambiguous x- xs' -> Ambiguous xs'--data MaybeAmbiguous a = None [MatchError] | Unambiguous a | Ambiguous [a]- deriving Show------------------------------------ Basic matchers-----{---- | A primitive matcher that looks up a value in a finite 'Map'. The--- value must match exactly.----matchExactly :: forall a b. Ord a => [(a, b)] -> (a -> Match b)-matchExactly xs =- \x -> case Map.lookup x m of- Nothing -> matchZero- Just ys -> ExactMatch 0 ys- where- m :: Ord a => Map a [b]- m = Map.fromListWith (++) [ (k,[x]) | (k,x) <- xs ]--}---- | A primitive matcher that looks up a value in a finite 'Map'. It checks--- for an exact or inexact match. We get an inexact match if the match--- is not exact, but the canonical forms match. It takes a canonicalisation--- function for this purpose.------ So for example if we used string case fold as the canonicalisation--- function, then we would get case insensitive matching (but it will still--- report an exact match when the case matches too).----matchInexactly :: (Ord a, Ord a') =>- (a -> a') ->- [(a, b)] -> (a -> Match b)-matchInexactly cannonicalise xs =- \x -> case Map.lookup x m of- Just ys -> exactMatches ys- Nothing -> case Map.lookup (cannonicalise x) m' of- Just ys -> inexactMatches ys- Nothing -> matchZero- where- m = Map.fromListWith (++) [ (k,[x]) | (k,x) <- xs ]-- -- the map of canonicalised keys to groups of inexact matches- m' = Map.mapKeysWith (++) cannonicalise m------------------------------------- Utils-----caseFold :: String -> String-caseFold = lowercase----- | Check that the given build targets are valid in the current context.------ Also swizzle into a more convenient form.----checkBuildTargets :: Verbosity -> PackageDescription -> LocalBuildInfo -> [BuildTarget]- -> IO [TargetInfo]-checkBuildTargets _ pkg_descr lbi [] =- return (allTargetsInBuildOrder' pkg_descr lbi)--checkBuildTargets verbosity pkg_descr lbi targets = do-- let (enabled, disabled) =- partitionEithers- [ case componentDisabledReason (componentEnabledSpec lbi) comp of- Nothing -> Left target'- Just reason -> Right (cname, reason)- | target <- targets- , let target'@(cname,_) = swizzleTarget target- , let comp = getComponent pkg_descr cname ]-- case disabled of- [] -> return ()- ((cname,reason):_) -> die' verbosity $ formatReason (showComponentName cname) reason-- for_ [ (c, t) | (c, Just t) <- enabled ] $ \(c, t) ->- warn verbosity $ "Ignoring '" ++ either prettyShow id t ++ ". The whole "- ++ showComponentName c ++ " will be processed. (Support for "- ++ "module and file targets has not been implemented yet.)"-- -- Pick out the actual CLBIs for each of these cnames- enabled' <- for enabled $ \(cname, _) -> do- case componentNameTargets' pkg_descr lbi cname of- [] -> error "checkBuildTargets: nothing enabled"- [target] -> return target- _targets -> error "checkBuildTargets: multiple copies enabled"-- return enabled'-- where- swizzleTarget (BuildTargetComponent c) = (c, Nothing)- swizzleTarget (BuildTargetModule c m) = (c, Just (Left m))- swizzleTarget (BuildTargetFile c f) = (c, Just (Right f))-- formatReason cn DisabledComponent =- "Cannot process the " ++ cn ++ " because the component is marked "- ++ "as disabled in the .cabal file."- formatReason cn DisabledAllTests =- "Cannot process the " ++ cn ++ " because test suites are not "- ++ "enabled. Run configure with the flag --enable-tests"- formatReason cn DisabledAllBenchmarks =- "Cannot process the " ++ cn ++ " because benchmarks are not "- ++ "enabled. Re-run configure with the flag --enable-benchmarks"- formatReason cn (DisabledAllButOne cn') =- "Cannot process the " ++ cn ++ " because this package was "- ++ "configured only to build " ++ cn' ++ ". Re-run configure "- ++ "with the argument " ++ cn+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}++-----------------------------------------------------------------------------++-----------------------------------------------------------------------------++-- |+-- Module : Distribution.Client.BuildTargets+-- Copyright : (c) Duncan Coutts 2012+-- License : BSD-like+--+-- Maintainer : duncan@community.haskell.org+--+-- Handling for user-specified build targets+module Distribution.Simple.BuildTarget+ ( -- * Main interface+ readTargetInfos+ , readBuildTargets -- in case you don't have LocalBuildInfo++ -- * Build targets+ , BuildTarget (..)+ , showBuildTarget+ , QualLevel (..)+ , buildTargetComponentName++ -- * Parsing user build targets+ , UserBuildTarget+ , readUserBuildTargets+ , showUserBuildTarget+ , UserBuildTargetProblem (..)+ , reportUserBuildTargetProblems++ -- * Resolving build targets+ , resolveBuildTargets+ , BuildTargetProblem (..)+ , reportBuildTargetProblems+ ) where++import Distribution.Compat.Prelude+import Prelude ()++import Distribution.Types.ComponentRequestedSpec+import Distribution.Types.ForeignLib+import Distribution.Types.LocalBuildInfo+import Distribution.Types.TargetInfo+import Distribution.Types.UnqualComponentName++import qualified Distribution.Compat.CharParsing as P+import Distribution.ModuleName+import Distribution.Package+import Distribution.PackageDescription+import Distribution.Parsec+import Distribution.Pretty+import Distribution.Simple.Errors+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.Utils+import Distribution.Utils.Path+import Distribution.Verbosity++import Control.Arrow ((&&&))+import Control.Monad (msum)+import Data.List (groupBy, stripPrefix)+import qualified Data.List.NonEmpty as NE+import qualified Data.Map as Map+import System.Directory (doesDirectoryExist, doesFileExist)+import System.FilePath as FilePath+ ( dropExtension+ , hasTrailingPathSeparator+ , joinPath+ , normalise+ , splitDirectories+ , splitPath+ )++-- | Take a list of 'String' build targets, and parse and validate them+-- into actual 'TargetInfo's to be built/registered/whatever.+readTargetInfos :: Verbosity -> PackageDescription -> LocalBuildInfo -> [String] -> IO [TargetInfo]+readTargetInfos verbosity pkg_descr lbi args = do+ build_targets <- readBuildTargets verbosity pkg_descr args+ checkBuildTargets verbosity pkg_descr lbi build_targets++-- ------------------------------------------------------------++-- * User build targets++-- ------------------------------------------------------------++-- | Various ways that a user may specify a build target.+data UserBuildTarget+ = -- | A target specified by a single name. This could be a component+ -- module or file.+ --+ -- > cabal build foo+ -- > cabal build Data.Foo+ -- > cabal build Data/Foo.hs Data/Foo.hsc+ UserBuildTargetSingle String+ | -- | A target specified by a qualifier and name. This could be a component+ -- name qualified by the component namespace kind, or a module or file+ -- qualified by the component name.+ --+ -- > cabal build lib:foo exe:foo+ -- > cabal build foo:Data.Foo+ -- > cabal build foo:Data/Foo.hs+ UserBuildTargetDouble String String+ | -- | A fully qualified target, either a module or file qualified by a+ -- component name with the component namespace kind.+ --+ -- > cabal build lib:foo:Data/Foo.hs exe:foo:Data/Foo.hs+ -- > cabal build lib:foo:Data.Foo exe:foo:Data.Foo+ UserBuildTargetTriple String String String+ deriving (Show, Eq, Ord)++-- ------------------------------------------------------------++-- * Resolved build targets++-- ------------------------------------------------------------++-- | A fully resolved build target.+data BuildTarget+ = -- | A specific component+ BuildTargetComponent ComponentName+ | -- | A specific module within a specific component.+ BuildTargetModule ComponentName ModuleName+ | -- | A specific file within a specific component.+ BuildTargetFile ComponentName FilePath+ deriving (Eq, Show, Generic)++instance Binary BuildTarget++buildTargetComponentName :: BuildTarget -> ComponentName+buildTargetComponentName (BuildTargetComponent cn) = cn+buildTargetComponentName (BuildTargetModule cn _) = cn+buildTargetComponentName (BuildTargetFile cn _) = cn++-- | Read a list of user-supplied build target strings and resolve them to+-- 'BuildTarget's according to a 'PackageDescription'. If there are problems+-- with any of the targets e.g. they don't exist or are misformatted, throw an+-- 'IOException'.+readBuildTargets :: Verbosity -> PackageDescription -> [String] -> IO [BuildTarget]+readBuildTargets verbosity pkg targetStrs = do+ let (uproblems, utargets) = readUserBuildTargets targetStrs+ reportUserBuildTargetProblems verbosity uproblems++ utargets' <- traverse checkTargetExistsAsFile utargets++ let (bproblems, btargets) = resolveBuildTargets pkg utargets'+ reportBuildTargetProblems verbosity bproblems++ return btargets++checkTargetExistsAsFile :: UserBuildTarget -> IO (UserBuildTarget, Bool)+checkTargetExistsAsFile t = do+ fexists <- existsAsFile (fileComponentOfTarget t)+ return (t, fexists)+ where+ existsAsFile f = do+ exists <- doesFileExist f+ case splitPath f of+ (d : _) | hasTrailingPathSeparator d -> doesDirectoryExist d+ (d : _ : _) | not exists -> doesDirectoryExist d+ _ -> return exists++ fileComponentOfTarget (UserBuildTargetSingle s1) = s1+ fileComponentOfTarget (UserBuildTargetDouble _ s2) = s2+ fileComponentOfTarget (UserBuildTargetTriple _ _ s3) = s3++-- ------------------------------------------------------------++-- * Parsing user targets++-- ------------------------------------------------------------++readUserBuildTargets+ :: [String]+ -> ( [UserBuildTargetProblem]+ , [UserBuildTarget]+ )+readUserBuildTargets = partitionEithers . map readUserBuildTarget++-- |+--+-- >>> readUserBuildTarget "comp"+-- Right (UserBuildTargetSingle "comp")+--+-- >>> readUserBuildTarget "lib:comp"+-- Right (UserBuildTargetDouble "lib" "comp")+--+-- >>> readUserBuildTarget "pkg:lib:comp"+-- Right (UserBuildTargetTriple "pkg" "lib" "comp")+--+-- >>> readUserBuildTarget "\"comp\""+-- Right (UserBuildTargetSingle "comp")+--+-- >>> readUserBuildTarget "lib:\"comp\""+-- Right (UserBuildTargetDouble "lib" "comp")+--+-- >>> readUserBuildTarget "pkg:lib:\"comp\""+-- Right (UserBuildTargetTriple "pkg" "lib" "comp")+--+-- >>> readUserBuildTarget "pkg:lib:comp:more"+-- Left (UserBuildTargetUnrecognised "pkg:lib:comp:more")+--+-- >>> readUserBuildTarget "pkg:\"lib\":comp"+-- Left (UserBuildTargetUnrecognised "pkg:\"lib\":comp")+readUserBuildTarget+ :: String+ -> Either+ UserBuildTargetProblem+ UserBuildTarget+readUserBuildTarget targetstr =+ case explicitEitherParsec parseTargetApprox targetstr of+ Left _ -> Left (UserBuildTargetUnrecognised targetstr)+ Right tgt -> Right tgt+ where+ parseTargetApprox :: CabalParsing m => m UserBuildTarget+ parseTargetApprox = do+ -- read one, two, or three tokens, where last could be "hs-string"+ ts <- tokens+ return $ case ts of+ (a, Nothing) -> UserBuildTargetSingle a+ (a, Just (b, Nothing)) -> UserBuildTargetDouble a b+ (a, Just (b, Just c)) -> UserBuildTargetTriple a b c++ tokens :: CabalParsing m => m (String, Maybe (String, Maybe String))+ tokens =+ (\s -> (s, Nothing)) <$> parsecHaskellString+ <|> (,) <$> token <*> P.optional (P.char ':' *> tokens2)++ tokens2 :: CabalParsing m => m (String, Maybe String)+ tokens2 =+ (\s -> (s, Nothing)) <$> parsecHaskellString+ <|> (,) <$> token <*> P.optional (P.char ':' *> (parsecHaskellString <|> token))++ token :: CabalParsing m => m String+ token = P.munch1 (\x -> not (isSpace x) && x /= ':')++data UserBuildTargetProblem+ = UserBuildTargetUnrecognised String+ deriving (Show)++reportUserBuildTargetProblems :: Verbosity -> [UserBuildTargetProblem] -> IO ()+reportUserBuildTargetProblems verbosity problems = do+ case [target | UserBuildTargetUnrecognised target <- problems] of+ [] -> return ()+ target ->+ dieWithException verbosity $+ UnrecognisedBuildTarget target++showUserBuildTarget :: UserBuildTarget -> String+showUserBuildTarget = intercalate ":" . getComponents+ where+ getComponents (UserBuildTargetSingle s1) = [s1]+ getComponents (UserBuildTargetDouble s1 s2) = [s1, s2]+ getComponents (UserBuildTargetTriple s1 s2 s3) = [s1, s2, s3]++-- | Unless you use 'QL1', this function is PARTIAL;+-- use 'showBuildTarget' instead.+showBuildTarget' :: QualLevel -> PackageId -> BuildTarget -> String+showBuildTarget' ql pkgid bt =+ showUserBuildTarget (renderBuildTarget ql bt pkgid)++-- | Unambiguously render a 'BuildTarget', so that it can+-- be parsed in all situations.+showBuildTarget :: PackageId -> BuildTarget -> String+showBuildTarget pkgid t =+ showBuildTarget' (qlBuildTarget t) pkgid t+ where+ qlBuildTarget BuildTargetComponent{} = QL2+ qlBuildTarget _ = QL3++-- ------------------------------------------------------------++-- * Resolving user targets to build targets++-- ------------------------------------------------------------++{-+stargets =+ [ BuildTargetComponent (CExeName "foo")+ , BuildTargetModule (CExeName "foo") (mkMn "Foo")+ , BuildTargetModule (CExeName "tst") (mkMn "Foo")+ ]+ where+ mkMn :: String -> ModuleName+ mkMn = fromJust . simpleParse++ex_pkgid :: PackageIdentifier+Just ex_pkgid = simpleParse "thelib"+-}++-- | Given a bunch of user-specified targets, try to resolve what it is they+-- refer to.+resolveBuildTargets+ :: PackageDescription+ -> [(UserBuildTarget, Bool)]+ -> ([BuildTargetProblem], [BuildTarget])+resolveBuildTargets pkg =+ partitionEithers+ . map (uncurry (resolveBuildTarget pkg))++resolveBuildTarget+ :: PackageDescription+ -> UserBuildTarget+ -> Bool+ -> Either BuildTargetProblem BuildTarget+resolveBuildTarget pkg userTarget fexists =+ case findMatch (matchBuildTarget pkg userTarget fexists) of+ Unambiguous target -> Right target+ Ambiguous targets -> Left (BuildTargetAmbiguous userTarget targets')+ where+ targets' =+ disambiguateBuildTargets+ (packageId pkg)+ userTarget+ targets+ None errs -> Left (classifyMatchErrors errs)+ where+ classifyMatchErrors errs+ | Just expected' <- NE.nonEmpty expected =+ let unzip' = fmap fst &&& fmap snd+ (things, got :| _) = unzip' expected'+ in BuildTargetExpected userTarget (NE.toList things) got+ | not (null nosuch) = BuildTargetNoSuch userTarget nosuch+ | otherwise = error $ "resolveBuildTarget: internal error in matching"+ where+ expected = [(thing, got) | MatchErrorExpected thing got <- errs]+ nosuch = [(thing, got) | MatchErrorNoSuch thing got <- errs]++data BuildTargetProblem+ = -- | [expected thing] (actually got)+ BuildTargetExpected UserBuildTarget [String] String+ | -- | [(no such thing, actually got)]+ BuildTargetNoSuch UserBuildTarget [(String, String)]+ | BuildTargetAmbiguous UserBuildTarget [(UserBuildTarget, BuildTarget)]+ deriving (Show)++disambiguateBuildTargets+ :: PackageId+ -> UserBuildTarget+ -> [BuildTarget]+ -> [(UserBuildTarget, BuildTarget)]+disambiguateBuildTargets pkgid original =+ disambiguate (userTargetQualLevel original)+ where+ disambiguate ql ts+ | null amb = unamb+ | otherwise = unamb ++ disambiguate (succ ql) amb+ where+ (amb, unamb) = step ql ts++ userTargetQualLevel (UserBuildTargetSingle _) = QL1+ userTargetQualLevel (UserBuildTargetDouble _ _) = QL2+ userTargetQualLevel (UserBuildTargetTriple _ _ _) = QL3++ step+ :: QualLevel+ -> [BuildTarget]+ -> ([BuildTarget], [(UserBuildTarget, BuildTarget)])+ step ql =+ (\(amb, unamb) -> (map snd $ concat amb, concat unamb))+ . partition (\g -> length g > 1)+ . groupBy (equating fst)+ . sortBy (comparing fst)+ . map (\t -> (renderBuildTarget ql t pkgid, t))++data QualLevel = QL1 | QL2 | QL3+ deriving (Enum, Show)++renderBuildTarget :: QualLevel -> BuildTarget -> PackageId -> UserBuildTarget+renderBuildTarget ql target pkgid =+ case ql of+ QL1 -> UserBuildTargetSingle s1 where s1 = single target+ QL2 -> UserBuildTargetDouble s1 s2 where (s1, s2) = double target+ QL3 -> UserBuildTargetTriple s1 s2 s3 where (s1, s2, s3) = triple target+ where+ single (BuildTargetComponent cn) = dispCName cn+ single (BuildTargetModule _ m) = prettyShow m+ single (BuildTargetFile _ f) = f++ double (BuildTargetComponent cn) = (dispKind cn, dispCName cn)+ double (BuildTargetModule cn m) = (dispCName cn, prettyShow m)+ double (BuildTargetFile cn f) = (dispCName cn, f)++ triple (BuildTargetComponent _) = error "triple BuildTargetComponent"+ triple (BuildTargetModule cn m) = (dispKind cn, dispCName cn, prettyShow m)+ triple (BuildTargetFile cn f) = (dispKind cn, dispCName cn, f)++ dispCName = componentStringName pkgid+ dispKind = showComponentKindShort . componentKind++reportBuildTargetProblems :: Verbosity -> [BuildTargetProblem] -> IO ()+reportBuildTargetProblems verbosity problems = do+ case [(t, e, g) | BuildTargetExpected t e g <- problems] of+ [] -> return ()+ targets ->+ dieWithException verbosity $+ ReportBuildTargetProblems $+ map (\(target, expected, got) -> (showUserBuildTarget target, expected, got)) targets++ case [(t, e) | BuildTargetNoSuch t e <- problems] of+ [] -> return ()+ targets ->+ dieWithException verbosity $+ UnknownBuildTarget $+ map (\(target, nosuch) -> (showUserBuildTarget target, nosuch)) targets++ case [(t, ts) | BuildTargetAmbiguous t ts <- problems] of+ [] -> return ()+ targets ->+ dieWithException verbosity $+ AmbiguousBuildTarget $+ map+ ( \(target, amb) ->+ ( showUserBuildTarget target+ , (map (\(ut, bt) -> (showUserBuildTarget ut, showBuildTargetKind bt)) amb)+ )+ )+ targets+ where+ showBuildTargetKind (BuildTargetComponent _) = "component"+ showBuildTargetKind (BuildTargetModule _ _) = "module"+ showBuildTargetKind (BuildTargetFile _ _) = "file"++----------------------------------+-- Top level BuildTarget matcher+--++matchBuildTarget+ :: PackageDescription+ -> UserBuildTarget+ -> Bool+ -> Match BuildTarget+matchBuildTarget pkg = \utarget fexists ->+ case utarget of+ UserBuildTargetSingle str1 ->+ matchBuildTarget1 cinfo str1 fexists+ UserBuildTargetDouble str1 str2 ->+ matchBuildTarget2 cinfo str1 str2 fexists+ UserBuildTargetTriple str1 str2 str3 ->+ matchBuildTarget3 cinfo str1 str2 str3 fexists+ where+ cinfo = pkgComponentInfo pkg++matchBuildTarget1 :: [ComponentInfo] -> String -> Bool -> Match BuildTarget+matchBuildTarget1 cinfo str1 fexists =+ matchComponent1 cinfo str1+ `matchPlusShadowing` matchModule1 cinfo str1+ `matchPlusShadowing` matchFile1 cinfo str1 fexists++matchBuildTarget2+ :: [ComponentInfo]+ -> String+ -> String+ -> Bool+ -> Match BuildTarget+matchBuildTarget2 cinfo str1 str2 fexists =+ matchComponent2 cinfo str1 str2+ `matchPlusShadowing` matchModule2 cinfo str1 str2+ `matchPlusShadowing` matchFile2 cinfo str1 str2 fexists++matchBuildTarget3+ :: [ComponentInfo]+ -> String+ -> String+ -> String+ -> Bool+ -> Match BuildTarget+matchBuildTarget3 cinfo str1 str2 str3 fexists =+ matchModule3 cinfo str1 str2 str3+ `matchPlusShadowing` matchFile3 cinfo str1 str2 str3 fexists++data ComponentInfo = ComponentInfo+ { cinfoName :: ComponentName+ , cinfoStrName :: ComponentStringName+ , cinfoSrcDirs :: [FilePath]+ , cinfoModules :: [ModuleName]+ , cinfoHsFiles :: [FilePath] -- other hs files (like main.hs)+ , cinfoAsmFiles :: [FilePath]+ , cinfoCmmFiles :: [FilePath]+ , cinfoCFiles :: [FilePath]+ , cinfoCxxFiles :: [FilePath]+ , cinfoJsFiles :: [FilePath]+ }++type ComponentStringName = String++pkgComponentInfo :: PackageDescription -> [ComponentInfo]+pkgComponentInfo pkg =+ [ ComponentInfo+ { cinfoName = componentName c+ , cinfoStrName = componentStringName pkg (componentName c)+ , cinfoSrcDirs = map getSymbolicPath $ hsSourceDirs bi+ , cinfoModules = componentModules c+ , cinfoHsFiles = map getSymbolicPath $ componentHsFiles c+ , cinfoAsmFiles = map getSymbolicPath $ asmSources bi+ , cinfoCmmFiles = map getSymbolicPath $ cmmSources bi+ , cinfoCFiles = map getSymbolicPath $ cSources bi+ , cinfoCxxFiles = map getSymbolicPath $ cxxSources bi+ , cinfoJsFiles = map getSymbolicPath $ jsSources bi+ }+ | c <- pkgComponents pkg+ , let bi = componentBuildInfo c+ ]++componentStringName :: Package pkg => pkg -> ComponentName -> ComponentStringName+componentStringName pkg (CLibName LMainLibName) = prettyShow (packageName pkg)+componentStringName _ (CLibName (LSubLibName name)) = unUnqualComponentName name+componentStringName _ (CFLibName name) = unUnqualComponentName name+componentStringName _ (CExeName name) = unUnqualComponentName name+componentStringName _ (CTestName name) = unUnqualComponentName name+componentStringName _ (CBenchName name) = unUnqualComponentName name++componentModules :: Component -> [ModuleName]+-- TODO: Use of 'explicitLibModules' here is a bit wrong:+-- a user could very well ask to build a specific signature+-- that was inherited from other packages. To fix this+-- we have to plumb 'LocalBuildInfo' through this code.+-- Fortunately, this is only used by 'pkgComponentInfo'+-- Please don't export this function unless you plan on fixing+-- this.+componentModules (CLib lib) = explicitLibModules lib+componentModules (CFLib flib) = foreignLibModules flib+componentModules (CExe exe) = exeModules exe+componentModules (CTest test) = testModules test+componentModules (CBench bench) = benchmarkModules bench++componentHsFiles :: Component -> [RelativePath Source File]+componentHsFiles (CExe exe) = [modulePath exe]+componentHsFiles+ ( CTest+ TestSuite+ { testInterface = TestSuiteExeV10 _ mainfile+ }+ ) = [mainfile]+componentHsFiles+ ( CBench+ Benchmark+ { benchmarkInterface = BenchmarkExeV10 _ mainfile+ }+ ) = [mainfile]+componentHsFiles _ = []++{-+ex_cs :: [ComponentInfo]+ex_cs =+ [ (mkC (CExeName "foo") ["src1", "src1/src2"] ["Foo", "Src2.Bar", "Bar"])+ , (mkC (CExeName "tst") ["src1", "test"] ["Foo"])+ ]+ where+ mkC n ds ms = ComponentInfo n (componentStringName pkgid n) ds (map mkMn ms)+ mkMn :: String -> ModuleName+ mkMn = fromJust . simpleParse+ pkgid :: PackageIdentifier+ Just pkgid = simpleParse "thelib"+-}++------------------------------+-- Matching component kinds+--++data ComponentKind = LibKind | FLibKind | ExeKind | TestKind | BenchKind+ deriving (Eq, Ord, Show, Enum, Bounded)++componentKind :: ComponentName -> ComponentKind+componentKind (CLibName _) = LibKind+componentKind (CFLibName _) = FLibKind+componentKind (CExeName _) = ExeKind+componentKind (CTestName _) = TestKind+componentKind (CBenchName _) = BenchKind++cinfoKind :: ComponentInfo -> ComponentKind+cinfoKind = componentKind . cinfoName++matchComponentKind :: String -> Match ComponentKind+matchComponentKind s+ | s `elem` ["lib", "library"] = return' LibKind+ | s `elem` ["flib", "foreign-lib", "foreign-library"] = return' FLibKind+ | s `elem` ["exe", "executable"] = return' ExeKind+ | s `elem` ["tst", "test", "test-suite"] = return' TestKind+ | s `elem` ["bench", "benchmark"] = return' BenchKind+ | otherwise = matchErrorExpected "component kind" s+ where+ return' ck = increaseConfidence >> return ck++showComponentKind :: ComponentKind -> String+showComponentKind LibKind = "library"+showComponentKind FLibKind = "foreign-library"+showComponentKind ExeKind = "executable"+showComponentKind TestKind = "test-suite"+showComponentKind BenchKind = "benchmark"++showComponentKindShort :: ComponentKind -> String+showComponentKindShort LibKind = "lib"+showComponentKindShort FLibKind = "flib"+showComponentKindShort ExeKind = "exe"+showComponentKindShort TestKind = "test"+showComponentKindShort BenchKind = "bench"++------------------------------+-- Matching component targets+--++matchComponent1 :: [ComponentInfo] -> String -> Match BuildTarget+matchComponent1 cs = \str1 -> do+ guardComponentName str1+ c <- matchComponentName cs str1+ return (BuildTargetComponent (cinfoName c))++matchComponent2 :: [ComponentInfo] -> String -> String -> Match BuildTarget+matchComponent2 cs = \str1 str2 -> do+ ckind <- matchComponentKind str1+ guardComponentName str2+ c <- matchComponentKindAndName cs ckind str2+ return (BuildTargetComponent (cinfoName c))++-- utils:++guardComponentName :: String -> Match ()+guardComponentName s+ | all validComponentChar s+ && not (null s) =+ increaseConfidence+ | otherwise = matchErrorExpected "component name" s+ where+ validComponentChar c =+ isAlphaNum c+ || c == '.'+ || c == '_'+ || c == '-'+ || c == '\''++matchComponentName :: [ComponentInfo] -> String -> Match ComponentInfo+matchComponentName cs str =+ orNoSuchThing "component" str $+ increaseConfidenceFor $+ matchInexactly+ caseFold+ [(cinfoStrName c, c) | c <- cs]+ str++matchComponentKindAndName+ :: [ComponentInfo]+ -> ComponentKind+ -> String+ -> Match ComponentInfo+matchComponentKindAndName cs ckind str =+ orNoSuchThing (showComponentKind ckind ++ " component") str $+ increaseConfidenceFor $+ matchInexactly+ (\(ck, cn) -> (ck, caseFold cn))+ [((cinfoKind c, cinfoStrName c), c) | c <- cs]+ (ckind, str)++------------------------------+-- Matching module targets+--++matchModule1 :: [ComponentInfo] -> String -> Match BuildTarget+matchModule1 cs = \str1 -> do+ guardModuleName str1+ nubMatchErrors $ do+ c <- tryEach cs+ let ms = cinfoModules c+ m <- matchModuleName ms str1+ return (BuildTargetModule (cinfoName c) m)++matchModule2 :: [ComponentInfo] -> String -> String -> Match BuildTarget+matchModule2 cs = \str1 str2 -> do+ guardComponentName str1+ guardModuleName str2+ c <- matchComponentName cs str1+ let ms = cinfoModules c+ m <- matchModuleName ms str2+ return (BuildTargetModule (cinfoName c) m)++matchModule3+ :: [ComponentInfo]+ -> String+ -> String+ -> String+ -> Match BuildTarget+matchModule3 cs str1 str2 str3 = do+ ckind <- matchComponentKind str1+ guardComponentName str2+ c <- matchComponentKindAndName cs ckind str2+ guardModuleName str3+ let ms = cinfoModules c+ m <- matchModuleName ms str3+ return (BuildTargetModule (cinfoName c) m)++-- utils:++guardModuleName :: String -> Match ()+guardModuleName s+ | all validModuleChar s+ && not (null s) =+ increaseConfidence+ | otherwise = matchErrorExpected "module name" s+ where+ validModuleChar c = isAlphaNum c || c == '.' || c == '_' || c == '\''++matchModuleName :: [ModuleName] -> String -> Match ModuleName+matchModuleName ms str =+ orNoSuchThing "module" str $+ increaseConfidenceFor $+ matchInexactly+ caseFold+ [ (prettyShow m, m)+ | m <- ms+ ]+ str++------------------------------+-- Matching file targets+--++matchFile1 :: [ComponentInfo] -> String -> Bool -> Match BuildTarget+matchFile1 cs str1 exists =+ nubMatchErrors $ do+ c <- tryEach cs+ filepath <- matchComponentFile c str1 exists+ return (BuildTargetFile (cinfoName c) filepath)++matchFile2 :: [ComponentInfo] -> String -> String -> Bool -> Match BuildTarget+matchFile2 cs str1 str2 exists = do+ guardComponentName str1+ c <- matchComponentName cs str1+ filepath <- matchComponentFile c str2 exists+ return (BuildTargetFile (cinfoName c) filepath)++matchFile3+ :: [ComponentInfo]+ -> String+ -> String+ -> String+ -> Bool+ -> Match BuildTarget+matchFile3 cs str1 str2 str3 exists = do+ ckind <- matchComponentKind str1+ guardComponentName str2+ c <- matchComponentKindAndName cs ckind str2+ filepath <- matchComponentFile c str3 exists+ return (BuildTargetFile (cinfoName c) filepath)++matchComponentFile :: ComponentInfo -> String -> Bool -> Match FilePath+matchComponentFile c str fexists =+ expecting "file" str $+ matchPlus+ (matchFileExists str fexists)+ ( matchPlusShadowing+ ( msum+ [ matchModuleFileRooted dirs ms str+ , matchOtherFileRooted dirs hsFiles str+ ]+ )+ ( msum+ [ matchModuleFileUnrooted ms str+ , matchOtherFileUnrooted hsFiles str+ , matchOtherFileUnrooted cFiles str+ , matchOtherFileUnrooted jsFiles str+ ]+ )+ )+ where+ dirs = cinfoSrcDirs c+ ms = cinfoModules c+ hsFiles = cinfoHsFiles c+ cFiles = cinfoCFiles c+ jsFiles = cinfoJsFiles c++-- utils++matchFileExists :: FilePath -> Bool -> Match a+matchFileExists _ False = mzero+matchFileExists fname True = do+ increaseConfidence+ matchErrorNoSuch "file" fname++matchModuleFileUnrooted :: [ModuleName] -> String -> Match FilePath+matchModuleFileUnrooted ms str = do+ let filepath = normalise str+ _ <- matchModuleFileStem ms filepath+ return filepath++matchModuleFileRooted :: [FilePath] -> [ModuleName] -> String -> Match FilePath+matchModuleFileRooted dirs ms str = nubMatches $ do+ let filepath = normalise str+ filepath' <- matchDirectoryPrefix dirs filepath+ _ <- matchModuleFileStem ms filepath'+ return filepath++matchModuleFileStem :: [ModuleName] -> FilePath -> Match ModuleName+matchModuleFileStem ms =+ increaseConfidenceFor+ . matchInexactly+ caseFold+ [(toFilePath m, m) | m <- ms]+ . dropExtension++matchOtherFileRooted :: [FilePath] -> [FilePath] -> FilePath -> Match FilePath+matchOtherFileRooted dirs fs str = do+ let filepath = normalise str+ filepath' <- matchDirectoryPrefix dirs filepath+ _ <- matchFile fs filepath'+ return filepath++matchOtherFileUnrooted :: [FilePath] -> FilePath -> Match FilePath+matchOtherFileUnrooted fs str = do+ let filepath = normalise str+ _ <- matchFile fs filepath+ return filepath++matchFile :: [FilePath] -> FilePath -> Match FilePath+matchFile fs =+ increaseConfidenceFor+ . matchInexactly caseFold [(f, f) | f <- fs]++matchDirectoryPrefix :: [FilePath] -> FilePath -> Match FilePath+matchDirectoryPrefix dirs filepath =+ exactMatches $+ catMaybes+ [stripDirectory (normalise dir) filepath | dir <- dirs]+ where+ stripDirectory :: FilePath -> FilePath -> Maybe FilePath+ stripDirectory dir fp =+ joinPath `fmap` stripPrefix (splitDirectories dir) (splitDirectories fp)++------------------------------+-- Matching monad+--++-- | A matcher embodies a way to match some input as being some recognised+-- value. In particular it deals with multiple and ambiguous matches.+--+-- There are various matcher primitives ('matchExactly', 'matchInexactly'),+-- ways to combine matchers ('ambiguousWith', 'shadows') and finally we can+-- run a matcher against an input using 'findMatch'.+data Match a+ = NoMatch Confidence [MatchError]+ | ExactMatch Confidence [a]+ | InexactMatch Confidence [a]+ deriving (Show)++type Confidence = Int++data MatchError+ = MatchErrorExpected String String+ | MatchErrorNoSuch String String+ deriving (Show, Eq)++instance Alternative Match where+ empty = mzero+ (<|>) = mplus++instance MonadPlus Match where+ mzero = matchZero+ mplus = matchPlus++matchZero :: Match a+matchZero = NoMatch 0 []++-- | Combine two matchers. Exact matches are used over inexact matches+-- but if we have multiple exact, or inexact then the we collect all the+-- ambiguous matches.+matchPlus :: Match a -> Match a -> Match a+matchPlus (ExactMatch d1 xs) (ExactMatch d2 xs') =+ ExactMatch (max d1 d2) (xs ++ xs')+matchPlus a@(ExactMatch _ _) (InexactMatch _ _) = a+matchPlus a@(ExactMatch _ _) (NoMatch _ _) = a+matchPlus (InexactMatch _ _) b@(ExactMatch _ _) = b+matchPlus (InexactMatch d1 xs) (InexactMatch d2 xs') =+ InexactMatch (max d1 d2) (xs ++ xs')+matchPlus a@(InexactMatch _ _) (NoMatch _ _) = a+matchPlus (NoMatch _ _) b@(ExactMatch _ _) = b+matchPlus (NoMatch _ _) b@(InexactMatch _ _) = b+matchPlus a@(NoMatch d1 ms) b@(NoMatch d2 ms')+ | d1 > d2 = a+ | d1 < d2 = b+ | otherwise = NoMatch d1 (ms ++ ms')++-- | Combine two matchers. This is similar to 'ambiguousWith' with the+-- difference that an exact match from the left matcher shadows any exact+-- match on the right. Inexact matches are still collected however.+matchPlusShadowing :: Match a -> Match a -> Match a+matchPlusShadowing a@(ExactMatch _ _) (ExactMatch _ _) = a+matchPlusShadowing a b = matchPlus a b++instance Functor Match where+ fmap _ (NoMatch d ms) = NoMatch d ms+ fmap f (ExactMatch d xs) = ExactMatch d (fmap f xs)+ fmap f (InexactMatch d xs) = InexactMatch d (fmap f xs)++instance Applicative Match where+ pure a = ExactMatch 0 [a]+ (<*>) = ap++instance Monad Match where+ return = pure++ NoMatch d ms >>= _ = NoMatch d ms+ ExactMatch d xs >>= f =+ addDepth d $+ foldr matchPlus matchZero (map f xs)+ InexactMatch d xs >>= f =+ addDepth d . forceInexact $+ foldr matchPlus matchZero (map f xs)++addDepth :: Confidence -> Match a -> Match a+addDepth d' (NoMatch d msgs) = NoMatch (d' + d) msgs+addDepth d' (ExactMatch d xs) = ExactMatch (d' + d) xs+addDepth d' (InexactMatch d xs) = InexactMatch (d' + d) xs++forceInexact :: Match a -> Match a+forceInexact (ExactMatch d ys) = InexactMatch d ys+forceInexact m = m++------------------------------+-- Various match primitives+--++matchErrorExpected, matchErrorNoSuch :: String -> String -> Match a+matchErrorExpected thing got = NoMatch 0 [MatchErrorExpected thing got]+matchErrorNoSuch thing got = NoMatch 0 [MatchErrorNoSuch thing got]++expecting :: String -> String -> Match a -> Match a+expecting thing got (NoMatch 0 _) = matchErrorExpected thing got+expecting _ _ m = m++orNoSuchThing :: String -> String -> Match a -> Match a+orNoSuchThing thing got (NoMatch 0 _) = matchErrorNoSuch thing got+orNoSuchThing _ _ m = m++increaseConfidence :: Match ()+increaseConfidence = ExactMatch 1 [()]++increaseConfidenceFor :: Match a -> Match a+increaseConfidenceFor m = m >>= \r -> increaseConfidence >> return r++nubMatches :: Eq a => Match a -> Match a+nubMatches (NoMatch d msgs) = NoMatch d msgs+nubMatches (ExactMatch d xs) = ExactMatch d (nub xs)+nubMatches (InexactMatch d xs) = InexactMatch d (nub xs)++nubMatchErrors :: Match a -> Match a+nubMatchErrors (NoMatch d msgs) = NoMatch d (nub msgs)+nubMatchErrors (ExactMatch d xs) = ExactMatch d xs+nubMatchErrors (InexactMatch d xs) = InexactMatch d xs++-- | Lift a list of matches to an exact match.+exactMatches, inexactMatches :: [a] -> Match a+exactMatches [] = matchZero+exactMatches xs = ExactMatch 0 xs+inexactMatches [] = matchZero+inexactMatches xs = InexactMatch 0 xs++tryEach :: [a] -> Match a+tryEach = exactMatches++------------------------------+-- Top level match runner+--++-- | Given a matcher and a key to look up, use the matcher to find all the+-- possible matches. There may be 'None', a single 'Unambiguous' match or+-- you may have an 'Ambiguous' match with several possibilities.+findMatch :: Eq b => Match b -> MaybeAmbiguous b+findMatch match =+ case match of+ NoMatch _ msgs -> None (nub msgs)+ ExactMatch _ xs -> checkAmbiguous xs+ InexactMatch _ xs -> checkAmbiguous xs+ where+ checkAmbiguous xs = case nub xs of+ [x] -> Unambiguous x+ xs' -> Ambiguous xs'++data MaybeAmbiguous a = None [MatchError] | Unambiguous a | Ambiguous [a]+ deriving (Show)++------------------------------+-- Basic matchers+--++{-+-- | A primitive matcher that looks up a value in a finite 'Map'. The+-- value must match exactly.+--+matchExactly :: forall a b. Ord a => [(a, b)] -> (a -> Match b)+matchExactly xs =+ \x -> case Map.lookup x m of+ Nothing -> matchZero+ Just ys -> ExactMatch 0 ys+ where+ m :: Ord a => Map a [b]+ m = Map.fromListWith (++) [ (k,[x]) | (k,x) <- xs ]+-}++-- | A primitive matcher that looks up a value in a finite 'Map'. It checks+-- for an exact or inexact match. We get an inexact match if the match+-- is not exact, but the canonical forms match. It takes a canonicalisation+-- function for this purpose.+--+-- So for example if we used string case fold as the canonicalisation+-- function, then we would get case insensitive matching (but it will still+-- report an exact match when the case matches too).+matchInexactly+ :: (Ord a, Ord a')+ => (a -> a')+ -> [(a, b)]+ -> (a -> Match b)+matchInexactly cannonicalise xs =+ \x -> case Map.lookup x m of+ Just ys -> exactMatches ys+ Nothing -> case Map.lookup (cannonicalise x) m' of+ Just ys -> inexactMatches ys+ Nothing -> matchZero+ where+ m = Map.fromListWith (++) [(k, [x]) | (k, x) <- xs]++ -- the map of canonicalised keys to groups of inexact matches+ m' = Map.mapKeysWith (++) cannonicalise m++------------------------------+-- Utils+--++caseFold :: String -> String+caseFold = lowercase++-- | Check that the given build targets are valid in the current context.+--+-- Also swizzle into a more convenient form.+checkBuildTargets+ :: Verbosity+ -> PackageDescription+ -> LocalBuildInfo+ -> [BuildTarget]+ -> IO [TargetInfo]+checkBuildTargets _ pkg_descr lbi [] =+ return (allTargetsInBuildOrder' pkg_descr lbi)+checkBuildTargets+ verbosity+ pkg_descr+ lbi@(LocalBuildInfo{componentEnabledSpec = enabledComps})+ targets = do+ let (enabled, disabled) =+ partitionEithers+ [ case componentDisabledReason enabledComps comp of+ Nothing -> Left target'+ Just reason -> Right (cname, reason)+ | target <- targets+ , let target'@(cname, _) = swizzleTarget target+ , let comp = getComponent pkg_descr cname+ ]++ case disabled of+ [] -> return ()+ ((cname, reason) : _) -> dieWithException verbosity $ CheckBuildTargets $ formatReason (showComponentName cname) reason++ for_ [(c, t) | (c, Just t) <- enabled] $ \(c, t) ->+ warn verbosity $+ "Ignoring '"+ ++ either prettyShow id t+ ++ ". The whole "+ ++ showComponentName c+ ++ " will be processed. (Support for "+ ++ "module and file targets has not been implemented yet.)"++ -- Pick out the actual CLBIs for each of these cnames+ enabled' <- for enabled $ \(cname, _) -> do+ case componentNameTargets' pkg_descr lbi cname of+ [] -> error "checkBuildTargets: nothing enabled"+ [target] -> return target+ _targets -> error "checkBuildTargets: multiple copies enabled"++ return enabled'+ where+ swizzleTarget (BuildTargetComponent c) = (c, Nothing)+ swizzleTarget (BuildTargetModule c m) = (c, Just (Left m))+ swizzleTarget (BuildTargetFile c f) = (c, Just (Right f))++ formatReason cn DisabledComponent =+ "Cannot process the "+ ++ cn+ ++ " because the component is marked "+ ++ "as disabled in the .cabal file."+ formatReason cn DisabledAllTests =+ "Cannot process the "+ ++ cn+ ++ " because test suites are not "+ ++ "enabled. Run configure with the flag --enable-tests"+ formatReason cn DisabledAllBenchmarks =+ "Cannot process the "+ ++ cn+ ++ " because benchmarks are not "+ ++ "enabled. Re-run configure with the flag --enable-benchmarks"+ formatReason cn (DisabledAllButOne cn') =+ "Cannot process the "+ ++ cn+ ++ " because this package was "+ ++ "configured only to build "+ ++ cn'+ ++ ". Re-run configure "+ ++ "with the argument "+ ++ cn
src/Distribution/Simple/BuildToolDepends.hs view
@@ -5,18 +5,44 @@ -- the functions contained to access those fields directly. module Distribution.Simple.BuildToolDepends where -import Prelude ()-import Distribution.Compat.Prelude+import Distribution.Compat.Prelude+import Prelude () import qualified Data.Map as Map -import Distribution.Package-import Distribution.PackageDescription+import Distribution.Package+import Distribution.PackageDescription --- | Desugar a "build-tools" entry into proper a executable dependency if+-- | Same as 'desugarBuildTool', but requires atomic information (package+-- name, executable names) instead of a whole 'PackageDescription'.+desugarBuildToolSimple+ :: PackageName+ -> [UnqualComponentName]+ -> LegacyExeDependency+ -> Maybe ExeDependency+desugarBuildToolSimple pname exeNames (LegacyExeDependency name reqVer)+ | foundLocal = Just $ ExeDependency pname toolName reqVer+ | otherwise = Map.lookup name allowMap+ where+ toolName = mkUnqualComponentName name+ foundLocal = toolName `elem` exeNames+ allowlist =+ [ "hscolour"+ , "haddock"+ , "happy"+ , "alex"+ , "hsc2hs"+ , "c2hs"+ , "cpphs"+ , "hspec-discover"+ ]+ allowMap = Map.fromList $ flip map allowlist $ \n ->+ (n, ExeDependency (mkPackageName n) (mkUnqualComponentName n) reqVer)++-- | Desugar a "build-tools" entry into a proper executable dependency if -- possible. ----- An entry can be so desguared in two cases:+-- An entry can be so desugared in two cases: -- -- 1. The name in build-tools matches a locally defined executable. The -- executable dependency produced is on that exe in the current package.@@ -26,31 +52,25 @@ -- the same, but the hard-coding could just as well be per-key. -- -- The first cases matches first.-desugarBuildTool :: PackageDescription- -> LegacyExeDependency- -> Maybe ExeDependency+desugarBuildTool+ :: PackageDescription+ -> LegacyExeDependency+ -> Maybe ExeDependency desugarBuildTool pkg led =- if foundLocal- then Just $ ExeDependency (packageName pkg) toolName reqVer- else Map.lookup name whiteMap- where- LegacyExeDependency name reqVer = led- toolName = mkUnqualComponentName name- foundLocal = toolName `elem` map exeName (executables pkg)- whitelist = [ "hscolour", "haddock", "happy", "alex", "hsc2hs", "c2hs"- , "cpphs", "greencard", "hspec-discover"- ]- whiteMap = Map.fromList $ flip map whitelist $ \n ->- (n, ExeDependency (mkPackageName n) (mkUnqualComponentName n) reqVer)+ desugarBuildToolSimple+ (packageName pkg)+ (map exeName $ executables pkg)+ led -- | Get everything from "build-tool-depends", along with entries from -- "build-tools" that we know how to desugar. -- -- This should almost always be used instead of just accessing the -- `buildToolDepends` field directly.-getAllToolDependencies :: PackageDescription- -> BuildInfo- -> [ExeDependency]+getAllToolDependencies+ :: PackageDescription+ -> BuildInfo+ -> [ExeDependency] getAllToolDependencies pkg bi = buildToolDepends bi ++ mapMaybe (desugarBuildTool pkg) (buildTools bi) @@ -78,14 +98,14 @@ isInternal :: PackageDescription -> ExeDependency -> Bool isInternal pkg (ExeDependency n _ _) = n == packageName pkg - -- | Get internal "build-tool-depends", along with internal "build-tools" -- -- This is a tiny function, but used in a number of places. The same -- restrictions that apply to `isInternal` also apply to this function.-getAllInternalToolDependencies :: PackageDescription- -> BuildInfo- -> [UnqualComponentName]+getAllInternalToolDependencies+ :: PackageDescription+ -> BuildInfo+ -> [UnqualComponentName] getAllInternalToolDependencies pkg bi = [ toolname | dep@(ExeDependency _ toolname _) <- getAllToolDependencies pkg bi
+ src/Distribution/Simple/BuildWay.hs view
@@ -0,0 +1,14 @@+{-# LANGUAGE LambdaCase #-}++module Distribution.Simple.BuildWay where++data BuildWay = StaticWay | DynWay | ProfWay | ProfDynWay+ deriving (Eq, Ord, Show, Read, Enum)++-- | Returns the object/interface extension prefix for the given build way (e.g. "dyn_" for 'DynWay')+buildWayPrefix :: BuildWay -> String+buildWayPrefix = \case+ StaticWay -> ""+ ProfWay -> "p_"+ DynWay -> "dyn_"+ ProfDynWay -> "p_dyn_"
src/Distribution/Simple/CCompiler.hs view
@@ -1,14 +1,4 @@ -------------------------------------------------------------------------------- |--- Module : Distribution.Simple.CCompiler--- Copyright : 2011, Dan Knapp------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ This simple package provides types and functions for interacting with--- C compilers. Currently it's just a type enumerating extant C-like--- languages, which we call dialects. {- Redistribution and use in source and binary forms, with or without@@ -39,85 +29,109 @@ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -} -module Distribution.Simple.CCompiler (- CDialect(..),- cSourceExtensions,- cDialectFilenameExtension,- filenameCDialect+-- |+-- Module : Distribution.Simple.CCompiler+-- Copyright : 2011, Dan Knapp+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- This simple package provides types and functions for interacting with+-- C compilers. Currently it's just a type enumerating extant C-like+-- languages, which we call dialects.+module Distribution.Simple.CCompiler+ ( CDialect (..)+ , cSourceExtensions+ , cDialectFilenameExtension+ , filenameCDialect ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () import System.FilePath- ( takeExtension )-+ ( takeExtension+ ) -- | Represents a dialect of C. The Monoid instance expresses backward -- compatibility, in the sense that 'mappend a b' is the least inclusive -- dialect which both 'a' and 'b' can be correctly interpreted as.-data CDialect = C- | ObjectiveC- | CPlusPlus- | ObjectiveCPlusPlus- deriving (Eq, Show)+data CDialect+ = C+ | ObjectiveC+ | CPlusPlus+ | ObjectiveCPlusPlus+ deriving (Eq, Show) instance Monoid CDialect where mempty = C mappend = (<>) instance Semigroup CDialect where- C <> anything = anything- ObjectiveC <> CPlusPlus = ObjectiveCPlusPlus- CPlusPlus <> ObjectiveC = ObjectiveCPlusPlus- _ <> ObjectiveCPlusPlus = ObjectiveCPlusPlus- ObjectiveC <> _ = ObjectiveC- CPlusPlus <> _ = CPlusPlus- ObjectiveCPlusPlus <> _ = ObjectiveCPlusPlus+ C <> anything = anything+ ObjectiveC <> CPlusPlus = ObjectiveCPlusPlus+ CPlusPlus <> ObjectiveC = ObjectiveCPlusPlus+ _ <> ObjectiveCPlusPlus = ObjectiveCPlusPlus+ ObjectiveC <> _ = ObjectiveC+ CPlusPlus <> _ = CPlusPlus+ ObjectiveCPlusPlus <> _ = ObjectiveCPlusPlus -- | A list of all file extensions which are recognized as possibly containing -- some dialect of C code. Note that this list is only for source files, -- not for header files. cSourceExtensions :: [String]-cSourceExtensions = ["c", "i", "ii", "m", "mi", "mm", "M", "mii", "cc", "cp",- "cxx", "cpp", "CPP", "c++", "C"]-+cSourceExtensions =+ [ "c"+ , "i"+ , "ii"+ , "m"+ , "mi"+ , "mm"+ , "M"+ , "mii"+ , "cc"+ , "cp"+ , "cxx"+ , "cpp"+ , "CPP"+ , "c++"+ , "C"+ ] -- | Takes a dialect of C and whether code is intended to be passed through -- the preprocessor, and returns a filename extension for containing that -- code. cDialectFilenameExtension :: CDialect -> Bool -> String-cDialectFilenameExtension C True = "c"+cDialectFilenameExtension C True = "c" cDialectFilenameExtension C False = "i"-cDialectFilenameExtension ObjectiveC True = "m"+cDialectFilenameExtension ObjectiveC True = "m" cDialectFilenameExtension ObjectiveC False = "mi"-cDialectFilenameExtension CPlusPlus True = "cpp"-cDialectFilenameExtension CPlusPlus False = "ii"-cDialectFilenameExtension ObjectiveCPlusPlus True = "mm"+cDialectFilenameExtension CPlusPlus True = "cpp"+cDialectFilenameExtension CPlusPlus False = "ii"+cDialectFilenameExtension ObjectiveCPlusPlus True = "mm" cDialectFilenameExtension ObjectiveCPlusPlus False = "mii" - -- | Infers from a filename's extension the dialect of C which it contains, -- and whether it is intended to be passed through the preprocessor. filenameCDialect :: String -> Maybe (CDialect, Bool) filenameCDialect filename = do extension <- case takeExtension filename of- '.':ext -> Just ext- _ -> Nothing+ '.' : ext -> Just ext+ _ -> Nothing case extension of- "c" -> return (C, True)- "i" -> return (C, False)- "ii" -> return (CPlusPlus, False)- "m" -> return (ObjectiveC, True)- "mi" -> return (ObjectiveC, False)- "mm" -> return (ObjectiveCPlusPlus, True)- "M" -> return (ObjectiveCPlusPlus, True)+ "c" -> return (C, True)+ "i" -> return (C, False)+ "ii" -> return (CPlusPlus, False)+ "m" -> return (ObjectiveC, True)+ "mi" -> return (ObjectiveC, False)+ "mm" -> return (ObjectiveCPlusPlus, True)+ "M" -> return (ObjectiveCPlusPlus, True) "mii" -> return (ObjectiveCPlusPlus, False)- "cc" -> return (CPlusPlus, True)- "cp" -> return (CPlusPlus, True)+ "cc" -> return (CPlusPlus, True)+ "cp" -> return (CPlusPlus, True) "cxx" -> return (CPlusPlus, True) "cpp" -> return (CPlusPlus, True) "CPP" -> return (CPlusPlus, True) "c++" -> return (CPlusPlus, True)- "C" -> return (CPlusPlus, True)- _ -> Nothing+ "C" -> return (CPlusPlus, True)+ _ -> Nothing
src/Distribution/Simple/Command.hs view
@@ -1,8 +1,11 @@ {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Command -- Copyright : Duncan Coutts 2007@@ -18,88 +21,103 @@ -- run. It handles some common stuff automatically, like the @--help@ and -- command line completion flags. It is designed to allow other tools make -- derived commands. This feature is used heavily in @cabal-install@.--module Distribution.Simple.Command (-- -- * Command interface- CommandUI(..),- commandShowOptions,- CommandParse(..),- commandParseArgs,- getNormalCommandDescriptions,- helpCommandUI,-- -- ** Constructing commands- ShowOrParseArgs(..),- usageDefault,- usageAlternatives,- mkCommandUI,- hiddenCommand,+module Distribution.Simple.Command+ ( -- * Command interface+ CommandUI (..)+ , commandShowOptions+ , CommandParse (..)+ , commandParseArgs+ , getNormalCommandDescriptions+ , helpCommandUI - -- ** Associating actions with commands- Command,- commandAddAction,- noExtraFlags,+ -- ** Constructing commands+ , ShowOrParseArgs (..)+ , usageDefault+ , usageAlternatives+ , mkCommandUI+ , hiddenCommand - -- ** Building lists of commands- CommandType(..),- CommandSpec(..),- commandFromSpec,+ -- ** Associating actions with commands+ , Command+ , commandAddAction+ , noExtraFlags - -- ** Running commands- commandsRun,+ -- ** Building lists of commands+ , CommandType (..)+ , CommandSpec (..)+ , commandFromSpec --- * Option Fields- OptionField(..), Name,+ -- ** Running commands+ , commandsRun+ , commandsRunWithFallback+ , defaultCommandFallback --- ** Constructing Option Fields- option, multiOption,+ -- * Option Fields+ , OptionField (..)+ , Name --- ** Liftings & Projections- liftOption, liftOptionL,+ -- ** Constructing Option Fields+ , option+ , multiOption --- * Option Descriptions- OptDescr(..), Description, SFlags, LFlags, OptFlags, ArgPlaceHolder,+ -- ** Liftings & Projections+ , liftOption+ , liftOptionL --- ** OptDescr 'smart' constructors- MkOptDescr,- reqArg, reqArg', optArg, optArg', noArg,- boolOpt, boolOpt', choiceOpt, choiceOptFromEnum+ -- * Option Descriptions+ , OptDescr (..)+ , fmapOptDescr+ , Description+ , SFlags+ , LFlags+ , OptFlags+ , ArgPlaceHolder + -- ** OptDescr 'smart' constructors+ , MkOptDescr+ , reqArg+ , reqArg'+ , optArg+ , optArg'+ , optArgDef'+ , noArg+ , boolOpt+ , boolOpt'+ , choiceOpt+ , choiceOptFromEnum ) where -import Prelude () import Distribution.Compat.Prelude hiding (get)+import Prelude () import qualified Data.Array as Array import qualified Data.List as List+import Distribution.Compat.Lens (ALens', (#~), (^#)) import qualified Distribution.GetOpt as GetOpt import Distribution.ReadE import Distribution.Simple.Utils-import Distribution.Compat.Lens (ALens', (^#), (#~)) --data CommandUI flags = CommandUI {- -- | The name of the command as it would be entered on the command line.- -- For example @\"build\"@.- commandName :: String,- -- | A short, one line description of the command to use in help texts.- commandSynopsis :: String,- -- | A function that maps a program name to a usage summary for this- -- command.- commandUsage :: String -> String,- -- | Additional explanation of the command to use in help texts.- commandDescription :: Maybe (String -> String),- -- | Post-Usage notes and examples in help texts- commandNotes :: Maybe (String -> String),- -- | Initial \/ empty flags- commandDefaultFlags :: flags,- -- | All the Option fields for this command- commandOptions :: ShowOrParseArgs -> [OptionField flags]+data CommandUI flags = CommandUI+ { commandName :: String+ -- ^ The name of the command as it would be entered on the command line.+ -- For example @\"build\"@.+ , commandSynopsis :: String+ -- ^ A short, one line description of the command to use in help texts.+ , commandUsage :: String -> String+ -- ^ A function that maps a program name to a usage summary for this+ -- command.+ , commandDescription :: Maybe (String -> String)+ -- ^ Additional explanation of the command to use in help texts.+ , commandNotes :: Maybe (String -> String)+ -- ^ Post-Usage notes and examples in help texts+ , commandDefaultFlags :: flags+ -- ^ Initial \/ empty flags+ , commandOptions :: ShowOrParseArgs -> [OptionField flags]+ -- ^ All the Option fields for this command } data ShowOrParseArgs = ShowArgs | ParseArgs-type Name = String+type Name = String type Description = String -- | We usually have a data type for storing configuration values, where@@ -107,481 +125,670 @@ -- the value either via command line flags or a configuration file. -- An individual OptionField models such a field, and we usually -- build a list of options associated to a configuration data type.-data OptionField a = OptionField {- optionName :: Name,- optionDescr :: [OptDescr a] }+data OptionField a = OptionField+ { optionName :: Name+ , optionDescr :: [OptDescr a]+ } -- | An OptionField takes one or more OptDescrs, describing the command line -- interface for the field.-data OptDescr a = ReqArg Description OptFlags ArgPlaceHolder- (ReadE (a->a)) (a -> [String])-- | OptArg Description OptFlags ArgPlaceHolder- (ReadE (a->a)) (a->a) (a -> [Maybe String])-- | ChoiceOpt [(Description, OptFlags, a->a, a -> Bool)]+data OptDescr a+ = ReqArg+ Description+ OptFlags+ ArgPlaceHolder+ (ReadE (a -> a))+ (a -> [String])+ | OptArg+ Description+ OptFlags+ ArgPlaceHolder+ (ReadE (a -> a))+ (String, a -> a)+ (a -> [Maybe String])+ | ChoiceOpt [(Description, OptFlags, a -> a, a -> Bool)]+ | BoolOpt+ Description+ OptFlags {-True-}+ OptFlags {-False-}+ (Bool -> a -> a)+ (a -> Maybe Bool) - | BoolOpt Description OptFlags{-True-} OptFlags{-False-}- (Bool -> a -> a) (a-> Maybe Bool)+fmapOptDescr :: forall a b. (b -> a) -> (a -> (b -> b)) -> OptDescr a -> OptDescr b+fmapOptDescr x u = \case+ ReqArg d o p upd get -> ReqArg d o p (fmap m upd) (get . x)+ OptArg d o p upd (str, g) get -> OptArg d o p (fmap m upd) (str, m g) (get . x)+ ChoiceOpt opts -> ChoiceOpt $ fmap (\(d, o, upd, get) -> (d, o, m upd, get . x)) opts+ BoolOpt d true false upd get -> BoolOpt d true false (\b -> m $ upd b) (get . x)+ where+ m :: (a -> a) -> (b -> b)+ m upd_a b = u (upd_a $ x b) b -- | Short command line option strings-type SFlags = [Char]+type SFlags = [Char]+ -- | Long command line option strings-type LFlags = [String]-type OptFlags = (SFlags,LFlags)-type ArgPlaceHolder = String+type LFlags = [String] +type OptFlags = (SFlags, LFlags)+type ArgPlaceHolder = String -- | Create an option taking a single OptDescr. -- No explicit Name is given for the Option, the name is the first LFlag given.-option :: SFlags -> LFlags -> Description -> get -> set -> MkOptDescr get set a- -> OptionField a-option sf lf@(n:_) d get set arg = OptionField n [arg sf lf d get set]-option _ _ _ _ _ _ = error $ "Distribution.command.option: "- ++ "An OptionField must have at least one LFlag"+--+-- Example: @'option' sf lf d get set@+-- * @sf@: Short option name, for example: @[\'d\']@. No hyphen permitted.+-- * @lf@: Long option name, for example: @["debug"]@. No hyphens permitted.+-- * @d@: Description of the option, shown to the user in help messages.+-- * @get@: Get the current value of the flag.+-- * @set@: Set the value of the flag. Gets the current value of the flag as a+-- parameter.+option+ :: SFlags+ -> LFlags+ -> Description+ -> get+ -> set+ -> MkOptDescr get set a+ -> OptionField a+option sf lf@(n : _) d get set arg = OptionField n [arg sf lf d get set]+option _ _ _ _ _ _ =+ error $+ "Distribution.command.option: "+ ++ "An OptionField must have at least one LFlag" -- | Create an option taking several OptDescrs. -- You will have to give the flags and description individually to the -- OptDescr constructor.-multiOption :: Name -> get -> set- -> [get -> set -> OptDescr a] -- ^MkOptDescr constructors partially- -- applied to flags and description.- -> OptionField a+multiOption+ :: Name+ -> get+ -> set+ -> [get -> set -> OptDescr a]+ -- ^ MkOptDescr constructors partially+ -- applied to flags and description.+ -> OptionField a multiOption n get set args = OptionField n [arg get set | arg <- args] -type MkOptDescr get set a = SFlags -> LFlags -> Description -> get -> set- -> OptDescr a+type MkOptDescr get set a =+ SFlags+ -> LFlags+ -> Description+ -> get+ -> set+ -> OptDescr a -- | Create a string-valued command line interface.-reqArg :: Monoid b => ArgPlaceHolder -> ReadE b -> (b -> [String])- -> MkOptDescr (a -> b) (b -> a -> a) a+-- Usually called in the context of 'option' or 'multiOption'.+--+-- Example: @'reqArg' ad mkflag showflag@+--+-- * @ad@: Placeholder shown to the user, e.g. @"FILES"@ if files are expected+-- parameters.+-- * @mkflag@: How to parse the argument into the option.+-- * @showflag@: If parsing goes wrong, display a useful error message to+-- the user.+reqArg+ :: Monoid b+ => ArgPlaceHolder+ -> ReadE b+ -> (b -> [String])+ -> MkOptDescr (a -> b) (b -> a -> a) a reqArg ad mkflag showflag sf lf d get set =- ReqArg d (sf,lf) ad (fmap (\a b -> set (get b `mappend` a) b) mkflag)- (showflag . get)+ ReqArg+ d+ (sf, lf)+ ad+ (fmap (\a b -> set (get b `mappend` a) b) mkflag)+ (showflag . get) -- | Create a string-valued command line interface with a default value.-optArg :: Monoid b => ArgPlaceHolder -> ReadE b -> b -> (b -> [Maybe String])- -> MkOptDescr (a -> b) (b -> a -> a) a-optArg ad mkflag def showflag sf lf d get set =- OptArg d (sf,lf) ad (fmap (\a b -> set (get b `mappend` a) b) mkflag)- (\b -> set (get b `mappend` def) b)- (showflag . get)+optArg+ :: Monoid b+ => ArgPlaceHolder+ -> ReadE b+ -> (String, b)+ -> (b -> [Maybe String])+ -> MkOptDescr (a -> b) (b -> a -> a) a+optArg ad mkflag (dv, mkDef) showflag sf lf d get set =+ OptArg+ d+ (sf, lf)+ ad+ (fmap (\a b -> set (get b `mappend` a) b) mkflag)+ (dv, \b -> set (get b `mappend` mkDef) b)+ (showflag . get) -- | (String -> a) variant of "reqArg"-reqArg' :: Monoid b => ArgPlaceHolder -> (String -> b) -> (b -> [String])- -> MkOptDescr (a -> b) (b -> a -> a) a+reqArg'+ :: Monoid b+ => ArgPlaceHolder+ -> (String -> b)+ -> (b -> [String])+ -> MkOptDescr (a -> b) (b -> a -> a) a reqArg' ad mkflag showflag =- reqArg ad (succeedReadE mkflag) showflag+ reqArg ad (succeedReadE mkflag) showflag -- | (String -> a) variant of "optArg"-optArg' :: Monoid b => ArgPlaceHolder -> (Maybe String -> b)- -> (b -> [Maybe String])- -> MkOptDescr (a -> b) (b -> a -> a) a+optArg'+ :: Monoid b+ => ArgPlaceHolder+ -> (Maybe String -> b)+ -> (b -> [Maybe String])+ -> MkOptDescr (a -> b) (b -> a -> a) a optArg' ad mkflag showflag =- optArg ad (succeedReadE (mkflag . Just)) def showflag- where def = mkflag Nothing+ optArg ad (succeedReadE (mkflag . Just)) ("", mkflag Nothing) showflag -noArg :: (Eq b) => b -> MkOptDescr (a -> b) (b -> a -> a) a-noArg flag sf lf d = choiceOpt [(flag, (sf,lf), d)] sf lf d+optArgDef'+ :: Monoid b+ => ArgPlaceHolder+ -> (String, Maybe String -> b)+ -> (b -> [Maybe String])+ -> MkOptDescr (a -> b) (b -> a -> a) a+optArgDef' ad (dv, mkflag) showflag =+ optArg ad (succeedReadE (mkflag . Just)) (dv, mkflag Nothing) showflag -boolOpt :: (b -> Maybe Bool) -> (Bool -> b) -> SFlags -> SFlags- -> MkOptDescr (a -> b) (b -> a -> a) a-boolOpt g s sfT sfF _sf _lf@(n:_) d get set =- BoolOpt d (sfT, ["enable-"++n]) (sfF, ["disable-"++n]) (set.s) (g.get)-boolOpt _ _ _ _ _ _ _ _ _ = error- "Distribution.Simple.Setup.boolOpt: unreachable"+noArg :: Eq b => b -> MkOptDescr (a -> b) (b -> a -> a) a+noArg flag sf lf d = choiceOpt [(flag, (sf, lf), d)] sf lf d -boolOpt' :: (b -> Maybe Bool) -> (Bool -> b) -> OptFlags -> OptFlags- -> MkOptDescr (a -> b) (b -> a -> a) a-boolOpt' g s ffT ffF _sf _lf d get set = BoolOpt d ffT ffF (set.s) (g . get)+boolOpt+ :: (b -> Maybe Bool)+ -> (Bool -> b)+ -> SFlags+ -> SFlags+ -> MkOptDescr (a -> b) (b -> a -> a) a+boolOpt g s sfT sfF _sf _lf@(n : _) d get set =+ BoolOpt d (sfT, ["enable-" ++ n]) (sfF, ["disable-" ++ n]) (set . s) (g . get)+boolOpt _ _ _ _ _ _ _ _ _ =+ error+ "Distribution.Simple.Setup.boolOpt: unreachable" +boolOpt'+ :: (b -> Maybe Bool)+ -> (Bool -> b)+ -> OptFlags+ -> OptFlags+ -> MkOptDescr (a -> b) (b -> a -> a) a+boolOpt' g s ffT ffF _sf _lf d get set = BoolOpt d ffT ffF (set . s) (g . get)+ -- | create a Choice option-choiceOpt :: Eq b => [(b,OptFlags,Description)]- -> MkOptDescr (a -> b) (b -> a -> a) a-choiceOpt aa_ff _sf _lf _d get set = ChoiceOpt alts- where alts = [(d,flags, set alt, (==alt) . get) | (alt,flags,d) <- aa_ff]+choiceOpt+ :: Eq b+ => [(b, OptFlags, Description)]+ -> MkOptDescr (a -> b) (b -> a -> a) a+choiceOpt aa_ff _sf _lf _d get set = ChoiceOpt alts+ where+ alts = [(d, flags, set alt, (== alt) . get) | (alt, flags, d) <- aa_ff] -- | create a Choice option out of an enumeration type. -- As long flags, the Show output is used. As short flags, the first character -- which does not conflict with a previous one is used.-choiceOptFromEnum :: (Bounded b, Enum b, Show b, Eq b) =>- MkOptDescr (a -> b) (b -> a -> a) a+choiceOptFromEnum+ :: (Bounded b, Enum b, Show b, Eq b)+ => MkOptDescr (a -> b) (b -> a -> a) a choiceOptFromEnum _sf _lf d get =- choiceOpt [ (x, (sf, [map toLower $ show x]), d')- | (x, sf) <- sflags'- , let d' = d ++ show x]- _sf _lf d get- where sflags' = foldl f [] [firstOne..]- f prev x = let prevflags = concatMap snd prev in- prev ++ take 1 [(x, [toLower sf])- | sf <- show x, isAlpha sf- , toLower sf `notElem` prevflags]- firstOne = minBound `asTypeOf` get undefined+ choiceOpt+ [ (x, (sf, [map toLower $ show x]), d')+ | (x, sf) <- sflags'+ , let d' = d ++ show x+ ]+ _sf+ _lf+ d+ get+ where+ sflags' = foldl f [] [firstOne ..]+ f prev x =+ let prevflags = concatMap snd prev+ in prev+ ++ take+ 1+ [ (x, [toLower sf])+ | sf <- show x+ , isAlpha sf+ , toLower sf `notElem` prevflags+ ]+ firstOne = minBound `asTypeOf` get undefined -commandGetOpts :: ShowOrParseArgs -> CommandUI flags- -> [GetOpt.OptDescr (flags -> flags)]+commandGetOpts+ :: ShowOrParseArgs+ -> CommandUI flags+ -> [GetOpt.OptDescr (flags -> flags)] commandGetOpts showOrParse command =- concatMap viewAsGetOpt (commandOptions command showOrParse)+ concatMap viewAsGetOpt (commandOptions command showOrParse) -viewAsGetOpt :: OptionField a -> [GetOpt.OptDescr (a->a)]+viewAsGetOpt :: OptionField a -> [GetOpt.OptDescr (a -> a)] viewAsGetOpt (OptionField _n aa) = concatMap optDescrToGetOpt aa where- optDescrToGetOpt (ReqArg d (cs,ss) arg_desc set _) =- [GetOpt.Option cs ss (GetOpt.ReqArg set' arg_desc) d]- where set' = readEOrFail set- optDescrToGetOpt (OptArg d (cs,ss) arg_desc set def _) =- [GetOpt.Option cs ss (GetOpt.OptArg set' arg_desc) d]- where set' Nothing = def- set' (Just txt) = readEOrFail set txt+ optDescrToGetOpt (ReqArg d (cs, ss) arg_desc set _) =+ [GetOpt.Option cs ss (GetOpt.ReqArg (runReadE set) arg_desc) d]+ optDescrToGetOpt (OptArg d (cs, ss) arg_desc set (dv, def) _) =+ [GetOpt.Option cs ss (GetOpt.OptArg dv set' arg_desc) d]+ where+ set' Nothing = Right def+ set' (Just txt) = runReadE set txt optDescrToGetOpt (ChoiceOpt alts) =- [GetOpt.Option sf lf (GetOpt.NoArg set) d | (d,(sf,lf),set,_) <- alts ]- optDescrToGetOpt (BoolOpt d (sfT, lfT) ([], []) set _) =- [ GetOpt.Option sfT lfT (GetOpt.NoArg (set True)) d ]- optDescrToGetOpt (BoolOpt d ([], []) (sfF, lfF) set _) =- [ GetOpt.Option sfF lfF (GetOpt.NoArg (set False)) d ]- optDescrToGetOpt (BoolOpt d (sfT,lfT) (sfF, lfF) set _) =- [ GetOpt.Option sfT lfT (GetOpt.NoArg (set True)) ("Enable " ++ d)- , GetOpt.Option sfF lfF (GetOpt.NoArg (set False)) ("Disable " ++ d) ]+ [GetOpt.Option sf lf (GetOpt.NoArg set) d | (d, (sf, lf), set, _) <- alts]+ optDescrToGetOpt (BoolOpt d (sfT, lfT) ([], []) set _) =+ [GetOpt.Option sfT lfT (GetOpt.NoArg (set True)) d]+ optDescrToGetOpt (BoolOpt d ([], []) (sfF, lfF) set _) =+ [GetOpt.Option sfF lfF (GetOpt.NoArg (set False)) d]+ optDescrToGetOpt (BoolOpt d (sfT, lfT) (sfF, lfF) set _) =+ [ GetOpt.Option sfT lfT (GetOpt.NoArg (set True)) ("Enable " ++ d)+ , GetOpt.Option sfF lfF (GetOpt.NoArg (set False)) ("Disable " ++ d)+ ] getCurrentChoice :: OptDescr a -> a -> [String] getCurrentChoice (ChoiceOpt alts) a =- [ lf | (_,(_sf,lf:_), _, currentChoice) <- alts, currentChoice a]-+ [lf | (_, (_sf, lf : _), _, currentChoice) <- alts, currentChoice a] getCurrentChoice _ _ = error "Command.getChoice: expected a Choice OptDescr" - liftOption :: (b -> a) -> (a -> (b -> b)) -> OptionField a -> OptionField b liftOption get' set' opt =- opt { optionDescr = liftOptDescr get' set' `map` optionDescr opt}+ opt{optionDescr = liftOptDescr get' set' `map` optionDescr opt} -- | @since 3.4.0.0 liftOptionL :: ALens' b a -> OptionField a -> OptionField b liftOptionL l = liftOption (^# l) (l #~) - liftOptDescr :: (b -> a) -> (a -> (b -> b)) -> OptDescr a -> OptDescr b liftOptDescr get' set' (ChoiceOpt opts) =- ChoiceOpt [ (d, ff, liftSet get' set' set , (get . get'))- | (d, ff, set, get) <- opts]--liftOptDescr get' set' (OptArg d ff ad set def get) =- OptArg d ff ad (liftSet get' set' `fmap` set)- (liftSet get' set' def) (get . get')-+ ChoiceOpt+ [ (d, ff, liftSet get' set' set, (get . get'))+ | (d, ff, set, get) <- opts+ ]+liftOptDescr get' set' (OptArg d ff ad set (dv, mkDef) get) =+ OptArg+ d+ ff+ ad+ (liftSet get' set' `fmap` set)+ (dv, liftSet get' set' mkDef)+ (get . get') liftOptDescr get' set' (ReqArg d ff ad set get) =- ReqArg d ff ad (liftSet get' set' `fmap` set) (get . get')-+ ReqArg d ff ad (liftSet get' set' `fmap` set) (get . get') liftOptDescr get' set' (BoolOpt d ffT ffF set get) =- BoolOpt d ffT ffF (liftSet get' set' . set) (get . get')+ BoolOpt d ffT ffF (liftSet get' set' . set) (get . get') liftSet :: (b -> a) -> (a -> (b -> b)) -> (a -> a) -> b -> b liftSet get' set' set x = set' (set $ get' x) x -- | Show flags in the standard long option command line format commandShowOptions :: CommandUI flags -> flags -> [String]-commandShowOptions command v = concat- [ showOptDescr v od | o <- commandOptions command ParseArgs- , od <- optionDescr o]+commandShowOptions command v =+ concat+ [ showOptDescr v od | o <- commandOptions command ParseArgs, od <- optionDescr o+ ] where- maybePrefix [] = []- maybePrefix (lOpt:_) = ["--" ++ lOpt]+ maybePrefix [] = []+ maybePrefix (lOpt : _) = ["--" ++ lOpt] showOptDescr :: a -> OptDescr a -> [String]- showOptDescr x (BoolOpt _ (_,lfTs) (_,lfFs) _ enabled)- = case enabled x of- Nothing -> []- Just True -> maybePrefix lfTs- Just False -> maybePrefix lfFs- showOptDescr x c@ChoiceOpt{}- = ["--" ++ val | val <- getCurrentChoice c x]- showOptDescr x (ReqArg _ (_ssff,lf:_) _ _ showflag)- = [ "--"++lf++"="++flag- | flag <- showflag x ]- showOptDescr x (OptArg _ (_ssff,lf:_) _ _ _ showflag)- = [ case flag of- Just s -> "--"++lf++"="++s- Nothing -> "--"++lf- | flag <- showflag x ]- showOptDescr _ _- = error "Distribution.Simple.Command.showOptDescr: unreachable"-+ showOptDescr x (BoolOpt _ (_, lfTs) (_, lfFs) _ enabled) =+ case enabled x of+ Nothing -> []+ Just True -> maybePrefix lfTs+ Just False -> maybePrefix lfFs+ showOptDescr x c@ChoiceOpt{} =+ ["--" ++ val | val <- getCurrentChoice c x]+ showOptDescr x (ReqArg _ (_ssff, lf : _) _ _ showflag) =+ [ "--" ++ lf ++ "=" ++ flag+ | flag <- showflag x+ ]+ showOptDescr x (OptArg _ (_ssff, lf : _) _ _ _ showflag) =+ [ case flag of+ Just s -> "--" ++ lf ++ "=" ++ s+ Nothing -> "--" ++ lf+ | flag <- showflag x+ ]+ showOptDescr _ _ =+ error "Distribution.Simple.Command.showOptDescr: unreachable" commandListOptions :: CommandUI flags -> [String] commandListOptions command = concatMap listOption $ addCommonFlags ShowArgs $ -- This is a slight hack, we don't want- -- "--list-options" showing up in the- -- list options output, so use ShowArgs+ -- "--list-options" showing up in the+ -- list options output, so use ShowArgs commandGetOpts ShowArgs command where listOption (GetOpt.Option shortNames longNames _ _) =- [ "-" ++ [name] | name <- shortNames ]- ++ [ "--" ++ name | name <- longNames ]+ ["-" ++ [name] | name <- shortNames]+ ++ ["--" ++ name | name <- longNames] -- | The help text for this command with descriptions of all the options. commandHelp :: CommandUI flags -> String -> String commandHelp command pname =- commandSynopsis command- ++ "\n\n"- ++ commandUsage command pname- ++ ( case commandDescription command of- Nothing -> ""- Just desc -> '\n': desc pname)- ++ "\n"- ++ ( if cname == ""- then "Global flags:"- else "Flags for " ++ cname ++ ":" )- ++ ( GetOpt.usageInfo ""- . addCommonFlags ShowArgs- $ commandGetOpts ShowArgs command )- ++ ( case commandNotes command of- Nothing -> ""- Just notes -> '\n': notes pname)- where cname = commandName command+ commandSynopsis command+ ++ "\n\n"+ ++ commandUsage command pname+ ++ ( case commandDescription command of+ Nothing -> ""+ Just desc -> '\n' : desc pname+ )+ ++ "\n"+ ++ ( if cname == ""+ then "Global flags:"+ else "Flags for " ++ cname ++ ":"+ )+ ++ ( GetOpt.usageInfo ""+ . addCommonFlags ShowArgs+ $ commandGetOpts ShowArgs command+ )+ ++ ( case commandNotes command of+ Nothing -> ""+ Just notes -> '\n' : notes pname+ )+ where+ cname = commandName command -- | Default "usage" documentation text for commands. usageDefault :: String -> String -> String usageDefault name pname =- "Usage: " ++ pname ++ " " ++ name ++ " [FLAGS]\n\n"- ++ "Flags for " ++ name ++ ":"+ "Usage: "+ ++ pname+ ++ " "+ ++ name+ ++ " [FLAGS]\n\n"+ ++ "Flags for "+ ++ name+ ++ ":" -- | Create "usage" documentation from a list of parameter -- configurations. usageAlternatives :: String -> [String] -> String -> String-usageAlternatives name strs pname = unlines- [ start ++ pname ++ " " ++ name ++ " " ++ s- | let starts = "Usage: " : repeat " or: "- , (start, s) <- zip starts strs- ]+usageAlternatives name strs pname =+ unlines+ [ start ++ pname ++ " " ++ name ++ " " ++ s+ | let starts = "Usage: " : repeat " or: "+ , (start, s) <- zip starts strs+ ] -- | Make a Command from standard 'GetOpt' options.-mkCommandUI :: String -- ^ name- -> String -- ^ synopsis- -> [String] -- ^ usage alternatives- -> flags -- ^ initial\/empty flags- -> (ShowOrParseArgs -> [OptionField flags]) -- ^ options- -> CommandUI flags-mkCommandUI name synopsis usages flags options = CommandUI- { commandName = name- , commandSynopsis = synopsis- , commandDescription = Nothing- , commandNotes = Nothing- , commandUsage = usageAlternatives name usages- , commandDefaultFlags = flags- , commandOptions = options- }+mkCommandUI+ :: String+ -- ^ name+ -> String+ -- ^ synopsis+ -> [String]+ -- ^ usage alternatives+ -> flags+ -- ^ initial\/empty flags+ -> (ShowOrParseArgs -> [OptionField flags])+ -- ^ options+ -> CommandUI flags+mkCommandUI name synopsis usages flags options =+ CommandUI+ { commandName = name+ , commandSynopsis = synopsis+ , commandDescription = Nothing+ , commandNotes = Nothing+ , commandUsage = usageAlternatives name usages+ , commandDefaultFlags = flags+ , commandOptions = options+ } -- | Common flags that apply to every command data CommonFlag = HelpFlag | ListOptionsFlag commonFlags :: ShowOrParseArgs -> [GetOpt.OptDescr CommonFlag] commonFlags showOrParseArgs = case showOrParseArgs of- ShowArgs -> [help]+ ShowArgs -> [help] ParseArgs -> [help, list]- where- help = GetOpt.Option helpShortFlags ["help"] (GetOpt.NoArg HelpFlag)- "Show this help text"+ where+ help =+ GetOpt.Option+ helpShortFlags+ ["help"]+ (GetOpt.NoArg HelpFlag)+ "Show this help text" helpShortFlags = case showOrParseArgs of- ShowArgs -> ['h']+ ShowArgs -> ['h'] ParseArgs -> ['h', '?']- list = GetOpt.Option [] ["list-options"] (GetOpt.NoArg ListOptionsFlag)- "Print a list of command line flags"+ list =+ GetOpt.Option+ []+ ["list-options"]+ (GetOpt.NoArg ListOptionsFlag)+ "Print a list of command line flags" -addCommonFlags :: ShowOrParseArgs- -> [GetOpt.OptDescr a]- -> [GetOpt.OptDescr (Either CommonFlag a)]+addCommonFlags+ :: ShowOrParseArgs+ -> [GetOpt.OptDescr a]+ -> [GetOpt.OptDescr (Either CommonFlag a)] addCommonFlags showOrParseArgs options =- map (fmapOptDesc Left) (commonFlags showOrParseArgs)- ++ map (fmapOptDesc Right) options- where fmapOptDesc f (GetOpt.Option s l d m) =- GetOpt.Option s l (fmapArgDesc f d) m- fmapArgDesc f (GetOpt.NoArg a) = GetOpt.NoArg (f a)- fmapArgDesc f (GetOpt.ReqArg s d) = GetOpt.ReqArg (f . s) d- fmapArgDesc f (GetOpt.OptArg s d) = GetOpt.OptArg (f . s) d+ map (fmap Left) (commonFlags showOrParseArgs)+ ++ map (fmap Right) options -- | Parse a bunch of command line arguments----commandParseArgs :: CommandUI flags- -> Bool -- ^ Is the command a global or subcommand?- -> [String]- -> CommandParse (flags -> flags, [String])+commandParseArgs+ :: CommandUI flags+ -> Bool+ -- ^ Is the command a global or subcommand?+ -> [String]+ -> CommandParse (flags -> flags, [String]) commandParseArgs command global args =- let options = addCommonFlags ParseArgs- $ commandGetOpts ParseArgs command- order | global = GetOpt.RequireOrder- | otherwise = GetOpt.Permute- in case GetOpt.getOpt' order options args of- (flags, _, _, _)- | any listFlag flags -> CommandList (commandListOptions command)- | any helpFlag flags -> CommandHelp (commandHelp command)- where listFlag (Left ListOptionsFlag) = True; listFlag _ = False- helpFlag (Left HelpFlag) = True; helpFlag _ = False- (flags, opts, opts', [])- | global || null opts' -> CommandReadyToGo (accum flags, mix opts opts')- | otherwise -> CommandErrors (unrecognised opts')- (_, _, _, errs) -> CommandErrors errs-- where -- Note: It is crucial to use reverse function composition here or to- -- reverse the flags here as we want to process the flags left to right- -- but data flow in function composition is right to left.- accum flags = foldr (flip (.)) id [ f | Right f <- flags ]- unrecognised opts = [ "unrecognized "- ++ "'" ++ (commandName command) ++ "'"- ++ " option `" ++ opt ++ "'\n"- | opt <- opts ]- -- For unrecognised global flags we put them in the position just after- -- the command, if there is one. This gives us a chance to parse them- -- as sub-command rather than global flags.- mix [] ys = ys- mix (x:xs) ys = x:ys++xs+ let options =+ addCommonFlags ParseArgs $+ commandGetOpts ParseArgs command+ order+ | global = GetOpt.RequireOrder+ | otherwise = GetOpt.Permute+ in case GetOpt.getOpt' order options args of+ (flags, _, _, _)+ | any listFlag flags -> CommandList (commandListOptions command)+ | any helpFlag flags -> CommandHelp (commandHelp command)+ where+ listFlag (Left ListOptionsFlag) = True; listFlag _ = False+ helpFlag (Left HelpFlag) = True; helpFlag _ = False+ (flags, opts, opts', [])+ | global || null opts' -> CommandReadyToGo (accum flags, mix opts opts')+ | otherwise -> CommandErrors (unrecognised opts')+ (_, _, _, errs) -> CommandErrors errs+ where+ -- Note: It is crucial to use reverse function composition here or to+ -- reverse the flags here as we want to process the flags left to right+ -- but data flow in function composition is right to left.+ accum flags = foldr (flip (.)) id [f | Right f <- flags]+ unrecognised opts =+ [ "unrecognized "+ ++ "'"+ ++ (commandName command)+ ++ "'"+ ++ " option `"+ ++ opt+ ++ "'\n"+ | opt <- opts+ ]+ -- For unrecognised global flags we put them in the position just after+ -- the command, if there is one. This gives us a chance to parse them+ -- as sub-command rather than global flags.+ mix [] ys = ys+ mix (x : xs) ys = x : ys ++ xs -data CommandParse flags = CommandHelp (String -> String)- | CommandList [String]- | CommandErrors [String]- | CommandReadyToGo flags+data CommandParse flags+ = CommandHelp (String -> String)+ | CommandList [String]+ | CommandErrors [String]+ | CommandReadyToGo flags instance Functor CommandParse where- fmap _ (CommandHelp help) = CommandHelp help- fmap _ (CommandList opts) = CommandList opts- fmap _ (CommandErrors errs) = CommandErrors errs+ fmap _ (CommandHelp help) = CommandHelp help+ fmap _ (CommandList opts) = CommandList opts+ fmap _ (CommandErrors errs) = CommandErrors errs fmap f (CommandReadyToGo flags) = CommandReadyToGo (f flags) - data CommandType = NormalCommand | HiddenCommand-data Command action =- Command String String ([String] -> CommandParse action) CommandType+data Command action+ = Command String String ([String] -> CommandParse action) CommandType -- | Mark command as hidden. Hidden commands don't show up in the 'progname -- help' or 'progname --help' output. hiddenCommand :: Command action -> Command action-hiddenCommand (Command name synopsys f _cmdType) =- Command name synopsys f HiddenCommand+hiddenCommand (Command name synopsis f _cmdType) =+ Command name synopsis f HiddenCommand -commandAddAction :: CommandUI flags- -> (flags -> [String] -> action)- -> Command action+commandAddAction+ :: CommandUI flags+ -> (flags -> [String] -> action)+ -> Command action commandAddAction command action =- Command (commandName command)- (commandSynopsis command)- (fmap (uncurry applyDefaultArgs) . commandParseArgs command False)- NormalCommand+ Command+ (commandName command)+ (commandSynopsis command)+ (fmap (uncurry applyDefaultArgs) . commandParseArgs command False)+ NormalCommand+ where+ applyDefaultArgs mkflags args =+ let flags = mkflags (commandDefaultFlags command)+ in action flags args - where applyDefaultArgs mkflags args =- let flags = mkflags (commandDefaultFlags command)- in action flags args+-- Print suggested command if edit distance is < 5+badCommand :: [Command action] -> String -> CommandParse a+badCommand commands' cname =+ case eDists of+ [] -> CommandErrors [unErr]+ (s : _) ->+ CommandErrors+ [ unErr+ , "Maybe you meant `" ++ s ++ "`?\n"+ ]+ where+ eDists =+ map fst . List.sortBy (comparing snd) $+ [ (cname', dist)+ | -- Note that this is not commandNames, so close suggestions will show+ -- hidden commands+ (Command cname' _ _ _) <- commands'+ , let dist = editDistance cname' cname+ , dist < 5+ ]+ unErr = "unrecognised command: " ++ cname ++ " (try --help)" -commandsRun :: CommandUI a- -> [Command action]- -> [String]- -> CommandParse (a, CommandParse action)+commandsRun+ :: CommandUI a+ -> [Command action]+ -> [String]+ -> IO (CommandParse (a, CommandParse action)) commandsRun globalCommand commands args =- case commandParseArgs globalCommand True args of- CommandHelp help -> CommandHelp help- CommandList opts -> CommandList (opts ++ commandNames)- CommandErrors errs -> CommandErrors errs- CommandReadyToGo (mkflags, args') -> case args' of- ("help":cmdArgs) -> handleHelpCommand cmdArgs- (name:cmdArgs) -> case lookupCommand name of- [Command _ _ action _]- -> CommandReadyToGo (flags, action cmdArgs)- _ -> CommandReadyToGo (flags, badCommand name)- [] -> CommandReadyToGo (flags, noCommand)- where flags = mkflags (commandDefaultFlags globalCommand)+ commandsRunWithFallback globalCommand commands defaultCommandFallback args - where- lookupCommand cname = [ cmd | cmd@(Command cname' _ _ _) <- commands'- , cname' == cname ]- noCommand = CommandErrors ["no command given (try --help)\n"]+defaultCommandFallback+ :: [Command action]+ -> String+ -> [String]+ -> IO (CommandParse action)+defaultCommandFallback commands' name _cmdArgs = pure $ badCommand commands' name - -- Print suggested command if edit distance is < 5- badCommand :: String -> CommandParse a- badCommand cname =- case eDists of- [] -> CommandErrors [unErr]- (s:_) -> CommandErrors [ unErr- , "Maybe you meant `" ++ s ++ "`?\n"]+commandsRunWithFallback+ :: CommandUI a+ -> [Command action]+ -> ([Command action] -> String -> [String] -> IO (CommandParse action))+ -> [String]+ -> IO (CommandParse (a, CommandParse action))+commandsRunWithFallback globalCommand commands defaultCommand args =+ case commandParseArgs globalCommand True args of+ CommandHelp help -> pure $ CommandHelp help+ CommandList opts -> pure $ CommandList (opts ++ commandNames)+ CommandErrors errs -> pure $ CommandErrors errs+ CommandReadyToGo (mkflags, args') -> case args' of+ ("help" : cmdArgs) -> handleHelpCommand flags cmdArgs+ (name : cmdArgs) -> case lookupCommand name of+ [Command _ _ action _] ->+ pure $ CommandReadyToGo (flags, action cmdArgs)+ _ -> do+ final_cmd <- defaultCommand commands' name cmdArgs+ return $ CommandReadyToGo (flags, final_cmd)+ [] -> pure $ CommandReadyToGo (flags, noCommand) where- eDists = map fst . List.sortBy (comparing snd) $- [ (cname', dist)- | (Command cname' _ _ _) <- commands'- , let dist = editDistance cname' cname- , dist < 5 ]- unErr = "unrecognised command: " ++ cname ++ " (try --help)"+ flags = mkflags (commandDefaultFlags globalCommand)+ where+ lookupCommand cname =+ [ cmd | cmd@(Command cname' _ _ _) <- commands', cname' == cname+ ] - commands' = commands ++ [commandAddAction helpCommandUI undefined]- commandNames = [ name | (Command name _ _ NormalCommand) <- commands' ]+ noCommand = CommandErrors ["no command given (try --help)\n"] + commands' = commands ++ [commandAddAction helpCommandUI undefined]+ commandNames = [name | (Command name _ _ NormalCommand) <- commands']+ -- A bit of a hack: support "prog help" as a synonym of "prog --help" -- furthermore, support "prog help command" as "prog command --help"- handleHelpCommand cmdArgs =+ handleHelpCommand flags cmdArgs = case commandParseArgs helpCommandUI True cmdArgs of- CommandHelp help -> CommandHelp help- CommandList list -> CommandList (list ++ commandNames)- CommandErrors _ -> CommandHelp globalHelp- CommandReadyToGo (_,[]) -> CommandHelp globalHelp- CommandReadyToGo (_,(name:cmdArgs')) ->+ CommandHelp help -> pure $ CommandHelp help+ CommandList list -> pure $ CommandList (list ++ commandNames)+ CommandErrors _ -> pure $ CommandHelp globalHelp+ CommandReadyToGo (_, []) -> pure $ CommandHelp globalHelp+ CommandReadyToGo (_, (name : cmdArgs')) -> case lookupCommand name of [Command _ _ action _] ->- case action ("--help":cmdArgs') of- CommandHelp help -> CommandHelp help- CommandList _ -> CommandList []- _ -> CommandHelp globalHelp- _ -> badCommand name-- where globalHelp = commandHelp globalCommand+ case action ("--help" : cmdArgs') of+ CommandHelp help -> pure $ CommandHelp help+ CommandList _ -> pure $ CommandList []+ _ -> pure $ CommandHelp globalHelp+ _ -> do+ fall_back <- defaultCommand commands' name ("--help" : cmdArgs')+ return $ CommandReadyToGo (flags, fall_back)+ where+ globalHelp = commandHelp globalCommand -- Levenshtein distance, from https://wiki.haskell.org/Edit_distance -- (Author: JeanPhilippeBernardy, Simple Permissive Licence) editDistance :: Eq a => [a] -> [a] -> Int-editDistance xs ys = table Array.! (m,n)+editDistance xs ys = table Array.! (m, n) where- (m,n) = (length xs, length ys)- x = Array.array (1,m) (zip [1..] xs)- y = Array.array (1,n) (zip [1..] ys)+ (m, n) = (length xs, length ys)+ x = Array.array (1, m) (zip [1 ..] xs)+ y = Array.array (1, n) (zip [1 ..] ys) - table :: Array.Array (Int,Int) Int+ table :: Array.Array (Int, Int) Int table = Array.array bnds [(ij, dist ij) | ij <- Array.range bnds]- bnds = ((0,0),(m,n))+ bnds = ((0, 0), (m, n)) - dist (0,j) = j- dist (i,0) = i- dist (i,j) = minimum- [ table Array.! (i-1,j) + 1- , table Array.! (i,j-1) + 1- , if x Array.! i == y Array.! j- then table Array.! (i-1,j-1)- else 1 + table Array.! (i-1,j-1)- ]+ dist (0, j) = j+ dist (i, 0) = i+ dist (i, j) =+ minimum+ [ table Array.! (i - 1, j) + 1+ , table Array.! (i, j - 1) + 1+ , if x Array.! i == y Array.! j+ then table Array.! (i - 1, j - 1)+ else 1 + table Array.! (i - 1, j - 1)+ ] -- | Utility function, many commands do not accept additional flags. This -- action fails with a helpful error message if the user supplies any extra.--- noExtraFlags :: [String] -> IO () noExtraFlags [] = return () noExtraFlags extraFlags = dieNoVerbosity $ "Unrecognised flags: " ++ intercalate ", " extraFlags---TODO: eliminate this function and turn it into a variant on commandAddAction++-- TODO: eliminate this function and turn it into a variant on commandAddAction -- instead like commandAddActionNoArgs that doesn't supply the [String] -- | Helper function for creating globalCommand description getNormalCommandDescriptions :: [Command action] -> [(String, String)] getNormalCommandDescriptions cmds = [ (name, description)- | Command name description _ NormalCommand <- cmds ]+ | Command name description _ NormalCommand <- cmds+ ] helpCommandUI :: CommandUI () helpCommandUI =- (mkCommandUI- "help"- "Help about commands."- ["[FLAGS]", "COMMAND [FLAGS]"]- ()- (const []))- {- commandNotes = Just $ \pname ->- "Examples:\n"- ++ " " ++ pname ++ " help help\n"- ++ " Oh, appararently you already know this.\n"- }+ ( mkCommandUI+ "help"+ "Help about commands."+ ["[FLAGS]", "COMMAND [FLAGS]"]+ ()+ (const [])+ )+ { commandNotes = Just $ \pname ->+ "Examples:\n"+ ++ " "+ ++ pname+ ++ " help help\n"+ ++ " Oh, apparently you already know this.\n"+ } -- | wraps a @CommandUI@ together with a function that turns it into a @Command@. -- By hiding the type of flags for the UI allows construction of a list of all UIs at the
src/Distribution/Simple/Compiler.hs view
@@ -1,7 +1,9 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveTraversable #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Compiler -- Copyright : Isaac Jones 2003-2004@@ -20,87 +22,108 @@ -- only know about a single global package collection but GHC has a global and -- per-user one and it lets you create arbitrary other package databases. We do -- not yet fully support this latter feature.--module Distribution.Simple.Compiler (- -- * Haskell implementations- module Distribution.Compiler,- Compiler(..),- showCompilerId, showCompilerIdWithAbi,- compilerFlavor, compilerVersion,- compilerCompatFlavor,- compilerCompatVersion,- compilerInfo,+module Distribution.Simple.Compiler+ ( -- * Haskell implementations+ module Distribution.Compiler+ , Compiler (..)+ , showCompilerId+ , showCompilerIdWithAbi+ , compilerFlavor+ , compilerVersion+ , compilerCompatFlavor+ , compilerCompatVersion+ , compilerInfo - -- * Support for package databases- PackageDB(..),- PackageDBStack,- registrationPackageDB,- absolutePackageDBPaths,- absolutePackageDBPath,+ -- * Support for package databases+ , PackageDB+ , PackageDBStack+ , PackageDBCWD+ , PackageDBStackCWD+ , PackageDBX (..)+ , PackageDBStackX+ , PackageDBS+ , PackageDBStackS+ , registrationPackageDB+ , absolutePackageDBPaths+ , absolutePackageDBPath+ , interpretPackageDB+ , interpretPackageDBStack+ , coercePackageDB+ , coercePackageDBStack - -- * Support for optimisation levels- OptimisationLevel(..),- flagToOptimisationLevel,+ -- * Support for optimisation levels+ , OptimisationLevel (..)+ , flagToOptimisationLevel - -- * Support for debug info levels- DebugInfoLevel(..),- flagToDebugInfoLevel,+ -- * Support for debug info levels+ , DebugInfoLevel (..)+ , flagToDebugInfoLevel - -- * Support for language extensions- CompilerFlag,- languageToFlags,- unsupportedLanguages,- extensionsToFlags,- unsupportedExtensions,- parmakeSupported,- reexportedModulesSupported,- renamingPackageFlagsSupported,- unifiedIPIDRequired,- packageKeySupported,- unitIdSupported,- coverageSupported,- profilingSupported,- backpackSupported,- arResponseFilesSupported,- libraryDynDirSupported,- libraryVisibilitySupported,+ -- * Support for language extensions+ , CompilerFlag+ , languageToFlags+ , unsupportedLanguages+ , extensionsToFlags+ , unsupportedExtensions+ , parmakeSupported+ , reexportedModulesSupported+ , renamingPackageFlagsSupported+ , unifiedIPIDRequired+ , packageKeySupported+ , unitIdSupported+ , coverageSupported+ , profilingSupported+ , profilingDynamicSupported+ , profilingDynamicSupportedOrUnknown+ , profilingVanillaSupported+ , profilingVanillaSupportedOrUnknown+ , dynamicSupported+ , backpackSupported+ , arResponseFilesSupported+ , arDashLSupported+ , libraryDynDirSupported+ , libraryVisibilitySupported+ , jsemSupported+ , reexportedAsSupported - -- * Support for profiling detail levels- ProfDetailLevel(..),- knownProfDetailLevels,- flagToProfDetailLevel,- showProfDetailLevel,+ -- * Support for profiling detail levels+ , ProfDetailLevel (..)+ , knownProfDetailLevels+ , flagToProfDetailLevel+ , showProfDetailLevel ) where -import Prelude () import Distribution.Compat.Prelude import Distribution.Pretty+import Prelude () import Distribution.Compiler+import Distribution.Simple.Utils+import Distribution.Utils.Path import Distribution.Version+ import Language.Haskell.Extension-import Distribution.Simple.Utils import qualified Data.Map as Map (lookup) import System.Directory (canonicalizePath) -data Compiler = Compiler {- compilerId :: CompilerId,- -- ^ Compiler flavour and version.- compilerAbiTag :: AbiTag,- -- ^ Tag for distinguishing incompatible ABI's on the same- -- architecture/os.- compilerCompat :: [CompilerId],- -- ^ Other implementations that this compiler claims to be- -- compatible with.- compilerLanguages :: [(Language, CompilerFlag)],- -- ^ Supported language standards.- compilerExtensions :: [(Extension, Maybe CompilerFlag)],- -- ^ Supported extensions.- compilerProperties :: Map String String- -- ^ A key-value map for properties not covered by the above fields.- }- deriving (Eq, Generic, Typeable, Show, Read)+data Compiler = Compiler+ { compilerId :: CompilerId+ -- ^ Compiler flavour and version.+ , compilerAbiTag :: AbiTag+ -- ^ Tag for distinguishing incompatible ABI's on the same+ -- architecture/os.+ , compilerCompat :: [CompilerId]+ -- ^ Other implementations that this compiler claims to be+ -- compatible with.+ , compilerLanguages :: [(Language, CompilerFlag)]+ -- ^ Supported language standards.+ , compilerExtensions :: [(Extension, Maybe CompilerFlag)]+ -- ^ Supported extensions.+ , compilerProperties :: Map String String+ -- ^ A key-value map for properties not covered by the above fields.+ }+ deriving (Eq, Generic, Show, Read) instance Binary Compiler instance Structured Compiler@@ -110,30 +133,27 @@ showCompilerIdWithAbi :: Compiler -> String showCompilerIdWithAbi comp =- prettyShow (compilerId comp) ++- case compilerAbiTag comp of- NoAbiTag -> []- AbiTag xs -> '-':xs+ prettyShow (compilerId comp)+ ++ case compilerAbiTag comp of+ NoAbiTag -> []+ AbiTag xs -> '-' : xs -compilerFlavor :: Compiler -> CompilerFlavor+compilerFlavor :: Compiler -> CompilerFlavor compilerFlavor = (\(CompilerId f _) -> f) . compilerId compilerVersion :: Compiler -> Version compilerVersion = (\(CompilerId _ v) -> v) . compilerId - -- | Is this compiler compatible with the compiler flavour we're interested in? -- -- For example this checks if the compiler is actually GHC or is another -- compiler that claims to be compatible with some version of GHC, e.g. GHCJS. -- -- > if compilerCompatFlavor GHC compiler then ... else ...--- compilerCompatFlavor :: CompilerFlavor -> Compiler -> Bool compilerCompatFlavor flavor comp =- flavor == compilerFlavor comp- || flavor `elem` [ flavor' | CompilerId flavor' _ <- compilerCompat comp ]-+ flavor == compilerFlavor comp+ || flavor `elem` [flavor' | CompilerId flavor' _ <- compilerCompat comp] -- | Is this compiler compatible with the compiler flavour we're interested in, -- and if so what version does it claim to be compatible with.@@ -144,38 +164,44 @@ -- > case compilerCompatVersion GHC compiler of -- > Just (Version (7:_)) -> ... -- > _ -> ...--- compilerCompatVersion :: CompilerFlavor -> Compiler -> Maybe Version compilerCompatVersion flavor comp | compilerFlavor comp == flavor = Just (compilerVersion comp)- | otherwise =- listToMaybe [ v | CompilerId fl v <- compilerCompat comp, fl == flavor ]+ | otherwise =+ listToMaybe [v | CompilerId fl v <- compilerCompat comp, fl == flavor] compilerInfo :: Compiler -> CompilerInfo-compilerInfo c = CompilerInfo (compilerId c)- (compilerAbiTag c)- (Just . compilerCompat $ c)- (Just . map fst . compilerLanguages $ c)- (Just . map fst . compilerExtensions $ c)+compilerInfo c =+ CompilerInfo+ (compilerId c)+ (compilerAbiTag c)+ (Just . compilerCompat $ c)+ (Just . map fst . compilerLanguages $ c)+ (Just . map fst . compilerExtensions $ c) -- ------------------------------------------------------------+ -- * Package databases+ -- ------------------------------------------------------------ --- |Some compilers have a notion of a database of available packages.--- For some there is just one global db of packages, other compilers--- support a per-user or an arbitrary db specified at some location in--- the file system. This can be used to build isloated environments of--- packages, for example to build a collection of related packages--- without installing them globally.+-- | Some compilers have a notion of a database of available packages.+-- For some there is just one global db of packages, other compilers+-- support a per-user or an arbitrary db specified at some location in+-- the file system. This can be used to build isolated environments of+-- packages, for example to build a collection of related packages+-- without installing them globally. ---data PackageDB = GlobalPackageDB- | UserPackageDB- | SpecificPackageDB FilePath- deriving (Eq, Generic, Ord, Show, Read, Typeable)+-- Abstracted over+data PackageDBX fp+ = GlobalPackageDB+ | UserPackageDB+ | -- | NB: the path might be relative or it might be absolute+ SpecificPackageDB fp+ deriving (Eq, Generic, Ord, Show, Read, Functor, Foldable, Traversable) -instance Binary PackageDB-instance Structured PackageDB+instance Binary fp => Binary (PackageDBX fp)+instance Structured fp => Structured (PackageDBX fp) -- | We typically get packages from several databases, and stack them -- together. This type lets us be explicit about that stacking. For example@@ -192,114 +218,165 @@ -- we can use several custom package dbs and the user package db together. -- -- When it comes to writing, the top most (last) package is used.----type PackageDBStack = [PackageDB]+type PackageDBStackX from = [PackageDBX from] +type PackageDB = PackageDBX (SymbolicPath Pkg (Dir PkgDB))+type PackageDBStack = PackageDBStackX (SymbolicPath Pkg (Dir PkgDB))++type PackageDBS from = PackageDBX (SymbolicPath from (Dir PkgDB))+type PackageDBStackS from = PackageDBStackX (SymbolicPath from (Dir PkgDB))++type PackageDBCWD = PackageDBX FilePath+type PackageDBStackCWD = PackageDBStackX FilePath+ -- | Return the package that we should register into. This is the package db at -- the top of the stack.----registrationPackageDB :: PackageDBStack -> PackageDB-registrationPackageDB dbs = case safeLast dbs of+registrationPackageDB :: PackageDBStackX from -> PackageDBX from+registrationPackageDB dbs = case safeLast dbs of Nothing -> error "internal error: empty package db set"- Just p -> p+ Just p -> p -- | Make package paths absolute+absolutePackageDBPaths+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -> PackageDBStack+ -> IO PackageDBStack+absolutePackageDBPaths mbWorkDir = traverse $ absolutePackageDBPath mbWorkDir +absolutePackageDBPath+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -> PackageDB+ -> IO PackageDB+absolutePackageDBPath _ GlobalPackageDB = return GlobalPackageDB+absolutePackageDBPath _ UserPackageDB = return UserPackageDB+absolutePackageDBPath mbWorkDir (SpecificPackageDB db) = do+ let db' =+ case symbolicPathRelative_maybe db of+ Nothing -> getSymbolicPath db+ Just rel_path -> interpretSymbolicPath mbWorkDir rel_path+ SpecificPackageDB . makeSymbolicPath <$> canonicalizePath db' -absolutePackageDBPaths :: PackageDBStack -> IO PackageDBStack-absolutePackageDBPaths = traverse absolutePackageDBPath+interpretPackageDB :: Maybe (SymbolicPath CWD (Dir Pkg)) -> PackageDB -> PackageDBCWD+interpretPackageDB _ GlobalPackageDB = GlobalPackageDB+interpretPackageDB _ UserPackageDB = UserPackageDB+interpretPackageDB mbWorkDir (SpecificPackageDB db) =+ SpecificPackageDB (interpretSymbolicPath mbWorkDir db) -absolutePackageDBPath :: PackageDB -> IO PackageDB-absolutePackageDBPath GlobalPackageDB = return GlobalPackageDB-absolutePackageDBPath UserPackageDB = return UserPackageDB-absolutePackageDBPath (SpecificPackageDB db) =- SpecificPackageDB `liftM` canonicalizePath db+interpretPackageDBStack :: Maybe (SymbolicPath CWD (Dir Pkg)) -> PackageDBStack -> PackageDBStackCWD+interpretPackageDBStack mbWorkDir = map (interpretPackageDB mbWorkDir) +-- | Transform a package db using a FilePath into one using symbolic paths.+coercePackageDB :: PackageDBCWD -> PackageDBX (SymbolicPath CWD (Dir PkgDB))+coercePackageDB GlobalPackageDB = GlobalPackageDB+coercePackageDB UserPackageDB = UserPackageDB+coercePackageDB (SpecificPackageDB db) = SpecificPackageDB (makeSymbolicPath db)++coercePackageDBStack+ :: [PackageDBCWD]+ -> [PackageDBX (SymbolicPath CWD (Dir PkgDB))]+coercePackageDBStack = map coercePackageDB+ -- ------------------------------------------------------------+ -- * Optimisation levels+ -- ------------------------------------------------------------ -- | Some compilers support optimising. Some have different levels. -- For compilers that do not the level is just capped to the level -- they do support.----data OptimisationLevel = NoOptimisation- | NormalOptimisation- | MaximumOptimisation- deriving (Bounded, Enum, Eq, Generic, Read, Show, Typeable)+data OptimisationLevel+ = NoOptimisation+ | NormalOptimisation+ | MaximumOptimisation+ deriving (Bounded, Enum, Eq, Generic, Read, Show) instance Binary OptimisationLevel instance Structured OptimisationLevel flagToOptimisationLevel :: Maybe String -> OptimisationLevel-flagToOptimisationLevel Nothing = NormalOptimisation+flagToOptimisationLevel Nothing = NormalOptimisation flagToOptimisationLevel (Just s) = case reads s of [(i, "")] | i >= fromEnum (minBound :: OptimisationLevel)- && i <= fromEnum (maxBound :: OptimisationLevel)- -> toEnum i- | otherwise -> error $ "Bad optimisation level: " ++ show i- ++ ". Valid values are 0..2"- _ -> error $ "Can't parse optimisation level " ++ s+ && i <= fromEnum (maxBound :: OptimisationLevel) ->+ toEnum i+ | otherwise ->+ error $+ "Bad optimisation level: "+ ++ show i+ ++ ". Valid values are 0..2"+ _ -> error $ "Can't parse optimisation level " ++ s -- ------------------------------------------------------------+ -- * Debug info levels+ -- ------------------------------------------------------------ -- | Some compilers support emitting debug info. Some have different -- levels. For compilers that do not the level is just capped to the -- level they do support.----data DebugInfoLevel = NoDebugInfo- | MinimalDebugInfo- | NormalDebugInfo- | MaximalDebugInfo- deriving (Bounded, Enum, Eq, Generic, Read, Show, Typeable)+data DebugInfoLevel+ = NoDebugInfo+ | MinimalDebugInfo+ | NormalDebugInfo+ | MaximalDebugInfo+ deriving (Bounded, Enum, Eq, Generic, Read, Show) instance Binary DebugInfoLevel instance Structured DebugInfoLevel flagToDebugInfoLevel :: Maybe String -> DebugInfoLevel-flagToDebugInfoLevel Nothing = NormalDebugInfo+flagToDebugInfoLevel Nothing = NormalDebugInfo flagToDebugInfoLevel (Just s) = case reads s of [(i, "")] | i >= fromEnum (minBound :: DebugInfoLevel)- && i <= fromEnum (maxBound :: DebugInfoLevel)- -> toEnum i- | otherwise -> error $ "Bad debug info level: " ++ show i- ++ ". Valid values are 0..3"- _ -> error $ "Can't parse debug info level " ++ s+ && i <= fromEnum (maxBound :: DebugInfoLevel) ->+ toEnum i+ | otherwise ->+ error $+ "Bad debug info level: "+ ++ show i+ ++ ". Valid values are 0..3"+ _ -> error $ "Can't parse debug info level " ++ s -- ------------------------------------------------------------+ -- * Languages and Extensions+ -- ------------------------------------------------------------ unsupportedLanguages :: Compiler -> [Language] -> [Language] unsupportedLanguages comp langs =- [ lang | lang <- langs- , isNothing (languageToFlag comp lang) ]+ [ lang | lang <- langs, isNothing (languageToFlag comp lang)+ ] languageToFlags :: Compiler -> Maybe Language -> [CompilerFlag]-languageToFlags comp = filter (not . null)- . catMaybes . map (languageToFlag comp)- . maybe [Haskell98] (\x->[x])+languageToFlags comp =+ filter (not . null)+ . catMaybes+ . map (languageToFlag comp)+ . maybe [Haskell98] (\x -> [x]) languageToFlag :: Compiler -> Language -> Maybe CompilerFlag languageToFlag comp ext = lookup ext (compilerLanguages comp) ---- |For the given compiler, return the extensions it does not support.+-- | For the given compiler, return the extensions it does not support. unsupportedExtensions :: Compiler -> [Extension] -> [Extension] unsupportedExtensions comp exts =- [ ext | ext <- exts- , isNothing (extensionToFlag' comp ext) ]+ [ ext | ext <- exts, isNothing (extensionToFlag' comp ext)+ ] type CompilerFlag = String --- |For the given compiler, return the flags for the supported extensions.+-- | For the given compiler, return the flags for the supported extensions. extensionsToFlags :: Compiler -> [Extension] -> [CompilerFlag]-extensionsToFlags comp = nub . filter (not . null)- . catMaybes . map (extensionToFlag comp)+extensionsToFlags comp =+ nub+ . filter (not . null)+ . catMaybes+ . map (extensionToFlag comp) -- | Looks up the flag for a given extension, for a given compiler. -- Ignores the subtlety of extensions which lack associated flags.@@ -328,8 +405,9 @@ -- | Does this compiler support thinning/renaming on package flags? renamingPackageFlagsSupported :: Compiler -> Bool-renamingPackageFlagsSupported = ghcSupported- "Support thinning and renaming package flags"+renamingPackageFlagsSupported =+ ghcSupported+ "Support thinning and renaming package flags" -- | Does this compiler have unified IPIDs (so no package keys) unifiedIPIDRequired :: Compiler -> Bool@@ -347,63 +425,134 @@ backpackSupported :: Compiler -> Bool backpackSupported = ghcSupported "Support Backpack" +-- | Does this compiler support the -jsem option?+jsemSupported :: Compiler -> Bool+jsemSupported comp = case compilerFlavor comp of+ GHC -> v >= mkVersion [9, 7]+ _ -> False+ where+ v = compilerVersion comp++-- | Does the compiler support the -reexported-modules "A as B" syntax+reexportedAsSupported :: Compiler -> Bool+reexportedAsSupported comp = case compilerFlavor comp of+ GHC -> v >= mkVersion [9, 12]+ _ -> False+ where+ v = compilerVersion comp+ -- | Does this compiler support a package database entry with: -- "dynamic-library-dirs"? libraryDynDirSupported :: Compiler -> Bool libraryDynDirSupported comp = case compilerFlavor comp of GHC ->- -- Not just v >= mkVersion [8,0,1,20161022], as there- -- are many GHC 8.1 nightlies which don't support this.- ((v >= mkVersion [8,0,1,20161022] && v < mkVersion [8,1]) ||- v >= mkVersion [8,1,20161021])- _ -> False- where- v = compilerVersion comp+ -- Not just v >= mkVersion [8,0,1,20161022], as there+ -- are many GHC 8.1 nightlies which don't support this.+ ( (v >= mkVersion [8, 0, 1, 20161022] && v < mkVersion [8, 1])+ || v >= mkVersion [8, 1, 20161021]+ )+ _ -> False+ where+ v = compilerVersion comp -- | Does this compiler's "ar" command supports response file -- arguments (i.e. @file-style arguments). arResponseFilesSupported :: Compiler -> Bool arResponseFilesSupported = ghcSupported "ar supports at file" +-- | Does this compiler's "ar" command support llvm-ar's -L flag,+-- which compels the archiver to add an input archive's members+-- rather than adding the archive itself.+arDashLSupported :: Compiler -> Bool+arDashLSupported = ghcSupported "ar supports -L"+ -- | Does this compiler support Haskell program coverage? coverageSupported :: Compiler -> Bool coverageSupported comp = case compilerFlavor comp of- GHC -> True+ GHC -> True GHCJS -> True- _ -> False+ _ -> False -- | Does this compiler support profiling? profilingSupported :: Compiler -> Bool profilingSupported comp = case compilerFlavor comp of- GHC -> True+ GHC -> True GHCJS -> True- _ -> False+ _ -> False +-- | Returns Just if we can certainly determine whether a way is supported+-- if we don't know, return Nothing+waySupported :: String -> Compiler -> Maybe Bool+waySupported way comp =+ case compilerFlavor comp of+ GHC ->+ -- Information about compiler ways is only accurately reported after+ -- 9.10.1. Which is useful as this is before profiling dynamic support+ -- was introduced. (See GHC #24881)+ if compilerVersion comp >= mkVersion [9, 10, 1]+ then case Map.lookup "RTS ways" (compilerProperties comp) of+ Just ways -> Just (way `elem` words ways)+ Nothing -> Just False+ else Nothing+ _ -> Nothing++-- | Either profiling is definitely supported or we don't know (so assume+-- it is)+profilingVanillaSupportedOrUnknown :: Compiler -> Bool+profilingVanillaSupportedOrUnknown comp = profilingVanillaSupported comp `elem` [Just True, Nothing]++-- | Is the compiler distributed with profiling libraries+profilingVanillaSupported :: Compiler -> Maybe Bool+profilingVanillaSupported comp = waySupported "p" comp++-- | Is the compiler distributed with profiling dynamic libraries+profilingDynamicSupported :: Compiler -> Maybe Bool+profilingDynamicSupported comp+ | GHC <- compilerFlavor comp+ , -- Certainly not before 9.11, as prof+dyn was not implemented yet.+ compilerVersion comp <= mkVersion [9, 11, 0] =+ Just False+ | otherwise =+ waySupported "p_dyn" comp++-- | Either profiling dynamic is definitely supported or we don't know (so assume+-- it is)+profilingDynamicSupportedOrUnknown :: Compiler -> Bool+profilingDynamicSupportedOrUnknown comp =+ profilingDynamicSupported comp `elem` [Just True, Nothing]++-- | Is the compiler distributed with dynamic libraries+dynamicSupported :: Compiler -> Maybe Bool+dynamicSupported comp = waySupported "dyn" comp+ -- | Does this compiler support a package database entry with: -- "visibility"? libraryVisibilitySupported :: Compiler -> Bool libraryVisibilitySupported comp = case compilerFlavor comp of- GHC -> v >= mkVersion [8,8]- _ -> False- where- v = compilerVersion comp+ GHC -> v >= mkVersion [8, 8]+ _ -> False+ where+ v = compilerVersion comp -- | Utility function for GHC only features ghcSupported :: String -> Compiler -> Bool ghcSupported key comp = case compilerFlavor comp of- GHC -> checkProp+ GHC -> checkProp GHCJS -> checkProp- _ -> False- where checkProp =- case Map.lookup key (compilerProperties comp) of- Just "YES" -> True- _ -> False+ _ -> False+ where+ checkProp =+ case Map.lookup key (compilerProperties comp) of+ Just "YES" -> True+ _ -> False -- ------------------------------------------------------------+ -- * Profiling detail level+ -- ------------------------------------------------------------ -- | Some compilers (notably GHC) support profiling and can instrument@@ -412,42 +561,47 @@ -- For compilers that do not support this notion or the particular detail -- levels, this is either ignored or just capped to some similar level -- they do support.----data ProfDetailLevel = ProfDetailNone- | ProfDetailDefault- | ProfDetailExportedFunctions- | ProfDetailToplevelFunctions- | ProfDetailAllFunctions- | ProfDetailOther String- deriving (Eq, Generic, Read, Show, Typeable)+data ProfDetailLevel+ = ProfDetailNone+ | ProfDetailDefault+ | ProfDetailExportedFunctions+ | ProfDetailToplevelFunctions+ | ProfDetailAllFunctions+ | ProfDetailTopLate+ | ProfDetailOther String+ deriving (Eq, Generic, Read, Show) instance Binary ProfDetailLevel instance Structured ProfDetailLevel flagToProfDetailLevel :: String -> ProfDetailLevel flagToProfDetailLevel "" = ProfDetailDefault-flagToProfDetailLevel s =- case lookup (lowercase s)- [ (name, value)- | (primary, aliases, value) <- knownProfDetailLevels- , name <- primary : aliases ]- of Just value -> value- Nothing -> ProfDetailOther s+flagToProfDetailLevel s =+ case lookup+ (lowercase s)+ [ (name, value)+ | (primary, aliases, value) <- knownProfDetailLevels+ , name <- primary : aliases+ ] of+ Just value -> value+ Nothing -> ProfDetailOther s knownProfDetailLevels :: [(String, [String], ProfDetailLevel)] knownProfDetailLevels =- [ ("default", [], ProfDetailDefault)- , ("none", [], ProfDetailNone)- , ("exported-functions", ["exported"], ProfDetailExportedFunctions)+ [ ("default", [], ProfDetailDefault)+ , ("none", [], ProfDetailNone)+ , ("exported-functions", ["exported"], ProfDetailExportedFunctions) , ("toplevel-functions", ["toplevel", "top"], ProfDetailToplevelFunctions)- , ("all-functions", ["all"], ProfDetailAllFunctions)+ , ("all-functions", ["all"], ProfDetailAllFunctions)+ , ("late-toplevel", ["late"], ProfDetailTopLate) ] showProfDetailLevel :: ProfDetailLevel -> String showProfDetailLevel dl = case dl of- ProfDetailNone -> "none"- ProfDetailDefault -> "default"- ProfDetailExportedFunctions -> "exported-functions"- ProfDetailToplevelFunctions -> "toplevel-functions"- ProfDetailAllFunctions -> "all-functions"- ProfDetailOther other -> other+ ProfDetailNone -> "none"+ ProfDetailDefault -> "default"+ ProfDetailExportedFunctions -> "exported-functions"+ ProfDetailToplevelFunctions -> "toplevel-functions"+ ProfDetailAllFunctions -> "all-functions"+ ProfDetailTopLate -> "late-toplevel"+ ProfDetailOther other -> other
src/Distribution/Simple/Configure.hs view
@@ -1,2085 +1,2978 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE ScopedTypeVariables #-}---------------------------------------------------------------------------------- |--- Module : Distribution.Simple.Configure--- Copyright : Isaac Jones 2003-2005--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ This deals with the /configure/ phase. It provides the 'configure' action--- which is given the package description and configure flags. It then tries--- to: configure the compiler; resolves any conditionals in the package--- description; resolve the package dependencies; check if all the extensions--- used by this package are supported by the compiler; check that all the build--- tools are available (including version checks if appropriate); checks for--- any required @pkg-config@ packages (updating the 'BuildInfo' with the--- results)------ Then based on all this it saves the info in the 'LocalBuildInfo' and writes--- it out to the @dist\/setup-config@ file. It also displays various details to--- the user, the amount of information displayed depending on the verbosity--- level.--module Distribution.Simple.Configure- ( configure- , writePersistBuildConfig- , getConfigStateFile- , getPersistBuildConfig- , checkPersistBuildConfigOutdated- , tryGetPersistBuildConfig- , maybeGetPersistBuildConfig- , findDistPref, findDistPrefOrDefault- , getInternalLibraries- , computeComponentId- , computeCompatPackageKey- , localBuildInfoFile- , getInstalledPackages- , getInstalledPackagesMonitorFiles- , getPackageDBContents- , configCompilerEx, configCompilerAuxEx- , computeEffectiveProfiling- , ccLdOptionsBuildInfo- , checkForeignDeps- , interpretPackageDbFlags- , ConfigStateFileError(..)- , tryGetConfigStateFile- , platformDefines,- ) where--import qualified Prelude as Unsafe (tail)-import Distribution.Compat.Prelude--import Distribution.Compiler-import Distribution.Types.IncludeRenaming-import Distribution.Utils.NubList-import Distribution.Simple.Compiler-import Distribution.Simple.PreProcess-import Distribution.Package-import qualified Distribution.InstalledPackageInfo as IPI-import Distribution.InstalledPackageInfo (InstalledPackageInfo)-import qualified Distribution.Simple.PackageIndex as PackageIndex-import Distribution.Simple.PackageIndex (InstalledPackageIndex)-import Distribution.PackageDescription-import Distribution.PackageDescription.PrettyPrint-import Distribution.PackageDescription.Configuration-import Distribution.PackageDescription.Check hiding (doesFileExist)-import Distribution.Simple.BuildToolDepends-import Distribution.Simple.Program-import Distribution.Simple.Setup as Setup-import Distribution.Simple.BuildTarget-import Distribution.Simple.LocalBuildInfo-import Distribution.Types.PackageVersionConstraint-import Distribution.Types.LocalBuildInfo-import Distribution.Types.ComponentRequestedSpec-import Distribution.Types.GivenComponent-import Distribution.Simple.Utils-import Distribution.System-import Distribution.Version-import Distribution.Verbosity-import qualified Distribution.Compat.Graph as Graph-import Distribution.Compat.Stack-import Distribution.Backpack.Configure-import Distribution.Backpack.DescribeUnitId-import Distribution.Backpack.PreExistingComponent-import Distribution.Backpack.ConfiguredComponent (newPackageDepsBehaviour)-import Distribution.Backpack.Id-import Distribution.Utils.LogProgress--import qualified Distribution.Simple.GHC as GHC-import qualified Distribution.Simple.GHCJS as GHCJS-import qualified Distribution.Simple.UHC as UHC-import qualified Distribution.Simple.HaskellSuite as HaskellSuite--import Control.Exception- ( try )-import Distribution.Utils.Structured ( structuredDecodeOrFailIO, structuredEncode )-import Distribution.Compat.Directory ( listDirectory )-import Data.ByteString.Lazy ( ByteString )-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy.Char8 as BLC8-import Data.List- ( (\\), inits, stripPrefix, intersect, dropWhileEnd )-import qualified Data.Map as Map-import System.Directory- ( canonicalizePath, createDirectoryIfMissing, doesFileExist- , getTemporaryDirectory, removeFile)-import System.FilePath- ( (</>), isAbsolute, takeDirectory )-import Distribution.Compat.Directory- ( doesPathExist )-import qualified System.Info- ( compilerName, compilerVersion )-import System.IO- ( hPutStrLn, hClose )-import Distribution.Pretty- ( pretty, defaultStyle, prettyShow )-import Distribution.Parsec- ( simpleParsec )-import Text.PrettyPrint- ( Doc, ($+$), char, comma, hsep, nest- , punctuate, quotes, render, renderStyle, sep, text )-import Distribution.Compat.Environment ( lookupEnv )--import qualified Data.Set as Set-import qualified Distribution.Compat.NonEmptySet as NES---type UseExternalInternalDeps = Bool---- | The errors that can be thrown when reading the @setup-config@ file.-data ConfigStateFileError- = ConfigStateFileNoHeader -- ^ No header found.- | ConfigStateFileBadHeader -- ^ Incorrect header.- | ConfigStateFileNoParse -- ^ Cannot parse file contents.- | ConfigStateFileMissing -- ^ No file!- | ConfigStateFileBadVersion PackageIdentifier PackageIdentifier- (Either ConfigStateFileError LocalBuildInfo) -- ^ Mismatched version.- deriving (Typeable)---- | Format a 'ConfigStateFileError' as a user-facing error message.-dispConfigStateFileError :: ConfigStateFileError -> Doc-dispConfigStateFileError ConfigStateFileNoHeader =- text "Saved package config file header is missing."- <+> text "Re-run the 'configure' command."-dispConfigStateFileError ConfigStateFileBadHeader =- text "Saved package config file header is corrupt."- <+> text "Re-run the 'configure' command."-dispConfigStateFileError ConfigStateFileNoParse =- text "Saved package config file is corrupt."- <+> text "Re-run the 'configure' command."-dispConfigStateFileError ConfigStateFileMissing =- text "Run the 'configure' command first."-dispConfigStateFileError (ConfigStateFileBadVersion oldCabal oldCompiler _) =- text "Saved package config file is outdated:"- $+$ badCabal $+$ badCompiler- $+$ text "Re-run the 'configure' command."- where- badCabal =- text "• the Cabal version changed from"- <+> pretty oldCabal <+> "to" <+> pretty currentCabalId- badCompiler- | oldCompiler == currentCompilerId = mempty- | otherwise =- text "• the compiler changed from"- <+> pretty oldCompiler <+> "to" <+> pretty currentCompilerId--instance Show ConfigStateFileError where- show = renderStyle defaultStyle . dispConfigStateFileError--instance Exception ConfigStateFileError---- | Read the 'localBuildInfoFile'. Throw an exception if the file is--- missing, if the file cannot be read, or if the file was created by an older--- version of Cabal.-getConfigStateFile :: FilePath -- ^ The file path of the @setup-config@ file.- -> IO LocalBuildInfo-getConfigStateFile filename = do- exists <- doesFileExist filename- unless exists $ throwIO ConfigStateFileMissing- -- Read the config file into a strict ByteString to avoid problems with- -- lazy I/O, then convert to lazy because the binary package needs that.- contents <- BS.readFile filename- let (header, body) = BLC8.span (/='\n') (BLC8.fromChunks [contents])-- (cabalId, compId) <- parseHeader header-- let getStoredValue = do- result <- structuredDecodeOrFailIO (BLC8.tail body)- case result of- Left _ -> throwIO ConfigStateFileNoParse- Right x -> return x- deferErrorIfBadVersion act- | cabalId /= currentCabalId = do- eResult <- try act- throwIO $ ConfigStateFileBadVersion cabalId compId eResult- | otherwise = act- deferErrorIfBadVersion getStoredValue- where- _ = callStack -- TODO: attach call stack to exception---- | Read the 'localBuildInfoFile', returning either an error or the local build--- info.-tryGetConfigStateFile :: FilePath -- ^ The file path of the @setup-config@ file.- -> IO (Either ConfigStateFileError LocalBuildInfo)-tryGetConfigStateFile = try . getConfigStateFile---- | Try to read the 'localBuildInfoFile'.-tryGetPersistBuildConfig :: FilePath -- ^ The @dist@ directory path.- -> IO (Either ConfigStateFileError LocalBuildInfo)-tryGetPersistBuildConfig = try . getPersistBuildConfig---- | Read the 'localBuildInfoFile'. Throw an exception if the file is--- missing, if the file cannot be read, or if the file was created by an older--- version of Cabal.-getPersistBuildConfig :: FilePath -- ^ The @dist@ directory path.- -> IO LocalBuildInfo-getPersistBuildConfig = getConfigStateFile . localBuildInfoFile---- | Try to read the 'localBuildInfoFile'.-maybeGetPersistBuildConfig :: FilePath -- ^ The @dist@ directory path.- -> IO (Maybe LocalBuildInfo)-maybeGetPersistBuildConfig =- liftM (either (const Nothing) Just) . tryGetPersistBuildConfig---- | After running configure, output the 'LocalBuildInfo' to the--- 'localBuildInfoFile'.-writePersistBuildConfig :: FilePath -- ^ The @dist@ directory path.- -> LocalBuildInfo -- ^ The 'LocalBuildInfo' to write.- -> IO ()-writePersistBuildConfig distPref lbi = do- createDirectoryIfMissing False distPref- writeFileAtomic (localBuildInfoFile distPref) $- BLC8.unlines [showHeader pkgId, structuredEncode lbi]- where- pkgId = localPackage lbi---- | Identifier of the current Cabal package.-currentCabalId :: PackageIdentifier-currentCabalId = PackageIdentifier (mkPackageName "Cabal") cabalVersion---- | Identifier of the current compiler package.-currentCompilerId :: PackageIdentifier-currentCompilerId = PackageIdentifier (mkPackageName System.Info.compilerName)- (mkVersion' System.Info.compilerVersion)---- | Parse the @setup-config@ file header, returning the package identifiers--- for Cabal and the compiler.-parseHeader :: ByteString -- ^ The file contents.- -> IO (PackageIdentifier, PackageIdentifier)-parseHeader header = case BLC8.words header of- ["Saved", "package", "config", "for", pkgId, "written", "by", cabalId,- "using", compId] ->- maybe (throwIO ConfigStateFileBadHeader) return $ do- _ <- simpleParsec (fromUTF8LBS pkgId) :: Maybe PackageIdentifier- cabalId' <- simpleParsec (BLC8.unpack cabalId)- compId' <- simpleParsec (BLC8.unpack compId)- return (cabalId', compId')- _ -> throwIO ConfigStateFileNoHeader---- | Generate the @setup-config@ file header.-showHeader :: PackageIdentifier -- ^ The processed package.- -> ByteString-showHeader pkgId = BLC8.unwords- [ "Saved", "package", "config", "for"- , toUTF8LBS $ prettyShow pkgId- , "written", "by"- , BLC8.pack $ prettyShow currentCabalId- , "using"- , BLC8.pack $ prettyShow currentCompilerId- ]---- | Check that localBuildInfoFile is up-to-date with respect to the--- .cabal file.-checkPersistBuildConfigOutdated :: FilePath -> FilePath -> IO Bool-checkPersistBuildConfigOutdated distPref pkg_descr_file =- pkg_descr_file `moreRecentFile` localBuildInfoFile distPref---- | Get the path of @dist\/setup-config@.-localBuildInfoFile :: FilePath -- ^ The @dist@ directory path.- -> FilePath-localBuildInfoFile distPref = distPref </> "setup-config"---- -------------------------------------------------------------------------------- * Configuration--- --------------------------------------------------------------------------------- | Return the \"dist/\" prefix, or the default prefix. The prefix is taken--- from (in order of highest to lowest preference) the override prefix, the--- \"CABAL_BUILDDIR\" environment variable, or the default prefix.-findDistPref :: FilePath -- ^ default \"dist\" prefix- -> Setup.Flag FilePath -- ^ override \"dist\" prefix- -> IO FilePath-findDistPref defDistPref overrideDistPref = do- envDistPref <- liftM parseEnvDistPref (lookupEnv "CABAL_BUILDDIR")- return $ fromFlagOrDefault defDistPref (mappend envDistPref overrideDistPref)- where- parseEnvDistPref env =- case env of- Just distPref | not (null distPref) -> toFlag distPref- _ -> NoFlag---- | Return the \"dist/\" prefix, or the default prefix. The prefix is taken--- from (in order of highest to lowest preference) the override prefix, the--- \"CABAL_BUILDDIR\" environment variable, or 'defaultDistPref' is used. Call--- this function to resolve a @*DistPref@ flag whenever it is not known to be--- set. (The @*DistPref@ flags are always set to a definite value before--- invoking 'UserHooks'.)-findDistPrefOrDefault :: Setup.Flag FilePath -- ^ override \"dist\" prefix- -> IO FilePath-findDistPrefOrDefault = findDistPref defaultDistPref---- |Perform the \"@.\/setup configure@\" action.--- Returns the @.setup-config@ file.-configure :: (GenericPackageDescription, HookedBuildInfo)- -> ConfigFlags -> IO LocalBuildInfo-configure (pkg_descr0, pbi) cfg = do- -- Determine the component we are configuring, if a user specified- -- one on the command line. We use a fake, flattened version of- -- the package since at this point, we're not really sure what- -- components we *can* configure. @Nothing@ means that we should- -- configure everything (the old behavior).- (mb_cname :: Maybe ComponentName) <- do- let flat_pkg_descr = flattenPackageDescription pkg_descr0- targets <- readBuildTargets verbosity flat_pkg_descr (configArgs cfg)- -- TODO: bleat if you use the module/file syntax- let targets' = [ cname | BuildTargetComponent cname <- targets ]- case targets' of- _ | null (configArgs cfg) -> return Nothing- [cname] -> return (Just cname)- [] -> die' verbosity "No valid component targets found"- _ -> die' verbosity- "Can only configure either single component or all of them"-- let use_external_internal_deps = isJust mb_cname- case mb_cname of- Nothing -> setupMessage verbosity "Configuring" (packageId pkg_descr0)- Just cname -> setupMessage' verbosity "Configuring" (packageId pkg_descr0)- cname (Just (configInstantiateWith cfg))-- -- configCID is only valid for per-component configure- when (isJust (flagToMaybe (configCID cfg)) && isNothing mb_cname) $- die' verbosity "--cid is only supported for per-component configure"-- checkDeprecatedFlags verbosity cfg- checkExactConfiguration verbosity pkg_descr0 cfg-- -- Where to build the package- let buildDir :: FilePath -- e.g. dist/build- -- fromFlag OK due to Distribution.Simple calling- -- findDistPrefOrDefault to fill it in- buildDir = fromFlag (configDistPref cfg) </> "build"- createDirectoryIfMissingVerbose (lessVerbose verbosity) True buildDir-- -- What package database(s) to use- let packageDbs :: PackageDBStack- packageDbs- = interpretPackageDbFlags- (fromFlag (configUserInstall cfg))- (configPackageDBs cfg)-- -- comp: the compiler we're building with- -- compPlatform: the platform we're building for- -- programDb: location and args of all programs we're- -- building with- (comp :: Compiler,- compPlatform :: Platform,- programDb :: ProgramDb)- <- configCompilerEx- (flagToMaybe (configHcFlavor cfg))- (flagToMaybe (configHcPath cfg))- (flagToMaybe (configHcPkg cfg))- (mkProgramDb cfg (configPrograms cfg))- (lessVerbose verbosity)-- -- The InstalledPackageIndex of all installed packages- installedPackageSet :: InstalledPackageIndex- <- getInstalledPackages (lessVerbose verbosity) comp- packageDbs programDb-- -- The set of package names which are "shadowed" by internal- -- packages, and which component they map to- let internalPackageSet :: Set LibraryName- internalPackageSet = getInternalLibraries pkg_descr0-- -- Make a data structure describing what components are enabled.- let enabled :: ComponentRequestedSpec- enabled = case mb_cname of- Just cname -> OneComponentRequestedSpec cname- Nothing -> ComponentRequestedSpec- -- The flag name (@--enable-tests@) is a- -- little bit of a misnomer, because- -- just passing this flag won't- -- "enable", in our internal- -- nomenclature; it's just a request; a- -- @buildable: False@ might make it- -- not possible to enable.- { testsRequested = fromFlag (configTests cfg)- , benchmarksRequested =- fromFlag (configBenchmarks cfg) }- -- Some sanity checks related to enabling components.- when (isJust mb_cname- && (fromFlag (configTests cfg) || fromFlag (configBenchmarks cfg))) $- die' verbosity $- "--enable-tests/--enable-benchmarks are incompatible with" ++- " explicitly specifying a component to configure."-- -- Some sanity checks related to dynamic/static linking.- when (fromFlag (configDynExe cfg) && fromFlag (configFullyStaticExe cfg)) $- die' verbosity $- "--enable-executable-dynamic and --enable-executable-static" ++- " are incompatible with each other."-- -- allConstraints: The set of all 'Dependency's we have. Used ONLY- -- to 'configureFinalizedPackage'.- -- requiredDepsMap: A map from 'PackageName' to the specifically- -- required 'InstalledPackageInfo', due to --dependency- --- -- NB: These constraints are to be applied to ALL components of- -- a package. Thus, it's not an error if allConstraints contains- -- more constraints than is necessary for a component (another- -- component might need it.)- --- -- NB: The fact that we bundle all the constraints together means- -- that is not possible to configure a test-suite to use one- -- version of a dependency, and the executable to use another.- (allConstraints :: [PackageVersionConstraint],- requiredDepsMap :: Map (PackageName, ComponentName) InstalledPackageInfo)- <- either (die' verbosity) return $- combinedConstraints (configConstraints cfg)- (configDependencies cfg)- installedPackageSet-- -- pkg_descr: The resolved package description, that does not contain any- -- conditionals, because we have an assignment for- -- every flag, either picking them ourselves using a- -- simple naive algorithm, or having them be passed to- -- us by 'configConfigurationsFlags')- -- flags: The 'FlagAssignment' that the conditionals were- -- resolved with.- --- -- NB: Why doesn't finalizing a package also tell us what the- -- dependencies are (e.g. when we run the naive algorithm,- -- we are checking if dependencies are satisfiable)? The- -- primary reason is that we may NOT have done any solving:- -- if the flags are all chosen for us, this step is a simple- -- matter of flattening according to that assignment. It's- -- cleaner to then configure the dependencies afterwards.- (pkg_descr :: PackageDescription,- flags :: FlagAssignment)- <- configureFinalizedPackage verbosity cfg enabled- allConstraints- (dependencySatisfiable- use_external_internal_deps- (fromFlagOrDefault False (configExactConfiguration cfg))- (fromFlagOrDefault False (configAllowDependingOnPrivateLibs cfg))- (packageName pkg_descr0)- installedPackageSet- internalPackageSet- requiredDepsMap)- comp- compPlatform- pkg_descr0-- debug verbosity $ "Finalized package description:\n"- ++ showPackageDescription pkg_descr-- let cabalFileDir = maybe "." takeDirectory $- flagToMaybe (configCabalFilePath cfg)- checkCompilerProblems verbosity comp pkg_descr enabled- checkPackageProblems verbosity cabalFileDir pkg_descr0- (updatePackageDescription pbi pkg_descr)-- -- The list of 'InstalledPackageInfo' recording the selected- -- dependencies on external packages.- --- -- Invariant: For any package name, there is at most one package- -- in externalPackageDeps which has that name.- --- -- NB: The dependency selection is global over ALL components- -- in the package (similar to how allConstraints and- -- requiredDepsMap are global over all components). In particular,- -- if *any* component (post-flag resolution) has an unsatisfiable- -- dependency, we will fail. This can sometimes be undesirable- -- for users, see #1786 (benchmark conflicts with executable),- --- -- In the presence of Backpack, these package dependencies are- -- NOT complete: they only ever include the INDEFINITE- -- dependencies. After we apply an instantiation, we'll get- -- definite references which constitute extra dependencies.- -- (Why not have cabal-install pass these in explicitly?- -- For one it's deterministic; for two, we need to associate- -- them with renamings which would require a far more complicated- -- input scheme than what we have today.)- externalPkgDeps :: [PreExistingComponent]- <- configureDependencies- verbosity- use_external_internal_deps- internalPackageSet- installedPackageSet- requiredDepsMap- pkg_descr- enabled-- -- Compute installation directory templates, based on user- -- configuration.- --- -- TODO: Move this into a helper function.- defaultDirs :: InstallDirTemplates- <- defaultInstallDirs' use_external_internal_deps- (compilerFlavor comp)- (fromFlag (configUserInstall cfg))- (hasLibs pkg_descr)- let installDirs :: InstallDirTemplates- installDirs = combineInstallDirs fromFlagOrDefault- defaultDirs (configInstallDirs cfg)-- -- Check languages and extensions- -- TODO: Move this into a helper function.- let langlist = nub $ catMaybes $ map defaultLanguage- (enabledBuildInfos pkg_descr enabled)- let langs = unsupportedLanguages comp langlist- when (not (null langs)) $- die' verbosity $ "The package " ++ prettyShow (packageId pkg_descr0)- ++ " requires the following languages which are not "- ++ "supported by " ++ prettyShow (compilerId comp) ++ ": "- ++ intercalate ", " (map prettyShow langs)- let extlist = nub $ concatMap allExtensions- (enabledBuildInfos pkg_descr enabled)- let exts = unsupportedExtensions comp extlist- when (not (null exts)) $- die' verbosity $ "The package " ++ prettyShow (packageId pkg_descr0)- ++ " requires the following language extensions which are not "- ++ "supported by " ++ prettyShow (compilerId comp) ++ ": "- ++ intercalate ", " (map prettyShow exts)-- -- Check foreign library build requirements- let flibs = [flib | CFLib flib <- enabledComponents pkg_descr enabled]- let unsupportedFLibs = unsupportedForeignLibs comp compPlatform flibs- when (not (null unsupportedFLibs)) $- die' verbosity $ "Cannot build some foreign libraries: "- ++ intercalate "," unsupportedFLibs-- -- Configure certain external build tools, see below for which ones.- let requiredBuildTools = do- bi <- enabledBuildInfos pkg_descr enabled- -- First, we collect any tool dep that we know is external. This is,- -- in practice:- --- -- 1. `build-tools` entries on the whitelist- --- -- 2. `build-tool-depends` that aren't from the current package.- let externBuildToolDeps =- [ LegacyExeDependency (unUnqualComponentName eName) versionRange- | buildTool@(ExeDependency _ eName versionRange)- <- getAllToolDependencies pkg_descr bi- , not $ isInternal pkg_descr buildTool ]- -- Second, we collect any build-tools entry we don't know how to- -- desugar. We'll never have any idea how to build them, so we just- -- hope they are already on the PATH.- let unknownBuildTools =- [ buildTool- | buildTool <- buildTools bi- , Nothing == desugarBuildTool pkg_descr buildTool ]- externBuildToolDeps ++ unknownBuildTools-- programDb' <-- configureAllKnownPrograms (lessVerbose verbosity) programDb- >>= configureRequiredPrograms verbosity requiredBuildTools-- (pkg_descr', programDb'') <-- configurePkgconfigPackages verbosity pkg_descr programDb' enabled-- -- Compute internal component graph- --- -- The general idea is that we take a look at all the source level- -- components (which may build-depends on each other) and form a graph.- -- From there, we build a ComponentLocalBuildInfo for each of the- -- components, which lets us actually build each component.- -- internalPackageSet- -- use_external_internal_deps- (buildComponents :: [ComponentLocalBuildInfo],- packageDependsIndex :: InstalledPackageIndex) <-- runLogProgress verbosity $ configureComponentLocalBuildInfos- verbosity- use_external_internal_deps- enabled- (fromFlagOrDefault False (configDeterministic cfg))- (configIPID cfg)- (configCID cfg)- pkg_descr- externalPkgDeps- (configConfigurationsFlags cfg)- (configInstantiateWith cfg)- installedPackageSet- comp-- -- Decide if we're going to compile with split sections.- split_sections :: Bool <-- if not (fromFlag $ configSplitSections cfg)- then return False- else case compilerFlavor comp of- GHC | compilerVersion comp >= mkVersion [8,0]- -> return True- GHCJS- -> return True- _ -> do warn verbosity- ("this compiler does not support " ++- "--enable-split-sections; ignoring")- return False-- -- Decide if we're going to compile with split objects.- split_objs :: Bool <-- if not (fromFlag $ configSplitObjs cfg)- then return False- else case compilerFlavor comp of- _ | split_sections- -> do warn verbosity- ("--enable-split-sections and " ++- "--enable-split-objs are mutually" ++- "exclusive; ignoring the latter")- return False- GHC- -> return True- GHCJS- -> return True- _ -> do warn verbosity- ("this compiler does not support " ++- "--enable-split-objs; ignoring")- return False-- let ghciLibByDefault =- case compilerId comp of- CompilerId GHC _ ->- -- If ghc is non-dynamic, then ghci needs object files,- -- so we build one by default.- --- -- Technically, archive files should be sufficient for ghci,- -- but because of GHC bug #8942, it has never been safe to- -- rely on them. By the time that bug was fixed, ghci had- -- been changed to read shared libraries instead of archive- -- files (see next code block).- not (GHC.isDynamic comp)- CompilerId GHCJS _ ->- not (GHCJS.isDynamic comp)- _ -> False-- let sharedLibsByDefault- | fromFlag (configDynExe cfg) =- -- build a shared library if dynamically-linked- -- executables are requested- True- | otherwise = case compilerId comp of- CompilerId GHC _ ->- -- if ghc is dynamic, then ghci needs a shared- -- library, so we build one by default.- GHC.isDynamic comp- CompilerId GHCJS _ ->- GHCJS.isDynamic comp- _ -> False- withSharedLib_ =- -- build shared libraries if required by GHC or by the- -- executable linking mode, but allow the user to force- -- building only static library archives with- -- --disable-shared.- fromFlagOrDefault sharedLibsByDefault $ configSharedLib cfg-- withStaticLib_ =- -- build a static library (all dependent libraries rolled- -- into a huge .a archive) via GHCs -staticlib flag.- fromFlagOrDefault False $ configStaticLib cfg-- withDynExe_ = fromFlag $ configDynExe cfg-- withFullyStaticExe_ = fromFlag $ configFullyStaticExe cfg- when (withDynExe_ && not withSharedLib_) $ warn verbosity $- "Executables will use dynamic linking, but a shared library "- ++ "is not being built. Linking will fail if any executables "- ++ "depend on the library."-- setProfLBI <- configureProfiling verbosity cfg comp-- setCoverageLBI <- configureCoverage verbosity cfg comp---- -- Turn off library and executable stripping when `debug-info` is set- -- to anything other than zero.- let- strip_libexe s f =- let defaultStrip = fromFlagOrDefault True (f cfg)- in case fromFlag (configDebugInfo cfg) of- NoDebugInfo -> return defaultStrip- _ -> case f cfg of- Flag True -> do- warn verbosity $ "Setting debug-info implies "- ++ s ++ "-stripping: False"- return False-- _ -> return False-- strip_lib <- strip_libexe "library" configStripLibs- strip_exe <- strip_libexe "executable" configStripExes--- let reloc = fromFlagOrDefault False $ configRelocatable cfg-- let buildComponentsMap =- foldl' (\m clbi -> Map.insertWith (++)- (componentLocalName clbi) [clbi] m)- Map.empty buildComponents-- let lbi = (setCoverageLBI . setProfLBI)- LocalBuildInfo {- configFlags = cfg,- flagAssignment = flags,- componentEnabledSpec = enabled,- extraConfigArgs = [], -- Currently configure does not- -- take extra args, but if it- -- did they would go here.- installDirTemplates = installDirs,- compiler = comp,- hostPlatform = compPlatform,- buildDir = buildDir,- cabalFilePath = flagToMaybe (configCabalFilePath cfg),- componentGraph = Graph.fromDistinctList buildComponents,- componentNameMap = buildComponentsMap,- installedPkgs = packageDependsIndex,- pkgDescrFile = Nothing,- localPkgDescr = pkg_descr',- withPrograms = programDb'',- withVanillaLib = fromFlag $ configVanillaLib cfg,- withSharedLib = withSharedLib_,- withStaticLib = withStaticLib_,- withDynExe = withDynExe_,- withFullyStaticExe = withFullyStaticExe_,- withProfLib = False,- withProfLibDetail = ProfDetailNone,- withProfExe = False,- withProfExeDetail = ProfDetailNone,- withOptimization = fromFlag $ configOptimization cfg,- withDebugInfo = fromFlag $ configDebugInfo cfg,- withGHCiLib = fromFlagOrDefault ghciLibByDefault $- configGHCiLib cfg,- splitSections = split_sections,- splitObjs = split_objs,- stripExes = strip_exe,- stripLibs = strip_lib,- exeCoverage = False,- libCoverage = False,- withPackageDB = packageDbs,- progPrefix = fromFlag $ configProgPrefix cfg,- progSuffix = fromFlag $ configProgSuffix cfg,- relocatable = reloc- }-- when reloc (checkRelocatable verbosity pkg_descr lbi)-- -- TODO: This is not entirely correct, because the dirs may vary- -- across libraries/executables- let dirs = absoluteInstallDirs pkg_descr lbi NoCopyDest- relative = prefixRelativeInstallDirs (packageId pkg_descr) lbi-- -- PKGROOT: allowing ${pkgroot} to be passed as --prefix to- -- cabal configure, is only a hidden option. It allows packages- -- to be relocatable with their package database. This however- -- breaks when the Paths_* or other includes are used that- -- contain hard coded paths. This is still an open TODO.- --- -- Allowing ${pkgroot} here, however requires less custom hooks- -- in scripts that *really* want ${pkgroot}. See haskell/cabal/#4872- unless (isAbsolute (prefix dirs)- || "${pkgroot}" `isPrefixOf` prefix dirs) $ die' verbosity $- "expected an absolute directory name for --prefix: " ++ prefix dirs-- when ("${pkgroot}" `isPrefixOf` prefix dirs) $- warn verbosity $ "Using ${pkgroot} in prefix " ++ prefix dirs- ++ " will not work if you rely on the Path_* module "- ++ " or other hard coded paths. Cabal does not yet "- ++ " support fully relocatable builds! "- ++ " See #462 #2302 #2994 #3305 #3473 #3586 #3909"- ++ " #4097 #4291 #4872"-- info verbosity $ "Using " ++ prettyShow currentCabalId- ++ " compiled by " ++ prettyShow currentCompilerId- info verbosity $ "Using compiler: " ++ showCompilerId comp- info verbosity $ "Using install prefix: " ++ prefix dirs-- let dirinfo name dir isPrefixRelative =- info verbosity $ name ++ " installed in: " ++ dir ++ relNote- where relNote = case buildOS of- Windows | not (hasLibs pkg_descr)- && isNothing isPrefixRelative- -> " (fixed location)"- _ -> ""-- dirinfo "Executables" (bindir dirs) (bindir relative)- dirinfo "Libraries" (libdir dirs) (libdir relative)- dirinfo "Dynamic Libraries" (dynlibdir dirs) (dynlibdir relative)- dirinfo "Private executables" (libexecdir dirs) (libexecdir relative)- dirinfo "Data files" (datadir dirs) (datadir relative)- dirinfo "Documentation" (docdir dirs) (docdir relative)- dirinfo "Configuration files" (sysconfdir dirs) (sysconfdir relative)-- sequence_ [ reportProgram verbosity prog configuredProg- | (prog, configuredProg) <- knownPrograms programDb'' ]-- return lbi-- where- verbosity = fromFlag (configVerbosity cfg)--mkProgramDb :: ConfigFlags -> ProgramDb -> ProgramDb-mkProgramDb cfg initialProgramDb = programDb- where- programDb = userSpecifyArgss (configProgramArgs cfg)- . userSpecifyPaths (configProgramPaths cfg)- . setProgramSearchPath searchpath- $ initialProgramDb- searchpath = getProgramSearchPath initialProgramDb- ++ map ProgramSearchPathDir- (fromNubList $ configProgramPathExtra cfg)---- -------------------------------------------------------------------------------- Helper functions for configure---- | Check if the user used any deprecated flags.-checkDeprecatedFlags :: Verbosity -> ConfigFlags -> IO ()-checkDeprecatedFlags verbosity cfg = do- unless (configProfExe cfg == NoFlag) $ do- let enable | fromFlag (configProfExe cfg) = "enable"- | otherwise = "disable"- warn verbosity- ("The flag --" ++ enable ++ "-executable-profiling is deprecated. "- ++ "Please use --" ++ enable ++ "-profiling instead.")-- unless (configLibCoverage cfg == NoFlag) $ do- let enable | fromFlag (configLibCoverage cfg) = "enable"- | otherwise = "disable"- warn verbosity- ("The flag --" ++ enable ++ "-library-coverage is deprecated. "- ++ "Please use --" ++ enable ++ "-coverage instead.")---- | Sanity check: if '--exact-configuration' was given, ensure that the--- complete flag assignment was specified on the command line.-checkExactConfiguration- :: Verbosity -> GenericPackageDescription -> ConfigFlags -> IO ()-checkExactConfiguration verbosity pkg_descr0 cfg =- when (fromFlagOrDefault False (configExactConfiguration cfg)) $ do- let cmdlineFlags = map fst (unFlagAssignment (configConfigurationsFlags cfg))- allFlags = map flagName . genPackageFlags $ pkg_descr0- diffFlags = allFlags \\ cmdlineFlags- when (not . null $ diffFlags) $- die' verbosity $ "'--exact-configuration' was given, "- ++ "but the following flags were not specified: "- ++ intercalate ", " (map show diffFlags)---- | Create a PackageIndex that makes *any libraries that might be*--- defined internally to this package look like installed packages, in--- case an executable should refer to any of them as dependencies.------ It must be *any libraries that might be* defined rather than the--- actual definitions, because these depend on conditionals in the .cabal--- file, and we haven't resolved them yet. finalizePD--- does the resolution of conditionals, and it takes internalPackageSet--- as part of its input.-getInternalLibraries :: GenericPackageDescription- -> Set LibraryName-getInternalLibraries pkg_descr0 =- -- TODO: some day, executables will be fair game here too!- let pkg_descr = flattenPackageDescription pkg_descr0- in Set.fromList (map libName (allLibraries pkg_descr))---- | Returns true if a dependency is satisfiable. This function may--- report a dependency satisfiable even when it is not, but not vice--- versa. This is to be passed to finalize-dependencySatisfiable- :: Bool -- ^ use external internal deps?- -> Bool -- ^ exact configuration?- -> Bool -- ^ allow depending on private libs?- -> PackageName- -> InstalledPackageIndex -- ^ installed set- -> Set LibraryName -- ^ library components- -> Map (PackageName, ComponentName) InstalledPackageInfo- -- ^ required dependencies- -> (Dependency -> Bool)-dependencySatisfiable- use_external_internal_deps- exact_config- allow_private_deps- pn installedPackageSet packageLibraries requiredDepsMap- (Dependency depName vr sublibs)- | exact_config- -- When we're given '--exact-configuration', we assume that all- -- dependencies and flags are exactly specified on the command- -- line. Thus we only consult the 'requiredDepsMap'. Note that- -- we're not doing the version range check, so if there's some- -- dependency that wasn't specified on the command line,- -- 'finalizePD' will fail.- -- TODO: mention '--exact-configuration' in the error message- -- when this fails?- = if isInternalDep && not use_external_internal_deps- -- Except for internal deps, when we're NOT per-component mode;- -- those are just True.- then internalDepSatisfiable- else- -- Backward compatibility for the old sublibrary syntax- (sublibs == mainLibSet- && Map.member- (pn, CLibName $ LSubLibName $- packageNameToUnqualComponentName depName)- requiredDepsMap)-- || all visible sublibs-- | isInternalDep- = if use_external_internal_deps- -- When we are doing per-component configure, we now need to- -- test if the internal dependency is in the index. This has- -- DIFFERENT semantics from normal dependency satisfiability.- then internalDepSatisfiableExternally- -- If a 'PackageName' is defined by an internal component, the dep is- -- satisfiable (we're going to build it ourselves)- else internalDepSatisfiable-- | otherwise- = depSatisfiable-- where- -- Internal dependency is when dependency is the same as package.- isInternalDep = pn == depName-- depSatisfiable =- not . null $ PackageIndex.lookupDependency installedPackageSet depName vr-- internalDepSatisfiable =- Set.isSubsetOf (NES.toSet sublibs) packageLibraries- internalDepSatisfiableExternally =- all (\ln -> not $ null $ PackageIndex.lookupInternalDependency installedPackageSet pn vr ln) sublibs-- -- Check whether a library exists and is visible.- -- We don't disambiguate between dependency on non-existent or private- -- library yet, so we just return a bool and later report a generic error.- visible lib = maybe- False -- Does not even exist (wasn't in the depsMap)- (\ipi -> IPI.libVisibility ipi == LibraryVisibilityPublic- -- If the override is enabled, the visibility does- -- not matter (it's handled externally)- || allow_private_deps- -- If it's a library of the same package then it's- -- always visible.- -- This is only triggered when passing a component- -- of the same package as --dependency, such as in:- -- cabal-testsuite/PackageTests/ConfigureComponent/SubLib/setup-explicit.test.hs- || pkgName (IPI.sourcePackageId ipi) == pn)- maybeIPI- where maybeIPI = Map.lookup (depName, CLibName lib) requiredDepsMap---- | Finalize a generic package description. The workhorse is--- 'finalizePD' but there's a bit of other nattering--- about necessary.------ TODO: what exactly is the business with @flaggedTests@ and--- @flaggedBenchmarks@?-configureFinalizedPackage- :: Verbosity- -> ConfigFlags- -> ComponentRequestedSpec- -> [PackageVersionConstraint]- -> (Dependency -> Bool) -- ^ tests if a dependency is satisfiable.- -- Might say it's satisfiable even when not.- -> Compiler- -> Platform- -> GenericPackageDescription- -> IO (PackageDescription, FlagAssignment)-configureFinalizedPackage verbosity cfg enabled- allConstraints satisfies comp compPlatform pkg_descr0 = do-- (pkg_descr0', flags) <-- case finalizePD- (configConfigurationsFlags cfg)- enabled- satisfies- compPlatform- (compilerInfo comp)- allConstraints- pkg_descr0- of Right r -> return r- Left missing ->- die' verbosity $ "Encountered missing or private dependencies:\n"- ++ (render . nest 4 . sep . punctuate comma- . map (pretty . simplifyDependency)- $ missing)-- -- add extra include/lib dirs as specified in cfg- -- we do it here so that those get checked too- let pkg_descr = addExtraIncludeLibDirs pkg_descr0'-- unless (nullFlagAssignment flags) $- info verbosity $ "Flags chosen: "- ++ intercalate ", " [ unFlagName fn ++ "=" ++ prettyShow value- | (fn, value) <- unFlagAssignment flags ]-- return (pkg_descr, flags)- where- addExtraIncludeLibDirs pkg_descr =- let extraBi = mempty { extraLibDirs = configExtraLibDirs cfg- , extraFrameworkDirs = configExtraFrameworkDirs cfg- , includeDirs = configExtraIncludeDirs cfg}- modifyLib l = l{ libBuildInfo = libBuildInfo l- `mappend` extraBi }- modifyExecutable e = e{ buildInfo = buildInfo e- `mappend` extraBi}- modifyForeignLib f = f{ foreignLibBuildInfo = foreignLibBuildInfo f- `mappend` extraBi}- modifyTestsuite t = t{ testBuildInfo = testBuildInfo t- `mappend` extraBi}- modifyBenchmark b = b{ benchmarkBuildInfo = benchmarkBuildInfo b- `mappend` extraBi}- in pkg_descr- { library = modifyLib `fmap` library pkg_descr- , subLibraries = modifyLib `map` subLibraries pkg_descr- , executables = modifyExecutable `map` executables pkg_descr- , foreignLibs = modifyForeignLib `map` foreignLibs pkg_descr- , testSuites = modifyTestsuite `map` testSuites pkg_descr- , benchmarks = modifyBenchmark `map` benchmarks pkg_descr- }---- | Check for use of Cabal features which require compiler support-checkCompilerProblems- :: Verbosity -> Compiler -> PackageDescription -> ComponentRequestedSpec -> IO ()-checkCompilerProblems verbosity comp pkg_descr enabled = do- unless (renamingPackageFlagsSupported comp ||- all (all (isDefaultIncludeRenaming . mixinIncludeRenaming) . mixins)- (enabledBuildInfos pkg_descr enabled)) $- die' verbosity $- "Your compiler does not support thinning and renaming on "- ++ "package flags. To use this feature you must use "- ++ "GHC 7.9 or later."-- when (any (not.null.reexportedModules) (allLibraries pkg_descr)- && not (reexportedModulesSupported comp)) $- die' verbosity $- "Your compiler does not support module re-exports. To use "- ++ "this feature you must use GHC 7.9 or later."-- when (any (not.null.signatures) (allLibraries pkg_descr)- && not (backpackSupported comp)) $- die' verbosity $- "Your compiler does not support Backpack. To use "- ++ "this feature you must use GHC 8.1 or later."---- | Select dependencies for the package.-configureDependencies- :: Verbosity- -> UseExternalInternalDeps- -> Set LibraryName- -> InstalledPackageIndex -- ^ installed packages- -> Map (PackageName, ComponentName) InstalledPackageInfo -- ^ required deps- -> PackageDescription- -> ComponentRequestedSpec- -> IO [PreExistingComponent]-configureDependencies verbosity use_external_internal_deps- packageLibraries installedPackageSet requiredDepsMap pkg_descr enableSpec = do- let failedDeps :: [FailedDependency]- allPkgDeps :: [ResolvedDependency]- (failedDeps, allPkgDeps) = partitionEithers $ concat- [ fmap (\s -> (dep, s)) <$> status- | dep <- enabledBuildDepends pkg_descr enableSpec- , let status = selectDependency (package pkg_descr)- packageLibraries installedPackageSet- requiredDepsMap use_external_internal_deps dep ]-- internalPkgDeps = [ pkgid- | (_, InternalDependency pkgid) <- allPkgDeps ]- -- NB: we have to SAVE the package name, because this is the only- -- way we can be able to resolve package names in the package- -- description.- externalPkgDeps = [ pec- | (_, ExternalDependency pec) <- allPkgDeps ]-- when (not (null internalPkgDeps)- && not (newPackageDepsBehaviour pkg_descr)) $- die' verbosity $ "The field 'build-depends: "- ++ intercalate ", " (map (prettyShow . packageName) internalPkgDeps)- ++ "' refers to a library which is defined within the same "- ++ "package. To use this feature the package must specify at "- ++ "least 'cabal-version: >= 1.8'."-- reportFailedDependencies verbosity failedDeps- reportSelectedDependencies verbosity allPkgDeps-- return externalPkgDeps---- | Select and apply coverage settings for the build based on the--- 'ConfigFlags' and 'Compiler'.-configureCoverage :: Verbosity -> ConfigFlags -> Compiler- -> IO (LocalBuildInfo -> LocalBuildInfo)-configureCoverage verbosity cfg comp = do- let tryExeCoverage = fromFlagOrDefault False (configCoverage cfg)- tryLibCoverage = fromFlagOrDefault tryExeCoverage- (mappend (configCoverage cfg) (configLibCoverage cfg))- if coverageSupported comp- then do- let apply lbi = lbi { libCoverage = tryLibCoverage- , exeCoverage = tryExeCoverage- }- return apply- else do- let apply lbi = lbi { libCoverage = False- , exeCoverage = False- }- when (tryExeCoverage || tryLibCoverage) $ warn verbosity- ("The compiler " ++ showCompilerId comp ++ " does not support "- ++ "program coverage. Program coverage has been disabled.")- return apply---- | Compute the effective value of the profiling flags--- @--enable-library-profiling@ and @--enable-executable-profiling@--- from the specified 'ConfigFlags'. This may be useful for--- external Cabal tools which need to interact with Setup in--- a backwards-compatible way: the most predictable mechanism--- for enabling profiling across many legacy versions is to--- NOT use @--enable-profiling@ and use those two flags instead.------ Note that @--enable-executable-profiling@ also affects profiling--- of benchmarks and (non-detailed) test suites.-computeEffectiveProfiling :: ConfigFlags -> (Bool {- lib -}, Bool {- exe -})-computeEffectiveProfiling cfg =- -- The --profiling flag sets the default for both libs and exes,- -- but can be overidden by --library-profiling, or the old deprecated- -- --executable-profiling flag.- --- -- The --profiling-detail and --library-profiling-detail flags behave- -- similarly- let tryExeProfiling = fromFlagOrDefault False- (mappend (configProf cfg) (configProfExe cfg))- tryLibProfiling = fromFlagOrDefault tryExeProfiling- (mappend (configProf cfg) (configProfLib cfg))- in (tryLibProfiling, tryExeProfiling)---- | Select and apply profiling settings for the build based on the--- 'ConfigFlags' and 'Compiler'.-configureProfiling :: Verbosity -> ConfigFlags -> Compiler- -> IO (LocalBuildInfo -> LocalBuildInfo)-configureProfiling verbosity cfg comp = do- let (tryLibProfiling, tryExeProfiling) = computeEffectiveProfiling cfg-- tryExeProfileLevel = fromFlagOrDefault ProfDetailDefault- (configProfDetail cfg)- tryLibProfileLevel = fromFlagOrDefault ProfDetailDefault- (mappend- (configProfDetail cfg)- (configProfLibDetail cfg))-- checkProfileLevel (ProfDetailOther other) = do- warn verbosity- ("Unknown profiling detail level '" ++ other- ++ "', using default.\nThe profiling detail levels are: "- ++ intercalate ", "- [ name | (name, _, _) <- knownProfDetailLevels ])- return ProfDetailDefault- checkProfileLevel other = return other-- (exeProfWithoutLibProf, applyProfiling) <-- if profilingSupported comp- then do- exeLevel <- checkProfileLevel tryExeProfileLevel- libLevel <- checkProfileLevel tryLibProfileLevel- let apply lbi = lbi { withProfLib = tryLibProfiling- , withProfLibDetail = libLevel- , withProfExe = tryExeProfiling- , withProfExeDetail = exeLevel- }- return (tryExeProfiling && not tryLibProfiling, apply)- else do- let apply lbi = lbi { withProfLib = False- , withProfLibDetail = ProfDetailNone- , withProfExe = False- , withProfExeDetail = ProfDetailNone- }- when (tryExeProfiling || tryLibProfiling) $ warn verbosity- ("The compiler " ++ showCompilerId comp ++ " does not support "- ++ "profiling. Profiling has been disabled.")- return (False, apply)-- when exeProfWithoutLibProf $ warn verbosity- ("Executables will be built with profiling, but library "- ++ "profiling is disabled. Linking will fail if any executables "- ++ "depend on the library.")-- return applyProfiling---- -------------------------------------------------------------------------------- Configuring package dependencies--reportProgram :: Verbosity -> Program -> Maybe ConfiguredProgram -> IO ()-reportProgram verbosity prog Nothing- = info verbosity $ "No " ++ programName prog ++ " found"-reportProgram verbosity prog (Just configuredProg)- = info verbosity $ "Using " ++ programName prog ++ version ++ location- where location = case programLocation configuredProg of- FoundOnSystem p -> " found on system at: " ++ p- UserSpecified p -> " given by user at: " ++ p- version = case programVersion configuredProg of- Nothing -> ""- Just v -> " version " ++ prettyShow v--hackageUrl :: String-hackageUrl = "http://hackage.haskell.org/package/"--type ResolvedDependency = (Dependency, DependencyResolution)--data DependencyResolution- -- | An external dependency from the package database, OR an- -- internal dependency which we are getting from the package- -- database.- = ExternalDependency PreExistingComponent- -- | An internal dependency ('PackageId' should be a library name)- -- which we are going to have to build. (The- -- 'PackageId' here is a hack to get a modest amount of- -- polymorphism out of the 'Package' typeclass.)- | InternalDependency PackageId--data FailedDependency = DependencyNotExists PackageName- | DependencyMissingInternal PackageName LibraryName- | DependencyNoVersion Dependency---- | Test for a package dependency and record the version we have installed.-selectDependency :: PackageId -- ^ Package id of current package- -> Set LibraryName -- ^ package libraries- -> InstalledPackageIndex -- ^ Installed packages- -> Map (PackageName, ComponentName) InstalledPackageInfo- -- ^ Packages for which we have been given specific deps to- -- use- -> UseExternalInternalDeps -- ^ Are we configuring a- -- single component?- -> Dependency- -> [Either FailedDependency DependencyResolution]-selectDependency pkgid internalIndex installedIndex requiredDepsMap- use_external_internal_deps- (Dependency dep_pkgname vr libs) =- -- If the dependency specification matches anything in the internal package- -- index, then we prefer that match to anything in the second.- -- For example:- --- -- Name: MyLibrary- -- Version: 0.1- -- Library- -- ..- -- Executable my-exec- -- build-depends: MyLibrary- --- -- We want "build-depends: MyLibrary" always to match the internal library- -- even if there is a newer installed library "MyLibrary-0.2".- if dep_pkgname == pn- then- if use_external_internal_deps- then do_external_internal <$> NES.toList libs- else do_internal <$> NES.toList libs- else- do_external_external <$> NES.toList libs- where- pn = packageName pkgid-- -- It's an internal library, and we're not per-component build- do_internal lib- | Set.member lib internalIndex- = Right $ InternalDependency $ PackageIdentifier dep_pkgname $ packageVersion pkgid-- | otherwise- = Left $ DependencyMissingInternal dep_pkgname lib-- -- We have to look it up externally- do_external_external :: LibraryName -> Either FailedDependency DependencyResolution- do_external_external lib = do- ipi <- case Map.lookup (dep_pkgname, CLibName lib) requiredDepsMap of- -- If we know the exact pkg to use, then use it.- Just pkginstance -> Right pkginstance- -- Otherwise we just pick an arbitrary instance of the latest version.- Nothing -> case pickLastIPI $ PackageIndex.lookupDependency installedIndex dep_pkgname vr of- Nothing -> Left (DependencyNotExists dep_pkgname)- Just pkg -> Right pkg- return $ ExternalDependency $ ipiToPreExistingComponent ipi-- do_external_internal :: LibraryName -> Either FailedDependency DependencyResolution- do_external_internal lib = do- ipi <- case Map.lookup (dep_pkgname, CLibName lib) requiredDepsMap of- -- If we know the exact pkg to use, then use it.- Just pkginstance -> Right pkginstance- Nothing -> case pickLastIPI $ PackageIndex.lookupInternalDependency installedIndex pn vr lib of- -- It's an internal library, being looked up externally- Nothing -> Left (DependencyMissingInternal dep_pkgname lib)- Just pkg -> Right pkg- return $ ExternalDependency $ ipiToPreExistingComponent ipi-- pickLastIPI :: [(Version, [InstalledPackageInfo])] -> Maybe InstalledPackageInfo- pickLastIPI pkgs = safeHead . snd . last =<< nonEmpty pkgs--reportSelectedDependencies :: Verbosity- -> [ResolvedDependency] -> IO ()-reportSelectedDependencies verbosity deps =- info verbosity $ unlines- [ "Dependency " ++ prettyShow (simplifyDependency dep)- ++ ": using " ++ prettyShow pkgid- | (dep, resolution) <- deps- , let pkgid = case resolution of- ExternalDependency pkg' -> packageId pkg'- InternalDependency pkgid' -> pkgid' ]--reportFailedDependencies :: Verbosity -> [FailedDependency] -> IO ()-reportFailedDependencies _ [] = return ()-reportFailedDependencies verbosity failed =- die' verbosity (intercalate "\n\n" (map reportFailedDependency failed))-- where- reportFailedDependency (DependencyNotExists pkgname) =- "there is no version of " ++ prettyShow pkgname ++ " installed.\n"- ++ "Perhaps you need to download and install it from\n"- ++ hackageUrl ++ prettyShow pkgname ++ "?"-- reportFailedDependency (DependencyMissingInternal pkgname lib) =- "internal dependency " ++ prettyShow (prettyLibraryNameComponent lib) ++ " not installed.\n"- ++ "Perhaps you need to configure and install it first?\n"- ++ "(This library was defined by " ++ prettyShow pkgname ++ ")"-- reportFailedDependency (DependencyNoVersion dep) =- "cannot satisfy dependency " ++ prettyShow (simplifyDependency dep) ++ "\n"---- | List all installed packages in the given package databases.--- Non-existent package databases do not cause errors, they just get skipped--- with a warning and treated as empty ones, since technically they do not--- contain any package.-getInstalledPackages :: Verbosity -> Compiler- -> PackageDBStack -- ^ The stack of package databases.- -> ProgramDb- -> IO InstalledPackageIndex-getInstalledPackages verbosity comp packageDBs progdb = do- when (null packageDBs) $- die' verbosity $ "No package databases have been specified. If you use "- ++ "--package-db=clear, you must follow it with --package-db= "- ++ "with 'global', 'user' or a specific file."-- info verbosity "Reading installed packages..."- -- do not check empty packagedbs (ghc-pkg would error out)- packageDBs' <- filterM packageDBExists packageDBs- case compilerFlavor comp of- GHC -> GHC.getInstalledPackages verbosity comp packageDBs' progdb- GHCJS -> GHCJS.getInstalledPackages verbosity packageDBs' progdb- UHC -> UHC.getInstalledPackages verbosity comp packageDBs' progdb- HaskellSuite {} ->- HaskellSuite.getInstalledPackages verbosity packageDBs' progdb- flv -> die' verbosity $ "don't know how to find the installed packages for "- ++ prettyShow flv- where- packageDBExists (SpecificPackageDB path) = do- exists <- doesPathExist path- unless exists $- warn verbosity $ "Package db " <> path <> " does not exist yet"- return exists- -- Checking the user and global package dbs is more complicated and needs- -- way more data. Also ghc-pkg won't error out unless the user/global- -- pkgdb is overridden with an empty one, so we just don't check for them.- packageDBExists UserPackageDB = pure True- packageDBExists GlobalPackageDB = pure True---- | Like 'getInstalledPackages', but for a single package DB.------ NB: Why isn't this always a fall through to 'getInstalledPackages'?--- That is because 'getInstalledPackages' performs some sanity checks--- on the package database stack in question. However, when sandboxes--- are involved these sanity checks are not desirable.-getPackageDBContents :: Verbosity -> Compiler- -> PackageDB -> ProgramDb- -> IO InstalledPackageIndex-getPackageDBContents verbosity comp packageDB progdb = do- info verbosity "Reading installed packages..."- case compilerFlavor comp of- GHC -> GHC.getPackageDBContents verbosity packageDB progdb- GHCJS -> GHCJS.getPackageDBContents verbosity packageDB progdb- -- For other compilers, try to fall back on 'getInstalledPackages'.- _ -> getInstalledPackages verbosity comp [packageDB] progdb----- | A set of files (or directories) that can be monitored to detect when--- there might have been a change in the installed packages.----getInstalledPackagesMonitorFiles :: Verbosity -> Compiler- -> PackageDBStack- -> ProgramDb -> Platform- -> IO [FilePath]-getInstalledPackagesMonitorFiles verbosity comp packageDBs progdb platform =- case compilerFlavor comp of- GHC -> GHC.getInstalledPackagesMonitorFiles- verbosity platform progdb packageDBs- other -> do- warn verbosity $ "don't know how to find change monitoring files for "- ++ "the installed package databases for " ++ prettyShow other- return []---- | The user interface specifies the package dbs to use with a combination of--- @--global@, @--user@ and @--package-db=global|user|clear|$file@.--- This function combines the global/user flag and interprets the package-db--- flag into a single package db stack.----interpretPackageDbFlags :: Bool -> [Maybe PackageDB] -> PackageDBStack-interpretPackageDbFlags userInstall specificDBs =- extra initialStack specificDBs- where- initialStack | userInstall = [GlobalPackageDB, UserPackageDB]- | otherwise = [GlobalPackageDB]-- extra dbs' [] = dbs'- extra _ (Nothing:dbs) = extra [] dbs- extra dbs' (Just db:dbs) = extra (dbs' ++ [db]) dbs---- We are given both --constraint="foo < 2.0" style constraints and also--- specific packages to pick via --dependency="foo=foo-2.0-177d5cdf20962d0581".------ When finalising the package we have to take into account the specific--- installed deps we've been given, and the finalise function expects--- constraints, so we have to translate these deps into version constraints.------ But after finalising we then have to make sure we pick the right specific--- deps in the end. So we still need to remember which installed packages to--- pick.-combinedConstraints- :: [PackageVersionConstraint]- -> [GivenComponent]- -> InstalledPackageIndex- -> Either String ([PackageVersionConstraint],- Map (PackageName, ComponentName) InstalledPackageInfo)-combinedConstraints constraints dependencies installedPackages = do-- when (not (null badComponentIds)) $- Left $ render $ text "The following package dependencies were requested"- $+$ nest 4 (dispDependencies badComponentIds)- $+$ text "however the given installed package instance does not exist."-- --TODO: we don't check that all dependencies are used!-- return (allConstraints, idConstraintMap)-- where- allConstraints :: [PackageVersionConstraint]- allConstraints = constraints- ++ [ thisPackageVersionConstraint (packageId pkg)- | (_, _, _, Just pkg) <- dependenciesPkgInfo ]-- idConstraintMap :: Map (PackageName, ComponentName) InstalledPackageInfo- idConstraintMap = Map.fromList- -- NB: do NOT use the packageName from- -- dependenciesPkgInfo!- [ ((pn, cname), pkg)- | (pn, cname, _, Just pkg) <- dependenciesPkgInfo ]-- -- The dependencies along with the installed package info, if it exists- dependenciesPkgInfo :: [(PackageName, ComponentName, ComponentId,- Maybe InstalledPackageInfo)]- dependenciesPkgInfo =- [ (pkgname, CLibName lname, cid, mpkg)- | GivenComponent pkgname lname cid <- dependencies- , let mpkg = PackageIndex.lookupComponentId- installedPackages cid- ]-- -- If we looked up a package specified by an installed package id- -- (i.e. someone has written a hash) and didn't find it then it's- -- an error.- badComponentIds =- [ (pkgname, cname, cid)- | (pkgname, cname, cid, Nothing) <- dependenciesPkgInfo ]-- dispDependencies deps =- hsep [ text "--dependency="- <<>> quotes- (pretty pkgname- <<>> case cname of- CLibName LMainLibName -> ""- CLibName (LSubLibName n) -> ":" <<>> pretty n- _ -> ":" <<>> pretty cname- <<>> char '='- <<>> pretty cid)- | (pkgname, cname, cid) <- deps ]---- -------------------------------------------------------------------------------- Configuring program dependencies--configureRequiredPrograms :: Verbosity -> [LegacyExeDependency] -> ProgramDb- -> IO ProgramDb-configureRequiredPrograms verbosity deps progdb =- foldM (configureRequiredProgram verbosity) progdb deps---- | Configure a required program, ensuring that it exists in the PATH--- (or where the user has specified the program must live) and making it--- available for use via the 'ProgramDb' interface. If the program is--- known (exists in the input 'ProgramDb'), we will make sure that the--- program matches the required version; otherwise we will accept--- any version of the program and assume that it is a simpleProgram.-configureRequiredProgram :: Verbosity -> ProgramDb -> LegacyExeDependency- -> IO ProgramDb-configureRequiredProgram verbosity progdb- (LegacyExeDependency progName verRange) =- case lookupKnownProgram progName progdb of- Nothing ->- -- Try to configure it as a 'simpleProgram' automatically- --- -- There's a bit of a story behind this line. In old versions- -- of Cabal, there were only internal build-tools dependencies. So the- -- behavior in this case was:- --- -- - If a build-tool dependency was internal, don't do- -- any checking.- --- -- - If it was external, call 'configureRequiredProgram' to- -- "configure" the executable. In particular, if- -- the program was not "known" (present in 'ProgramDb'),- -- then we would just error. This was fine, because- -- the only way a program could be executed from 'ProgramDb'- -- is if some library code from Cabal actually called it,- -- and the pre-existing Cabal code only calls known- -- programs from 'defaultProgramDb', and so if it- -- is calling something else, you have a Custom setup- -- script, and in that case you are expected to register- -- the program you want to call in the ProgramDb.- --- -- OK, so that was fine, until I (ezyang, in 2016) refactored- -- Cabal to support per-component builds. In this case, what- -- was previously an internal build-tool dependency now became- -- an external one, and now previously "internal" dependencies- -- are now external. But these are permitted to exist even- -- when they are not previously configured (something that- -- can only occur by a Custom script.)- --- -- So, I decided, "Fine, let's just accept these in any- -- case." Thus this line. The alternative would have been to- -- somehow detect when a build-tools dependency was "internal" (by- -- looking at the unflattened package description) but this- -- would also be incompatible with future work to support- -- external executable dependencies: we definitely cannot- -- assume they will be preinitialized in the 'ProgramDb'.- configureProgram verbosity (simpleProgram progName) progdb- Just prog- -- requireProgramVersion always requires the program have a version- -- but if the user says "build-depends: foo" ie no version constraint- -- then we should not fail if we cannot discover the program version.- | verRange == anyVersion -> do- (_, progdb') <- requireProgram verbosity prog progdb- return progdb'- | otherwise -> do- (_, _, progdb') <- requireProgramVersion verbosity prog verRange progdb- return progdb'---- -------------------------------------------------------------------------------- Configuring pkg-config package dependencies--configurePkgconfigPackages :: Verbosity -> PackageDescription- -> ProgramDb -> ComponentRequestedSpec- -> IO (PackageDescription, ProgramDb)-configurePkgconfigPackages verbosity pkg_descr progdb enabled- | null allpkgs = return (pkg_descr, progdb)- | otherwise = do- (_, _, progdb') <- requireProgramVersion- (lessVerbose verbosity) pkgConfigProgram- (orLaterVersion $ mkVersion [0,9,0]) progdb- traverse_ requirePkg allpkgs- mlib' <- traverse addPkgConfigBILib (library pkg_descr)- libs' <- traverse addPkgConfigBILib (subLibraries pkg_descr)- exes' <- traverse addPkgConfigBIExe (executables pkg_descr)- tests' <- traverse addPkgConfigBITest (testSuites pkg_descr)- benches' <- traverse addPkgConfigBIBench (benchmarks pkg_descr)- let pkg_descr' = pkg_descr { library = mlib',- subLibraries = libs', executables = exes',- testSuites = tests', benchmarks = benches' }- return (pkg_descr', progdb')-- where- allpkgs = concatMap pkgconfigDepends (enabledBuildInfos pkg_descr enabled)- pkgconfig = getDbProgramOutput (lessVerbose verbosity)- pkgConfigProgram progdb-- requirePkg dep@(PkgconfigDependency pkgn range) = do- version <- pkgconfig ["--modversion", pkg]- `catchIO` (\_ -> die' verbosity notFound)- `catchExit` (\_ -> die' verbosity notFound)- let trim = dropWhile isSpace . dropWhileEnd isSpace- let v = PkgconfigVersion (toUTF8BS $ trim version)- if not (withinPkgconfigVersionRange v range)- then die' verbosity (badVersion v)- else info verbosity (depSatisfied v)- where- notFound = "The pkg-config package '" ++ pkg ++ "'"- ++ versionRequirement- ++ " is required but it could not be found."- badVersion v = "The pkg-config package '" ++ pkg ++ "'"- ++ versionRequirement- ++ " is required but the version installed on the"- ++ " system is version " ++ prettyShow v- depSatisfied v = "Dependency " ++ prettyShow dep- ++ ": using version " ++ prettyShow v-- versionRequirement- | isAnyPkgconfigVersion range = ""- | otherwise = " version " ++ prettyShow range-- pkg = unPkgconfigName pkgn-- -- Adds pkgconfig dependencies to the build info for a component- addPkgConfigBI compBI setCompBI comp = do- bi <- pkgconfigBuildInfo (pkgconfigDepends (compBI comp))- return $ setCompBI comp (compBI comp `mappend` bi)-- -- Adds pkgconfig dependencies to the build info for a library- addPkgConfigBILib = addPkgConfigBI libBuildInfo $- \lib bi -> lib { libBuildInfo = bi }-- -- Adds pkgconfig dependencies to the build info for an executable- addPkgConfigBIExe = addPkgConfigBI buildInfo $- \exe bi -> exe { buildInfo = bi }-- -- Adds pkgconfig dependencies to the build info for a test suite- addPkgConfigBITest = addPkgConfigBI testBuildInfo $- \test bi -> test { testBuildInfo = bi }-- -- Adds pkgconfig dependencies to the build info for a benchmark- addPkgConfigBIBench = addPkgConfigBI benchmarkBuildInfo $- \bench bi -> bench { benchmarkBuildInfo = bi }-- pkgconfigBuildInfo :: [PkgconfigDependency] -> IO BuildInfo- pkgconfigBuildInfo [] = return mempty- pkgconfigBuildInfo pkgdeps = do- let pkgs = nub [ prettyShow pkg | PkgconfigDependency pkg _ <- pkgdeps ]- ccflags <- pkgconfig ("--cflags" : pkgs)- ldflags <- pkgconfig ("--libs" : pkgs)- return (ccLdOptionsBuildInfo (words ccflags) (words ldflags))---- | Makes a 'BuildInfo' from C compiler and linker flags.------ This can be used with the output from configuration programs like pkg-config--- and similar package-specific programs like mysql-config, freealut-config etc.--- For example:------ > ccflags <- getDbProgramOutput verbosity prog progdb ["--cflags"]--- > ldflags <- getDbProgramOutput verbosity prog progdb ["--libs"]--- > return (ccldOptionsBuildInfo (words ccflags) (words ldflags))----ccLdOptionsBuildInfo :: [String] -> [String] -> BuildInfo-ccLdOptionsBuildInfo cflags ldflags =- let (includeDirs', cflags') = partition ("-I" `isPrefixOf`) cflags- (extraLibs', ldflags') = partition ("-l" `isPrefixOf`) ldflags- (extraLibDirs', ldflags'') = partition ("-L" `isPrefixOf`) ldflags'- in mempty {- includeDirs = map (drop 2) includeDirs',- extraLibs = map (drop 2) extraLibs',- extraLibDirs = map (drop 2) extraLibDirs',- ccOptions = cflags',- ldOptions = ldflags''- }---- -------------------------------------------------------------------------------- Determining the compiler details--configCompilerAuxEx :: ConfigFlags- -> IO (Compiler, Platform, ProgramDb)-configCompilerAuxEx cfg = configCompilerEx (flagToMaybe $ configHcFlavor cfg)- (flagToMaybe $ configHcPath cfg)- (flagToMaybe $ configHcPkg cfg)- programDb- (fromFlag (configVerbosity cfg))- where- programDb = mkProgramDb cfg defaultProgramDb--configCompilerEx :: Maybe CompilerFlavor -> Maybe FilePath -> Maybe FilePath- -> ProgramDb -> Verbosity- -> IO (Compiler, Platform, ProgramDb)-configCompilerEx Nothing _ _ _ verbosity = die' verbosity "Unknown compiler"-configCompilerEx (Just hcFlavor) hcPath hcPkg progdb verbosity = do- (comp, maybePlatform, programDb) <- case hcFlavor of- GHC -> GHC.configure verbosity hcPath hcPkg progdb- GHCJS -> GHCJS.configure verbosity hcPath hcPkg progdb- UHC -> UHC.configure verbosity hcPath hcPkg progdb- HaskellSuite {} -> HaskellSuite.configure verbosity hcPath hcPkg progdb- _ -> die' verbosity "Unknown compiler"- return (comp, fromMaybe buildPlatform maybePlatform, programDb)---- -------------------------------------------------------------------------------- Testing C lib and header dependencies---- Try to build a test C program which includes every header and links every--- lib. If that fails, try to narrow it down by preprocessing (only) and linking--- with individual headers and libs. If none is the obvious culprit then give a--- generic error message.--- TODO: produce a log file from the compiler errors, if any.-checkForeignDeps :: PackageDescription -> LocalBuildInfo -> Verbosity -> IO ()-checkForeignDeps pkg lbi verbosity =- ifBuildsWith allHeaders (commonCcArgs ++ makeLdArgs allLibs) -- I'm feeling- -- lucky- (return ())- (do missingLibs <- findMissingLibs- missingHdr <- findOffendingHdr- explainErrors missingHdr missingLibs)- where- allHeaders = collectField includes- allLibs = collectField extraLibs-- ifBuildsWith headers args success failure = do- checkDuplicateHeaders- ok <- builds (makeProgram headers) args- if ok then success else failure-- -- Ensure that there is only one header with a given name- -- in either the generated (most likely by `configure`)- -- build directory (e.g. `dist/build`) or in the source directory.- --- -- If it exists in both, we'll remove the one in the source- -- directory, as the generated should take precedence.- --- -- C compilers like to prefer source local relative includes,- -- so the search paths provided to the compiler via -I are- -- ignored if the included file can be found relative to the- -- including file. As such we need to take drastic measures- -- and delete the offending file in the source directory.- checkDuplicateHeaders = do- let relIncDirs = filter (not . isAbsolute) (collectField includeDirs)- isHeader = isSuffixOf ".h"- genHeaders <- for relIncDirs $ \dir ->- fmap (dir </>) . filter isHeader <$>- listDirectory (buildDir lbi </> dir) `catchIO` (\_ -> return [])- srcHeaders <- for relIncDirs $ \dir ->- fmap (dir </>) . filter isHeader <$>- listDirectory (baseDir lbi </> dir) `catchIO` (\_ -> return [])- let commonHeaders = concat genHeaders `intersect` concat srcHeaders- for_ commonHeaders $ \hdr -> do- warn verbosity $ "Duplicate header found in "- ++ (buildDir lbi </> hdr)- ++ " and "- ++ (baseDir lbi </> hdr)- ++ "; removing "- ++ (baseDir lbi </> hdr)- removeFile (baseDir lbi </> hdr)-- findOffendingHdr =- ifBuildsWith allHeaders ccArgs- (return Nothing)- (go . Unsafe.tail . inits $ allHeaders) -- inits always contains at least []- where- go [] = return Nothing -- cannot happen- go (hdrs:hdrsInits) =- -- Try just preprocessing first- ifBuildsWith hdrs cppArgs- -- If that works, try compiling too- (ifBuildsWith hdrs ccArgs- (go hdrsInits)- (return . fmap Right . safeLast $ hdrs))- (return . fmap Left . safeLast $ hdrs)--- cppArgs = "-E":commonCppArgs -- preprocess only- ccArgs = "-c":commonCcArgs -- don't try to link-- findMissingLibs = ifBuildsWith [] (makeLdArgs allLibs)- (return [])- (filterM (fmap not . libExists) allLibs)-- libExists lib = builds (makeProgram []) (makeLdArgs [lib])-- baseDir lbi' = fromMaybe "." (takeDirectory <$> cabalFilePath lbi')-- commonCppArgs = platformDefines lbi- -- TODO: This is a massive hack, to work around the- -- fact that the test performed here should be- -- PER-component (c.f. the "I'm Feeling Lucky"; we- -- should NOT be glomming everything together.)- ++ [ "-I" ++ buildDir lbi </> "autogen" ]- -- `configure' may generate headers in the build directory- ++ [ "-I" ++ buildDir lbi </> dir- | dir <- ordNub (collectField includeDirs)- , not (isAbsolute dir)]- -- we might also reference headers from the- -- packages directory.- ++ [ "-I" ++ baseDir lbi </> dir- | dir <- ordNub (collectField includeDirs)- , not (isAbsolute dir)]- ++ [ "-I" ++ dir | dir <- ordNub (collectField includeDirs)- , isAbsolute dir]- ++ ["-I" ++ baseDir lbi]- ++ collectField cppOptions- ++ collectField ccOptions- ++ [ "-I" ++ dir- | dir <- ordNub [ dir- | dep <- deps- , dir <- IPI.includeDirs dep ]- -- dedupe include dirs of dependencies- -- to prevent quadratic blow-up- ]- ++ [ opt- | dep <- deps- , opt <- IPI.ccOptions dep ]-- commonCcArgs = commonCppArgs- ++ collectField ccOptions- ++ [ opt- | dep <- deps- , opt <- IPI.ccOptions dep ]-- commonLdArgs = [ "-L" ++ dir- | dir <- ordNub (collectField extraLibDirs) ]- ++ collectField ldOptions- ++ [ "-L" ++ dir- | dir <- ordNub [ dir- | dep <- deps- , dir <- IPI.libraryDirs dep ]- ]- --TODO: do we also need dependent packages' ld options?- makeLdArgs libs = [ "-l"++lib | lib <- libs ] ++ commonLdArgs-- makeProgram hdrs = unlines $- [ "#include \"" ++ hdr ++ "\"" | hdr <- hdrs ] ++- ["int main(int argc, char** argv) { return 0; }"]-- collectField f = concatMap f allBi- allBi = enabledBuildInfos pkg (componentEnabledSpec lbi)- deps = PackageIndex.topologicalOrder (installedPkgs lbi)-- builds program args = do- tempDir <- getTemporaryDirectory- withTempFile tempDir ".c" $ \cName cHnd ->- withTempFile tempDir "" $ \oNname oHnd -> do- hPutStrLn cHnd program- hClose cHnd- hClose oHnd- _ <- getDbProgramOutput verbosity- gccProgram (withPrograms lbi) (cName:"-o":oNname:args)- return True- `catchIO` (\_ -> return False)- `catchExit` (\_ -> return False)-- explainErrors Nothing [] = return () -- should be impossible!- explainErrors _ _- | isNothing . lookupProgram gccProgram . withPrograms $ lbi-- = die' verbosity $ unlines- [ "No working gcc",- "This package depends on foreign library but we cannot "- ++ "find a working C compiler. If you have it in a "- ++ "non-standard location you can use the --with-gcc "- ++ "flag to specify it." ]-- explainErrors hdr libs = die' verbosity $ unlines $- [ if plural- then "Missing dependencies on foreign libraries:"- else "Missing dependency on a foreign library:"- | missing ]- ++ case hdr of- Just (Left h) -> ["* Missing (or bad) header file: " ++ h ]- _ -> []- ++ case libs of- [] -> []- [lib] -> ["* Missing (or bad) C library: " ++ lib]- _ -> ["* Missing (or bad) C libraries: " ++- intercalate ", " libs]- ++ [if plural then messagePlural else messageSingular | missing]- ++ case hdr of- Just (Left _) -> [ headerCppMessage ]- Just (Right h) -> [ (if missing then "* " else "")- ++ "Bad header file: " ++ h- , headerCcMessage ]- _ -> []-- where- plural = length libs >= 2- -- Is there something missing? (as opposed to broken)- missing = not (null libs)- || case hdr of Just (Left _) -> True; _ -> False-- messageSingular =- "This problem can usually be solved by installing the system "- ++ "package that provides this library (you may need the "- ++ "\"-dev\" version). If the library is already installed "- ++ "but in a non-standard location then you can use the flags "- ++ "--extra-include-dirs= and --extra-lib-dirs= to specify "- ++ "where it is."- ++ "If the library file does exist, it may contain errors that "- ++ "are caught by the C compiler at the preprocessing stage. "- ++ "In this case you can re-run configure with the verbosity "- ++ "flag -v3 to see the error messages."- messagePlural =- "This problem can usually be solved by installing the system "- ++ "packages that provide these libraries (you may need the "- ++ "\"-dev\" versions). If the libraries are already installed "- ++ "but in a non-standard location then you can use the flags "- ++ "--extra-include-dirs= and --extra-lib-dirs= to specify "- ++ "where they are."- ++ "If the library files do exist, it may contain errors that "- ++ "are caught by the C compiler at the preprocessing stage. "- ++ "In this case you can re-run configure with the verbosity "- ++ "flag -v3 to see the error messages."- headerCppMessage =- "If the header file does exist, it may contain errors that "- ++ "are caught by the C compiler at the preprocessing stage. "- ++ "In this case you can re-run configure with the verbosity "- ++ "flag -v3 to see the error messages."- headerCcMessage =- "The header file contains a compile error. "- ++ "You can re-run configure with the verbosity flag "- ++ "-v3 to see the error messages from the C compiler."---- | Output package check warnings and errors. Exit if any errors.-checkPackageProblems :: Verbosity- -> FilePath- -- ^ Path to the @.cabal@ file's directory- -> GenericPackageDescription- -> PackageDescription- -> IO ()-checkPackageProblems verbosity dir gpkg pkg = do- ioChecks <- checkPackageFiles verbosity pkg dir- let pureChecks = checkPackage gpkg (Just pkg)- errors = [ e | PackageBuildImpossible e <- pureChecks ++ ioChecks ]- warnings = [ w | PackageBuildWarning w <- pureChecks ++ ioChecks ]- if null errors- then traverse_ (warn verbosity) warnings- else die' verbosity (intercalate "\n\n" errors)---- | Preform checks if a relocatable build is allowed-checkRelocatable :: Verbosity- -> PackageDescription- -> LocalBuildInfo- -> IO ()-checkRelocatable verbosity pkg lbi- = sequence_ [ checkOS- , checkCompiler- , packagePrefixRelative- , depsPrefixRelative- ]- where- -- Check if the OS support relocatable builds.- --- -- If you add new OS' to this list, and your OS supports dynamic libraries- -- and RPATH, make sure you add your OS to RPATH-support list of:- -- Distribution.Simple.GHC.getRPaths- checkOS- = unless (os `elem` [ OSX, Linux ])- $ die' verbosity $ "Operating system: " ++ prettyShow os ++- ", does not support relocatable builds"- where- (Platform _ os) = hostPlatform lbi-- -- Check if the Compiler support relocatable builds- checkCompiler- = unless (compilerFlavor comp `elem` [ GHC ])- $ die' verbosity $ "Compiler: " ++ show comp ++- ", does not support relocatable builds"- where- comp = compiler lbi-- -- Check if all the install dirs are relative to same prefix- packagePrefixRelative- = unless (relativeInstallDirs installDirs)- $ die' verbosity $ "Installation directories are not prefix_relative:\n" ++- show installDirs- where- -- NB: should be good enough to check this against the default- -- component ID, but if we wanted to be strictly correct we'd- -- check for each ComponentId.- installDirs = absoluteInstallDirs pkg lbi NoCopyDest- p = prefix installDirs- relativeInstallDirs (InstallDirs {..}) =- all isJust- (fmap (stripPrefix p)- [ bindir, libdir, dynlibdir, libexecdir, includedir, datadir- , docdir, mandir, htmldir, haddockdir, sysconfdir] )-- -- Check if the library dirs of the dependencies that are in the package- -- database to which the package is installed are relative to the- -- prefix of the package- depsPrefixRelative = do- pkgr <- GHC.pkgRoot verbosity lbi (registrationPackageDB (withPackageDB lbi))- traverse_ (doCheck pkgr) ipkgs- where- doCheck pkgr ipkg- | maybe False (== pkgr) (IPI.pkgRoot ipkg)- = for_ (IPI.libraryDirs ipkg) $ \libdir -> do- -- When @prefix@ is not under @pkgroot@,- -- @shortRelativePath prefix pkgroot@ will return a path with- -- @..@s and following check will fail without @canonicalizePath@.- canonicalized <- canonicalizePath libdir- unless (p `isPrefixOf` canonicalized) $- die' verbosity $ msg libdir- | otherwise- = return ()- -- NB: should be good enough to check this against the default- -- component ID, but if we wanted to be strictly correct we'd- -- check for each ComponentId.- installDirs = absoluteInstallDirs pkg lbi NoCopyDest- p = prefix installDirs- ipkgs = PackageIndex.allPackages (installedPkgs lbi)- msg l = "Library directory of a dependency: " ++ show l ++- "\nis not relative to the installation prefix:\n" ++- show p---- -------------------------------------------------------------------------------- Testing foreign library requirements--unsupportedForeignLibs :: Compiler -> Platform -> [ForeignLib] -> [String]-unsupportedForeignLibs comp platform =- mapMaybe (checkForeignLibSupported comp platform)--checkForeignLibSupported :: Compiler -> Platform -> ForeignLib -> Maybe String-checkForeignLibSupported comp platform flib = go (compilerFlavor comp)- where- go :: CompilerFlavor -> Maybe String- go GHC- | compilerVersion comp < mkVersion [7,8] = unsupported [- "Building foreign libraires is only supported with GHC >= 7.8"- ]- | otherwise = goGhcPlatform platform- go _ = unsupported [- "Building foreign libraries is currently only supported with ghc"- ]-- goGhcPlatform :: Platform -> Maybe String- goGhcPlatform (Platform X86_64 OSX ) = goGhcOsx (foreignLibType flib)- goGhcPlatform (Platform _ Linux ) = goGhcLinux (foreignLibType flib)- goGhcPlatform (Platform I386 Windows) = goGhcWindows (foreignLibType flib)- goGhcPlatform (Platform X86_64 Windows) = goGhcWindows (foreignLibType flib)- goGhcPlatform _ = unsupported [- "Building foreign libraries is currently only supported on OSX, "- , "Linux and Windows"- ]-- goGhcOsx :: ForeignLibType -> Maybe String- goGhcOsx ForeignLibNativeShared- | not (null (foreignLibModDefFile flib)) = unsupported [- "Module definition file not supported on OSX"- ]- | not (null (foreignLibVersionInfo flib)) = unsupported [- "Foreign library versioning not currently supported on OSX"- ]- | otherwise =- Nothing- goGhcOsx _ = unsupported [- "We can currently only build shared foreign libraries on OSX"- ]-- goGhcLinux :: ForeignLibType -> Maybe String- goGhcLinux ForeignLibNativeShared- | not (null (foreignLibModDefFile flib)) = unsupported [- "Module definition file not supported on Linux"- ]- | not (null (foreignLibVersionInfo flib))- && not (null (foreignLibVersionLinux flib)) = unsupported [- "You must not specify both lib-version-info and lib-version-linux"- ]- | otherwise =- Nothing- goGhcLinux _ = unsupported [- "We can currently only build shared foreign libraries on Linux"- ]-- goGhcWindows :: ForeignLibType -> Maybe String- goGhcWindows ForeignLibNativeShared- | not standalone = unsupported [- "We can currently only build standalone libraries on Windows. Use\n"- , " if os(Windows)\n"- , " options: standalone\n"- , "in your foreign-library stanza."- ]- | not (null (foreignLibVersionInfo flib)) = unsupported [- "Foreign library versioning not currently supported on Windows.\n"- , "You can specify module definition files in the mod-def-file field."- ]- | otherwise =- Nothing- goGhcWindows _ = unsupported [- "We can currently only build shared foreign libraries on Windows"- ]+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}++-----------------------------------------------------------------------------++-- |+-- Module : Distribution.Simple.Configure+-- Copyright : Isaac Jones 2003-2005+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- This deals with the /configure/ phase. It provides the 'configure' action+-- which is given the package description and configure flags. It then tries+-- to: configure the compiler; resolves any conditionals in the package+-- description; resolve the package dependencies; check if all the extensions+-- used by this package are supported by the compiler; check that all the build+-- tools are available (including version checks if appropriate); checks for+-- any required @pkg-config@ packages (updating the 'BuildInfo' with the+-- results)+--+-- Then based on all this it saves the info in the 'LocalBuildInfo' and writes+-- it out to the @dist\/setup-config@ file. It also displays various details to+-- the user, the amount of information displayed depending on the verbosity+-- level.+module Distribution.Simple.Configure+ ( configure+ , configure_setupHooks+ , writePersistBuildConfig+ , getConfigStateFile+ , getPersistBuildConfig+ , checkPersistBuildConfigOutdated+ , tryGetPersistBuildConfig+ , maybeGetPersistBuildConfig+ , findDistPref+ , findDistPrefOrDefault+ , getInternalLibraries+ , computeComponentId+ , computeCompatPackageKey+ , localBuildInfoFile+ , getInstalledPackages+ , getInstalledPackagesMonitorFiles+ , getInstalledPackagesById+ , getPackageDBContents+ , configCompiler+ , configCompilerEx+ , configCompilerAuxEx+ , configCompilerProgDb+ , computeEffectiveProfiling+ , ccLdOptionsBuildInfo+ , checkForeignDeps+ , interpretPackageDbFlags+ , ConfigStateFileError (..)+ , tryGetConfigStateFile+ , platformDefines+ ) where++import Control.Monad+import Distribution.Compat.Prelude+import Prelude ()++import Distribution.Backpack.Configure+import Distribution.Backpack.ConfiguredComponent (newPackageDepsBehaviour)+import Distribution.Backpack.DescribeUnitId+import Distribution.Backpack.Id+import Distribution.Backpack.PreExistingComponent+import qualified Distribution.Compat.Graph as Graph+import Distribution.Compat.Stack+import Distribution.Compiler+import Distribution.InstalledPackageInfo (InstalledPackageInfo)+import qualified Distribution.InstalledPackageInfo as IPI+import Distribution.Package+import Distribution.PackageDescription+import Distribution.PackageDescription.Check hiding (doesFileExist)+import Distribution.PackageDescription.Configuration+import Distribution.PackageDescription.PrettyPrint+import Distribution.Simple.BuildTarget+import Distribution.Simple.BuildToolDepends+import Distribution.Simple.BuildWay+import Distribution.Simple.Compiler+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.PackageIndex (InstalledPackageIndex, lookupUnitId)+import qualified Distribution.Simple.PackageIndex as PackageIndex+import Distribution.Simple.PreProcess+import Distribution.Simple.Program+import Distribution.Simple.Program.Db+ ( ProgramDb (..)+ , lookupProgramByName+ , modifyProgramSearchPath+ , prependProgramSearchPath+ , updateConfiguredProgs+ )+import Distribution.Simple.Setup.Common as Setup+import Distribution.Simple.Setup.Config as Setup+import Distribution.Simple.SetupHooks.Internal+ ( ConfigureHooks (..)+ , applyComponentDiffs+ , noConfigureHooks+ )+import qualified Distribution.Simple.SetupHooks.Internal as SetupHooks+import Distribution.Simple.Utils+import Distribution.System+import Distribution.Types.ComponentRequestedSpec+import Distribution.Types.DependencySatisfaction (DependencySatisfaction (..))+import Distribution.Types.GivenComponent+import qualified Distribution.Types.LocalBuildConfig as LBC+import Distribution.Types.LocalBuildInfo+import Distribution.Types.MissingDependencyReason (MissingDependencyReason (..))+import Distribution.Types.PackageVersionConstraint+import Distribution.Utils.LogProgress+import Distribution.Utils.NubList+import Distribution.Utils.String (trim)+import Distribution.Verbosity+import Distribution.Version++import qualified Distribution.Simple.GHC as GHC+import qualified Distribution.Simple.GHCJS as GHCJS+import qualified Distribution.Simple.UHC as UHC++import Control.Exception+ ( try+ )+import qualified Data.ByteString as BS+import Data.ByteString.Lazy (ByteString)+import qualified Data.ByteString.Lazy.Char8 as BLC8+import Data.List+ ( intersect+ , stripPrefix+ , (\\)+ )+import qualified Data.List.NonEmpty as NEL+import qualified Data.Map as Map+import Distribution.Compat.Directory+ ( doesPathExist+ , listDirectory+ )+import Distribution.Compat.Environment (lookupEnv)+import Distribution.Parsec+ ( simpleParsec+ )+import Distribution.Pretty+ ( defaultStyle+ , pretty+ , prettyShow+ )+import Distribution.Simple.Errors+import Distribution.Types.AnnotatedId+import Distribution.Utils.Path+import Distribution.Utils.Structured (structuredDecodeOrFailIO, structuredEncode)+import System.Directory+ ( canonicalizePath+ , createDirectoryIfMissing+ , doesFileExist+ , removeFile+ )+import System.FilePath+ ( isAbsolute+ )+import System.IO+ ( hClose+ , hPutStrLn+ )+import qualified System.Info+ ( compilerName+ , compilerVersion+ )+import Text.PrettyPrint+ ( Doc+ , char+ , hsep+ , quotes+ , renderStyle+ , text+ , ($+$)+ )++import qualified Data.Maybe as M+import qualified Data.Set as Set+import qualified Distribution.Compat.NonEmptySet as NES++type UseExternalInternalDeps = Bool++-- | The errors that can be thrown when reading the @setup-config@ file.+data ConfigStateFileError+ = -- | No header found.+ ConfigStateFileNoHeader+ | -- | Incorrect header.+ ConfigStateFileBadHeader+ | -- | Cannot parse file contents.+ ConfigStateFileNoParse+ | -- | No file!+ ConfigStateFileMissing+ { cfgStateFileErrorCwd :: Maybe (SymbolicPath CWD (Dir Pkg))+ , cfgStateFileErrorFile :: SymbolicPath Pkg File+ }+ | -- | Mismatched version.+ ConfigStateFileBadVersion+ PackageIdentifier+ PackageIdentifier+ (Either ConfigStateFileError LocalBuildInfo)++-- | Format a 'ConfigStateFileError' as a user-facing error message.+dispConfigStateFileError :: ConfigStateFileError -> Doc+dispConfigStateFileError ConfigStateFileNoHeader =+ text "Saved package config file header is missing."+ <+> text "Re-run the 'Setup configure' command."+dispConfigStateFileError ConfigStateFileBadHeader =+ text "Saved package config file header is corrupt."+ <+> text "Re-run the 'Setup configure' command."+dispConfigStateFileError ConfigStateFileNoParse =+ text "Saved package config file is corrupt."+ <+> text "Re-run the 'Setup configure' command."+dispConfigStateFileError ConfigStateFileMissing{} =+ text "Run the 'Setup configure' command first."+dispConfigStateFileError (ConfigStateFileBadVersion oldCabal oldCompiler _) =+ text "Saved package config file is outdated:"+ $+$ badCabal+ $+$ badCompiler+ $+$ text "Re-run the 'Setup configure' command."+ where+ badCabal =+ text "• the Cabal version changed from"+ <+> pretty oldCabal+ <+> "to"+ <+> pretty currentCabalId+ badCompiler+ | oldCompiler == currentCompilerId = mempty+ | otherwise =+ text "• the compiler changed from"+ <+> pretty oldCompiler+ <+> "to"+ <+> pretty currentCompilerId++instance Show ConfigStateFileError where+ show = renderStyle defaultStyle . dispConfigStateFileError++instance Exception ConfigStateFileError++-- | Read the 'localBuildInfoFile'. Throw an exception if the file is+-- missing, if the file cannot be read, or if the file was created by an older+-- version of Cabal.+getConfigStateFile+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -> SymbolicPath Pkg File+ -- ^ The file path of the @setup-config@ file.+ -> IO LocalBuildInfo+getConfigStateFile mbWorkDir setupConfigFile = do+ let filename = interpretSymbolicPath mbWorkDir setupConfigFile+ exists <- doesFileExist filename+ unless exists $ throwIO $ ConfigStateFileMissing mbWorkDir setupConfigFile+ -- Read the config file into a strict ByteString to avoid problems with+ -- lazy I/O, then convert to lazy because the binary package needs that.+ contents <- BS.readFile filename+ let (header, body) = BLC8.span (/= '\n') (BLC8.fromChunks [contents])++ (cabalId, compId) <- parseHeader header++ let getStoredValue = do+ result <- structuredDecodeOrFailIO (BLC8.tail body)+ case result of+ Left _ -> throwIO ConfigStateFileNoParse+ Right x -> return x+ deferErrorIfBadVersion act+ | cabalId /= currentCabalId = do+ eResult <- try act+ throwIO $ ConfigStateFileBadVersion cabalId compId eResult+ | otherwise = act+ deferErrorIfBadVersion getStoredValue+ where+ _ = callStack -- TODO: attach call stack to exception++-- | Read the 'localBuildInfoFile', returning either an error or the local build+-- info.+tryGetConfigStateFile+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ Working directory.+ -> SymbolicPath Pkg File+ -- ^ The file path of the @setup-config@ file.+ -> IO (Either ConfigStateFileError LocalBuildInfo)+tryGetConfigStateFile mbWorkDir = try . getConfigStateFile mbWorkDir++-- | Try to read the 'localBuildInfoFile'.+tryGetPersistBuildConfig+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ Working directory.+ -> SymbolicPath Pkg (Dir Dist)+ -- ^ The @dist@ directory path.+ -> IO (Either ConfigStateFileError LocalBuildInfo)+tryGetPersistBuildConfig mbWorkDir = try . getPersistBuildConfig mbWorkDir++-- | Read the 'localBuildInfoFile'. Throw an exception if the file is+-- missing, if the file cannot be read, or if the file was created by an older+-- version of Cabal.+getPersistBuildConfig+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ Working directory.+ -> SymbolicPath Pkg (Dir Dist)+ -- ^ The @dist@ directory path.+ -> IO LocalBuildInfo+getPersistBuildConfig mbWorkDir distPref =+ getConfigStateFile mbWorkDir $ localBuildInfoFile distPref++-- | Try to read the 'localBuildInfoFile'.+maybeGetPersistBuildConfig+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ Working directory.+ -> SymbolicPath Pkg (Dir Dist)+ -- ^ The @dist@ directory path.+ -> IO (Maybe LocalBuildInfo)+maybeGetPersistBuildConfig mbWorkDir =+ liftM (either (const Nothing) Just) . tryGetPersistBuildConfig mbWorkDir++-- | After running configure, output the 'LocalBuildInfo' to the+-- 'localBuildInfoFile'.+writePersistBuildConfig+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ Working directory+ -> SymbolicPath Pkg (Dir Dist)+ -- ^ The @dist@ directory path.+ -> LocalBuildInfo+ -- ^ The 'LocalBuildInfo' to write.+ -> IO ()+writePersistBuildConfig mbWorkDir distPref lbi = do+ createDirectoryIfMissing False (i distPref)+ writeFileAtomic (i $ localBuildInfoFile distPref) $+ BLC8.unlines [showHeader pkgId, structuredEncode lbi]+ where+ i = interpretSymbolicPath mbWorkDir -- See Note [Symbolic paths] in Distribution.Utils.Path+ pkgId = localPackage lbi++-- | Identifier of the current Cabal package.+currentCabalId :: PackageIdentifier+currentCabalId = PackageIdentifier (mkPackageName "Cabal") cabalVersion++-- | Identifier of the current compiler package.+currentCompilerId :: PackageIdentifier+currentCompilerId =+ PackageIdentifier+ (mkPackageName System.Info.compilerName)+ (mkVersion' System.Info.compilerVersion)++-- | Parse the @setup-config@ file header, returning the package identifiers+-- for Cabal and the compiler.+parseHeader+ :: ByteString+ -- ^ The file contents.+ -> IO (PackageIdentifier, PackageIdentifier)+parseHeader header = case BLC8.words header of+ [ "Saved"+ , "package"+ , "config"+ , "for"+ , pkgId+ , "written"+ , "by"+ , cabalId+ , "using"+ , compId+ ] ->+ maybe (throwIO ConfigStateFileBadHeader) return $ do+ _ <- simpleParsec (fromUTF8LBS pkgId) :: Maybe PackageIdentifier+ cabalId' <- simpleParsec (BLC8.unpack cabalId)+ compId' <- simpleParsec (BLC8.unpack compId)+ return (cabalId', compId')+ _ -> throwIO ConfigStateFileNoHeader++-- | Generate the @setup-config@ file header.+showHeader+ :: PackageIdentifier+ -- ^ The processed package.+ -> ByteString+showHeader pkgId =+ BLC8.unwords+ [ "Saved"+ , "package"+ , "config"+ , "for"+ , toUTF8LBS $ prettyShow pkgId+ , "written"+ , "by"+ , BLC8.pack $ prettyShow currentCabalId+ , "using"+ , BLC8.pack $ prettyShow currentCompilerId+ ]++-- | Check that localBuildInfoFile is up-to-date with respect to the+-- .cabal file.+checkPersistBuildConfigOutdated+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -> SymbolicPath Pkg (Dir Dist)+ -> SymbolicPath Pkg File+ -> IO Bool+checkPersistBuildConfigOutdated mbWorkDir distPref pkg_descr_file =+ i pkg_descr_file `moreRecentFile` i (localBuildInfoFile distPref)+ where+ i = interpretSymbolicPath mbWorkDir -- See Note [Symbolic paths] in Distribution.Utils.Path++-- | Get the path of @dist\/setup-config@.+localBuildInfoFile+ :: SymbolicPath Pkg (Dir Dist)+ -- ^ The @dist@ directory path.+ -> SymbolicPath Pkg File+localBuildInfoFile distPref = distPref </> makeRelativePathEx "setup-config"++-- -----------------------------------------------------------------------------++-- * Configuration++-- -----------------------------------------------------------------------------++-- | Return the \"dist/\" prefix, or the default prefix. The prefix is taken+-- from (in order of highest to lowest preference) the override prefix, the+-- \"CABAL_BUILDDIR\" environment variable, or the default prefix.+findDistPref+ :: SymbolicPath Pkg (Dir Dist)+ -- ^ default \"dist\" prefix+ -> Setup.Flag (SymbolicPath Pkg (Dir Dist))+ -- ^ override \"dist\" prefix+ -> IO (SymbolicPath Pkg (Dir Dist))+findDistPref defDistPref overrideDistPref = do+ envDistPref <- liftM parseEnvDistPref (lookupEnv "CABAL_BUILDDIR")+ return $ fromFlagOrDefault defDistPref (mappend envDistPref overrideDistPref)+ where+ parseEnvDistPref env =+ case env of+ Just distPref | not (null distPref) -> toFlag $ makeSymbolicPath distPref+ _ -> NoFlag++-- | Return the \"dist/\" prefix, or the default prefix. The prefix is taken+-- from (in order of highest to lowest preference) the override prefix, the+-- \"CABAL_BUILDDIR\" environment variable, or 'defaultDistPref' is used. Call+-- this function to resolve a @*DistPref@ flag whenever it is not known to be+-- set. (The @*DistPref@ flags are always set to a definite value before+-- invoking 'UserHooks'.)+findDistPrefOrDefault+ :: Setup.Flag (SymbolicPath Pkg (Dir Dist))+ -- ^ override \"dist\" prefix+ -> IO (SymbolicPath Pkg (Dir Dist))+findDistPrefOrDefault = findDistPref defaultDistPref++-- | Perform the \"@.\/setup configure@\" action.+-- Returns the @.setup-config@ file.+configure+ :: (GenericPackageDescription, HookedBuildInfo)+ -> ConfigFlags+ -> IO LocalBuildInfo+configure = configure_setupHooks noConfigureHooks++configure_setupHooks+ :: ConfigureHooks+ -> (GenericPackageDescription, HookedBuildInfo)+ -> ConfigFlags+ -> IO LocalBuildInfo+configure_setupHooks+ (ConfigureHooks{preConfPackageHook, postConfPackageHook, preConfComponentHook})+ (g_pkg_descr, hookedBuildInfo)+ cfg = do+ -- Cabal pre-configure+ let verbosity = fromFlag (configVerbosity cfg)+ distPref = fromFlag $ configDistPref cfg+ mbWorkDir = flagToMaybe $ configWorkingDir cfg+ (lbc0, comp, platform, enabledComps) <- preConfigurePackage cfg g_pkg_descr++ -- Package-wide pre-configure hook+ lbc1 <-+ case preConfPackageHook of+ Nothing -> return lbc0+ Just pre_conf -> do+ let programDb0 = LBC.withPrograms lbc0+ programDb0' = programDb0{unconfiguredProgs = Map.empty}+ input =+ SetupHooks.PreConfPackageInputs+ { SetupHooks.configFlags = cfg+ , SetupHooks.localBuildConfig = lbc0{LBC.withPrograms = programDb0'}+ , -- Unconfigured programs are not supplied to the hook,+ -- as these cannot be passed over a serialisation boundary+ -- (see the "Binary ProgramDb" instance).+ SetupHooks.compiler = comp+ , SetupHooks.platform = platform+ }+ SetupHooks.PreConfPackageOutputs+ { SetupHooks.buildOptions = opts1+ , SetupHooks.extraConfiguredProgs = progs1+ } <-+ pre_conf input+ -- The package-wide pre-configure hook returns BuildOptions that+ -- overrides the one it was passed in, as well as an update to+ -- the ProgramDb in the form of new configured programs to add+ -- to the program database.+ return $+ lbc0+ { LBC.withBuildOptions = opts1+ , LBC.withPrograms =+ updateConfiguredProgs+ (`Map.union` progs1)+ programDb0+ }++ -- Cabal package-wide configure+ (lbc2, pbd2, pkg_info) <-+ finalizeAndConfigurePackage cfg lbc1 g_pkg_descr comp platform enabledComps++ -- Package-wide post-configure hook+ for_ postConfPackageHook $ \postConfPkg -> do+ let input =+ SetupHooks.PostConfPackageInputs+ { SetupHooks.localBuildConfig = lbc2+ , SetupHooks.packageBuildDescr = pbd2+ }+ postConfPkg input++ -- Per-component pre-configure hook+ pkg_descr <- do+ let pkg_descr2 = LBC.localPkgDescr pbd2+ applyComponentDiffs+ verbosity+ ( \c -> for preConfComponentHook $ \computeDiff -> do+ let input =+ SetupHooks.PreConfComponentInputs+ { SetupHooks.localBuildConfig = lbc2+ , SetupHooks.packageBuildDescr = pbd2+ , SetupHooks.component = c+ }+ SetupHooks.PreConfComponentOutputs+ { SetupHooks.componentDiff = diff+ } <-+ computeDiff input+ return diff+ )+ pkg_descr2+ let pbd3 = pbd2{LBC.localPkgDescr = pkg_descr}++ -- Cabal per-component configure+ externalPkgDeps <- finalCheckPackage g_pkg_descr pbd3 hookedBuildInfo pkg_info+ lbi <- configureComponents lbc2 pbd3 pkg_info externalPkgDeps++ writePersistBuildConfig mbWorkDir distPref lbi++ return lbi++preConfigurePackage+ :: ConfigFlags+ -> GenericPackageDescription+ -> IO (LBC.LocalBuildConfig, Compiler, Platform, ComponentRequestedSpec)+preConfigurePackage cfg g_pkg_descr = do+ let verbosity = fromFlag $ configVerbosity cfg++ -- Determine the component we are configuring, if a user specified+ -- one on the command line. We use a fake, flattened version of+ -- the package since at this point, we're not really sure what+ -- components we *can* configure. @Nothing@ means that we should+ -- configure everything (the old behavior).+ (mb_cname :: Maybe ComponentName) <- do+ let flat_pkg_descr = flattenPackageDescription g_pkg_descr+ targets0 = configTargets cfg+ targets <- readBuildTargets verbosity flat_pkg_descr targets0+ -- TODO: bleat if you use the module/file syntax+ let targets' = [cname | BuildTargetComponent cname <- targets]+ case targets' of+ _ | null targets0 -> return Nothing+ [cname] -> return (Just cname)+ [] -> dieWithException verbosity NoValidComponent+ _ -> dieWithException verbosity ConfigureEitherSingleOrAll++ case mb_cname of+ Nothing -> setupMessage verbosity "Configuring" (packageId g_pkg_descr)+ Just cname ->+ setupMessage'+ verbosity+ "Configuring"+ (packageId g_pkg_descr)+ cname+ (Just (configInstantiateWith cfg))++ -- configCID is only valid for per-component configure+ when (isJust (flagToMaybe (configCID cfg)) && isNothing mb_cname) $+ dieWithException verbosity ConfigCIDValidForPreComponent++ -- Make a data structure describing what components are enabled.+ let enabled :: ComponentRequestedSpec+ enabled = case mb_cname of+ Just cname -> OneComponentRequestedSpec cname+ Nothing ->+ ComponentRequestedSpec+ { -- The flag name (@--enable-tests@) is a+ -- little bit of a misnomer, because+ -- just passing this flag won't+ -- "enable", in our internal+ -- nomenclature; it's just a request; a+ -- @buildable: False@ might make it+ -- not possible to enable.+ testsRequested = fromFlag (configTests cfg)+ , benchmarksRequested =+ fromFlag (configBenchmarks cfg)+ }+ -- Some sanity checks related to enabling components.+ when+ ( isJust mb_cname+ && (fromFlag (configTests cfg) || fromFlag (configBenchmarks cfg))+ )+ $ dieWithException verbosity SanityCheckForEnableComponents++ checkDeprecatedFlags verbosity cfg+ checkExactConfiguration verbosity g_pkg_descr cfg++ programDbPre <- mkProgramDb cfg (configPrograms cfg)+ -- comp: the compiler we're building with+ -- compPlatform: the platform we're building for+ -- programDb: location and args of all programs we're+ -- building with+ ( comp :: Compiler+ , compPlatform :: Platform+ , programDb00 :: ProgramDb+ ) <-+ configCompilerEx+ (flagToMaybe (configHcFlavor cfg))+ (flagToMaybe (configHcPath cfg))+ (flagToMaybe (configHcPkg cfg))+ programDbPre+ (lessVerbose verbosity)++ -- Where to build the package+ let builddir :: SymbolicPath Pkg (Dir Build) -- e.g. dist/build+ builddir = setupFlagsBuildDir $ configCommonFlags cfg+ mbWorkDir = flagToMaybe $ configWorkingDir cfg+ -- NB: create this directory now so that all configure hooks get+ -- to see it. (In practice, the Configure build-type needs it before+ -- the postConfPackageHook runs.)+ createDirectoryIfMissingVerbose (lessVerbose verbosity) True $+ interpretSymbolicPath mbWorkDir builddir++ lbc <- computeLocalBuildConfig cfg comp programDb00+ return (lbc, comp, compPlatform, enabled)++computeLocalBuildConfig+ :: ConfigFlags+ -> Compiler+ -> ProgramDb+ -> IO LBC.LocalBuildConfig+computeLocalBuildConfig cfg comp programDb = do+ let common = configCommonFlags cfg+ verbosity = fromFlag $ setupVerbosity common+ -- Decide if we're going to compile with split sections.+ split_sections :: Bool <-+ if not (fromFlag $ configSplitSections cfg)+ then return False+ else case compilerFlavor comp of+ GHC+ | compilerVersion comp >= mkVersion [8, 0] ->+ return True+ GHCJS ->+ return True+ _ -> do+ warn+ verbosity+ ( "this compiler does not support "+ ++ "--enable-split-sections; ignoring"+ )+ return False++ -- Decide if we're going to compile with split objects.+ split_objs :: Bool <-+ if not (fromFlag $ configSplitObjs cfg)+ then return False+ else case compilerFlavor comp of+ _ | split_sections ->+ do+ warn+ verbosity+ ( "--enable-split-sections and "+ ++ "--enable-split-objs are mutually "+ ++ "exclusive; ignoring the latter"+ )+ return False+ GHC ->+ return True+ GHCJS ->+ return True+ _ -> do+ warn+ verbosity+ ( "this compiler does not support "+ ++ "--enable-split-objs; ignoring"+ )+ return False++ -- Basically yes/no/unknown.+ let linkerSupportsRelocations :: Maybe Bool+ linkerSupportsRelocations =+ case lookupProgramByName "ld" programDb of+ Nothing -> Nothing+ Just ld ->+ case Map.lookup "Supports relocatable output" $ programProperties ld of+ Just "YES" -> Just True+ Just "NO" -> Just False+ _other -> Nothing+ let ghciLibByDefault =+ case compilerId comp of+ CompilerId GHC _ ->+ -- If ghc is non-dynamic, then ghci needs object files,+ -- so we build one by default.+ --+ -- Technically, archive files should be sufficient for ghci,+ -- but because of GHC bug #8942, it has never been safe to+ -- rely on them. By the time that bug was fixed, ghci had+ -- been changed to read shared libraries instead of archive+ -- files (see next code block).+ not (GHC.compilerBuildWay comp `elem` [DynWay, ProfDynWay])+ CompilerId GHCJS _ ->+ not (GHCJS.isDynamic comp)+ _ -> False++ withGHCiLib_ <-+ case fromFlagOrDefault ghciLibByDefault (configGHCiLib cfg) of+ -- NOTE: If linkerSupportsRelocations is Nothing this may still fail if the+ -- linker does not support -r.+ True | not (fromMaybe True linkerSupportsRelocations) -> do+ warn verbosity $+ "--enable-library-for-ghci is not supported with the current"+ ++ " linker; ignoring..."+ return False+ v -> return v++ let sharedLibsByDefault+ | fromFlag (configDynExe cfg) =+ -- build a shared library if dynamically-linked+ -- executables are requested+ True+ | otherwise = case compilerId comp of+ CompilerId GHC _ ->+ -- if ghc is dynamic, then ghci needs a shared+ -- library, so we build one by default.+ GHC.compilerBuildWay comp == DynWay+ CompilerId GHCJS _ ->+ GHCJS.isDynamic comp+ _ -> False+ withSharedLib_ =+ -- build shared libraries if required by GHC or by the+ -- executable linking mode, but allow the user to force+ -- building only static library archives with+ -- --disable-shared.+ fromFlagOrDefault sharedLibsByDefault $ configSharedLib cfg++ withStaticLib_ =+ -- build a static library (all dependent libraries rolled+ -- into a huge .a archive) via GHCs -staticlib flag.+ fromFlagOrDefault False $ configStaticLib cfg++ withDynExe_ = fromFlag $ configDynExe cfg++ withFullyStaticExe_ = fromFlag $ configFullyStaticExe cfg++ setProfiling <- configureProfiling verbosity cfg comp++ setCoverage <- configureCoverage verbosity cfg comp++ -- Turn off library and executable stripping when `debug-info` is set+ -- to anything other than zero.+ let+ strip_libexe s f =+ let defaultStrip = fromFlagOrDefault True (f cfg)+ in case fromFlag (configDebugInfo cfg) of+ NoDebugInfo -> return defaultStrip+ _ -> case f cfg of+ Flag True -> do+ warn verbosity $+ "Setting debug-info implies "+ ++ s+ ++ "-stripping: False"+ return False+ _ -> return False++ strip_lib <- strip_libexe "library" configStripLibs+ strip_exe <- strip_libexe "executable" configStripExes++ let buildOptions =+ setCoverage . setProfiling $+ LBC.BuildOptions+ { withVanillaLib = fromFlag $ configVanillaLib cfg+ , withSharedLib = withSharedLib_+ , withStaticLib = withStaticLib_+ , withDynExe = withDynExe_+ , withFullyStaticExe = withFullyStaticExe_+ , withProfLib = False+ , withProfLibShared = False+ , withProfLibDetail = ProfDetailNone+ , withProfExe = False+ , withProfExeDetail = ProfDetailNone+ , withOptimization = fromFlag $ configOptimization cfg+ , withDebugInfo = fromFlag $ configDebugInfo cfg+ , withGHCiLib = withGHCiLib_+ , splitSections = split_sections+ , splitObjs = split_objs+ , stripExes = strip_exe+ , stripLibs = strip_lib+ , exeCoverage = False+ , libCoverage = False+ , relocatable = fromFlagOrDefault False $ configRelocatable cfg+ }++ -- Dynamic executable, but no shared vanilla libraries+ when (LBC.withDynExe buildOptions && not (LBC.withProfExe buildOptions) && not (LBC.withSharedLib buildOptions)) $+ warn verbosity $+ "Executables will use dynamic linking, but a shared library "+ ++ "is not being built. Linking will fail if any executables "+ ++ "depend on the library."++ -- Profiled dynamic executable, but no shared profiling libraries+ when (LBC.withDynExe buildOptions && LBC.withProfExe buildOptions && not (LBC.withProfLibShared buildOptions)) $+ warn verbosity $+ "Executables will use profiled dynamic linking, but a profiled shared library "+ ++ "is not being built. Linking will fail if any executables "+ ++ "depend on the library."++ return $+ LBC.LocalBuildConfig+ { extraConfigArgs = [] -- Currently configure does not+ -- take extra args, but if it+ -- did they would go here.+ , withPrograms = programDb+ , withBuildOptions = buildOptions+ }++data PackageInfo = PackageInfo+ { internalPackageSet :: Set LibraryName+ , promisedDepsSet :: Map (PackageName, ComponentName) PromisedComponent+ , installedPackageSet :: InstalledPackageIndex+ , requiredDepsMap :: Map (PackageName, ComponentName) InstalledPackageInfo+ }++configurePackage+ :: ConfigFlags+ -> LBC.LocalBuildConfig+ -> PackageDescription+ -> FlagAssignment+ -> ComponentRequestedSpec+ -> Compiler+ -> Platform+ -> ProgramDb+ -> PackageDBStack+ -> IO (LBC.LocalBuildConfig, LBC.PackageBuildDescr)+configurePackage cfg lbc0 pkg_descr00 flags enabled comp platform programDb0 packageDbs = do+ let common = configCommonFlags cfg+ verbosity = fromFlag $ setupVerbosity common++ -- add extra include/lib dirs as specified in cfg+ pkg_descr0 = addExtraIncludeLibDirsFromConfigFlags pkg_descr00 cfg+ -- TODO: it is not clear whether this adding these dirs is necessary+ -- when we are directly stating from a PackageDescription (e.g. when+ -- cabal-install has determined a PackageDescription, instead of rediscovering+ -- when working with a GenericPackageDescription).+ -- Could this function call be moved to the end of finalizeAndConfigurePackage+ -- right before calling configurePackage?++ -- Configure certain external build tools, see below for which ones.+ let requiredBuildTools+ -- If --ignore-build-tools is set, no build tool is required:+ | fromFlagOrDefault False $ configIgnoreBuildTools cfg =+ []+ | otherwise = do+ bi <- enabledBuildInfos pkg_descr0 enabled+ -- First, we collect any tool dep that we know is external. This is,+ -- in practice:+ --+ -- 1. `build-tools` entries on the whitelist+ --+ -- 2. `build-tool-depends` that aren't from the current package.+ let externBuildToolDeps =+ [ LegacyExeDependency (unUnqualComponentName eName) versionRange+ | buildTool@(ExeDependency _ eName versionRange) <-+ getAllToolDependencies pkg_descr0 bi+ , not $ isInternal pkg_descr0 buildTool+ ]+ -- Second, we collect any build-tools entry we don't know how to+ -- desugar. We'll never have any idea how to build them, so we just+ -- hope they are already on the PATH.+ let unknownBuildTools =+ [ buildTool+ | buildTool <- buildTools bi+ , Nothing == desugarBuildTool pkg_descr0 buildTool+ ]+ externBuildToolDeps ++ unknownBuildTools++ programDb1 <-+ configureAllKnownPrograms (lessVerbose verbosity) programDb0+ >>= configureRequiredPrograms verbosity requiredBuildTools++ (pkg_descr2, programDb2) <-+ configurePkgconfigPackages verbosity pkg_descr0 programDb1 enabled++ let use_external_internal_deps =+ case enabled of+ OneComponentRequestedSpec{} -> True+ ComponentRequestedSpec{} -> False++ -- Compute installation directory templates, based on user+ -- configuration.+ --+ -- TODO: Move this into a helper function.+ defaultDirs :: InstallDirTemplates <-+ defaultInstallDirs'+ use_external_internal_deps+ (compilerFlavor comp)+ (fromFlag (configUserInstall cfg))+ (hasLibs pkg_descr2)+ let+ installDirs =+ combineInstallDirs+ fromFlagOrDefault+ defaultDirs+ (configInstallDirs cfg)+ lbc = lbc0{LBC.withPrograms = programDb2}+ pbd =+ LBC.PackageBuildDescr+ { configFlags = cfg+ , flagAssignment = flags+ , componentEnabledSpec = enabled+ , compiler = comp+ , hostPlatform = platform+ , localPkgDescr = pkg_descr2+ , installDirTemplates = installDirs+ , withPackageDB = packageDbs+ , pkgDescrFile = Nothing+ , extraCoverageFor = []+ }++ debug verbosity $+ "Finalized package description:\n"+ ++ showPackageDescription pkg_descr2++ return (lbc, pbd)++finalizeAndConfigurePackage+ :: ConfigFlags+ -> LBC.LocalBuildConfig+ -> GenericPackageDescription+ -> Compiler+ -> Platform+ -> ComponentRequestedSpec+ -> IO (LBC.LocalBuildConfig, LBC.PackageBuildDescr, PackageInfo)+finalizeAndConfigurePackage cfg lbc0 g_pkg_descr comp platform enabled = do+ let common = configCommonFlags cfg+ verbosity = fromFlag $ setupVerbosity common+ mbWorkDir = flagToMaybe $ setupWorkingDir common++ let programDb0 = LBC.withPrograms lbc0+ -- What package database(s) to use+ packageDbs :: PackageDBStack+ packageDbs =+ interpretPackageDbFlags+ (fromFlag (configUserInstall cfg))+ (configPackageDBs cfg)++ -- The InstalledPackageIndex of all installed packages+ installedPackageSet :: InstalledPackageIndex <-+ getInstalledPackages+ (lessVerbose verbosity)+ comp+ mbWorkDir+ packageDbs+ programDb0++ -- The set of package names which are "shadowed" by internal+ -- packages, and which component they map to+ let internalPackageSet :: Set LibraryName+ internalPackageSet = getInternalLibraries g_pkg_descr++ -- Some sanity checks related to dynamic/static linking.+ when (fromFlag (configDynExe cfg) && fromFlag (configFullyStaticExe cfg)) $+ dieWithException verbosity SanityCheckForDynamicStaticLinking++ -- allConstraints: The set of all 'Dependency's we have. Used ONLY+ -- to 'configureFinalizedPackage'.+ -- requiredDepsMap: A map from 'PackageName' to the specifically+ -- required 'InstalledPackageInfo', due to --dependency+ --+ -- NB: These constraints are to be applied to ALL components of+ -- a package. Thus, it's not an error if allConstraints contains+ -- more constraints than is necessary for a component (another+ -- component might need it.)+ --+ -- NB: The fact that we bundle all the constraints together means+ -- that is not possible to configure a test-suite to use one+ -- version of a dependency, and the executable to use another.+ ( allConstraints :: [PackageVersionConstraint]+ , requiredDepsMap :: Map (PackageName, ComponentName) InstalledPackageInfo+ ) <-+ either (dieWithException verbosity) return $+ combinedConstraints+ (configConstraints cfg)+ (configDependencies cfg)+ installedPackageSet++ let+ promisedDepsSet = mkPromisedDepsSet (configPromisedDependencies cfg)+ pkg_info =+ PackageInfo+ { internalPackageSet+ , promisedDepsSet+ , installedPackageSet+ , requiredDepsMap+ }++ -- pkg_descr: The resolved package description, that does not contain any+ -- conditionals, because we have an assignment for+ -- every flag, either picking them ourselves using a+ -- simple naive algorithm, or having them be passed to+ -- us by 'configConfigurationsFlags')+ -- flags: The 'FlagAssignment' that the conditionals were+ -- resolved with.+ --+ -- NB: Why doesn't finalizing a package also tell us what the+ -- dependencies are (e.g. when we run the naive algorithm,+ -- we are checking if dependencies are satisfiable)? The+ -- primary reason is that we may NOT have done any solving:+ -- if the flags are all chosen for us, this step is a simple+ -- matter of flattening according to that assignment. It's+ -- cleaner to then configure the dependencies afterwards.+ let use_external_internal_deps = case enabled of+ OneComponentRequestedSpec{} -> True+ ComponentRequestedSpec{} -> False+ ( pkg_descr0 :: PackageDescription+ , flags :: FlagAssignment+ ) <-+ configureFinalizedPackage+ verbosity+ cfg+ enabled+ allConstraints+ ( dependencySatisfiable+ use_external_internal_deps+ (fromFlagOrDefault False (configExactConfiguration cfg))+ (fromFlagOrDefault False (configAllowDependingOnPrivateLibs cfg))+ (packageName g_pkg_descr)+ installedPackageSet+ internalPackageSet+ promisedDepsSet+ requiredDepsMap+ )+ comp+ platform+ g_pkg_descr++ (lbc, pbd) <-+ configurePackage+ cfg+ lbc0+ pkg_descr0+ flags+ enabled+ comp+ platform+ programDb0+ packageDbs+ return (lbc, pbd, pkg_info)++addExtraIncludeLibDirsFromConfigFlags+ :: PackageDescription -> ConfigFlags -> PackageDescription+addExtraIncludeLibDirsFromConfigFlags pkg_descr cfg =+ let extraBi =+ mempty+ { extraLibDirs = configExtraLibDirs cfg+ , extraLibDirsStatic = configExtraLibDirsStatic cfg+ , extraFrameworkDirs = configExtraFrameworkDirs cfg+ , includeDirs = configExtraIncludeDirs cfg+ }+ modifyLib l =+ l+ { libBuildInfo =+ libBuildInfo l+ `mappend` extraBi+ }+ modifyExecutable e =+ e+ { buildInfo =+ buildInfo e+ `mappend` extraBi+ }+ modifyForeignLib f =+ f+ { foreignLibBuildInfo =+ foreignLibBuildInfo f+ `mappend` extraBi+ }+ modifyTestsuite t =+ t+ { testBuildInfo =+ testBuildInfo t+ `mappend` extraBi+ }+ modifyBenchmark b =+ b+ { benchmarkBuildInfo =+ benchmarkBuildInfo b+ `mappend` extraBi+ }+ in pkg_descr+ { library = modifyLib `fmap` library pkg_descr+ , subLibraries = modifyLib `map` subLibraries pkg_descr+ , executables = modifyExecutable `map` executables pkg_descr+ , foreignLibs = modifyForeignLib `map` foreignLibs pkg_descr+ , testSuites = modifyTestsuite `map` testSuites pkg_descr+ , benchmarks = modifyBenchmark `map` benchmarks pkg_descr+ }++finalCheckPackage+ :: GenericPackageDescription+ -> LBC.PackageBuildDescr+ -> HookedBuildInfo+ -> PackageInfo+ -> IO ([PreExistingComponent], [ConfiguredPromisedComponent])+finalCheckPackage+ g_pkg_descr+ ( LBC.PackageBuildDescr+ { configFlags = cfg+ , localPkgDescr = pkg_descr+ , compiler = comp+ , hostPlatform = compPlatform+ , componentEnabledSpec = enabled+ }+ )+ hookedBuildInfo+ (PackageInfo{internalPackageSet, promisedDepsSet, installedPackageSet, requiredDepsMap}) =+ do+ let common = configCommonFlags cfg+ verbosity = fromFlag $ setupVerbosity common+ cabalFileDir = packageRoot common+ use_external_internal_deps =+ case enabled of+ OneComponentRequestedSpec{} -> True+ ComponentRequestedSpec{} -> False++ checkCompilerProblems verbosity comp pkg_descr enabled+ checkPackageProblems+ verbosity+ cabalFileDir+ g_pkg_descr+ (updatePackageDescription hookedBuildInfo pkg_descr)+ -- NB: we apply the HookedBuildInfo to check it is valid,+ -- but we don't propagate it.+ -- Other UserHooks must separately return it again, and we+ -- will re-apply it each time.++ -- Check languages and extensions+ -- TODO: Move this into a helper function.+ let langlist =+ nub $+ catMaybes $+ map+ defaultLanguage+ (enabledBuildInfos pkg_descr enabled)+ let langs = unsupportedLanguages comp langlist+ when (not (null langs)) $+ dieWithException verbosity $+ UnsupportedLanguages (packageId g_pkg_descr) (compilerId comp) (map prettyShow langs)+ let extlist =+ nub $+ concatMap+ allExtensions+ (enabledBuildInfos pkg_descr enabled)+ let exts = unsupportedExtensions comp extlist+ when (not (null exts)) $+ dieWithException verbosity $+ UnsupportedLanguageExtension (packageId g_pkg_descr) (compilerId comp) (map prettyShow exts)++ -- Check foreign library build requirements+ let flibs = [flib | CFLib flib <- enabledComponents pkg_descr enabled]+ let unsupportedFLibs = unsupportedForeignLibs comp compPlatform flibs+ when (not (null unsupportedFLibs)) $+ dieWithException verbosity $+ CantFindForeignLibraries unsupportedFLibs++ -- The list of 'InstalledPackageInfo' recording the selected+ -- dependencies on external packages.+ --+ -- Invariant: For any package name, there is at most one package+ -- in externalPackageDeps which has that name.+ --+ -- NB: The dependency selection is global over ALL components+ -- in the package (similar to how allConstraints and+ -- requiredDepsMap are global over all components). In particular,+ -- if *any* component (post-flag resolution) has an unsatisfiable+ -- dependency, we will fail. This can sometimes be undesirable+ -- for users, see #1786 (benchmark conflicts with executable),+ --+ -- In the presence of Backpack, these package dependencies are+ -- NOT complete: they only ever include the INDEFINITE+ -- dependencies. After we apply an instantiation, we'll get+ -- definite references which constitute extra dependencies.+ -- (Why not have cabal-install pass these in explicitly?+ -- For one it's deterministic; for two, we need to associate+ -- them with renamings which would require a far more complicated+ -- input scheme than what we have today.)+ configureDependencies+ verbosity+ use_external_internal_deps+ internalPackageSet+ promisedDepsSet+ installedPackageSet+ requiredDepsMap+ pkg_descr+ enabled++configureComponents+ :: LBC.LocalBuildConfig+ -> LBC.PackageBuildDescr+ -> PackageInfo+ -> ([PreExistingComponent], [ConfiguredPromisedComponent])+ -> IO LocalBuildInfo+configureComponents+ lbc@(LBC.LocalBuildConfig{withPrograms = programDb})+ pbd0@( LBC.PackageBuildDescr+ { configFlags = cfg+ , localPkgDescr = pkg_descr+ , compiler = comp+ , componentEnabledSpec = enabled+ }+ )+ (PackageInfo{promisedDepsSet, installedPackageSet})+ externalPkgDeps =+ do+ let common = configCommonFlags cfg+ verbosity = fromFlag $ setupVerbosity common+ use_external_internal_deps =+ case enabled of+ OneComponentRequestedSpec{} -> True+ ComponentRequestedSpec{} -> False++ -- Compute internal component graph+ --+ -- The general idea is that we take a look at all the source level+ -- components (which may build-depends on each other) and form a graph.+ -- From there, we build a ComponentLocalBuildInfo for each of the+ -- components, which lets us actually build each component.+ ( buildComponents :: [ComponentLocalBuildInfo]+ , packageDependsIndex :: InstalledPackageIndex+ ) <-+ runLogProgress verbosity $+ configureComponentLocalBuildInfos+ verbosity+ use_external_internal_deps+ enabled+ (fromFlagOrDefault False (configDeterministic cfg))+ (configIPID cfg)+ (configCID cfg)+ pkg_descr+ externalPkgDeps+ (configConfigurationsFlags cfg)+ (configInstantiateWith cfg)+ installedPackageSet+ comp++ let buildComponentsMap =+ foldl'+ ( \m clbi ->+ Map.insertWith+ (++)+ (componentLocalName clbi)+ [clbi]+ m+ )+ Map.empty+ buildComponents++ let cbd =+ LBC.ComponentBuildDescr+ { componentGraph = Graph.fromDistinctList buildComponents+ , componentNameMap = buildComponentsMap+ , promisedPkgs = promisedDepsSet+ , installedPkgs = packageDependsIndex+ }++ -- For whole-package configure, we determine the+ -- extraCoverageFor of the main lib and sub libs here.+ extraCoverageUnitIds = case enabled of+ -- Whole package configure, add package libs+ ComponentRequestedSpec{} -> mapMaybe mbCompUnitId buildComponents+ -- Component configure, no need to do anything since+ -- extra-coverage-for will be passed for all other components that+ -- should be covered.+ OneComponentRequestedSpec{} -> []+ mbCompUnitId LibComponentLocalBuildInfo{componentUnitId} = Just componentUnitId+ mbCompUnitId _ = Nothing++ pbd =+ pbd0+ { LBC.extraCoverageFor = extraCoverageUnitIds+ }++ lbd =+ LBC.LocalBuildDescr+ { packageBuildDescr = pbd+ , componentBuildDescr = cbd+ }++ lbi =+ NewLocalBuildInfo+ { localBuildDescr = lbd+ , localBuildConfig = lbc+ }++ when (LBC.relocatable $ LBC.withBuildOptions lbc) $+ checkRelocatable verbosity pkg_descr lbi++ when (LBC.withDynExe $ LBC.withBuildOptions lbc) $+ checkSharedExes verbosity lbi++ -- TODO: This is not entirely correct, because the dirs may vary+ -- across libraries/executables+ let dirs = absoluteInstallDirs pkg_descr lbi NoCopyDest+ relative = prefixRelativeInstallDirs (packageId pkg_descr) lbi++ -- PKGROOT: allowing ${pkgroot} to be passed as --prefix to+ -- cabal configure, is only a hidden option. It allows packages+ -- to be relocatable with their package database. This however+ -- breaks when the Paths_* or other includes are used that+ -- contain hard coded paths. This is still an open TODO.+ --+ -- Allowing ${pkgroot} here, however requires less custom hooks+ -- in scripts that *really* want ${pkgroot}. See haskell/cabal/#4872+ unless+ ( isAbsolute (prefix dirs)+ || "${pkgroot}" `isPrefixOf` prefix dirs+ )+ $ dieWithException verbosity+ $ ExpectedAbsoluteDirectory (prefix dirs)++ when ("${pkgroot}" `isPrefixOf` prefix dirs) $+ warn verbosity $+ "Using ${pkgroot} in prefix "+ ++ prefix dirs+ ++ " will not work if you rely on the Path_* module "+ ++ " or other hard coded paths. Cabal does not yet "+ ++ " support fully relocatable builds! "+ ++ " See #462 #2302 #2994 #3305 #3473 #3586 #3909"+ ++ " #4097 #4291 #4872"++ info verbosity $+ "Using "+ ++ prettyShow currentCabalId+ ++ " compiled by "+ ++ prettyShow currentCompilerId+ info verbosity $ "Using compiler: " ++ showCompilerId comp+ info verbosity $ "Using install prefix: " ++ prefix dirs++ let dirinfo name dir isPrefixRelative =+ info verbosity $ name ++ " installed in: " ++ dir ++ relNote+ where+ relNote = case buildOS of+ Windows+ | not (hasLibs pkg_descr)+ && isNothing isPrefixRelative ->+ " (fixed location)"+ _ -> ""++ dirinfo "Executables" (bindir dirs) (bindir relative)+ dirinfo "Libraries" (libdir dirs) (libdir relative)+ dirinfo "Dynamic Libraries" (dynlibdir dirs) (dynlibdir relative)+ dirinfo "Private executables" (libexecdir dirs) (libexecdir relative)+ dirinfo "Data files" (datadir dirs) (datadir relative)+ dirinfo "Documentation" (docdir dirs) (docdir relative)+ dirinfo "Configuration files" (sysconfdir dirs) (sysconfdir relative)++ sequence_+ [ reportProgram verbosity prog configuredProg+ | (prog, configuredProg) <- knownPrograms programDb+ ]++ return lbi++mkPromisedDepsSet :: [PromisedComponent] -> Map (PackageName, ComponentName) PromisedComponent+mkPromisedDepsSet comps = Map.fromList [((packageName pn, CLibName ln), p) | p@(PromisedComponent pn ln _) <- comps]++-- | Adds the extra program paths from the flags provided to @configure@ as+-- well as specified locations for certain known programs and their default+-- arguments.+mkProgramDb :: ConfigFlags -> ProgramDb -> IO ProgramDb+mkProgramDb cfg initialProgramDb = do+ programDb <-+ modifyProgramSearchPath (getProgramSearchPath initialProgramDb ++) -- We need to have the paths to programs installed by build-tool-depends before all other paths+ <$> prependProgramSearchPath (fromFlagOrDefault normal (configVerbosity cfg)) searchpath [] initialProgramDb+ pure+ . userSpecifyArgss (configProgramArgs cfg)+ . userSpecifyPaths (configProgramPaths cfg)+ $ programDb+ where+ searchpath = fromNubList (configProgramPathExtra cfg)++-- Note. We try as much as possible to _prepend_ rather than postpend the extra-prog-path+-- so that we can override the system path. However, in a v2-build, at this point, the "system" path+-- has already been extended by both the built-tools-depends paths, as well as the program-path-extra+-- so for v2 builds adding it again is entirely unnecessary. However, it needs to get added again _anyway_+-- so as to take effect for v1 builds or standalone calls to Setup.hs+-- In this instance, the lesser evil is to not allow it to override the system path.++-- -----------------------------------------------------------------------------+-- Helper functions for configure++-- | Check if the user used any deprecated flags.+checkDeprecatedFlags :: Verbosity -> ConfigFlags -> IO ()+checkDeprecatedFlags verbosity cfg = do+ unless (configProfExe cfg == NoFlag) $ do+ let enable+ | fromFlag (configProfExe cfg) = "enable"+ | otherwise = "disable"+ warn+ verbosity+ ( "The flag --"+ ++ enable+ ++ "-executable-profiling is deprecated. "+ ++ "Please use --"+ ++ enable+ ++ "-profiling instead."+ )++ unless (configLibCoverage cfg == NoFlag) $ do+ let enable+ | fromFlag (configLibCoverage cfg) = "enable"+ | otherwise = "disable"+ warn+ verbosity+ ( "The flag --"+ ++ enable+ ++ "-library-coverage is deprecated. "+ ++ "Please use --"+ ++ enable+ ++ "-coverage instead."+ )++-- | Sanity check: if '--exact-configuration' was given, ensure that the+-- complete flag assignment was specified on the command line.+checkExactConfiguration+ :: Verbosity -> GenericPackageDescription -> ConfigFlags -> IO ()+checkExactConfiguration verbosity pkg_descr0 cfg =+ when (fromFlagOrDefault False (configExactConfiguration cfg)) $ do+ let cmdlineFlags = map fst (unFlagAssignment (configConfigurationsFlags cfg))+ allFlags = map flagName . genPackageFlags $ pkg_descr0+ diffFlags = allFlags \\ cmdlineFlags+ when (not . null $ diffFlags) $+ dieWithException verbosity $+ FlagsNotSpecified diffFlags++-- | Create a PackageIndex that makes *any libraries that might be*+-- defined internally to this package look like installed packages, in+-- case an executable should refer to any of them as dependencies.+--+-- It must be *any libraries that might be* defined rather than the+-- actual definitions, because these depend on conditionals in the .cabal+-- file, and we haven't resolved them yet. finalizePD+-- does the resolution of conditionals, and it takes internalPackageSet+-- as part of its input.+getInternalLibraries+ :: GenericPackageDescription+ -> Set LibraryName+getInternalLibraries pkg_descr0 =+ -- TODO: some day, executables will be fair game here too!+ let pkg_descr = flattenPackageDescription pkg_descr0+ in Set.fromList (map libName (allLibraries pkg_descr))++-- | Returns true if a dependency is satisfiable. This function may+-- report a dependency satisfiable even when it is not, but not vice+-- versa. This is to be passed to finalize+dependencySatisfiable+ :: Bool+ -- ^ use external internal deps?+ -> Bool+ -- ^ exact configuration?+ -> Bool+ -- ^ allow depending on private libs?+ -> PackageName+ -> InstalledPackageIndex+ -- ^ installed set+ -> Set LibraryName+ -- ^ library components+ -> Map (PackageName, ComponentName) PromisedComponent+ -> Map (PackageName, ComponentName) InstalledPackageInfo+ -- ^ required dependencies+ -> (Dependency -> DependencySatisfaction)+dependencySatisfiable+ use_external_internal_deps+ exact_config+ allow_private_deps+ pn+ installedPackageSet+ packageLibraries+ promisedDeps+ requiredDepsMap+ (Dependency depName vr sublibs)+ | exact_config =+ -- When we're given '--exact-configuration', we assume that all+ -- dependencies and flags are exactly specified on the command+ -- line. Thus we only consult the 'requiredDepsMap'. Note that+ -- we're not doing the version range check, so if there's some+ -- dependency that wasn't specified on the command line,+ -- 'finalizePD' will fail.+ -- TODO: mention '--exact-configuration' in the error message+ -- when this fails?+ if isInternalDep && not use_external_internal_deps+ then -- Except for internal deps, when we're NOT per-component mode;+ -- those are just True.+ internalDepSatisfiable+ else -- Backward compatibility for the old sublibrary syntax++ let depComponentName =+ CLibName $ LSubLibName $ packageNameToUnqualComponentName depName+ invisibleLibraries = NES.filter (not . visible) sublibs+ in if sublibs == mainLibSet && Map.member (pn, depComponentName) requiredDepsMap+ then Satisfied+ else case nonEmpty $ Set.toList invisibleLibraries of+ Nothing -> Satisfied+ Just invisibleLibraries' -> Unsatisfied $ MissingLibrary invisibleLibraries'+ | isInternalDep =+ if use_external_internal_deps+ then -- When we are doing per-component configure, we now need to+ -- test if the internal dependency is in the index. This has+ -- DIFFERENT semantics from normal dependency satisfiability.+ internalDepSatisfiableExternally+ else -- If a 'PackageName' is defined by an internal component, the dep is+ -- satisfiable (we're going to build it ourselves)+ internalDepSatisfiable+ | otherwise =+ depSatisfiable+ where+ -- Internal dependency is when dependency is the same as package.+ isInternalDep = pn == depName++ depSatisfiable =+ let allVersions = PackageIndex.lookupPackageName installedPackageSet depName+ eligibleVersions =+ [ version+ | (version, _infos) <- PackageIndex.eligibleDependencies allVersions+ ]+ in if null $ PackageIndex.matchingDependencies vr allVersions+ then+ if null eligibleVersions+ then Unsatisfied $ MissingPackage+ else Unsatisfied $ WrongVersion eligibleVersions+ else Satisfied++ internalDepSatisfiable =+ let missingLibraries = (NES.toSet sublibs) `Set.difference` packageLibraries+ in case nonEmpty $ Set.toList missingLibraries of+ Nothing -> Satisfied+ Just missingLibraries' -> Unsatisfied $ MissingLibrary missingLibraries'++ internalDepSatisfiableExternally =+ -- TODO: Might need to propagate information on which versions _are_ available, if any...+ let missingLibraries =+ NES.filter (null . PackageIndex.lookupInternalDependency installedPackageSet pn vr) sublibs+ in case nonEmpty $ Set.toList missingLibraries of+ Nothing -> Satisfied+ Just missingLibraries' -> Unsatisfied $ MissingLibrary missingLibraries'++ -- Check whether a library exists and is visible.+ -- We don't disambiguate between dependency on non-existent or private+ -- library yet, so we just return a bool and later report a generic error.+ visible lib =+ maybe+ False -- Does not even exist (wasn't in the depsMap)+ ( \ipi ->+ IPI.libVisibility ipi == LibraryVisibilityPublic+ -- If the override is enabled, the visibility does+ -- not matter (it's handled externally)+ || allow_private_deps+ -- If it's a library of the same package then it's+ -- always visible.+ -- This is only triggered when passing a component+ -- of the same package as --dependency, such as in:+ -- cabal-testsuite/PackageTests/ConfigureComponent/SubLib/setup-explicit.test.hs+ || pkgName (IPI.sourcePackageId ipi) == pn+ )+ maybeIPI+ -- Don't check if it's visible, we promise to build it before we need it.+ || promised+ where+ maybeIPI = Map.lookup (depName, CLibName lib) requiredDepsMap+ promised = isJust $ Map.lookup (depName, CLibName lib) promisedDeps++-- | Finalize a generic package description.+--+-- The workhorse is 'finalizePD'.+configureFinalizedPackage+ :: Verbosity+ -> ConfigFlags+ -> ComponentRequestedSpec+ -> [PackageVersionConstraint]+ -> (Dependency -> DependencySatisfaction)+ -- ^ tests if a dependency is satisfiable.+ -- Might say it's satisfiable even when not.+ -> Compiler+ -> Platform+ -> GenericPackageDescription+ -> IO (PackageDescription, FlagAssignment)+configureFinalizedPackage+ verbosity+ cfg+ enabled+ allConstraints+ satisfies+ comp+ compPlatform+ pkg_descr0 = do+ (pkg_descr, flags) <-+ case finalizePD+ (configConfigurationsFlags cfg)+ enabled+ satisfies+ compPlatform+ (compilerInfo comp)+ allConstraints+ pkg_descr0 of+ Right r -> return r+ Left missing ->+ dieWithException verbosity $ EncounteredMissingDependency missing++ unless (nullFlagAssignment flags) $+ info verbosity $+ "Flags chosen: "+ ++ intercalate+ ", "+ [ unFlagName fn ++ "=" ++ prettyShow value+ | (fn, value) <- unFlagAssignment flags+ ]++ return (pkg_descr, flags)++-- | Check for use of Cabal features which require compiler support+checkCompilerProblems+ :: Verbosity -> Compiler -> PackageDescription -> ComponentRequestedSpec -> IO ()+checkCompilerProblems verbosity comp pkg_descr enabled = do+ unless+ ( renamingPackageFlagsSupported comp+ || all+ (all (isDefaultIncludeRenaming . mixinIncludeRenaming) . mixins)+ (enabledBuildInfos pkg_descr enabled)+ )+ $ dieWithException verbosity CompilerDoesn'tSupportThinning+ when+ ( any (not . null . reexportedModules) (allLibraries pkg_descr)+ && not (reexportedModulesSupported comp)+ )+ $ dieWithException verbosity CompilerDoesn'tSupportReexports+ when+ ( any (not . null . signatures) (allLibraries pkg_descr)+ && not (backpackSupported comp)+ )+ $ dieWithException verbosity CompilerDoesn'tSupportBackpack++-- | Select dependencies for the package.+configureDependencies+ :: Verbosity+ -> UseExternalInternalDeps+ -> Set LibraryName+ -> Map (PackageName, ComponentName) PromisedComponent+ -> InstalledPackageIndex+ -- ^ installed packages+ -> Map (PackageName, ComponentName) InstalledPackageInfo+ -- ^ required deps+ -> PackageDescription+ -> ComponentRequestedSpec+ -> IO ([PreExistingComponent], [ConfiguredPromisedComponent])+configureDependencies+ verbosity+ use_external_internal_deps+ packageLibraries+ promisedDeps+ installedPackageSet+ requiredDepsMap+ pkg_descr+ enableSpec = do+ let failedDeps :: [FailedDependency]+ allPkgDeps :: [ResolvedDependency]+ (failedDeps, allPkgDeps) =+ partitionEithers $+ concat+ [ fmap (\s -> (dep, s)) <$> status+ | dep <- enabledBuildDepends pkg_descr enableSpec+ , let status =+ selectDependency+ (package pkg_descr)+ packageLibraries+ promisedDeps+ installedPackageSet+ requiredDepsMap+ use_external_internal_deps+ dep+ ]++ internalPkgDeps =+ [ pkgid+ | (_, InternalDependency pkgid) <- allPkgDeps+ ]+ -- NB: we have to SAVE the package name, because this is the only+ -- way we can be able to resolve package names in the package+ -- description.+ externalPkgDeps =+ [ pec+ | (_, ExternalDependency pec) <- allPkgDeps+ ]++ promisedPkgDeps =+ [ fpec+ | (_, PromisedDependency fpec) <- allPkgDeps+ ]++ when+ ( not (null internalPkgDeps)+ && not (newPackageDepsBehaviour pkg_descr)+ )+ $ dieWithException verbosity+ $ LibraryWithinSamePackage internalPkgDeps+ reportFailedDependencies verbosity failedDeps+ reportSelectedDependencies verbosity allPkgDeps++ return (externalPkgDeps, promisedPkgDeps)++-- | Select and apply coverage settings for the build based on the+-- 'ConfigFlags' and 'Compiler'.+configureCoverage+ :: Verbosity+ -> ConfigFlags+ -> Compiler+ -> IO (LBC.BuildOptions -> LBC.BuildOptions)+configureCoverage verbosity cfg comp = do+ let tryExeCoverage = fromFlagOrDefault False (configCoverage cfg)+ tryLibCoverage =+ fromFlagOrDefault+ tryExeCoverage+ (mappend (configCoverage cfg) (configLibCoverage cfg))+ -- TODO: Should we also enforce something here on that --coverage-for cannot+ -- include indefinite components or instantiations?+ if coverageSupported comp+ then do+ let apply buildOptions =+ buildOptions+ { LBC.libCoverage = tryLibCoverage+ , LBC.exeCoverage = tryExeCoverage+ }+ return apply+ else do+ let apply buildOptions =+ buildOptions+ { LBC.libCoverage = False+ , LBC.exeCoverage = False+ }+ when (tryExeCoverage || tryLibCoverage) $+ warn+ verbosity+ ( "The compiler "+ ++ showCompilerId comp+ ++ " does not support "+ ++ "program coverage. Program coverage has been disabled."+ )+ return apply++-- | Compute the effective value of the profiling flags+-- @--enable-library-profiling@ and @--enable-executable-profiling@+-- from the specified 'ConfigFlags'. This may be useful for+-- external Cabal tools which need to interact with Setup in+-- a backwards-compatible way: the most predictable mechanism+-- for enabling profiling across many legacy versions is to+-- NOT use @--enable-profiling@ and use those two flags instead.+--+-- Note that @--enable-executable-profiling@ also affects profiling+-- of benchmarks and (non-detailed) test suites.+computeEffectiveProfiling :: ConfigFlags -> (Bool {- lib vanilla-}, Bool {- lib shared -}, Bool {- exe -})+computeEffectiveProfiling cfg =+ -- The --profiling flag sets the default for both libs and exes,+ -- but can be overridden by --library-profiling, or the old deprecated+ -- --executable-profiling flag.+ --+ -- The --profiling-detail and --library-profiling-detail flags behave+ -- similarly+ let dynamicExe = fromFlagOrDefault False (configDynExe cfg)+ tryExeProfiling =+ fromFlagOrDefault+ False+ (mappend (configProf cfg) (configProfExe cfg))+ tryLibProfiling =+ fromFlagOrDefault+ (tryExeProfiling && not dynamicExe)+ (configProfLib cfg)+ tryLibProfilingShared =+ fromFlagOrDefault+ (tryExeProfiling && dynamicExe)+ (configProfShared cfg)+ in (tryLibProfiling, tryLibProfilingShared, tryExeProfiling)++-- | Select and apply profiling settings for the build based on the+-- 'ConfigFlags' and 'Compiler'.+configureProfiling+ :: Verbosity+ -> ConfigFlags+ -> Compiler+ -> IO (LBC.BuildOptions -> LBC.BuildOptions)+configureProfiling verbosity cfg comp = do+ let (tryLibProfiling, tryLibProfilingShared, tryExeProfiling) = computeEffectiveProfiling cfg++ tryExeProfileLevel =+ fromFlagOrDefault+ ProfDetailDefault+ (configProfDetail cfg)+ tryLibProfileLevel =+ fromFlagOrDefault+ ProfDetailDefault+ ( mappend+ (configProfDetail cfg)+ (configProfLibDetail cfg)+ )++ checkProfileLevel (ProfDetailOther other) = do+ warn+ verbosity+ ( "Unknown profiling detail level '"+ ++ other+ ++ "', using default.\nThe profiling detail levels are: "+ ++ intercalate+ ", "+ [name | (name, _, _) <- knownProfDetailLevels]+ )+ return ProfDetailDefault+ checkProfileLevel other = return other++ applyProfiling <-+ if profilingSupported comp && (profilingVanillaSupportedOrUnknown comp || profilingDynamicSupportedOrUnknown comp)+ then do+ exeLevel <- checkProfileLevel tryExeProfileLevel+ libLevel <- checkProfileLevel tryLibProfileLevel+ let apply buildOptions =+ buildOptions+ { LBC.withProfLib = tryLibProfiling+ , LBC.withProfLibDetail = libLevel+ , LBC.withProfExe = tryExeProfiling+ , LBC.withProfExeDetail = exeLevel+ }+ let compilerSupportsProfilingDynamic = profilingDynamicSupportedOrUnknown comp+ apply2 <-+ if compilerSupportsProfilingDynamic+ then -- Case 1: We support profiled shared libraries so turn on shared profiling+ -- libraries if the user asked for it.+ return $ \buildOptions -> apply buildOptions{LBC.withProfLibShared = tryLibProfilingShared}+ else -- Case 2: Compiler doesn't support profiling shared so turn them off+ do+ -- If we wanted to enable profiling shared libraries.. tell the+ -- user we couldn't.+ when (profilingVanillaSupportedOrUnknown comp && tryLibProfilingShared) $+ warn+ verbosity+ ( "The compiler "+ ++ showCompilerId comp+ ++ " does not support "+ ++ "profiling shared objects. Static profiled objects "+ ++ "will be built."+ )+ return $ \buildOptions ->+ let original_options = apply buildOptions+ in original_options+ { LBC.withProfLibShared = False+ , LBC.withProfLib = profilingVanillaSupportedOrUnknown comp && (tryLibProfilingShared || LBC.withProfLib original_options)+ , LBC.withDynExe = if LBC.withProfExe original_options then False else LBC.withDynExe original_options+ }++ when (tryExeProfiling && not (tryLibProfiling || tryLibProfilingShared)) $ do+ warn+ verbosity+ ( "Executables will be built with profiling, but library "+ ++ "profiling is disabled. Linking will fail if any executables "+ ++ "depend on the library."+ )+ return apply2+ else do+ let apply buildOptions =+ buildOptions+ { LBC.withProfLib = False+ , LBC.withProfLibShared = False+ , LBC.withProfLibDetail = ProfDetailNone+ , LBC.withProfExe = False+ , LBC.withProfExeDetail = ProfDetailNone+ }+ when (tryExeProfiling || tryLibProfiling) $+ warn+ verbosity+ ( "The compiler "+ ++ showCompilerId comp+ ++ " does not support "+ ++ "profiling. Profiling has been disabled."+ )+ return apply++ return applyProfiling++-- -----------------------------------------------------------------------------+-- Configuring package dependencies++reportProgram :: Verbosity -> Program -> Maybe ConfiguredProgram -> IO ()+reportProgram verbosity prog Nothing =+ info verbosity $ "No " ++ programName prog ++ " found"+reportProgram verbosity prog (Just configuredProg) =+ info verbosity $ "Using " ++ programName prog ++ version ++ location+ where+ location = case programLocation configuredProg of+ FoundOnSystem p -> " found on system at: " ++ p+ UserSpecified p -> " given by user at: " ++ p+ version = case programVersion configuredProg of+ Nothing -> ""+ Just v -> " version " ++ prettyShow v++hackageUrl :: String+hackageUrl = "http://hackage.haskell.org/package/"++type ResolvedDependency = (Dependency, DependencyResolution)++data DependencyResolution+ = -- | An external dependency from the package database, OR an+ -- internal dependency which we are getting from the package+ -- database.+ ExternalDependency PreExistingComponent+ | -- | A promised dependency, which doesn't yet exist, but should be provided+ -- at the build time.+ --+ -- We have these such that we can configure components without actually+ -- building its dependencies, if these dependencies need to be built later+ -- again. For example, when launching a multi-repl,+ -- we need to build packages in the interactive ghci session, no matter+ -- whether they have been built before.+ -- Building them in the configure phase is then redundant and costs time.+ PromisedDependency ConfiguredPromisedComponent+ | -- | An internal dependency ('PackageId' should be a library name)+ -- which we are going to have to build. (The+ -- 'PackageId' here is a hack to get a modest amount of+ -- polymorphism out of the Pkg' typeclass.)+ InternalDependency PackageId++-- | Test for a package dependency and record the version we have installed.+selectDependency+ :: PackageId+ -- ^ Package id of current package+ -> Set LibraryName+ -- ^ package libraries+ -> Map (PackageName, ComponentName) PromisedComponent+ -- ^ Set of components that are promised, i.e. are not installed already. See 'PromisedDependency' for more details.+ -> InstalledPackageIndex+ -- ^ Installed packages+ -> Map (PackageName, ComponentName) InstalledPackageInfo+ -- ^ Packages for which we have been given specific deps to+ -- use+ -> UseExternalInternalDeps+ -- ^ Are we configuring a+ -- single component?+ -> Dependency+ -> [Either FailedDependency DependencyResolution]+selectDependency+ pkgid+ internalIndex+ promisedIndex+ installedIndex+ requiredDepsMap+ use_external_internal_deps+ (Dependency dep_pkgname vr libs) =+ -- If the dependency specification matches anything in the internal package+ -- index, then we prefer that match to anything in the second.+ -- For example:+ --+ -- Name: MyLibrary+ -- Version: 0.1+ -- Library+ -- ..+ -- Executable my-exec+ -- build-depends: MyLibrary+ --+ -- We want "build-depends: MyLibrary" always to match the internal library+ -- even if there is a newer installed library "MyLibrary-0.2".+ if dep_pkgname == pn+ then+ if use_external_internal_deps+ then do_external_internal <$> NES.toList libs+ else do_internal <$> NES.toList libs+ else do_external_external <$> NES.toList libs+ where+ pn = packageName pkgid++ -- It's an internal library, and we're not per-component build+ do_internal lib+ | Set.member lib internalIndex =+ Right $ InternalDependency $ PackageIdentifier dep_pkgname $ packageVersion pkgid+ | otherwise =+ Left $ DependencyMissingInternal dep_pkgname lib++ -- We have to look it up externally+ do_external_external :: LibraryName -> Either FailedDependency DependencyResolution+ do_external_external lib+ | Just pc <- Map.lookup (dep_pkgname, CLibName lib) promisedIndex =+ return $ PromisedDependency (ConfiguredPromisedComponent dep_pkgname (AnnotatedId (promisedComponentPackage pc) (CLibName lib) (promisedComponentId pc)))+ do_external_external lib = do+ ipi <- case Map.lookup (dep_pkgname, CLibName lib) requiredDepsMap of+ -- If we know the exact pkg to use, then use it.+ Just pkginstance -> Right pkginstance+ -- Otherwise we just pick an arbitrary instance of the latest version.+ Nothing -> case pickLastIPI $ PackageIndex.lookupInternalDependency installedIndex dep_pkgname vr lib of+ Nothing -> Left (DependencyNotExists dep_pkgname)+ Just pkg -> Right pkg+ return $ ExternalDependency $ ipiToPreExistingComponent ipi++ do_external_internal :: LibraryName -> Either FailedDependency DependencyResolution+ do_external_internal lib+ | Just pc <- Map.lookup (dep_pkgname, CLibName lib) promisedIndex =+ return $ PromisedDependency (ConfiguredPromisedComponent dep_pkgname (AnnotatedId (promisedComponentPackage pc) (CLibName lib) (promisedComponentId pc)))+ do_external_internal lib = do+ ipi <- case Map.lookup (dep_pkgname, CLibName lib) requiredDepsMap of+ -- If we know the exact pkg to use, then use it.+ Just pkginstance -> Right pkginstance+ Nothing -> case pickLastIPI $ PackageIndex.lookupInternalDependency installedIndex pn vr lib of+ -- It's an internal library, being looked up externally+ Nothing -> Left (DependencyMissingInternal dep_pkgname lib)+ Just pkg -> Right pkg+ return $ ExternalDependency $ ipiToPreExistingComponent ipi++ pickLastIPI :: [(Version, [InstalledPackageInfo])] -> Maybe InstalledPackageInfo+ pickLastIPI pkgs = safeHead . snd . last =<< nonEmpty pkgs++reportSelectedDependencies+ :: Verbosity+ -> [ResolvedDependency]+ -> IO ()+reportSelectedDependencies verbosity deps =+ info verbosity $+ unlines+ [ "Dependency "+ ++ prettyShow (simplifyDependency dep)+ ++ ": using "+ ++ prettyShow pkgid+ | (dep, resolution) <- deps+ , let pkgid = case resolution of+ ExternalDependency pkg' -> packageId pkg'+ InternalDependency pkgid' -> pkgid'+ PromisedDependency promisedComp -> packageId promisedComp+ ]++reportFailedDependencies :: Verbosity -> [FailedDependency] -> IO ()+reportFailedDependencies _ [] = return ()+reportFailedDependencies verbosity failed =+ dieWithException verbosity $ ReportFailedDependencies failed hackageUrl++-- | List all installed packages in the given package databases.+-- Non-existent package databases do not cause errors, they just get skipped+-- with a warning and treated as empty ones, since technically they do not+-- contain any package.+getInstalledPackages+ :: Verbosity+ -> Compiler+ -> Maybe (SymbolicPath CWD (Dir from))+ -> PackageDBStackX (SymbolicPath from (Dir PkgDB))+ -- ^ The stack of package databases.+ -> ProgramDb+ -> IO InstalledPackageIndex+getInstalledPackages verbosity comp mbWorkDir packageDBs progdb = do+ when (null packageDBs) $+ dieWithException verbosity NoPackageDatabaseSpecified++ info verbosity "Reading installed packages..."+ -- do not check empty packagedbs (ghc-pkg would error out)+ packageDBs' <- filterM packageDBExists packageDBs+ case compilerFlavor comp of+ GHC -> GHC.getInstalledPackages verbosity comp mbWorkDir packageDBs' progdb+ GHCJS -> GHCJS.getInstalledPackages verbosity mbWorkDir packageDBs' progdb+ UHC -> UHC.getInstalledPackages verbosity comp mbWorkDir packageDBs' progdb+ flv ->+ dieWithException verbosity $ HowToFindInstalledPackages flv+ where+ packageDBExists (SpecificPackageDB path0) = do+ let path = interpretSymbolicPath mbWorkDir path0+ exists <- doesPathExist path+ unless exists $+ warn verbosity $+ "Package db " <> path <> " does not exist yet"+ return exists+ -- Checking the user and global package dbs is more complicated and needs+ -- way more data. Also ghc-pkg won't error out unless the user/global+ -- pkgdb is overridden with an empty one, so we just don't check for them.+ packageDBExists UserPackageDB = pure True+ packageDBExists GlobalPackageDB = pure True++-- | Like 'getInstalledPackages', but for a single package DB.+--+-- NB: Why isn't this always a fall through to 'getInstalledPackages'?+-- That is because 'getInstalledPackages' performs some sanity checks+-- on the package database stack in question. However, when sandboxes+-- are involved these sanity checks are not desirable.+getPackageDBContents+ :: Verbosity+ -> Compiler+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> PackageDB+ -> ProgramDb+ -> IO InstalledPackageIndex+getPackageDBContents verbosity comp mbWorkDir packageDB progdb = do+ info verbosity "Reading installed packages..."+ case compilerFlavor comp of+ GHC -> GHC.getPackageDBContents verbosity mbWorkDir packageDB progdb+ GHCJS -> GHCJS.getPackageDBContents verbosity mbWorkDir packageDB progdb+ -- For other compilers, try to fall back on 'getInstalledPackages'.+ _ -> getInstalledPackages verbosity comp mbWorkDir [packageDB] progdb++-- | A set of files (or directories) that can be monitored to detect when+-- there might have been a change in the installed packages.+getInstalledPackagesMonitorFiles+ :: Verbosity+ -> Compiler+ -> Maybe (SymbolicPath CWD ('Dir from))+ -> PackageDBStackS from+ -> ProgramDb+ -> Platform+ -> IO [FilePath]+getInstalledPackagesMonitorFiles verbosity comp mbWorkDir packageDBs progdb platform =+ case compilerFlavor comp of+ GHC ->+ GHC.getInstalledPackagesMonitorFiles+ verbosity+ mbWorkDir+ platform+ progdb+ packageDBs+ other -> do+ warn verbosity $+ "don't know how to find change monitoring files for "+ ++ "the installed package databases for "+ ++ prettyShow other+ return []++-- | Looks up the 'InstalledPackageInfo' of the given 'UnitId's from the+-- 'PackageDBStack' in the 'LocalBuildInfo'.+getInstalledPackagesById+ :: (Exception (VerboseException exception), Show exception, Typeable exception)+ => Verbosity+ -> LocalBuildInfo+ -> (UnitId -> exception)+ -- ^ Construct an exception that is thrown if a+ -- unit-id is not found in the installed packages,+ -- from the unit-id that is missing.+ -> [UnitId]+ -- ^ The unit ids to lookup in the installed packages+ -> IO [InstalledPackageInfo]+getInstalledPackagesById verbosity lbi@LocalBuildInfo{compiler = comp, withPackageDB = pkgDb, withPrograms = progDb} mkException unitids = do+ let mbWorkDir = mbWorkDirLBI lbi+ ipindex <- getInstalledPackages verbosity comp mbWorkDir pkgDb progDb+ mapM+ ( \uid -> case lookupUnitId ipindex uid of+ Nothing -> dieWithException verbosity (mkException uid)+ Just ipkg -> return ipkg+ )+ unitids++-- | The user interface specifies the package dbs to use with a combination of+-- @--global@, @--user@ and @--package-db=global|user|clear|$file@.+-- This function combines the global/user flag and interprets the package-db+-- flag into a single package db stack.+interpretPackageDbFlags :: Bool -> [Maybe (PackageDBX fp)] -> PackageDBStackX fp+interpretPackageDbFlags userInstall specificDBs =+ extra initialStack specificDBs+ where+ initialStack+ | userInstall = [GlobalPackageDB, UserPackageDB]+ | otherwise = [GlobalPackageDB]++ extra dbs' [] = dbs'+ extra _ (Nothing : dbs) = extra [] dbs+ extra dbs' (Just db : dbs) = extra (dbs' ++ [db]) dbs++-- We are given both --constraint="foo < 2.0" style constraints and also+-- specific packages to pick via --dependency="foo=foo-2.0-177d5cdf20962d0581".+--+-- When finalising the package we have to take into account the specific+-- installed deps we've been given, and the finalise function expects+-- constraints, so we have to translate these deps into version constraints.+--+-- But after finalising we then have to make sure we pick the right specific+-- deps in the end. So we still need to remember which installed packages to+-- pick.+combinedConstraints+ :: [PackageVersionConstraint]+ -> [GivenComponent]+ -- ^ installed dependencies+ -> InstalledPackageIndex+ -> Either+ CabalException+ ( [PackageVersionConstraint]+ , Map (PackageName, ComponentName) InstalledPackageInfo+ )+combinedConstraints constraints dependencies installedPackages = do+ when (not (null badComponentIds)) $+ Left $+ CombinedConstraints (dispDependencies badComponentIds)++ -- TODO: we don't check that all dependencies are used!++ return (allConstraints, idConstraintMap)+ where+ allConstraints :: [PackageVersionConstraint]+ allConstraints =+ constraints+ ++ [ thisPackageVersionConstraint (packageId pkg)+ | (_, _, _, Just pkg) <- dependenciesPkgInfo+ ]++ idConstraintMap :: Map (PackageName, ComponentName) InstalledPackageInfo+ idConstraintMap =+ Map.fromList+ -- NB: do NOT use the packageName from+ -- dependenciesPkgInfo!+ [ ((pn, cname), pkg)+ | (pn, cname, _, Just pkg) <- dependenciesPkgInfo+ ]++ -- The dependencies along with the installed package info, if it exists+ dependenciesPkgInfo :: [(PackageName, ComponentName, ComponentId, Maybe InstalledPackageInfo)]+ dependenciesPkgInfo =+ [ (pkgname, CLibName lname, cid, mpkg)+ | GivenComponent pkgname lname cid <- dependencies+ , let mpkg =+ PackageIndex.lookupComponentId+ installedPackages+ cid+ ]++ -- If we looked up a package specified by an installed package id+ -- (i.e. someone has written a hash) and didn't find it then it's+ -- an error.+ badComponentIds =+ [ (pkgname, cname, cid)+ | (pkgname, cname, cid, Nothing) <- dependenciesPkgInfo+ ]++ dispDependencies deps =+ hsep+ [ text "--dependency="+ <<>> quotes+ ( pretty pkgname+ <<>> case cname of+ CLibName LMainLibName -> ""+ CLibName (LSubLibName n) -> ":" <<>> pretty n+ _ -> ":" <<>> pretty cname+ <<>> char '='+ <<>> pretty cid+ )+ | (pkgname, cname, cid) <- deps+ ]++-- -----------------------------------------------------------------------------+-- Configuring program dependencies++configureRequiredPrograms+ :: Verbosity+ -> [LegacyExeDependency]+ -> ProgramDb+ -> IO ProgramDb+configureRequiredPrograms verbosity deps progdb =+ foldM (configureRequiredProgram verbosity) progdb deps++-- | Configure a required program, ensuring that it exists in the PATH+-- (or where the user has specified the program must live) and making it+-- available for use via the 'ProgramDb' interface. If the program is+-- known (exists in the input 'ProgramDb'), we will make sure that the+-- program matches the required version; otherwise we will accept+-- any version of the program and assume that it is a simpleProgram.+configureRequiredProgram+ :: Verbosity+ -> ProgramDb+ -> LegacyExeDependency+ -> IO ProgramDb+configureRequiredProgram+ verbosity+ progdb+ (LegacyExeDependency progName verRange) =+ case lookupProgramByName progName progdb of+ Just prog ->+ -- If the program has already been configured, use it+ -- (as long as the version is compatible).+ --+ -- Not doing so means falling back to the "simpleProgram" path below,+ -- which might fail if the program has custom logic to find a version+ -- (such as hsc2hs).+ let loc = locationPath $ programLocation prog+ in case programVersion prog of+ Nothing+ | verRange == anyVersion ->+ return progdb+ | otherwise ->+ dieWithException verbosity $!+ UnknownVersionDb (programId prog) verRange loc+ Just version+ | withinRange version verRange ->+ return progdb+ | otherwise ->+ dieWithException verbosity $!+ BadVersionDb (programId prog) version verRange loc+ Nothing ->+ -- Otherwise, try to configure it as a 'simpleProgram' automatically+ case lookupKnownProgram progName progdb of+ Nothing ->+ -- There's a bit of a story behind this line. In old versions+ -- of Cabal, there were only internal build-tools dependencies. So the+ -- behavior in this case was:+ --+ -- - If a build-tool dependency was internal, don't do+ -- any checking.+ --+ -- - If it was external, call 'configureRequiredProgram' to+ -- "configure" the executable. In particular, if+ -- the program was not "known" (present in 'ProgramDb'),+ -- then we would just error. This was fine, because+ -- the only way a program could be executed from 'ProgramDb'+ -- is if some library code from Cabal actually called it,+ -- and the pre-existing Cabal code only calls known+ -- programs from 'defaultProgramDb', and so if it+ -- is calling something else, you have a Custom setup+ -- script, and in that case you are expected to register+ -- the program you want to call in the ProgramDb.+ --+ -- OK, so that was fine, until I (ezyang, in 2016) refactored+ -- Cabal to support per-component builds. In this case, what+ -- was previously an internal build-tool dependency now became+ -- an external one, and now previously "internal" dependencies+ -- are now external. But these are permitted to exist even+ -- when they are not previously configured (something that+ -- can only occur by a Custom script.)+ --+ -- So, I decided, "Fine, let's just accept these in any+ -- case." Thus this line. The alternative would have been to+ -- somehow detect when a build-tools dependency was "internal" (by+ -- looking at the unflattened package description) but this+ -- would also be incompatible with future work to support+ -- external executable dependencies: we definitely cannot+ -- assume they will be preinitialized in the 'ProgramDb'.+ configureProgram verbosity (simpleProgram progName) progdb+ Just prog+ -- requireProgramVersion always requires the program have a version+ -- but if the user says "build-depends: foo" ie no version constraint+ -- then we should not fail if we cannot discover the program version.+ | verRange == anyVersion -> do+ (_, progdb') <- requireProgram verbosity prog progdb+ return progdb'+ | otherwise -> do+ (_, _, progdb') <- requireProgramVersion verbosity prog verRange progdb+ return progdb'++-- -----------------------------------------------------------------------------+-- Configuring pkg-config package dependencies++configurePkgconfigPackages+ :: Verbosity+ -> PackageDescription+ -> ProgramDb+ -> ComponentRequestedSpec+ -> IO (PackageDescription, ProgramDb)+configurePkgconfigPackages verbosity pkg_descr progdb enabled+ | null allpkgs = return (pkg_descr, progdb)+ | otherwise = do+ (_, _, progdb') <-+ requireProgramVersion+ (lessVerbose verbosity)+ pkgConfigProgram+ (orLaterVersion $ mkVersion [0, 9, 0])+ progdb+ traverse_ requirePkg allpkgs+ mlib' <- traverse addPkgConfigBILib (library pkg_descr)+ libs' <- traverse addPkgConfigBILib (subLibraries pkg_descr)+ exes' <- traverse addPkgConfigBIExe (executables pkg_descr)+ tests' <- traverse addPkgConfigBITest (testSuites pkg_descr)+ benches' <- traverse addPkgConfigBIBench (benchmarks pkg_descr)+ let pkg_descr' =+ pkg_descr+ { library = mlib'+ , subLibraries = libs'+ , executables = exes'+ , testSuites = tests'+ , benchmarks = benches'+ }+ return (pkg_descr', progdb')+ where+ allpkgs = concatMap pkgconfigDepends (enabledBuildInfos pkg_descr enabled)+ pkgconfig =+ getDbProgramOutput+ (lessVerbose verbosity)+ pkgConfigProgram+ progdb++ requirePkg dep@(PkgconfigDependency pkgn range) = do+ version <-+ pkgconfig ["--modversion", pkg]+ `catchIO` (\_ -> dieWithException verbosity $ PkgConfigNotFound pkg versionRequirement)+ `catchExit` (\_ -> dieWithException verbosity $ PkgConfigNotFound pkg versionRequirement)+ let v = PkgconfigVersion (toUTF8BS $ trim version)+ if not (withinPkgconfigVersionRange v range)+ then dieWithException verbosity $ BadVersion pkg versionRequirement v+ else info verbosity (depSatisfied v)+ where+ depSatisfied v =+ "Dependency "+ ++ prettyShow dep+ ++ ": using version "+ ++ prettyShow v++ versionRequirement+ | isAnyPkgconfigVersion range = ""+ | otherwise = " version " ++ prettyShow range++ pkg = unPkgconfigName pkgn++ -- Adds pkgconfig dependencies to the build info for a component+ addPkgConfigBI compBI setCompBI comp = do+ bi <- pkgconfigBuildInfo (pkgconfigDepends (compBI comp))+ return $ setCompBI comp (compBI comp `mappend` bi)++ -- Adds pkgconfig dependencies to the build info for a library+ addPkgConfigBILib = addPkgConfigBI libBuildInfo $+ \lib bi -> lib{libBuildInfo = bi}++ -- Adds pkgconfig dependencies to the build info for an executable+ addPkgConfigBIExe = addPkgConfigBI buildInfo $+ \exe bi -> exe{buildInfo = bi}++ -- Adds pkgconfig dependencies to the build info for a test suite+ addPkgConfigBITest = addPkgConfigBI testBuildInfo $+ \test bi -> test{testBuildInfo = bi}++ -- Adds pkgconfig dependencies to the build info for a benchmark+ addPkgConfigBIBench = addPkgConfigBI benchmarkBuildInfo $+ \bench bi -> bench{benchmarkBuildInfo = bi}++ pkgconfigBuildInfo :: [PkgconfigDependency] -> IO BuildInfo+ pkgconfigBuildInfo [] = return mempty+ pkgconfigBuildInfo pkgdeps = do+ let pkgs = nub [prettyShow pkg | PkgconfigDependency pkg _ <- pkgdeps]+ ccflags <- pkgconfig ("--cflags" : pkgs)+ ldflags <- pkgconfig ("--libs" : pkgs)+ ldflags_static <- pkgconfig ("--libs" : "--static" : pkgs)+ return (ccLdOptionsBuildInfo (words ccflags) (words ldflags) (words ldflags_static))++-- | Makes a 'BuildInfo' from C compiler and linker flags.+--+-- This can be used with the output from configuration programs like pkg-config+-- and similar package-specific programs like mysql-config, freealut-config etc.+-- For example:+--+-- > ccflags <- getDbProgramOutput verbosity prog progdb ["--cflags"]+-- > ldflags <- getDbProgramOutput verbosity prog progdb ["--libs"]+-- > ldflags_static <- getDbProgramOutput verbosity prog progdb ["--libs", "--static"]+-- > return (ccldOptionsBuildInfo (words ccflags) (words ldflags) (words ldflags_static))+ccLdOptionsBuildInfo :: [String] -> [String] -> [String] -> BuildInfo+ccLdOptionsBuildInfo cflags ldflags ldflags_static =+ let (includeDirs', cflags') = partition ("-I" `isPrefixOf`) cflags+ (extraLibs', ldflags') = partition ("-l" `isPrefixOf`) ldflags+ (extraLibDirs', ldflags'') = partition ("-L" `isPrefixOf`) ldflags'+ (extraLibsStatic') = filter ("-l" `isPrefixOf`) ldflags_static+ (extraLibDirsStatic') = filter ("-L" `isPrefixOf`) ldflags_static+ in mempty+ { includeDirs = map (makeSymbolicPath . drop 2) includeDirs'+ , extraLibs = map (drop 2) extraLibs'+ , extraLibDirs = map (makeSymbolicPath . drop 2) extraLibDirs'+ , extraLibsStatic = map (drop 2) extraLibsStatic'+ , extraLibDirsStatic = map (makeSymbolicPath . drop 2) extraLibDirsStatic'+ , ccOptions = cflags'+ , ldOptions = ldflags''+ }++-- -----------------------------------------------------------------------------+-- Determining the compiler details++configCompilerAuxEx+ :: ConfigFlags+ -> IO (Compiler, Platform, ProgramDb)+configCompilerAuxEx cfg = do+ programDb <- mkProgramDb cfg defaultProgramDb+ let common = configCommonFlags cfg+ verbosity = fromFlag $ setupVerbosity common+ configCompilerEx+ (flagToMaybe $ configHcFlavor cfg)+ (flagToMaybe $ configHcPath cfg)+ (flagToMaybe $ configHcPkg cfg)+ programDb+ verbosity++-- | Configure the compiler and associated programs such as @hc-pkg@, @haddock@+-- and toolchain program such as @ar@, @ld@.+configCompilerEx+ :: Maybe CompilerFlavor+ -> Maybe FilePath+ -- ^ user-specified @hc@ path (optional)+ -> Maybe FilePath+ -- ^ user-specified @hc-pkg@ path (optional)+ -> ProgramDb+ -> Verbosity+ -> IO (Compiler, Platform, ProgramDb)+configCompilerEx Nothing _ _ _ verbosity = dieWithException verbosity UnknownCompilerException+configCompilerEx (Just hcFlavor) hcPath hcPkg progdb verbosity = do+ (comp, maybePlatform, programDb) <- case hcFlavor of+ GHC -> GHC.configure verbosity hcPath hcPkg progdb+ GHCJS -> GHCJS.configure verbosity hcPath hcPkg progdb+ UHC -> UHC.configure verbosity hcPath progdb+ _ -> dieWithException verbosity UnknownCompilerException+ return (comp, fromMaybe buildPlatform maybePlatform, programDb)++-- | Configure the compiler ONLY.+configCompiler+ :: Maybe CompilerFlavor+ -> Maybe FilePath+ -- ^ user-specified @hc@ path (optional)+ -> ProgramDb+ -> Verbosity+ -> IO (Compiler, Platform, ProgramDb)+configCompiler mbFlavor hcPath progdb verbosity = do+ (comp, maybePlatform, programDb) <-+ case mbFlavor of+ Nothing -> dieWithException verbosity UnknownCompilerException+ Just hcFlavor ->+ case hcFlavor of+ GHC -> GHC.configureCompiler verbosity hcPath progdb+ GHCJS -> GHCJS.configureCompiler verbosity hcPath progdb+ UHC -> UHC.configure verbosity hcPath progdb+ _ -> dieWithException verbosity UnknownCompilerException+ return (comp, fromMaybe buildPlatform maybePlatform, programDb)++-- | Configure programs associated to the compiler, such as @hc-pkg@, @haddock@+-- and toolchain program such as @ar@, @ld@.+configCompilerProgDb+ :: Verbosity+ -> Compiler+ -> ProgramDb+ -- ^ program database containing the compiler+ -> Maybe FilePath+ -- ^ user-specified @hc-pkg@ path (optional)+ -> IO ProgramDb+configCompilerProgDb verbosity comp hcProgDb hcPkgPath = do+ case compilerFlavor comp of+ GHC -> GHC.compilerProgramDb verbosity comp hcProgDb hcPkgPath+ GHCJS -> GHCJS.compilerProgramDb verbosity comp hcProgDb hcPkgPath+ _ -> return hcProgDb++-- -----------------------------------------------------------------------------+-- Testing C lib and header dependencies++-- Try to build a test C program which includes every header and links every+-- lib. If that fails, try to narrow it down by preprocessing (only) and linking+-- with individual headers and libs. If none is the obvious culprit then give a+-- generic error message.+-- TODO: produce a log file from the compiler errors, if any.+checkForeignDeps :: PackageDescription -> LocalBuildInfo -> Verbosity -> IO ()+checkForeignDeps pkg lbi verbosity =+ ifBuildsWith+ allHeaders+ (commonCcArgs ++ makeLdArgs allLibs) -- I'm feeling lucky+ (return ())+ ( do+ missingLibs <- findMissingLibs+ missingHdr <- findOffendingHdr+ explainErrors missingHdr missingLibs+ )+ where+ allHeaders = collectField (fmap getSymbolicPath . includes)+ allLibs =+ collectField $+ if withFullyStaticExe lbi+ then extraLibsStatic+ else extraLibs++ ifBuildsWith headers args success failure = do+ checkDuplicateHeaders+ ok <- builds (makeProgram headers) args+ if ok then success else failure++ -- Ensure that there is only one header with a given name+ -- in either the generated (most likely by `configure`)+ -- build directory (e.g. `dist/build`) or in the source directory.+ --+ -- If it exists in both, we'll remove the one in the source+ -- directory, as the generated should take precedence.+ --+ -- C compilers like to prefer source local relative includes,+ -- so the search paths provided to the compiler via -I are+ -- ignored if the included file can be found relative to the+ -- including file. As such we need to take drastic measures+ -- and delete the offending file in the source directory.+ checkDuplicateHeaders = do+ let relIncDirs = filter (not . isAbsolute) (collectField (fmap getSymbolicPath . includeDirs))+ isHeader = isSuffixOf ".h"+ genHeaders <- for relIncDirs $ \dir ->+ fmap (dir </>) . filter isHeader+ <$> listDirectory (i (buildDir lbi) </> dir) `catchIO` (\_ -> return [])+ srcHeaders <- for relIncDirs $ \dir ->+ fmap (dir </>) . filter isHeader+ <$> listDirectory (baseDir </> dir) `catchIO` (\_ -> return [])+ let commonHeaders = concat genHeaders `intersect` concat srcHeaders+ for_ commonHeaders $ \hdr -> do+ warn verbosity $+ "Duplicate header found in "+ ++ (getSymbolicPath (buildDir lbi) </> hdr)+ ++ " and "+ ++ (baseDir </> hdr)+ ++ "; removing "+ ++ (baseDir </> hdr)+ removeFile (baseDir </> hdr)++ findOffendingHdr =+ ifBuildsWith+ allHeaders+ ccArgs+ (return Nothing)+ (go . tail . NEL.inits $ allHeaders)+ where+ go [] = return Nothing -- cannot happen+ go (hdrs : hdrsInits) =+ -- Try just preprocessing first+ ifBuildsWith+ hdrs+ cppArgs+ -- If that works, try compiling too+ ( ifBuildsWith+ hdrs+ ccArgs+ (go hdrsInits)+ (return . fmap Right . safeLast $ hdrs)+ )+ (return . fmap Left . safeLast $ hdrs)++ cppArgs = "-E" : commonCppArgs -- preprocess only+ ccArgs = "-c" : commonCcArgs -- don't try to link+ findMissingLibs =+ ifBuildsWith+ []+ (makeLdArgs allLibs)+ (return [])+ (filterM (fmap not . libExists) allLibs)++ libExists lib = builds (makeProgram []) (makeLdArgs [lib])++ common = configCommonFlags $ configFlags lbi+ baseDir = packageRoot common++ -- See Note [Symbolic paths] in Distribution.Utils.Path+ i = interpretSymbolicPathLBI lbi+ mbWorkDir = mbWorkDirLBI lbi++ commonCppArgs =+ platformDefines lbi+ -- TODO: This is a massive hack, to work around the+ -- fact that the test performed here should be+ -- PER-component (c.f. the "I'm Feeling Lucky"; we+ -- should NOT be glomming everything together.)+ ++ ["-I" ++ i (buildDir lbi </> makeRelativePathEx "autogen")]+ -- `configure' may generate headers in the build directory+ ++ [ "-I" ++ i (buildDir lbi </> unsafeCoerceSymbolicPath dir)+ | dir <- mapMaybe symbolicPathRelative_maybe $ ordNub (collectField includeDirs)+ ]+ -- we might also reference headers from the+ -- packages directory.+ ++ [ "-I" ++ baseDir </> getSymbolicPath dir+ | dir <- mapMaybe symbolicPathRelative_maybe $ ordNub (collectField includeDirs)+ ]+ ++ [ "-I" ++ dir+ | dir <- ordNub (collectField (fmap getSymbolicPath . includeDirs))+ , isAbsolute dir+ ]+ ++ ["-I" ++ baseDir]+ ++ collectField cppOptions+ ++ collectField ccOptions+ ++ [ "-I" ++ dir+ | dir <-+ ordNub+ [ dir+ | dep <- deps+ , dir <- IPI.includeDirs dep+ ]+ -- dedupe include dirs of dependencies+ -- to prevent quadratic blow-up+ ]+ ++ [ opt+ | dep <- deps+ , opt <- IPI.ccOptions dep+ ]++ commonCcArgs =+ commonCppArgs+ ++ collectField ccOptions+ ++ [ opt+ | dep <- deps+ , opt <- IPI.ccOptions dep+ ]++ commonLdArgs =+ [ "-L" ++ getSymbolicPath dir+ | dir <-+ ordNub $+ collectField+ ( if withFullyStaticExe lbi+ then extraLibDirsStatic+ else extraLibDirs+ )+ ]+ ++ collectField ldOptions+ ++ [ "-L" ++ dir+ | dir <-+ ordNub+ [ dir+ | dep <- deps+ , dir <-+ if withFullyStaticExe lbi+ then IPI.libraryDirsStatic dep+ else IPI.libraryDirs dep+ ]+ ]+ -- TODO: do we also need dependent packages' ld options?+ makeLdArgs libs = ["-l" ++ lib | lib <- libs] ++ commonLdArgs++ makeProgram hdrs =+ unlines $+ ["#include \"" ++ hdr ++ "\"" | hdr <- hdrs]+ ++ ["int main(int argc, char** argv) { return 0; }"]++ collectField f = concatMap f allBi+ allBi = enabledBuildInfos pkg (componentEnabledSpec lbi)+ deps = PackageIndex.topologicalOrder (installedPkgs lbi)++ builds :: String -> [ProgArg] -> IO Bool+ builds program args =+ withTempFileCwd ".c" $ \cName cHnd ->+ withTempFileCwd "" $ \oNname oHnd ->+ do+ hPutStrLn cHnd program+ hClose cHnd+ hClose oHnd+ _ <-+ getDbProgramOutputCwd+ verbosity+ mbWorkDir+ gccProgram+ (withPrograms lbi)+ (getSymbolicPath cName : "-o" : getSymbolicPath oNname : args)+ return True+ `catchIO` (\_ -> return False)+ `catchExit` (\_ -> return False)++ explainErrors Nothing [] = return () -- should be impossible!+ explainErrors _ _+ | isNothing . lookupProgram gccProgram . withPrograms $ lbi =+ dieWithException verbosity NoWorkingGcc+ explainErrors hdr libs =+ dieWithException verbosity $ ExplainErrors hdr libs++-- | Output package check warnings and errors. Exit if any errors.+checkPackageProblems+ :: Verbosity+ -> FilePath+ -- ^ Path to the @.cabal@ file's directory+ -> GenericPackageDescription+ -> PackageDescription+ -> IO ()+checkPackageProblems verbosity dir gpkg pkg = do+ ioChecks <- checkPackageFiles verbosity pkg dir+ let pureChecks = checkPackage gpkg+ (errors, warnings) =+ partitionEithers (M.mapMaybe classEW $ pureChecks ++ ioChecks)+ if null errors+ then traverse_ (warn verbosity) (map ppPackageCheck warnings)+ else dieWithException verbosity $ CheckPackageProblems (map ppPackageCheck errors)+ where+ -- Classify error/warnings. Left: error, Right: warning.+ classEW :: PackageCheck -> Maybe (Either PackageCheck PackageCheck)+ classEW e@(PackageBuildImpossible _) = Just (Left e)+ classEW w@(PackageBuildWarning _) = Just (Right w)+ classEW (PackageDistSuspicious _) = Nothing+ classEW (PackageDistSuspiciousWarn _) = Nothing+ classEW (PackageDistInexcusable _) = Nothing++-- | Perform checks if a shared executable can be built+checkSharedExes+ :: Verbosity+ -> LocalBuildInfo+ -> IO ()+checkSharedExes verbosity lbi =+ when (os == Windows) $+ dieWithException verbosity $+ NoOSSupport os "shared executables"+ where+ (Platform _ os) = hostPlatform lbi++-- | Preform checks if a relocatable build is allowed+checkRelocatable+ :: Verbosity+ -> PackageDescription+ -> LocalBuildInfo+ -> IO ()+checkRelocatable verbosity pkg lbi =+ sequence_+ [ checkOS+ , checkCompiler+ , packagePrefixRelative+ , depsPrefixRelative+ ]+ where+ -- Check if the OS support relocatable builds.+ --+ -- If you add new OS' to this list, and your OS supports dynamic libraries+ -- and RPATH, make sure you add your OS to RPATH-support list of:+ -- Distribution.Simple.GHC.getRPaths+ checkOS =+ unless (os `elem` [OSX, Linux]) $+ dieWithException verbosity $+ NoOSSupport os "relocatable builds"+ where+ (Platform _ os) = hostPlatform lbi++ -- Check if the Compiler support relocatable builds+ checkCompiler =+ unless (compilerFlavor comp `elem` [GHC]) $+ dieWithException verbosity $+ NoCompilerSupport (show comp)+ where+ comp = compiler lbi++ -- Check if all the install dirs are relative to same prefix+ packagePrefixRelative =+ unless (relativeInstallDirs installDirs) $+ dieWithException verbosity $+ InstallDirsNotPrefixRelative (installDirs)+ where+ -- NB: should be good enough to check this against the default+ -- component ID, but if we wanted to be strictly correct we'd+ -- check for each ComponentId.+ installDirs = absoluteInstallDirs pkg lbi NoCopyDest+ p = prefix installDirs+ relativeInstallDirs (InstallDirs{..}) =+ all+ isJust+ ( fmap+ (stripPrefix p)+ [ bindir+ , libdir+ , dynlibdir+ , libexecdir+ , includedir+ , datadir+ , docdir+ , mandir+ , htmldir+ , haddockdir+ , sysconfdir+ ]+ )++ -- Check if the library dirs of the dependencies that are in the package+ -- database to which the package is installed are relative to the+ -- prefix of the package+ depsPrefixRelative = do+ pkgr <- GHC.pkgRoot verbosity lbi (registrationPackageDB (withPackageDB lbi))+ traverse_ (doCheck $ getSymbolicPath pkgr) ipkgs+ where+ doCheck pkgr ipkg+ | maybe False (== pkgr) (IPI.pkgRoot ipkg) =+ for_ (IPI.libraryDirs ipkg) $ \libdir -> do+ -- When @prefix@ is not under @pkgroot@,+ -- @shortRelativePath prefix pkgroot@ will return a path with+ -- @..@s and following check will fail without @canonicalizePath@.+ canonicalized <- canonicalizePath libdir+ -- The @prefix@ itself must also be canonicalized because+ -- canonicalizing @libdir@ may expand symlinks which would make+ -- @prefix@ no longer being a prefix of @canonical libdir@,+ -- while @canonical p@ could be a prefix of @canonical libdir@+ p' <- canonicalizePath p+ unless (p' `isPrefixOf` canonicalized) $+ dieWithException verbosity $+ LibDirDepsPrefixNotRelative libdir p+ | otherwise =+ return ()+ -- NB: should be good enough to check this against the default+ -- component ID, but if we wanted to be strictly correct we'd+ -- check for each ComponentId.+ installDirs = absoluteInstallDirs pkg lbi NoCopyDest+ p = prefix installDirs+ ipkgs = PackageIndex.allPackages (installedPkgs lbi)++-- -----------------------------------------------------------------------------+-- Testing foreign library requirements++unsupportedForeignLibs :: Compiler -> Platform -> [ForeignLib] -> [String]+unsupportedForeignLibs comp platform =+ mapMaybe (checkForeignLibSupported comp platform)++checkForeignLibSupported :: Compiler -> Platform -> ForeignLib -> Maybe String+checkForeignLibSupported comp platform flib = go (compilerFlavor comp)+ where+ go :: CompilerFlavor -> Maybe String+ go GHC+ | compilerVersion comp < mkVersion [7, 8] =+ unsupported+ [ "Building foreign libraries is only supported with GHC >= 7.8"+ ]+ | otherwise = goGhcPlatform platform+ go _ =+ unsupported+ [ "Building foreign libraries is currently only supported with ghc"+ ]++ goGhcPlatform :: Platform -> Maybe String+ goGhcPlatform (Platform _ OSX) = goGhcOsx (foreignLibType flib)+ goGhcPlatform (Platform _ Linux) = goGhcLinux (foreignLibType flib)+ goGhcPlatform (Platform _ FreeBSD) = goGhcLinux (foreignLibType flib)+ goGhcPlatform (Platform I386 Windows) = goGhcWindows (foreignLibType flib)+ goGhcPlatform (Platform X86_64 Windows) = goGhcWindows (foreignLibType flib)+ goGhcPlatform _ =+ unsupported+ [ "Building foreign libraries is currently only supported on Mac OS, "+ , "Linux and Windows"+ ]++ goGhcOsx :: ForeignLibType -> Maybe String+ goGhcOsx ForeignLibNativeShared+ | not (null (foreignLibModDefFile flib)) =+ unsupported+ [ "Module definition file not supported on OSX"+ ]+ | not (null (foreignLibVersionInfo flib)) =+ unsupported+ [ "Foreign library versioning not currently supported on OSX"+ ]+ | otherwise =+ Nothing+ goGhcOsx _ =+ unsupported+ [ "We can currently only build shared foreign libraries on OSX"+ ]++ goGhcLinux :: ForeignLibType -> Maybe String+ goGhcLinux ForeignLibNativeShared+ | not (null (foreignLibModDefFile flib)) =+ unsupported+ [ "Module definition file not supported on Linux"+ ]+ | not (null (foreignLibVersionInfo flib))+ && not (null (foreignLibVersionLinux flib)) =+ unsupported+ [ "You must not specify both lib-version-info and lib-version-linux"+ ]+ | otherwise =+ Nothing+ goGhcLinux _ =+ unsupported+ [ "We can currently only build shared foreign libraries on Linux"+ ]++ goGhcWindows :: ForeignLibType -> Maybe String+ goGhcWindows ForeignLibNativeShared+ | not standalone =+ unsupported+ [ "We can currently only build standalone libraries on Windows. Use\n"+ , " if os(Windows)\n"+ , " options: standalone\n"+ , "in your foreign-library stanza."+ ]+ | not (null (foreignLibVersionInfo flib)) =+ unsupported+ [ "Foreign library versioning not currently supported on Windows.\n"+ , "You can specify module definition files in the mod-def-file field."+ ]+ | otherwise =+ Nothing+ goGhcWindows _ =+ unsupported+ [ "We can currently only build shared foreign libraries on Windows"+ ] standalone :: Bool standalone = ForeignLibStandalone `elem` foreignLibOptions flib
+ src/Distribution/Simple/ConfigureScript.hs view
@@ -0,0 +1,247 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+-----------------------------------------------------------------------------+{-# OPTIONS_GHC -Wno-deprecations #-}++-- |+-- Module : Distribution.Simple.ConfigureScript+-- Copyright : Isaac Jones 2003-2005+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+module Distribution.Simple.ConfigureScript+ ( runConfigureScript+ ) where++import Distribution.Compat.Prelude+import Prelude ()++-- local+import Distribution.PackageDescription+import Distribution.Pretty+import Distribution.Simple.Configure (findDistPrefOrDefault)+import Distribution.Simple.Errors+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.Program+import Distribution.Simple.Program.Db+import Distribution.Simple.Setup.Common+import Distribution.Simple.Setup.Config+import Distribution.Simple.Utils+import Distribution.System (Platform, buildPlatform)+import Distribution.Utils.NubList+import Distribution.Utils.Path++-- Base+import System.Directory (createDirectoryIfMissing, doesFileExist)+import qualified System.FilePath as FilePath+#ifdef mingw32_HOST_OS+import System.FilePath (normalise, splitDrive)+#endif+import Distribution.Compat.Directory (makeAbsolute)+import Distribution.Compat.Environment (getEnvironment)+import Distribution.Compat.GetShortPathName (getShortPathName)++import qualified Data.List.NonEmpty as NonEmpty+import qualified Data.Map as Map++runConfigureScript+ :: ConfigFlags+ -> FlagAssignment+ -> ProgramDb+ -> Platform+ -- ^ host platform+ -> IO ()+runConfigureScript cfg flags programDb hp = do+ let commonCfg = configCommonFlags cfg+ verbosity = fromFlag $ setupVerbosity commonCfg+ dist_dir <- findDistPrefOrDefault $ setupDistPref commonCfg+ let build_dir = dist_dir </> makeRelativePathEx "build"+ mbWorkDir = flagToMaybe $ setupWorkingDir commonCfg+ configureScriptPath = packageRoot commonCfg </> "configure"+ confExists <- doesFileExist configureScriptPath+ unless confExists $+ dieWithException verbosity (ConfigureScriptNotFound configureScriptPath)+ configureFile <-+ makeAbsolute $ configureScriptPath+ env <- getEnvironment+ (ccProg, ccFlags) <- configureCCompiler verbosity programDb+ ccProgShort <- getShortPathName ccProg+ -- The C compiler's compilation and linker flags (e.g.+ -- "C compiler flags" and "Gcc Linker flags" from GHC) have already+ -- been merged into ccFlags, so we set both CFLAGS and LDFLAGS+ -- to ccFlags+ -- We don't try and tell configure which ld to use, as we don't have+ -- a way to pass its flags too++ -- Do not presume the CXX compiler is available, but it always will be after 9.4.+ (mcxxProgShort, mcxxFlags) <- do+ mprog <- needProgram verbosity gppProgram programDb+ case mprog of+ Just (p, _) -> do+ let pInv = programInvocation p []+ let cxxProg = progInvokePath pInv+ let cxxFlags = progInvokeArgs pInv+ cxxProgShort <- getShortPathName cxxProg+ return (Just cxxProgShort, Just cxxFlags)+ Nothing -> return (Nothing, Nothing)++ let configureFile' = toUnix configureFile+ -- autoconf is fussy about filenames, and has a set of forbidden+ -- characters that can't appear in the build directory, etc:+ -- https://www.gnu.org/software/autoconf/manual/autoconf.html#File-System-Conventions+ --+ -- This has caused hard-to-debug failures in the past (#5368), so we+ -- detect some cases early and warn with a clear message. Windows's+ -- use of backslashes is problematic here, so we'll switch to+ -- slashes, but we do still want to fail on backslashes in POSIX+ -- paths.+ --+ -- TODO: We don't check for colons, tildes or leading dashes. We+ -- also should check the builddir's path, destdir, and all other+ -- paths as well.+ for_ badAutoconfCharacters $ \(c, cname) ->+ when (c `elem` FilePath.dropDrive configureFile') $+ warn verbosity $+ concat+ [ "The path to the './configure' script, '"+ , configureFile'+ , "', contains the character '"+ , [c]+ , "' ("+ , cname+ , ")."+ , " This may cause the script to fail with an obscure error, or for"+ , " building the package to fail later."+ ]++ let+ -- Convert a flag name to name of environment variable to represent its+ -- value for the configure script.+ flagEnvVar :: FlagName -> String+ flagEnvVar flag = "CABAL_FLAG_" ++ map f (unFlagName flag)+ where+ f c+ | isAlphaNum c = c+ | otherwise = '_'+ -- A map from such env vars to every flag name and value where the name+ -- name maps to that that env var.+ cabalFlagMap :: Map String (NonEmpty (FlagName, Bool))+ cabalFlagMap =+ Map.fromListWith+ (<>)+ [ (flagEnvVar flag, (flag, bool) :| [])+ | (flag, bool) <- unFlagAssignment flags+ ]+ -- A map from env vars to flag names to the single flag we will go with+ cabalFlagMapDeconflicted :: Map String (FlagName, Bool) <-+ flip Map.traverseWithKey cabalFlagMap $ \envVar -> \case+ -- No conflict: no problem+ singleFlag :| [] -> pure singleFlag+ -- Conflict: warn and discard all but first+ collidingFlags@(firstFlag :| _ : _) -> do+ let quote s = "'" ++ s ++ "'"+ toName = quote . unFlagName . fst+ renderedList = intercalate ", " $ NonEmpty.toList $ toName <$> collidingFlags+ warn verbosity $+ unwords+ [ "Flags"+ , renderedList+ , "all map to the same environment variable"+ , quote envVar+ , "causing a collision."+ , "The value first flag"+ , toName firstFlag+ , "will be used."+ ]+ pure firstFlag++ let cabalFlagEnv =+ [ (envVar, Just val)+ | (envVar, (_, bool)) <- Map.toList cabalFlagMapDeconflicted+ , let val = if bool then "1" else "0"+ ]+ ++ [+ ( "CABAL_FLAGS"+ , Just $ unwords [showFlagValue fv | fv <- unFlagAssignment flags]+ )+ ]+ let extraPath = fromNubList $ configProgramPathExtra cfg+ let mkFlagsEnv fs var = maybe (unwords fs) (++ (" " ++ unwords fs)) (lookup var env)+ spSep = [FilePath.searchPathSeparator]+ pathEnv =+ maybe+ (intercalate spSep extraPath)+ ((intercalate spSep extraPath ++ spSep) ++)+ $ lookup "PATH" env+ overEnv =+ ("CFLAGS", Just (mkFlagsEnv ccFlags "CFLAGS"))+ : [("CXXFLAGS", Just (mkFlagsEnv cxxFlags "CXXFLAGS")) | Just cxxFlags <- [mcxxFlags]]+ ++ [("PATH", Just pathEnv) | not (null extraPath)]+ ++ cabalFlagEnv+ maybeHostFlag = if hp == buildPlatform then [] else ["--host=" ++ show (pretty hp)]+ args' =+ configureFile'+ : args+ ++ ["CC=" ++ ccProgShort]+ ++ ["CXX=" ++ cxxProgShort | Just cxxProgShort <- [mcxxProgShort]]+ ++ maybeHostFlag+ shProg = simpleProgram "sh"+ progDb <- prependProgramSearchPath verbosity extraPath [] emptyProgramDb+ shConfiguredProg <-+ lookupProgram shProg+ `fmap` configureProgram verbosity shProg progDb+ case shConfiguredProg of+ Just sh -> do+ let build_in = interpretSymbolicPath mbWorkDir build_dir+ createDirectoryIfMissing True build_in+ runProgramInvocation verbosity $+ (programInvocation (sh{programOverrideEnv = overEnv}) args')+ { progInvokeCwd = Just build_in+ }+ Nothing -> dieWithException verbosity NotFoundMsg+ where+ args = configureArgs backwardsCompatHack cfg+ backwardsCompatHack = False++-- | Convert Windows path to Unix ones+toUnix :: String -> String+#ifdef mingw32_HOST_OS+toUnix s = let tmp = normalise s+ (l, rest) = case splitDrive tmp of+ ([], x) -> ("/" , x)+ (h:_, x) -> ('/':h:"/", x)+ parts = FilePath.splitDirectories rest+ in l ++ intercalate "/" parts+#else+toUnix s = intercalate "/" $ FilePath.splitDirectories s+#endif++badAutoconfCharacters :: [(Char, String)]+badAutoconfCharacters =+ [ (' ', "space")+ , ('\t', "tab")+ , ('\n', "newline")+ , ('\0', "null")+ , ('"', "double quote")+ , ('#', "hash")+ , ('$', "dollar sign")+ , ('&', "ampersand")+ , ('\'', "single quote")+ , ('(', "left bracket")+ , (')', "right bracket")+ , ('*', "star")+ , (';', "semicolon")+ , ('<', "less-than sign")+ , ('=', "equals sign")+ , ('>', "greater-than sign")+ , ('?', "question mark")+ , ('[', "left square bracket")+ , ('\\', "backslash")+ , ('`', "backtick")+ , ('|', "pipe")+ ]
+ src/Distribution/Simple/Errors.hs view
@@ -0,0 +1,806 @@+-----------------------------------------------------------------------------++-- Module : Distribution.Simple.Errors+-- Copyright : Suganya Arun+-- License : BSD3+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- A collection of Exception Types in the Cabal library package++module Distribution.Simple.Errors+ ( CabalException (..)+ , FailedDependency (..)+ , exceptionCode+ , exceptionMessage+ ) where++import Distribution.Compat.Prelude+import Distribution.Compiler+import Distribution.InstalledPackageInfo+import Distribution.ModuleName+import Distribution.Package+import Distribution.PackageDescription+import Distribution.Pretty+ ( Pretty (pretty)+ , prettyShow+ )+import Distribution.Simple.InstallDirs+import Distribution.Simple.PreProcess.Types (Suffix)+import Distribution.Simple.SetupHooks.Errors+import Distribution.System (OS)+import Distribution.Types.MissingDependency (MissingDependency)+import Distribution.Types.VersionRange.Internal ()+import Distribution.Version+import Text.PrettyPrint++data FailedDependency+ = DependencyNotExists PackageName+ | DependencyMissingInternal PackageName LibraryName+ | DependencyNoVersion Dependency+ deriving (Show)++-- Types representing exceptions thrown by functions in all the modules of Cabal Package+data CabalException+ = NoBenchMarkProgram FilePath+ | EnableBenchMark+ | BenchMarkNameDisabled String+ | NoBenchMark String+ | -- | @NoLibraryFound@ has been downgraded to a warning, and is therefore no longer emitted.+ NoLibraryFound+ | CompilerNotInstalled CompilerFlavor+ | CantFindIncludeFile String+ | UnsupportedTestSuite String+ | UnsupportedBenchMark String+ | NoIncludeFileFound String+ | NoModuleFound ModuleName [Suffix]+ | RegMultipleInstancePkg+ | SuppressingChecksOnFile+ | NoSupportDirStylePackageDb+ | OnlySupportSpecificPackageDb+ | FailedToParseOutputDescribe String PackageId+ | DumpFailed String String+ | FailedToParseOutputDump String+ | ListFailed String+ | FailedToParseOutputList String+ | ProgramNotFound String+ | NoSupportForHoogle+ | NoSupportForQuickJumpFlag+ | NoGHCVersionFromHaddock+ | NoGHCVersionFromCompiler+ | HaddockAndGHCVersionDoesntMatch Version Version+ | MustHaveSharedLibraries+ | HaddockPackageFlags [(InstalledPackageInfo, [UnitId])]+ | UnknownCompilerFlavor CompilerFlavor+ | FailedToDetermineTarget+ | NoMultipleTargets+ | REPLNotSupported+ | NoSupportBuildingTestSuite TestType+ | NoSupportBuildingBenchMark BenchmarkType+ | BuildingNotSupportedWithCompiler+ | PkgDumpFailed+ | FailedToParseOutput+ | CantFindSourceModule ModuleName+ | VersionMismatchJS FilePath Version FilePath Version+ | VersionMismatchGHCJS FilePath Version FilePath Version+ | GlobalPackageDBLimitation+ | GlobalPackageDBSpecifiedFirst+ | MatchDirFileGlob String+ | MatchDirFileGlobErrors [String]+ | ErrorParsingFileDoesntExist FilePath+ | FailedParsing String+ | NotFoundMsg+ | UnrecognisedBuildTarget [String]+ | ReportBuildTargetProblems [(String, [String], String)]+ | UnknownBuildTarget [(String, [(String, String)])]+ | AmbiguousBuildTarget [(String, [(String, String)])]+ | CheckBuildTargets String+ | VersionMismatchGHC FilePath Version FilePath Version+ | CheckPackageDbStackPost76+ | CheckPackageDbStackPre76+ | GlobalPackageDbSpecifiedFirst+ | CantInstallForeignLib+ | NoSupportForPreProcessingTest TestType+ | NoSupportForPreProcessingBenchmark BenchmarkType+ | CantFindSourceForPreProcessFile String+ | NoSupportPreProcessingTestExtras TestType+ | NoSupportPreProcessingBenchmarkExtras BenchmarkType+ | UnlitException String+ | RunProgramInvocationException FilePath String+ | GetProgramInvocationException FilePath String+ | GetProgramInvocationLBSException FilePath String+ | CheckSemaphoreSupport+ | NoLibraryForPackage+ | SanityCheckHookedBuildInfo UnqualComponentName+ | ConfigureScriptNotFound FilePath+ | NoValidComponent+ | ConfigureEitherSingleOrAll+ | ConfigCIDValidForPreComponent+ | SanityCheckForEnableComponents+ | SanityCheckForDynamicStaticLinking+ | UnsupportedLanguages PackageIdentifier CompilerId [String]+ | UnsupportedLanguageExtension PackageIdentifier CompilerId [String]+ | CantFindForeignLibraries [String]+ | ExpectedAbsoluteDirectory FilePath+ | FlagsNotSpecified [FlagName]+ | EncounteredMissingDependency [MissingDependency]+ | CompilerDoesn'tSupportThinning+ | CompilerDoesn'tSupportReexports+ | CompilerDoesn'tSupportBackpack+ | LibraryWithinSamePackage [PackageId]+ | ReportFailedDependencies [FailedDependency] String+ | NoPackageDatabaseSpecified+ | HowToFindInstalledPackages CompilerFlavor+ | PkgConfigNotFound String String+ | BadVersion String String PkgconfigVersion+ | UnknownCompilerException+ | NoWorkingGcc+ | NoOSSupport OS String+ | NoCompilerSupport String+ | InstallDirsNotPrefixRelative (InstallDirs FilePath)+ | ExplainErrors (Maybe (Either [Char] [Char])) [String]+ | CheckPackageProblems [String]+ | LibDirDepsPrefixNotRelative FilePath FilePath+ | CombinedConstraints Doc+ | CantParseGHCOutput+ | IncompatibleWithCabal String String+ | Couldn'tFindTestProgram FilePath+ | TestCoverageSupport+ | Couldn'tFindTestProgLibV09 FilePath+ | TestCoverageSupportLibV09+ | RawSystemStdout String+ | FindFile FilePath+ | FindModuleFileEx ModuleName [Suffix] [FilePath]+ | MultipleFilesWithExtension String+ | NoDesc+ | MultiDesc [String]+ | RelocRegistrationInfo+ | CreatePackageDB+ | WithHcPkg String+ | RegisMultiplePkgNotSupported+ | RegisteringNotImplemented+ | NoTestSuitesEnabled+ | TestNameDisabled String+ | NoSuchTest String+ | ConfigureProgram String FilePath+ | RequireProgram String+ | NoProgramFound String VersionRange+ | BadVersionDb String Version VersionRange FilePath+ | UnknownVersionDb String VersionRange FilePath+ | MissingCoveredInstalledLibrary UnitId+ | SetupHooksException SetupHooksException+ | MultiReplDoesNotSupportComplexReexportedModules PackageName ComponentName+ deriving (Show)++exceptionCode :: CabalException -> Int+exceptionCode e = case e of+ NoBenchMarkProgram{} -> 1678+ EnableBenchMark{} -> 1453+ BenchMarkNameDisabled{} -> 2781+ NoBenchMark{} -> 1654+ NoLibraryFound -> 2546+ CompilerNotInstalled{} -> 7465+ CantFindIncludeFile{} -> 3876+ UnsupportedTestSuite{} -> 3245+ UnsupportedBenchMark{} -> 9123+ NoIncludeFileFound{} -> 2987+ NoModuleFound{} -> 6421+ RegMultipleInstancePkg{} -> 3421+ SuppressingChecksOnFile{} -> 5436+ NoSupportDirStylePackageDb -> 2980+ OnlySupportSpecificPackageDb -> 6547+ FailedToParseOutputDescribe{} -> 7218+ DumpFailed{} -> 6736+ FailedToParseOutputDump{} -> 9076+ ListFailed{} -> 5109+ FailedToParseOutputList{} -> 7650+ ProgramNotFound{} -> 4123+ NoSupportForHoogle{} -> 8706+ NoSupportForQuickJumpFlag{} -> 7086+ NoGHCVersionFromHaddock -> 5045+ NoGHCVersionFromCompiler -> 4098+ HaddockAndGHCVersionDoesntMatch{} -> 1998+ MustHaveSharedLibraries{} -> 6032+ HaddockPackageFlags{} -> 4569+ UnknownCompilerFlavor{} -> 3102+ FailedToDetermineTarget{} -> 5049+ NoMultipleTargets{} -> 6091+ REPLNotSupported{} -> 1098+ NoSupportBuildingTestSuite{} -> 4106+ NoSupportBuildingBenchMark{} -> 5320+ BuildingNotSupportedWithCompiler{} -> 7077+ -- Retired: ProvideHaskellSuiteTool{} -> 7509+ -- Retired: CannotDetermineCompilerVersion{} -> 4519+ PkgDumpFailed{} -> 2291+ FailedToParseOutput{} -> 5500+ CantFindSourceModule{} -> 8870+ VersionMismatchJS{} -> 9001+ VersionMismatchGHCJS{} -> 4001+ GlobalPackageDBLimitation{} -> 5002+ GlobalPackageDBSpecifiedFirst{} -> 3901+ MatchDirFileGlob{} -> 9760+ MatchDirFileGlobErrors{} -> 6661+ ErrorParsingFileDoesntExist{} -> 1234+ FailedParsing{} -> 6565+ NotFoundMsg{} -> 8011+ UnrecognisedBuildTarget{} -> 3410+ ReportBuildTargetProblems{} -> 5504+ UnknownBuildTarget{} -> 4444+ AmbiguousBuildTarget{} -> 7865+ CheckBuildTargets{} -> 4733+ VersionMismatchGHC{} -> 4000+ CheckPackageDbStackPost76{} -> 3000+ CheckPackageDbStackPre76{} -> 5640+ GlobalPackageDbSpecifiedFirst{} -> 2345+ CantInstallForeignLib{} -> 8221+ NoSupportForPreProcessingTest{} -> 3008+ NoSupportForPreProcessingBenchmark{} -> 6990+ CantFindSourceForPreProcessFile{} -> 7554+ NoSupportPreProcessingTestExtras{} -> 7886+ NoSupportPreProcessingBenchmarkExtras{} -> 9999+ UnlitException{} -> 5454+ RunProgramInvocationException{} -> 8012+ GetProgramInvocationException{} -> 7300+ GetProgramInvocationLBSException{} -> 6578+ CheckSemaphoreSupport{} -> 2002+ NoLibraryForPackage{} -> 8004+ SanityCheckHookedBuildInfo{} -> 6007+ ConfigureScriptNotFound{} -> 4567+ NoValidComponent{} -> 5680+ ConfigureEitherSingleOrAll{} -> 2001+ ConfigCIDValidForPreComponent{} -> 7006+ SanityCheckForEnableComponents{} -> 5004+ SanityCheckForDynamicStaticLinking{} -> 4007+ UnsupportedLanguages{} -> 8074+ UnsupportedLanguageExtension{} -> 5656+ CantFindForeignLibraries{} -> 4574+ ExpectedAbsoluteDirectory{} -> 6662+ FlagsNotSpecified{} -> 9080+ EncounteredMissingDependency{} -> 8010+ CompilerDoesn'tSupportThinning{} -> 4003+ CompilerDoesn'tSupportReexports{} -> 3456+ CompilerDoesn'tSupportBackpack{} -> 5446+ LibraryWithinSamePackage{} -> 7007+ ReportFailedDependencies{} -> 4321+ NoPackageDatabaseSpecified{} -> 2300+ HowToFindInstalledPackages{} -> 3003+ PkgConfigNotFound{} -> 7123+ BadVersion{} -> 7600+ UnknownCompilerException{} -> 3022+ NoWorkingGcc{} -> 1088+ NoOSSupport{} -> 3339+ NoCompilerSupport{} -> 2290+ InstallDirsNotPrefixRelative{} -> 6000+ ExplainErrors{} -> 4345+ CheckPackageProblems{} -> 5559+ LibDirDepsPrefixNotRelative{} -> 6667+ CombinedConstraints{} -> 5000+ CantParseGHCOutput{} -> 1980+ IncompatibleWithCabal{} -> 8123+ Couldn'tFindTestProgram{} -> 5678+ TestCoverageSupport{} -> 7890+ Couldn'tFindTestProgLibV09{} -> 9012+ TestCoverageSupportLibV09{} -> 1076+ RawSystemStdout{} -> 3098+ FindFile{} -> 2115+ FindModuleFileEx{} -> 6663+ MultipleFilesWithExtension{} -> 3333+ NoDesc{} -> 7654+ MultiDesc{} -> 5554+ RelocRegistrationInfo{} -> 4343+ CreatePackageDB{} -> 6787+ WithHcPkg{} -> 9876+ RegisMultiplePkgNotSupported{} -> 7632+ RegisteringNotImplemented{} -> 5411+ NoTestSuitesEnabled{} -> 9061+ TestNameDisabled{} -> 8210+ NoSuchTest{} -> 8000+ ConfigureProgram{} -> 5490+ RequireProgram{} -> 6666+ NoProgramFound{} -> 7620+ BadVersionDb{} -> 8038+ UnknownVersionDb{} -> 1008+ MissingCoveredInstalledLibrary{} -> 9341+ SetupHooksException err ->+ setupHooksExceptionCode err+ MultiReplDoesNotSupportComplexReexportedModules{} -> 9355++versionRequirement :: VersionRange -> String+versionRequirement range+ | isAnyVersion range = ""+ | otherwise = " version " ++ prettyShow range++exceptionMessage :: CabalException -> String+exceptionMessage e = case e of+ NoBenchMarkProgram cmd -> "Could not find benchmark program \"" ++ cmd ++ "\". Did you build the package first?"+ EnableBenchMark -> "No benchmarks enabled. Did you remember to \'Setup configure\' with " ++ "\'--enable-benchmarks\'?"+ BenchMarkNameDisabled bmName -> "Package configured with benchmark " ++ bmName ++ " disabled."+ NoBenchMark bmName -> "no such benchmark: " ++ bmName+ NoLibraryFound -> "No executables and no library found. Nothing to do."+ CompilerNotInstalled compilerFlavor -> "installing with " ++ prettyShow compilerFlavor ++ "is not implemented"+ CantFindIncludeFile file -> "can't find include file " ++ file+ UnsupportedTestSuite test_type -> "Unsupported test suite type: " ++ test_type+ UnsupportedBenchMark benchMarkType -> "Unsupported benchmark type: " ++ benchMarkType+ NoIncludeFileFound f -> "can't find include file " ++ f+ NoModuleFound m suffixes ->+ "Could not find module: "+ ++ prettyShow m+ ++ " with any suffix: "+ ++ show (map prettyShow suffixes)+ ++ ".\n"+ ++ "If the module "+ ++ "is autogenerated it should be added to 'autogen-modules'."+ RegMultipleInstancePkg -> "HcPkg.register: the compiler does not support registering multiple instances of packages."+ SuppressingChecksOnFile -> "HcPkg.register: the compiler does not support suppressing checks on files."+ NoSupportDirStylePackageDb -> "HcPkg.writeRegistrationFileDirectly: compiler does not support dir style package dbs"+ OnlySupportSpecificPackageDb -> "HcPkg.writeRegistrationFileDirectly: only supports SpecificPackageDB for now"+ FailedToParseOutputDescribe programId pkgId -> "failed to parse output of '" ++ programId ++ " describe " ++ prettyShow pkgId ++ "'"+ DumpFailed programId exception -> programId ++ " dump failed: " ++ exception+ FailedToParseOutputDump programId -> "failed to parse output of '" ++ programId ++ " dump'"+ ListFailed programId -> programId ++ " list failed"+ FailedToParseOutputList programId -> "failed to parse output of '" ++ programId ++ " list'"+ ProgramNotFound progName -> "The program '" ++ progName ++ "' is required but it could not be found"+ NoSupportForHoogle -> "Haddock 2.0 and 2.1 do not support the --hoogle flag."+ NoSupportForQuickJumpFlag -> "Haddock prior to 2.19 does not support the --quickjump flag."+ NoGHCVersionFromHaddock -> "Could not get GHC version from Haddock"+ NoGHCVersionFromCompiler -> "Could not get GHC version from compiler"+ HaddockAndGHCVersionDoesntMatch ghcVersion haddockGhcVersion ->+ "Haddock's internal GHC version must match the configured "+ ++ "GHC version.\n"+ ++ "The GHC version is "+ ++ prettyShow ghcVersion+ ++ " but "+ ++ "haddock is using GHC version "+ ++ prettyShow haddockGhcVersion+ MustHaveSharedLibraries -> "Must have vanilla or shared libraries enabled in order to run haddock"+ HaddockPackageFlags inf ->+ "internal error when calculating transitive "+ ++ "package dependencies.\nDebug info: "+ ++ show inf+ UnknownCompilerFlavor compilerFlavor -> "dumpBuildInfo: Unknown compiler flavor: " ++ show compilerFlavor+ FailedToDetermineTarget -> "Failed to determine target."+ NoMultipleTargets -> "The 'repl' command does not support multiple targets at once."+ REPLNotSupported -> "A REPL is not supported with this compiler."+ NoSupportBuildingTestSuite test_type -> "No support for building test suite type " ++ show test_type+ NoSupportBuildingBenchMark benchMarkType -> "No support for building benchmark type " ++ show benchMarkType+ BuildingNotSupportedWithCompiler -> "Building is not supported with this compiler."+ PkgDumpFailed -> "pkg dump failed"+ FailedToParseOutput -> "failed to parse output of 'pkg dump'"+ CantFindSourceModule moduleName -> "can't find source for module " ++ prettyShow moduleName+ VersionMismatchJS ghcjsProgPath ghcjsVersion ghcjsPkgProgPath ghcjsPkgGhcjsVersion ->+ "Version mismatch between ghcjs and ghcjs-pkg: "+ ++ show ghcjsProgPath+ ++ " is version "+ ++ prettyShow ghcjsVersion+ ++ " "+ ++ show ghcjsPkgProgPath+ ++ " is version "+ ++ prettyShow ghcjsPkgGhcjsVersion+ VersionMismatchGHCJS ghcjsProgPath ghcjsGhcVersion ghcjsPkgProgPath ghcjsPkgVersion ->+ "Version mismatch between ghcjs and ghcjs-pkg: "+ ++ show ghcjsProgPath+ ++ " was built with GHC version "+ ++ prettyShow ghcjsGhcVersion+ ++ " "+ ++ show ghcjsPkgProgPath+ ++ " was built with GHC version "+ ++ prettyShow ghcjsPkgVersion+ GlobalPackageDBLimitation ->+ "With current ghc versions the global package db is always used "+ ++ "and must be listed first. This ghc limitation may be lifted in "+ ++ "the future, see https://gitlab.haskell.org/ghc/ghc/-/issues/5977"+ GlobalPackageDBSpecifiedFirst ->+ "If the global package db is specified, it must be "+ ++ "specified first and cannot be specified multiple times"+ MatchDirFileGlob pathError -> pathError+ MatchDirFileGlobErrors errors -> unlines errors+ ErrorParsingFileDoesntExist filePath -> "Error Parsing: file \"" ++ filePath ++ "\" doesn't exist. Cannot continue."+ FailedParsing name -> "Failed parsing \"" ++ name ++ "\"."+ NotFoundMsg ->+ "The package has a './configure' script. "+ ++ "If you are on Windows, This requires a "+ ++ "Unix compatibility toolchain such as MinGW+MSYS or Cygwin. "+ ++ "If you are not on Windows, ensure that an 'sh' command "+ ++ "is discoverable in your path."+ UnrecognisedBuildTarget target ->+ unlines+ [ "Unrecognised build target '" ++ name ++ "'."+ | name <- target+ ]+ ++ "Examples:\n"+ ++ " - build foo -- component name "+ ++ "(library, executable, test-suite or benchmark)\n"+ ++ " - build Data.Foo -- module name\n"+ ++ " - build Data/Foo.hsc -- file name\n"+ ++ " - build lib:foo exe:foo -- component qualified by kind\n"+ ++ " - build foo:Data.Foo -- module qualified by component\n"+ ++ " - build foo:Data/Foo.hsc -- file qualified by component"+ ReportBuildTargetProblems targets ->+ unlines+ [ "Unrecognised build target '"+ ++ target+ ++ "'.\n"+ ++ "Expected a "+ ++ intercalate " or " expected+ ++ ", rather than '"+ ++ got+ ++ "'."+ | (target, expected, got) <- targets+ ]+ UnknownBuildTarget targets ->+ unlines+ [ "Unknown build target '"+ ++ target+ ++ "'.\nThere is no "+ ++ intercalate+ " or "+ [ mungeThing thing ++ " '" ++ got ++ "'"+ | (thing, got) <- nosuch+ ]+ ++ "."+ | (target, nosuch) <- targets+ ]+ where+ mungeThing "file" = "file target"+ mungeThing thing = thing+ AmbiguousBuildTarget targets ->+ unlines+ [ "Ambiguous build target '"+ ++ target+ ++ "'. It could be:\n "+ ++ unlines+ [ " "+ ++ ut+ ++ " ("+ ++ bt+ ++ ")"+ | (ut, bt) <- amb+ ]+ | (target, amb) <- targets+ ]+ CheckBuildTargets errorStr -> errorStr+ VersionMismatchGHC ghcProgPath ghcVersion ghcPkgProgPath ghcPkgVersion ->+ "Version mismatch between ghc and ghc-pkg: "+ ++ ghcProgPath+ ++ " is version "+ ++ prettyShow ghcVersion+ ++ " "+ ++ ghcPkgProgPath+ ++ " is version "+ ++ prettyShow ghcPkgVersion+ CheckPackageDbStackPost76 ->+ "If the global package db is specified, it must be "+ ++ "specified first and cannot be specified multiple times"+ CheckPackageDbStackPre76 ->+ "With current ghc versions the global package db is always used "+ ++ "and must be listed first. This ghc limitation is lifted in GHC 7.6,"+ ++ "see https://gitlab.haskell.org/ghc/ghc/-/issues/5977"+ GlobalPackageDbSpecifiedFirst ->+ "If the global package db is specified, it must be "+ ++ "specified first and cannot be specified multiple times"+ CantInstallForeignLib -> "Can't install foreign-library symlink on non-Linux OS"+ NoSupportForPreProcessingTest tt ->+ "No support for preprocessing test "+ ++ "suite type "+ ++ prettyShow tt+ NoSupportForPreProcessingBenchmark tt ->+ "No support for preprocessing benchmark type "+ ++ prettyShow tt+ CantFindSourceForPreProcessFile errorStr -> errorStr+ NoSupportPreProcessingTestExtras tt ->+ "No support for preprocessing test suite type "+ ++ prettyShow tt+ NoSupportPreProcessingBenchmarkExtras tt ->+ "No support for preprocessing benchmark "+ ++ "type "+ ++ prettyShow tt+ UnlitException str -> str+ RunProgramInvocationException path errors -> "'" ++ path ++ "' exited with an error:\n" ++ errors+ GetProgramInvocationException path errors -> "'" ++ path ++ "' exited with an error:\n" ++ errors+ GetProgramInvocationLBSException path errors -> "'" ++ path ++ "' exited with an error:\n" ++ errors+ CheckSemaphoreSupport ->+ "Your compiler does not support the -jsem flag. "+ ++ "To use this feature you must use GHC 9.8 or later."+ NoLibraryForPackage ->+ "The buildinfo contains info for a library, "+ ++ "but the package does not have a library."+ SanityCheckHookedBuildInfo exe1 ->+ "The buildinfo contains info for an executable called '"+ ++ prettyShow exe1+ ++ "' but the package does not have an "+ ++ "executable with that name."+ ConfigureScriptNotFound fp -> "configure script not found at " ++ fp ++ "."+ NoValidComponent -> "No valid component targets found"+ ConfigureEitherSingleOrAll -> "Can only configure either a single component or all of them"+ ConfigCIDValidForPreComponent -> "--cid is only supported for per-component configure"+ SanityCheckForEnableComponents ->+ "--enable-tests/--enable-benchmarks are incompatible with"+ ++ " explicitly specifying a component to configure."+ SanityCheckForDynamicStaticLinking ->+ "--enable-executable-dynamic and --enable-executable-static"+ ++ " are incompatible with each other."+ UnsupportedLanguages pkgId compilerId langs ->+ "The package "+ ++ prettyShow pkgId+ ++ " requires the following languages which are not "+ ++ "supported by "+ ++ prettyShow compilerId+ ++ ": "+ ++ intercalate ", " langs+ UnsupportedLanguageExtension pkgId compilerId exts ->+ "The package "+ ++ prettyShow pkgId+ ++ " requires the following language extensions which are not "+ ++ "supported by "+ ++ prettyShow compilerId+ ++ ": "+ ++ intercalate ", " exts+ CantFindForeignLibraries unsupportedFLibs ->+ "Cannot build some foreign libraries: "+ ++ intercalate ", " unsupportedFLibs+ ExpectedAbsoluteDirectory fPath -> "expected an absolute directory name for --prefix: " ++ fPath+ FlagsNotSpecified diffFlags ->+ "'--exact-configuration' was given, "+ ++ "but the following flags were not specified: "+ ++ intercalate ", " (map show diffFlags)+ EncounteredMissingDependency missing ->+ "Encountered missing or private dependencies:\n"+ ++ ( render+ . nest 4+ . sep+ . punctuate comma+ . map pretty+ $ missing+ )+ CompilerDoesn'tSupportThinning ->+ "Your compiler does not support thinning and renaming on "+ ++ "package flags. To use this feature you must use "+ ++ "GHC 7.9 or later."+ CompilerDoesn'tSupportReexports ->+ "Your compiler does not support module re-exports. To use "+ ++ "this feature you must use GHC 7.9 or later."+ CompilerDoesn'tSupportBackpack ->+ "Your compiler does not support Backpack. To use "+ ++ "this feature you must use GHC 8.1 or later."+ LibraryWithinSamePackage internalPkgDeps ->+ "The field 'build-depends: "+ ++ intercalate ", " (map (prettyShow . packageName) internalPkgDeps)+ ++ "' refers to a library which is defined within the same "+ ++ "package. To use this feature the package must specify at "+ ++ "least 'cabal-version: >= 1.8'."+ ReportFailedDependencies failed hackageUrl -> intercalate "\n\n" (map reportFailedDependency failed)+ where+ reportFailedDependency (DependencyNotExists pkgname) =+ "there is no version of "+ ++ prettyShow pkgname+ ++ " installed.\n"+ ++ "Perhaps you need to download and install it from\n"+ ++ hackageUrl+ ++ prettyShow pkgname+ ++ "?"+ reportFailedDependency (DependencyMissingInternal pkgname lib) =+ "internal dependency "+ ++ prettyShow (prettyLibraryNameComponent lib)+ ++ " not installed.\n"+ ++ "Perhaps you need to configure and install it first?\n"+ ++ "(This library was defined by "+ ++ prettyShow pkgname+ ++ ")"+ reportFailedDependency (DependencyNoVersion dep) =+ "cannot satisfy dependency " ++ prettyShow (simplifyDependency dep) ++ "\n"+ NoPackageDatabaseSpecified ->+ "No package databases have been specified. If you use "+ ++ "--package-db=clear, you must follow it with --package-db= "+ ++ "with 'global', 'user' or a specific file."+ HowToFindInstalledPackages flv ->+ "don't know how to find the installed packages for "+ ++ prettyShow flv+ PkgConfigNotFound pkg versionReq ->+ "The pkg-config package '"+ ++ pkg+ ++ "'"+ ++ versionReq+ ++ " is required but it could not be found."+ BadVersion pkg versionReq v ->+ "The pkg-config package '"+ ++ pkg+ ++ "'"+ ++ versionReq+ ++ " is required but the version installed on the"+ ++ " system is version "+ ++ prettyShow v+ UnknownCompilerException -> "Unknown compiler"+ NoWorkingGcc ->+ unlines+ [ "No working gcc"+ , "This package depends on a foreign library but we cannot "+ ++ "find a working C compiler. If you have it in a "+ ++ "non-standard location you can use the --with-gcc "+ ++ "flag to specify it."+ ]+ NoOSSupport os what ->+ "Operating system: "+ ++ prettyShow os+ ++ ", does not support "+ ++ what+ NoCompilerSupport comp ->+ "Compiler: "+ ++ comp+ ++ ", does not support relocatable builds"+ InstallDirsNotPrefixRelative installDirs -> "Installation directories are not prefix_relative:\n" ++ show installDirs+ ExplainErrors hdr libs ->+ unlines $+ [ if plural+ then "Missing dependencies on foreign libraries:"+ else "Missing dependency on a foreign library:"+ | missing+ ]+ ++ case hdr of+ Just (Left h) -> ["* Missing (or bad) header file: " ++ h]+ _ -> []+ ++ case libs of+ [] -> []+ [lib] -> ["* Missing (or bad) C library: " ++ lib]+ _ ->+ [ "* Missing (or bad) C libraries: "+ ++ intercalate ", " libs+ ]+ ++ [if plural then messagePlural else messageSingular | missing]+ ++ case hdr of+ Just (Left _) -> [headerCppMessage]+ Just (Right h) ->+ [ (if missing then "* " else "")+ ++ "Bad header file: "+ ++ h+ , headerCcMessage+ ]+ _ -> []+ where+ plural = length libs >= 2+ -- Is there something missing? (as opposed to broken)+ missing =+ not (null libs)+ || case hdr of Just (Left _) -> True; _ -> False+ messageSingular =+ "This problem can usually be solved by installing the system "+ ++ "package that provides this library (you may need the "+ ++ "\"-dev\" version). If the library is already installed "+ ++ "but in a non-standard location then you can use the flags "+ ++ "--extra-include-dirs= and --extra-lib-dirs= to specify "+ ++ "where it is."+ ++ "If the library file does exist, it may contain errors that "+ ++ "are caught by the C compiler at the preprocessing stage. "+ ++ "In this case you can re-run 'Setup configure' with the "+ ++ "verbosity flag -v3 to see the error messages."+ messagePlural =+ "This problem can usually be solved by installing the system "+ ++ "packages that provide these libraries (you may need the "+ ++ "\"-dev\" versions). If the libraries are already installed "+ ++ "but in a non-standard location then you can use the flags "+ ++ "--extra-include-dirs= and --extra-lib-dirs= to specify "+ ++ "where they are."+ ++ "If the library files do exist, it may contain errors that "+ ++ "are caught by the C compiler at the preprocessing stage. "+ ++ "In this case you can re-run 'Setup configure' with the "+ ++ "verbosity flag -v3 to see the error messages."+ headerCppMessage =+ "If the header file does exist, it may contain errors that "+ ++ "are caught by the C compiler at the preprocessing stage. "+ ++ "In this case you can re-run 'Setup configure' with the "+ ++ "verbosity flag -v3 to see the error messages."+ headerCcMessage =+ "The header file contains a compile error. "+ ++ "You can re-run 'Setup configure' with the verbosity flag "+ ++ "-v3 to see the error messages from the C compiler."+ CheckPackageProblems errors -> intercalate "\n\n" errors+ LibDirDepsPrefixNotRelative l p ->+ "Library directory of a dependency: "+ ++ show l+ ++ "\nis not relative to the installation prefix:\n"+ ++ show p+ CombinedConstraints dispDepend ->+ render $+ text "The following package dependencies were requested"+ $+$ nest 4 dispDepend+ $+$ text "however the given installed package instance does not exist."+ CantParseGHCOutput -> "Can't parse --info output of GHC"+ IncompatibleWithCabal compilerName packagePathEnvVar ->+ "Use of "+ ++ compilerName+ ++ "'s environment variable "+ ++ packagePathEnvVar+ ++ " is incompatible with Cabal. Use the "+ ++ "flag --package-db to specify a package database (it can be "+ ++ "used multiple times)."+ Couldn'tFindTestProgram cmd ->+ "Could not find test program \""+ ++ cmd+ ++ "\". Did you build the package first?"+ TestCoverageSupport -> "Test coverage is only supported for packages with a library component."+ Couldn'tFindTestProgLibV09 cmd ->+ "Could not find test program \""+ ++ cmd+ ++ "\". Did you build the package first?"+ TestCoverageSupportLibV09 -> "Test coverage is only supported for packages with a library component."+ RawSystemStdout errors -> errors+ FindFile fileName -> fileName ++ " doesn't exist"+ FindModuleFileEx mod_name extensions searchPath ->+ "Could not find module: "+ ++ prettyShow mod_name+ ++ " with any suffix: "+ ++ show (map prettyShow extensions)+ ++ " in the search path: "+ ++ show searchPath+ MultipleFilesWithExtension buildInfoExt -> "Multiple files with extension " ++ buildInfoExt+ NoDesc ->+ "No cabal file found.\n"+ ++ "Please create a package description file <pkgname>.cabal"+ MultiDesc l ->+ "Multiple cabal files found.\n"+ ++ "Please use only one of: "+ ++ intercalate ", " l+ RelocRegistrationInfo ->+ "Distribution.Simple.Register.relocRegistrationInfo: \+ \not implemented for this compiler"+ CreatePackageDB ->+ "Distribution.Simple.Register.createPackageDB: "+ ++ "not implemented for this compiler"+ WithHcPkg name ->+ "Distribution.Simple.Register."+ ++ name+ ++ ":\+ \not implemented for this compiler"+ RegisMultiplePkgNotSupported -> "Registering multiple package instances is not yet supported for this compiler"+ RegisteringNotImplemented -> "Registering is not implemented for this compiler"+ NoTestSuitesEnabled ->+ "No test suites enabled. Did you remember to 'Setup configure' with "+ ++ "\'--enable-tests\'?"+ TestNameDisabled tName ->+ "Package configured with test suite "+ ++ tName+ ++ " disabled."+ NoSuchTest tName -> "no such test: " ++ tName+ ConfigureProgram name path ->+ "Cannot find the program '"+ ++ name+ ++ "'. User-specified path '"+ ++ path+ ++ "' does not refer to an executable and "+ ++ "the program is not on the system path."+ RequireProgram progName -> "The program '" ++ progName ++ "' is required but it could not be found."+ NoProgramFound progName versionRange ->+ "The program '"+ ++ progName+ ++ "'"+ ++ versionRequirement versionRange+ ++ " is required but it could not be found."+ BadVersionDb progName version range locationPath ->+ "The program '"+ ++ progName+ ++ "'"+ ++ versionRequirement range+ ++ " is required but the version found at "+ ++ locationPath+ ++ " is version "+ ++ prettyShow version+ UnknownVersionDb progName versionRange locationPath ->+ "The program '"+ ++ progName+ ++ "'"+ ++ versionRequirement versionRange+ ++ " is required but the version of "+ ++ locationPath+ ++ " could not be determined."+ MissingCoveredInstalledLibrary unitId ->+ "Failed to find the installed unit '"+ ++ prettyShow unitId+ ++ "' in package database stack."+ SetupHooksException err ->+ setupHooksExceptionMessage err+ MultiReplDoesNotSupportComplexReexportedModules pname cname ->+ "When attempting start the repl for "+ ++ showComponentName cname+ ++ " from package "+ ++ prettyShow pname+ ++ " a module renaming was found.\n"+ ++ "Multi-repl does not work with complicated reexported-modules until GHC-9.12."
+ src/Distribution/Simple/FileMonitor/Types.hs view
@@ -0,0 +1,217 @@+{-# LANGUAGE DeriveGeneric #-}++-- |+-- Module: Distribution.Simple.FileMonitor.Types+--+-- Types for monitoring files and directories.+module Distribution.Simple.FileMonitor.Types+ ( -- * Globs with respect to a root+ RootedGlob (..)+ , FilePathRoot (..)+ , Glob++ -- * File monitoring+ , MonitorFilePath (..)+ , MonitorKindFile (..)+ , MonitorKindDir (..)++ -- ** Utility constructors of t'MonitorFilePath'+ , monitorFile+ , monitorFileHashed+ , monitorNonExistentFile+ , monitorFileExistence+ , monitorDirectory+ , monitorNonExistentDirectory+ , monitorDirectoryExistence+ , monitorFileOrDirectory+ , monitorFileGlob+ , monitorFileGlobExistence+ , monitorFileSearchPath+ , monitorFileHashedSearchPath+ )+where++import Distribution.Compat.Prelude+import Distribution.Simple.Glob.Internal+ ( Glob (..)+ )++import qualified Distribution.Compat.CharParsing as P+import Distribution.Parsec+import Distribution.Pretty+import qualified Text.PrettyPrint as Disp++--------------------------------------------------------------------------------+-- Rooted globs.+--++-- | A file path specified by globbing, relative+-- to some root directory.+data RootedGlob+ = RootedGlob+ FilePathRoot+ -- ^ what the glob is relative to+ Glob+ -- ^ the glob+ deriving (Eq, Show, Generic)++instance Binary RootedGlob+instance Structured RootedGlob++data FilePathRoot+ = FilePathRelative+ | -- | e.g. @"/"@, @"c:\"@ or result of 'takeDrive'+ FilePathRoot FilePath+ | FilePathHomeDir+ deriving (Eq, Show, Generic)++instance Binary FilePathRoot+instance Structured FilePathRoot++------------------------------------------------------------------------------+-- Types for specifying files to monitor+--++-- | A description of a file (or set of files) to monitor for changes.+--+-- Where file paths are relative they are relative to a common directory+-- (e.g. project root), not necessarily the process current directory.+data MonitorFilePath+ = MonitorFile+ { monitorKindFile :: !MonitorKindFile+ , monitorKindDir :: !MonitorKindDir+ , monitorPath :: !FilePath+ }+ | MonitorFileGlob+ { monitorKindFile :: !MonitorKindFile+ , monitorKindDir :: !MonitorKindDir+ , monitorPathGlob :: !RootedGlob+ }+ deriving (Eq, Show, Generic)++data MonitorKindFile+ = FileExists+ | FileModTime+ | FileHashed+ | FileNotExists+ deriving (Eq, Show, Generic)++data MonitorKindDir+ = DirExists+ | DirModTime+ | DirNotExists+ deriving (Eq, Show, Generic)++instance Binary MonitorFilePath+instance Binary MonitorKindFile+instance Binary MonitorKindDir++instance Structured MonitorFilePath+instance Structured MonitorKindFile+instance Structured MonitorKindDir++-- | Monitor a single file for changes, based on its modification time.+-- The monitored file is considered to have changed if it no longer+-- exists or if its modification time has changed.+monitorFile :: FilePath -> MonitorFilePath+monitorFile = MonitorFile FileModTime DirNotExists++-- | Monitor a single file for changes, based on its modification time+-- and content hash. The monitored file is considered to have changed if+-- it no longer exists or if its modification time and content hash have+-- changed.+monitorFileHashed :: FilePath -> MonitorFilePath+monitorFileHashed = MonitorFile FileHashed DirNotExists++-- | Monitor a single non-existent file for changes. The monitored file+-- is considered to have changed if it exists.+monitorNonExistentFile :: FilePath -> MonitorFilePath+monitorNonExistentFile = MonitorFile FileNotExists DirNotExists++-- | Monitor a single file for existence only. The monitored file is+-- considered to have changed if it no longer exists.+monitorFileExistence :: FilePath -> MonitorFilePath+monitorFileExistence = MonitorFile FileExists DirNotExists++-- | Monitor a single directory for changes, based on its modification+-- time. The monitored directory is considered to have changed if it no+-- longer exists or if its modification time has changed.+monitorDirectory :: FilePath -> MonitorFilePath+monitorDirectory = MonitorFile FileNotExists DirModTime++-- | Monitor a single non-existent directory for changes. The monitored+-- directory is considered to have changed if it exists.+monitorNonExistentDirectory :: FilePath -> MonitorFilePath+-- Just an alias for monitorNonExistentFile, since you can't+-- tell the difference between a non-existent directory and+-- a non-existent file :)+monitorNonExistentDirectory = monitorNonExistentFile++-- | Monitor a single directory for existence. The monitored directory is+-- considered to have changed only if it no longer exists.+monitorDirectoryExistence :: FilePath -> MonitorFilePath+monitorDirectoryExistence = MonitorFile FileNotExists DirExists++-- | Monitor a single file or directory for changes, based on its modification+-- time. The monitored file is considered to have changed if it no longer+-- exists or if its modification time has changed.+monitorFileOrDirectory :: FilePath -> MonitorFilePath+monitorFileOrDirectory = MonitorFile FileModTime DirModTime++-- | Monitor a set of files (or directories) identified by a file glob.+-- The monitored glob is considered to have changed if the set of files+-- matching the glob changes (i.e. creations or deletions), or for files if the+-- modification time and content hash of any matching file has changed.+monitorFileGlob :: RootedGlob -> MonitorFilePath+monitorFileGlob = MonitorFileGlob FileHashed DirExists++-- | Monitor a set of files (or directories) identified by a file glob for+-- existence only. The monitored glob is considered to have changed if the set+-- of files matching the glob changes (i.e. creations or deletions).+monitorFileGlobExistence :: RootedGlob -> MonitorFilePath+monitorFileGlobExistence = MonitorFileGlob FileExists DirExists++-- | Creates a list of files to monitor when you search for a file which+-- unsuccessfully looked in @notFoundAtPaths@ before finding it at+-- @foundAtPath@.+monitorFileSearchPath :: [FilePath] -> FilePath -> [MonitorFilePath]+monitorFileSearchPath notFoundAtPaths foundAtPath =+ monitorFile foundAtPath+ : map monitorNonExistentFile notFoundAtPaths++-- | Similar to 'monitorFileSearchPath', but also instructs us to+-- monitor the hash of the found file.+monitorFileHashedSearchPath :: [FilePath] -> FilePath -> [MonitorFilePath]+monitorFileHashedSearchPath notFoundAtPaths foundAtPath =+ monitorFileHashed foundAtPath+ : map monitorNonExistentFile notFoundAtPaths++------------------------------------------------------------------------------+-- Parsing & pretty-printing+--++instance Pretty RootedGlob where+ pretty (RootedGlob root pathglob) = pretty root Disp.<> pretty pathglob++instance Parsec RootedGlob where+ parsec = do+ root <- parsec+ case root of+ FilePathRelative -> RootedGlob root <$> parsec+ _ -> RootedGlob root <$> parsec <|> pure (RootedGlob root GlobDirTrailing)++instance Pretty FilePathRoot where+ pretty FilePathRelative = Disp.empty+ pretty (FilePathRoot root) = Disp.text root+ pretty FilePathHomeDir = Disp.char '~' Disp.<> Disp.char '/'++instance Parsec FilePathRoot where+ parsec = root <|> P.try home <|> P.try drive <|> pure FilePathRelative+ where+ root = FilePathRoot "/" <$ P.char '/'+ home = FilePathHomeDir <$ P.string "~/"+ drive = do+ dr <- P.satisfy $ \c -> (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')+ _ <- P.char ':'+ _ <- P.char '/' <|> P.char '\\'+ return (FilePathRoot (toUpper dr : ":\\"))
src/Distribution/Simple/Flag.hs view
@@ -1,7 +1,8 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}+ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Flag -- Copyright : Isaac Jones 2003-2004@@ -16,117 +17,109 @@ -- for an explanation. -- -- Split off from "Distribution.Simple.Setup" to break import cycles.-module Distribution.Simple.Flag (- Flag(..),- allFlags,- toFlag,- fromFlag,- fromFlagOrDefault,- flagElim,- flagToMaybe,- flagToList,- maybeToFlag,- BooleanFlag(..) ) where+module Distribution.Simple.Flag+ ( Flag+ , pattern Flag+ , pattern NoFlag+ , allFlags+ , toFlag+ , fromFlag+ , fromFlagOrDefault+ , flagElim+ , flagToMaybe+ , flagToList+ , maybeToFlag+ , mergeListFlag+ , BooleanFlag (..)+ ) where -import Prelude ()+import Data.Monoid (Last (..)) import Distribution.Compat.Prelude hiding (get) import Distribution.Compat.Stack+import Prelude () -- ------------------------------------------------------------+ -- * Flag type+ -- ------------------------------------------------------------ -- | All flags are monoids, they come in two flavours: -- -- 1. list flags eg ----- > --ghc-option=foo --ghc-option=bar+-- > --ghc-option=foo --ghc-option=bar ----- gives us all the values ["foo", "bar"]+-- gives us all the values ["foo", "bar"] -- -- 2. singular value flags, eg: ----- > --enable-foo --disable-foo+-- > --enable-foo --disable-foo ----- gives us Just False--- So this Flag type is for the latter singular kind of flag.+-- gives us Just False+--+-- So, this 'Flag' type is for the latter singular kind of flag. -- Its monoid instance gives us the behaviour where it starts out as -- 'NoFlag' and later flags override earlier ones. ---data Flag a = Flag a | NoFlag deriving (Eq, Generic, Show, Read, Typeable)--instance Binary a => Binary (Flag a)-instance Structured a => Structured (Flag a)--instance Functor Flag where- fmap f (Flag x) = Flag (f x)- fmap _ NoFlag = NoFlag--instance Applicative Flag where- (Flag x) <*> y = x <$> y- NoFlag <*> _ = NoFlag- pure = Flag--instance Monoid (Flag a) where- mempty = NoFlag- mappend = (<>)+-- Isomorphic to 'Maybe' a.+type Flag = Last -instance Semigroup (Flag a) where- _ <> f@(Flag _) = f- f <> NoFlag = f+pattern Flag :: a -> Last a+pattern Flag a = Last (Just a) -instance Bounded a => Bounded (Flag a) where- minBound = toFlag minBound- maxBound = toFlag maxBound+pattern NoFlag :: Last a+pattern NoFlag = Last Nothing -instance Enum a => Enum (Flag a) where- fromEnum = fromEnum . fromFlag- toEnum = toFlag . toEnum- enumFrom (Flag a) = map toFlag . enumFrom $ a- enumFrom _ = []- enumFromThen (Flag a) (Flag b) = toFlag `map` enumFromThen a b- enumFromThen _ _ = []- enumFromTo (Flag a) (Flag b) = toFlag `map` enumFromTo a b- enumFromTo _ _ = []- enumFromThenTo (Flag a) (Flag b) (Flag c) = toFlag `map` enumFromThenTo a b c- enumFromThenTo _ _ _ = []+{-# COMPLETE Flag, NoFlag #-} +-- | Wraps a value in 'Flag'. toFlag :: a -> Flag a toFlag = Flag +-- | Extracts a value from a 'Flag', and throws an exception on 'NoFlag'. fromFlag :: WithCallStack (Flag a -> a) fromFlag (Flag x) = x-fromFlag NoFlag = error "fromFlag NoFlag. Use fromFlagOrDefault"+fromFlag NoFlag = error "fromFlag NoFlag. Use fromFlagOrDefault" +-- | Extracts a value from a 'Flag', and returns the default value on 'NoFlag'. fromFlagOrDefault :: a -> Flag a -> a-fromFlagOrDefault _ (Flag x) = x-fromFlagOrDefault def NoFlag = def+fromFlagOrDefault def = fromMaybe def . getLast +-- | Converts a 'Flag' value to a 'Maybe' value. flagToMaybe :: Flag a -> Maybe a-flagToMaybe (Flag x) = Just x-flagToMaybe NoFlag = Nothing+flagToMaybe = getLast --- | @since 3.4.0.0+-- | Pushes a function through a 'Flag' value, and returns a default+-- if the 'Flag' value is 'NoFlag'.+--+-- @since 3.4.0.0 flagElim :: b -> (a -> b) -> Flag a -> b-flagElim n _ NoFlag = n-flagElim _ f (Flag x) = f x+flagElim n f = maybe n f . getLast +-- | Converts a 'Flag' value to a list. flagToList :: Flag a -> [a]-flagToList (Flag x) = [x]-flagToList NoFlag = []+flagToList = maybeToList . getLast +-- | Returns 'True' only if every 'Flag' 'Bool' value is Flag True, else 'False'. allFlags :: [Flag Bool] -> Flag Bool-allFlags flags = if all (\f -> fromFlagOrDefault False f) flags- then Flag True- else NoFlag+allFlags flags =+ if all (\f -> fromFlagOrDefault False f) flags+ then Flag True+ else NoFlag +-- | Converts a 'Maybe' value to a 'Flag' value. maybeToFlag :: Maybe a -> Flag a-maybeToFlag Nothing = NoFlag-maybeToFlag (Just x) = Flag x+maybeToFlag = Last +-- | Merge the elements of a list 'Flag' with another list 'Flag'.+mergeListFlag :: Flag [a] -> Flag [a] -> Flag [a]+mergeListFlag currentFlags v =+ Flag $ concat (flagToList currentFlags ++ flagToList v)+ -- | Types that represent boolean flags. class BooleanFlag a where- asBool :: a -> Bool+ asBool :: a -> Bool instance BooleanFlag Bool where asBool = id
src/Distribution/Simple/GHC.hs view
@@ -1,2088 +1,1166 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE MultiWayIf #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE CPP #-}---------------------------------------------------------------------------------- |--- Module : Distribution.Simple.GHC--- Copyright : Isaac Jones 2003-2007--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ This is a fairly large module. It contains most of the GHC-specific code for--- configuring, building and installing packages. It also exports a function--- for finding out what packages are already installed. Configuring involves--- finding the @ghc@ and @ghc-pkg@ programs, finding what language extensions--- this version of ghc supports and returning a 'Compiler' value.------ 'getInstalledPackages' involves calling the @ghc-pkg@ program to find out--- what packages are installed.------ Building is somewhat complex as there is quite a bit of information to take--- into account. We have to build libs and programs, possibly for profiling and--- shared libs. We have to support building libraries that will be usable by--- GHCi and also ghc's @-split-objs@ feature. We have to compile any C files--- using ghc. Linking, especially for @split-objs@ is remarkably complex,--- partly because there tend to be 1,000's of @.o@ files and this can often be--- more than we can pass to the @ld@ or @ar@ programs in one go.------ Installing for libs and exes involves finding the right files and copying--- them to the right places. One of the more tricky things about this module is--- remembering the layout of files in the build directory (which is not--- explicitly documented) and thus what search dirs are used for various kinds--- of files.--module Distribution.Simple.GHC (- getGhcInfo,- configure,- getInstalledPackages,- getInstalledPackagesMonitorFiles,- getPackageDBContents,- buildLib, buildFLib, buildExe,- replLib, replFLib, replExe,- startInterpreter,- installLib, installFLib, installExe,- libAbiHash,- hcPkgInfo,- registerPackage,- componentGhcOptions,- componentCcGhcOptions,- getGhcAppDir,- getLibDir,- isDynamic,- getGlobalPackageDB,- pkgRoot,- -- * Constructing and deconstructing GHC environment files- Internal.GhcEnvironmentFileEntry(..),- Internal.simpleGhcEnvironmentFile,- Internal.renderGhcEnvironmentFile,- Internal.writeGhcEnvironmentFile,- Internal.ghcPlatformAndVersionString,- readGhcEnvironmentFile,- parseGhcEnvironmentFile,- ParseErrorExc(..),- -- * Version-specific implementation quirks- getImplInfo,- GhcImplInfo(..)- ) where--import Prelude ()-import Distribution.Compat.Prelude--import qualified Distribution.Simple.GHC.Internal as Internal-import Distribution.CabalSpecVersion-import Distribution.Simple.GHC.ImplInfo-import Distribution.Simple.GHC.EnvironmentParser-import Distribution.PackageDescription.Utils (cabalBug)-import Distribution.PackageDescription as PD-import Distribution.InstalledPackageInfo (InstalledPackageInfo)-import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo-import Distribution.Simple.PackageIndex (InstalledPackageIndex)-import qualified Distribution.Simple.PackageIndex as PackageIndex-import Distribution.Simple.LocalBuildInfo-import Distribution.Types.ComponentLocalBuildInfo-import qualified Distribution.Simple.Hpc as Hpc-import Distribution.Simple.BuildPaths-import Distribution.Simple.Utils-import Distribution.Package-import qualified Distribution.ModuleName as ModuleName-import Distribution.ModuleName (ModuleName)-import Distribution.Simple.Program-import Distribution.Simple.Program.Builtin (runghcProgram)-import qualified Distribution.Simple.Program.HcPkg as HcPkg-import qualified Distribution.Simple.Program.Ar as Ar-import qualified Distribution.Simple.Program.Ld as Ld-import qualified Distribution.Simple.Program.Strip as Strip-import Distribution.Simple.Program.GHC-import Distribution.Simple.Setup-import qualified Distribution.Simple.Setup as Cabal-import Distribution.Simple.Compiler-import Distribution.Version-import Distribution.System-import Distribution.Verbosity-import Distribution.Pretty-import Distribution.Utils.NubList-import Distribution.Utils.Path-import Language.Haskell.Extension--import Control.Monad (msum, forM_)-import Data.Char (isLower)-import qualified Data.Map as Map-import System.Directory- ( doesFileExist, getAppUserDataDirectory, createDirectoryIfMissing- , canonicalizePath, removeFile, renameFile, getDirectoryContents )-import System.FilePath ( (</>), (<.>), takeExtension- , takeDirectory, replaceExtension- ,isRelative )-import qualified System.Info-#ifndef mingw32_HOST_OS-import System.Posix (createSymbolicLink)-#endif /* mingw32_HOST_OS */---- -------------------------------------------------------------------------------- Configuring--configure :: Verbosity -> Maybe FilePath -> Maybe FilePath- -> ProgramDb- -> IO (Compiler, Maybe Platform, ProgramDb)-configure verbosity hcPath hcPkgPath conf0 = do-- (ghcProg, ghcVersion, progdb1) <-- requireProgramVersion verbosity ghcProgram- (orLaterVersion (mkVersion [7,0,1]))- (userMaybeSpecifyPath "ghc" hcPath conf0)- let implInfo = ghcVersionImplInfo ghcVersion-- -- Cabal currently supports ghc >= 7.0.1 && < 9.4- -- ... and the following odd development version- unless (ghcVersion < mkVersion [9,4]) $- warn verbosity $- "Unknown/unsupported 'ghc' version detected "- ++ "(Cabal " ++ prettyShow cabalVersion ++ " supports 'ghc' version < 9.4): "- ++ programPath ghcProg ++ " is version " ++ prettyShow ghcVersion-- -- This is slightly tricky, we have to configure ghc first, then we use the- -- location of ghc to help find ghc-pkg in the case that the user did not- -- specify the location of ghc-pkg directly:- (ghcPkgProg, ghcPkgVersion, progdb2) <-- requireProgramVersion verbosity ghcPkgProgram {- programFindLocation = guessGhcPkgFromGhcPath ghcProg- }- anyVersion (userMaybeSpecifyPath "ghc-pkg" hcPkgPath progdb1)-- when (ghcVersion /= ghcPkgVersion) $ die' verbosity $- "Version mismatch between ghc and ghc-pkg: "- ++ programPath ghcProg ++ " is version " ++ prettyShow ghcVersion ++ " "- ++ programPath ghcPkgProg ++ " is version " ++ prettyShow ghcPkgVersion-- -- Likewise we try to find the matching hsc2hs and haddock programs.- let hsc2hsProgram' = hsc2hsProgram {- programFindLocation = guessHsc2hsFromGhcPath ghcProg- }- haddockProgram' = haddockProgram {- programFindLocation = guessHaddockFromGhcPath ghcProg- }- hpcProgram' = hpcProgram {- programFindLocation = guessHpcFromGhcPath ghcProg- }- runghcProgram' = runghcProgram {- programFindLocation = guessRunghcFromGhcPath ghcProg- }- progdb3 = addKnownProgram haddockProgram' $- addKnownProgram hsc2hsProgram' $- addKnownProgram hpcProgram' $- addKnownProgram runghcProgram' progdb2-- languages <- Internal.getLanguages verbosity implInfo ghcProg- extensions0 <- Internal.getExtensions verbosity implInfo ghcProg-- ghcInfo <- Internal.getGhcInfo verbosity implInfo ghcProg- let ghcInfoMap = Map.fromList ghcInfo- extensions = -- workaround https://gitlab.haskell.org/ghc/ghc/-/issues/11214- filterExt JavaScriptFFI $- -- see 'filterExtTH' comment below- filterExtTH $ extensions0-- -- starting with GHC 8.0, `TemplateHaskell` will be omitted from- -- `--supported-extensions` when it's not available.- -- for older GHCs we can use the "Have interpreter" property to- -- filter out `TemplateHaskell`- filterExtTH | ghcVersion < mkVersion [8]- , Just "NO" <- Map.lookup "Have interpreter" ghcInfoMap- = filterExt TemplateHaskell- | otherwise = id-- filterExt ext = filter ((/= EnableExtension ext) . fst)-- let comp = Compiler {- compilerId = CompilerId GHC ghcVersion,- compilerAbiTag = NoAbiTag,- compilerCompat = [],- compilerLanguages = languages,- compilerExtensions = extensions,- compilerProperties = ghcInfoMap- }- compPlatform = Internal.targetPlatform ghcInfo- -- configure gcc and ld- progdb4 = Internal.configureToolchain implInfo ghcProg ghcInfoMap progdb3- return (comp, compPlatform, progdb4)---- | Given something like /usr/local/bin/ghc-6.6.1(.exe) we try and find--- the corresponding tool; e.g. if the tool is ghc-pkg, we try looking--- for a versioned or unversioned ghc-pkg in the same dir, that is:------ > /usr/local/bin/ghc-pkg-ghc-6.6.1(.exe)--- > /usr/local/bin/ghc-pkg-6.6.1(.exe)--- > /usr/local/bin/ghc-pkg(.exe)----guessToolFromGhcPath :: Program -> ConfiguredProgram- -> Verbosity -> ProgramSearchPath- -> IO (Maybe (FilePath, [FilePath]))-guessToolFromGhcPath tool ghcProg verbosity searchpath- = do let toolname = programName tool- given_path = programPath ghcProg- given_dir = takeDirectory given_path- real_path <- canonicalizePath given_path- let real_dir = takeDirectory real_path- versionSuffix path = takeVersionSuffix (dropExeExtension path)- given_suf = versionSuffix given_path- real_suf = versionSuffix real_path- guessNormal dir = dir </> toolname <.> exeExtension buildPlatform- guessGhcVersioned dir suf = dir </> (toolname ++ "-ghc" ++ suf)- <.> exeExtension buildPlatform- guessVersioned dir suf = dir </> (toolname ++ suf)- <.> exeExtension buildPlatform- mkGuesses dir suf | null suf = [guessNormal dir]- | otherwise = [guessGhcVersioned dir suf,- guessVersioned dir suf,- guessNormal dir]- -- order matters here, see https://github.com/haskell/cabal/issues/7390- guesses = (if real_path == given_path- then []- else mkGuesses real_dir real_suf)- ++ mkGuesses given_dir given_suf- info verbosity $ "looking for tool " ++ toolname- ++ " near compiler in " ++ given_dir- debug verbosity $ "candidate locations: " ++ show guesses- exists <- traverse doesFileExist guesses- case [ file | (file, True) <- zip guesses exists ] of- -- If we can't find it near ghc, fall back to the usual- -- method.- [] -> programFindLocation tool verbosity searchpath- (fp:_) -> do info verbosity $ "found " ++ toolname ++ " in " ++ fp- let lookedAt = map fst- . takeWhile (\(_file, exist) -> not exist)- $ zip guesses exists- return (Just (fp, lookedAt))-- where takeVersionSuffix :: FilePath -> String- takeVersionSuffix = takeWhileEndLE isSuffixChar-- isSuffixChar :: Char -> Bool- isSuffixChar c = isDigit c || c == '.' || c == '-'---- | Given something like /usr/local/bin/ghc-6.6.1(.exe) we try and find a--- corresponding ghc-pkg, we try looking for both a versioned and unversioned--- ghc-pkg in the same dir, that is:------ > /usr/local/bin/ghc-pkg-ghc-6.6.1(.exe)--- > /usr/local/bin/ghc-pkg-6.6.1(.exe)--- > /usr/local/bin/ghc-pkg(.exe)----guessGhcPkgFromGhcPath :: ConfiguredProgram- -> Verbosity -> ProgramSearchPath- -> IO (Maybe (FilePath, [FilePath]))-guessGhcPkgFromGhcPath = guessToolFromGhcPath ghcPkgProgram---- | Given something like /usr/local/bin/ghc-6.6.1(.exe) we try and find a--- corresponding hsc2hs, we try looking for both a versioned and unversioned--- hsc2hs in the same dir, that is:------ > /usr/local/bin/hsc2hs-ghc-6.6.1(.exe)--- > /usr/local/bin/hsc2hs-6.6.1(.exe)--- > /usr/local/bin/hsc2hs(.exe)----guessHsc2hsFromGhcPath :: ConfiguredProgram- -> Verbosity -> ProgramSearchPath- -> IO (Maybe (FilePath, [FilePath]))-guessHsc2hsFromGhcPath = guessToolFromGhcPath hsc2hsProgram---- | Given something like /usr/local/bin/ghc-6.6.1(.exe) we try and find a--- corresponding haddock, we try looking for both a versioned and unversioned--- haddock in the same dir, that is:------ > /usr/local/bin/haddock-ghc-6.6.1(.exe)--- > /usr/local/bin/haddock-6.6.1(.exe)--- > /usr/local/bin/haddock(.exe)----guessHaddockFromGhcPath :: ConfiguredProgram- -> Verbosity -> ProgramSearchPath- -> IO (Maybe (FilePath, [FilePath]))-guessHaddockFromGhcPath = guessToolFromGhcPath haddockProgram--guessHpcFromGhcPath :: ConfiguredProgram- -> Verbosity -> ProgramSearchPath- -> IO (Maybe (FilePath, [FilePath]))-guessHpcFromGhcPath = guessToolFromGhcPath hpcProgram--guessRunghcFromGhcPath :: ConfiguredProgram- -> Verbosity -> ProgramSearchPath- -> IO (Maybe (FilePath, [FilePath]))-guessRunghcFromGhcPath = guessToolFromGhcPath runghcProgram---getGhcInfo :: Verbosity -> ConfiguredProgram -> IO [(String, String)]-getGhcInfo verbosity ghcProg = Internal.getGhcInfo verbosity implInfo ghcProg- where- version = fromMaybe (error "GHC.getGhcInfo: no ghc version") $ programVersion ghcProg- implInfo = ghcVersionImplInfo version---- | Given a single package DB, return all installed packages.-getPackageDBContents :: Verbosity -> PackageDB -> ProgramDb- -> IO InstalledPackageIndex-getPackageDBContents verbosity packagedb progdb = do- pkgss <- getInstalledPackages' verbosity [packagedb] progdb- toPackageIndex verbosity pkgss progdb---- | Given a package DB stack, return all installed packages.-getInstalledPackages :: Verbosity -> Compiler -> PackageDBStack- -> ProgramDb- -> IO InstalledPackageIndex-getInstalledPackages verbosity comp packagedbs progdb = do- checkPackageDbEnvVar verbosity- checkPackageDbStack verbosity comp packagedbs- pkgss <- getInstalledPackages' verbosity packagedbs progdb- index <- toPackageIndex verbosity pkgss progdb- return $! hackRtsPackage index-- where- hackRtsPackage index =- case PackageIndex.lookupPackageName index (mkPackageName "rts") of- [(_,[rts])]- -> PackageIndex.insert (removeMingwIncludeDir rts) index- _ -> index -- No (or multiple) ghc rts package is registered!!- -- Feh, whatever, the ghc test suite does some crazy stuff.---- | Given a list of @(PackageDB, InstalledPackageInfo)@ pairs, produce a--- @PackageIndex@. Helper function used by 'getPackageDBContents' and--- 'getInstalledPackages'.-toPackageIndex :: Verbosity- -> [(PackageDB, [InstalledPackageInfo])]- -> ProgramDb- -> IO InstalledPackageIndex-toPackageIndex verbosity pkgss progdb = do- -- On Windows, various fields have $topdir/foo rather than full- -- paths. We need to substitute the right value in so that when- -- we, for example, call gcc, we have proper paths to give it.- topDir <- getLibDir' verbosity ghcProg- let indices = [ PackageIndex.fromList (map (Internal.substTopDir topDir) pkgs)- | (_, pkgs) <- pkgss ]- return $! mconcat indices-- where- ghcProg = fromMaybe (error "GHC.toPackageIndex: no ghc program") $ lookupProgram ghcProgram progdb---- | Return the 'FilePath' to the GHC application data directory.------ @since 3.4.0.0-getGhcAppDir :: IO FilePath-getGhcAppDir = getAppUserDataDirectory "ghc"--getLibDir :: Verbosity -> LocalBuildInfo -> IO FilePath-getLibDir verbosity lbi =- dropWhileEndLE isSpace `fmap`- getDbProgramOutput verbosity ghcProgram- (withPrograms lbi) ["--print-libdir"]--getLibDir' :: Verbosity -> ConfiguredProgram -> IO FilePath-getLibDir' verbosity ghcProg =- dropWhileEndLE isSpace `fmap`- getProgramOutput verbosity ghcProg ["--print-libdir"]----- | Return the 'FilePath' to the global GHC package database.-getGlobalPackageDB :: Verbosity -> ConfiguredProgram -> IO FilePath-getGlobalPackageDB verbosity ghcProg =- dropWhileEndLE isSpace `fmap`- getProgramOutput verbosity ghcProg ["--print-global-package-db"]---- | Return the 'FilePath' to the per-user GHC package database.-getUserPackageDB- :: Verbosity -> ConfiguredProgram -> Platform -> IO FilePath-getUserPackageDB _verbosity ghcProg platform = do- -- It's rather annoying that we have to reconstruct this, because ghc- -- hides this information from us otherwise. But for certain use cases- -- like change monitoring it really can't remain hidden.- appdir <- getGhcAppDir- return (appdir </> platformAndVersion </> packageConfFileName)- where- platformAndVersion = Internal.ghcPlatformAndVersionString- platform ghcVersion- packageConfFileName = "package.conf.d"- ghcVersion = fromMaybe (error "GHC.getUserPackageDB: no ghc version") $ programVersion ghcProg--checkPackageDbEnvVar :: Verbosity -> IO ()-checkPackageDbEnvVar verbosity =- Internal.checkPackageDbEnvVar verbosity "GHC" "GHC_PACKAGE_PATH"--checkPackageDbStack :: Verbosity -> Compiler -> PackageDBStack -> IO ()-checkPackageDbStack verbosity comp =- if flagPackageConf implInfo- then checkPackageDbStackPre76 verbosity- else checkPackageDbStackPost76 verbosity- where implInfo = ghcVersionImplInfo (compilerVersion comp)--checkPackageDbStackPost76 :: Verbosity -> PackageDBStack -> IO ()-checkPackageDbStackPost76 _ (GlobalPackageDB:rest)- | GlobalPackageDB `notElem` rest = return ()-checkPackageDbStackPost76 verbosity rest- | GlobalPackageDB `elem` rest =- die' verbosity $ "If the global package db is specified, it must be "- ++ "specified first and cannot be specified multiple times"-checkPackageDbStackPost76 _ _ = return ()--checkPackageDbStackPre76 :: Verbosity -> PackageDBStack -> IO ()-checkPackageDbStackPre76 _ (GlobalPackageDB:rest)- | GlobalPackageDB `notElem` rest = return ()-checkPackageDbStackPre76 verbosity rest- | GlobalPackageDB `notElem` rest =- die' verbosity $- "With current ghc versions the global package db is always used "- ++ "and must be listed first. This ghc limitation is lifted in GHC 7.6,"- ++ "see https://gitlab.haskell.org/ghc/ghc/-/issues/5977"-checkPackageDbStackPre76 verbosity _ =- die' verbosity $ "If the global package db is specified, it must be "- ++ "specified first and cannot be specified multiple times"---- GHC < 6.10 put "$topdir/include/mingw" in rts's installDirs. This--- breaks when you want to use a different gcc, so we need to filter--- it out.-removeMingwIncludeDir :: InstalledPackageInfo -> InstalledPackageInfo-removeMingwIncludeDir pkg =- let ids = InstalledPackageInfo.includeDirs pkg- ids' = filter (not . ("mingw" `isSuffixOf`)) ids- in pkg { InstalledPackageInfo.includeDirs = ids' }---- | Get the packages from specific PackageDBs, not cumulative.----getInstalledPackages' :: Verbosity -> [PackageDB] -> ProgramDb- -> IO [(PackageDB, [InstalledPackageInfo])]-getInstalledPackages' verbosity packagedbs progdb =- sequenceA- [ do pkgs <- HcPkg.dump (hcPkgInfo progdb) verbosity packagedb- return (packagedb, pkgs)- | packagedb <- packagedbs ]--getInstalledPackagesMonitorFiles :: Verbosity -> Platform- -> ProgramDb- -> [PackageDB]- -> IO [FilePath]-getInstalledPackagesMonitorFiles verbosity platform progdb =- traverse getPackageDBPath- where- getPackageDBPath :: PackageDB -> IO FilePath- getPackageDBPath GlobalPackageDB =- selectMonitorFile =<< getGlobalPackageDB verbosity ghcProg-- getPackageDBPath UserPackageDB =- selectMonitorFile =<< getUserPackageDB verbosity ghcProg platform-- getPackageDBPath (SpecificPackageDB path) = selectMonitorFile path-- -- GHC has old style file dbs, and new style directory dbs.- -- Note that for dir style dbs, we only need to monitor the cache file, not- -- the whole directory. The ghc program itself only reads the cache file- -- so it's safe to only monitor this one file.- selectMonitorFile path = do- isFileStyle <- doesFileExist path- if isFileStyle then return path- else return (path </> "package.cache")-- ghcProg = fromMaybe (error "GHC.toPackageIndex: no ghc program") $ lookupProgram ghcProgram progdb----- -------------------------------------------------------------------------------- Building a library--buildLib :: Verbosity -> Cabal.Flag (Maybe Int)- -> PackageDescription -> LocalBuildInfo- -> Library -> ComponentLocalBuildInfo -> IO ()-buildLib = buildOrReplLib Nothing--replLib :: [String] -> Verbosity- -> Cabal.Flag (Maybe Int) -> PackageDescription- -> LocalBuildInfo -> Library- -> ComponentLocalBuildInfo -> IO ()-replLib = buildOrReplLib . Just--buildOrReplLib :: Maybe [String] -> Verbosity- -> Cabal.Flag (Maybe Int) -> PackageDescription- -> LocalBuildInfo -> Library- -> ComponentLocalBuildInfo -> IO ()-buildOrReplLib mReplFlags verbosity numJobs pkg_descr lbi lib clbi = do- let uid = componentUnitId clbi- libTargetDir = componentBuildDir lbi clbi- whenVanillaLib forceVanilla =- when (forceVanilla || withVanillaLib lbi)- whenProfLib = when (withProfLib lbi)- whenSharedLib forceShared =- when (forceShared || withSharedLib lbi)- whenStaticLib forceStatic =- when (forceStatic || withStaticLib lbi)- whenGHCiLib = when (withGHCiLib lbi)- forRepl = maybe False (const True) mReplFlags- whenReplLib = when forRepl- replFlags = fromMaybe mempty mReplFlags- comp = compiler lbi- ghcVersion = compilerVersion comp- implInfo = getImplInfo comp- platform@(Platform _hostArch hostOS) = hostPlatform lbi- has_code = not (componentIsIndefinite clbi)-- (ghcProg, _) <- requireProgram verbosity ghcProgram (withPrograms lbi)- let runGhcProg = runGHC verbosity ghcProg comp platform-- let libBi = libBuildInfo lib-- let isGhcDynamic = isDynamic comp- dynamicTooSupported = supportsDynamicToo comp- doingTH = usesTemplateHaskellOrQQ libBi- forceVanillaLib = doingTH && not isGhcDynamic- forceSharedLib = doingTH && isGhcDynamic- -- TH always needs default libs, even when building for profiling-- -- Determine if program coverage should be enabled and if so, what- -- '-hpcdir' should be.- let isCoverageEnabled = libCoverage lbi- -- TODO: Historically HPC files have been put into a directory which- -- has the package name. I'm going to avoid changing this for- -- now, but it would probably be better for this to be the- -- component ID instead...- pkg_name = prettyShow (PD.package pkg_descr)- distPref = fromFlag $ configDistPref $ configFlags lbi- hpcdir way- | forRepl = mempty -- HPC is not supported in ghci- | isCoverageEnabled = toFlag $ Hpc.mixDir distPref way pkg_name- | otherwise = mempty-- createDirectoryIfMissingVerbose verbosity True libTargetDir- -- TODO: do we need to put hs-boot files into place for mutually recursive- -- modules?- let cLikeSources = fromNubListR $ mconcat- [ toNubListR (cSources libBi)- , toNubListR (cxxSources libBi)- , toNubListR (cmmSources libBi)- , toNubListR (asmSources libBi)- ]- cLikeObjs = map (`replaceExtension` objExtension) cLikeSources- baseOpts = componentGhcOptions verbosity lbi libBi clbi libTargetDir- vanillaOpts = baseOpts `mappend` mempty {- ghcOptMode = toFlag GhcModeMake,- ghcOptNumJobs = numJobs,- ghcOptInputModules = toNubListR $ allLibModules lib clbi,- ghcOptHPCDir = hpcdir Hpc.Vanilla- }-- profOpts = vanillaOpts `mappend` mempty {- ghcOptProfilingMode = toFlag True,- ghcOptProfilingAuto = Internal.profDetailLevelFlag True- (withProfLibDetail lbi),- ghcOptHiSuffix = toFlag "p_hi",- ghcOptObjSuffix = toFlag "p_o",- ghcOptExtra = hcProfOptions GHC libBi,- ghcOptHPCDir = hpcdir Hpc.Prof- }-- sharedOpts = vanillaOpts `mappend` mempty {- ghcOptDynLinkMode = toFlag GhcDynamicOnly,- ghcOptFPic = toFlag True,- ghcOptHiSuffix = toFlag "dyn_hi",- ghcOptObjSuffix = toFlag "dyn_o",- ghcOptExtra = hcSharedOptions GHC libBi,- ghcOptHPCDir = hpcdir Hpc.Dyn- }- linkerOpts = mempty {- ghcOptLinkOptions = PD.ldOptions libBi- ++ [ "-static"- | withFullyStaticExe lbi ]- -- Pass extra `ld-options` given- -- through to GHC's linker.- ++ maybe [] programOverrideArgs- (lookupProgram ldProgram (withPrograms lbi)),- ghcOptLinkLibs = extraLibs libBi,- ghcOptLinkLibPath = toNubListR $ extraLibDirs libBi,- ghcOptLinkFrameworks = toNubListR $ PD.frameworks libBi,- ghcOptLinkFrameworkDirs = toNubListR $- PD.extraFrameworkDirs libBi,- ghcOptInputFiles = toNubListR- [libTargetDir </> x | x <- cLikeObjs]- }- replOpts = vanillaOpts {- ghcOptExtra = Internal.filterGhciFlags- (ghcOptExtra vanillaOpts)- <> replFlags,- ghcOptNumJobs = mempty- }- `mappend` linkerOpts- `mappend` mempty {- ghcOptMode = toFlag GhcModeInteractive,- ghcOptOptimisation = toFlag GhcNoOptimisation- }-- vanillaSharedOpts = vanillaOpts `mappend` mempty {- ghcOptDynLinkMode = toFlag GhcStaticAndDynamic,- ghcOptDynHiSuffix = toFlag "dyn_hi",- ghcOptDynObjSuffix = toFlag "dyn_o",- ghcOptHPCDir = hpcdir Hpc.Dyn- }-- unless (forRepl || null (allLibModules lib clbi)) $- do let vanilla = whenVanillaLib forceVanillaLib (runGhcProg vanillaOpts)- shared = whenSharedLib forceSharedLib (runGhcProg sharedOpts)- useDynToo = dynamicTooSupported &&- (forceVanillaLib || withVanillaLib lbi) &&- (forceSharedLib || withSharedLib lbi) &&- null (hcSharedOptions GHC libBi)- if not has_code- then vanilla- else- if useDynToo- then do- runGhcProg vanillaSharedOpts- case (hpcdir Hpc.Dyn, hpcdir Hpc.Vanilla) of- (Cabal.Flag dynDir, Cabal.Flag vanillaDir) ->- -- When the vanilla and shared library builds are done- -- in one pass, only one set of HPC module interfaces- -- are generated. This set should suffice for both- -- static and dynamically linked executables. We copy- -- the modules interfaces so they are available under- -- both ways.- copyDirectoryRecursive verbosity dynDir vanillaDir- _ -> return ()- else if isGhcDynamic- then do shared; vanilla- else do vanilla; shared- whenProfLib (runGhcProg profOpts)-- -- Build any C++ sources separately.- unless (not has_code || null (cxxSources libBi)) $ do- info verbosity "Building C++ Sources..."- sequence_- [ do let baseCxxOpts = Internal.componentCxxGhcOptions verbosity implInfo- lbi libBi clbi libTargetDir filename- vanillaCxxOpts = if isGhcDynamic- then baseCxxOpts { ghcOptFPic = toFlag True }- else baseCxxOpts- profCxxOpts = vanillaCxxOpts `mappend` mempty {- ghcOptProfilingMode = toFlag True,- ghcOptObjSuffix = toFlag "p_o"- }- sharedCxxOpts = vanillaCxxOpts `mappend` mempty {- ghcOptFPic = toFlag True,- ghcOptDynLinkMode = toFlag GhcDynamicOnly,- ghcOptObjSuffix = toFlag "dyn_o"- }- odir = fromFlag (ghcOptObjDir vanillaCxxOpts)- createDirectoryIfMissingVerbose verbosity True odir- let runGhcProgIfNeeded cxxOpts = do- needsRecomp <- checkNeedsRecompilation filename cxxOpts- when needsRecomp $ runGhcProg cxxOpts- runGhcProgIfNeeded vanillaCxxOpts- unless forRepl $- whenSharedLib forceSharedLib (runGhcProgIfNeeded sharedCxxOpts)- unless forRepl $ whenProfLib (runGhcProgIfNeeded profCxxOpts)- | filename <- cxxSources libBi]-- -- build any C sources- unless (not has_code || null (cSources libBi)) $ do- info verbosity "Building C Sources..."- sequence_- [ do let baseCcOpts = Internal.componentCcGhcOptions verbosity implInfo- lbi libBi clbi libTargetDir filename- vanillaCcOpts = if isGhcDynamic- -- Dynamic GHC requires C sources to be built- -- with -fPIC for REPL to work. See #2207.- then baseCcOpts { ghcOptFPic = toFlag True }- else baseCcOpts- profCcOpts = vanillaCcOpts `mappend` mempty {- ghcOptProfilingMode = toFlag True,- ghcOptObjSuffix = toFlag "p_o"- }- sharedCcOpts = vanillaCcOpts `mappend` mempty {- ghcOptFPic = toFlag True,- ghcOptDynLinkMode = toFlag GhcDynamicOnly,- ghcOptObjSuffix = toFlag "dyn_o"- }- odir = fromFlag (ghcOptObjDir vanillaCcOpts)- createDirectoryIfMissingVerbose verbosity True odir- let runGhcProgIfNeeded ccOpts = do- needsRecomp <- checkNeedsRecompilation filename ccOpts- when needsRecomp $ runGhcProg ccOpts- runGhcProgIfNeeded vanillaCcOpts- unless forRepl $- whenSharedLib forceSharedLib (runGhcProgIfNeeded sharedCcOpts)- unless forRepl $ whenProfLib (runGhcProgIfNeeded profCcOpts)- | filename <- cSources libBi]-- -- build any ASM sources- unless (not has_code || null (asmSources libBi)) $ do- info verbosity "Building Assembler Sources..."- sequence_- [ do let baseAsmOpts = Internal.componentAsmGhcOptions verbosity implInfo- lbi libBi clbi libTargetDir filename- vanillaAsmOpts = if isGhcDynamic- -- Dynamic GHC requires objects to be built- -- with -fPIC for REPL to work. See #2207.- then baseAsmOpts { ghcOptFPic = toFlag True }- else baseAsmOpts- profAsmOpts = vanillaAsmOpts `mappend` mempty {- ghcOptProfilingMode = toFlag True,- ghcOptObjSuffix = toFlag "p_o"- }- sharedAsmOpts = vanillaAsmOpts `mappend` mempty {- ghcOptFPic = toFlag True,- ghcOptDynLinkMode = toFlag GhcDynamicOnly,- ghcOptObjSuffix = toFlag "dyn_o"- }- odir = fromFlag (ghcOptObjDir vanillaAsmOpts)- createDirectoryIfMissingVerbose verbosity True odir- let runGhcProgIfNeeded asmOpts = do- needsRecomp <- checkNeedsRecompilation filename asmOpts- when needsRecomp $ runGhcProg asmOpts- runGhcProgIfNeeded vanillaAsmOpts- unless forRepl $- whenSharedLib forceSharedLib (runGhcProgIfNeeded sharedAsmOpts)- unless forRepl $ whenProfLib (runGhcProgIfNeeded profAsmOpts)- | filename <- asmSources libBi]-- -- build any Cmm sources- unless (not has_code || null (cmmSources libBi)) $ do- info verbosity "Building C-- Sources..."- sequence_- [ do let baseCmmOpts = Internal.componentCmmGhcOptions verbosity implInfo- lbi libBi clbi libTargetDir filename- vanillaCmmOpts = if isGhcDynamic- -- Dynamic GHC requires C sources to be built- -- with -fPIC for REPL to work. See #2207.- then baseCmmOpts { ghcOptFPic = toFlag True }- else baseCmmOpts- profCmmOpts = vanillaCmmOpts `mappend` mempty {- ghcOptProfilingMode = toFlag True,- ghcOptObjSuffix = toFlag "p_o"- }- sharedCmmOpts = vanillaCmmOpts `mappend` mempty {- ghcOptFPic = toFlag True,- ghcOptDynLinkMode = toFlag GhcDynamicOnly,- ghcOptObjSuffix = toFlag "dyn_o"- }- odir = fromFlag (ghcOptObjDir vanillaCmmOpts)- createDirectoryIfMissingVerbose verbosity True odir- let runGhcProgIfNeeded cmmOpts = do- needsRecomp <- checkNeedsRecompilation filename cmmOpts- when needsRecomp $ runGhcProg cmmOpts- runGhcProgIfNeeded vanillaCmmOpts- unless forRepl $- whenSharedLib forceSharedLib (runGhcProgIfNeeded sharedCmmOpts)- unless forRepl $ whenProfLib (runGhcProgIfNeeded profCmmOpts)- | filename <- cmmSources libBi]-- -- TODO: problem here is we need the .c files built first, so we can load them- -- with ghci, but .c files can depend on .h files generated by ghc by ffi- -- exports.- whenReplLib $ do- when (null (allLibModules lib clbi)) $ warn verbosity "No exposed modules"- runGhcProg replOpts-- -- link:- when has_code . unless forRepl $ do- info verbosity "Linking..."- let cLikeProfObjs = map (`replaceExtension` ("p_" ++ objExtension))- cLikeSources- cLikeSharedObjs = map (`replaceExtension` ("dyn_" ++ objExtension))- cLikeSources- compiler_id = compilerId (compiler lbi)- vanillaLibFilePath = libTargetDir </> mkLibName uid- profileLibFilePath = libTargetDir </> mkProfLibName uid- sharedLibFilePath = libTargetDir </>- mkSharedLibName (hostPlatform lbi) compiler_id uid- staticLibFilePath = libTargetDir </>- mkStaticLibName (hostPlatform lbi) compiler_id uid- ghciLibFilePath = libTargetDir </> Internal.mkGHCiLibName uid- ghciProfLibFilePath = libTargetDir </> Internal.mkGHCiProfLibName uid- libInstallPath = libdir $- absoluteComponentInstallDirs- pkg_descr lbi uid NoCopyDest- sharedLibInstallPath = libInstallPath </>- mkSharedLibName (hostPlatform lbi) compiler_id uid-- stubObjs <- catMaybes <$> sequenceA- [ findFileWithExtension [objExtension] [libTargetDir]- (ModuleName.toFilePath x ++"_stub")- | ghcVersion < mkVersion [7,2] -- ghc-7.2+ does not make _stub.o files- , x <- allLibModules lib clbi ]- stubProfObjs <- catMaybes <$> sequenceA- [ findFileWithExtension ["p_" ++ objExtension] [libTargetDir]- (ModuleName.toFilePath x ++"_stub")- | ghcVersion < mkVersion [7,2] -- ghc-7.2+ does not make _stub.o files- , x <- allLibModules lib clbi ]- stubSharedObjs <- catMaybes <$> sequenceA- [ findFileWithExtension ["dyn_" ++ objExtension] [libTargetDir]- (ModuleName.toFilePath x ++"_stub")- | ghcVersion < mkVersion [7,2] -- ghc-7.2+ does not make _stub.o files- , x <- allLibModules lib clbi ]-- hObjs <- Internal.getHaskellObjects implInfo lib lbi clbi- libTargetDir objExtension True- hProfObjs <-- if withProfLib lbi- then Internal.getHaskellObjects implInfo lib lbi clbi- libTargetDir ("p_" ++ objExtension) True- else return []- hSharedObjs <-- if withSharedLib lbi- then Internal.getHaskellObjects implInfo lib lbi clbi- libTargetDir ("dyn_" ++ objExtension) False- else return []-- unless (null hObjs && null cLikeObjs && null stubObjs) $ do- rpaths <- getRPaths lbi clbi-- let staticObjectFiles =- hObjs- ++ map (libTargetDir </>) cLikeObjs- ++ stubObjs- profObjectFiles =- hProfObjs- ++ map (libTargetDir </>) cLikeProfObjs- ++ stubProfObjs- dynamicObjectFiles =- hSharedObjs- ++ map (libTargetDir </>) cLikeSharedObjs- ++ stubSharedObjs- -- After the relocation lib is created we invoke ghc -shared- -- with the dependencies spelled out as -package arguments- -- and ghc invokes the linker with the proper library paths- ghcSharedLinkArgs =- mempty {- ghcOptShared = toFlag True,- ghcOptDynLinkMode = toFlag GhcDynamicOnly,- ghcOptInputFiles = toNubListR dynamicObjectFiles,- ghcOptOutputFile = toFlag sharedLibFilePath,- ghcOptExtra = hcSharedOptions GHC libBi,- -- For dynamic libs, Mac OS/X needs to know the install location- -- at build time. This only applies to GHC < 7.8 - see the- -- discussion in #1660.- ghcOptDylibName = if hostOS == OSX- && ghcVersion < mkVersion [7,8]- then toFlag sharedLibInstallPath- else mempty,- ghcOptHideAllPackages = toFlag True,- ghcOptNoAutoLinkPackages = toFlag True,- ghcOptPackageDBs = withPackageDB lbi,- ghcOptThisUnitId = case clbi of- LibComponentLocalBuildInfo { componentCompatPackageKey = pk }- -> toFlag pk- _ -> mempty,- ghcOptThisComponentId = case clbi of- LibComponentLocalBuildInfo- { componentInstantiatedWith = insts } ->- if null insts- then mempty- else toFlag (componentComponentId clbi)- _ -> mempty,- ghcOptInstantiatedWith = case clbi of- LibComponentLocalBuildInfo- { componentInstantiatedWith = insts }- -> insts- _ -> [],- ghcOptPackages = toNubListR $- Internal.mkGhcOptPackages clbi ,- ghcOptLinkLibs = extraLibs libBi,- ghcOptLinkLibPath = toNubListR $ extraLibDirs libBi,- ghcOptLinkFrameworks = toNubListR $ PD.frameworks libBi,- ghcOptLinkFrameworkDirs =- toNubListR $ PD.extraFrameworkDirs libBi,- ghcOptRPaths = rpaths- }- ghcStaticLinkArgs =- mempty {- ghcOptStaticLib = toFlag True,- ghcOptInputFiles = toNubListR staticObjectFiles,- ghcOptOutputFile = toFlag staticLibFilePath,- ghcOptExtra = hcStaticOptions GHC libBi,- ghcOptHideAllPackages = toFlag True,- ghcOptNoAutoLinkPackages = toFlag True,- ghcOptPackageDBs = withPackageDB lbi,- ghcOptThisUnitId = case clbi of- LibComponentLocalBuildInfo { componentCompatPackageKey = pk }- -> toFlag pk- _ -> mempty,- ghcOptThisComponentId = case clbi of- LibComponentLocalBuildInfo- { componentInstantiatedWith = insts } ->- if null insts- then mempty- else toFlag (componentComponentId clbi)- _ -> mempty,- ghcOptInstantiatedWith = case clbi of- LibComponentLocalBuildInfo- { componentInstantiatedWith = insts }- -> insts- _ -> [],- ghcOptPackages = toNubListR $- Internal.mkGhcOptPackages clbi ,- ghcOptLinkLibs = extraLibs libBi,- ghcOptLinkLibPath = toNubListR $ extraLibDirs libBi- }-- info verbosity (show (ghcOptPackages ghcSharedLinkArgs))-- whenVanillaLib False $ do- Ar.createArLibArchive verbosity lbi vanillaLibFilePath staticObjectFiles- whenGHCiLib $ do- (ldProg, _) <- requireProgram verbosity ldProgram (withPrograms lbi)- Ld.combineObjectFiles verbosity lbi ldProg- ghciLibFilePath staticObjectFiles-- whenProfLib $ do- Ar.createArLibArchive verbosity lbi profileLibFilePath profObjectFiles- whenGHCiLib $ do- (ldProg, _) <- requireProgram verbosity ldProgram (withPrograms lbi)- Ld.combineObjectFiles verbosity lbi ldProg- ghciProfLibFilePath profObjectFiles-- whenSharedLib False $- runGhcProg ghcSharedLinkArgs-- whenStaticLib False $- runGhcProg ghcStaticLinkArgs---- | Start a REPL without loading any source files.-startInterpreter :: Verbosity -> ProgramDb -> Compiler -> Platform- -> PackageDBStack -> IO ()-startInterpreter verbosity progdb comp platform packageDBs = do- let replOpts = mempty {- ghcOptMode = toFlag GhcModeInteractive,- ghcOptPackageDBs = packageDBs- }- checkPackageDbStack verbosity comp packageDBs- (ghcProg, _) <- requireProgram verbosity ghcProgram progdb- runGHC verbosity ghcProg comp platform replOpts---- -------------------------------------------------------------------------------- Building an executable or foreign library---- | Build a foreign library-buildFLib- :: Verbosity -> Cabal.Flag (Maybe Int)- -> PackageDescription -> LocalBuildInfo- -> ForeignLib -> ComponentLocalBuildInfo -> IO ()-buildFLib v njobs pkg lbi = gbuild v njobs pkg lbi . GBuildFLib--replFLib- :: [String] -> Verbosity- -> Cabal.Flag (Maybe Int) -> PackageDescription- -> LocalBuildInfo -> ForeignLib- -> ComponentLocalBuildInfo -> IO ()-replFLib replFlags v njobs pkg lbi =- gbuild v njobs pkg lbi . GReplFLib replFlags---- | Build an executable with GHC.----buildExe- :: Verbosity -> Cabal.Flag (Maybe Int)- -> PackageDescription -> LocalBuildInfo- -> Executable -> ComponentLocalBuildInfo -> IO ()-buildExe v njobs pkg lbi = gbuild v njobs pkg lbi . GBuildExe--replExe- :: [String] -> Verbosity- -> Cabal.Flag (Maybe Int) -> PackageDescription- -> LocalBuildInfo -> Executable- -> ComponentLocalBuildInfo -> IO ()-replExe replFlags v njobs pkg lbi =- gbuild v njobs pkg lbi . GReplExe replFlags---- | Building an executable, starting the REPL, and building foreign--- libraries are all very similar and implemented in 'gbuild'. The--- 'GBuildMode' distinguishes between the various kinds of operation.-data GBuildMode =- GBuildExe Executable- | GReplExe [String] Executable- | GBuildFLib ForeignLib- | GReplFLib [String] ForeignLib--gbuildInfo :: GBuildMode -> BuildInfo-gbuildInfo (GBuildExe exe) = buildInfo exe-gbuildInfo (GReplExe _ exe) = buildInfo exe-gbuildInfo (GBuildFLib flib) = foreignLibBuildInfo flib-gbuildInfo (GReplFLib _ flib) = foreignLibBuildInfo flib--gbuildName :: GBuildMode -> String-gbuildName (GBuildExe exe) = unUnqualComponentName $ exeName exe-gbuildName (GReplExe _ exe) = unUnqualComponentName $ exeName exe-gbuildName (GBuildFLib flib) = unUnqualComponentName $ foreignLibName flib-gbuildName (GReplFLib _ flib) = unUnqualComponentName $ foreignLibName flib--gbuildTargetName :: LocalBuildInfo -> GBuildMode -> String-gbuildTargetName lbi (GBuildExe exe) = exeTargetName (hostPlatform lbi) exe-gbuildTargetName lbi (GReplExe _ exe) = exeTargetName (hostPlatform lbi) exe-gbuildTargetName lbi (GBuildFLib flib) = flibTargetName lbi flib-gbuildTargetName lbi (GReplFLib _ flib) = flibTargetName lbi flib--exeTargetName :: Platform -> Executable -> String-exeTargetName platform exe = unUnqualComponentName (exeName exe) `withExt` exeExtension platform---- | Target name for a foreign library (the actual file name)------ We do not use mkLibName and co here because the naming for foreign libraries--- is slightly different (we don't use "_p" or compiler version suffices, and we--- don't want the "lib" prefix on Windows).------ TODO: We do use `dllExtension` and co here, but really that's wrong: they--- use the OS used to build cabal to determine which extension to use, rather--- than the target OS (but this is wrong elsewhere in Cabal as well).-flibTargetName :: LocalBuildInfo -> ForeignLib -> String-flibTargetName lbi flib =- case (os, foreignLibType flib) of- (Windows, ForeignLibNativeShared) -> nm <.> "dll"- (Windows, ForeignLibNativeStatic) -> nm <.> "lib"- (Linux, ForeignLibNativeShared) -> "lib" ++ nm <.> versionedExt- (_other, ForeignLibNativeShared) ->- "lib" ++ nm <.> dllExtension (hostPlatform lbi)- (_other, ForeignLibNativeStatic) ->- "lib" ++ nm <.> staticLibExtension (hostPlatform lbi)- (_any, ForeignLibTypeUnknown) -> cabalBug "unknown foreign lib type"- where- nm :: String- nm = unUnqualComponentName $ foreignLibName flib-- os :: OS- os = let (Platform _ os') = hostPlatform lbi- in os'-- -- If a foreign lib foo has lib-version-info 5:1:2 or- -- lib-version-linux 3.2.1, it should be built as libfoo.so.3.2.1- -- Libtool's version-info data is translated into library versions in a- -- nontrivial way: so refer to libtool documentation.- versionedExt :: String- versionedExt =- let nums = foreignLibVersion flib os- in foldl (<.>) "so" (map show nums)---- | Name for the library when building.------ If the `lib-version-info` field or the `lib-version-linux` field of--- a foreign library target is set, we need to incorporate that--- version into the SONAME field.------ If a foreign library foo has lib-version-info 5:1:2, it should be--- built as libfoo.so.3.2.1. We want it to get soname libfoo.so.3.--- However, GHC does not allow overriding soname by setting linker--- options, as it sets a soname of its own (namely the output--- filename), after the user-supplied linker options. Hence, we have--- to compile the library with the soname as its filename. We rename--- the compiled binary afterwards.------ This method allows to adjust the name of the library at build time--- such that the correct soname can be set.-flibBuildName :: LocalBuildInfo -> ForeignLib -> String-flibBuildName lbi flib- -- On linux, if a foreign-library has version data, the first digit is used- -- to produce the SONAME.- | (os, foreignLibType flib) ==- (Linux, ForeignLibNativeShared)- = let nums = foreignLibVersion flib os- in "lib" ++ nm <.> foldl (<.>) "so" (map show (take 1 nums))- | otherwise = flibTargetName lbi flib- where- os :: OS- os = let (Platform _ os') = hostPlatform lbi- in os'-- nm :: String- nm = unUnqualComponentName $ foreignLibName flib--gbuildIsRepl :: GBuildMode -> Bool-gbuildIsRepl (GBuildExe _) = False-gbuildIsRepl (GReplExe _ _) = True-gbuildIsRepl (GBuildFLib _) = False-gbuildIsRepl (GReplFLib _ _) = True--gbuildNeedDynamic :: LocalBuildInfo -> GBuildMode -> Bool-gbuildNeedDynamic lbi bm =- case bm of- GBuildExe _ -> withDynExe lbi- GReplExe _ _ -> withDynExe lbi- GBuildFLib flib -> withDynFLib flib- GReplFLib _ flib -> withDynFLib flib- where- withDynFLib flib =- case foreignLibType flib of- ForeignLibNativeShared ->- ForeignLibStandalone `notElem` foreignLibOptions flib- ForeignLibNativeStatic ->- False- ForeignLibTypeUnknown ->- cabalBug "unknown foreign lib type"--gbuildModDefFiles :: GBuildMode -> [FilePath]-gbuildModDefFiles (GBuildExe _) = []-gbuildModDefFiles (GReplExe _ _) = []-gbuildModDefFiles (GBuildFLib flib) = foreignLibModDefFile flib-gbuildModDefFiles (GReplFLib _ flib) = foreignLibModDefFile flib---- | "Main" module name when overridden by @ghc-options: -main-is ...@--- or 'Nothing' if no @-main-is@ flag could be found.------ In case of 'Nothing', 'Distribution.ModuleName.main' can be assumed.-exeMainModuleName :: Executable -> Maybe ModuleName-exeMainModuleName Executable{buildInfo = bnfo} =- -- GHC honors the last occurrence of a module name updated via -main-is- --- -- Moreover, -main-is when parsed left-to-right can update either- -- the "Main" module name, or the "main" function name, or both,- -- see also 'decodeMainIsArg'.- msum $ reverse $ map decodeMainIsArg $ findIsMainArgs ghcopts- where- ghcopts = hcOptions GHC bnfo-- findIsMainArgs [] = []- findIsMainArgs ("-main-is":arg:rest) = arg : findIsMainArgs rest- findIsMainArgs (_:rest) = findIsMainArgs rest---- | Decode argument to '-main-is'------ Returns 'Nothing' if argument set only the function name.------ This code has been stolen/refactored from GHC's DynFlags.setMainIs--- function. The logic here is deliberately imperfect as it is--- intended to be bug-compatible with GHC's parser. See discussion in--- https://github.com/haskell/cabal/pull/4539#discussion_r118981753.-decodeMainIsArg :: String -> Maybe ModuleName-decodeMainIsArg arg- | headOf main_fn isLower- -- The arg looked like "Foo.Bar.baz"- = Just (ModuleName.fromString main_mod)- | headOf arg isUpper -- The arg looked like "Foo" or "Foo.Bar"- = Just (ModuleName.fromString arg)- | otherwise -- The arg looked like "baz"- = Nothing- where- headOf :: String -> (Char -> Bool) -> Bool- headOf str pred' = any pred' (safeHead str)-- (main_mod, main_fn) = splitLongestPrefix arg (== '.')-- splitLongestPrefix :: String -> (Char -> Bool) -> (String,String)- splitLongestPrefix str pred'- | null r_pre = (str, [])- | otherwise = (reverse (safeTail r_pre), reverse r_suf)- -- 'safeTail' drops the char satisfying 'pred'- where (r_suf, r_pre) = break pred' (reverse str)---- | A collection of:--- * C input files--- * C++ input files--- * GHC input files--- * GHC input modules------ Used to correctly build and link sources.-data BuildSources = BuildSources {- cSourcesFiles :: [FilePath],- cxxSourceFiles :: [FilePath],- inputSourceFiles :: [FilePath],- inputSourceModules :: [ModuleName]- }---- | Locate and return the 'BuildSources' required to build and link.-gbuildSources :: Verbosity- -> CabalSpecVersion- -> FilePath- -> GBuildMode- -> IO BuildSources-gbuildSources verbosity specVer tmpDir bm =- case bm of- GBuildExe exe -> exeSources exe- GReplExe _ exe -> exeSources exe- GBuildFLib flib -> return $ flibSources flib- GReplFLib _ flib -> return $ flibSources flib- where- exeSources :: Executable -> IO BuildSources- exeSources exe@Executable{buildInfo = bnfo, modulePath = modPath} = do- main <- findFileEx verbosity (tmpDir : map getSymbolicPath (hsSourceDirs bnfo)) modPath- let mainModName = fromMaybe ModuleName.main $ exeMainModuleName exe- otherModNames = exeModules exe-- if isHaskell main- then- if specVer < CabalSpecV2_0 && (mainModName `elem` otherModNames)- then do- -- The cabal manual clearly states that `other-modules` is- -- intended for non-main modules. However, there's at least one- -- important package on Hackage (happy-1.19.5) which- -- violates this. We workaround this here so that we don't- -- invoke GHC with e.g. 'ghc --make Main src/Main.hs' which- -- would result in GHC complaining about duplicate Main- -- modules.- --- -- Finally, we only enable this workaround for- -- specVersion < 2, as 'cabal-version:>=2.0' cabal files- -- have no excuse anymore to keep doing it wrong... ;-)- warn verbosity $ "Enabling workaround for Main module '"- ++ prettyShow mainModName- ++ "' listed in 'other-modules' illegally!"-- return BuildSources {- cSourcesFiles = cSources bnfo,- cxxSourceFiles = cxxSources bnfo,- inputSourceFiles = [main],- inputSourceModules = filter (/= mainModName) $- exeModules exe- }-- else return BuildSources {- cSourcesFiles = cSources bnfo,- cxxSourceFiles = cxxSources bnfo,- inputSourceFiles = [main],- inputSourceModules = exeModules exe- }- else let (csf, cxxsf)- | isCxx main = ( cSources bnfo, main : cxxSources bnfo)- -- if main is not a Haskell source- -- and main is not a C++ source- -- then we assume that it is a C source- | otherwise = (main : cSources bnfo, cxxSources bnfo)-- in return BuildSources {- cSourcesFiles = csf,- cxxSourceFiles = cxxsf,- inputSourceFiles = [],- inputSourceModules = exeModules exe- }-- flibSources :: ForeignLib -> BuildSources- flibSources flib@ForeignLib{foreignLibBuildInfo = bnfo} =- BuildSources {- cSourcesFiles = cSources bnfo,- cxxSourceFiles = cxxSources bnfo,- inputSourceFiles = [],- inputSourceModules = foreignLibModules flib- }-- isHaskell :: FilePath -> Bool- isHaskell fp = elem (takeExtension fp) [".hs", ".lhs"]-- isCxx :: FilePath -> Bool- isCxx fp = elem (takeExtension fp) [".cpp", ".cxx", ".c++"]---- | Generic build function. See comment for 'GBuildMode'.-gbuild :: Verbosity -> Cabal.Flag (Maybe Int)- -> PackageDescription -> LocalBuildInfo- -> GBuildMode -> ComponentLocalBuildInfo -> IO ()-gbuild verbosity numJobs pkg_descr lbi bm clbi = do- (ghcProg, _) <- requireProgram verbosity ghcProgram (withPrograms lbi)- let replFlags = case bm of- GReplExe flags _ -> flags- GReplFLib flags _ -> flags- GBuildExe{} -> mempty- GBuildFLib{} -> mempty- comp = compiler lbi- platform = hostPlatform lbi- implInfo = getImplInfo comp- runGhcProg = runGHC verbosity ghcProg comp platform-- let (bnfo, threaded) = case bm of- GBuildFLib _ -> popThreadedFlag (gbuildInfo bm)- _ -> (gbuildInfo bm, False)-- -- the name that GHC really uses (e.g., with .exe on Windows for executables)- let targetName = gbuildTargetName lbi bm- let targetDir = buildDir lbi </> (gbuildName bm)- let tmpDir = targetDir </> (gbuildName bm ++ "-tmp")- createDirectoryIfMissingVerbose verbosity True targetDir- createDirectoryIfMissingVerbose verbosity True tmpDir-- -- TODO: do we need to put hs-boot files into place for mutually recursive- -- modules? FIX: what about exeName.hi-boot?-- -- Determine if program coverage should be enabled and if so, what- -- '-hpcdir' should be.- let isCoverageEnabled = exeCoverage lbi- distPref = fromFlag $ configDistPref $ configFlags lbi- hpcdir way- | gbuildIsRepl bm = mempty -- HPC is not supported in ghci- | isCoverageEnabled = toFlag $ Hpc.mixDir distPref way (gbuildName bm)- | otherwise = mempty-- rpaths <- getRPaths lbi clbi- buildSources <- gbuildSources verbosity (specVersion pkg_descr) tmpDir bm-- let cSrcs = cSourcesFiles buildSources- cxxSrcs = cxxSourceFiles buildSources- inputFiles = inputSourceFiles buildSources- inputModules = inputSourceModules buildSources- isGhcDynamic = isDynamic comp- dynamicTooSupported = supportsDynamicToo comp- cLikeObjs = map (`replaceExtension` objExtension) cSrcs- cxxObjs = map (`replaceExtension` objExtension) cxxSrcs- needDynamic = gbuildNeedDynamic lbi bm- needProfiling = withProfExe lbi-- -- build executables- baseOpts = (componentGhcOptions verbosity lbi bnfo clbi tmpDir)- `mappend` mempty {- ghcOptMode = toFlag GhcModeMake,- ghcOptInputFiles = toNubListR inputFiles,- ghcOptInputModules = toNubListR inputModules- }- staticOpts = baseOpts `mappend` mempty {- ghcOptDynLinkMode = toFlag GhcStaticOnly,- ghcOptHPCDir = hpcdir Hpc.Vanilla- }- profOpts = baseOpts `mappend` mempty {- ghcOptProfilingMode = toFlag True,- ghcOptProfilingAuto = Internal.profDetailLevelFlag False- (withProfExeDetail lbi),- ghcOptHiSuffix = toFlag "p_hi",- ghcOptObjSuffix = toFlag "p_o",- ghcOptExtra = hcProfOptions GHC bnfo,- ghcOptHPCDir = hpcdir Hpc.Prof- }- dynOpts = baseOpts `mappend` mempty {- ghcOptDynLinkMode = toFlag GhcDynamicOnly,- -- TODO: Does it hurt to set -fPIC for executables?- ghcOptFPic = toFlag True,- ghcOptHiSuffix = toFlag "dyn_hi",- ghcOptObjSuffix = toFlag "dyn_o",- ghcOptExtra = hcSharedOptions GHC bnfo,- ghcOptHPCDir = hpcdir Hpc.Dyn- }- dynTooOpts = staticOpts `mappend` mempty {- ghcOptDynLinkMode = toFlag GhcStaticAndDynamic,- ghcOptDynHiSuffix = toFlag "dyn_hi",- ghcOptDynObjSuffix = toFlag "dyn_o",- ghcOptHPCDir = hpcdir Hpc.Dyn- }- linkerOpts = mempty {- ghcOptLinkOptions = PD.ldOptions bnfo- ++ [ "-static"- | withFullyStaticExe lbi ]- -- Pass extra `ld-options` given- -- through to GHC's linker.- ++ maybe [] programOverrideArgs- (lookupProgram ldProgram (withPrograms lbi)),- ghcOptLinkLibs = extraLibs bnfo,- ghcOptLinkLibPath = toNubListR $ extraLibDirs bnfo,- ghcOptLinkFrameworks = toNubListR $- PD.frameworks bnfo,- ghcOptLinkFrameworkDirs = toNubListR $- PD.extraFrameworkDirs bnfo,- ghcOptInputFiles = toNubListR- [tmpDir </> x | x <- cLikeObjs ++ cxxObjs]- }- dynLinkerOpts = mempty {- ghcOptRPaths = rpaths,- ghcOptInputFiles = toNubListR- [tmpDir </> x | x <- cLikeObjs ++ cxxObjs]- }- replOpts = baseOpts {- ghcOptExtra = Internal.filterGhciFlags- (ghcOptExtra baseOpts)- <> replFlags- }- -- For a normal compile we do separate invocations of ghc for- -- compiling as for linking. But for repl we have to do just- -- the one invocation, so that one has to include all the- -- linker stuff too, like -l flags and any .o files from C- -- files etc.- `mappend` linkerOpts- `mappend` mempty {- ghcOptMode = toFlag GhcModeInteractive,- ghcOptOptimisation = toFlag GhcNoOptimisation- }- commonOpts | needProfiling = profOpts- | needDynamic = dynOpts- | otherwise = staticOpts- compileOpts | useDynToo = dynTooOpts- | otherwise = commonOpts- withStaticExe = not needProfiling && not needDynamic-- -- For building exe's that use TH with -prof or -dynamic we actually have- -- to build twice, once without -prof/-dynamic and then again with- -- -prof/-dynamic. This is because the code that TH needs to run at- -- compile time needs to be the vanilla ABI so it can be loaded up and run- -- by the compiler.- -- With dynamic-by-default GHC the TH object files loaded at compile-time- -- need to be .dyn_o instead of .o.- doingTH = usesTemplateHaskellOrQQ bnfo- -- Should we use -dynamic-too instead of compiling twice?- useDynToo = dynamicTooSupported && isGhcDynamic- && doingTH && withStaticExe- && null (hcSharedOptions GHC bnfo)- compileTHOpts | isGhcDynamic = dynOpts- | otherwise = staticOpts- compileForTH- | gbuildIsRepl bm = False- | useDynToo = False- | isGhcDynamic = doingTH && (needProfiling || withStaticExe)- | otherwise = doingTH && (needProfiling || needDynamic)-- -- Build static/dynamic object files for TH, if needed.- when compileForTH $- runGhcProg compileTHOpts { ghcOptNoLink = toFlag True- , ghcOptNumJobs = numJobs }-- -- Do not try to build anything if there are no input files.- -- This can happen if the cabal file ends up with only cSrcs- -- but no Haskell modules.- unless ((null inputFiles && null inputModules)- || gbuildIsRepl bm) $- runGhcProg compileOpts { ghcOptNoLink = toFlag True- , ghcOptNumJobs = numJobs }-- -- build any C++ sources- unless (null cxxSrcs) $ do- info verbosity "Building C++ Sources..."- sequence_- [ do let baseCxxOpts = Internal.componentCxxGhcOptions verbosity implInfo- lbi bnfo clbi tmpDir filename- vanillaCxxOpts = if isGhcDynamic- -- Dynamic GHC requires C++ sources to be built- -- with -fPIC for REPL to work. See #2207.- then baseCxxOpts { ghcOptFPic = toFlag True }- else baseCxxOpts- profCxxOpts = vanillaCxxOpts `mappend` mempty {- ghcOptProfilingMode = toFlag True- }- sharedCxxOpts = vanillaCxxOpts `mappend` mempty {- ghcOptFPic = toFlag True,- ghcOptDynLinkMode = toFlag GhcDynamicOnly- }- opts | needProfiling = profCxxOpts- | needDynamic = sharedCxxOpts- | otherwise = vanillaCxxOpts- -- TODO: Placing all Haskell, C, & C++ objects in a single directory- -- Has the potential for file collisions. In general we would- -- consider this a user error. However, we should strive to- -- add a warning if this occurs.- odir = fromFlag (ghcOptObjDir opts)- createDirectoryIfMissingVerbose verbosity True odir- needsRecomp <- checkNeedsRecompilation filename opts- when needsRecomp $- runGhcProg opts- | filename <- cxxSrcs ]-- -- build any C sources- unless (null cSrcs) $ do- info verbosity "Building C Sources..."- sequence_- [ do let baseCcOpts = Internal.componentCcGhcOptions verbosity implInfo- lbi bnfo clbi tmpDir filename- vanillaCcOpts = if isGhcDynamic- -- Dynamic GHC requires C sources to be built- -- with -fPIC for REPL to work. See #2207.- then baseCcOpts { ghcOptFPic = toFlag True }- else baseCcOpts- profCcOpts = vanillaCcOpts `mappend` mempty {- ghcOptProfilingMode = toFlag True- }- sharedCcOpts = vanillaCcOpts `mappend` mempty {- ghcOptFPic = toFlag True,- ghcOptDynLinkMode = toFlag GhcDynamicOnly- }- opts | needProfiling = profCcOpts- | needDynamic = sharedCcOpts- | otherwise = vanillaCcOpts- odir = fromFlag (ghcOptObjDir opts)- createDirectoryIfMissingVerbose verbosity True odir- needsRecomp <- checkNeedsRecompilation filename opts- when needsRecomp $- runGhcProg opts- | filename <- cSrcs ]-- -- TODO: problem here is we need the .c files built first, so we can load them- -- with ghci, but .c files can depend on .h files generated by ghc by ffi- -- exports.- case bm of- GReplExe _ _ -> runGhcProg replOpts- GReplFLib _ _ -> runGhcProg replOpts- GBuildExe _ -> do- let linkOpts = commonOpts- `mappend` linkerOpts- `mappend` mempty {- ghcOptLinkNoHsMain = toFlag (null inputFiles)- }- `mappend` (if withDynExe lbi then dynLinkerOpts else mempty)-- info verbosity "Linking..."- -- Work around old GHCs not relinking in this- -- situation, see #3294- let target = targetDir </> targetName- when (compilerVersion comp < mkVersion [7,7]) $ do- e <- doesFileExist target- when e (removeFile target)- runGhcProg linkOpts { ghcOptOutputFile = toFlag target }- GBuildFLib flib -> do- let rtsInfo = extractRtsInfo lbi- rtsOptLinkLibs = [- if needDynamic- then if threaded- then dynRtsThreadedLib (rtsDynamicInfo rtsInfo)- else dynRtsVanillaLib (rtsDynamicInfo rtsInfo)- else if threaded- then statRtsThreadedLib (rtsStaticInfo rtsInfo)- else statRtsVanillaLib (rtsStaticInfo rtsInfo)- ]- linkOpts = case foreignLibType flib of- ForeignLibNativeShared ->- commonOpts- `mappend` linkerOpts- `mappend` dynLinkerOpts- `mappend` mempty {- ghcOptLinkNoHsMain = toFlag True,- ghcOptShared = toFlag True,- ghcOptLinkLibs = rtsOptLinkLibs,- ghcOptLinkLibPath = toNubListR $ rtsLibPaths rtsInfo,- ghcOptFPic = toFlag True,- ghcOptLinkModDefFiles = toNubListR $ gbuildModDefFiles bm- }- -- See Note [RPATH]- `mappend` ifNeedsRPathWorkaround lbi mempty {- ghcOptLinkOptions = ["-Wl,--no-as-needed"]- , ghcOptLinkLibs = ["ffi"]- }- ForeignLibNativeStatic ->- -- this should be caught by buildFLib- -- (and if we do implement this, we probably don't even want to call- -- ghc here, but rather Ar.createArLibArchive or something)- cabalBug "static libraries not yet implemented"- ForeignLibTypeUnknown ->- cabalBug "unknown foreign lib type"- -- We build under a (potentially) different filename to set a- -- soname on supported platforms. See also the note for- -- @flibBuildName@.- info verbosity "Linking..."- let buildName = flibBuildName lbi flib- runGhcProg linkOpts { ghcOptOutputFile = toFlag (targetDir </> buildName) }- renameFile (targetDir </> buildName) (targetDir </> targetName)--{--Note [RPATH]-~~~~~~~~~~~~--Suppose that the dynamic library depends on `base`, but not (directly) on-`integer-gmp` (which, however, is a dependency of `base`). We will link the-library as-- gcc ... -lHSbase-4.7.0.2-ghc7.8.4 -lHSinteger-gmp-0.5.1.0-ghc7.8.4 ...--However, on systems (like Ubuntu) where the linker gets called with `-as-needed`-by default, the linker will notice that `integer-gmp` isn't actually a direct-dependency and hence omit the link.--Then when we attempt to link a C program against this dynamic library, the-_static_ linker will attempt to verify that all symbols can be resolved. The-dynamic library itself does not require any symbols from `integer-gmp`, but-`base` does. In order to verify that the symbols used by `base` can be-resolved, the static linker needs to be able to _find_ integer-gmp.--Finding the `base` dependency is simple, because the dynamic elf header-(`readelf -d`) for the library that we have created looks something like-- (NEEDED) Shared library: [libHSbase-4.7.0.2-ghc7.8.4.so]- (RPATH) Library rpath: [/path/to/base-4.7.0.2:...]--However, when it comes to resolving the dependency on `integer-gmp`, it needs-to look at the dynamic header for `base`. On modern ghc (7.8 and higher) this-looks something like-- (NEEDED) Shared library: [libHSinteger-gmp-0.5.1.0-ghc7.8.4.so]- (RPATH) Library rpath: [$ORIGIN/../integer-gmp-0.5.1.0:...]--This specifies the location of `integer-gmp` _in terms of_ the location of base-(using the `$ORIGIN`) variable. But here's the crux: when the static linker-attempts to verify that all symbols can be resolved, [**IT DOES NOT RESOLVE-`$ORIGIN`**](http://stackoverflow.com/questions/6323603/ld-using-rpath-origin-inside-a-shared-library-recursive).-As a consequence, it will not be able to resolve the symbols and report the-missing symbols as errors, _even though the dynamic linker **would** be able to-resolve these symbols_. We can tell the static linker not to report these-errors by using `--unresolved-symbols=ignore-all` and all will be fine when we-run the program ([(indeed, this is what the gold linker-does)](https://sourceware.org/ml/binutils/2013-05/msg00038.html), but it makes-the resulting library more difficult to use.--Instead what we can do is make sure that the generated dynamic library has-explicit top-level dependencies on these libraries. This means that the static-linker knows where to find them, and when we have transitive dependencies on-the same libraries the linker will only load them once, so we avoid needing to-look at the `RPATH` of our dependencies. We can do this by passing-`--no-as-needed` to the linker, so that it doesn't omit any libraries.--Note that on older ghc (7.6 and before) the Haskell libraries don't have an-RPATH set at all, which makes it even more important that we make these-top-level dependencies.--Finally, we have to explicitly link against `libffi` for the same reason. For-newer ghc this _happens_ to be unnecessary on many systems because `libffi` is-a library which is not specific to GHC, and when the static linker verifies-that all symbols can be resolved it will find the `libffi` that is globally-installed (completely independent from ghc). Of course, this may well be the-_wrong_ version of `libffi`, but it's quite possible that symbol resolution-happens to work. This is of course the wrong approach, which is why we link-explicitly against `libffi` so that we will find the _right_ version of-`libffi`.--}---- | Do we need the RPATH workaround?------ See Note [RPATH].-ifNeedsRPathWorkaround :: Monoid a => LocalBuildInfo -> a -> a-ifNeedsRPathWorkaround lbi a =- case hostPlatform lbi of- Platform _ Linux -> a- _otherwise -> mempty--data DynamicRtsInfo = DynamicRtsInfo {- dynRtsVanillaLib :: FilePath- , dynRtsThreadedLib :: FilePath- , dynRtsDebugLib :: FilePath- , dynRtsEventlogLib :: FilePath- , dynRtsThreadedDebugLib :: FilePath- , dynRtsThreadedEventlogLib :: FilePath- }--data StaticRtsInfo = StaticRtsInfo {- statRtsVanillaLib :: FilePath- , statRtsThreadedLib :: FilePath- , statRtsDebugLib :: FilePath- , statRtsEventlogLib :: FilePath- , statRtsThreadedDebugLib :: FilePath- , statRtsThreadedEventlogLib :: FilePath- , statRtsProfilingLib :: FilePath- , statRtsThreadedProfilingLib :: FilePath- }--data RtsInfo = RtsInfo {- rtsDynamicInfo :: DynamicRtsInfo- , rtsStaticInfo :: StaticRtsInfo- , rtsLibPaths :: [FilePath]- }---- | Extract (and compute) information about the RTS library------ TODO: This hardcodes the name as @HSrts-ghc<version>@. I don't know if we can--- find this information somewhere. We can lookup the 'hsLibraries' field of--- 'InstalledPackageInfo' but it will tell us @["HSrts", "Cffi"]@, which--- doesn't really help.-extractRtsInfo :: LocalBuildInfo -> RtsInfo-extractRtsInfo lbi =- case PackageIndex.lookupPackageName- (installedPkgs lbi) (mkPackageName "rts") of- [(_, [rts])] -> aux rts- _otherwise -> error "No (or multiple) ghc rts package is registered"- where- aux :: InstalledPackageInfo -> RtsInfo- aux rts = RtsInfo {- rtsDynamicInfo = DynamicRtsInfo {- dynRtsVanillaLib = withGhcVersion "HSrts"- , dynRtsThreadedLib = withGhcVersion "HSrts_thr"- , dynRtsDebugLib = withGhcVersion "HSrts_debug"- , dynRtsEventlogLib = withGhcVersion "HSrts_l"- , dynRtsThreadedDebugLib = withGhcVersion "HSrts_thr_debug"- , dynRtsThreadedEventlogLib = withGhcVersion "HSrts_thr_l"- }- , rtsStaticInfo = StaticRtsInfo {- statRtsVanillaLib = "HSrts"- , statRtsThreadedLib = "HSrts_thr"- , statRtsDebugLib = "HSrts_debug"- , statRtsEventlogLib = "HSrts_l"- , statRtsThreadedDebugLib = "HSrts_thr_debug"- , statRtsThreadedEventlogLib = "HSrts_thr_l"- , statRtsProfilingLib = "HSrts_p"- , statRtsThreadedProfilingLib = "HSrts_thr_p"- }- , rtsLibPaths = InstalledPackageInfo.libraryDirs rts- }- withGhcVersion = (++ ("-ghc" ++ prettyShow (compilerVersion (compiler lbi))))---- | Returns True if the modification date of the given source file is newer than--- the object file we last compiled for it, or if no object file exists yet.-checkNeedsRecompilation :: FilePath -> GhcOptions -> IO Bool-checkNeedsRecompilation filename opts = filename `moreRecentFile` oname- where oname = getObjectFileName filename opts---- | Finds the object file name of the given source file-getObjectFileName :: FilePath -> GhcOptions -> FilePath-getObjectFileName filename opts = oname- where odir = fromFlag (ghcOptObjDir opts)- oext = fromFlagOrDefault "o" (ghcOptObjSuffix opts)- oname = odir </> replaceExtension filename oext---- | Calculate the RPATHs for the component we are building.------ Calculates relative RPATHs when 'relocatable' is set.-getRPaths :: LocalBuildInfo- -> ComponentLocalBuildInfo -- ^ Component we are building- -> IO (NubListR FilePath)-getRPaths lbi clbi | supportRPaths hostOS = do- libraryPaths <- depLibraryPaths False (relocatable lbi) lbi clbi- let hostPref = case hostOS of- OSX -> "@loader_path"- _ -> "$ORIGIN"- relPath p = if isRelative p then hostPref </> p else p- rpaths = toNubListR (map relPath libraryPaths)- return rpaths- where- (Platform _ hostOS) = hostPlatform lbi- compid = compilerId . compiler $ lbi-- -- The list of RPath-supported operating systems below reflects the- -- platforms on which Cabal's RPATH handling is tested. It does _NOT_- -- reflect whether the OS supports RPATH.-- -- E.g. when this comment was written, the *BSD operating systems were- -- untested with regards to Cabal RPATH handling, and were hence set to- -- 'False', while those operating systems themselves do support RPATH.- supportRPaths Linux = True- supportRPaths Windows = False- supportRPaths OSX = True- supportRPaths FreeBSD =- case compid of- CompilerId GHC ver | ver >= mkVersion [7,10,2] -> True- _ -> False- supportRPaths OpenBSD = False- supportRPaths NetBSD = False- supportRPaths DragonFly = False- supportRPaths Solaris = False- supportRPaths AIX = False- supportRPaths HPUX = False- supportRPaths IRIX = False- supportRPaths HaLVM = False- supportRPaths IOS = False- supportRPaths Android = False- supportRPaths Ghcjs = False- supportRPaths Hurd = False- supportRPaths (OtherOS _) = False- -- Do _not_ add a default case so that we get a warning here when a new OS- -- is added.--getRPaths _ _ = return mempty---- | Remove the "-threaded" flag when building a foreign library, as it has no--- effect when used with "-shared". Returns the updated 'BuildInfo', along--- with whether or not the flag was present, so we can use it to link against--- the appropriate RTS on our own.-popThreadedFlag :: BuildInfo -> (BuildInfo, Bool)-popThreadedFlag bi =- ( bi { options = filterHcOptions (/= "-threaded") (options bi) }- , hasThreaded (options bi))-- where- filterHcOptions :: (String -> Bool)- -> PerCompilerFlavor [String]- -> PerCompilerFlavor [String]- filterHcOptions p (PerCompilerFlavor ghc ghcjs) =- PerCompilerFlavor (filter p ghc) ghcjs-- hasThreaded :: PerCompilerFlavor [String] -> Bool- hasThreaded (PerCompilerFlavor ghc _) = elem "-threaded" ghc---- | Extracts a String representing a hash of the ABI of a built--- library. It can fail if the library has not yet been built.----libAbiHash :: Verbosity -> PackageDescription -> LocalBuildInfo- -> Library -> ComponentLocalBuildInfo -> IO String-libAbiHash verbosity _pkg_descr lbi lib clbi = do- let- libBi = libBuildInfo lib- comp = compiler lbi- platform = hostPlatform lbi- vanillaArgs0 =- (componentGhcOptions verbosity lbi libBi clbi (componentBuildDir lbi clbi))- `mappend` mempty {- ghcOptMode = toFlag GhcModeAbiHash,- ghcOptInputModules = toNubListR $ exposedModules lib- }- vanillaArgs =- -- Package DBs unnecessary, and break ghc-cabal. See #3633- -- BUT, put at least the global database so that 7.4 doesn't- -- break.- vanillaArgs0 { ghcOptPackageDBs = [GlobalPackageDB]- , ghcOptPackages = mempty }- sharedArgs = vanillaArgs `mappend` mempty {- ghcOptDynLinkMode = toFlag GhcDynamicOnly,- ghcOptFPic = toFlag True,- ghcOptHiSuffix = toFlag "dyn_hi",- ghcOptObjSuffix = toFlag "dyn_o",- ghcOptExtra = hcSharedOptions GHC libBi- }- profArgs = vanillaArgs `mappend` mempty {- ghcOptProfilingMode = toFlag True,- ghcOptProfilingAuto = Internal.profDetailLevelFlag True- (withProfLibDetail lbi),- ghcOptHiSuffix = toFlag "p_hi",- ghcOptObjSuffix = toFlag "p_o",- ghcOptExtra = hcProfOptions GHC libBi- }- ghcArgs- | withVanillaLib lbi = vanillaArgs- | withSharedLib lbi = sharedArgs- | withProfLib lbi = profArgs- | otherwise = error "libAbiHash: Can't find an enabled library way"-- (ghcProg, _) <- requireProgram verbosity ghcProgram (withPrograms lbi)- hash <- getProgramInvocationOutput verbosity- (ghcInvocation ghcProg comp platform ghcArgs)- return (takeWhile (not . isSpace) hash)--componentGhcOptions :: Verbosity -> LocalBuildInfo- -> BuildInfo -> ComponentLocalBuildInfo -> FilePath- -> GhcOptions-componentGhcOptions verbosity lbi =- Internal.componentGhcOptions verbosity implInfo lbi- where- comp = compiler lbi- implInfo = getImplInfo comp--componentCcGhcOptions :: Verbosity -> LocalBuildInfo- -> BuildInfo -> ComponentLocalBuildInfo- -> FilePath -> FilePath- -> GhcOptions-componentCcGhcOptions verbosity lbi =- Internal.componentCcGhcOptions verbosity implInfo lbi- where- comp = compiler lbi- implInfo = getImplInfo comp---- -------------------------------------------------------------------------------- Installing---- |Install executables for GHC.-installExe :: Verbosity- -> LocalBuildInfo- -> FilePath -- ^Where to copy the files to- -> FilePath -- ^Build location- -> (FilePath, FilePath) -- ^Executable (prefix,suffix)- -> PackageDescription- -> Executable- -> IO ()-installExe verbosity lbi binDir buildPref- (progprefix, progsuffix) _pkg exe = do- createDirectoryIfMissingVerbose verbosity True binDir- let exeName' = unUnqualComponentName $ exeName exe- exeFileName = exeTargetName (hostPlatform lbi) exe- fixedExeBaseName = progprefix ++ exeName' ++ progsuffix- installBinary dest = do- installExecutableFile verbosity- (buildPref </> exeName' </> exeFileName)- (dest <.> exeExtension (hostPlatform lbi))- when (stripExes lbi) $- Strip.stripExe verbosity (hostPlatform lbi) (withPrograms lbi)- (dest <.> exeExtension (hostPlatform lbi))- installBinary (binDir </> fixedExeBaseName)---- |Install foreign library for GHC.-installFLib :: Verbosity- -> LocalBuildInfo- -> FilePath -- ^install location- -> FilePath -- ^Build location- -> PackageDescription- -> ForeignLib- -> IO ()-installFLib verbosity lbi targetDir builtDir _pkg flib =- install (foreignLibIsShared flib)- builtDir- targetDir- (flibTargetName lbi flib)- where- install isShared srcDir dstDir name = do- let src = srcDir </> name- dst = dstDir </> name- createDirectoryIfMissingVerbose verbosity True targetDir- -- TODO: Should we strip? (stripLibs lbi)- if isShared- then installExecutableFile verbosity src dst- else installOrdinaryFile verbosity src dst- -- Now install appropriate symlinks if library is versioned- let (Platform _ os) = hostPlatform lbi- when (not (null (foreignLibVersion flib os))) $ do- when (os /= Linux) $ die' verbosity- -- It should be impossible to get here.- "Can't install foreign-library symlink on non-Linux OS"-#ifndef mingw32_HOST_OS- -- 'createSymbolicLink file1 file2' creates a symbolic link- -- named 'file2' which points to the file 'file1'.- -- Note that we do want a symlink to 'name' rather than- -- 'dst', because the symlink will be relative to the- -- directory it's created in.- -- Finally, we first create the symlinks in a temporary- -- directory and then rename to simulate 'ln --force'.- withTempDirectory verbosity dstDir nm $ \tmpDir -> do- let link1 = flibBuildName lbi flib- link2 = "lib" ++ nm <.> "so"- createSymbolicLink name (tmpDir </> link1)- renameFile (tmpDir </> link1) (dstDir </> link1)- createSymbolicLink name (tmpDir </> link2)- renameFile (tmpDir </> link2) (dstDir </> link2)- where- nm :: String- nm = unUnqualComponentName $ foreignLibName flib-#endif /* mingw32_HOST_OS */----- |Install for ghc, .hi, .a and, if --with-ghci given, .o-installLib :: Verbosity- -> LocalBuildInfo- -> FilePath -- ^install location- -> FilePath -- ^install location for dynamic libraries- -> FilePath -- ^Build location- -> PackageDescription- -> Library- -> ComponentLocalBuildInfo- -> IO ()-installLib verbosity lbi targetDir dynlibTargetDir _builtDir pkg lib clbi = do- -- copy .hi files over:- whenVanilla $ copyModuleFiles "hi"- whenProf $ copyModuleFiles "p_hi"- whenShared $ copyModuleFiles "dyn_hi"-- -- copy the built library files over:- whenHasCode $ do- whenVanilla $ do- sequence_ [ installOrdinary- builtDir- targetDir- (mkGenericStaticLibName (l ++ f))- | l <- getHSLibraryName- (componentUnitId clbi):(extraBundledLibs (libBuildInfo lib))- , f <- "":extraLibFlavours (libBuildInfo lib)- ]- whenGHCi $ installOrdinary builtDir targetDir ghciLibName- whenProf $ do- installOrdinary builtDir targetDir profileLibName- whenGHCi $ installOrdinary builtDir targetDir ghciProfLibName- whenShared $ if- -- The behavior for "extra-bundled-libraries" changed in version 2.5.0.- -- See ghc issue #15837 and Cabal PR #5855.- | specVersion pkg < CabalSpecV3_0 -> do- sequence_ [ installShared builtDir dynlibTargetDir- (mkGenericSharedLibName platform compiler_id (l ++ f))- | l <- getHSLibraryName uid : extraBundledLibs (libBuildInfo lib)- , f <- "":extraDynLibFlavours (libBuildInfo lib)- ]- | otherwise -> do- sequence_ [ installShared- builtDir- dynlibTargetDir- (mkGenericSharedLibName- platform- compiler_id- (getHSLibraryName uid ++ f))- | f <- "":extraDynLibFlavours (libBuildInfo lib)- ]- sequence_ [ do- files <- getDirectoryContents builtDir- let l' = mkGenericSharedBundledLibName- platform- compiler_id- l- forM_ files $ \ file ->- when (l' `isPrefixOf` file) $ do- isFile <- doesFileExist (builtDir </> file)- when isFile $ do- installShared- builtDir- dynlibTargetDir- file- | l <- extraBundledLibs (libBuildInfo lib)- ]-- where- builtDir = componentBuildDir lbi clbi-- install isShared srcDir dstDir name = do- let src = srcDir </> name- dst = dstDir </> name-- createDirectoryIfMissingVerbose verbosity True dstDir-- if isShared- then installExecutableFile verbosity src dst- else installOrdinaryFile verbosity src dst-- when (stripLibs lbi) $ Strip.stripLib verbosity- platform (withPrograms lbi) dst-- installOrdinary = install False- installShared = install True-- copyModuleFiles ext =- findModuleFilesEx verbosity [builtDir] [ext] (allLibModules lib clbi)- >>= installOrdinaryFiles verbosity targetDir-- compiler_id = compilerId (compiler lbi)- platform = hostPlatform lbi- uid = componentUnitId clbi- profileLibName = mkProfLibName uid- ghciLibName = Internal.mkGHCiLibName uid- ghciProfLibName = Internal.mkGHCiProfLibName uid-- hasLib = not $ null (allLibModules lib clbi)- && null (cSources (libBuildInfo lib))- && null (cxxSources (libBuildInfo lib))- && null (cmmSources (libBuildInfo lib))- && null (asmSources (libBuildInfo lib))- has_code = not (componentIsIndefinite clbi)- whenHasCode = when has_code- whenVanilla = when (hasLib && withVanillaLib lbi)- whenProf = when (hasLib && withProfLib lbi && has_code)- whenGHCi = when (hasLib && withGHCiLib lbi && has_code)- whenShared = when (hasLib && withSharedLib lbi && has_code)---- -------------------------------------------------------------------------------- Registering--hcPkgInfo :: ProgramDb -> HcPkg.HcPkgInfo-hcPkgInfo progdb = HcPkg.HcPkgInfo- { HcPkg.hcPkgProgram = ghcPkgProg- , HcPkg.noPkgDbStack = v < [6,9]- , HcPkg.noVerboseFlag = v < [6,11]- , HcPkg.flagPackageConf = v < [7,5]- , HcPkg.supportsDirDbs = v >= [6,8]- , HcPkg.requiresDirDbs = v >= [7,10]- , HcPkg.nativeMultiInstance = v >= [7,10]- , HcPkg.recacheMultiInstance = v >= [6,12]- , HcPkg.suppressFilesCheck = v >= [6,6]- }- where- v = versionNumbers ver- ghcPkgProg = fromMaybe (error "GHC.hcPkgInfo: no ghc program") $ lookupProgram ghcPkgProgram progdb- ver = fromMaybe (error "GHC.hcPkgInfo: no ghc version") $ programVersion ghcPkgProg--registerPackage- :: Verbosity- -> ProgramDb- -> PackageDBStack- -> InstalledPackageInfo- -> HcPkg.RegisterOptions- -> IO ()-registerPackage verbosity progdb packageDbs installedPkgInfo registerOptions =- HcPkg.register (hcPkgInfo progdb) verbosity packageDbs- installedPkgInfo registerOptions--pkgRoot :: Verbosity -> LocalBuildInfo -> PackageDB -> IO FilePath-pkgRoot verbosity lbi = pkgRoot'- where- pkgRoot' GlobalPackageDB =- let ghcProg = fromMaybe (error "GHC.pkgRoot: no ghc program") $ lookupProgram ghcProgram (withPrograms lbi)- in fmap takeDirectory (getGlobalPackageDB verbosity ghcProg)- pkgRoot' UserPackageDB = do- appDir <- getGhcAppDir- let ver = compilerVersion (compiler lbi)- subdir = System.Info.arch ++ '-':System.Info.os- ++ '-':prettyShow ver- rootDir = appDir </> subdir- -- We must create the root directory for the user package database if it- -- does not yet exists. Otherwise '${pkgroot}' will resolve to a- -- directory at the time of 'ghc-pkg register', and registration will- -- fail.- createDirectoryIfMissing True rootDir- return rootDir- pkgRoot' (SpecificPackageDB fp) = return (takeDirectory fp)---- -------------------------------------------------------------------------------- Utils--isDynamic :: Compiler -> Bool-isDynamic = Internal.ghcLookupProperty "GHC Dynamic"--supportsDynamicToo :: Compiler -> Bool-supportsDynamicToo = Internal.ghcLookupProperty "Support dynamic-too"--withExt :: FilePath -> String -> FilePath-withExt fp ext = fp <.> if takeExtension fp /= ('.':ext) then ext else ""+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}++-----------------------------------------------------------------------------++-- |+-- Module : Distribution.Simple.GHC+-- Copyright : Isaac Jones 2003-2007+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- This is a fairly large module. It contains most of the GHC-specific code for+-- configuring, building and installing packages. It also exports a function+-- for finding out what packages are already installed. Configuring involves+-- finding the @ghc@ and @ghc-pkg@ programs, finding what language extensions+-- this version of ghc supports and returning a 'Compiler' value.+--+-- 'getInstalledPackages' involves calling the @ghc-pkg@ program to find out+-- what packages are installed.+--+-- Building is somewhat complex as there is quite a bit of information to take+-- into account. We have to build libs and programs, possibly for profiling and+-- shared libs. We have to support building libraries that will be usable by+-- GHCi and also ghc's @-split-objs@ feature. We have to compile any C files+-- using ghc. Linking, especially for @split-objs@ is remarkably complex,+-- partly because there tend to be 1,000's of @.o@ files and this can often be+-- more than we can pass to the @ld@ or @ar@ programs in one go.+--+-- Installing for libs and exes involves finding the right files and copying+-- them to the right places. One of the more tricky things about this module is+-- remembering the layout of files in the build directory (which is not+-- explicitly documented) and thus what search dirs are used for various kinds+-- of files.+module Distribution.Simple.GHC+ ( getGhcInfo+ , configure+ , configureCompiler+ , compilerProgramDb+ , getInstalledPackages+ , getInstalledPackagesMonitorFiles+ , getPackageDBContents+ , buildLib+ , buildFLib+ , buildExe+ , replLib+ , replFLib+ , replExe+ , startInterpreter+ , installLib+ , installFLib+ , installExe+ , libAbiHash+ , hcPkgInfo+ , registerPackage+ , Internal.componentGhcOptions+ , Internal.componentCcGhcOptions+ , getGhcAppDir+ , getLibDir+ , compilerBuildWay+ , getGlobalPackageDB+ , pkgRoot++ -- * Constructing and deconstructing GHC environment files+ , Internal.GhcEnvironmentFileEntry (..)+ , Internal.simpleGhcEnvironmentFile+ , Internal.renderGhcEnvironmentFile+ , Internal.writeGhcEnvironmentFile+ , Internal.ghcPlatformAndVersionString+ , readGhcEnvironmentFile+ , parseGhcEnvironmentFile+ , ParseErrorExc (..)++ -- * Version-specific implementation quirks+ , getImplInfo+ , GhcImplInfo (..)+ ) where++import Distribution.Compat.Prelude+import Prelude ()++import Control.Arrow ((***))+import Control.Monad (forM_)+import qualified Data.Map as Map+import Data.Maybe (fromJust)+import Distribution.CabalSpecVersion+import Distribution.InstalledPackageInfo (InstalledPackageInfo)+import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo+import Distribution.Package+import Distribution.PackageDescription as PD+import Distribution.Pretty+import Distribution.Simple.Build.Inputs (PreBuildComponentInputs (..))+import Distribution.Simple.BuildPaths+import Distribution.Simple.Compiler+import Distribution.Simple.Errors+import qualified Distribution.Simple.GHC.Build as GHC+import Distribution.Simple.GHC.Build.Modules (BuildWay (..))+import Distribution.Simple.GHC.Build.Utils+import Distribution.Simple.GHC.EnvironmentParser+import Distribution.Simple.GHC.ImplInfo+import qualified Distribution.Simple.GHC.Internal as Internal+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.PackageIndex (InstalledPackageIndex)+import qualified Distribution.Simple.PackageIndex as PackageIndex+import Distribution.Simple.PreProcess.Types+import Distribution.Simple.Program+import Distribution.Simple.Program.Builtin (runghcProgram)+import Distribution.Simple.Program.GHC+import qualified Distribution.Simple.Program.HcPkg as HcPkg+import qualified Distribution.Simple.Program.Strip as Strip+import Distribution.Simple.Setup.Common+import Distribution.Simple.Setup.Repl+import Distribution.Simple.Utils+import Distribution.System+import Distribution.Types.ComponentLocalBuildInfo+import Distribution.Types.ParStrat+import Distribution.Types.TargetInfo+import Distribution.Utils.NubList+import Distribution.Utils.Path+import Distribution.Verbosity+import Distribution.Version+import Language.Haskell.Extension+import System.FilePath+ ( isRelative+ , takeDirectory+ )+import qualified System.Info+#ifndef mingw32_HOST_OS+import System.Posix (createSymbolicLink)+#endif /* mingw32_HOST_OS */++{- FOURMOLU_DISABLE -}+import System.Directory+ ( canonicalizePath+ , createDirectoryIfMissing+ , doesDirectoryExist+ , doesFileExist+ , getAppUserDataDirectory+ , getDirectoryContents+#ifndef mingw32_HOST_OS+ , renameFile+#endif+ )+{- FOURMOLU_ENABLE -}++import Distribution.Simple.Setup (BuildingWhat (..))+import Distribution.Simple.Setup.Build++-- -----------------------------------------------------------------------------+-- Configuring++-- | Configure GHC, and then auxiliary programs such as @ghc-pkg@, @haddock@+-- as well as toolchain programs such as @ar@, @ld.+configure+ :: Verbosity+ -> Maybe FilePath+ -- ^ user-specified @ghc@ path (optional)+ -> Maybe FilePath+ -- ^ user-specified @ghc-pkg@ path (optional)+ -> ProgramDb+ -> IO (Compiler, Maybe Platform, ProgramDb)+configure verbosity hcPath hcPkgPath conf0 = do+ (comp, compPlatform, progdb1) <- configureCompiler verbosity hcPath conf0+ compProgDb <- compilerProgramDb verbosity comp progdb1 hcPkgPath+ return (comp, compPlatform, compProgDb)++-- | Configure GHC.+configureCompiler+ :: Verbosity+ -> Maybe FilePath+ -- ^ user-specified @ghc@ path (optional)+ -> ProgramDb+ -> IO (Compiler, Maybe Platform, ProgramDb)+configureCompiler verbosity hcPath conf0 = do+ (ghcProg, ghcVersion, progdb1) <-+ requireProgramVersion+ verbosity+ ghcProgram+ (orLaterVersion (mkVersion [7, 0, 1]))+ (userMaybeSpecifyPath "ghc" hcPath conf0)++ -- Cabal currently supports GHC less than `maxGhcVersion`+ let maxGhcVersion = mkVersion [9, 16]+ unless (ghcVersion < maxGhcVersion) $+ warn verbosity $+ "Unknown/unsupported 'ghc' version detected "+ ++ "(Cabal "+ ++ prettyShow cabalVersion+ ++ " supports 'ghc' version < "+ ++ prettyShow maxGhcVersion+ ++ "): "+ ++ programPath ghcProg+ ++ " is version "+ ++ prettyShow ghcVersion++ let implInfo = ghcVersionImplInfo ghcVersion+ languages <- Internal.getLanguages verbosity implInfo ghcProg+ extensions0 <- Internal.getExtensions verbosity implInfo ghcProg++ ghcInfo <- Internal.getGhcInfo verbosity implInfo ghcProg++ let ghcInfoMap = Map.fromList ghcInfo+ filterJS = if ghcVersion < mkVersion [9, 8] then filterExt JavaScriptFFI else id+ extensions =+ -- workaround https://gitlab.haskell.org/ghc/ghc/-/issues/11214+ filterJS $+ -- see 'filterExtTH' comment below+ filterExtTH $+ extensions0++ -- starting with GHC 8.0, `TemplateHaskell` will be omitted from+ -- `--supported-extensions` when it's not available.+ -- for older GHCs we can use the "Have interpreter" property to+ -- filter out `TemplateHaskell`+ filterExtTH+ | ghcVersion < mkVersion [8]+ , Just "NO" <- Map.lookup "Have interpreter" ghcInfoMap =+ filterExt TemplateHaskell+ | otherwise = id++ filterExt ext = filter ((/= EnableExtension ext) . fst)++ compilerId :: CompilerId+ compilerId = CompilerId GHC ghcVersion++ -- The @AbiTag@ is the @Project Unit Id@ but with redundant information from the compiler version removed.+ -- For development versions of the compiler these look like:+ -- @Project Unit Id@: "ghc-9.13-inplace"+ -- @compilerId@: "ghc-9.13.20250413"+ -- So, we need to be careful to only strip the /common/ prefix.+ -- In this example, @AbiTag@ is "inplace".+ compilerAbiTag :: AbiTag+ compilerAbiTag =+ maybe+ NoAbiTag+ AbiTag+ ( dropWhile (== '-') . stripCommonPrefix (prettyShow compilerId)+ <$> Map.lookup "Project Unit Id" ghcInfoMap+ )++ let comp =+ Compiler+ { compilerId+ , compilerAbiTag+ , compilerCompat = []+ , compilerLanguages = languages+ , compilerExtensions = extensions+ , compilerProperties = ghcInfoMap+ }+ compPlatform = Internal.targetPlatform ghcInfo+ return (comp, compPlatform, progdb1)++-- | Given a configured @ghc@ program, configure auxiliary programs such+-- as @ghc-pkg@ or @haddock@, as well as toolchain programs such as @ar@, @ld@,+-- based on:+--+-- - the location of the @ghc@ executable,+-- - toolchain information in the GHC settings file.+compilerProgramDb+ :: Verbosity+ -> Compiler+ -> ProgramDb+ -> Maybe FilePath+ -- ^ user-specified @ghc-pkg@ path (optional)+ -> IO ProgramDb+compilerProgramDb verbosity comp progdb1 hcPkgPath = do+ let+ ghcProg = fromJust $ lookupProgram ghcProgram progdb1+ ghcVersion = compilerVersion comp++ -- This is slightly tricky, we have to configure ghc first, then we use the+ -- location of ghc to help find ghc-pkg in the case that the user did not+ -- specify the location of ghc-pkg directly:+ (ghcPkgProg, ghcPkgVersion, progdb2) <-+ requireProgramVersion+ verbosity+ ghcPkgProgram+ { programFindLocation = guessGhcPkgFromGhcPath ghcProg+ }+ anyVersion+ (userMaybeSpecifyPath "ghc-pkg" hcPkgPath progdb1)++ when (ghcVersion /= ghcPkgVersion) $+ dieWithException verbosity $+ VersionMismatchGHC (programPath ghcProg) ghcVersion (programPath ghcPkgProg) ghcPkgVersion+ -- Likewise we try to find the matching hsc2hs and haddock programs.+ let hsc2hsProgram' =+ hsc2hsProgram+ { programFindLocation = guessHsc2hsFromGhcPath ghcProg+ }+ haddockProgram' =+ haddockProgram+ { programFindLocation = guessHaddockFromGhcPath ghcProg+ }+ hpcProgram' =+ hpcProgram+ { programFindLocation = guessHpcFromGhcPath ghcProg+ }+ runghcProgram' =+ runghcProgram+ { programFindLocation = guessRunghcFromGhcPath ghcProg+ }+ progdb3 =+ addKnownProgram haddockProgram' $+ addKnownProgram hsc2hsProgram' $+ addKnownProgram hpcProgram' $+ addKnownProgram runghcProgram' progdb2++ -- configure gcc, ld, ar etc... based on the paths stored+ -- in the GHC settings file+ progdb4 =+ Internal.configureToolchain+ (ghcVersionImplInfo ghcVersion)+ ghcProg+ (compilerProperties comp)+ progdb3+ return progdb4++-- | Given something like /usr/local/bin/ghc-6.6.1(.exe) we try and find+-- the corresponding tool; e.g. if the tool is ghc-pkg, we try looking+-- for a versioned or unversioned ghc-pkg in the same dir, that is:+--+-- > /usr/local/bin/ghc-pkg-ghc-6.6.1(.exe)+-- > /usr/local/bin/ghc-pkg-6.6.1(.exe)+-- > /usr/local/bin/ghc-pkg(.exe)+guessToolFromGhcPath+ :: Program+ -> ConfiguredProgram+ -> Verbosity+ -> ProgramSearchPath+ -> IO (Maybe (FilePath, [FilePath]))+guessToolFromGhcPath tool ghcProg verbosity searchpath =+ do+ let toolname = programName tool+ given_path = programPath ghcProg+ given_dir = takeDirectory given_path+ real_path <- canonicalizePath given_path+ let real_dir = takeDirectory real_path+ versionSuffix path = takeVersionSuffix (dropExeExtension path)+ given_suf = versionSuffix given_path+ real_suf = versionSuffix real_path+ guessNormal dir = dir </> toolname <.> exeExtension buildPlatform+ guessGhcVersioned dir suf =+ dir+ </> (toolname ++ "-ghc" ++ suf)+ <.> exeExtension buildPlatform+ guessVersioned dir suf =+ dir+ </> (toolname ++ suf)+ <.> exeExtension buildPlatform+ mkGuesses dir suf+ | null suf = [guessNormal dir]+ | otherwise =+ [ guessGhcVersioned dir suf+ , guessVersioned dir suf+ , guessNormal dir+ ]+ -- order matters here, see https://github.com/haskell/cabal/issues/7390+ guesses =+ ( if real_path == given_path+ then []+ else mkGuesses real_dir real_suf+ )+ ++ mkGuesses given_dir given_suf+ info verbosity $+ "looking for tool "+ ++ toolname+ ++ " near compiler in "+ ++ given_dir+ debug verbosity $ "candidate locations: " ++ show guesses+ exists <- traverse doesFileExist guesses+ case [file | (file, True) <- zip guesses exists] of+ -- If we can't find it near ghc, fall back to the usual+ -- method.+ [] -> programFindLocation tool verbosity searchpath+ (fp : _) -> do+ info verbosity $ "found " ++ toolname ++ " in " ++ fp+ let lookedAt =+ map fst+ . takeWhile (\(_file, exist) -> not exist)+ $ zip guesses exists+ return (Just (fp, lookedAt))+ where+ takeVersionSuffix :: FilePath -> String+ takeVersionSuffix = takeWhileEndLE isSuffixChar++ isSuffixChar :: Char -> Bool+ isSuffixChar c = isDigit c || c == '.' || c == '-'++-- | Given something like /usr/local/bin/ghc-6.6.1(.exe) we try and find a+-- corresponding ghc-pkg, we try looking for both a versioned and unversioned+-- ghc-pkg in the same dir, that is:+--+-- > /usr/local/bin/ghc-pkg-ghc-6.6.1(.exe)+-- > /usr/local/bin/ghc-pkg-6.6.1(.exe)+-- > /usr/local/bin/ghc-pkg(.exe)+guessGhcPkgFromGhcPath+ :: ConfiguredProgram+ -> Verbosity+ -> ProgramSearchPath+ -> IO (Maybe (FilePath, [FilePath]))+guessGhcPkgFromGhcPath = guessToolFromGhcPath ghcPkgProgram++-- | Given something like /usr/local/bin/ghc-6.6.1(.exe) we try and find a+-- corresponding hsc2hs, we try looking for both a versioned and unversioned+-- hsc2hs in the same dir, that is:+--+-- > /usr/local/bin/hsc2hs-ghc-6.6.1(.exe)+-- > /usr/local/bin/hsc2hs-6.6.1(.exe)+-- > /usr/local/bin/hsc2hs(.exe)+guessHsc2hsFromGhcPath+ :: ConfiguredProgram+ -> Verbosity+ -> ProgramSearchPath+ -> IO (Maybe (FilePath, [FilePath]))+guessHsc2hsFromGhcPath = guessToolFromGhcPath hsc2hsProgram++-- | Given something like /usr/local/bin/ghc-6.6.1(.exe) we try and find a+-- corresponding haddock, we try looking for both a versioned and unversioned+-- haddock in the same dir, that is:+--+-- > /usr/local/bin/haddock-ghc-6.6.1(.exe)+-- > /usr/local/bin/haddock-6.6.1(.exe)+-- > /usr/local/bin/haddock(.exe)+guessHaddockFromGhcPath+ :: ConfiguredProgram+ -> Verbosity+ -> ProgramSearchPath+ -> IO (Maybe (FilePath, [FilePath]))+guessHaddockFromGhcPath = guessToolFromGhcPath haddockProgram++guessHpcFromGhcPath+ :: ConfiguredProgram+ -> Verbosity+ -> ProgramSearchPath+ -> IO (Maybe (FilePath, [FilePath]))+guessHpcFromGhcPath = guessToolFromGhcPath hpcProgram++guessRunghcFromGhcPath+ :: ConfiguredProgram+ -> Verbosity+ -> ProgramSearchPath+ -> IO (Maybe (FilePath, [FilePath]))+guessRunghcFromGhcPath = guessToolFromGhcPath runghcProgram++getGhcInfo :: Verbosity -> ConfiguredProgram -> IO [(String, String)]+getGhcInfo verbosity ghcProg = Internal.getGhcInfo verbosity implInfo ghcProg+ where+ version = fromMaybe (error "GHC.getGhcInfo: no ghc version") $ programVersion ghcProg+ implInfo = ghcVersionImplInfo version++-- | Given a single package DB, return all installed packages.+getPackageDBContents+ :: Verbosity+ -> Maybe (SymbolicPath CWD (Dir from))+ -> PackageDBX (SymbolicPath from (Dir PkgDB))+ -> ProgramDb+ -> IO InstalledPackageIndex+getPackageDBContents verbosity mbWorkDir packagedb progdb = do+ pkgss <- getInstalledPackages' verbosity mbWorkDir [packagedb] progdb+ toPackageIndex verbosity pkgss progdb++-- | Given a package DB stack, return all installed packages.+getInstalledPackages+ :: Verbosity+ -> Compiler+ -> Maybe (SymbolicPath CWD (Dir from))+ -> PackageDBStackX (SymbolicPath from (Dir PkgDB))+ -> ProgramDb+ -> IO InstalledPackageIndex+getInstalledPackages verbosity comp mbWorkDir packagedbs progdb = do+ checkPackageDbEnvVar verbosity+ checkPackageDbStack verbosity comp packagedbs+ pkgss <- getInstalledPackages' verbosity mbWorkDir packagedbs progdb+ index <- toPackageIndex verbosity pkgss progdb+ return $! hackRtsPackage index+ where+ hackRtsPackage index =+ case PackageIndex.lookupPackageName index (mkPackageName "rts") of+ [(_, [rts])] ->+ PackageIndex.insert (removeMingwIncludeDir rts) index+ _ -> index -- No (or multiple) ghc rts package is registered!!+ -- Feh, whatever, the ghc test suite does some crazy stuff.++-- | Given a list of @(PackageDB, InstalledPackageInfo)@ pairs, produce a+-- @PackageIndex@. Helper function used by 'getPackageDBContents' and+-- 'getInstalledPackages'.+toPackageIndex+ :: Verbosity+ -> [(PackageDBX a, [InstalledPackageInfo])]+ -> ProgramDb+ -> IO InstalledPackageIndex+toPackageIndex verbosity pkgss progdb = do+ -- On Windows, various fields have $topdir/foo rather than full+ -- paths. We need to substitute the right value in so that when+ -- we, for example, call gcc, we have proper paths to give it.+ topDir <- getLibDir' verbosity ghcProg+ let indices =+ [ PackageIndex.fromList (map (Internal.substTopDir topDir) pkgs)+ | (_, pkgs) <- pkgss+ ]+ return $! mconcat indices+ where+ ghcProg = fromMaybe (error "GHC.toPackageIndex: no ghc program") $ lookupProgram ghcProgram progdb++-- | Return the 'FilePath' to the GHC application data directory.+--+-- @since 3.4.0.0+getGhcAppDir :: IO FilePath+getGhcAppDir = getAppUserDataDirectory "ghc"++getLibDir :: Verbosity -> LocalBuildInfo -> IO FilePath+getLibDir verbosity lbi =+ dropWhileEndLE isSpace+ `fmap` getDbProgramOutput+ verbosity+ ghcProgram+ (withPrograms lbi)+ ["--print-libdir"]++getLibDir' :: Verbosity -> ConfiguredProgram -> IO FilePath+getLibDir' verbosity ghcProg =+ dropWhileEndLE isSpace+ `fmap` getProgramOutput verbosity ghcProg ["--print-libdir"]++-- | Return the 'FilePath' to the global GHC package database.+getGlobalPackageDB :: Verbosity -> ConfiguredProgram -> IO FilePath+getGlobalPackageDB verbosity ghcProg =+ dropWhileEndLE isSpace+ `fmap` getProgramOutput verbosity ghcProg ["--print-global-package-db"]++-- | Return the 'FilePath' to the per-user GHC package database.+getUserPackageDB+ :: Verbosity -> ConfiguredProgram -> Platform -> IO FilePath+getUserPackageDB _verbosity ghcProg platform = do+ -- It's rather annoying that we have to reconstruct this, because ghc+ -- hides this information from us otherwise. But for certain use cases+ -- like change monitoring it really can't remain hidden.+ appdir <- getGhcAppDir+ return (appdir </> platformAndVersion </> packageConfFileName)+ where+ platformAndVersion =+ Internal.ghcPlatformAndVersionString+ platform+ ghcVersion+ packageConfFileName = "package.conf.d"+ ghcVersion = fromMaybe (error "GHC.getUserPackageDB: no ghc version") $ programVersion ghcProg++checkPackageDbEnvVar :: Verbosity -> IO ()+checkPackageDbEnvVar verbosity =+ Internal.checkPackageDbEnvVar verbosity "GHC" "GHC_PACKAGE_PATH"++checkPackageDbStack :: Eq fp => Verbosity -> Compiler -> PackageDBStackX fp -> IO ()+checkPackageDbStack verbosity comp =+ if flagPackageConf implInfo+ then checkPackageDbStackPre76 verbosity+ else checkPackageDbStackPost76 verbosity+ where+ implInfo = ghcVersionImplInfo (compilerVersion comp)++checkPackageDbStackPost76 :: Eq fp => Verbosity -> PackageDBStackX fp -> IO ()+checkPackageDbStackPost76 _ (GlobalPackageDB : rest)+ | GlobalPackageDB `notElem` rest = return ()+checkPackageDbStackPost76 verbosity rest+ | GlobalPackageDB `elem` rest =+ dieWithException verbosity CheckPackageDbStackPost76+checkPackageDbStackPost76 _ _ = return ()++checkPackageDbStackPre76 :: Eq fp => Verbosity -> PackageDBStackX fp -> IO ()+checkPackageDbStackPre76 _ (GlobalPackageDB : rest)+ | GlobalPackageDB `notElem` rest = return ()+checkPackageDbStackPre76 verbosity rest+ | GlobalPackageDB `notElem` rest =+ dieWithException verbosity CheckPackageDbStackPre76+checkPackageDbStackPre76 verbosity _ =+ dieWithException verbosity GlobalPackageDbSpecifiedFirst++-- GHC < 6.10 put "$topdir/include/mingw" in rts's installDirs. This+-- breaks when you want to use a different gcc, so we need to filter+-- it out.+removeMingwIncludeDir :: InstalledPackageInfo -> InstalledPackageInfo+removeMingwIncludeDir pkg =+ let ids = InstalledPackageInfo.includeDirs pkg+ ids' = filter (not . ("mingw" `isSuffixOf`)) ids+ in pkg{InstalledPackageInfo.includeDirs = ids'}++-- | Get the packages from specific PackageDBs, not cumulative.+getInstalledPackages'+ :: Verbosity+ -> Maybe (SymbolicPath CWD (Dir from))+ -> [PackageDBX (SymbolicPath from (Dir PkgDB))]+ -> ProgramDb+ -> IO [(PackageDBX (SymbolicPath from (Dir PkgDB)), [InstalledPackageInfo])]+getInstalledPackages' verbosity mbWorkDir packagedbs progdb =+ sequenceA+ [ do+ pkgs <- HcPkg.dump (hcPkgInfo progdb) verbosity mbWorkDir packagedb+ return (packagedb, pkgs)+ | packagedb <- packagedbs+ ]++getInstalledPackagesMonitorFiles+ :: forall from+ . Verbosity+ -> Maybe (SymbolicPath CWD (Dir from))+ -> Platform+ -> ProgramDb+ -> [PackageDBS from]+ -> IO [FilePath]+getInstalledPackagesMonitorFiles verbosity mbWorkDir platform progdb =+ traverse getPackageDBPath+ where+ getPackageDBPath :: PackageDBS from -> IO FilePath+ getPackageDBPath GlobalPackageDB =+ selectMonitorFile =<< getGlobalPackageDB verbosity ghcProg+ getPackageDBPath UserPackageDB =+ selectMonitorFile =<< getUserPackageDB verbosity ghcProg platform+ getPackageDBPath (SpecificPackageDB path) = selectMonitorFile (interpretSymbolicPath mbWorkDir path)++ -- GHC has old style file dbs, and new style directory dbs.+ -- Note that for dir style dbs, we only need to monitor the cache file, not+ -- the whole directory. The ghc program itself only reads the cache file+ -- so it's safe to only monitor this one file.+ selectMonitorFile path0 = do+ let path =+ if isRelative path0+ then interpretSymbolicPath mbWorkDir (makeRelativePathEx path0)+ else path0+ isFileStyle <- doesFileExist path+ if isFileStyle+ then return path+ else return (path </> "package.cache")++ ghcProg = fromMaybe (error "GHC.toPackageIndex: no ghc program") $ lookupProgram ghcProgram progdb++-- -----------------------------------------------------------------------------+-- Building a library++buildLib+ :: BuildFlags+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> Library+ -> ComponentLocalBuildInfo+ -> IO ()+buildLib flags numJobs pkg lbi lib clbi =+ GHC.build numJobs pkg $+ PreBuildComponentInputs+ { buildingWhat = BuildNormal flags+ , localBuildInfo = lbi+ , targetInfo = TargetInfo clbi (CLib lib)+ }++replLib+ :: ReplFlags+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> Library+ -> ComponentLocalBuildInfo+ -> IO ()+replLib flags numJobs pkg lbi lib clbi =+ GHC.build numJobs pkg $+ PreBuildComponentInputs+ { buildingWhat = BuildRepl flags+ , localBuildInfo = lbi+ , targetInfo = TargetInfo clbi (CLib lib)+ }++-- | Start a REPL without loading any source files.+startInterpreter+ :: Verbosity+ -> ProgramDb+ -> Compiler+ -> Platform+ -> PackageDBStack+ -> IO ()+startInterpreter verbosity progdb comp platform packageDBs = do+ let replOpts =+ mempty+ { ghcOptMode = toFlag GhcModeInteractive+ , ghcOptPackageDBs = packageDBs+ }+ checkPackageDbStack verbosity comp packageDBs+ (ghcProg, _) <- requireProgram verbosity ghcProgram progdb+ -- This doesn't pass source file arguments to GHC, so we don't have to worry+ -- about using a response file here.+ runGHC verbosity ghcProg comp platform Nothing replOpts++-- -----------------------------------------------------------------------------+-- Building an executable or foreign library++-- | Build a foreign library+buildFLib+ :: Verbosity+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> ForeignLib+ -> ComponentLocalBuildInfo+ -> IO ()+buildFLib v numJobs pkg lbi flib clbi =+ GHC.build numJobs pkg $+ PreBuildComponentInputs+ { buildingWhat =+ BuildNormal $+ mempty+ { buildCommonFlags =+ mempty{setupVerbosity = toFlag v}+ }+ , localBuildInfo = lbi+ , targetInfo = TargetInfo clbi (CFLib flib)+ }++replFLib+ :: ReplFlags+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> ForeignLib+ -> ComponentLocalBuildInfo+ -> IO ()+replFLib replFlags njobs pkg lbi flib clbi =+ GHC.build njobs pkg $+ PreBuildComponentInputs+ { buildingWhat = BuildRepl replFlags+ , localBuildInfo = lbi+ , targetInfo = TargetInfo clbi (CFLib flib)+ }++-- | Build an executable with GHC.+buildExe+ :: Verbosity+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> Executable+ -> ComponentLocalBuildInfo+ -> IO ()+buildExe v njobs pkg lbi exe clbi =+ GHC.build njobs pkg $+ PreBuildComponentInputs+ { buildingWhat =+ BuildNormal $+ mempty+ { buildCommonFlags =+ mempty{setupVerbosity = toFlag v}+ }+ , localBuildInfo = lbi+ , targetInfo = TargetInfo clbi (CExe exe)+ }++replExe+ :: ReplFlags+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> Executable+ -> ComponentLocalBuildInfo+ -> IO ()+replExe replFlags njobs pkg lbi exe clbi =+ GHC.build njobs pkg $+ PreBuildComponentInputs+ { buildingWhat = BuildRepl replFlags+ , localBuildInfo = lbi+ , targetInfo = TargetInfo clbi (CExe exe)+ }++-- | Extracts a String representing a hash of the ABI of a built+-- library. It can fail if the library has not yet been built.+libAbiHash+ :: Verbosity+ -> PackageDescription+ -> LocalBuildInfo+ -> Library+ -> ComponentLocalBuildInfo+ -> IO String+libAbiHash verbosity _pkg_descr lbi lib clbi = do+ let+ libBi = libBuildInfo lib+ comp = compiler lbi+ platform = hostPlatform lbi+ mbWorkDir = mbWorkDirLBI lbi+ vanillaArgs =+ (Internal.componentGhcOptions verbosity lbi libBi clbi (componentBuildDir lbi clbi))+ `mappend` mempty+ { ghcOptMode = toFlag GhcModeAbiHash+ , ghcOptInputModules = toNubListR $ exposedModules lib+ }+ sharedArgs =+ vanillaArgs+ `mappend` mempty+ { ghcOptDynLinkMode = toFlag GhcDynamicOnly+ , ghcOptFPic = toFlag True+ , ghcOptHiSuffix = toFlag "dyn_hi"+ , ghcOptObjSuffix = toFlag "dyn_o"+ , ghcOptExtra = hcOptions GHC libBi ++ hcSharedOptions GHC libBi+ }+ profArgs =+ vanillaArgs+ `mappend` mempty+ { ghcOptProfilingMode = toFlag True+ , ghcOptProfilingAuto =+ Internal.profDetailLevelFlag+ True+ (withProfLibDetail lbi)+ , ghcOptHiSuffix = toFlag "p_hi"+ , ghcOptObjSuffix = toFlag "p_o"+ , ghcOptExtra = hcOptions GHC libBi ++ hcProfOptions GHC libBi+ }+ profDynArgs =+ vanillaArgs+ `mappend` mempty+ { ghcOptProfilingMode = toFlag True+ , ghcOptProfilingAuto =+ Internal.profDetailLevelFlag+ True+ (withProfLibDetail lbi)+ , ghcOptDynLinkMode = toFlag GhcDynamicOnly+ , ghcOptFPic = toFlag True+ , ghcOptHiSuffix = toFlag "p_dyn_hi"+ , ghcOptObjSuffix = toFlag "p_dyn_o"+ , ghcOptExtra = hcOptions GHC libBi ++ hcProfSharedOptions GHC libBi+ }+ ghcArgs =+ let (libWays, _, _) = buildWays lbi+ in case libWays (componentIsIndefinite clbi) of+ (ProfDynWay : _) -> profDynArgs+ (ProfWay : _) -> profArgs+ (StaticWay : _) -> vanillaArgs+ (DynWay : _) -> sharedArgs+ _ -> error "libAbiHash: Can't find an enabled library way"++ (ghcProg, _) <- requireProgram verbosity ghcProgram (withPrograms lbi)++ hash <-+ getProgramInvocationOutput+ verbosity+ =<< ghcInvocation verbosity ghcProg comp platform mbWorkDir ghcArgs++ return (takeWhile (not . isSpace) hash)++-- -----------------------------------------------------------------------------+-- Installing++-- | Install executables for GHC.+installExe+ :: Verbosity+ -> LocalBuildInfo+ -> FilePath+ -- ^ Where to copy the files to+ -> FilePath+ -- ^ Build location+ -> (FilePath, FilePath)+ -- ^ Executable (prefix,suffix)+ -> PackageDescription+ -> Executable+ -> IO ()+installExe+ verbosity+ lbi+ binDir+ buildPref+ (progprefix, progsuffix)+ _pkg+ exe = do+ createDirectoryIfMissingVerbose verbosity True binDir+ let exeName' = unUnqualComponentName $ exeName exe+ exeFileName = exeTargetName (hostPlatform lbi) (exeName exe)+ fixedExeBaseName = progprefix ++ exeName' ++ progsuffix+ installBinary dest = do+ installExecutableFile+ verbosity+ (buildPref </> exeName' </> exeFileName)+ (dest <.> exeExtension (hostPlatform lbi))+ when (stripExes lbi) $+ Strip.stripExe+ verbosity+ (hostPlatform lbi)+ (withPrograms lbi)+ (dest <.> exeExtension (hostPlatform lbi))+ installBinary (binDir </> fixedExeBaseName)++-- | Install foreign library for GHC.+installFLib+ :: Verbosity+ -> LocalBuildInfo+ -> FilePath+ -- ^ install location+ -> FilePath+ -- ^ Build location+ -> PackageDescription+ -> ForeignLib+ -> IO ()+installFLib verbosity lbi targetDir builtDir _pkg flib =+ install+ (foreignLibIsShared flib)+ builtDir+ targetDir+ (flibTargetName lbi flib)+ where+ install isShared srcDir dstDir name = do+ let src = srcDir </> name+ dst = dstDir </> name+ createDirectoryIfMissingVerbose verbosity True targetDir+ -- TODO: Should we strip? (stripLibs lbi)+ if isShared+ then installExecutableFile verbosity src dst+ else installOrdinaryFile verbosity src dst+ -- Now install appropriate symlinks if library is versioned+ let (Platform _ os) = hostPlatform lbi+ when (not (null (foreignLibVersion flib os))) $ do+ when (os /= Linux) $+ dieWithException verbosity $+ CantInstallForeignLib+#ifndef mingw32_HOST_OS+ -- 'createSymbolicLink file1 file2' creates a symbolic link+ -- named 'file2' which points to the file 'file1'.+ -- Note that we do want a symlink to 'name' rather than+ -- 'dst', because the symlink will be relative to the+ -- directory it's created in.+ -- Finally, we first create the symlinks in a temporary+ -- directory and then rename to simulate 'ln --force'.+ withTempDirectory verbosity dstDir nm $ \tmpDir -> do+ let link1 = flibBuildName lbi flib+ link2 = "lib" ++ nm <.> "so"+ createSymbolicLink name (tmpDir </> link1)+ renameFile (tmpDir </> link1) (dstDir </> link1)+ createSymbolicLink name (tmpDir </> link2)+ renameFile (tmpDir </> link2) (dstDir </> link2)+ where+ nm :: String+ nm = unUnqualComponentName $ foreignLibName flib+#endif /* mingw32_HOST_OS */++-- | Install for ghc, .hi, .a and, if --with-ghci given, .o+installLib+ :: Verbosity+ -> LocalBuildInfo+ -> FilePath+ -- ^ install location+ -> FilePath+ -- ^ install location for dynamic libraries+ -> FilePath+ -- ^ Build location+ -> PackageDescription+ -> Library+ -> ComponentLocalBuildInfo+ -> IO ()+installLib verbosity lbi targetDir dynlibTargetDir _builtDir pkg lib clbi = do+ let+ (wantedLibWays, _, _) = buildWays lbi+ isIndef = componentIsIndefinite clbi+ libWays = wantedLibWays isIndef++ info verbosity ("Wanted install ways: " ++ show libWays)++ -- copy .hi files over:+ forM_ (wantedLibWays isIndef) $ \w -> case w of+ StaticWay -> copyModuleFiles (Suffix "hi")+ DynWay -> copyModuleFiles (Suffix "dyn_hi")+ ProfWay -> copyModuleFiles (Suffix "p_hi")+ ProfDynWay -> copyModuleFiles (Suffix "p_dyn_hi")++ -- copy extra compilation artifacts that ghc plugins may produce+ copyDirectoryIfExists extraCompilationArtifacts++ -- copy the built library files over:+ when (has_code && hasLib) $ do+ forM_ libWays $ \w -> case w of+ StaticWay -> do+ sequence_+ [ installOrdinary+ builtDir+ targetDir+ (mkGenericStaticLibName (l ++ f))+ | l <-+ getHSLibraryName+ (componentUnitId clbi)+ : (extraBundledLibs (libBuildInfo lib))+ , f <- "" : extraLibFlavours (libBuildInfo lib)+ ]+ whenGHCi $ installOrdinary builtDir targetDir ghciLibName+ ProfWay -> do+ installOrdinary builtDir targetDir profileLibName+ whenGHCi $ installOrdinary builtDir targetDir ghciProfLibName+ ProfDynWay -> do+ installShared+ builtDir+ dynlibTargetDir+ (mkProfSharedLibName platform compiler_id uid)+ DynWay -> do+ if+ -- The behavior for "extra-bundled-libraries" changed in version 2.5.0.+ -- See ghc issue #15837 and Cabal PR #5855.+ | specVersion pkg < CabalSpecV3_0 -> do+ sequence_+ [ installShared+ builtDir+ dynlibTargetDir+ (mkGenericSharedLibName platform compiler_id (l ++ f))+ | l <- getHSLibraryName uid : extraBundledLibs (libBuildInfo lib)+ , f <- "" : extraDynLibFlavours (libBuildInfo lib)+ ]+ | otherwise -> do+ sequence_+ [ installShared+ builtDir+ dynlibTargetDir+ ( mkGenericSharedLibName+ platform+ compiler_id+ (getHSLibraryName uid ++ f)+ )+ | f <- "" : extraDynLibFlavours (libBuildInfo lib)+ ]+ sequence_+ [ do+ files <- getDirectoryContents (i builtDir)+ let l' =+ mkGenericSharedBundledLibName+ platform+ compiler_id+ (l ++ f)+ forM_ files $ \file ->+ when (l' `isPrefixOf` file) $ do+ isFile <- doesFileExist (i $ builtDir </> makeRelativePathEx file)+ when isFile $ do+ installShared+ builtDir+ dynlibTargetDir+ file+ | l <- extraBundledLibs (libBuildInfo lib)+ , f <- "" : extraDynLibFlavours (libBuildInfo lib)+ ]+ where+ -- See Note [Symbolic paths] in Distribution.Utils.Path+ i = interpretSymbolicPathLBI lbi++ builtDir = componentBuildDir lbi clbi+ mbWorkDir = mbWorkDirLBI lbi++ install isShared srcDir dstDir name = do+ let src = i $ srcDir </> makeRelativePathEx name+ dst = dstDir </> name++ createDirectoryIfMissingVerbose verbosity True dstDir++ if isShared+ then installExecutableFile verbosity src dst+ else installOrdinaryFile verbosity src dst++ when (stripLibs lbi) $+ Strip.stripLib+ verbosity+ platform+ (withPrograms lbi)+ dst++ installOrdinary = install False+ installShared = install True++ copyModuleFiles ext = do+ files <- findModuleFilesCwd verbosity mbWorkDir [builtDir] [ext] (allLibModules lib clbi)+ let files' = map (i *** getSymbolicPath) files+ installOrdinaryFiles verbosity targetDir files'++ copyDirectoryIfExists :: RelativePath Build (Dir Artifacts) -> IO ()+ copyDirectoryIfExists dirName = do+ let src = i $ builtDir </> dirName+ dst = targetDir </> getSymbolicPath dirName+ dirExists <- doesDirectoryExist src+ when dirExists $ copyDirectoryRecursive verbosity src dst++ compiler_id = compilerId (compiler lbi)+ platform = hostPlatform lbi+ uid = componentUnitId clbi+ profileLibName = mkProfLibName uid+ ghciLibName = Internal.mkGHCiLibName uid+ ghciProfLibName = Internal.mkGHCiProfLibName uid++ hasLib =+ not $+ null (allLibModules lib clbi)+ && null (cSources (libBuildInfo lib))+ && null (cxxSources (libBuildInfo lib))+ && null (cmmSources (libBuildInfo lib))+ && null (asmSources (libBuildInfo lib))+ && (null (jsSources (libBuildInfo lib)) || not hasJsSupport)+ hasJsSupport = case hostPlatform lbi of+ Platform JavaScript _ -> True+ _ -> False+ has_code = not (componentIsIndefinite clbi)+ whenGHCi = when (hasLib && withGHCiLib lbi && has_code)++-- -----------------------------------------------------------------------------+-- Registering++hcPkgInfo :: ProgramDb -> HcPkg.HcPkgInfo+hcPkgInfo progdb =+ HcPkg.HcPkgInfo+ { HcPkg.hcPkgProgram = ghcPkgProg+ , HcPkg.noPkgDbStack = v < [6, 9]+ , HcPkg.noVerboseFlag = v < [6, 11]+ , HcPkg.flagPackageConf = v < [7, 5]+ , HcPkg.supportsDirDbs = v >= [6, 8]+ , HcPkg.requiresDirDbs = v >= [7, 10]+ , HcPkg.nativeMultiInstance = v >= [7, 10]+ , HcPkg.recacheMultiInstance = v >= [6, 12]+ , HcPkg.suppressFilesCheck = v >= [6, 6]+ }+ where+ v = versionNumbers ver+ ghcPkgProg = fromMaybe (error "GHC.hcPkgInfo: no ghc program") $ lookupProgram ghcPkgProgram progdb+ ver = fromMaybe (error "GHC.hcPkgInfo: no ghc version") $ programVersion ghcPkgProg++registerPackage+ :: Verbosity+ -> ProgramDb+ -> Maybe (SymbolicPath CWD (Dir from))+ -> PackageDBStackS from+ -> InstalledPackageInfo+ -> HcPkg.RegisterOptions+ -> IO ()+registerPackage verbosity progdb mbWorkDir packageDbs installedPkgInfo registerOptions =+ HcPkg.register+ (hcPkgInfo progdb)+ verbosity+ mbWorkDir+ packageDbs+ installedPkgInfo+ registerOptions++pkgRoot :: Verbosity -> LocalBuildInfo -> PackageDB -> IO (SymbolicPath CWD (Dir Pkg))+pkgRoot verbosity lbi = fmap makeSymbolicPath . pkgRoot'+ where+ pkgRoot' GlobalPackageDB =+ let ghcProg = fromMaybe (error "GHC.pkgRoot: no ghc program") $ lookupProgram ghcProgram (withPrograms lbi)+ in fmap takeDirectory (getGlobalPackageDB verbosity ghcProg)+ pkgRoot' UserPackageDB = do+ appDir <- getGhcAppDir+ let ver = compilerVersion (compiler lbi)+ subdir =+ System.Info.arch+ ++ '-'+ : System.Info.os+ ++ '-'+ : prettyShow ver+ rootDir = appDir </> subdir+ -- We must create the root directory for the user package database if it+ -- does not yet exist. Otherwise '${pkgroot}' will resolve to a+ -- directory at the time of 'ghc-pkg register', and registration will+ -- fail.+ createDirectoryIfMissing True rootDir+ return rootDir+ pkgRoot' (SpecificPackageDB fp) =+ return $+ takeDirectory $+ interpretSymbolicPathLBI lbi fp
+ src/Distribution/Simple/GHC/Build.hs view
@@ -0,0 +1,155 @@+{-# LANGUAGE DataKinds #-}++module Distribution.Simple.GHC.Build where++import Distribution.Compat.Prelude+import Prelude ()++import Control.Monad.IO.Class+import Distribution.PackageDescription as PD hiding (buildInfo)+import Distribution.Simple.Build.Inputs+import Distribution.Simple.Flag (Flag)+import Distribution.Simple.GHC.Build.ExtraSources+import Distribution.Simple.GHC.Build.Link+import Distribution.Simple.GHC.Build.Modules+import Distribution.Simple.GHC.Build.Utils (compilerBuildWay, isHaskell, withDynFLib)+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.Program.Builtin (ghcProgram)+import Distribution.Simple.Program.Db (requireProgram)+import Distribution.Simple.Utils++import Distribution.Types.ComponentLocalBuildInfo+import Distribution.Types.PackageName.Magic (fakePackageId)+import Distribution.Types.ParStrat+import Distribution.Utils.NubList (fromNubListR)+import Distribution.Utils.Path++import System.FilePath (splitDirectories)++{- Note [Build Target Dir vs Target Dir]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Where to place the build result (targetDir) and the build artifacts (buildTargetDir).++\* For libraries, targetDir == buildTargetDir, where both the library and+artifacts are put together.++\* For executables or foreign libs, buildTargetDir == targetDir/<name-of-target-dir>-tmp, where+ the targetDir is the location where the target (e.g. the executable) is written to+ and buildTargetDir is where the compilation artifacts (e.g. Main.o) will live+ Arguably, this difference should not exist (#9498) (TODO)++For instance, for a component `cabal-benchmarks`:+ targetDir == <buildDir>/cabal-benchmarks+ buildTargetDir == <buildDir>/cabal-benchmarks/cabal-benchmarks-tmp++Or, for a library `Cabal`:+ targetDir == <buildDir>/.+ buildTargetDir == targetDir++Furthermore, we need to account for the limit of characters in ghc+invocations that different OSes constrain us to. Cabal invocations can+rapidly reach this limit, in part, due to the long length of cabal v2+prefixes. To minimize the likelihood, we use+`tryMakeRelativeToWorkingDir` to shorten the paths used in invocations+(see da6321bb).++However, in executables, we don't do this. It seems that we don't need to do it+for executable-like components because the linking step, instead of passing as+an argument the path to each module, it simply passes the module name, the sources dir, and --make.+RM: I believe we can use --make + module names instead of paths-to-objects+for linking libraries too (2024-01) (TODO)+-}++-- | The main build phase of building a component.+-- Includes building Haskell modules, extra build sources, and linking.+build+ :: Flag ParStrat+ -> PackageDescription+ -> PreBuildComponentInputs+ -- ^ The context and component being built in it.+ -> IO ()+build numJobs pkg_descr pbci = do+ let+ verbosity = buildVerbosity pbci+ isLib = buildIsLib pbci+ lbi = localBuildInfo pbci+ bi = buildBI pbci+ clbi = buildCLBI pbci+ isIndef = componentIsIndefinite clbi+ mbWorkDir = mbWorkDirLBI lbi+ i = interpretSymbolicPathLBI lbi -- See Note [Symbolic paths] in Distribution.Utils.Path++ -- Create a few directories for building the component+ -- See Note [Build Target Dir vs Target Dir]+ let targetDir0 :: SymbolicPath Pkg ('Dir Build)+ targetDir0 = componentBuildDir lbi clbi+ buildTargetDir0 :: SymbolicPath Pkg ('Dir Artifacts)+ buildTargetDir0+ -- Libraries use the target dir for building (see above)+ | isLib = coerceSymbolicPath targetDir0+ -- In other cases, use targetDir/<name-of-target-dir>-tmp+ | targetDirName : _ <- reverse $ splitDirectories $ getSymbolicPath targetDir0 =+ coerceSymbolicPath targetDir0 </> makeRelativePathEx (targetDirName ++ "-tmp")+ | otherwise = error "GHC.build: targetDir is empty"++ liftIO $ do+ createDirectoryIfMissingVerbose verbosity True $ i targetDir0+ createDirectoryIfMissingVerbose verbosity True $ i buildTargetDir0++ -- See Note [Build Target Dir vs Target Dir] as well+ let targetDir = targetDir0 -- NB: no 'makeRelative'+ buildTargetDir <-+ if isLib+ then -- NB: this might fail to make the buildTargetDir relative,+ -- as noted in #9776. Oh well.+ tryMakeRelative mbWorkDir buildTargetDir0+ else return buildTargetDir0+ -- To preserve the previous behaviour, we don't use relative dirs for+ -- executables. Historically, this isn't needed to reduce the CLI limit+ -- (unlike for libraries) because we link executables with the module names+ -- instead of passing the path to object file -- that's something else we+ -- can now fix after the refactor lands.++ (ghcProg, _) <- liftIO $ requireProgram verbosity ghcProgram (withPrograms lbi)++ -- Ways which are wanted from configuration flags+ let wantedWays@(wantedLibWays, wantedFLibWay, wantedExeWay) = buildWays lbi++ -- Ways which are needed due to the compiler configuration+ let doingTH = usesTemplateHaskellOrQQ bi+ defaultGhcWay = compilerBuildWay (buildCompiler pbci)+ wantedModBuildWays = case buildComponent pbci of+ CLib _ -> wantedLibWays isIndef+ CFLib fl -> [wantedFLibWay (withDynFLib fl)]+ CExe _ -> [wantedExeWay]+ CTest _ -> [wantedExeWay]+ CBench _ -> [wantedExeWay]+ finalModBuildWays =+ wantedModBuildWays+ ++ [defaultGhcWay | doingTH && defaultGhcWay `notElem` wantedModBuildWays]+ compNameStr = showComponentName $ componentName $ buildComponent pbci++ liftIO $ info verbosity ("Wanted module build ways(" ++ compNameStr ++ "): " ++ show wantedModBuildWays)+ liftIO $ info verbosity ("Final module build ways(" ++ compNameStr ++ "): " ++ show finalModBuildWays)+ -- We need a separate build and link phase, and C sources must be compiled+ -- after Haskell modules, because C sources may depend on stub headers+ -- generated from compiling Haskell modules (#842, #3294).+ (mbMainFile, inputModules) <- componentInputs buildTargetDir pkg_descr pbci+ let (hsMainFile, nonHsMainFile) =+ case mbMainFile of+ Just mainFile+ | PD.package pkg_descr == fakePackageId+ || isHaskell (getSymbolicPath mainFile) ->+ (Just mainFile, Nothing)+ | otherwise ->+ (Nothing, Just mainFile)+ Nothing -> (Nothing, Nothing)+ buildOpts <- buildHaskellModules numJobs ghcProg hsMainFile inputModules buildTargetDir finalModBuildWays pbci+ extraSources <- buildAllExtraSources nonHsMainFile ghcProg buildTargetDir wantedWays pbci+ linkOrLoadComponent+ ghcProg+ pkg_descr+ (fromNubListR extraSources)+ (buildTargetDir, targetDir)+ (wantedWays, buildOpts)+ pbci
+ src/Distribution/Simple/GHC/Build/ExtraSources.hs view
@@ -0,0 +1,272 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DisambiguateRecordFields #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RankNTypes #-}++module Distribution.Simple.GHC.Build.ExtraSources where++import Control.Monad+import Data.Foldable+import Distribution.Simple.Flag+import qualified Distribution.Simple.GHC.Internal as Internal+import Distribution.Simple.Program.GHC+import Distribution.Simple.Utils+import Distribution.Utils.NubList++import Distribution.Types.BuildInfo+import Distribution.Types.Component+import Distribution.Types.TargetInfo++import Distribution.Simple.Build.Inputs+import Distribution.Simple.GHC.Build.Modules+import Distribution.Simple.GHC.Build.Utils+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.Program.Types+import Distribution.Simple.Setup.Common (commonSetupTempFileOptions)+import Distribution.System (Arch (JavaScript), Platform (..))+import Distribution.Types.ComponentLocalBuildInfo+import Distribution.Utils.Path+import Distribution.Verbosity (Verbosity)++-- | An action that builds all the extra build sources of a component, i.e. C,+-- C++, Js, Asm, C-- sources.+buildAllExtraSources+ :: Maybe (SymbolicPath Pkg File)+ -- ^ An optional non-Haskell Main file+ -> ConfiguredProgram+ -- ^ The GHC configured program+ -> SymbolicPath Pkg (Dir Artifacts)+ -- ^ The build directory for this target+ -> (Bool -> [BuildWay], Bool -> BuildWay, BuildWay)+ -- ^ Needed build ways+ -> PreBuildComponentInputs+ -- ^ The context and component being built in it.+ -> IO (NubListR (SymbolicPath Pkg File))+ -- ^ Returns the (nubbed) list of extra sources that were built+buildAllExtraSources =+ mconcat+ [ buildCSources+ , buildCxxSources+ , buildJsSources+ , buildAsmSources+ , buildCmmSources+ ]++buildCSources+ , buildCxxSources+ , buildJsSources+ , buildAsmSources+ , buildCmmSources+ :: Maybe (SymbolicPath Pkg File)+ -- ^ An optional non-Haskell Main file+ -> ConfiguredProgram+ -- ^ The GHC configured program+ -> SymbolicPath Pkg (Dir Artifacts)+ -- ^ The build directory for this target+ -> (Bool -> [BuildWay], Bool -> BuildWay, BuildWay)+ -- ^ Needed build ways+ -> PreBuildComponentInputs+ -- ^ The context and component being built in it.+ -> IO (NubListR (SymbolicPath Pkg File))+ -- ^ Returns the list of extra sources that were built+buildCSources mbMainFile =+ buildExtraSources+ "C Sources"+ Internal.componentCcGhcOptions+ ( \c -> do+ let cFiles = cSources (componentBuildInfo c)+ case c of+ CExe{}+ | Just main <- mbMainFile+ , isC $ getSymbolicPath main ->+ cFiles ++ [main]+ _otherwise -> cFiles+ )+buildCxxSources mbMainFile =+ buildExtraSources+ "C++ Sources"+ Internal.componentCxxGhcOptions+ ( \c -> do+ let cxxFiles = cxxSources (componentBuildInfo c)+ case c of+ CExe{}+ | Just main <- mbMainFile+ , isCxx $ getSymbolicPath main ->+ cxxFiles ++ [main]+ _otherwise -> cxxFiles+ )+buildJsSources _mbMainFile ghcProg buildTargetDir neededWays = do+ Platform hostArch _ <- hostPlatform <$> localBuildInfo+ let hasJsSupport = hostArch == JavaScript+ buildExtraSources+ "JS Sources"+ Internal.componentJsGhcOptions+ ( \c ->+ if hasJsSupport+ then -- JS files are C-like with GHC's JS backend: they are+ -- "compiled" into `.o` files (renamed with a header).+ -- This is a difference from GHCJS, for which we only+ -- pass the JS files at link time.+ jsSources (componentBuildInfo c)+ else mempty+ )+ ghcProg+ buildTargetDir+ neededWays+buildAsmSources _mbMainFile =+ buildExtraSources+ "Assembler Sources"+ Internal.componentAsmGhcOptions+ (asmSources . componentBuildInfo)+buildCmmSources _mbMainFile =+ buildExtraSources+ "C-- Sources"+ Internal.componentCmmGhcOptions+ (cmmSources . componentBuildInfo)++-- | Create 'PreBuildComponentRules' for a given type of extra build sources+-- which are compiled via a GHC invocation with the given options. Used to+-- define built-in extra sources, such as, C, Cxx, Js, Asm, and Cmm sources.+buildExtraSources+ :: String+ -- ^ String describing the extra sources being built, for printing.+ -> ( Verbosity+ -> LocalBuildInfo+ -> BuildInfo+ -> ComponentLocalBuildInfo+ -> SymbolicPath Pkg (Dir Artifacts)+ -> SymbolicPath Pkg File+ -> GhcOptions+ )+ -- ^ Function to determine the @'GhcOptions'@ for the+ -- invocation of GHC when compiling these extra sources (e.g.+ -- @'Internal.componentCxxGhcOptions'@,+ -- @'Internal.componentCmmGhcOptions'@)+ -> (Component -> [SymbolicPath Pkg File])+ -- ^ View the extra sources of a component, typically from+ -- the build info (e.g. @'asmSources'@, @'cSources'@).+ -- @'Executable'@ components might additionally add the+ -- program entry point (@main-is@ file) to the extra sources,+ -- if it should be compiled as the rest of them.+ -> ConfiguredProgram+ -- ^ The GHC configured program+ -> SymbolicPath Pkg (Dir Artifacts)+ -- ^ The build directory for this target+ -> (Bool -> [BuildWay], Bool -> BuildWay, BuildWay)+ -- ^ Needed build ways+ -> PreBuildComponentInputs+ -- ^ The context and component being built in it.+ -> IO (NubListR (SymbolicPath Pkg File))+ -- ^ Returns the list of extra sources that were built+buildExtraSources+ description+ componentSourceGhcOptions+ viewSources+ ghcProg+ buildTargetDir+ (neededLibWays, neededFLibWay, neededExeWay) =+ \PreBuildComponentInputs{buildingWhat, localBuildInfo = lbi, targetInfo} -> do+ let+ bi = componentBuildInfo (targetComponent targetInfo)+ verbosity = buildingWhatVerbosity buildingWhat+ clbi = targetCLBI targetInfo+ isIndef = componentIsIndefinite clbi+ mbWorkDir = mbWorkDirLBI lbi+ i = interpretSymbolicPath mbWorkDir+ sources = viewSources (targetComponent targetInfo)+ comp = compiler lbi+ platform = hostPlatform lbi+ tempFileOptions = commonSetupTempFileOptions $ buildingWhatCommonFlags buildingWhat+ runGhcProg =+ runGHCWithResponseFile+ "ghc.rsp"+ Nothing+ tempFileOptions+ verbosity+ ghcProg+ comp+ platform+ mbWorkDir++ buildAction :: SymbolicPath Pkg File -> IO ()+ buildAction sourceFile = do+ let baseSrcOpts =+ componentSourceGhcOptions+ verbosity+ lbi+ bi+ clbi+ buildTargetDir+ sourceFile+ vanillaSrcOpts =+ -- -fPIC is used in case you are using the repl+ -- of a dynamically linked GHC+ baseSrcOpts{ghcOptFPic = toFlag True}+ profSrcOpts =+ vanillaSrcOpts+ `mappend` mempty+ { ghcOptProfilingMode = toFlag True+ }+ sharedSrcOpts =+ vanillaSrcOpts+ `mappend` mempty+ { ghcOptFPic = toFlag True+ , ghcOptDynLinkMode = toFlag GhcDynamicOnly+ }+ profSharedSrcOpts =+ vanillaSrcOpts+ `mappend` mempty+ { ghcOptProfilingMode = toFlag True+ , ghcOptFPic = toFlag True+ , ghcOptDynLinkMode = toFlag GhcDynamicOnly+ }+ -- TODO: Placing all Haskell, C, & C++ objects in a single directory+ -- Has the potential for file collisions. In general we would+ -- consider this a user error. However, we should strive to+ -- add a warning if this occurs.+ odir = fromFlag (ghcOptObjDir vanillaSrcOpts)++ compileIfNeeded :: GhcOptions -> IO ()+ compileIfNeeded opts = do+ needsRecomp <- checkNeedsRecompilation mbWorkDir sourceFile opts+ when needsRecomp $ runGhcProg opts++ createDirectoryIfMissingVerbose verbosity True (i odir)+ case targetComponent targetInfo of+ -- For libraries, we compile extra objects in the four ways: vanilla, shared, profiled and profiled shared.+ -- We suffix shared objects with `.dyn_o`, profiled ones with `.p_o` and profiled shared ones with `.p_dyn_o`.+ CLib _lib+ -- Unless for repl, in which case we only need the vanilla way+ | BuildRepl _ <- buildingWhat ->+ compileIfNeeded vanillaSrcOpts+ | otherwise ->+ do+ forM_ (neededLibWays isIndef) $ \case+ StaticWay -> compileIfNeeded vanillaSrcOpts+ DynWay -> compileIfNeeded sharedSrcOpts{ghcOptObjSuffix = toFlag "dyn_o"}+ ProfWay -> compileIfNeeded profSrcOpts{ghcOptObjSuffix = toFlag "p_o"}+ ProfDynWay -> compileIfNeeded profSharedSrcOpts{ghcOptObjSuffix = toFlag "p_dyn_o"}+ CFLib flib ->+ case neededFLibWay (withDynFLib flib) of+ StaticWay -> compileIfNeeded vanillaSrcOpts+ DynWay -> compileIfNeeded sharedSrcOpts+ ProfWay -> compileIfNeeded profSrcOpts+ ProfDynWay -> compileIfNeeded profSharedSrcOpts+ -- For the remaining component types (Exec, Test, Bench), we also+ -- determine with which options to build the objects (vanilla vs shared vs+ -- profiled), but predicate is the same for the three kinds.+ _exeLike ->+ case neededExeWay of+ StaticWay -> compileIfNeeded vanillaSrcOpts+ DynWay -> compileIfNeeded sharedSrcOpts+ ProfWay -> compileIfNeeded profSrcOpts+ ProfDynWay -> compileIfNeeded profSharedSrcOpts++ -- build any sources+ if (null sources || componentIsIndefinite clbi)+ then return mempty+ else do+ info verbosity ("Building " ++ description ++ "...")+ traverse_ buildAction sources+ return (toNubListR sources)
+ src/Distribution/Simple/GHC/Build/Link.hs view
@@ -0,0 +1,812 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE RankNTypes #-}++module Distribution.Simple.GHC.Build.Link where++import Distribution.Compat.Prelude+import Prelude ()++import Control.Monad+import Control.Monad.IO.Class+import qualified Data.ByteString.Lazy.Char8 as BS+import qualified Data.Set as Set+import Distribution.Backpack+import Distribution.Compat.Binary (encode)+import Distribution.Compat.ResponseFile+import Distribution.InstalledPackageInfo (InstalledPackageInfo)+import qualified Distribution.InstalledPackageInfo as IPI+import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo+import qualified Distribution.ModuleName as ModuleName+import Distribution.Package+import Distribution.PackageDescription as PD+import Distribution.PackageDescription.Utils (cabalBug)+import Distribution.Pretty+import Distribution.Simple.Build.Inputs+import Distribution.Simple.BuildPaths+import Distribution.Simple.Compiler+import Distribution.Simple.Errors+import Distribution.Simple.GHC.Build.Modules+import Distribution.Simple.GHC.Build.Utils (exeTargetName, flibBuildName, flibTargetName, withDynFLib)+import Distribution.Simple.GHC.ImplInfo+import qualified Distribution.Simple.GHC.Internal as Internal+import Distribution.Simple.LocalBuildInfo+import qualified Distribution.Simple.PackageIndex as PackageIndex+import Distribution.Simple.PreProcess.Types+import Distribution.Simple.Program+import qualified Distribution.Simple.Program.Ar as Ar+import Distribution.Simple.Program.GHC+import qualified Distribution.Simple.Program.Ld as Ld+import Distribution.Simple.Setup.Common+import Distribution.Simple.Setup.Config+import Distribution.Simple.Setup.Repl+import Distribution.Simple.Utils+import Distribution.System+import Distribution.Types.ComponentLocalBuildInfo+import Distribution.Utils.NubList+import Distribution.Utils.Path+import Distribution.Verbosity+import Distribution.Version++import System.Directory+ ( createDirectoryIfMissing+ , doesDirectoryExist+ , doesFileExist+ , removeFile+ , renameFile+ )+import System.FilePath+ ( isRelative+ , replaceExtension+ )++-- | Links together the object files of the Haskell modules and extra sources+-- using the context in which the component is being built.+--+-- If the build kind is 'BuildRepl', we load the component into GHCi instead of linking.+linkOrLoadComponent+ :: ConfiguredProgram+ -- ^ The configured GHC program that will be used for linking+ -> PackageDescription+ -- ^ The package description containing the component being built+ -> [SymbolicPath Pkg File]+ -- ^ The full list of extra build sources (all C, C++, Js,+ -- Asm, and Cmm sources), which were compiled to object+ -- files.+ -> (SymbolicPath Pkg (Dir Artifacts), SymbolicPath Pkg (Dir Build))+ -- ^ The build target dir, and the target dir.+ -- See Note [Build Target Dir vs Target Dir] in Distribution.Simple.GHC.Build+ -> ((Bool -> [BuildWay], Bool -> BuildWay, BuildWay), BuildWay -> GhcOptions)+ -- ^ The set of build ways wanted based on the user opts, and a function to+ -- convert a build way into the set of ghc options that were used to build+ -- that way.+ -> PreBuildComponentInputs+ -- ^ The context and component being built in it.+ -> IO ()+linkOrLoadComponent+ ghcProg+ pkg_descr+ extraSources+ (buildTargetDir, targetDir)+ ((wantedLibWays, wantedFLibWay, wantedExeWay), buildOpts)+ pbci = do+ let+ verbosity = buildVerbosity pbci+ target = targetInfo pbci+ component = buildComponent pbci+ what = buildingWhat pbci+ lbi = localBuildInfo pbci+ bi = buildBI pbci+ clbi = buildCLBI pbci+ isIndef = componentIsIndefinite clbi+ mbWorkDir = mbWorkDirLBI lbi+ tempFileOptions = commonSetupTempFileOptions $ buildingWhatCommonFlags what++ -- See Note [Symbolic paths] in Distribution.Utils.Path+ i = interpretSymbolicPathLBI lbi++ -- ensure extra lib dirs exist before passing to ghc+ cleanedExtraLibDirs <- liftIO $ filterM (doesDirectoryExist . i) (extraLibDirs bi)+ cleanedExtraLibDirsStatic <- liftIO $ filterM (doesDirectoryExist . i) (extraLibDirsStatic bi)++ let+ extraSourcesObjs :: [RelativePath Artifacts File]+ extraSourcesObjs =+ [ makeRelativePathEx $ getSymbolicPath src `replaceExtension` objExtension+ | src <- extraSources+ ]++ -- TODO: Shouldn't we use withStaticLib for libraries and something else+ -- for foreign libs in the three cases where we use `withFullyStaticExe` below?+ linkerOpts rpaths =+ mempty+ { ghcOptLinkOptions =+ PD.ldOptions bi+ ++ [ "-static"+ | withFullyStaticExe lbi+ ]+ -- Pass extra `ld-options` given+ -- through to GHC's linker.+ ++ maybe+ []+ programOverrideArgs+ (lookupProgram ldProgram (withPrograms lbi))+ , ghcOptLinkLibs =+ if withFullyStaticExe lbi+ then extraLibsStatic bi+ else extraLibs bi+ , ghcOptLinkLibPath =+ toNubListR $+ if withFullyStaticExe lbi+ then cleanedExtraLibDirsStatic+ else cleanedExtraLibDirs+ , ghcOptLinkFrameworks = toNubListR $ map getSymbolicPath $ PD.frameworks bi+ , ghcOptLinkFrameworkDirs = toNubListR $ PD.extraFrameworkDirs bi+ , ghcOptInputFiles =+ toNubListR+ [ coerceSymbolicPath $ buildTargetDir </> obj+ | obj <- extraSourcesObjs+ ]+ , ghcOptNoLink = Flag False+ , ghcOptRPaths = rpaths+ }++ case what of+ BuildRepl replFlags -> liftIO $ do+ let+ -- For repl we use the vanilla (static) ghc options+ staticOpts = buildOpts StaticWay+ replOpts =+ staticOpts+ { -- Repl options use Static as the base, but doesn't need to pass -static.+ -- However, it maybe should, for uniformity.+ ghcOptDynLinkMode = NoFlag+ , ghcOptExtra =+ Internal.filterGhciFlags+ (ghcOptExtra staticOpts)+ <> replOptionsFlags (replReplOptions replFlags)+ }+ -- For a normal compile we do separate invocations of ghc for+ -- compiling as for linking. But for repl we have to do just+ -- the one invocation, so that one has to include all the+ -- linker stuff too, like -l flags and any .o files from C+ -- files etc.+ --+ -- TODO: The repl doesn't use the runtime paths from linkerOpts+ -- (ghcOptRPaths), which looks like a bug. After the refactor we+ -- can fix this.+ `mappend` linkerOpts mempty+ `mappend` mempty+ { ghcOptMode = toFlag GhcModeInteractive+ , ghcOptOptimisation = toFlag GhcNoOptimisation+ }+ replOpts_final =+ replOpts+ { ghcOptInputModules = replNoLoad (replReplOptions replFlags) (ghcOptInputModules replOpts)+ , ghcOptInputFiles = replNoLoad (replReplOptions replFlags) (ghcOptInputFiles replOpts)+ }++ -- TODO: problem here is we need the .c files built first, so we can load them+ -- with ghci, but .c files can depend on .h files generated by ghc by ffi+ -- exports.+ when (case component of CLib lib -> null (allLibModules lib clbi); _ -> False) $+ warn verbosity "No exposed modules"+ runReplOrWriteFlags+ ghcProg+ lbi+ replFlags+ replOpts_final+ (pkgName (PD.package pkg_descr))+ target+ _otherwise ->+ let+ runGhcProg =+ runGHCWithResponseFile+ "ghc.rsp"+ Nothing+ tempFileOptions+ verbosity+ ghcProg+ comp+ platform+ mbWorkDir+ platform = hostPlatform lbi+ comp = compiler lbi+ get_rpaths ways =+ if DynWay `Set.member` ways then getRPaths pbci else return (toNubListR [])+ in+ when (not $ componentIsIndefinite clbi) $ do+ -- If not building dynamically, we don't pass any runtime paths.+ liftIO $ do+ info verbosity "Linking..."+ let linkExeLike name = do+ rpaths <- get_rpaths (Set.singleton wantedExeWay)+ linkExecutable (linkerOpts rpaths) (wantedExeWay, buildOpts) targetDir name runGhcProg lbi+ case component of+ CLib lib -> do+ let libWays = wantedLibWays isIndef+ rpaths <- get_rpaths (Set.fromList libWays)+ linkLibrary buildTargetDir cleanedExtraLibDirs pkg_descr verbosity runGhcProg lib lbi clbi extraSources rpaths libWays+ CFLib flib -> do+ let flib_way = wantedFLibWay (withDynFLib flib)+ rpaths <- get_rpaths (Set.singleton flib_way)+ linkFLib flib bi lbi (linkerOpts rpaths) (flib_way, buildOpts) targetDir runGhcProg+ CExe exe -> linkExeLike (exeName exe)+ CTest test -> linkExeLike (testName test)+ CBench bench -> linkExeLike (benchmarkName bench)++-- | Link a library component+linkLibrary+ :: SymbolicPath Pkg (Dir Artifacts)+ -- ^ The library target build directory+ -> [SymbolicPath Pkg (Dir Lib)]+ -- ^ The list of extra lib dirs that exist (aka "cleaned")+ -> PackageDescription+ -- ^ The package description containing this library+ -> Verbosity+ -> (GhcOptions -> IO ())+ -- ^ Run the configured Ghc program+ -> Library+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> [SymbolicPath Pkg File]+ -- ^ Extra build sources (that were compiled to objects)+ -> NubListR FilePath+ -- ^ A list with the runtime-paths (rpaths), or empty if not linking dynamically+ -> [BuildWay]+ -- ^ Wanted build ways and corresponding build options+ -> IO ()+linkLibrary buildTargetDir cleanedExtraLibDirs pkg_descr verbosity runGhcProg lib lbi clbi extraSources rpaths wantedWays = do+ let+ common = configCommonFlags $ configFlags lbi+ mbWorkDir = flagToMaybe $ setupWorkingDir common++ compiler_id = compilerId comp+ comp = compiler lbi+ ghcVersion = compilerVersion comp+ implInfo = getImplInfo comp+ uid = componentUnitId clbi+ libBi = libBuildInfo lib+ Platform _hostArch hostOS = hostPlatform lbi+ vanillaLibFilePath = buildTargetDir </> makeRelativePathEx (mkLibName uid)+ profileLibFilePath = buildTargetDir </> makeRelativePathEx (mkProfLibName uid)+ sharedLibFilePath =+ buildTargetDir+ </> makeRelativePathEx (mkSharedLibName (hostPlatform lbi) compiler_id uid)+ profSharedLibFilePath =+ buildTargetDir+ </> makeRelativePathEx (mkProfSharedLibName (hostPlatform lbi) compiler_id uid)+ staticLibFilePath =+ buildTargetDir+ </> makeRelativePathEx (mkStaticLibName (hostPlatform lbi) compiler_id uid)+ ghciLibFilePath = buildTargetDir </> makeRelativePathEx (Internal.mkGHCiLibName uid)+ ghciProfLibFilePath = buildTargetDir </> makeRelativePathEx (Internal.mkGHCiProfLibName uid)+ libInstallPath =+ libdir $+ absoluteComponentInstallDirs+ pkg_descr+ lbi+ uid+ NoCopyDest+ sharedLibInstallPath =+ libInstallPath+ </> mkSharedLibName (hostPlatform lbi) compiler_id uid+ profSharedLibInstallPath =+ libInstallPath+ </> mkProfSharedLibName (hostPlatform lbi) compiler_id uid++ getObjFiles :: BuildWay -> IO [SymbolicPath Pkg File]+ getObjFiles way =+ mconcat+ [ Internal.getHaskellObjects+ implInfo+ lib+ lbi+ clbi+ buildTargetDir+ (buildWayPrefix way ++ objExtension)+ True+ , pure $ map (srcObjPath way) extraSources+ , catMaybes+ <$> sequenceA+ [ findFileCwdWithExtension+ mbWorkDir+ [Suffix $ buildWayPrefix way ++ objExtension]+ [buildTargetDir]+ xPath+ | ghcVersion < mkVersion [7, 2] -- ghc-7.2+ does not make _stub.o files+ , x <- allLibModules lib clbi+ , let xPath :: RelativePath Artifacts File+ xPath = makeRelativePathEx $ ModuleName.toFilePath x ++ "_stub"+ ]+ ]++ -- Get the @.o@ path from a source path (e.g. @.hs@),+ -- in the library target build directory.+ srcObjPath :: BuildWay -> SymbolicPath Pkg File -> SymbolicPath Pkg File+ srcObjPath way srcPath =+ case symbolicPathRelative_maybe objPath of+ -- Absolute path: should already be in the target build directory+ -- (e.g. a preprocessed file)+ -- TODO: assert this?+ Nothing -> objPath+ Just objRelPath -> coerceSymbolicPath buildTargetDir </> objRelPath+ where+ objPath = srcPath `replaceExtensionSymbolicPath` (buildWayPrefix way ++ objExtension)++ -- I'm fairly certain that, just like the executable, we can keep just the+ -- module input list, and point to the right sources dir (as is already+ -- done), and GHC will pick up the right suffix (p_ for profile, dyn_ when+ -- -shared...). The downside to doing this is that GHC would have to+ -- reconstruct the module graph again.+ -- That would mean linking the lib would be just like the executable, and+ -- we could more easily merge the two.+ --+ -- Right now, instead, we pass the path to each object file.+ ghcBaseLinkArgs =+ mempty+ { -- TODO: This basically duplicates componentGhcOptions.+ -- I think we want to do the same as we do for executables: re-use the+ -- base options, and link by module names, not object paths.+ ghcOptExtra = hcStaticOptions GHC libBi+ , ghcOptHideAllPackages = toFlag True+ , ghcOptNoAutoLinkPackages = toFlag True+ , ghcOptPackageDBs = withPackageDB lbi+ , ghcOptThisUnitId = case clbi of+ LibComponentLocalBuildInfo{componentCompatPackageKey = pk} ->+ toFlag pk+ _ -> mempty+ , ghcOptThisComponentId = case clbi of+ LibComponentLocalBuildInfo+ { componentInstantiatedWith = insts+ } ->+ if null insts+ then mempty+ else toFlag (componentComponentId clbi)+ _ -> mempty+ , ghcOptInstantiatedWith = case clbi of+ LibComponentLocalBuildInfo+ { componentInstantiatedWith = insts+ } ->+ insts+ _ -> []+ , ghcOptPackages =+ toNubListR $+ Internal.mkGhcOptPackages mempty clbi+ }++ -- After the relocation lib is created we invoke ghc -shared+ -- with the dependencies spelled out as -package arguments+ -- and ghc invokes the linker with the proper library paths+ ghcSharedLinkArgs :: [SymbolicPath Pkg File] -> GhcOptions+ ghcSharedLinkArgs dynObjectFiles =+ ghcBaseLinkArgs+ { ghcOptShared = toFlag True+ , ghcOptDynLinkMode = toFlag GhcDynamicOnly+ , ghcOptInputFiles = toNubListR $ map coerceSymbolicPath dynObjectFiles+ , ghcOptOutputFile = toFlag sharedLibFilePath+ , -- For dynamic libs, Mac OS/X needs to know the install location+ -- at build time. This only applies to GHC < 7.8 - see the+ -- discussion in #1660.+ ghcOptDylibName =+ if hostOS == OSX+ && ghcVersion < mkVersion [7, 8]+ then toFlag sharedLibInstallPath+ else mempty+ , ghcOptLinkLibs = extraLibs libBi+ , ghcOptLinkLibPath = toNubListR $ cleanedExtraLibDirs+ , ghcOptLinkFrameworks = toNubListR $ map getSymbolicPath $ PD.frameworks libBi+ , ghcOptLinkFrameworkDirs =+ toNubListR $ PD.extraFrameworkDirs libBi+ , ghcOptRPaths = rpaths+ }+ ghcProfSharedLinkArgs pdynObjectFiles =+ ghcBaseLinkArgs+ { ghcOptShared = toFlag True+ , ghcOptProfilingMode = toFlag True+ , ghcOptProfilingAuto =+ Internal.profDetailLevelFlag+ True+ (withProfLibDetail lbi)+ , ghcOptDynLinkMode = toFlag GhcDynamicOnly+ , ghcOptInputFiles = toNubListR pdynObjectFiles+ , ghcOptOutputFile = toFlag profSharedLibFilePath+ , -- For dynamic libs, Mac OS/X needs to know the install location+ -- at build time. This only applies to GHC < 7.8 - see the+ -- discussion in #1660.+ ghcOptDylibName =+ if hostOS == OSX+ && ghcVersion < mkVersion [7, 8]+ then toFlag profSharedLibInstallPath+ else mempty+ , ghcOptLinkLibs = extraLibs libBi+ , ghcOptLinkLibPath = toNubListR $ cleanedExtraLibDirs+ , ghcOptLinkFrameworks = toNubListR $ map getSymbolicPath $ PD.frameworks libBi+ , ghcOptLinkFrameworkDirs =+ toNubListR $ PD.extraFrameworkDirs libBi+ , ghcOptRPaths = rpaths+ }+ ghcStaticLinkArgs staticObjectFiles =+ ghcBaseLinkArgs+ { ghcOptStaticLib = toFlag True+ , ghcOptInputFiles = toNubListR $ map coerceSymbolicPath staticObjectFiles+ , ghcOptOutputFile = toFlag staticLibFilePath+ , ghcOptLinkLibs = extraLibs libBi+ , -- TODO: Shouldn't this use cleanedExtraLibDirsStatic instead?+ ghcOptLinkLibPath = toNubListR $ cleanedExtraLibDirs+ }++ staticObjectFiles <- getObjFiles StaticWay+ profObjectFiles <- getObjFiles ProfWay+ dynamicObjectFiles <- getObjFiles DynWay+ profDynamicObjectFiles <- getObjFiles ProfDynWay++ let+ linkWay = \case+ ProfWay -> do+ Ar.createArLibArchive verbosity lbi profileLibFilePath profObjectFiles+ when (withGHCiLib lbi) $ do+ (ldProg, _) <- requireProgram verbosity ldProgram (withPrograms lbi)+ Ld.combineObjectFiles+ verbosity+ lbi+ ldProg+ ghciProfLibFilePath+ profObjectFiles+ ProfDynWay -> do+ runGhcProg $ ghcProfSharedLinkArgs profDynamicObjectFiles+ DynWay -> do+ runGhcProg $ ghcSharedLinkArgs dynamicObjectFiles+ StaticWay -> do+ when (withVanillaLib lbi) $ do+ Ar.createArLibArchive verbosity lbi vanillaLibFilePath staticObjectFiles+ when (withGHCiLib lbi) $ do+ (ldProg, _) <- requireProgram verbosity ldProgram (withPrograms lbi)+ Ld.combineObjectFiles+ verbosity+ lbi+ ldProg+ ghciLibFilePath+ staticObjectFiles+ when (withStaticLib lbi) $ do+ runGhcProg $ ghcStaticLinkArgs staticObjectFiles++ -- ROMES: Why exactly branch on staticObjectFiles, rather than any other build+ -- kind that we might have wanted instead?+ -- This would be simpler by not adding every object to the invocation, and+ -- rather using module names.+ unless (null staticObjectFiles) $ do+ info verbosity (show (ghcOptPackages (Internal.componentGhcOptions verbosity lbi libBi clbi buildTargetDir)))+ traverse_ linkWay wantedWays++-- | Link the executable resulting from building this component, be it an+-- executable, test, or benchmark component.+linkExecutable+ :: (GhcOptions)+ -- ^ The linker-specific GHC options+ -> (BuildWay, BuildWay -> GhcOptions)+ -- ^ The wanted build ways and corresponding GhcOptions that were+ -- used to compile the modules in that way.+ -> SymbolicPath Pkg (Dir Build)+ -- ^ The target dir (2024-01:note: not the same as build target+ -- dir, see Note [Build Target Dir vs Target Dir] in Distribution.Simple.GHC.Build)+ -> UnqualComponentName+ -- ^ Name of executable-like target+ -> (GhcOptions -> IO ())+ -- ^ Run the configured GHC program+ -> LocalBuildInfo+ -> IO ()+linkExecutable linkerOpts (way, buildOpts) targetDir targetName runGhcProg lbi = do+ let baseOpts = buildOpts way+ linkOpts =+ baseOpts+ `mappend` linkerOpts+ `mappend` mempty+ { -- If there are no input Haskell files we pass -no-hs-main, and+ -- assume there is a main function in another non-haskell object+ ghcOptLinkNoHsMain = toFlag (ghcOptInputFiles baseOpts == mempty && ghcOptInputScripts baseOpts == mempty)+ }+ comp = compiler lbi++ -- Work around old GHCs not relinking in this+ -- situation, see #3294+ let target =+ targetDir </> makeRelativePathEx (exeTargetName (hostPlatform lbi) targetName)+ when (compilerVersion comp < mkVersion [7, 7]) $ do+ let targetPath = interpretSymbolicPathLBI lbi target+ e <- doesFileExist targetPath+ when e (removeFile targetPath)+ runGhcProg linkOpts{ghcOptOutputFile = toFlag target}++-- | Link a foreign library component+linkFLib+ :: ForeignLib+ -> BuildInfo+ -> LocalBuildInfo+ -> (GhcOptions)+ -- ^ The linker-specific GHC options+ -> (BuildWay, BuildWay -> GhcOptions)+ -- ^ The wanted build ways and corresponding GhcOptions that were+ -- used to compile the modules in that way.+ -> SymbolicPath Pkg (Dir Build)+ -- ^ The target dir (2024-01:note: not the same as build target+ -- dir, see Note [Build Target Dir vs Target Dir] in Distribution.Simple.GHC.Build)+ -> (GhcOptions -> IO ())+ -- ^ Run the configured GHC program+ -> IO ()+linkFLib flib bi lbi linkerOpts (way, buildOpts) targetDir runGhcProg = do+ let+ comp = compiler lbi++ -- Instruct GHC to link against libHSrts.+ rtsLinkOpts :: GhcOptions+ rtsLinkOpts+ | supportsFLinkRts =+ mempty+ { ghcOptLinkRts = toFlag True+ }+ | otherwise =+ mempty+ { ghcOptLinkLibs = rtsOptLinkLibs+ , ghcOptLinkLibPath = toNubListR $ map makeSymbolicPath $ rtsLibPaths rtsInfo+ }+ where+ threaded = hasThreaded bi+ supportsFLinkRts = compilerVersion comp >= mkVersion [9, 0]+ rtsInfo = extractRtsInfo lbi+ rtsOptLinkLibs =+ [ if withDynFLib flib+ then+ if threaded+ then dynRtsThreadedLib (rtsDynamicInfo rtsInfo)+ else dynRtsVanillaLib (rtsDynamicInfo rtsInfo)+ else+ if threaded+ then statRtsThreadedLib (rtsStaticInfo rtsInfo)+ else statRtsVanillaLib (rtsStaticInfo rtsInfo)+ ]++ linkOpts :: GhcOptions+ linkOpts = case foreignLibType flib of+ ForeignLibNativeShared ->+ (buildOpts way)+ `mappend` linkerOpts+ `mappend` rtsLinkOpts+ `mappend` mempty+ { ghcOptLinkNoHsMain = toFlag True+ , ghcOptShared = toFlag True+ , ghcOptFPic = toFlag True+ , ghcOptLinkModDefFiles = toNubListR $ fmap getSymbolicPath $ foreignLibModDefFile flib+ }+ ForeignLibNativeStatic ->+ -- this should be caught by buildFLib+ -- (and if we do implement this, we probably don't even want to call+ -- ghc here, but rather Ar.createArLibArchive or something)+ cabalBug "static libraries not yet implemented"+ ForeignLibTypeUnknown ->+ cabalBug "unknown foreign lib type"+ -- We build under a (potentially) different filename to set a+ -- soname on supported platforms. See also the note for+ -- @flibBuildName@.+ let buildName = flibBuildName lbi flib+ let outFile = targetDir </> makeRelativePathEx buildName+ runGhcProg linkOpts{ghcOptOutputFile = toFlag outFile}+ let i = interpretSymbolicPathLBI lbi+ renameFile (i outFile) (i targetDir </> flibTargetName lbi flib)++-- | Calculate the RPATHs for the component we are building.+--+-- Calculates relative RPATHs when 'relocatable' is set.+getRPaths+ :: PreBuildComponentInputs+ -- ^ The context and component being built in it.+ -> IO (NubListR FilePath)+getRPaths pbci = do+ let+ lbi = localBuildInfo pbci+ bi = buildBI pbci+ clbi = buildCLBI pbci++ (Platform _ hostOS) = hostPlatform lbi+ compid = compilerId . compiler $ lbi++ -- The list of RPath-supported operating systems below reflects the+ -- platforms on which Cabal's RPATH handling is tested. It does _NOT_+ -- reflect whether the OS supports RPATH.++ -- E.g. when this comment was written, the *BSD operating systems were+ -- untested with regards to Cabal RPATH handling, and were hence set to+ -- 'False', while those operating systems themselves do support RPATH.+ supportRPaths Linux = True+ supportRPaths Windows = False+ supportRPaths OSX = True+ supportRPaths FreeBSD =+ case compid of+ CompilerId GHC ver | ver >= mkVersion [7, 10, 2] -> True+ _ -> False+ supportRPaths OpenBSD = False+ supportRPaths NetBSD = False+ supportRPaths DragonFly = False+ supportRPaths Solaris = False+ supportRPaths AIX = False+ supportRPaths HPUX = False+ supportRPaths IRIX = False+ supportRPaths HaLVM = False+ supportRPaths IOS = False+ supportRPaths Android = False+ supportRPaths Ghcjs = False+ supportRPaths Wasi = False+ supportRPaths Hurd = True+ supportRPaths Haiku = False+ supportRPaths (OtherOS _) = False+ -- Do _not_ add a default case so that we get a warning here when a new OS+ -- is added.++ if supportRPaths hostOS+ then do+ libraryPaths <- liftIO $ depLibraryPaths False (relocatable lbi) lbi clbi+ let hostPref = case hostOS of+ OSX -> "@loader_path"+ _ -> "$ORIGIN"+ relPath p = if isRelative p then hostPref </> p else p+ rpaths =+ toNubListR (map relPath libraryPaths)+ <> toNubListR (map getSymbolicPath $ extraLibDirs bi)+ return rpaths+ else return mempty++data DynamicRtsInfo = DynamicRtsInfo+ { dynRtsVanillaLib :: FilePath+ , dynRtsThreadedLib :: FilePath+ , dynRtsDebugLib :: FilePath+ , dynRtsEventlogLib :: FilePath+ , dynRtsThreadedDebugLib :: FilePath+ , dynRtsThreadedEventlogLib :: FilePath+ }++data StaticRtsInfo = StaticRtsInfo+ { statRtsVanillaLib :: FilePath+ , statRtsThreadedLib :: FilePath+ , statRtsDebugLib :: FilePath+ , statRtsEventlogLib :: FilePath+ , statRtsThreadedDebugLib :: FilePath+ , statRtsThreadedEventlogLib :: FilePath+ , statRtsProfilingLib :: FilePath+ , statRtsThreadedProfilingLib :: FilePath+ }++data RtsInfo = RtsInfo+ { rtsDynamicInfo :: DynamicRtsInfo+ , rtsStaticInfo :: StaticRtsInfo+ , rtsLibPaths :: [FilePath]+ }++-- | Extract (and compute) information about the RTS library+--+-- TODO: This hardcodes the name as @HSrts-ghc<version>@. I don't know if we can+-- find this information somewhere. We can lookup the 'hsLibraries' field of+-- 'InstalledPackageInfo' but it will tell us @["HSrts", "Cffi"]@, which+-- doesn't really help.+extractRtsInfo :: LocalBuildInfo -> RtsInfo+extractRtsInfo lbi =+ case PackageIndex.lookupPackageName+ (installedPkgs lbi)+ (mkPackageName "rts") of+ [(_, [rts])] -> aux rts+ _otherwise -> error "No (or multiple) ghc rts package is registered"+ where+ aux :: InstalledPackageInfo -> RtsInfo+ aux rts =+ RtsInfo+ { rtsDynamicInfo =+ DynamicRtsInfo+ { dynRtsVanillaLib = withGhcVersion "HSrts"+ , dynRtsThreadedLib = withGhcVersion "HSrts_thr"+ , dynRtsDebugLib = withGhcVersion "HSrts_debug"+ , dynRtsEventlogLib = withGhcVersion "HSrts_l"+ , dynRtsThreadedDebugLib = withGhcVersion "HSrts_thr_debug"+ , dynRtsThreadedEventlogLib = withGhcVersion "HSrts_thr_l"+ }+ , rtsStaticInfo =+ StaticRtsInfo+ { statRtsVanillaLib = "HSrts"+ , statRtsThreadedLib = "HSrts_thr"+ , statRtsDebugLib = "HSrts_debug"+ , statRtsEventlogLib = "HSrts_l"+ , statRtsThreadedDebugLib = "HSrts_thr_debug"+ , statRtsThreadedEventlogLib = "HSrts_thr_l"+ , statRtsProfilingLib = "HSrts_p"+ , statRtsThreadedProfilingLib = "HSrts_thr_p"+ }+ , rtsLibPaths = InstalledPackageInfo.libraryDirs rts+ }+ withGhcVersion = (++ ("-ghc" ++ prettyShow (compilerVersion (compiler lbi))))++-- | Determine whether the given 'BuildInfo' is intended to link against the+-- threaded RTS. This is used to determine which RTS to link against when+-- building a foreign library with a GHC without support for @-flink-rts@.+hasThreaded :: BuildInfo -> Bool+hasThreaded bi = elem "-threaded" ghc+ where+ PerCompilerFlavor ghc _ = options bi++-- | Load a target component into a repl, or write to disk a script which runs+-- GHCi with the GHC options Cabal elaborated to load the component interactively.+runReplOrWriteFlags+ :: ConfiguredProgram+ -> LocalBuildInfo+ -> ReplFlags+ -> GhcOptions+ -> PackageName+ -> TargetInfo+ -> IO ()+runReplOrWriteFlags ghcProg lbi rflags ghcOpts pkg_name target =+ let bi = componentBuildInfo $ targetComponent target+ clbi = targetCLBI target+ cname = componentName (targetComponent target)+ comp = compiler lbi+ platform = hostPlatform lbi+ common = configCommonFlags $ configFlags lbi+ mbWorkDir = mbWorkDirLBI lbi+ verbosity = fromFlag $ setupVerbosity common+ tempFileOptions = commonSetupTempFileOptions common+ in case replOptionsFlagOutput (replReplOptions rflags) of+ NoFlag -> do+ -- If a specific GHC implementation is specified, use it+ runReplProgram+ (flagToMaybe $ replWithRepl (replReplOptions rflags))+ tempFileOptions+ verbosity+ ghcProg+ comp+ platform+ mbWorkDir+ ghcOpts+ Flag out_dir -> do+ let uid = componentUnitId clbi+ this_unit = prettyShow uid+ getOpenModName (OpenModule _ mn) = Just mn+ getOpenModName (OpenModuleVar{}) = Nothing+ reexported_modules =+ [ (from_mn, to_mn) | LibComponentLocalBuildInfo{componentExposedModules = exposed_mods} <- [clbi], IPI.ExposedModule to_mn (Just m) <- exposed_mods, Just from_mn <- [getOpenModName m]+ ]+ renderReexportedModule (from_mn, to_mn)+ | reexportedAsSupported comp =+ pure $ prettyShow from_mn ++ " as " ++ prettyShow to_mn+ | otherwise =+ if from_mn == to_mn+ then pure $ prettyShow to_mn+ else dieWithException verbosity (MultiReplDoesNotSupportComplexReexportedModules pkg_name cname)+ hidden_modules = otherModules bi+ render_extra_opts = do+ rexp_mods <- mapM renderReexportedModule reexported_modules+ pure $+ concat $+ [ ["-this-package-name", prettyShow pkg_name]+ , case mbWorkDir of+ Nothing -> []+ Just wd -> ["-working-dir", getSymbolicPath wd]+ ]+ ++ [ ["-reexported-module", m] | m <- rexp_mods+ ]+ ++ [ ["-hidden-module", prettyShow m] | m <- hidden_modules+ ]+ -- Create "paths" subdirectory if it doesn't exist. This is where we write+ -- information about how the PATH was augmented.+ createDirectoryIfMissing False (out_dir </> "paths")+ -- Write out the PATH information into `paths` subdirectory.+ writeFileAtomic (out_dir </> "paths" </> this_unit) (encode ghcProg)+ -- Write out options for this component into a file ready for loading into+ -- the multi-repl+ extra_opts <- render_extra_opts+ writeFileAtomic (out_dir </> this_unit) $+ BS.pack $+ escapeArgs $+ extra_opts+ ++ renderGhcOptions comp platform (ghcOpts{ghcOptMode = NoFlag})+ ++ programOverrideArgs ghcProg++replNoLoad :: Ord a => ReplOptions -> NubListR a -> NubListR a+replNoLoad replFlags l+ | replOptionsNoLoad replFlags == Flag True = mempty+ | otherwise = l
+ src/Distribution/Simple/GHC/Build/Modules.hs view
@@ -0,0 +1,418 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RankNTypes #-}++module Distribution.Simple.GHC.Build.Modules+ ( buildHaskellModules+ , BuildWay (..)+ , buildWayPrefix+ , componentInputs+ ) where++import Control.Monad.IO.Class+import Distribution.Compat.Prelude++import Data.List (sortOn, (\\))+import qualified Data.Set as Set+import Distribution.CabalSpecVersion+import Distribution.ModuleName (ModuleName)+import qualified Distribution.PackageDescription as PD+import Distribution.Pretty+import Distribution.Simple.Build.Inputs+import Distribution.Simple.BuildWay+import Distribution.Simple.Compiler+import Distribution.Simple.GHC.Build.Utils+import qualified Distribution.Simple.GHC.Internal as Internal+import qualified Distribution.Simple.Hpc as Hpc+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.Program.GHC+import Distribution.Simple.Program.Types+import Distribution.Simple.Setup.Common+import Distribution.Simple.Utils+import Distribution.Types.Benchmark+import Distribution.Types.BenchmarkInterface+import Distribution.Types.BuildInfo+import Distribution.Types.Executable+import Distribution.Types.ForeignLib+import Distribution.Types.PackageName.Magic+import Distribution.Types.ParStrat+import Distribution.Types.TestSuite+import Distribution.Types.TestSuiteInterface+import Distribution.Utils.NubList+import Distribution.Utils.Path+import System.FilePath ()++{-+Note [Building Haskell Modules accounting for TH]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~++There are multiple ways in which we may want to build our Haskell modules:+ * The static way (-static)+ * The dynamic/shared way (-dynamic)+ * The profiled way (-prof)++For libraries, we may /want/ to build modules in all three ways, or in any combination, depending on user options.+For executables, we just /want/ to build the executable in the requested way.++In practice, however, we may /need/ to build modules in additional ways beyonds the ones that were requested.+This can happen because of Template Haskell.++When we're using Template Haskell, we /need/ to additionally build modules with+the used GHC's default/vanilla ABI. This is because the code that TH needs to+run at compile time needs to be the vanilla ABI so it can be loaded up and run+by the compiler. With dynamic-by-default GHC the TH object files loaded at+compile-time need to be .dyn_o instead of .o.++ * If the GHC is dynamic by default, that means we may need to also build+ the dynamic way in addition the wanted way.++ * If the GHC is static by default, we may need to build statically additionally.++Of course, if the /wanted/ way is the way additionally /needed/ for TH, we don't need to do extra work.++If it turns out that in the end we need to build both statically and+dynamically, we want to make use of GHC's -static -dynamic-too capability, which+builds modules in the two ways in a single invocation.++If --dynamic-too is not supported by the GHC, then we need to be careful about+the order in which modules are built. Specifically, we must first build the+modules for TH with the vanilla ABI, and only afterwards the desired+(non-default) ways.++A few examples:++To build an executable with profiling, with a dynamic by default GHC, and TH is used:+ * Build dynamic (needed) objects+ * Build profiled objects++To build a library with profiling and dynamically, with a static by default GHC, and TH is used:+ * Build dynamic (wanted) and static (needed) objects together with --dynamic-too+ * Build profiled objects++To build an executable statically, with a static by default GHC, regardless of whether TH is used:+ * Simply build static objects++-}++-- | Compile the Haskell modules of the component being built.+buildHaskellModules+ :: Flag ParStrat+ -- ^ The parallelism strategy (e.g. num of jobs)+ -> ConfiguredProgram+ -- ^ The GHC configured program+ -> Maybe (SymbolicPath Pkg File)+ -- ^ Optional path to a Haskell Main file to build+ -> [ModuleName]+ -- ^ The Haskell modules to build+ -> SymbolicPath Pkg ('Dir Artifacts)+ -- ^ The path to the build directory for this target, which+ -- has already been created.+ -> [BuildWay]+ -- ^ The set of needed build ways according to user options+ -> PreBuildComponentInputs+ -- ^ The context and component being built in it.+ -> IO (BuildWay -> GhcOptions)+ -- ^ Returns a mapping from build ways to the 'GhcOptions' used in the+ -- invocation used to compile the component in that 'BuildWay'.+ -- This can be useful in, eg, a linker invocation, in which we want to use the+ -- same options and list the same inputs as those used for building.+buildHaskellModules numJobs ghcProg mbMainFile inputModules buildTargetDir neededLibWays pbci = do+ -- See Note [Building Haskell Modules accounting for TH]++ let+ verbosity = buildVerbosity pbci+ isLib = buildIsLib pbci+ clbi = buildCLBI pbci+ lbi = localBuildInfo pbci+ bi = buildBI pbci+ what = buildingWhat pbci+ comp = buildCompiler pbci+ i = interpretSymbolicPathLBI lbi -- See Note [Symbolic paths] in Distribution.Utils.Path++ -- If this component will be loaded into a repl, we don't compile the modules at all.+ forRepl+ | BuildRepl{} <- what = True+ | otherwise = False++ -- TODO: do we need to put hs-boot files into place for mutually recursive+ -- modules? FIX: what about exeName.hi-boot?++ -- Determine if program coverage should be enabled and if so, what+ -- '-hpcdir' should be.+ isCoverageEnabled = if isLib then libCoverage lbi else exeCoverage lbi+ hpcdir way+ | forRepl = mempty -- HPC is not supported in ghci+ | isCoverageEnabled = Flag $ Hpc.mixDir (coerceSymbolicPath $ coerceSymbolicPath buildTargetDir </> extraCompilationArtifacts) way+ | otherwise = mempty++ mbWorkDir = mbWorkDirLBI lbi+ tempFileOptions = commonSetupTempFileOptions $ buildingWhatCommonFlags what+ runGhcProg =+ runGHCWithResponseFile+ "ghc.rsp"+ Nothing+ tempFileOptions+ verbosity+ ghcProg+ comp+ platform+ mbWorkDir+ platform = hostPlatform lbi++ (hsMains, scriptMains) =+ partition (isHaskell . getSymbolicPath) (maybeToList mbMainFile)++ -- We define the base opts which are shared across different build ways in+ -- 'buildHaskellModules'+ baseOpts way =+ (Internal.componentGhcOptions verbosity lbi bi clbi buildTargetDir)+ `mappend` mempty+ { ghcOptMode = toFlag GhcModeMake+ , -- Previously we didn't pass -no-link when building libs,+ -- but I think that could result in a bug (e.g. if a lib module is+ -- called Main and exports main). So we really want nolink when+ -- building libs too (TODO).+ ghcOptNoLink = if isLib then NoFlag else toFlag True+ , ghcOptNumJobs = numJobs+ , ghcOptInputModules = toNubListR inputModules+ , ghcOptInputFiles = toNubListR hsMains+ , ghcOptInputScripts = toNubListR scriptMains+ , ghcOptExtra = buildWayExtraHcOptions way GHC bi+ , ghcOptHiSuffix = optSuffixFlag (buildWayPrefix way) "hi"+ , ghcOptObjSuffix = optSuffixFlag (buildWayPrefix way) "o"+ , ghcOptHPCDir = hpcdir (buildWayHpcWay way) -- maybe this should not be passed for vanilla?+ }+ where+ optSuffixFlag "" _ = NoFlag+ optSuffixFlag pre x = toFlag (pre ++ x)++ -- For libs we don't pass -static when building static, leaving it+ -- implicit. We should just always pass -static, but we don't want to+ -- change behaviour when doing the refactor.+ staticOpts = (baseOpts StaticWay){ghcOptDynLinkMode = if isLib then NoFlag else toFlag GhcStaticOnly}+ dynOpts =+ (baseOpts DynWay)+ { ghcOptDynLinkMode = toFlag GhcDynamicOnly -- use -dynamic+ , -- TODO: Does it hurt to set -fPIC for executables?+ ghcOptFPic = toFlag True -- use -fPIC+ }+ profOpts =+ (baseOpts ProfWay)+ { ghcOptProfilingMode = toFlag True+ , ghcOptProfilingAuto =+ Internal.profDetailLevelFlag+ (if isLib then True else False)+ ((if isLib then withProfLibDetail else withProfExeDetail) lbi)+ }+ profDynOpts =+ (baseOpts ProfDynWay)+ { ghcOptDynLinkMode = toFlag GhcDynamicOnly -- use -dynamic+ , -- TODO: Does it hurt to set -fPIC for executables?+ ghcOptFPic = toFlag True -- use -fPIC+ , ghcOptProfilingMode = toFlag True+ , ghcOptProfilingAuto =+ Internal.profDetailLevelFlag+ (if isLib then True else False)+ ((if isLib then withProfLibDetail else withProfExeDetail) lbi)+ }++ -- Options for building both static and dynamic way at the same time, using+ -- the GHC flag -static and -dynamic-too+ dynTooOpts =+ (baseOpts StaticWay)+ { ghcOptDynLinkMode = toFlag GhcStaticAndDynamic -- use -dynamic-too+ , ghcOptDynHiSuffix = toFlag (buildWayPrefix DynWay ++ "hi")+ , ghcOptDynObjSuffix = toFlag (buildWayPrefix DynWay ++ "o")+ , ghcOptHPCDir = hpcdir Hpc.Dyn+ -- Should we pass hcSharedOpts in the -dynamic-too ghc invocation?+ -- (Note that `baseOtps StaticWay = hcStaticOptions`, not hcSharedOpts)+ }++ profDynTooOpts =+ (baseOpts ProfWay)+ { ghcOptDynLinkMode = toFlag GhcStaticAndDynamic -- use -dynamic-too+ , -- TODO: Does it hurt to set -fPIC for executables?+ ghcOptFPic = toFlag True -- use -fPIC+ , ghcOptProfilingMode = toFlag True+ , ghcOptProfilingAuto =+ Internal.profDetailLevelFlag+ (if isLib then True else False)+ ((if isLib then withProfLibDetail else withProfExeDetail) lbi)+ , ghcOptDynHiSuffix = toFlag (buildWayPrefix ProfDynWay ++ "hi")+ , ghcOptDynObjSuffix = toFlag (buildWayPrefix ProfDynWay ++ "o")+ , ghcOptHPCDir = hpcdir Hpc.ProfDyn+ -- Should we pass hcSharedOpts in the -dynamic-too ghc invocation?+ -- (Note that `baseOtps StaticWay = hcStaticOptions`, not hcSharedOpts)+ }++ -- Determines how to build for each way, also serves as the base options+ -- for loading modules in 'linkOrLoadComponent'+ buildOpts way = case way of+ StaticWay -> staticOpts+ DynWay -> dynOpts+ ProfWay -> profOpts+ ProfDynWay -> profDynOpts++ -- If there aren't modules, or if we're loading the modules in repl, don't build.+ unless (forRepl || (isNothing mbMainFile && null inputModules)) $ liftIO $ do+ -- See Note [Building Haskell Modules accounting for TH]+ let+ neededLibWaysSet = Set.fromList neededLibWays++ -- If we need both static and dynamic, use dynamic-too instead of+ -- compiling twice (if we support it)+ useDynamicToo =+ StaticWay `Set.member` neededLibWaysSet+ && DynWay `Set.member` neededLibWaysSet+ && supportsDynamicToo comp+ && null (hcSharedOptions GHC bi)++ useProfDynamicToo =+ ProfWay `Set.member` neededLibWaysSet+ && ProfDynWay `Set.member` neededLibWaysSet+ && supportsDynamicToo comp+ && null (hcSharedOptions GHC bi)++ defaultGhcWay = compilerBuildWay comp++ order w+ | w == defaultGhcWay = 0+ | otherwise = fromEnum w + 1++ -- The ways we'll build, in order+ orderedBuilds+ -- We need to make sure that the way which is the way the compiler is built+ -- is built first so that Template Haskell works.+ | useProfDynamicToo && useDynamicToo =+ if defaultGhcWay `elem` [ProfDynWay, ProfWay]+ then [buildProfAndProfDynamicToo, buildStaticAndDynamicToo]+ else [buildStaticAndDynamicToo, buildProfAndProfDynamicToo]+ | useProfDynamicToo && not useDynamicToo =+ if defaultGhcWay `elem` [ProfDynWay, ProfWay]+ then+ [buildProfAndProfDynamicToo]+ ++ (runGhcProg . buildOpts <$> neededLibWays \\ [ProfDynWay, ProfWay])+ else+ (runGhcProg . buildOpts <$> neededLibWays \\ [ProfDynWay, ProfWay])+ ++ [buildProfAndProfDynamicToo]+ | useDynamicToo =+ if defaultGhcWay `elem` [StaticWay, DynWay]+ then+ [buildStaticAndDynamicToo]+ ++ (runGhcProg . buildOpts <$> neededLibWays \\ [StaticWay, DynWay])+ else+ (runGhcProg . buildOpts <$> neededLibWays \\ [StaticWay, DynWay])+ ++ [buildStaticAndDynamicToo]+ -- Otherwise, we need to ensure the defaultGhcWay is built first+ | otherwise =+ runGhcProg . buildOpts <$> sortOn order neededLibWays++ buildStaticAndDynamicToo = do+ runGhcProg dynTooOpts+ case (hpcdir Hpc.Dyn, hpcdir Hpc.Vanilla) of+ (Flag dynDir, Flag vanillaDir) ->+ -- When the vanilla and shared library builds are done+ -- in one pass, only one set of HPC module interfaces+ -- are generated. This set should suffice for both+ -- static and dynamically linked executables. We copy+ -- the modules interfaces so they are available under+ -- both ways.+ copyDirectoryRecursive verbosity (i dynDir) (i vanillaDir)+ _ -> return ()++ buildProfAndProfDynamicToo = do+ runGhcProg profDynTooOpts+ case (hpcdir Hpc.ProfDyn, hpcdir Hpc.Prof) of+ (Flag profDynDir, Flag profDir) ->+ -- When the vanilla and shared library builds are done+ -- in one pass, only one set of HPC module interfaces+ -- are generated. This set should suffice for both+ -- static and dynamically linked executables. We copy+ -- the modules interfaces so they are available under+ -- both ways.+ copyDirectoryRecursive verbosity (i profDynDir) (i profDir)+ _ -> return ()+ in+ -- REVIEW:ADD? info verbosity "Building Haskell Sources..."+ sequence_ orderedBuilds+ return buildOpts++-- | Returns the corresponding 'Hpc.Way' for a 'BuildWay'+buildWayHpcWay :: BuildWay -> Hpc.Way+buildWayHpcWay = \case+ StaticWay -> Hpc.Vanilla+ ProfWay -> Hpc.Prof+ DynWay -> Hpc.Dyn+ ProfDynWay -> Hpc.ProfDyn++-- | Returns a function to extract the extra haskell compiler options from a+-- 'BuildInfo' and 'CompilerFlavor'+buildWayExtraHcOptions :: BuildWay -> CompilerFlavor -> BuildInfo -> [String]+buildWayExtraHcOptions = \case+ StaticWay -> hcStaticOptions+ ProfWay -> hcProfOptions+ DynWay -> hcSharedOptions+ ProfDynWay -> hcProfSharedOptions++-- | Returns a pair of the main file and Haskell modules of the component being+-- built. The main file is not necessarily a Haskell file. It could also be+-- e.g. a C source, or, a Haskell repl script (that does not necessarily have+-- an extension).+--+-- The main file is Nothing if the component is not executable.+componentInputs+ :: SymbolicPath Pkg (Dir Artifacts)+ -- ^ Target build dir+ -> PD.PackageDescription+ -> PreBuildComponentInputs+ -- ^ The context and component being built in it.+ -> IO (Maybe (SymbolicPath Pkg File), [ModuleName])+ -- ^ The main input file, and the Haskell modules+componentInputs buildTargetDir pkg_descr pbci =+ case component of+ CLib lib ->+ pure (Nothing, allLibModules lib clbi)+ CFLib flib ->+ pure (Nothing, foreignLibModules flib)+ CExe Executable{buildInfo = bi', modulePath} ->+ exeLikeInputs bi' modulePath+ CTest TestSuite{testBuildInfo = bi', testInterface = TestSuiteExeV10 _ mainFile} ->+ exeLikeInputs bi' mainFile+ CBench Benchmark{benchmarkBuildInfo = bi', benchmarkInterface = BenchmarkExeV10 _ mainFile} ->+ exeLikeInputs bi' mainFile+ CTest TestSuite{} -> error "testSuiteExeV10AsExe: wrong kind"+ CBench Benchmark{} -> error "benchmarkExeV10asExe: wrong kind"+ where+ verbosity = buildVerbosity pbci+ component = buildComponent pbci+ clbi = buildCLBI pbci+ mbWorkDir = mbWorkDirLBI $ localBuildInfo pbci+ exeLikeInputs bnfo modulePath = liftIO $ do+ main <- findExecutableMain verbosity mbWorkDir buildTargetDir (bnfo, modulePath)+ let mainModName = exeMainModuleName bnfo+ otherModNames = otherModules bnfo++ -- Scripts have fakePackageId and are always Haskell but can have any extension.+ if isHaskell (getSymbolicPath main) || PD.package pkg_descr == fakePackageId+ then+ if PD.specVersion pkg_descr < CabalSpecV2_0 && (mainModName `elem` otherModNames)+ then do+ -- The cabal manual clearly states that `other-modules` is+ -- intended for non-main modules. However, there's at least one+ -- important package on Hackage (happy-1.19.5) which+ -- violates this. We workaround this here so that we don't+ -- invoke GHC with e.g. 'ghc --make Main src/Main.hs' which+ -- would result in GHC complaining about duplicate Main+ -- modules.+ --+ -- Finally, we only enable this workaround for+ -- specVersion < 2, as 'cabal-version:>=2.0' cabal files+ -- have no excuse anymore to keep doing it wrong... ;-)+ warn verbosity $+ "Enabling workaround for Main module '"+ ++ prettyShow mainModName+ ++ "' listed in 'other-modules' illegally!"+ return (Just main, filter (/= mainModName) otherModNames)+ else return (Just main, otherModNames)+ else return (Just main, otherModNames)
+ src/Distribution/Simple/GHC/Build/Utils.hs view
@@ -0,0 +1,246 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE RankNTypes #-}++module Distribution.Simple.GHC.Build.Utils where++import Distribution.Compat.Prelude+import Prelude ()++import Control.Monad (msum)+import Data.Char (isLower)+import Distribution.ModuleName (ModuleName)+import qualified Distribution.ModuleName as ModuleName+import Distribution.PackageDescription as PD+import Distribution.PackageDescription.Utils (cabalBug)+import Distribution.Simple.BuildPaths+import Distribution.Simple.BuildWay+import Distribution.Simple.Compiler+import qualified Distribution.Simple.GHC.Internal as Internal+import Distribution.Simple.Program.GHC+import Distribution.Simple.Setup.Common+import Distribution.Simple.Utils+import Distribution.System+import Distribution.Types.LocalBuildInfo+ ( LocalBuildInfo (hostPlatform)+ )+import Distribution.Utils.Path+import Distribution.Verbosity+import System.FilePath+ ( replaceExtension+ , takeExtension+ )++-- | Find the path to the entry point of an executable (typically specified in+-- @main-is@, and found in @hs-source-dirs@ -- yes, even when @main-is@ is not a Haskell file).+findExecutableMain+ :: Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> SymbolicPath Pkg (Dir build)+ -- ^ Build directory+ -> (BuildInfo, RelativePath Source File)+ -- ^ The build info and module path of an executable-like component (Exe, Test, Bench)+ -> IO (SymbolicPath Pkg File)+ -- ^ The path to the main source file.+findExecutableMain verbosity mbWorkDir buildDir (bnfo, modPath) =+ findFileCwd verbosity mbWorkDir (coerceSymbolicPath buildDir : hsSourceDirs bnfo) modPath++-- | Does this compiler support the @-dynamic-too@ option+supportsDynamicToo :: Compiler -> Bool+supportsDynamicToo = Internal.ghcLookupProperty "Support dynamic-too"++compilerBuildWay :: Compiler -> BuildWay+compilerBuildWay c =+ case (isDynamic c, isProfiled c) of+ (True, True) -> ProfDynWay+ (True, False) -> DynWay+ (False, True) -> ProfWay+ (False, False) -> StaticWay++-- | Is this compiler's RTS dynamically linked?+isDynamic :: Compiler -> Bool+isDynamic = Internal.ghcLookupProperty "GHC Dynamic"++isProfiled :: Compiler -> Bool+isProfiled = Internal.ghcLookupProperty "GHC Profiled"++-- | Should we dynamically link the foreign library, based on its 'foreignLibType'?+withDynFLib :: ForeignLib -> Bool+withDynFLib flib =+ case foreignLibType flib of+ ForeignLibNativeShared ->+ ForeignLibStandalone `notElem` foreignLibOptions flib+ ForeignLibNativeStatic ->+ False+ ForeignLibTypeUnknown ->+ cabalBug "unknown foreign lib type"++-- | Is this file a C++ source file, i.e. ends with .cpp, .cxx, or .c++?+isCxx :: FilePath -> Bool+isCxx fp = elem (takeExtension fp) [".cpp", ".cxx", ".c++"]++-- | Is this a C source file, i.e. ends with .c?+isC :: FilePath -> Bool+isC fp = elem (takeExtension fp) [".c"]++-- | FilePath has a Haskell extension: .hs or .lhs+isHaskell :: FilePath -> Bool+isHaskell fp = elem (takeExtension fp) [".hs", ".lhs"]++-- | Returns True if the modification date of the given source file is newer than+-- the object file we last compiled for it, or if no object file exists yet.+checkNeedsRecompilation+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -> SymbolicPath Pkg File+ -> GhcOptions+ -> IO Bool+checkNeedsRecompilation mbWorkDir filename opts =+ i filename `moreRecentFile` oname+ where+ oname = getObjectFileName mbWorkDir filename opts+ i = interpretSymbolicPath mbWorkDir -- See Note [Symbolic paths] in Distribution.Utils.Path++-- | Finds the object file name of the given source file+getObjectFileName+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -> SymbolicPath Pkg File+ -> GhcOptions+ -> FilePath+getObjectFileName mbWorkDir filename opts = oname+ where+ i = interpretSymbolicPath mbWorkDir -- See Note [Symbolic paths] in Distribution.Utils.Path+ odir = i $ fromFlag (ghcOptObjDir opts)+ oext = fromFlagOrDefault "o" (ghcOptObjSuffix opts)+ -- NB: the filepath might be absolute, e.g. if it is the path to+ -- an autogenerated .hs file.+ oname = odir </> replaceExtension (getSymbolicPath filename) oext++-- | Target name for a foreign library (the actual file name)+--+-- We do not use mkLibName and co here because the naming for foreign libraries+-- is slightly different (we don't use "_p" or compiler version suffices, and we+-- don't want the "lib" prefix on Windows).+--+-- TODO: We do use `dllExtension` and co here, but really that's wrong: they+-- use the OS used to build cabal to determine which extension to use, rather+-- than the target OS (but this is wrong elsewhere in Cabal as well).+flibTargetName :: LocalBuildInfo -> ForeignLib -> String+flibTargetName lbi flib =+ case (os, foreignLibType flib) of+ (Windows, ForeignLibNativeShared) -> nm <.> "dll"+ (Windows, ForeignLibNativeStatic) -> nm <.> "lib"+ (Linux, ForeignLibNativeShared) -> "lib" ++ nm <.> versionedExt+ (_other, ForeignLibNativeShared) ->+ "lib" ++ nm <.> dllExtension (hostPlatform lbi)+ (_other, ForeignLibNativeStatic) ->+ "lib" ++ nm <.> staticLibExtension (hostPlatform lbi)+ (_any, ForeignLibTypeUnknown) -> cabalBug "unknown foreign lib type"+ where+ nm :: String+ nm = unUnqualComponentName $ foreignLibName flib++ os :: OS+ Platform _ os = hostPlatform lbi++ -- If a foreign lib foo has lib-version-info 5:1:2 or+ -- lib-version-linux 3.2.1, it should be built as libfoo.so.3.2.1+ -- Libtool's version-info data is translated into library versions in a+ -- nontrivial way: so refer to libtool documentation.+ versionedExt :: String+ versionedExt =+ let nums = foreignLibVersion flib os+ in foldl (<.>) "so" (map show nums)++-- | Name for the library when building.+--+-- If the `lib-version-info` field or the `lib-version-linux` field of+-- a foreign library target is set, we need to incorporate that+-- version into the SONAME field.+--+-- If a foreign library foo has lib-version-info 5:1:2, it should be+-- built as libfoo.so.3.2.1. We want it to get soname libfoo.so.3.+-- However, GHC does not allow overriding soname by setting linker+-- options, as it sets a soname of its own (namely the output+-- filename), after the user-supplied linker options. Hence, we have+-- to compile the library with the soname as its filename. We rename+-- the compiled binary afterwards.+--+-- This method allows to adjust the name of the library at build time+-- such that the correct soname can be set.+flibBuildName :: LocalBuildInfo -> ForeignLib -> String+flibBuildName lbi flib+ -- On linux, if a foreign-library has version data, the first digit is used+ -- to produce the SONAME.+ | (os, foreignLibType flib)+ == (Linux, ForeignLibNativeShared) =+ let nums = foreignLibVersion flib os+ in "lib" ++ nm <.> foldl (<.>) "so" (map show (take 1 nums))+ | otherwise = flibTargetName lbi flib+ where+ os :: OS+ Platform _ os = hostPlatform lbi++ nm :: String+ nm = unUnqualComponentName $ foreignLibName flib++-- | Gets the target name (name of actual executable file) from the name of an+-- executable-like component ('Executable', 'TestSuite', 'Benchmark').+exeTargetName :: Platform -> UnqualComponentName -> String+exeTargetName platform name = unUnqualComponentName name `withExt` exeExtension platform+ where+ withExt :: FilePath -> String -> FilePath+ withExt fp ext = fp <.> if takeExtension fp /= ('.' : ext) then ext else ""++-- | "Main" module name when overridden by @ghc-options: -main-is ...@+-- or 'Nothing' if no @-main-is@ flag could be found.+--+-- In case of 'Nothing', 'Distribution.ModuleName.main' can be assumed.+exeMainModuleName+ :: BuildInfo+ -- ^ The build info of the executable-like component (Exe, Test, Bench)+ -> ModuleName+exeMainModuleName bnfo =+ -- GHC honors the last occurrence of a module name updated via -main-is+ --+ -- Moreover, -main-is when parsed left-to-right can update either+ -- the "Main" module name, or the "main" function name, or both,+ -- see also 'decodeMainIsArg'.+ fromMaybe ModuleName.main $ msum $ reverse $ map decodeMainIsArg $ findIsMainArgs ghcopts+ where+ ghcopts = hcOptions GHC bnfo++ findIsMainArgs [] = []+ findIsMainArgs ("-main-is" : arg : rest) = arg : findIsMainArgs rest+ findIsMainArgs (_ : rest) = findIsMainArgs rest++-- | Decode argument to '-main-is'+--+-- Returns 'Nothing' if argument set only the function name.+--+-- This code has been stolen/refactored from GHC's DynFlags.setMainIs+-- function. The logic here is deliberately imperfect as it is+-- intended to be bug-compatible with GHC's parser. See discussion in+-- https://github.com/haskell/cabal/pull/4539#discussion_r118981753.+decodeMainIsArg :: String -> Maybe ModuleName+decodeMainIsArg arg+ | headOf main_fn isLower =+ -- The arg looked like "Foo.Bar.baz"+ Just (ModuleName.fromString main_mod)+ | headOf arg isUpper -- The arg looked like "Foo" or "Foo.Bar"+ =+ Just (ModuleName.fromString arg)+ | otherwise -- The arg looked like "baz"+ =+ Nothing+ where+ headOf :: String -> (Char -> Bool) -> Bool+ headOf str pred' = any pred' (safeHead str)++ (main_mod, main_fn) = splitLongestPrefix arg (== '.')++ splitLongestPrefix :: String -> (Char -> Bool) -> (String, String)+ splitLongestPrefix str pred'+ | null r_pre = (str, [])+ | otherwise = (reverse (safeTail r_pre), reverse r_suf)+ where+ -- 'safeTail' drops the char satisfying 'pred'+ (r_suf, r_pre) = break pred' (reverse str)
src/Distribution/Simple/GHC/EnvironmentParser.hs view
@@ -1,49 +1,53 @@-{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE LambdaCase #-} {-# LANGUAGE RankNTypes #-}-{-# LANGUAGE RecordWildCards #-}-module Distribution.Simple.GHC.EnvironmentParser- ( parseGhcEnvironmentFile, readGhcEnvironmentFile, ParseErrorExc(..) ) where -import Prelude ()+module Distribution.Simple.GHC.EnvironmentParser (parseGhcEnvironmentFile, readGhcEnvironmentFile, ParseErrorExc (..)) where+ import Distribution.Compat.Prelude+import Prelude () import Distribution.Simple.Compiler- ( PackageDB(..) ) import Distribution.Simple.GHC.Internal- ( GhcEnvironmentFileEntry(..) )+ ( GhcEnvironmentFileEntry (..)+ ) import Distribution.Types.UnitId- ( mkUnitId )+ ( mkUnitId+ ) import qualified Text.Parsec as P import Text.Parsec.String- ( Parser, parseFromFile )+ ( Parser+ , parseFromFile+ ) -parseEnvironmentFileLine :: Parser GhcEnvironmentFileEntry-parseEnvironmentFileLine = GhcEnvFileComment <$> comment- <|> GhcEnvFilePackageId <$> unitId- <|> GhcEnvFilePackageDb <$> packageDb- <|> pure GhcEnvFileClearPackageDbStack <* clearDb- where- comment = P.string "--" *> P.many (P.noneOf "\r\n")- unitId = P.try $ P.string "package-id" *> P.spaces *>- (mkUnitId <$> P.many1 (P.satisfy $ \c -> isAlphaNum c || c `elem` "-_.+"))- packageDb = (P.string "global-package-db" *> pure GlobalPackageDB)- <|> (P.string "user-package-db" *> pure UserPackageDB)- <|> (P.string "package-db" *> P.spaces *> (SpecificPackageDB <$> P.many1 (P.noneOf "\r\n") <* P.lookAhead P.endOfLine))- clearDb = P.string "clear-package-db"+parseEnvironmentFileLine :: Parser (GhcEnvironmentFileEntry FilePath)+parseEnvironmentFileLine =+ GhcEnvFileComment <$> comment+ <|> GhcEnvFilePackageId <$> unitId+ <|> GhcEnvFilePackageDb <$> packageDb+ <|> pure GhcEnvFileClearPackageDbStack <* clearDb+ where+ comment = P.string "--" *> P.many (P.noneOf "\r\n")+ unitId =+ P.try $+ P.string "package-id"+ *> P.spaces+ *> (mkUnitId <$> P.many1 (P.satisfy $ \c -> isAlphaNum c || c `elem` "-_.+"))+ packageDb =+ (P.string "global-package-db" *> pure GlobalPackageDB)+ <|> (P.string "user-package-db" *> pure UserPackageDB)+ <|> (P.string "package-db" *> P.spaces *> (SpecificPackageDB <$> P.many1 (P.noneOf "\r\n") <* P.lookAhead P.endOfLine))+ clearDb = P.string "clear-package-db" newtype ParseErrorExc = ParseErrorExc P.ParseError- deriving (Show, Typeable)+ deriving (Show) instance Exception ParseErrorExc -parseGhcEnvironmentFile :: Parser [GhcEnvironmentFileEntry]+parseGhcEnvironmentFile :: Parser [GhcEnvironmentFileEntry FilePath] parseGhcEnvironmentFile = parseEnvironmentFileLine `P.sepEndBy` P.endOfLine <* P.eof -readGhcEnvironmentFile :: FilePath -> IO [GhcEnvironmentFileEntry]+readGhcEnvironmentFile :: FilePath -> IO [GhcEnvironmentFileEntry FilePath] readGhcEnvironmentFile path =- either (throwIO . ParseErrorExc) return =<<- parseFromFile parseGhcEnvironmentFile path+ either (throwIO . ParseErrorExc) return+ =<< parseFromFile parseGhcEnvironmentFile path
src/Distribution/Simple/GHC/ImplInfo.hs view
@@ -1,4 +1,5 @@ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.GHC.ImplInfo --@@ -8,89 +9,127 @@ -- This module contains the data structure describing invocation -- details for a GHC or GHC-derived compiler, such as supported flags -- and workarounds for bugs.--module Distribution.Simple.GHC.ImplInfo (- GhcImplInfo(..), getImplInfo,- ghcVersionImplInfo, ghcjsVersionImplInfo- ) where+module Distribution.Simple.GHC.ImplInfo+ ( GhcImplInfo (..)+ , getImplInfo+ , ghcVersionImplInfo+ , ghcjsVersionImplInfo+ ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () import Distribution.Simple.Compiler import Distribution.Version -{- |- Information about features and quirks of a GHC-based implementation.-- Compiler flavors based on GHC behave similarly enough that some of- the support code for them is shared. Every implementation has its- own peculiarities, that may or may not be a direct result of the- underlying GHC version. This record keeps track of these differences.-- All shared code (i.e. everything not in the Distribution.Simple.FLAVOR- module) should use implementation info rather than version numbers- to test for supported features.--}-+-- |+-- Information about features and quirks of a GHC-based implementation.+--+-- Compiler flavors based on GHC behave similarly enough that some of+-- the support code for them is shared. Every implementation has its+-- own peculiarities, that may or may not be a direct result of the+-- underlying GHC version. This record keeps track of these differences.+--+-- All shared code (i.e. everything not in the Distribution.Simple.FLAVOR+-- module) should use implementation info rather than version numbers+-- to test for supported features. data GhcImplInfo = GhcImplInfo- { supportsHaskell2010 :: Bool -- ^ -XHaskell2010 and -XHaskell98 flags- , supportsGHC2021 :: Bool -- ^ -XGHC2021 flag- , reportsNoExt :: Bool -- ^ --supported-languages gives Ext and NoExt- , alwaysNondecIndent :: Bool -- ^ NondecreasingIndentation is always on- , flagGhciScript :: Bool -- ^ -ghci-script flag supported- , flagProfAuto :: Bool -- ^ new style -fprof-auto* flags- , flagPackageConf :: Bool -- ^ use package-conf instead of package-db- , flagDebugInfo :: Bool -- ^ -g flag supported- , supportsDebugLevels :: Bool -- ^ supports numeric @-g@ levels- , supportsPkgEnvFiles :: Bool -- ^ picks up @.ghc.environment@ files- , flagWarnMissingHomeModules :: Bool -- ^ -Wmissing-home-modules is supported+ { supportsHaskell2010 :: Bool+ -- ^ -XHaskell2010 and -XHaskell98 flags+ , supportsGHC2021 :: Bool+ -- ^ -XGHC2021 flag+ , supportsGHC2024 :: Bool+ -- ^ -XGHC2024 flag+ , reportsNoExt :: Bool+ -- ^ --supported-languages gives Ext and NoExt+ , alwaysNondecIndent :: Bool+ -- ^ NondecreasingIndentation is always on+ , flagGhciScript :: Bool+ -- ^ -ghci-script flag supported+ , flagProfAuto :: Bool+ -- ^ new style -fprof-auto* flags+ , flagProfLate :: Bool+ -- ^ fprof-late flag+ , flagPackageConf :: Bool+ -- ^ use package-conf instead of package-db+ , flagDebugInfo :: Bool+ -- ^ -g flag supported+ , flagHie :: Bool+ -- ^ -hiedir flag supported+ , supportsDebugLevels :: Bool+ -- ^ supports numeric @-g@ levels+ , supportsPkgEnvFiles :: Bool+ -- ^ picks up @.ghc.environment@ files+ , flagWarnMissingHomeModules :: Bool+ -- ^ -Wmissing-home-modules is supported+ , unitIdForExes :: Bool+ -- ^ Pass -this-unit-id flag when building executables } getImplInfo :: Compiler -> GhcImplInfo getImplInfo comp = case compilerFlavor comp of- GHC -> ghcVersionImplInfo (compilerVersion comp)+ GHC -> ghcVersionImplInfo (compilerVersion comp) GHCJS -> case compilerCompatVersion GHC comp of- Just ghcVer -> ghcjsVersionImplInfo (compilerVersion comp) ghcVer- _ -> error ("Distribution.Simple.GHC.Props.getImplProps: " ++- "could not find GHC version for GHCJS compiler")- x -> error ("Distribution.Simple.GHC.Props.getImplProps only works" ++- "for GHC-like compilers (GHC, GHCJS)" ++- ", but found " ++ show x)+ Just ghcVer -> ghcjsVersionImplInfo (compilerVersion comp) ghcVer+ _ ->+ error+ ( "Distribution.Simple.GHC.Props.getImplProps: "+ ++ "could not find GHC version for GHCJS compiler"+ )+ x ->+ error+ ( "Distribution.Simple.GHC.Props.getImplProps only works"+ ++ "for GHC-like compilers (GHC, GHCJS)"+ ++ ", but found "+ ++ show x+ ) ghcVersionImplInfo :: Version -> GhcImplInfo-ghcVersionImplInfo ver = GhcImplInfo- { supportsHaskell2010 = v >= [7]- , supportsGHC2021 = v >= [9,1]- , reportsNoExt = v >= [7]- , alwaysNondecIndent = v < [7,1]- , flagGhciScript = v >= [7,2]- , flagProfAuto = v >= [7,4]- , flagPackageConf = v < [7,5]- , flagDebugInfo = v >= [7,10]- , supportsDebugLevels = v >= [8,0]- , supportsPkgEnvFiles = v >= [8,0,1,20160901] -- broken in 8.0.1, fixed in 8.0.2- , flagWarnMissingHomeModules = v >= [8,2]- }+ghcVersionImplInfo ver =+ GhcImplInfo+ { supportsHaskell2010 = v >= [7]+ , supportsGHC2021 = v >= [9, 1]+ , supportsGHC2024 = v >= [9, 9]+ , reportsNoExt = v >= [7]+ , alwaysNondecIndent = v < [7, 1]+ , flagGhciScript = v >= [7, 2]+ , flagProfAuto = v >= [7, 4]+ , flagProfLate = v >= [9, 4]+ , flagPackageConf = v < [7, 5]+ , flagDebugInfo = v >= [7, 10]+ , flagHie = v >= [8, 8]+ , supportsDebugLevels = v >= [8, 0]+ , supportsPkgEnvFiles = v >= [8, 0, 1, 20160901] -- broken in 8.0.1, fixed in 8.0.2+ , flagWarnMissingHomeModules = v >= [8, 2]+ , unitIdForExes = v >= [9, 2]+ } where v = versionNumbers ver -ghcjsVersionImplInfo :: Version -- ^ The GHCJS version- -> Version -- ^ The GHC version- -> GhcImplInfo-ghcjsVersionImplInfo _ghcjsver ghcver = GhcImplInfo- { supportsHaskell2010 = True- , supportsGHC2021 = True- , reportsNoExt = True- , alwaysNondecIndent = False- , flagGhciScript = True- , flagProfAuto = True- , flagPackageConf = False- , flagDebugInfo = False- , supportsDebugLevels = ghcv >= [8,0]- , supportsPkgEnvFiles = ghcv >= [8,0,2] --TODO: check this works in ghcjs- , flagWarnMissingHomeModules = ghcv >= [8,2]- }+ghcjsVersionImplInfo+ :: Version+ -- ^ The GHCJS version+ -> Version+ -- ^ The GHC version+ -> GhcImplInfo+ghcjsVersionImplInfo _ghcjsver ghcver =+ GhcImplInfo+ { supportsHaskell2010 = True+ , supportsGHC2021 = True+ , supportsGHC2024 = ghcv >= [9, 9]+ , reportsNoExt = True+ , alwaysNondecIndent = False+ , flagGhciScript = True+ , flagProfAuto = True+ , flagProfLate = True+ , flagPackageConf = False+ , flagDebugInfo = False+ , flagHie = ghcv >= [8, 8]+ , supportsDebugLevels = ghcv >= [8, 0]+ , supportsPkgEnvFiles = ghcv >= [8, 0, 2] -- TODO: check this works in ghcjs+ , flagWarnMissingHomeModules = ghcv >= [8, 2]+ , unitIdForExes = ghcv >= [9, 2]+ } where ghcv = versionNumbers ghcver
src/Distribution/Simple/GHC/Internal.hs view
@@ -1,686 +1,864 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}---------------------------------------------------------------------------------- |--- Module : Distribution.Simple.GHC.Internal--- Copyright : Isaac Jones 2003-2007------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ This module contains functions shared by GHC (Distribution.Simple.GHC)--- and GHC-derived compilers.--module Distribution.Simple.GHC.Internal (- configureToolchain,- getLanguages,- getExtensions,- targetPlatform,- getGhcInfo,- componentCcGhcOptions,- componentCmmGhcOptions,- componentCxxGhcOptions,- componentAsmGhcOptions,- componentGhcOptions,- mkGHCiLibName,- mkGHCiProfLibName,- filterGhciFlags,- ghcLookupProperty,- getHaskellObjects,- mkGhcOptPackages,- substTopDir,- checkPackageDbEnvVar,- profDetailLevelFlag,- -- * GHC platform and version strings- ghcArchString,- ghcOsString,- ghcPlatformAndVersionString,- -- * Constructing GHC environment files- GhcEnvironmentFileEntry(..),- writeGhcEnvironmentFile,- simpleGhcEnvironmentFile,- ghcEnvironmentFileName,- renderGhcEnvironmentFile,- renderGhcEnvironmentFileEntry,- ) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Simple.GHC.ImplInfo-import Distribution.Types.ComponentLocalBuildInfo-import Distribution.Backpack-import qualified Distribution.InstalledPackageInfo as IPI-import Distribution.PackageDescription-import Distribution.Lex-import Distribution.Simple.Compiler-import Distribution.Simple.Program.GHC-import Distribution.Simple.Setup-import qualified Distribution.ModuleName as ModuleName-import Distribution.Simple.Program-import Distribution.Simple.LocalBuildInfo-import Distribution.Types.UnitId-import Distribution.Types.LocalBuildInfo-import Distribution.Types.TargetInfo-import Distribution.Simple.Utils-import Distribution.Simple.BuildPaths-import Distribution.System-import Distribution.Pretty ( prettyShow )-import Distribution.Parsec ( simpleParsec )-import Distribution.Utils.NubList ( toNubListR )-import Distribution.Verbosity-import Distribution.Compat.Stack-import Distribution.Version (Version)-import Distribution.Utils.Path-import Language.Haskell.Extension--import qualified Data.Map as Map-import qualified Data.ByteString.Lazy.Char8 as BS-import System.Directory ( getDirectoryContents, getTemporaryDirectory )-import System.Environment ( getEnv )-import System.FilePath ( (</>), (<.>), takeExtension- , takeDirectory, takeFileName)-import System.IO ( hClose, hPutStrLn )--targetPlatform :: [(String, String)] -> Maybe Platform-targetPlatform ghcInfo = platformFromTriple =<< lookup "Target platform" ghcInfo---- | Adjust the way we find and configure gcc and ld----configureToolchain :: GhcImplInfo- -> ConfiguredProgram- -> Map String String- -> ProgramDb- -> ProgramDb-configureToolchain _implInfo ghcProg ghcInfo =- addKnownProgram gccProgram {- programFindLocation = findProg gccProgramName extraGccPath,- programPostConf = configureGcc- }- . addKnownProgram ldProgram {- programFindLocation = findProg ldProgramName extraLdPath,- programPostConf = configureLd- }- . addKnownProgram arProgram {- programFindLocation = findProg arProgramName extraArPath- }- . addKnownProgram stripProgram {- programFindLocation = findProg stripProgramName extraStripPath- }- where- compilerDir = takeDirectory (programPath ghcProg)- base_dir = takeDirectory compilerDir- mingwBinDir = base_dir </> "mingw" </> "bin"- isWindows = case buildOS of Windows -> True; _ -> False- binPrefix = ""-- maybeName :: Program -> Maybe FilePath -> String- maybeName prog = maybe (programName prog) (dropExeExtension . takeFileName)-- gccProgramName = maybeName gccProgram mbGccLocation- ldProgramName = maybeName ldProgram mbLdLocation- arProgramName = maybeName arProgram mbArLocation- stripProgramName = maybeName stripProgram mbStripLocation-- mkExtraPath :: Maybe FilePath -> FilePath -> [FilePath]- mkExtraPath mbPath mingwPath | isWindows = mbDir ++ [mingwPath]- | otherwise = mbDir- where- mbDir = maybeToList . fmap takeDirectory $ mbPath-- extraGccPath = mkExtraPath mbGccLocation windowsExtraGccDir- extraLdPath = mkExtraPath mbLdLocation windowsExtraLdDir- extraArPath = mkExtraPath mbArLocation windowsExtraArDir- extraStripPath = mkExtraPath mbStripLocation windowsExtraStripDir-- -- on Windows finding and configuring ghc's gcc & binutils is a bit special- (windowsExtraGccDir, windowsExtraLdDir,- windowsExtraArDir, windowsExtraStripDir) =- let b = mingwBinDir </> binPrefix- in (b, b, b, b)-- findProg :: String -> [FilePath]- -> Verbosity -> ProgramSearchPath- -> IO (Maybe (FilePath, [FilePath]))- findProg progName extraPath v searchpath =- findProgramOnSearchPath v searchpath' progName- where- searchpath' = (map ProgramSearchPathDir extraPath) ++ searchpath-- -- Read tool locations from the 'ghc --info' output. Useful when- -- cross-compiling.- mbGccLocation = Map.lookup "C compiler command" ghcInfo- mbLdLocation = Map.lookup "ld command" ghcInfo- mbArLocation = Map.lookup "ar command" ghcInfo- mbStripLocation = Map.lookup "strip command" ghcInfo-- ccFlags = getFlags "C compiler flags"- -- GHC 7.8 renamed "Gcc Linker flags" to "C compiler link flags"- -- and "Ld Linker flags" to "ld flags" (GHC #4862).- gccLinkerFlags = getFlags "Gcc Linker flags" ++ getFlags "C compiler link flags"- ldLinkerFlags = getFlags "Ld Linker flags" ++ getFlags "ld flags"-- -- It appears that GHC 7.6 and earlier encode the tokenized flags as a- -- [String] in these settings whereas later versions just encode the flags as- -- String.- --- -- We first try to parse as a [String] and if this fails then tokenize the- -- flags ourself.- getFlags :: String -> [String]- getFlags key =- case Map.lookup key ghcInfo of- Nothing -> []- Just flags- | (flags', ""):_ <- reads flags -> flags'- | otherwise -> tokenizeQuotedWords flags-- configureGcc :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram- configureGcc _v gccProg = do- return gccProg {- programDefaultArgs = programDefaultArgs gccProg- ++ ccFlags ++ gccLinkerFlags- }-- configureLd :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram- configureLd v ldProg = do- ldProg' <- configureLd' v ldProg- return ldProg' {- programDefaultArgs = programDefaultArgs ldProg' ++ ldLinkerFlags- }-- -- we need to find out if ld supports the -x flag- configureLd' :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram- configureLd' verbosity ldProg = do- tempDir <- getTemporaryDirectory- ldx <- withTempFile tempDir ".c" $ \testcfile testchnd ->- withTempFile tempDir ".o" $ \testofile testohnd -> do- hPutStrLn testchnd "int foo() { return 0; }"- hClose testchnd; hClose testohnd- runProgram verbosity ghcProg- [ "-hide-all-packages"- , "-c", testcfile- , "-o", testofile- ]- withTempFile tempDir ".o" $ \testofile' testohnd' ->- do- hClose testohnd'- _ <- getProgramOutput verbosity ldProg- ["-x", "-r", testofile, "-o", testofile']- return True- `catchIO` (\_ -> return False)- `catchExit` (\_ -> return False)- if ldx- then return ldProg { programDefaultArgs = ["-x"] }- else return ldProg--getLanguages :: Verbosity -> GhcImplInfo -> ConfiguredProgram- -> IO [(Language, String)]-getLanguages _ implInfo _- -- TODO: should be using --supported-languages rather than hard coding- | supportsGHC2021 implInfo = return- [ (GHC2021, "-XGHC2021")- , (Haskell2010, "-XHaskell2010")- , (Haskell98, "-XHaskell98")- ]- | supportsHaskell2010 implInfo = return [(Haskell98, "-XHaskell98")- ,(Haskell2010, "-XHaskell2010")]- | otherwise = return [(Haskell98, "")]--getGhcInfo :: Verbosity -> GhcImplInfo -> ConfiguredProgram- -> IO [(String, String)]-getGhcInfo verbosity _implInfo ghcProg = do- xs <- getProgramOutput verbosity (suppressOverrideArgs ghcProg)- ["--info"]- case reads xs of- [(i, ss)]- | all isSpace ss ->- return i- _ ->- die' verbosity "Can't parse --info output of GHC"--getExtensions :: Verbosity -> GhcImplInfo -> ConfiguredProgram- -> IO [(Extension, Maybe String)]-getExtensions verbosity implInfo ghcProg = do- str <- getProgramOutput verbosity (suppressOverrideArgs ghcProg)- ["--supported-languages"]- let extStrs = if reportsNoExt implInfo- then lines str- else -- Older GHCs only gave us either Foo or NoFoo,- -- so we have to work out the other one ourselves- [ extStr''- | extStr <- lines str- , let extStr' = case extStr of- 'N' : 'o' : xs -> xs- _ -> "No" ++ extStr- , extStr'' <- [extStr, extStr']- ]- let extensions0 = [ (ext, Just $ "-X" ++ prettyShow ext)- | Just ext <- map simpleParsec extStrs ]- extensions1 = if alwaysNondecIndent implInfo- then -- ghc-7.2 split NondecreasingIndentation off- -- into a proper extension. Before that it- -- was always on.- -- Since it was not a proper extension, it could- -- not be turned off, hence we omit a- -- DisableExtension entry here.- (EnableExtension NondecreasingIndentation, Nothing) :- extensions0- else extensions0- return extensions1--componentCcGhcOptions :: Verbosity -> GhcImplInfo -> LocalBuildInfo- -> BuildInfo -> ComponentLocalBuildInfo- -> FilePath -> FilePath- -> GhcOptions-componentCcGhcOptions verbosity _implInfo lbi bi clbi odir filename =- mempty {- -- Respect -v0, but don't crank up verbosity on GHC if- -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!- ghcOptVerbosity = toFlag (min verbosity normal),- ghcOptMode = toFlag GhcModeCompile,- ghcOptInputFiles = toNubListR [filename],-- ghcOptCppIncludePath = toNubListR $ [autogenComponentModulesDir lbi clbi- ,autogenPackageModulesDir lbi- ,odir]- -- includes relative to the package- ++ includeDirs bi- -- potential includes generated by `configure'- -- in the build directory- ++ [buildDir lbi </> dir | dir <- includeDirs bi],- ghcOptHideAllPackages= toFlag True,- ghcOptPackageDBs = withPackageDB lbi,- ghcOptPackages = toNubListR $ mkGhcOptPackages clbi,- ghcOptCcOptions = (case withOptimization lbi of- NoOptimisation -> []- _ -> ["-O2"]) ++- (case withDebugInfo lbi of- NoDebugInfo -> []- MinimalDebugInfo -> ["-g1"]- NormalDebugInfo -> ["-g"]- MaximalDebugInfo -> ["-g3"]) ++- ccOptions bi,- ghcOptObjDir = toFlag odir- }---componentCxxGhcOptions :: Verbosity -> GhcImplInfo -> LocalBuildInfo- -> BuildInfo -> ComponentLocalBuildInfo- -> FilePath -> FilePath- -> GhcOptions-componentCxxGhcOptions verbosity _implInfo lbi bi clbi odir filename =- mempty {- -- Respect -v0, but don't crank up verbosity on GHC if- -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!- ghcOptVerbosity = toFlag (min verbosity normal),- ghcOptMode = toFlag GhcModeCompile,- ghcOptInputFiles = toNubListR [filename],-- ghcOptCppIncludePath = toNubListR $ [autogenComponentModulesDir lbi clbi- ,autogenPackageModulesDir lbi- ,odir]- -- includes relative to the package- ++ includeDirs bi- -- potential includes generated by `configure'- -- in the build directory- ++ [buildDir lbi </> dir | dir <- includeDirs bi],- ghcOptHideAllPackages= toFlag True,- ghcOptPackageDBs = withPackageDB lbi,- ghcOptPackages = toNubListR $ mkGhcOptPackages clbi,- ghcOptCxxOptions = (case withOptimization lbi of- NoOptimisation -> []- _ -> ["-O2"]) ++- (case withDebugInfo lbi of- NoDebugInfo -> []- MinimalDebugInfo -> ["-g1"]- NormalDebugInfo -> ["-g"]- MaximalDebugInfo -> ["-g3"]) ++- cxxOptions bi,- ghcOptObjDir = toFlag odir- }---componentAsmGhcOptions :: Verbosity -> GhcImplInfo -> LocalBuildInfo- -> BuildInfo -> ComponentLocalBuildInfo- -> FilePath -> FilePath- -> GhcOptions-componentAsmGhcOptions verbosity _implInfo lbi bi clbi odir filename =- mempty {- -- Respect -v0, but don't crank up verbosity on GHC if- -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!- ghcOptVerbosity = toFlag (min verbosity normal),- ghcOptMode = toFlag GhcModeCompile,- ghcOptInputFiles = toNubListR [filename],-- ghcOptCppIncludePath = toNubListR $ [autogenComponentModulesDir lbi clbi- ,autogenPackageModulesDir lbi- ,odir]- -- includes relative to the package- ++ includeDirs bi- -- potential includes generated by `configure'- -- in the build directory- ++ [buildDir lbi </> dir | dir <- includeDirs bi],- ghcOptHideAllPackages= toFlag True,- ghcOptPackageDBs = withPackageDB lbi,- ghcOptPackages = toNubListR $ mkGhcOptPackages clbi,- ghcOptAsmOptions = (case withOptimization lbi of- NoOptimisation -> []- _ -> ["-O2"]) ++- (case withDebugInfo lbi of- NoDebugInfo -> []- MinimalDebugInfo -> ["-g1"]- NormalDebugInfo -> ["-g"]- MaximalDebugInfo -> ["-g3"]) ++- asmOptions bi,- ghcOptObjDir = toFlag odir- }---componentGhcOptions :: Verbosity -> GhcImplInfo -> LocalBuildInfo- -> BuildInfo -> ComponentLocalBuildInfo -> FilePath- -> GhcOptions-componentGhcOptions verbosity implInfo lbi bi clbi odir =- mempty {- -- Respect -v0, but don't crank up verbosity on GHC if- -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!- ghcOptVerbosity = toFlag (min verbosity normal),- ghcOptCabal = toFlag True,- ghcOptThisUnitId = case clbi of- LibComponentLocalBuildInfo { componentCompatPackageKey = pk }- -> toFlag pk- _ -> mempty,- ghcOptThisComponentId = case clbi of- LibComponentLocalBuildInfo { componentComponentId = cid- , componentInstantiatedWith = insts } ->- if null insts- then mempty- else toFlag cid- _ -> mempty,- ghcOptInstantiatedWith = case clbi of- LibComponentLocalBuildInfo { componentInstantiatedWith = insts }- -> insts- _ -> [],- ghcOptNoCode = toFlag $ componentIsIndefinite clbi,- ghcOptHideAllPackages = toFlag True,- ghcOptWarnMissingHomeModules = toFlag $ flagWarnMissingHomeModules implInfo,- ghcOptPackageDBs = withPackageDB lbi,- ghcOptPackages = toNubListR $ mkGhcOptPackages clbi,- ghcOptSplitSections = toFlag (splitSections lbi),- ghcOptSplitObjs = toFlag (splitObjs lbi),- ghcOptSourcePathClear = toFlag True,- ghcOptSourcePath = toNubListR $ [odir] ++ (map getSymbolicPath (hsSourceDirs bi))- ++ [autogenComponentModulesDir lbi clbi]- ++ [autogenPackageModulesDir lbi],- ghcOptCppIncludePath = toNubListR $ [autogenComponentModulesDir lbi clbi- ,autogenPackageModulesDir lbi- ,odir]- -- includes relative to the package- ++ includeDirs bi- -- potential includes generated by `configure'- -- in the build directory- ++ [buildDir lbi </> dir | dir <- includeDirs bi],- ghcOptCppOptions = cppOptions bi,- ghcOptCppIncludes = toNubListR $- [autogenComponentModulesDir lbi clbi </> cppHeaderName],- ghcOptFfiIncludes = toNubListR $ includes bi,- ghcOptObjDir = toFlag odir,- ghcOptHiDir = toFlag odir,- ghcOptStubDir = toFlag odir,- ghcOptOutputDir = toFlag odir,- ghcOptOptimisation = toGhcOptimisation (withOptimization lbi),- ghcOptDebugInfo = toFlag (withDebugInfo lbi),- ghcOptExtra = hcOptions GHC bi,- ghcOptExtraPath = toNubListR $ exe_paths,- ghcOptLanguage = toFlag (fromMaybe Haskell98 (defaultLanguage bi)),- -- Unsupported extensions have already been checked by configure- ghcOptExtensions = toNubListR $ usedExtensions bi,- ghcOptExtensionMap = Map.fromList . compilerExtensions $ (compiler lbi)- }- where- exe_paths = [ componentBuildDir lbi (targetCLBI exe_tgt)- | uid <- componentExeDeps clbi- -- TODO: Ugh, localPkgDescr- , Just exe_tgt <- [unitIdTarget' (localPkgDescr lbi) lbi uid] ]--toGhcOptimisation :: OptimisationLevel -> Flag GhcOptimisation-toGhcOptimisation NoOptimisation = mempty --TODO perhaps override?-toGhcOptimisation NormalOptimisation = toFlag GhcNormalOptimisation-toGhcOptimisation MaximumOptimisation = toFlag GhcMaximumOptimisation---componentCmmGhcOptions :: Verbosity -> GhcImplInfo -> LocalBuildInfo- -> BuildInfo -> ComponentLocalBuildInfo- -> FilePath -> FilePath- -> GhcOptions-componentCmmGhcOptions verbosity _implInfo lbi bi clbi odir filename =- mempty {- -- Respect -v0, but don't crank up verbosity on GHC if- -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!- ghcOptVerbosity = toFlag (min verbosity normal),- ghcOptMode = toFlag GhcModeCompile,- ghcOptInputFiles = toNubListR [filename],-- ghcOptCppIncludePath = toNubListR $ [autogenComponentModulesDir lbi clbi- ,autogenPackageModulesDir lbi- ,odir]- -- includes relative to the package- ++ includeDirs bi- -- potential includes generated by `configure'- -- in the build directory- ++ [buildDir lbi </> dir | dir <- includeDirs bi],- ghcOptCppOptions = cppOptions bi,- ghcOptCppIncludes = toNubListR $- [autogenComponentModulesDir lbi clbi </> cppHeaderName],- ghcOptHideAllPackages= toFlag True,- ghcOptPackageDBs = withPackageDB lbi,- ghcOptPackages = toNubListR $ mkGhcOptPackages clbi,- ghcOptOptimisation = toGhcOptimisation (withOptimization lbi),- ghcOptDebugInfo = toFlag (withDebugInfo lbi),- ghcOptExtra = cmmOptions bi,- ghcOptObjDir = toFlag odir- }----- | Strip out flags that are not supported in ghci-filterGhciFlags :: [String] -> [String]-filterGhciFlags = filter supported- where- supported ('-':'O':_) = False- supported "-debug" = False- supported "-threaded" = False- supported "-ticky" = False- supported "-eventlog" = False- supported "-prof" = False- supported "-unreg" = False- supported _ = True--mkGHCiLibName :: UnitId -> String-mkGHCiLibName lib = getHSLibraryName lib <.> "o"--mkGHCiProfLibName :: UnitId -> String-mkGHCiProfLibName lib = getHSLibraryName lib <.> "p_o"--ghcLookupProperty :: String -> Compiler -> Bool-ghcLookupProperty prop comp =- case Map.lookup prop (compilerProperties comp) of- Just "YES" -> True- _ -> False---- when using -split-objs, we need to search for object files in the--- Module_split directory for each module.-getHaskellObjects :: GhcImplInfo -> Library -> LocalBuildInfo- -> ComponentLocalBuildInfo- -> FilePath -> String -> Bool -> IO [FilePath]-getHaskellObjects _implInfo lib lbi clbi pref wanted_obj_ext allow_split_objs- | splitObjs lbi && allow_split_objs = do- let splitSuffix = "_" ++ wanted_obj_ext ++ "_split"- dirs = [ pref </> (ModuleName.toFilePath x ++ splitSuffix)- | x <- allLibModules lib clbi ]- objss <- traverse getDirectoryContents dirs- let objs = [ dir </> obj- | (objs',dir) <- zip objss dirs, obj <- objs',- let obj_ext = takeExtension obj,- '.':wanted_obj_ext == obj_ext ]- return objs- | otherwise =- return [ pref </> ModuleName.toFilePath x <.> wanted_obj_ext- | x <- allLibModules lib clbi ]--mkGhcOptPackages :: ComponentLocalBuildInfo- -> [(OpenUnitId, ModuleRenaming)]-mkGhcOptPackages = componentIncludes--substTopDir :: FilePath -> IPI.InstalledPackageInfo -> IPI.InstalledPackageInfo-substTopDir topDir ipo- = ipo {- IPI.importDirs = map f (IPI.importDirs ipo),- IPI.libraryDirs = map f (IPI.libraryDirs ipo),- IPI.includeDirs = map f (IPI.includeDirs ipo),- IPI.frameworkDirs = map f (IPI.frameworkDirs ipo),- IPI.haddockInterfaces = map f (IPI.haddockInterfaces ipo),- IPI.haddockHTMLs = map f (IPI.haddockHTMLs ipo)- }- where f ('$':'t':'o':'p':'d':'i':'r':rest) = topDir ++ rest- f x = x---- Cabal does not use the environment variable GHC{,JS}_PACKAGE_PATH; let--- users know that this is the case. See ticket #335. Simply ignoring it is--- not a good idea, since then ghc and cabal are looking at different sets--- of package DBs and chaos is likely to ensue.------ An exception to this is when running cabal from within a `cabal exec`--- environment. In this case, `cabal exec` will set the--- CABAL_SANDBOX_PACKAGE_PATH to the same value that it set--- GHC{,JS}_PACKAGE_PATH to. If that is the case it is OK to allow--- GHC{,JS}_PACKAGE_PATH.-checkPackageDbEnvVar :: Verbosity -> String -> String -> IO ()-checkPackageDbEnvVar verbosity compilerName packagePathEnvVar = do- mPP <- lookupEnv packagePathEnvVar- when (isJust mPP) $ do- mcsPP <- lookupEnv "CABAL_SANDBOX_PACKAGE_PATH"- unless (mPP == mcsPP) abort- where- lookupEnv :: String -> IO (Maybe String)- lookupEnv name = (Just `fmap` getEnv name)- `catchIO` const (return Nothing)- abort =- die' verbosity $ "Use of " ++ compilerName ++ "'s environment variable "- ++ packagePathEnvVar ++ " is incompatible with Cabal. Use the "- ++ "flag --package-db to specify a package database (it can be "- ++ "used multiple times)."-- _ = callStack -- TODO: output stack when erroring--profDetailLevelFlag :: Bool -> ProfDetailLevel -> Flag GhcProfAuto-profDetailLevelFlag forLib mpl =- case mpl of- ProfDetailNone -> mempty- ProfDetailDefault | forLib -> toFlag GhcProfAutoExported- | otherwise -> toFlag GhcProfAutoToplevel- ProfDetailExportedFunctions -> toFlag GhcProfAutoExported- ProfDetailToplevelFunctions -> toFlag GhcProfAutoToplevel- ProfDetailAllFunctions -> toFlag GhcProfAutoAll- ProfDetailOther _ -> mempty----- -------------------------------------------------------------------------------- GHC platform and version strings---- | GHC's rendering of its host or target 'Arch' as used in its platform--- strings and certain file locations (such as user package db location).----ghcArchString :: Arch -> String-ghcArchString PPC = "powerpc"-ghcArchString PPC64 = "powerpc64"-ghcArchString other = prettyShow other---- | GHC's rendering of its host or target 'OS' as used in its platform--- strings and certain file locations (such as user package db location).----ghcOsString :: OS -> String-ghcOsString Windows = "mingw32"-ghcOsString OSX = "darwin"-ghcOsString Solaris = "solaris2"-ghcOsString other = prettyShow other---- | GHC's rendering of its platform and compiler version string as used in--- certain file locations (such as user package db location).--- For example @x86_64-linux-7.10.4@----ghcPlatformAndVersionString :: Platform -> Version -> String-ghcPlatformAndVersionString (Platform arch os) version =- intercalate "-" [ ghcArchString arch, ghcOsString os, prettyShow version ]----- -------------------------------------------------------------------------------- Constructing GHC environment files---- | The kinds of entries we can stick in a @.ghc.environment@ file.----data GhcEnvironmentFileEntry =- GhcEnvFileComment String -- ^ @-- a comment@- | GhcEnvFilePackageId UnitId -- ^ @package-id foo-1.0-4fe301a...@- | GhcEnvFilePackageDb PackageDB -- ^ @global-package-db@,- -- @user-package-db@ or- -- @package-db blah/package.conf.d/@- | GhcEnvFileClearPackageDbStack -- ^ @clear-package-db@- deriving (Eq, Ord, Show)---- | Make entries for a GHC environment file based on a 'PackageDBStack' and--- a bunch of package (unit) ids.------ If you need to do anything more complicated then either use this as a basis--- and add more entries, or just make all the entries directly.----simpleGhcEnvironmentFile :: PackageDBStack- -> [UnitId]- -> [GhcEnvironmentFileEntry]-simpleGhcEnvironmentFile packageDBs pkgids =- GhcEnvFileClearPackageDbStack- : map GhcEnvFilePackageDb packageDBs- ++ map GhcEnvFilePackageId pkgids---- | Write a @.ghc.environment-$arch-$os-$ver@ file in the given directory.------ The 'Platform' and GHC 'Version' are needed as part of the file name.------ Returns the name of the file written.-writeGhcEnvironmentFile :: FilePath -- ^ directory in which to put it- -> Platform -- ^ the GHC target platform- -> Version -- ^ the GHC version- -> [GhcEnvironmentFileEntry] -- ^ the content- -> IO FilePath-writeGhcEnvironmentFile directory platform ghcversion entries = do- writeFileAtomic envfile . BS.pack . renderGhcEnvironmentFile $ entries- return envfile- where- envfile = directory </> ghcEnvironmentFileName platform ghcversion---- | The @.ghc.environment-$arch-$os-$ver@ file name----ghcEnvironmentFileName :: Platform -> Version -> FilePath-ghcEnvironmentFileName platform ghcversion =- ".ghc.environment." ++ ghcPlatformAndVersionString platform ghcversion---- | Render a bunch of GHC environment file entries----renderGhcEnvironmentFile :: [GhcEnvironmentFileEntry] -> String-renderGhcEnvironmentFile =- unlines . map renderGhcEnvironmentFileEntry---- | Render an individual GHC environment file entry----renderGhcEnvironmentFileEntry :: GhcEnvironmentFileEntry -> String-renderGhcEnvironmentFileEntry entry = case entry of- GhcEnvFileComment comment -> format comment- where format = intercalate "\n" . map ("--" <++>) . lines- pref <++> "" = pref- pref <++> str = pref ++ " " ++ str- GhcEnvFilePackageId pkgid -> "package-id " ++ prettyShow pkgid- GhcEnvFilePackageDb pkgdb ->- case pkgdb of- GlobalPackageDB -> "global-package-db"- UserPackageDB -> "user-package-db"- SpecificPackageDB dbfile -> "package-db " ++ dbfile- GhcEnvFileClearPackageDbStack -> "clear-package-db"+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RankNTypes #-}++-----------------------------------------------------------------------------++-- |+-- Module : Distribution.Simple.GHC.Internal+-- Copyright : Isaac Jones 2003-2007+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- This module contains functions shared by GHC (Distribution.Simple.GHC)+-- and GHC-derived compilers.+module Distribution.Simple.GHC.Internal+ ( configureToolchain+ , getLanguages+ , getExtensions+ , targetPlatform+ , getGhcInfo+ , componentCcGhcOptions+ , componentCmmGhcOptions+ , componentCxxGhcOptions+ , componentAsmGhcOptions+ , componentJsGhcOptions+ , componentGhcOptions+ , mkGHCiLibName+ , mkGHCiProfLibName+ , filterGhciFlags+ , ghcLookupProperty+ , getHaskellObjects+ , mkGhcOptPackages+ , substTopDir+ , checkPackageDbEnvVar+ , profDetailLevelFlag++ -- * GHC platform and version strings+ , ghcArchString+ , ghcOsString+ , ghcPlatformAndVersionString++ -- * Constructing GHC environment files+ , GhcEnvironmentFileEntry (..)+ , writeGhcEnvironmentFile+ , simpleGhcEnvironmentFile+ , ghcEnvironmentFileName+ , renderGhcEnvironmentFile+ , renderGhcEnvironmentFileEntry+ ) where++import Distribution.Compat.Prelude+import Prelude ()++import Data.Bool (bool)+import qualified Data.ByteString.Lazy.Char8 as BS+import qualified Data.Map as Map+import qualified Data.Set as Set+import Distribution.Backpack+import Distribution.Compat.Stack+import qualified Distribution.InstalledPackageInfo as IPI+import Distribution.Lex+import qualified Distribution.ModuleName as ModuleName+import Distribution.PackageDescription+import Distribution.Parsec (simpleParsec)+import Distribution.Pretty (prettyShow)+import Distribution.Simple.BuildPaths+import Distribution.Simple.Compiler+import Distribution.Simple.Errors+import Distribution.Simple.Flag (Flag, maybeToFlag, toFlag, pattern NoFlag)+import Distribution.Simple.GHC.ImplInfo+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.Program+import Distribution.Simple.Program.GHC+import Distribution.Simple.Setup.Common (extraCompilationArtifacts)+import Distribution.Simple.Utils+import Distribution.System+import Distribution.Types.ComponentLocalBuildInfo+import Distribution.Types.GivenComponent+import Distribution.Types.LocalBuildInfo+import Distribution.Types.TargetInfo+import Distribution.Types.UnitId+import Distribution.Utils.NubList (NubListR, toNubListR)+import Distribution.Utils.Path+import Distribution.Verbosity+import Distribution.Version (Version)+import Language.Haskell.Extension+import System.Directory (getDirectoryContents)+import System.Environment (getEnv)+import System.FilePath+ ( takeDirectory+ , takeExtension+ , takeFileName+ )+import System.IO (hClose, hPutStrLn)++targetPlatform :: [(String, String)] -> Maybe Platform+targetPlatform ghcInfo = platformFromTriple =<< lookup "Target platform" ghcInfo++-- | Adjust the way we find and configure gcc and ld+configureToolchain+ :: GhcImplInfo+ -> ConfiguredProgram+ -> Map String String+ -> ProgramDb+ -> ProgramDb+configureToolchain _implInfo ghcProg ghcInfo =+ addKnownProgram+ gccProgram+ { programFindLocation = findProg gccProgramName extraGccPath+ , programPostConf = configureGcc+ }+ . addKnownProgram+ gppProgram+ { programFindLocation = findProg gppProgramName extraGppPath+ , programPostConf = configureGpp+ }+ . addKnownProgram+ ldProgram+ { programFindLocation = findProg ldProgramName extraLdPath+ , programPostConf = \v cp ->+ -- Call any existing configuration first and then add any new configuration+ configureLd v =<< programPostConf ldProgram v cp+ }+ . addKnownProgram+ arProgram+ { programFindLocation = findProg arProgramName extraArPath+ }+ . addKnownProgram+ stripProgram+ { programFindLocation = findProg stripProgramName extraStripPath+ }+ where+ compilerDir, base_dir, mingwBinDir :: FilePath+ compilerDir = takeDirectory (programPath ghcProg)+ base_dir = takeDirectory compilerDir+ mingwBinDir = base_dir </> "mingw" </> "bin"+ isWindows = case buildOS of Windows -> True; _ -> False+ binPrefix = ""++ maybeName :: Program -> Maybe FilePath -> String+ maybeName prog = maybe (programName prog) (dropExeExtension . takeFileName)++ gccProgramName = maybeName gccProgram mbGccLocation+ gppProgramName = maybeName gppProgram mbGppLocation+ ldProgramName = maybeName ldProgram mbLdLocation+ arProgramName = maybeName arProgram mbArLocation+ stripProgramName = maybeName stripProgram mbStripLocation++ mkExtraPath :: Maybe FilePath -> FilePath -> [FilePath]+ mkExtraPath mbPath mingwPath+ | isWindows = mbDir ++ [mingwPath]+ | otherwise = mbDir+ where+ mbDir = maybeToList . fmap takeDirectory $ mbPath++ extraGccPath = mkExtraPath mbGccLocation windowsExtraGccDir+ extraGppPath = mkExtraPath mbGppLocation windowsExtraGppDir+ extraLdPath = mkExtraPath mbLdLocation windowsExtraLdDir+ extraArPath = mkExtraPath mbArLocation windowsExtraArDir+ extraStripPath = mkExtraPath mbStripLocation windowsExtraStripDir++ -- on Windows finding and configuring ghc's gcc & binutils is a bit special+ ( windowsExtraGccDir+ , windowsExtraGppDir+ , windowsExtraLdDir+ , windowsExtraArDir+ , windowsExtraStripDir+ ) =+ let b = mingwBinDir </> binPrefix+ in (b, b, b, b, b)++ findProg+ :: String+ -> [FilePath]+ -> Verbosity+ -> ProgramSearchPath+ -> IO (Maybe (FilePath, [FilePath]))+ findProg progName extraPath v searchpath =+ findProgramOnSearchPath v searchpath' progName+ where+ searchpath' = (map ProgramSearchPathDir extraPath) ++ searchpath++ -- Read tool locations from the 'ghc --info' output. Useful when+ -- cross-compiling.+ mbGccLocation = Map.lookup "C compiler command" ghcInfo+ mbGppLocation = Map.lookup "C++ compiler command" ghcInfo+ mbLdLocation = Map.lookup "ld command" ghcInfo+ mbArLocation = Map.lookup "ar command" ghcInfo+ mbStripLocation = Map.lookup "strip command" ghcInfo++ ccFlags = getFlags "C compiler flags"+ cxxFlags = getFlags "C++ compiler flags"+ -- GHC 7.8 renamed "Gcc Linker flags" to "C compiler link flags"+ -- and "Ld Linker flags" to "ld flags" (GHC #4862).+ gccLinkerFlags = getFlags "Gcc Linker flags" ++ getFlags "C compiler link flags"+ ldLinkerFlags = getFlags "Ld Linker flags" ++ getFlags "ld flags"++ -- It appears that GHC 7.6 and earlier encode the tokenized flags as a+ -- [String] in these settings whereas later versions just encode the flags as+ -- String.+ --+ -- We first try to parse as a [String] and if this fails then tokenize the+ -- flags ourself.+ getFlags :: String -> [String]+ getFlags key =+ case Map.lookup key ghcInfo of+ Nothing -> []+ Just flags+ | (flags', "") : _ <- reads flags -> flags'+ | otherwise -> tokenizeQuotedWords flags++ configureGcc :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram+ configureGcc _v gccProg = do+ return+ gccProg+ { programDefaultArgs =+ programDefaultArgs gccProg+ ++ ccFlags+ ++ gccLinkerFlags+ }++ configureGpp :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram+ configureGpp _v gppProg = do+ return+ gppProg+ { programDefaultArgs =+ programDefaultArgs gppProg+ ++ cxxFlags+ }++ configureLd :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram+ configureLd v ldProg = do+ ldProg' <- configureLd' v ldProg+ return+ ldProg'+ { programDefaultArgs = programDefaultArgs ldProg' ++ ldLinkerFlags+ }++ -- we need to find out if ld supports the -x flag+ configureLd' :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram+ configureLd' verbosity ldProg = do+ ldx <- withTempFile ".c" $ \testcfile testchnd ->+ withTempFile ".o" $ \testofile testohnd -> do+ hPutStrLn testchnd "int foo() { return 0; }"+ hClose testchnd+ hClose testohnd+ runProgram+ verbosity+ ghcProg+ [ "-hide-all-packages"+ , "-c"+ , testcfile+ , "-o"+ , testofile+ ]+ withTempFile ".o" $ \testofile' testohnd' ->+ do+ hClose testohnd'+ _ <-+ getProgramOutput+ verbosity+ ldProg+ ["-x", "-r", testofile, "-o", testofile']+ return True+ `catchIO` (\_ -> return False)+ `catchExit` (\_ -> return False)+ if ldx+ then return ldProg{programDefaultArgs = ["-x"]}+ else return ldProg++getLanguages+ :: Verbosity+ -> GhcImplInfo+ -> ConfiguredProgram+ -> IO [(Language, String)]+getLanguages _ implInfo _+ -- TODO: should be using --supported-languages rather than hard coding+ | supportsGHC2024 implInfo =+ return+ [ (GHC2024, "-XGHC2024")+ , (GHC2021, "-XGHC2021")+ , (Haskell2010, "-XHaskell2010")+ , (Haskell98, "-XHaskell98")+ ]+ | supportsGHC2021 implInfo =+ return+ [ (GHC2021, "-XGHC2021")+ , (Haskell2010, "-XHaskell2010")+ , (Haskell98, "-XHaskell98")+ ]+ | supportsHaskell2010 implInfo =+ return+ [ (Haskell98, "-XHaskell98")+ , (Haskell2010, "-XHaskell2010")+ ]+ | otherwise = return [(Haskell98, "")]++getGhcInfo+ :: Verbosity+ -> GhcImplInfo+ -> ConfiguredProgram+ -> IO [(String, String)]+getGhcInfo verbosity _implInfo ghcProg = do+ xs <-+ getProgramOutput+ verbosity+ (suppressOverrideArgs ghcProg)+ ["--info"]+ case reads xs of+ [(i, ss)]+ | all isSpace ss ->+ return i+ _ ->+ dieWithException verbosity CantParseGHCOutput++getExtensions+ :: Verbosity+ -> GhcImplInfo+ -> ConfiguredProgram+ -> IO [(Extension, Maybe String)]+getExtensions verbosity implInfo ghcProg = do+ str <-+ getProgramOutput+ verbosity+ (suppressOverrideArgs ghcProg)+ ["--supported-languages"]+ let extStrs =+ if reportsNoExt implInfo+ then lines str+ else -- Older GHCs only gave us either Foo or NoFoo,+ -- so we have to work out the other one ourselves++ [ extStr''+ | extStr <- lines str+ , let extStr' = case extStr of+ 'N' : 'o' : xs -> xs+ _ -> "No" ++ extStr+ , extStr'' <- [extStr, extStr']+ ]+ let extensions0 =+ [ (ext, Just $ "-X" ++ prettyShow ext)+ | Just ext <- map simpleParsec extStrs+ ]+ extensions1 =+ if alwaysNondecIndent implInfo+ then -- ghc-7.2 split NondecreasingIndentation off+ -- into a proper extension. Before that it+ -- was always on.+ -- Since it was not a proper extension, it could+ -- not be turned off, hence we omit a+ -- DisableExtension entry here.++ (EnableExtension NondecreasingIndentation, Nothing)+ : extensions0+ else extensions0+ return extensions1++includePaths+ :: LocalBuildInfo+ -> BuildInfo+ -> ComponentLocalBuildInfo+ -> SymbolicPath Pkg p+ -> NubListR (SymbolicPath Pkg (Dir Include))+includePaths lbi bi clbi odir =+ toNubListR $+ [ coerceSymbolicPath $ autogenComponentModulesDir lbi clbi+ , coerceSymbolicPath $ autogenPackageModulesDir lbi+ , coerceSymbolicPath odir+ ]+ -- includes relative to the package+ ++ includeDirs bi+ -- potential includes generated by `configure'+ -- in the build directory+ ++ [ buildDir lbi </> dir+ | dir <- mapMaybe (symbolicPathRelative_maybe . unsafeCoerceSymbolicPath) $ includeDirs bi+ ]++componentCcGhcOptions+ :: Verbosity+ -> LocalBuildInfo+ -> BuildInfo+ -> ComponentLocalBuildInfo+ -> SymbolicPath Pkg (Dir Artifacts)+ -> SymbolicPath Pkg File+ -> GhcOptions+componentCcGhcOptions verbosity lbi bi clbi odir filename =+ mempty+ { -- Respect -v0, but don't crank up verbosity on GHC if+ -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!+ ghcOptVerbosity = toFlag (min verbosity normal)+ , ghcOptMode = toFlag GhcModeCompile+ , ghcOptInputFiles = toNubListR [filename]+ , ghcOptCppIncludePath = includePaths lbi bi clbi odir+ , ghcOptHideAllPackages = toFlag True+ , ghcOptPackageDBs = withPackageDB lbi+ , ghcOptPackages = toNubListR $ mkGhcOptPackages (promisedPkgs lbi) clbi+ , ghcOptCcOptions =+ ( case withOptimization lbi of+ NoOptimisation -> []+ _ -> ["-O2"]+ )+ ++ ( case withDebugInfo lbi of+ NoDebugInfo -> []+ MinimalDebugInfo -> ["-g1"]+ NormalDebugInfo -> ["-g"]+ MaximalDebugInfo -> ["-g3"]+ )+ ++ ccOptions bi+ , ghcOptCcProgram =+ maybeToFlag $+ programPath+ <$> lookupProgram gccProgram (withPrograms lbi)+ , ghcOptObjDir = toFlag odir+ , ghcOptExtra = hcOptions GHC bi+ }++componentCxxGhcOptions+ :: Verbosity+ -> LocalBuildInfo+ -> BuildInfo+ -> ComponentLocalBuildInfo+ -> SymbolicPath Pkg (Dir Artifacts)+ -> SymbolicPath Pkg File+ -> GhcOptions+componentCxxGhcOptions verbosity lbi bi clbi odir filename =+ mempty+ { -- Respect -v0, but don't crank up verbosity on GHC if+ -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!+ ghcOptVerbosity = toFlag (min verbosity normal)+ , ghcOptMode = toFlag GhcModeCompile+ , ghcOptInputFiles = toNubListR [filename]+ , ghcOptCppIncludePath = includePaths lbi bi clbi odir+ , ghcOptHideAllPackages = toFlag True+ , ghcOptPackageDBs = withPackageDB lbi+ , ghcOptPackages = toNubListR $ mkGhcOptPackages (promisedPkgs lbi) clbi+ , ghcOptCxxOptions =+ ( case withOptimization lbi of+ NoOptimisation -> []+ _ -> ["-O2"]+ )+ ++ ( case withDebugInfo lbi of+ NoDebugInfo -> []+ MinimalDebugInfo -> ["-g1"]+ NormalDebugInfo -> ["-g"]+ MaximalDebugInfo -> ["-g3"]+ )+ ++ cxxOptions bi+ , ghcOptCcProgram =+ maybeToFlag $+ programPath+ <$> lookupProgram gccProgram (withPrograms lbi)+ , ghcOptObjDir = toFlag odir+ , ghcOptExtra = hcOptions GHC bi+ }++componentAsmGhcOptions+ :: Verbosity+ -> LocalBuildInfo+ -> BuildInfo+ -> ComponentLocalBuildInfo+ -> SymbolicPath Pkg (Dir Artifacts)+ -> SymbolicPath Pkg File+ -> GhcOptions+componentAsmGhcOptions verbosity lbi bi clbi odir filename =+ mempty+ { -- Respect -v0, but don't crank up verbosity on GHC if+ -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!+ ghcOptVerbosity = toFlag (min verbosity normal)+ , ghcOptMode = toFlag GhcModeCompile+ , ghcOptInputFiles = toNubListR [filename]+ , ghcOptCppIncludePath = includePaths lbi bi clbi odir+ , ghcOptHideAllPackages = toFlag True+ , ghcOptPackageDBs = withPackageDB lbi+ , ghcOptPackages = toNubListR $ mkGhcOptPackages (promisedPkgs lbi) clbi+ , ghcOptAsmOptions =+ ( case withOptimization lbi of+ NoOptimisation -> []+ _ -> ["-O2"]+ )+ ++ ( case withDebugInfo lbi of+ NoDebugInfo -> []+ MinimalDebugInfo -> ["-g1"]+ NormalDebugInfo -> ["-g"]+ MaximalDebugInfo -> ["-g3"]+ )+ ++ asmOptions bi+ , ghcOptObjDir = toFlag odir+ , ghcOptExtra = hcOptions GHC bi+ }++componentJsGhcOptions+ :: Verbosity+ -> LocalBuildInfo+ -> BuildInfo+ -> ComponentLocalBuildInfo+ -> SymbolicPath Pkg (Dir Artifacts)+ -> SymbolicPath Pkg File+ -> GhcOptions+componentJsGhcOptions verbosity lbi bi clbi odir filename =+ mempty+ { -- Respect -v0, but don't crank up verbosity on GHC if+ -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!+ ghcOptVerbosity = toFlag (min verbosity normal)+ , ghcOptMode = toFlag GhcModeCompile+ , ghcOptInputFiles = toNubListR [filename]+ , ghcOptJSppOptions = jsppOptions bi+ , ghcOptCppIncludePath = includePaths lbi bi clbi odir+ , ghcOptHideAllPackages = toFlag True+ , ghcOptPackageDBs = withPackageDB lbi+ , ghcOptPackages = toNubListR $ mkGhcOptPackages (promisedPkgs lbi) clbi+ , ghcOptObjDir = toFlag odir+ , ghcOptExtra = hcOptions GHC bi+ }++componentGhcOptions+ :: Verbosity+ -> LocalBuildInfo+ -> BuildInfo+ -> ComponentLocalBuildInfo+ -> SymbolicPath Pkg (Dir build)+ -> GhcOptions+componentGhcOptions verbosity lbi bi clbi odir =+ let implInfo = getImplInfo $ compiler lbi+ in mempty+ { -- Respect -v0, but don't crank up verbosity on GHC if+ -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!+ ghcOptVerbosity = toFlag (min verbosity normal)+ , ghcOptCabal = toFlag True+ , ghcOptThisUnitId = case clbi of+ LibComponentLocalBuildInfo{componentCompatPackageKey = pk} ->+ toFlag pk+ _ | not (unitIdForExes implInfo) -> mempty+ ExeComponentLocalBuildInfo{componentUnitId = uid} ->+ toFlag (unUnitId uid)+ TestComponentLocalBuildInfo{componentUnitId = uid} ->+ toFlag (unUnitId uid)+ BenchComponentLocalBuildInfo{componentUnitId = uid} ->+ toFlag (unUnitId uid)+ FLibComponentLocalBuildInfo{componentUnitId = uid} ->+ toFlag (unUnitId uid)+ , ghcOptThisComponentId = case clbi of+ LibComponentLocalBuildInfo+ { componentComponentId = cid+ , componentInstantiatedWith = insts+ } ->+ if null insts+ then mempty+ else toFlag cid+ _ -> mempty+ , ghcOptInstantiatedWith = case clbi of+ LibComponentLocalBuildInfo{componentInstantiatedWith = insts} ->+ insts+ _ -> []+ , ghcOptNoCode = toFlag $ componentIsIndefinite clbi+ , ghcOptHideAllPackages = toFlag True+ , ghcOptWarnMissingHomeModules = toFlag $ flagWarnMissingHomeModules implInfo+ , ghcOptPackageDBs = withPackageDB lbi+ , ghcOptPackages = toNubListR $ mkGhcOptPackages mempty clbi+ , ghcOptSplitSections = toFlag (splitSections lbi)+ , ghcOptSplitObjs = toFlag (splitObjs lbi)+ , ghcOptSourcePathClear = toFlag True+ , ghcOptSourcePath =+ toNubListR $+ (hsSourceDirs bi)+ ++ [coerceSymbolicPath odir]+ ++ [autogenComponentModulesDir lbi clbi]+ ++ [autogenPackageModulesDir lbi]+ , ghcOptCppIncludePath = includePaths lbi bi clbi odir+ , ghcOptCppOptions = cppOptions bi+ , ghcOptJSppOptions = jsppOptions bi+ , ghcOptCppIncludes =+ toNubListR $+ [coerceSymbolicPath (autogenComponentModulesDir lbi clbi </> makeRelativePathEx cppHeaderName)]+ , ghcOptFfiIncludes = toNubListR $ map getSymbolicPath $ includes bi+ , ghcOptObjDir = toFlag $ coerceSymbolicPath odir+ , ghcOptHiDir = toFlag $ coerceSymbolicPath odir+ , ghcOptHieDir = bool NoFlag (toFlag $ coerceSymbolicPath odir </> (extraCompilationArtifacts </> makeRelativePathEx "hie")) $ flagHie implInfo+ , ghcOptStubDir = toFlag $ coerceSymbolicPath odir+ , ghcOptOutputDir = toFlag $ coerceSymbolicPath odir+ , ghcOptOptimisation = toGhcOptimisation (withOptimization lbi)+ , ghcOptDebugInfo = toFlag (withDebugInfo lbi)+ , ghcOptExtra = hcOptions GHC bi+ , ghcOptExtraPath = toNubListR exe_paths+ , ghcOptLanguage = toFlag (fromMaybe Haskell98 (defaultLanguage bi))+ , -- Unsupported extensions have already been checked by configure+ ghcOptExtensions = toNubListR $ usedExtensions bi+ , ghcOptExtensionMap = Map.fromList . compilerExtensions $ (compiler lbi)+ }+ where+ exe_paths =+ [ componentBuildDir lbi (targetCLBI exe_tgt)+ | uid <- componentExeDeps clbi+ , -- TODO: Ugh, localPkgDescr+ Just exe_tgt <- [unitIdTarget' (localPkgDescr lbi) lbi uid]+ ]++toGhcOptimisation :: OptimisationLevel -> Flag GhcOptimisation+toGhcOptimisation NoOptimisation = mempty -- TODO perhaps override?+toGhcOptimisation NormalOptimisation = toFlag GhcNormalOptimisation+toGhcOptimisation MaximumOptimisation = toFlag GhcMaximumOptimisation++componentCmmGhcOptions+ :: Verbosity+ -> LocalBuildInfo+ -> BuildInfo+ -> ComponentLocalBuildInfo+ -> SymbolicPath Pkg (Dir Artifacts)+ -> SymbolicPath Pkg File+ -> GhcOptions+componentCmmGhcOptions verbosity lbi bi clbi odir filename =+ mempty+ { -- Respect -v0, but don't crank up verbosity on GHC if+ -- Cabal verbosity is requested. For that, use --ghc-option=-v instead!+ ghcOptVerbosity = toFlag (min verbosity normal)+ , ghcOptMode = toFlag GhcModeCompile+ , ghcOptInputFiles = toNubListR [filename]+ , ghcOptCppIncludePath = includePaths lbi bi clbi odir+ , ghcOptCppOptions = cppOptions bi+ , ghcOptCppIncludes =+ toNubListR $+ [autogenComponentModulesDir lbi clbi </> makeRelativePathEx cppHeaderName]+ , ghcOptHideAllPackages = toFlag True+ , ghcOptPackageDBs = withPackageDB lbi+ , ghcOptPackages = toNubListR $ mkGhcOptPackages (promisedPkgs lbi) clbi+ , ghcOptOptimisation = toGhcOptimisation (withOptimization lbi)+ , ghcOptDebugInfo = toFlag (withDebugInfo lbi)+ , ghcOptExtra = hcOptions GHC bi <> cmmOptions bi+ , ghcOptObjDir = toFlag odir+ }++-- | Strip out flags that are not supported in ghci+filterGhciFlags :: [String] -> [String]+filterGhciFlags = filter supported+ where+ supported ('-' : 'O' : _) = False+ supported "-debug" = False+ supported "-threaded" = False+ supported "-ticky" = False+ supported "-eventlog" = False+ supported "-prof" = False+ supported "-unreg" = False+ supported _ = True++mkGHCiLibName :: UnitId -> String+mkGHCiLibName lib = getHSLibraryName lib <.> "o"++mkGHCiProfLibName :: UnitId -> String+mkGHCiProfLibName lib = getHSLibraryName lib <.> "p_o"++ghcLookupProperty :: String -> Compiler -> Bool+ghcLookupProperty prop comp =+ case Map.lookup prop (compilerProperties comp) of+ Just "YES" -> True+ _ -> False++-- when using -split-objs, we need to search for object files in the+-- Module_split directory for each module.+getHaskellObjects+ :: GhcImplInfo+ -> Library+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> SymbolicPath Pkg (Dir Artifacts)+ -> String+ -> Bool+ -> IO [SymbolicPath Pkg File]+getHaskellObjects _implInfo lib lbi clbi pref wanted_obj_ext allow_split_objs+ | splitObjs lbi && allow_split_objs = do+ let splitSuffix = "_" ++ wanted_obj_ext ++ "_split"+ dirs =+ [ pref </> makeRelativePathEx (ModuleName.toFilePath x ++ splitSuffix)+ | x <- allLibModules lib clbi+ ]+ objss <- traverse (getDirectoryContents . i) dirs+ let objs =+ [ dir </> makeRelativePathEx obj+ | (objs', dir) <- zip objss dirs+ , obj <- objs'+ , let obj_ext = takeExtension obj+ , '.' : wanted_obj_ext == obj_ext+ ]+ return objs+ | otherwise =+ return+ [ pref </> makeRelativePathEx (ModuleName.toFilePath x <.> wanted_obj_ext)+ | x <- allLibModules lib clbi+ ]+ where+ i = interpretSymbolicPathLBI lbi++-- | Create the required packaged arguments, but filtering out package arguments which+-- aren't yet built, but promised. This filtering is used when compiling C/Cxx/Asm files,+-- and is a hack to avoid passing bogus `-package` arguments to GHC. The assumption being that+-- in 99% of cases we will include the right `-package` so that the C file finds the right headers.+mkGhcOptPackages+ :: Map (PackageName, ComponentName) PromisedComponent+ -> ComponentLocalBuildInfo+ -> [(OpenUnitId, ModuleRenaming)]+mkGhcOptPackages promisedPkgsMap clbi =+ [ i | i@(uid, _) <- componentIncludes clbi, abstractUnitId uid `Set.notMember` promised_cids+ ]+ where+ -- Promised deps are going to be simple UnitIds+ promised_cids = Set.fromList (map (newSimpleUnitId . promisedComponentId) (Map.elems promisedPkgsMap))++substTopDir :: FilePath -> IPI.InstalledPackageInfo -> IPI.InstalledPackageInfo+substTopDir topDir ipo =+ ipo+ { IPI.importDirs = map f (IPI.importDirs ipo)+ , IPI.libraryDirs = map f (IPI.libraryDirs ipo)+ , IPI.libraryDirsStatic = map f (IPI.libraryDirsStatic ipo)+ , IPI.includeDirs = map f (IPI.includeDirs ipo)+ , IPI.frameworkDirs = map f (IPI.frameworkDirs ipo)+ , IPI.haddockInterfaces = map f (IPI.haddockInterfaces ipo)+ , IPI.haddockHTMLs = map f (IPI.haddockHTMLs ipo)+ }+ where+ f ('$' : 't' : 'o' : 'p' : 'd' : 'i' : 'r' : rest) = topDir ++ rest+ f x = x++-- Cabal does not use the environment variable GHC{,JS}_PACKAGE_PATH; let+-- users know that this is the case. See ticket #335. Simply ignoring it is+-- not a good idea, since then ghc and cabal are looking at different sets+-- of package DBs and chaos is likely to ensue.+--+-- An exception to this is when running cabal from within a `cabal exec`+-- environment. In this case, `cabal exec` will set the+-- CABAL_SANDBOX_PACKAGE_PATH to the same value that it set+-- GHC{,JS}_PACKAGE_PATH to. If that is the case it is OK to allow+-- GHC{,JS}_PACKAGE_PATH.+checkPackageDbEnvVar :: Verbosity -> String -> String -> IO ()+checkPackageDbEnvVar verbosity compilerName packagePathEnvVar = do+ mPP <- lookupEnv packagePathEnvVar+ when (isJust mPP) $ do+ mcsPP <- lookupEnv "CABAL_SANDBOX_PACKAGE_PATH"+ unless (mPP == mcsPP) abort+ where+ lookupEnv :: String -> IO (Maybe String)+ lookupEnv name =+ (Just `fmap` getEnv name)+ `catchIO` const (return Nothing)+ abort =+ dieWithException verbosity $ IncompatibleWithCabal compilerName packagePathEnvVar+ _ = callStack -- TODO: output stack when erroring++profDetailLevelFlag :: Bool -> ProfDetailLevel -> Flag GhcProfAuto+profDetailLevelFlag forLib mpl =+ case mpl of+ ProfDetailNone -> mempty+ ProfDetailDefault+ | forLib -> toFlag GhcProfAutoExported+ | otherwise -> toFlag GhcProfAutoToplevel+ ProfDetailExportedFunctions -> toFlag GhcProfAutoExported+ ProfDetailToplevelFunctions -> toFlag GhcProfAutoToplevel+ ProfDetailAllFunctions -> toFlag GhcProfAutoAll+ ProfDetailTopLate -> toFlag GhcProfLate+ ProfDetailOther _ -> mempty++-- -----------------------------------------------------------------------------+-- GHC platform and version strings++-- | GHC's rendering of its host or target 'Arch' as used in its platform+-- strings and certain file locations (such as user package db location).+ghcArchString :: Arch -> String+ghcArchString PPC = "powerpc"+ghcArchString PPC64 = "powerpc64"+ghcArchString PPC64LE = "powerpc64le"+ghcArchString other = prettyShow other++-- | GHC's rendering of its host or target 'OS' as used in its platform+-- strings and certain file locations (such as user package db location).+ghcOsString :: OS -> String+ghcOsString Windows = "mingw32"+ghcOsString OSX = "darwin"+ghcOsString Solaris = "solaris2"+ghcOsString Hurd = "gnu"+ghcOsString other = prettyShow other++-- | GHC's rendering of its platform and compiler version string as used in+-- certain file locations (such as user package db location).+-- For example @x86_64-linux-7.10.4@+ghcPlatformAndVersionString :: Platform -> Version -> String+ghcPlatformAndVersionString (Platform arch os) version =+ intercalate "-" [ghcArchString arch, ghcOsString os, prettyShow version]++-- -----------------------------------------------------------------------------+-- Constructing GHC environment files++-- | The kinds of entries we can stick in a @.ghc.environment@ file.+data GhcEnvironmentFileEntry fp+ = -- | @-- a comment@+ GhcEnvFileComment String+ | -- | @package-id foo-1.0-4fe301a...@+ GhcEnvFilePackageId UnitId+ | -- | @global-package-db@,+ -- @user-package-db@ or+ -- @package-db blah/package.conf.d/@+ GhcEnvFilePackageDb (PackageDBX fp)+ | -- | @clear-package-db@+ GhcEnvFileClearPackageDbStack+ deriving (Eq, Ord, Show)++-- | Make entries for a GHC environment file based on a 'PackageDBStack' and+-- a bunch of package (unit) ids.+--+-- If you need to do anything more complicated then either use this as a basis+-- and add more entries, or just make all the entries directly.+simpleGhcEnvironmentFile+ :: PackageDBStackX fp+ -> [UnitId]+ -> [GhcEnvironmentFileEntry fp]+simpleGhcEnvironmentFile packageDBs pkgids =+ GhcEnvFileClearPackageDbStack+ : map GhcEnvFilePackageDb packageDBs+ ++ map GhcEnvFilePackageId pkgids++-- | Write a @.ghc.environment-$arch-$os-$ver@ file in the given directory.+--+-- The 'Platform' and GHC 'Version' are needed as part of the file name.+--+-- Returns the name of the file written.+writeGhcEnvironmentFile+ :: FilePath+ -- ^ directory in which to put it+ -> Platform+ -- ^ the GHC target platform+ -> Version+ -- ^ the GHC version+ -> [GhcEnvironmentFileEntry FilePath]+ -- ^ the content+ -> IO FilePath+writeGhcEnvironmentFile directory platform ghcversion entries = do+ writeFileAtomic envfile . BS.pack . renderGhcEnvironmentFile $ entries+ return envfile+ where+ envfile = directory </> ghcEnvironmentFileName platform ghcversion++-- | The @.ghc.environment-$arch-$os-$ver@ file name+ghcEnvironmentFileName :: Platform -> Version -> FilePath+ghcEnvironmentFileName platform ghcversion =+ ".ghc.environment." ++ ghcPlatformAndVersionString platform ghcversion++-- | Render a bunch of GHC environment file entries+renderGhcEnvironmentFile :: [GhcEnvironmentFileEntry FilePath] -> String+renderGhcEnvironmentFile =+ unlines . map renderGhcEnvironmentFileEntry++-- | Render an individual GHC environment file entry+renderGhcEnvironmentFileEntry :: GhcEnvironmentFileEntry FilePath -> String+renderGhcEnvironmentFileEntry entry = case entry of+ GhcEnvFileComment comment -> format comment+ where+ format = intercalate "\n" . map ("--" <++>) . lines+ pref <++> "" = pref+ pref <++> str = pref ++ " " ++ str+ GhcEnvFilePackageId pkgid -> "package-id " ++ prettyShow pkgid+ GhcEnvFilePackageDb pkgdb ->+ case pkgdb of+ GlobalPackageDB -> "global-package-db"+ UserPackageDB -> "user-package-db"+ SpecificPackageDB dbfile -> "package-db " ++ dbfile+ GhcEnvFileClearPackageDbStack -> "clear-package-db"
src/Distribution/Simple/GHCJS.hs view
@@ -1,1826 +1,2116 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE CPP #-}--module Distribution.Simple.GHCJS (- getGhcInfo,- configure,- getInstalledPackages,- getInstalledPackagesMonitorFiles,- getPackageDBContents,- buildLib, buildFLib, buildExe,- replLib, replFLib, replExe,- startInterpreter,- installLib, installFLib, installExe,- libAbiHash,- hcPkgInfo,- registerPackage,- componentGhcOptions,- componentCcGhcOptions,- getLibDir,- isDynamic,- getGlobalPackageDB,- pkgRoot,- runCmd,- -- * Constructing and deconstructing GHC environment files- Internal.GhcEnvironmentFileEntry(..),- Internal.simpleGhcEnvironmentFile,- Internal.renderGhcEnvironmentFile,- Internal.writeGhcEnvironmentFile,- Internal.ghcPlatformAndVersionString,- readGhcEnvironmentFile,- parseGhcEnvironmentFile,- ParseErrorExc(..),- -- * Version-specific implementation quirks- getImplInfo,- GhcImplInfo(..)- ) where--import Prelude ()-import Distribution.Compat.Prelude--import qualified Distribution.Simple.GHC.Internal as Internal-import Distribution.Simple.GHC.ImplInfo-import Distribution.Simple.GHC.EnvironmentParser-import Distribution.PackageDescription.Utils (cabalBug)-import Distribution.PackageDescription as PD-import Distribution.InstalledPackageInfo (InstalledPackageInfo)-import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo-import Distribution.Simple.PackageIndex (InstalledPackageIndex)-import qualified Distribution.Simple.PackageIndex as PackageIndex-import Distribution.Simple.LocalBuildInfo-import Distribution.Types.ComponentLocalBuildInfo-import qualified Distribution.Simple.Hpc as Hpc-import Distribution.Simple.BuildPaths-import Distribution.Simple.Utils-import Distribution.Package-import qualified Distribution.ModuleName as ModuleName-import Distribution.ModuleName (ModuleName)-import Distribution.Simple.Program-import qualified Distribution.Simple.Program.HcPkg as HcPkg-import qualified Distribution.Simple.Program.Strip as Strip-import Distribution.Simple.Program.GHC-import Distribution.Simple.Setup-import qualified Distribution.Simple.Setup as Cabal-import Distribution.Simple.Compiler-import Distribution.CabalSpecVersion-import Distribution.Version-import Distribution.System-import Distribution.Verbosity-import Distribution.Pretty-import Distribution.Utils.NubList-import Distribution.Utils.Path--import Control.Monad (msum)-import Data.Char (isLower)-import qualified Data.Map as Map-import System.Directory- ( doesFileExist, getAppUserDataDirectory, createDirectoryIfMissing- , canonicalizePath, removeFile, renameFile )-import System.FilePath ( (</>), (<.>), takeExtension- , takeDirectory, replaceExtension- ,isRelative )-import qualified System.Info---- -------------------------------------------------------------------------------- Configuring--configure :: Verbosity -> Maybe FilePath -> Maybe FilePath- -> ProgramDb- -> IO (Compiler, Maybe Platform, ProgramDb)-configure verbosity hcPath hcPkgPath conf0 = do-- (ghcjsProg, ghcjsVersion, progdb1) <-- requireProgramVersion verbosity ghcjsProgram- (orLaterVersion (mkVersion [0,1]))- (userMaybeSpecifyPath "ghcjs" hcPath conf0)-- Just ghcjsGhcVersion <- findGhcjsGhcVersion verbosity (programPath ghcjsProg)- unless (ghcjsGhcVersion < mkVersion [8,8]) $- warn verbosity $- "Unknown/unsupported 'ghc' version detected "- ++ "(Cabal " ++ prettyShow cabalVersion ++ " supports 'ghc' version < 8.8): "- ++ programPath ghcjsProg ++ " is based on GHC version " ++- prettyShow ghcjsGhcVersion-- let implInfo = ghcjsVersionImplInfo ghcjsVersion ghcjsGhcVersion-- -- This is slightly tricky, we have to configure ghc first, then we use the- -- location of ghc to help find ghc-pkg in the case that the user did not- -- specify the location of ghc-pkg directly:- (ghcjsPkgProg, ghcjsPkgVersion, progdb2) <-- requireProgramVersion verbosity ghcjsPkgProgram {- programFindLocation = guessGhcjsPkgFromGhcjsPath ghcjsProg- }- anyVersion (userMaybeSpecifyPath "ghcjs-pkg" hcPkgPath progdb1)-- Just ghcjsPkgGhcjsVersion <- findGhcjsPkgGhcjsVersion- verbosity (programPath ghcjsPkgProg)-- when (ghcjsVersion /= ghcjsPkgGhcjsVersion) $ die' verbosity $- "Version mismatch between ghcjs and ghcjs-pkg: "- ++ programPath ghcjsProg ++ " is version " ++ prettyShow ghcjsVersion ++ " "- ++ programPath ghcjsPkgProg ++ " is version " ++ prettyShow ghcjsPkgGhcjsVersion-- when (ghcjsGhcVersion /= ghcjsPkgVersion) $ die' verbosity $- "Version mismatch between ghcjs and ghcjs-pkg: "- ++ programPath ghcjsProg- ++ " was built with GHC version " ++ prettyShow ghcjsGhcVersion ++ " "- ++ programPath ghcjsPkgProg- ++ " was built with GHC version " ++ prettyShow ghcjsPkgVersion--- -- Likewise we try to find the matching hsc2hs and haddock programs.- let hsc2hsProgram' = hsc2hsProgram {- programFindLocation =- guessHsc2hsFromGhcjsPath ghcjsProg- }- haddockProgram' = haddockProgram {- programFindLocation =- guessHaddockFromGhcjsPath ghcjsProg- }- hpcProgram' = hpcProgram {- programFindLocation = guessHpcFromGhcjsPath ghcjsProg- }- {-- runghcProgram' = runghcProgram {- programFindLocation = guessRunghcFromGhcjsPath ghcjsProg- } -}- progdb3 = addKnownProgram haddockProgram' $- addKnownProgram hsc2hsProgram' $- addKnownProgram hpcProgram' $- {- addKnownProgram runghcProgram' -} progdb2-- languages <- Internal.getLanguages verbosity implInfo ghcjsProg- extensions <- Internal.getExtensions verbosity implInfo ghcjsProg-- ghcjsInfo <- Internal.getGhcInfo verbosity implInfo ghcjsProg- let ghcInfoMap = Map.fromList ghcjsInfo-- let comp = Compiler {- compilerId = CompilerId GHCJS ghcjsVersion,- compilerAbiTag = AbiTag $- "ghc" ++ intercalate "_" (map show . versionNumbers $ ghcjsGhcVersion),- compilerCompat = [CompilerId GHC ghcjsGhcVersion],- compilerLanguages = languages,- compilerExtensions = extensions,- compilerProperties = ghcInfoMap- }- compPlatform = Internal.targetPlatform ghcjsInfo- return (comp, compPlatform, progdb3)--guessGhcjsPkgFromGhcjsPath :: ConfiguredProgram -> Verbosity- -> ProgramSearchPath -> IO (Maybe (FilePath, [FilePath]))-guessGhcjsPkgFromGhcjsPath = guessToolFromGhcjsPath ghcjsPkgProgram--guessHsc2hsFromGhcjsPath :: ConfiguredProgram -> Verbosity- -> ProgramSearchPath -> IO (Maybe (FilePath, [FilePath]))-guessHsc2hsFromGhcjsPath = guessToolFromGhcjsPath hsc2hsProgram--guessHaddockFromGhcjsPath :: ConfiguredProgram -> Verbosity- -> ProgramSearchPath -> IO (Maybe (FilePath, [FilePath]))-guessHaddockFromGhcjsPath = guessToolFromGhcjsPath haddockProgram--guessHpcFromGhcjsPath :: ConfiguredProgram- -> Verbosity -> ProgramSearchPath- -> IO (Maybe (FilePath, [FilePath]))-guessHpcFromGhcjsPath = guessToolFromGhcjsPath hpcProgram---guessToolFromGhcjsPath :: Program -> ConfiguredProgram- -> Verbosity -> ProgramSearchPath- -> IO (Maybe (FilePath, [FilePath]))-guessToolFromGhcjsPath tool ghcjsProg verbosity searchpath- = do let toolname = programName tool- given_path = programPath ghcjsProg- given_dir = takeDirectory given_path- real_path <- canonicalizePath given_path- let real_dir = takeDirectory real_path- versionSuffix path = takeVersionSuffix (dropExeExtension path)- given_suf = versionSuffix given_path- real_suf = versionSuffix real_path- guessNormal dir = dir </> toolname <.> exeExtension buildPlatform- guessGhcjs dir = dir </> (toolname ++ "-ghcjs")- <.> exeExtension buildPlatform- guessGhcjsVersioned dir suf = dir </> (toolname ++ "-ghcjs" ++ suf)- <.> exeExtension buildPlatform- guessVersioned dir suf = dir </> (toolname ++ suf)- <.> exeExtension buildPlatform- mkGuesses dir suf | null suf = [guessGhcjs dir, guessNormal dir]- | otherwise = [guessGhcjsVersioned dir suf,- guessVersioned dir suf,- guessGhcjs dir,- guessNormal dir]- guesses = mkGuesses given_dir given_suf ++- if real_path == given_path- then []- else mkGuesses real_dir real_suf- info verbosity $ "looking for tool " ++ toolname- ++ " near compiler in " ++ given_dir- debug verbosity $ "candidate locations: " ++ show guesses- exists <- traverse doesFileExist guesses- case [ file | (file, True) <- zip guesses exists ] of- -- If we can't find it near ghc, fall back to the usual- -- method.- [] -> programFindLocation tool verbosity searchpath- (fp:_) -> do info verbosity $ "found " ++ toolname ++ " in " ++ fp- let lookedAt = map fst- . takeWhile (\(_file, exist) -> not exist)- $ zip guesses exists- return (Just (fp, lookedAt))-- where takeVersionSuffix :: FilePath -> String- takeVersionSuffix = takeWhileEndLE isSuffixChar-- isSuffixChar :: Char -> Bool- isSuffixChar c = isDigit c || c == '.' || c == '-'--getGhcInfo :: Verbosity -> ConfiguredProgram -> IO [(String, String)]-getGhcInfo verbosity ghcjsProg = Internal.getGhcInfo verbosity implInfo ghcjsProg- where- version = fromMaybe (error "GHCJS.getGhcInfo: no version") $ programVersion ghcjsProg- implInfo = ghcVersionImplInfo version---- | Given a single package DB, return all installed packages.-getPackageDBContents :: Verbosity -> PackageDB -> ProgramDb- -> IO InstalledPackageIndex-getPackageDBContents verbosity packagedb progdb = do- pkgss <- getInstalledPackages' verbosity [packagedb] progdb- toPackageIndex verbosity pkgss progdb---- | Given a package DB stack, return all installed packages.-getInstalledPackages :: Verbosity -> PackageDBStack -> ProgramDb- -> IO InstalledPackageIndex-getInstalledPackages verbosity packagedbs progdb = do- checkPackageDbEnvVar verbosity- checkPackageDbStack verbosity packagedbs- pkgss <- getInstalledPackages' verbosity packagedbs progdb- index <- toPackageIndex verbosity pkgss progdb- return $! index--toPackageIndex :: Verbosity- -> [(PackageDB, [InstalledPackageInfo])]- -> ProgramDb- -> IO InstalledPackageIndex-toPackageIndex verbosity pkgss progdb = do- -- On Windows, various fields have $topdir/foo rather than full- -- paths. We need to substitute the right value in so that when- -- we, for example, call gcc, we have proper paths to give it.- topDir <- getLibDir' verbosity ghcjsProg- let indices = [ PackageIndex.fromList (map (Internal.substTopDir topDir) pkgs)- | (_, pkgs) <- pkgss ]- return $! (mconcat indices)-- where- ghcjsProg = fromMaybe (error "GHCJS.toPackageIndex no ghcjs program") $ lookupProgram ghcjsProgram progdb--getLibDir :: Verbosity -> LocalBuildInfo -> IO FilePath-getLibDir verbosity lbi =- dropWhileEndLE isSpace `fmap`- getDbProgramOutput verbosity ghcjsProgram- (withPrograms lbi) ["--print-libdir"]--getLibDir' :: Verbosity -> ConfiguredProgram -> IO FilePath-getLibDir' verbosity ghcjsProg =- dropWhileEndLE isSpace `fmap`- getProgramOutput verbosity ghcjsProg ["--print-libdir"]----- | Return the 'FilePath' to the global GHC package database.-getGlobalPackageDB :: Verbosity -> ConfiguredProgram -> IO FilePath-getGlobalPackageDB verbosity ghcProg =- dropWhileEndLE isSpace `fmap`- getProgramOutput verbosity ghcProg ["--print-global-package-db"]---- | Return the 'FilePath' to the per-user GHC package database.-getUserPackageDB :: Verbosity -> ConfiguredProgram -> Platform -> IO FilePath-getUserPackageDB _verbosity ghcjsProg platform = do- -- It's rather annoying that we have to reconstruct this, because ghc- -- hides this information from us otherwise. But for certain use cases- -- like change monitoring it really can't remain hidden.- appdir <- getAppUserDataDirectory "ghcjs"- return (appdir </> platformAndVersion </> packageConfFileName)- where- platformAndVersion = Internal.ghcPlatformAndVersionString- platform ghcjsVersion- packageConfFileName = "package.conf.d"- ghcjsVersion = fromMaybe (error "GHCJS.getUserPackageDB: no version") $ programVersion ghcjsProg--checkPackageDbEnvVar :: Verbosity -> IO ()-checkPackageDbEnvVar verbosity =- Internal.checkPackageDbEnvVar verbosity "GHCJS" "GHCJS_PACKAGE_PATH"--checkPackageDbStack :: Verbosity -> PackageDBStack -> IO ()-checkPackageDbStack _ (GlobalPackageDB:rest)- | GlobalPackageDB `notElem` rest = return ()-checkPackageDbStack verbosity rest- | GlobalPackageDB `notElem` rest =- die' verbosity $ "With current ghc versions the global package db is always used "- ++ "and must be listed first. This ghc limitation may be lifted in "- ++ "future, see https://gitlab.haskell.org/ghc/ghc/-/issues/5977"-checkPackageDbStack verbosity _ =- die' verbosity $ "If the global package db is specified, it must be "- ++ "specified first and cannot be specified multiple times"--getInstalledPackages' :: Verbosity -> [PackageDB] -> ProgramDb- -> IO [(PackageDB, [InstalledPackageInfo])]-getInstalledPackages' verbosity packagedbs progdb =- sequenceA- [ do pkgs <- HcPkg.dump (hcPkgInfo progdb) verbosity packagedb- return (packagedb, pkgs)- | packagedb <- packagedbs ]---- | Get the packages from specific PackageDBs, not cumulative.----getInstalledPackagesMonitorFiles :: Verbosity -> Platform- -> ProgramDb- -> [PackageDB]- -> IO [FilePath]-getInstalledPackagesMonitorFiles verbosity platform progdb =- traverse getPackageDBPath- where- getPackageDBPath :: PackageDB -> IO FilePath- getPackageDBPath GlobalPackageDB =- selectMonitorFile =<< getGlobalPackageDB verbosity ghcjsProg-- getPackageDBPath UserPackageDB =- selectMonitorFile =<< getUserPackageDB verbosity ghcjsProg platform-- getPackageDBPath (SpecificPackageDB path) = selectMonitorFile path-- -- GHC has old style file dbs, and new style directory dbs.- -- Note that for dir style dbs, we only need to monitor the cache file, not- -- the whole directory. The ghc program itself only reads the cache file- -- so it's safe to only monitor this one file.- selectMonitorFile path = do- isFileStyle <- doesFileExist path- if isFileStyle then return path- else return (path </> "package.cache")-- ghcjsProg = fromMaybe (error "GHCJS.toPackageIndex no ghcjs program") $ lookupProgram ghcjsProgram progdb---toJSLibName :: String -> String-toJSLibName lib- | takeExtension lib `elem` [".dll",".dylib",".so"]- = replaceExtension lib "js_so"- | takeExtension lib == ".a" = replaceExtension lib "js_a"- | otherwise = lib <.> "js_a"---- -------------------------------------------------------------------------------- Building a library--buildLib :: Verbosity -> Cabal.Flag (Maybe Int) -> PackageDescription- -> LocalBuildInfo -> Library -> ComponentLocalBuildInfo- -> IO ()-buildLib = buildOrReplLib Nothing--replLib :: [String] -> Verbosity- -> Cabal.Flag (Maybe Int) -> PackageDescription- -> LocalBuildInfo -> Library- -> ComponentLocalBuildInfo -> IO ()-replLib = buildOrReplLib . Just--buildOrReplLib :: Maybe [String] -> Verbosity- -> Cabal.Flag (Maybe Int) -> PackageDescription- -> LocalBuildInfo -> Library- -> ComponentLocalBuildInfo -> IO ()-buildOrReplLib mReplFlags verbosity numJobs pkg_descr lbi lib clbi = do- let uid = componentUnitId clbi- libTargetDir = componentBuildDir lbi clbi- whenVanillaLib forceVanilla =- when (forceVanilla || withVanillaLib lbi)- whenProfLib = when (withProfLib lbi)- whenSharedLib forceShared =- when (forceShared || withSharedLib lbi)- whenStaticLib forceStatic =- when (forceStatic || withStaticLib lbi)- -- whenGHCiLib = when (withGHCiLib lbi)- forRepl = maybe False (const True) mReplFlags- -- ifReplLib = when forRepl- comp = compiler lbi- implInfo = getImplInfo comp- platform@(Platform _hostArch _hostOS) = hostPlatform lbi- has_code = not (componentIsIndefinite clbi)-- (ghcjsProg, _) <- requireProgram verbosity ghcjsProgram (withPrograms lbi)- let runGhcjsProg = runGHC verbosity ghcjsProg comp platform-- let libBi = libBuildInfo lib-- -- fixme flags shouldn't depend on ghcjs being dynamic or not- let isGhcjsDynamic = isDynamic comp- dynamicTooSupported = supportsDynamicToo comp- doingTH = usesTemplateHaskellOrQQ libBi- forceVanillaLib = doingTH && not isGhcjsDynamic- forceSharedLib = doingTH && isGhcjsDynamic- -- TH always needs default libs, even when building for profiling-- -- Determine if program coverage should be enabled and if so, what- -- '-hpcdir' should be.- let isCoverageEnabled = libCoverage lbi- -- TODO: Historically HPC files have been put into a directory which- -- has the package name. I'm going to avoid changing this for- -- now, but it would probably be better for this to be the- -- component ID instead...- pkg_name = prettyShow (PD.package pkg_descr)- distPref = fromFlag $ configDistPref $ configFlags lbi- hpcdir way- | forRepl = mempty -- HPC is not supported in ghci- | isCoverageEnabled = toFlag $ Hpc.mixDir distPref way pkg_name- | otherwise = mempty-- createDirectoryIfMissingVerbose verbosity True libTargetDir- -- TODO: do we need to put hs-boot files into place for mutually recursive- -- modules?- let cLikeFiles = fromNubListR $ toNubListR (cSources libBi) <> toNubListR (cxxSources libBi)- jsSrcs = jsSources libBi- cObjs = map (`replaceExtension` objExtension) cLikeFiles- baseOpts = componentGhcOptions verbosity lbi libBi clbi libTargetDir- linkJsLibOpts = mempty {- ghcOptExtra =- [ "-link-js-lib" , getHSLibraryName uid- , "-js-lib-outputdir", libTargetDir ] ++- jsSrcs- }- vanillaOptsNoJsLib = baseOpts `mappend` mempty {- ghcOptMode = toFlag GhcModeMake,- ghcOptNumJobs = numJobs,- ghcOptInputModules = toNubListR $ allLibModules lib clbi,- ghcOptHPCDir = hpcdir Hpc.Vanilla- }- vanillaOpts = vanillaOptsNoJsLib `mappend` linkJsLibOpts-- profOpts = adjustExts "p_hi" "p_o" vanillaOpts `mappend` mempty {- ghcOptProfilingMode = toFlag True,- ghcOptProfilingAuto = Internal.profDetailLevelFlag True- (withProfLibDetail lbi),- -- ghcOptHiSuffix = toFlag "p_hi",- -- ghcOptObjSuffix = toFlag "p_o",- ghcOptExtra = hcProfOptions GHC libBi,- ghcOptHPCDir = hpcdir Hpc.Prof- }-- sharedOpts = adjustExts "dyn_hi" "dyn_o" vanillaOpts `mappend` mempty {- ghcOptDynLinkMode = toFlag GhcDynamicOnly,- ghcOptFPic = toFlag True,- -- ghcOptHiSuffix = toFlag "dyn_hi",- -- ghcOptObjSuffix = toFlag "dyn_o",- ghcOptExtra = hcSharedOptions GHC libBi,- ghcOptHPCDir = hpcdir Hpc.Dyn- }-- vanillaSharedOpts = vanillaOpts `mappend` mempty {- ghcOptDynLinkMode = toFlag GhcStaticAndDynamic,- ghcOptDynHiSuffix = toFlag "js_dyn_hi",- ghcOptDynObjSuffix = toFlag "js_dyn_o",- ghcOptHPCDir = hpcdir Hpc.Dyn- }-- unless (forRepl || null (allLibModules lib clbi) && null jsSrcs && null cObjs) $- do let vanilla = whenVanillaLib forceVanillaLib (runGhcjsProg vanillaOpts)- shared = whenSharedLib forceSharedLib (runGhcjsProg sharedOpts)- useDynToo = dynamicTooSupported &&- (forceVanillaLib || withVanillaLib lbi) &&- (forceSharedLib || withSharedLib lbi) &&- null (hcSharedOptions GHC libBi)- if not has_code- then vanilla- else- if useDynToo- then do- runGhcjsProg vanillaSharedOpts- case (hpcdir Hpc.Dyn, hpcdir Hpc.Vanilla) of- (Cabal.Flag dynDir, Cabal.Flag vanillaDir) ->- -- When the vanilla and shared library builds are done- -- in one pass, only one set of HPC module interfaces- -- are generated. This set should suffice for both- -- static and dynamically linked executables. We copy- -- the modules interfaces so they are available under- -- both ways.- copyDirectoryRecursive verbosity dynDir vanillaDir- _ -> return ()- else if isGhcjsDynamic- then do shared; vanilla- else do vanilla; shared- whenProfLib (runGhcjsProg profOpts)-- -- Build any C++ sources separately.- {-- unless (not has_code || null (cxxSources libBi) || not nativeToo) $ do- info verbosity "Building C++ Sources..."- sequence_- [ do let baseCxxOpts = Internal.componentCxxGhcOptions verbosity implInfo- lbi libBi clbi libTargetDir filename- vanillaCxxOpts = if isGhcjsDynamic- then baseCxxOpts { ghcOptFPic = toFlag True }- else baseCxxOpts- profCxxOpts = vanillaCxxOpts `mappend` mempty {- ghcOptProfilingMode = toFlag True,- ghcOptObjSuffix = toFlag "p_o"- }- sharedCxxOpts = vanillaCxxOpts `mappend` mempty {- ghcOptFPic = toFlag True,- ghcOptDynLinkMode = toFlag GhcDynamicOnly,- ghcOptObjSuffix = toFlag "dyn_o"- }- odir = fromFlag (ghcOptObjDir vanillaCxxOpts)- createDirectoryIfMissingVerbose verbosity True odir- let runGhcProgIfNeeded cxxOpts = do- needsRecomp <- checkNeedsRecompilation filename cxxOpts- when needsRecomp $ runGhcjsProg cxxOpts- runGhcProgIfNeeded vanillaCxxOpts- unless forRepl $- whenSharedLib forceSharedLib (runGhcProgIfNeeded sharedCxxOpts)- unless forRepl $ whenProfLib (runGhcProgIfNeeded profCxxOpts)- | filename <- cxxSources libBi]-- ifReplLib $ do- when (null (allLibModules lib clbi)) $ warn verbosity "No exposed modules"- ifReplLib (runGhcjsProg replOpts)--}- -- build any C sources- -- TODO: Add support for S and CMM files.- {-- unless (not has_code || null (cSources libBi) || not nativeToo) $ do- info verbosity "Building C Sources..."- sequence_- [ do let baseCcOpts = Internal.componentCcGhcOptions verbosity implInfo- lbi libBi clbi libTargetDir filename- vanillaCcOpts = if isGhcjsDynamic- -- Dynamic GHC requires C sources to be built- -- with -fPIC for REPL to work. See #2207.- then baseCcOpts { ghcOptFPic = toFlag True }- else baseCcOpts- profCcOpts = vanillaCcOpts `mappend` mempty {- ghcOptProfilingMode = toFlag True,- ghcOptObjSuffix = toFlag "p_o"- }- sharedCcOpts = vanillaCcOpts `mappend` mempty {- ghcOptFPic = toFlag True,- ghcOptDynLinkMode = toFlag GhcDynamicOnly,- ghcOptObjSuffix = toFlag "dyn_o"- }- odir = fromFlag (ghcOptObjDir vanillaCcOpts)- createDirectoryIfMissingVerbose verbosity True odir- let runGhcProgIfNeeded ccOpts = do- needsRecomp <- checkNeedsRecompilation filename ccOpts- when needsRecomp $ runGhcjsProg ccOpts- runGhcProgIfNeeded vanillaCcOpts- unless forRepl $- whenSharedLib forceSharedLib (runGhcProgIfNeeded sharedCcOpts)- unless forRepl $ whenProfLib (runGhcProgIfNeeded profCcOpts)- | filename <- cSources libBi]--}- -- TODO: problem here is we need the .c files built first, so we can load them- -- with ghci, but .c files can depend on .h files generated by ghc by ffi- -- exports.-- -- link:-- when has_code . when False {- fixme nativeToo -} . unless forRepl $ do- info verbosity "Linking..."- let cSharedObjs = map (`replaceExtension` ("dyn_" ++ objExtension))- (cSources libBi ++ cxxSources libBi)- compiler_id = compilerId (compiler lbi)- sharedLibFilePath = libTargetDir </> mkSharedLibName (hostPlatform lbi) compiler_id uid- staticLibFilePath = libTargetDir </> mkStaticLibName (hostPlatform lbi) compiler_id uid-- let stubObjs = []- stubSharedObjs = []--{-- stubObjs <- catMaybes <$> sequenceA- [ findFileWithExtension [objExtension] [libTargetDir]- (ModuleName.toFilePath x ++"_stub")- | ghcVersion < mkVersion [7,2] -- ghc-7.2+ does not make _stub.o files- , x <- allLibModules lib clbi ]- stubProfObjs <- catMaybes <$> sequenceA- [ findFileWithExtension ["p_" ++ objExtension] [libTargetDir]- (ModuleName.toFilePath x ++"_stub")- | ghcVersion < mkVersion [7,2] -- ghc-7.2+ does not make _stub.o files- , x <- allLibModules lib clbi ]- stubSharedObjs <- catMaybes <$> sequenceA- [ findFileWithExtension ["dyn_" ++ objExtension] [libTargetDir]- (ModuleName.toFilePath x ++"_stub")- | ghcVersion < mkVersion [7,2] -- ghc-7.2+ does not make _stub.o files- , x <- allLibModules lib clbi ]--}- hObjs <- Internal.getHaskellObjects implInfo lib lbi clbi- libTargetDir objExtension True- hSharedObjs <-- if withSharedLib lbi- then Internal.getHaskellObjects implInfo lib lbi clbi- libTargetDir ("dyn_" ++ objExtension) False- else return []-- unless (null hObjs && null cObjs && null stubObjs) $ do- rpaths <- getRPaths lbi clbi-- let staticObjectFiles =- hObjs- ++ map (libTargetDir </>) cObjs- ++ stubObjs- dynamicObjectFiles =- hSharedObjs- ++ map (libTargetDir </>) cSharedObjs- ++ stubSharedObjs- -- After the relocation lib is created we invoke ghc -shared- -- with the dependencies spelled out as -package arguments- -- and ghc invokes the linker with the proper library paths- ghcSharedLinkArgs =- mempty {- ghcOptShared = toFlag True,- ghcOptDynLinkMode = toFlag GhcDynamicOnly,- ghcOptInputFiles = toNubListR dynamicObjectFiles,- ghcOptOutputFile = toFlag sharedLibFilePath,- ghcOptExtra = hcSharedOptions GHC libBi,- -- For dynamic libs, Mac OS/X needs to know the install location- -- at build time. This only applies to GHC < 7.8 - see the- -- discussion in #1660.- {-- ghcOptDylibName = if hostOS == OSX- && ghcVersion < mkVersion [7,8]- then toFlag sharedLibInstallPath- else mempty, -}- ghcOptHideAllPackages = toFlag True,- ghcOptNoAutoLinkPackages = toFlag True,- ghcOptPackageDBs = withPackageDB lbi,- ghcOptThisUnitId = case clbi of- LibComponentLocalBuildInfo { componentCompatPackageKey = pk }- -> toFlag pk- _ -> mempty,- ghcOptThisComponentId = case clbi of- LibComponentLocalBuildInfo { componentInstantiatedWith = insts } ->- if null insts- then mempty- else toFlag (componentComponentId clbi)- _ -> mempty,- ghcOptInstantiatedWith = case clbi of- LibComponentLocalBuildInfo { componentInstantiatedWith = insts }- -> insts- _ -> [],- ghcOptPackages = toNubListR $- Internal.mkGhcOptPackages clbi ,- ghcOptLinkLibs = extraLibs libBi,- ghcOptLinkLibPath = toNubListR $ extraLibDirs libBi,- ghcOptLinkFrameworks = toNubListR $ PD.frameworks libBi,- ghcOptLinkFrameworkDirs =- toNubListR $ PD.extraFrameworkDirs libBi,- ghcOptRPaths = rpaths- }- ghcStaticLinkArgs =- mempty {- ghcOptStaticLib = toFlag True,- ghcOptInputFiles = toNubListR staticObjectFiles,- ghcOptOutputFile = toFlag staticLibFilePath,- ghcOptExtra = hcStaticOptions GHC libBi,- ghcOptHideAllPackages = toFlag True,- ghcOptNoAutoLinkPackages = toFlag True,- ghcOptPackageDBs = withPackageDB lbi,- ghcOptThisUnitId = case clbi of- LibComponentLocalBuildInfo { componentCompatPackageKey = pk }- -> toFlag pk- _ -> mempty,- ghcOptThisComponentId = case clbi of- LibComponentLocalBuildInfo { componentInstantiatedWith = insts } ->- if null insts- then mempty- else toFlag (componentComponentId clbi)- _ -> mempty,- ghcOptInstantiatedWith = case clbi of- LibComponentLocalBuildInfo { componentInstantiatedWith = insts }- -> insts- _ -> [],- ghcOptPackages = toNubListR $- Internal.mkGhcOptPackages clbi ,- ghcOptLinkLibs = extraLibs libBi,- ghcOptLinkLibPath = toNubListR $ extraLibDirs libBi- }-- info verbosity (show (ghcOptPackages ghcSharedLinkArgs))-{-- whenVanillaLib False $ do- Ar.createArLibArchive verbosity lbi vanillaLibFilePath staticObjectFiles- whenGHCiLib $ do- (ldProg, _) <- requireProgram verbosity ldProgram (withPrograms lbi)- Ld.combineObjectFiles verbosity lbi ldProg- ghciLibFilePath staticObjectFiles- -}-{-- whenProfLib $ do- Ar.createArLibArchive verbosity lbi profileLibFilePath profObjectFiles- whenGHCiLib $ do- (ldProg, _) <- requireProgram verbosity ldProgram (withPrograms lbi)- Ld.combineObjectFiles verbosity lbi ldProg- ghciProfLibFilePath profObjectFiles--}- whenSharedLib False $- runGhcjsProg ghcSharedLinkArgs-- whenStaticLib False $- runGhcjsProg ghcStaticLinkArgs---- | Start a REPL without loading any source files.-startInterpreter :: Verbosity -> ProgramDb -> Compiler -> Platform- -> PackageDBStack -> IO ()-startInterpreter verbosity progdb comp platform packageDBs = do- let replOpts = mempty {- ghcOptMode = toFlag GhcModeInteractive,- ghcOptPackageDBs = packageDBs- }- checkPackageDbStack verbosity packageDBs- (ghcjsProg, _) <- requireProgram verbosity ghcjsProgram progdb- runGHC verbosity ghcjsProg comp platform replOpts---- -------------------------------------------------------------------------------- Building an executable or foreign library---- | Build a foreign library-buildFLib- :: Verbosity -> Cabal.Flag (Maybe Int)- -> PackageDescription -> LocalBuildInfo- -> ForeignLib -> ComponentLocalBuildInfo -> IO ()-buildFLib v njobs pkg lbi = gbuild v njobs pkg lbi . GBuildFLib--replFLib- :: [String] -> Verbosity- -> Cabal.Flag (Maybe Int) -> PackageDescription- -> LocalBuildInfo -> ForeignLib- -> ComponentLocalBuildInfo -> IO ()-replFLib replFlags v njobs pkg lbi =- gbuild v njobs pkg lbi . GReplFLib replFlags---- | Build an executable with GHC.----buildExe- :: Verbosity -> Cabal.Flag (Maybe Int)- -> PackageDescription -> LocalBuildInfo- -> Executable -> ComponentLocalBuildInfo -> IO ()-buildExe v njobs pkg lbi = gbuild v njobs pkg lbi . GBuildExe--replExe- :: [String] -> Verbosity- -> Cabal.Flag (Maybe Int) -> PackageDescription- -> LocalBuildInfo -> Executable- -> ComponentLocalBuildInfo -> IO ()-replExe replFlags v njobs pkg lbi =- gbuild v njobs pkg lbi . GReplExe replFlags---- | Building an executable, starting the REPL, and building foreign--- libraries are all very similar and implemented in 'gbuild'. The--- 'GBuildMode' distinguishes between the various kinds of operation.-data GBuildMode =- GBuildExe Executable- | GReplExe [String] Executable- | GBuildFLib ForeignLib- | GReplFLib [String] ForeignLib--gbuildInfo :: GBuildMode -> BuildInfo-gbuildInfo (GBuildExe exe) = buildInfo exe-gbuildInfo (GReplExe _ exe) = buildInfo exe-gbuildInfo (GBuildFLib flib) = foreignLibBuildInfo flib-gbuildInfo (GReplFLib _ flib) = foreignLibBuildInfo flib--gbuildName :: GBuildMode -> String-gbuildName (GBuildExe exe) = unUnqualComponentName $ exeName exe-gbuildName (GReplExe _ exe) = unUnqualComponentName $ exeName exe-gbuildName (GBuildFLib flib) = unUnqualComponentName $ foreignLibName flib-gbuildName (GReplFLib _ flib) = unUnqualComponentName $ foreignLibName flib--gbuildTargetName :: LocalBuildInfo -> GBuildMode -> String-gbuildTargetName lbi (GBuildExe exe) = exeTargetName (hostPlatform lbi) exe-gbuildTargetName lbi (GReplExe _ exe) = exeTargetName (hostPlatform lbi) exe-gbuildTargetName lbi (GBuildFLib flib) = flibTargetName lbi flib-gbuildTargetName lbi (GReplFLib _ flib) = flibTargetName lbi flib--exeTargetName :: Platform -> Executable -> String-exeTargetName platform exe = unUnqualComponentName (exeName exe) `withExt` exeExtension platform---- | Target name for a foreign library (the actual file name)------ We do not use mkLibName and co here because the naming for foreign libraries--- is slightly different (we don't use "_p" or compiler version suffices, and we--- don't want the "lib" prefix on Windows).------ TODO: We do use `dllExtension` and co here, but really that's wrong: they--- use the OS used to build cabal to determine which extension to use, rather--- than the target OS (but this is wrong elsewhere in Cabal as well).-flibTargetName :: LocalBuildInfo -> ForeignLib -> String-flibTargetName lbi flib =- case (os, foreignLibType flib) of- (Windows, ForeignLibNativeShared) -> nm <.> "dll"- (Windows, ForeignLibNativeStatic) -> nm <.> "lib"- (Linux, ForeignLibNativeShared) -> "lib" ++ nm <.> versionedExt- (_other, ForeignLibNativeShared) -> "lib" ++ nm <.> dllExtension (hostPlatform lbi)- (_other, ForeignLibNativeStatic) -> "lib" ++ nm <.> staticLibExtension (hostPlatform lbi)- (_any, ForeignLibTypeUnknown) -> cabalBug "unknown foreign lib type"- where- nm :: String- nm = unUnqualComponentName $ foreignLibName flib-- os :: OS- os = let (Platform _ os') = hostPlatform lbi- in os'-- -- If a foreign lib foo has lib-version-info 5:1:2 or- -- lib-version-linux 3.2.1, it should be built as libfoo.so.3.2.1- -- Libtool's version-info data is translated into library versions in a- -- nontrivial way: so refer to libtool documentation.- versionedExt :: String- versionedExt =- let nums = foreignLibVersion flib os- in foldl (<.>) "so" (map show nums)---- | Name for the library when building.------ If the `lib-version-info` field or the `lib-version-linux` field of--- a foreign library target is set, we need to incorporate that--- version into the SONAME field.------ If a foreign library foo has lib-version-info 5:1:2, it should be--- built as libfoo.so.3.2.1. We want it to get soname libfoo.so.3.--- However, GHC does not allow overriding soname by setting linker--- options, as it sets a soname of its own (namely the output--- filename), after the user-supplied linker options. Hence, we have--- to compile the library with the soname as its filename. We rename--- the compiled binary afterwards.------ This method allows to adjust the name of the library at build time--- such that the correct soname can be set.-flibBuildName :: LocalBuildInfo -> ForeignLib -> String-flibBuildName lbi flib- -- On linux, if a foreign-library has version data, the first digit is used- -- to produce the SONAME.- | (os, foreignLibType flib) ==- (Linux, ForeignLibNativeShared)- = let nums = foreignLibVersion flib os- in "lib" ++ nm <.> foldl (<.>) "so" (map show (take 1 nums))- | otherwise = flibTargetName lbi flib- where- os :: OS- os = let (Platform _ os') = hostPlatform lbi- in os'-- nm :: String- nm = unUnqualComponentName $ foreignLibName flib--gbuildIsRepl :: GBuildMode -> Bool-gbuildIsRepl (GBuildExe _) = False-gbuildIsRepl (GReplExe _ _) = True-gbuildIsRepl (GBuildFLib _) = False-gbuildIsRepl (GReplFLib _ _) = True--gbuildNeedDynamic :: LocalBuildInfo -> GBuildMode -> Bool-gbuildNeedDynamic lbi bm =- case bm of- GBuildExe _ -> withDynExe lbi- GReplExe _ _ -> withDynExe lbi- GBuildFLib flib -> withDynFLib flib- GReplFLib _ flib -> withDynFLib flib- where- withDynFLib flib =- case foreignLibType flib of- ForeignLibNativeShared ->- ForeignLibStandalone `notElem` foreignLibOptions flib- ForeignLibNativeStatic ->- False- ForeignLibTypeUnknown ->- cabalBug "unknown foreign lib type"--gbuildModDefFiles :: GBuildMode -> [FilePath]-gbuildModDefFiles (GBuildExe _) = []-gbuildModDefFiles (GReplExe _ _) = []-gbuildModDefFiles (GBuildFLib flib) = foreignLibModDefFile flib-gbuildModDefFiles (GReplFLib _ flib) = foreignLibModDefFile flib---- | "Main" module name when overridden by @ghc-options: -main-is ...@--- or 'Nothing' if no @-main-is@ flag could be found.------ In case of 'Nothing', 'Distribution.ModuleName.main' can be assumed.-exeMainModuleName :: Executable -> Maybe ModuleName-exeMainModuleName Executable{buildInfo = bnfo} =- -- GHC honors the last occurrence of a module name updated via -main-is- --- -- Moreover, -main-is when parsed left-to-right can update either- -- the "Main" module name, or the "main" function name, or both,- -- see also 'decodeMainIsArg'.- msum $ reverse $ map decodeMainIsArg $ findIsMainArgs ghcopts- where- ghcopts = hcOptions GHC bnfo-- findIsMainArgs [] = []- findIsMainArgs ("-main-is":arg:rest) = arg : findIsMainArgs rest- findIsMainArgs (_:rest) = findIsMainArgs rest---- | Decode argument to '-main-is'------ Returns 'Nothing' if argument set only the function name.------ This code has been stolen/refactored from GHC's DynFlags.setMainIs--- function. The logic here is deliberately imperfect as it is--- intended to be bug-compatible with GHC's parser. See discussion in--- https://github.com/haskell/cabal/pull/4539#discussion_r118981753.-decodeMainIsArg :: String -> Maybe ModuleName-decodeMainIsArg arg- | headOf main_fn isLower- -- The arg looked like "Foo.Bar.baz"- = Just (ModuleName.fromString main_mod)- | headOf arg isUpper -- The arg looked like "Foo" or "Foo.Bar"- = Just (ModuleName.fromString arg)- | otherwise -- The arg looked like "baz"- = Nothing- where- headOf :: String -> (Char -> Bool) -> Bool- headOf str pred' = any pred' (safeHead str)-- (main_mod, main_fn) = splitLongestPrefix arg (== '.')-- splitLongestPrefix :: String -> (Char -> Bool) -> (String,String)- splitLongestPrefix str pred'- | null r_pre = (str, [])- | otherwise = (reverse (safeTail r_pre), reverse r_suf)- -- 'safeTail' drops the char satisfying 'pred'- where (r_suf, r_pre) = break pred' (reverse str)----- | A collection of:--- * C input files--- * C++ input files--- * GHC input files--- * GHC input modules------ Used to correctly build and link sources.-data BuildSources = BuildSources {- cSourcesFiles :: [FilePath],- cxxSourceFiles :: [FilePath],- inputSourceFiles :: [FilePath],- inputSourceModules :: [ModuleName]- }---- | Locate and return the 'BuildSources' required to build and link.-gbuildSources :: Verbosity- -> CabalSpecVersion- -> FilePath- -> GBuildMode- -> IO BuildSources-gbuildSources verbosity specVer tmpDir bm =- case bm of- GBuildExe exe -> exeSources exe- GReplExe _ exe -> exeSources exe- GBuildFLib flib -> return $ flibSources flib- GReplFLib _ flib -> return $ flibSources flib- where- exeSources :: Executable -> IO BuildSources- exeSources exe@Executable{buildInfo = bnfo, modulePath = modPath} = do- main <- findFileEx verbosity (tmpDir : map getSymbolicPath (hsSourceDirs bnfo)) modPath- let mainModName = fromMaybe ModuleName.main $ exeMainModuleName exe- otherModNames = exeModules exe-- if isHaskell main- then- if specVer < CabalSpecV2_0 && (mainModName `elem` otherModNames)- then do- -- The cabal manual clearly states that `other-modules` is- -- intended for non-main modules. However, there's at least one- -- important package on Hackage (happy-1.19.5) which- -- violates this. We workaround this here so that we don't- -- invoke GHC with e.g. 'ghc --make Main src/Main.hs' which- -- would result in GHC complaining about duplicate Main- -- modules.- --- -- Finally, we only enable this workaround for- -- specVersion < 2, as 'cabal-version:>=2.0' cabal files- -- have no excuse anymore to keep doing it wrong... ;-)- warn verbosity $ "Enabling workaround for Main module '"- ++ prettyShow mainModName- ++ "' listed in 'other-modules' illegally!"-- return BuildSources {- cSourcesFiles = cSources bnfo,- cxxSourceFiles = cxxSources bnfo,- inputSourceFiles = [main],- inputSourceModules = filter (/= mainModName) $ exeModules exe- }-- else return BuildSources {- cSourcesFiles = cSources bnfo,- cxxSourceFiles = cxxSources bnfo,- inputSourceFiles = [main],- inputSourceModules = exeModules exe- }- else let (csf, cxxsf)- | isCxx main = ( cSources bnfo, main : cxxSources bnfo)- -- if main is not a Haskell source- -- and main is not a C++ source- -- then we assume that it is a C source- | otherwise = (main : cSources bnfo, cxxSources bnfo)-- in return BuildSources {- cSourcesFiles = csf,- cxxSourceFiles = cxxsf,- inputSourceFiles = [],- inputSourceModules = exeModules exe- }-- flibSources :: ForeignLib -> BuildSources- flibSources flib@ForeignLib{foreignLibBuildInfo = bnfo} =- BuildSources {- cSourcesFiles = cSources bnfo,- cxxSourceFiles = cxxSources bnfo,- inputSourceFiles = [],- inputSourceModules = foreignLibModules flib- }-- isHaskell :: FilePath -> Bool- isHaskell fp = elem (takeExtension fp) [".hs", ".lhs"]-- isCxx :: FilePath -> Bool- isCxx fp = elem (takeExtension fp) [".cpp", ".cxx", ".c++"]---- | Generic build function. See comment for 'GBuildMode'.-gbuild :: Verbosity -> Cabal.Flag (Maybe Int)- -> PackageDescription -> LocalBuildInfo- -> GBuildMode -> ComponentLocalBuildInfo -> IO ()-gbuild verbosity numJobs pkg_descr lbi bm clbi = do- (ghcjsProg, _) <- requireProgram verbosity ghcjsProgram (withPrograms lbi)- let replFlags = case bm of- GReplExe flags _ -> flags- GReplFLib flags _ -> flags- GBuildExe{} -> mempty- GBuildFLib{} -> mempty- comp = compiler lbi- platform = hostPlatform lbi- implInfo = getImplInfo comp- runGhcProg = runGHC verbosity ghcjsProg comp platform-- let (bnfo, threaded) = case bm of- GBuildFLib _ -> popThreadedFlag (gbuildInfo bm)- _ -> (gbuildInfo bm, False)-- -- the name that GHC really uses (e.g., with .exe on Windows for executables)- let targetName = gbuildTargetName lbi bm- let targetDir = buildDir lbi </> (gbuildName bm)- let tmpDir = targetDir </> (gbuildName bm ++ "-tmp")- createDirectoryIfMissingVerbose verbosity True targetDir- createDirectoryIfMissingVerbose verbosity True tmpDir-- -- TODO: do we need to put hs-boot files into place for mutually recursive- -- modules? FIX: what about exeName.hi-boot?-- -- Determine if program coverage should be enabled and if so, what- -- '-hpcdir' should be.- let isCoverageEnabled = exeCoverage lbi- distPref = fromFlag $ configDistPref $ configFlags lbi- hpcdir way- | gbuildIsRepl bm = mempty -- HPC is not supported in ghci- | isCoverageEnabled = toFlag $ Hpc.mixDir distPref way (gbuildName bm)- | otherwise = mempty-- rpaths <- getRPaths lbi clbi- buildSources <- gbuildSources verbosity (specVersion pkg_descr) tmpDir bm-- let cSrcs = cSourcesFiles buildSources- cxxSrcs = cxxSourceFiles buildSources- inputFiles = inputSourceFiles buildSources- inputModules = inputSourceModules buildSources- isGhcDynamic = isDynamic comp- dynamicTooSupported = supportsDynamicToo comp- cObjs = map (`replaceExtension` objExtension) cSrcs- cxxObjs = map (`replaceExtension` objExtension) cxxSrcs- needDynamic = gbuildNeedDynamic lbi bm- needProfiling = withProfExe lbi-- -- build executables- baseOpts = (componentGhcOptions verbosity lbi bnfo clbi tmpDir)- `mappend` mempty {- ghcOptMode = toFlag GhcModeMake,- ghcOptInputFiles = toNubListR inputFiles,- ghcOptInputModules = toNubListR inputModules- }- staticOpts = baseOpts `mappend` mempty {- ghcOptDynLinkMode = toFlag GhcStaticOnly,- ghcOptHPCDir = hpcdir Hpc.Vanilla- }- profOpts = baseOpts `mappend` mempty {- ghcOptProfilingMode = toFlag True,- ghcOptProfilingAuto = Internal.profDetailLevelFlag False- (withProfExeDetail lbi),- ghcOptHiSuffix = toFlag "p_hi",- ghcOptObjSuffix = toFlag "p_o",- ghcOptExtra = hcProfOptions GHC bnfo,- ghcOptHPCDir = hpcdir Hpc.Prof- }- dynOpts = baseOpts `mappend` mempty {- ghcOptDynLinkMode = toFlag GhcDynamicOnly,- -- TODO: Does it hurt to set -fPIC for executables?- ghcOptFPic = toFlag True,- ghcOptHiSuffix = toFlag "dyn_hi",- ghcOptObjSuffix = toFlag "dyn_o",- ghcOptExtra = hcSharedOptions GHC bnfo,- ghcOptHPCDir = hpcdir Hpc.Dyn- }- dynTooOpts = staticOpts `mappend` mempty {- ghcOptDynLinkMode = toFlag GhcStaticAndDynamic,- ghcOptDynHiSuffix = toFlag "dyn_hi",- ghcOptDynObjSuffix = toFlag "dyn_o",- ghcOptHPCDir = hpcdir Hpc.Dyn- }- linkerOpts = mempty {- ghcOptLinkOptions = PD.ldOptions bnfo,- ghcOptLinkLibs = extraLibs bnfo,- ghcOptLinkLibPath = toNubListR $ extraLibDirs bnfo,- ghcOptLinkFrameworks = toNubListR $- PD.frameworks bnfo,- ghcOptLinkFrameworkDirs = toNubListR $- PD.extraFrameworkDirs bnfo,- ghcOptInputFiles = toNubListR- [tmpDir </> x | x <- cObjs ++ cxxObjs]- }- dynLinkerOpts = mempty {- ghcOptRPaths = rpaths- }- replOpts = baseOpts {- ghcOptExtra = Internal.filterGhciFlags- (ghcOptExtra baseOpts)- <> replFlags- }- -- For a normal compile we do separate invocations of ghc for- -- compiling as for linking. But for repl we have to do just- -- the one invocation, so that one has to include all the- -- linker stuff too, like -l flags and any .o files from C- -- files etc.- `mappend` linkerOpts- `mappend` mempty {- ghcOptMode = toFlag GhcModeInteractive,- ghcOptOptimisation = toFlag GhcNoOptimisation- }- commonOpts | needProfiling = profOpts- | needDynamic = dynOpts- | otherwise = staticOpts- compileOpts | useDynToo = dynTooOpts- | otherwise = commonOpts- withStaticExe = not needProfiling && not needDynamic-- -- For building exe's that use TH with -prof or -dynamic we actually have- -- to build twice, once without -prof/-dynamic and then again with- -- -prof/-dynamic. This is because the code that TH needs to run at- -- compile time needs to be the vanilla ABI so it can be loaded up and run- -- by the compiler.- -- With dynamic-by-default GHC the TH object files loaded at compile-time- -- need to be .dyn_o instead of .o.- doingTH = usesTemplateHaskellOrQQ bnfo- -- Should we use -dynamic-too instead of compiling twice?- useDynToo = dynamicTooSupported && isGhcDynamic- && doingTH && withStaticExe- && null (hcSharedOptions GHC bnfo)- compileTHOpts | isGhcDynamic = dynOpts- | otherwise = staticOpts- compileForTH- | gbuildIsRepl bm = False- | useDynToo = False- | isGhcDynamic = doingTH && (needProfiling || withStaticExe)- | otherwise = doingTH && (needProfiling || needDynamic)-- -- Build static/dynamic object files for TH, if needed.- when compileForTH $- runGhcProg compileTHOpts { ghcOptNoLink = toFlag True- , ghcOptNumJobs = numJobs }-- -- Do not try to build anything if there are no input files.- -- This can happen if the cabal file ends up with only cSrcs- -- but no Haskell modules.- unless ((null inputFiles && null inputModules)- || gbuildIsRepl bm) $- runGhcProg compileOpts { ghcOptNoLink = toFlag True- , ghcOptNumJobs = numJobs }-- -- build any C++ sources- unless (null cxxSrcs) $ do- info verbosity "Building C++ Sources..."- sequence_- [ do let baseCxxOpts = Internal.componentCxxGhcOptions verbosity implInfo- lbi bnfo clbi tmpDir filename- vanillaCxxOpts = if isGhcDynamic- -- Dynamic GHC requires C++ sources to be built- -- with -fPIC for REPL to work. See #2207.- then baseCxxOpts { ghcOptFPic = toFlag True }- else baseCxxOpts- profCxxOpts = vanillaCxxOpts `mappend` mempty {- ghcOptProfilingMode = toFlag True- }- sharedCxxOpts = vanillaCxxOpts `mappend` mempty {- ghcOptFPic = toFlag True,- ghcOptDynLinkMode = toFlag GhcDynamicOnly- }- opts | needProfiling = profCxxOpts- | needDynamic = sharedCxxOpts- | otherwise = vanillaCxxOpts- -- TODO: Placing all Haskell, C, & C++ objects in a single directory- -- Has the potential for file collisions. In general we would- -- consider this a user error. However, we should strive to- -- add a warning if this occurs.- odir = fromFlag (ghcOptObjDir opts)- createDirectoryIfMissingVerbose verbosity True odir- needsRecomp <- checkNeedsRecompilation filename opts- when needsRecomp $- runGhcProg opts- | filename <- cxxSrcs ]-- -- build any C sources- unless (null cSrcs) $ do- info verbosity "Building C Sources..."- sequence_- [ do let baseCcOpts = Internal.componentCcGhcOptions verbosity implInfo- lbi bnfo clbi tmpDir filename- vanillaCcOpts = if isGhcDynamic- -- Dynamic GHC requires C sources to be built- -- with -fPIC for REPL to work. See #2207.- then baseCcOpts { ghcOptFPic = toFlag True }- else baseCcOpts- profCcOpts = vanillaCcOpts `mappend` mempty {- ghcOptProfilingMode = toFlag True- }- sharedCcOpts = vanillaCcOpts `mappend` mempty {- ghcOptFPic = toFlag True,- ghcOptDynLinkMode = toFlag GhcDynamicOnly- }- opts | needProfiling = profCcOpts- | needDynamic = sharedCcOpts- | otherwise = vanillaCcOpts- odir = fromFlag (ghcOptObjDir opts)- createDirectoryIfMissingVerbose verbosity True odir- needsRecomp <- checkNeedsRecompilation filename opts- when needsRecomp $- runGhcProg opts- | filename <- cSrcs ]-- -- TODO: problem here is we need the .c files built first, so we can load them- -- with ghci, but .c files can depend on .h files generated by ghc by ffi- -- exports.- case bm of- GReplExe _ _ -> runGhcProg replOpts- GReplFLib _ _ -> runGhcProg replOpts- GBuildExe _ -> do- let linkOpts = commonOpts- `mappend` linkerOpts- `mappend` mempty {- ghcOptLinkNoHsMain = toFlag (null inputFiles)- }- `mappend` (if withDynExe lbi then dynLinkerOpts else mempty)-- info verbosity "Linking..."- -- Work around old GHCs not relinking in this- -- situation, see #3294- let target = targetDir </> targetName- when (compilerVersion comp < mkVersion [7,7]) $ do- e <- doesFileExist target- when e (removeFile target)- runGhcProg linkOpts { ghcOptOutputFile = toFlag target }- GBuildFLib flib -> do- let rtsInfo = extractRtsInfo lbi- rtsOptLinkLibs = [- if needDynamic- then if threaded- then dynRtsThreadedLib (rtsDynamicInfo rtsInfo)- else dynRtsVanillaLib (rtsDynamicInfo rtsInfo)- else if threaded- then statRtsThreadedLib (rtsStaticInfo rtsInfo)- else statRtsVanillaLib (rtsStaticInfo rtsInfo)- ]- linkOpts = case foreignLibType flib of- ForeignLibNativeShared ->- commonOpts- `mappend` linkerOpts- `mappend` dynLinkerOpts- `mappend` mempty {- ghcOptLinkNoHsMain = toFlag True,- ghcOptShared = toFlag True,- ghcOptLinkLibs = rtsOptLinkLibs,- ghcOptLinkLibPath = toNubListR $ rtsLibPaths rtsInfo,- ghcOptFPic = toFlag True,- ghcOptLinkModDefFiles = toNubListR $ gbuildModDefFiles bm- }- -- See Note [RPATH]- `mappend` ifNeedsRPathWorkaround lbi mempty {- ghcOptLinkOptions = ["-Wl,--no-as-needed"]- , ghcOptLinkLibs = ["ffi"]- }- ForeignLibNativeStatic ->- -- this should be caught by buildFLib- -- (and if we do implement this, we probably don't even want to call- -- ghc here, but rather Ar.createArLibArchive or something)- cabalBug "static libraries not yet implemented"- ForeignLibTypeUnknown ->- cabalBug "unknown foreign lib type"- -- We build under a (potentially) different filename to set a- -- soname on supported platforms. See also the note for- -- @flibBuildName@.- info verbosity "Linking..."- let buildName = flibBuildName lbi flib- runGhcProg linkOpts { ghcOptOutputFile = toFlag (targetDir </> buildName) }- renameFile (targetDir </> buildName) (targetDir </> targetName)--{--Note [RPATH]-~~~~~~~~~~~~--Suppose that the dynamic library depends on `base`, but not (directly) on-`integer-gmp` (which, however, is a dependency of `base`). We will link the-library as-- gcc ... -lHSbase-4.7.0.2-ghc7.8.4 -lHSinteger-gmp-0.5.1.0-ghc7.8.4 ...--However, on systems (like Ubuntu) where the linker gets called with `-as-needed`-by default, the linker will notice that `integer-gmp` isn't actually a direct-dependency and hence omit the link.--Then when we attempt to link a C program against this dynamic library, the-_static_ linker will attempt to verify that all symbols can be resolved. The-dynamic library itself does not require any symbols from `integer-gmp`, but-`base` does. In order to verify that the symbols used by `base` can be-resolved, the static linker needs to be able to _find_ integer-gmp.--Finding the `base` dependency is simple, because the dynamic elf header-(`readelf -d`) for the library that we have created looks something like-- (NEEDED) Shared library: [libHSbase-4.7.0.2-ghc7.8.4.so]- (RPATH) Library rpath: [/path/to/base-4.7.0.2:...]--However, when it comes to resolving the dependency on `integer-gmp`, it needs-to look at the dynamic header for `base`. On modern ghc (7.8 and higher) this-looks something like-- (NEEDED) Shared library: [libHSinteger-gmp-0.5.1.0-ghc7.8.4.so]- (RPATH) Library rpath: [$ORIGIN/../integer-gmp-0.5.1.0:...]--This specifies the location of `integer-gmp` _in terms of_ the location of base-(using the `$ORIGIN`) variable. But here's the crux: when the static linker-attempts to verify that all symbols can be resolved, [**IT DOES NOT RESOLVE-`$ORIGIN`**](http://stackoverflow.com/questions/6323603/ld-using-rpath-origin-inside-a-shared-library-recursive).-As a consequence, it will not be able to resolve the symbols and report the-missing symbols as errors, _even though the dynamic linker **would** be able to-resolve these symbols_. We can tell the static linker not to report these-errors by using `--unresolved-symbols=ignore-all` and all will be fine when we-run the program ([(indeed, this is what the gold linker-does)](https://sourceware.org/ml/binutils/2013-05/msg00038.html), but it makes-the resulting library more difficult to use.--Instead what we can do is make sure that the generated dynamic library has-explicit top-level dependencies on these libraries. This means that the static-linker knows where to find them, and when we have transitive dependencies on-the same libraries the linker will only load them once, so we avoid needing to-look at the `RPATH` of our dependencies. We can do this by passing-`--no-as-needed` to the linker, so that it doesn't omit any libraries.--Note that on older ghc (7.6 and before) the Haskell libraries don't have an-RPATH set at all, which makes it even more important that we make these-top-level dependencies.--Finally, we have to explicitly link against `libffi` for the same reason. For-newer ghc this _happens_ to be unnecessary on many systems because `libffi` is-a library which is not specific to GHC, and when the static linker verifies-that all symbols can be resolved it will find the `libffi` that is globally-installed (completely independent from ghc). Of course, this may well be the-_wrong_ version of `libffi`, but it's quite possible that symbol resolution-happens to work. This is of course the wrong approach, which is why we link-explicitly against `libffi` so that we will find the _right_ version of-`libffi`.--}---- | Do we need the RPATH workaround?------ See Note [RPATH].-ifNeedsRPathWorkaround :: Monoid a => LocalBuildInfo -> a -> a-ifNeedsRPathWorkaround lbi a =- case hostPlatform lbi of- Platform _ Linux -> a- _otherwise -> mempty--data DynamicRtsInfo = DynamicRtsInfo {- dynRtsVanillaLib :: FilePath- , dynRtsThreadedLib :: FilePath- , dynRtsDebugLib :: FilePath- , dynRtsEventlogLib :: FilePath- , dynRtsThreadedDebugLib :: FilePath- , dynRtsThreadedEventlogLib :: FilePath- }--data StaticRtsInfo = StaticRtsInfo {- statRtsVanillaLib :: FilePath- , statRtsThreadedLib :: FilePath- , statRtsDebugLib :: FilePath- , statRtsEventlogLib :: FilePath- , statRtsThreadedDebugLib :: FilePath- , statRtsThreadedEventlogLib :: FilePath- , statRtsProfilingLib :: FilePath- , statRtsThreadedProfilingLib :: FilePath- }--data RtsInfo = RtsInfo {- rtsDynamicInfo :: DynamicRtsInfo- , rtsStaticInfo :: StaticRtsInfo- , rtsLibPaths :: [FilePath]- }---- | Extract (and compute) information about the RTS library------ TODO: This hardcodes the name as @HSrts-ghc<version>@. I don't know if we can--- find this information somewhere. We can lookup the 'hsLibraries' field of--- 'InstalledPackageInfo' but it will tell us @["HSrts", "Cffi"]@, which--- doesn't really help.-extractRtsInfo :: LocalBuildInfo -> RtsInfo-extractRtsInfo lbi =- case PackageIndex.lookupPackageName (installedPkgs lbi) (mkPackageName "rts") of- [(_, [rts])] -> aux rts- _otherwise -> error "No (or multiple) ghc rts package is registered"- where- aux :: InstalledPackageInfo -> RtsInfo- aux rts = RtsInfo {- rtsDynamicInfo = DynamicRtsInfo {- dynRtsVanillaLib = withGhcVersion "HSrts"- , dynRtsThreadedLib = withGhcVersion "HSrts_thr"- , dynRtsDebugLib = withGhcVersion "HSrts_debug"- , dynRtsEventlogLib = withGhcVersion "HSrts_l"- , dynRtsThreadedDebugLib = withGhcVersion "HSrts_thr_debug"- , dynRtsThreadedEventlogLib = withGhcVersion "HSrts_thr_l"- }- , rtsStaticInfo = StaticRtsInfo {- statRtsVanillaLib = "HSrts"- , statRtsThreadedLib = "HSrts_thr"- , statRtsDebugLib = "HSrts_debug"- , statRtsEventlogLib = "HSrts_l"- , statRtsThreadedDebugLib = "HSrts_thr_debug"- , statRtsThreadedEventlogLib = "HSrts_thr_l"- , statRtsProfilingLib = "HSrts_p"- , statRtsThreadedProfilingLib = "HSrts_thr_p"- }- , rtsLibPaths = InstalledPackageInfo.libraryDirs rts- }- withGhcVersion = (++ ("-ghc" ++ prettyShow (compilerVersion (compiler lbi))))---- | Returns True if the modification date of the given source file is newer than--- the object file we last compiled for it, or if no object file exists yet.-checkNeedsRecompilation :: FilePath -> GhcOptions -> IO Bool-checkNeedsRecompilation filename opts = filename `moreRecentFile` oname- where oname = getObjectFileName filename opts---- | Finds the object file name of the given source file-getObjectFileName :: FilePath -> GhcOptions -> FilePath-getObjectFileName filename opts = oname- where odir = fromFlag (ghcOptObjDir opts)- oext = fromFlagOrDefault "o" (ghcOptObjSuffix opts)- oname = odir </> replaceExtension filename oext---- | Calculate the RPATHs for the component we are building.------ Calculates relative RPATHs when 'relocatable' is set.-getRPaths :: LocalBuildInfo- -> ComponentLocalBuildInfo -- ^ Component we are building- -> IO (NubListR FilePath)-getRPaths lbi clbi | supportRPaths hostOS = do- libraryPaths <- depLibraryPaths False (relocatable lbi) lbi clbi- let hostPref = case hostOS of- OSX -> "@loader_path"- _ -> "$ORIGIN"- relPath p = if isRelative p then hostPref </> p else p- rpaths = toNubListR (map relPath libraryPaths)- return rpaths- where- (Platform _ hostOS) = hostPlatform lbi- compid = compilerId . compiler $ lbi-- -- The list of RPath-supported operating systems below reflects the- -- platforms on which Cabal's RPATH handling is tested. It does _NOT_- -- reflect whether the OS supports RPATH.-- -- E.g. when this comment was written, the *BSD operating systems were- -- untested with regards to Cabal RPATH handling, and were hence set to- -- 'False', while those operating systems themselves do support RPATH.- supportRPaths Linux = True- supportRPaths Windows = False- supportRPaths OSX = True- supportRPaths FreeBSD =- case compid of- CompilerId GHC ver | ver >= mkVersion [7,10,2] -> True- _ -> False- supportRPaths OpenBSD = False- supportRPaths NetBSD = False- supportRPaths DragonFly = False- supportRPaths Solaris = False- supportRPaths AIX = False- supportRPaths HPUX = False- supportRPaths IRIX = False- supportRPaths HaLVM = False- supportRPaths IOS = False- supportRPaths Android = False- supportRPaths Ghcjs = False- supportRPaths Hurd = False- supportRPaths (OtherOS _) = False- -- Do _not_ add a default case so that we get a warning here when a new OS- -- is added.--getRPaths _ _ = return mempty---- | Remove the "-threaded" flag when building a foreign library, as it has no--- effect when used with "-shared". Returns the updated 'BuildInfo', along--- with whether or not the flag was present, so we can use it to link against--- the appropriate RTS on our own.-popThreadedFlag :: BuildInfo -> (BuildInfo, Bool)-popThreadedFlag bi =- ( bi { options = filterHcOptions (/= "-threaded") (options bi) }- , hasThreaded (options bi))-- where- filterHcOptions :: (String -> Bool)- -> PerCompilerFlavor [String]- -> PerCompilerFlavor [String]- filterHcOptions p (PerCompilerFlavor ghc ghcjs) =- PerCompilerFlavor (filter p ghc) ghcjs-- hasThreaded :: PerCompilerFlavor [String] -> Bool- hasThreaded (PerCompilerFlavor ghc _) = elem "-threaded" ghc---- | Extracts a String representing a hash of the ABI of a built--- library. It can fail if the library has not yet been built.----libAbiHash :: Verbosity -> PackageDescription -> LocalBuildInfo- -> Library -> ComponentLocalBuildInfo -> IO String-libAbiHash verbosity _pkg_descr lbi lib clbi = do- let- libBi = libBuildInfo lib- comp = compiler lbi- platform = hostPlatform lbi- vanillaArgs0 =- (componentGhcOptions verbosity lbi libBi clbi (componentBuildDir lbi clbi))- `mappend` mempty {- ghcOptMode = toFlag GhcModeAbiHash,- ghcOptInputModules = toNubListR $ exposedModules lib- }- vanillaArgs =- -- Package DBs unnecessary, and break ghc-cabal. See #3633- -- BUT, put at least the global database so that 7.4 doesn't- -- break.- vanillaArgs0 { ghcOptPackageDBs = [GlobalPackageDB]- , ghcOptPackages = mempty }- sharedArgs = vanillaArgs `mappend` mempty {- ghcOptDynLinkMode = toFlag GhcDynamicOnly,- ghcOptFPic = toFlag True,- ghcOptHiSuffix = toFlag "js_dyn_hi",- ghcOptObjSuffix = toFlag "js_dyn_o",- ghcOptExtra = hcSharedOptions GHC libBi- }- profArgs = vanillaArgs `mappend` mempty {- ghcOptProfilingMode = toFlag True,- ghcOptProfilingAuto = Internal.profDetailLevelFlag True- (withProfLibDetail lbi),- ghcOptHiSuffix = toFlag "js_p_hi",- ghcOptObjSuffix = toFlag "js_p_o",- ghcOptExtra = hcProfOptions GHC libBi- }- ghcArgs- | withVanillaLib lbi = vanillaArgs- | withSharedLib lbi = sharedArgs- | withProfLib lbi = profArgs- | otherwise = error "libAbiHash: Can't find an enabled library way"-- (ghcjsProg, _) <- requireProgram verbosity ghcjsProgram (withPrograms lbi)- hash <- getProgramInvocationOutput verbosity- (ghcInvocation ghcjsProg comp platform ghcArgs)- return (takeWhile (not . isSpace) hash)--componentGhcOptions :: Verbosity -> LocalBuildInfo- -> BuildInfo -> ComponentLocalBuildInfo -> FilePath- -> GhcOptions-componentGhcOptions verbosity lbi bi clbi odir =- let opts = Internal.componentGhcOptions verbosity implInfo lbi bi clbi odir- comp = compiler lbi- implInfo = getImplInfo comp- in opts { ghcOptExtra = ghcOptExtra opts `mappend` hcOptions GHCJS bi- }---componentCcGhcOptions :: Verbosity -> LocalBuildInfo- -> BuildInfo -> ComponentLocalBuildInfo- -> FilePath -> FilePath- -> GhcOptions-componentCcGhcOptions verbosity lbi =- Internal.componentCcGhcOptions verbosity implInfo lbi- where- comp = compiler lbi- implInfo = getImplInfo comp----- -------------------------------------------------------------------------------- Installing---- |Install executables for GHCJS.-installExe :: Verbosity- -> LocalBuildInfo- -> FilePath -- ^Where to copy the files to- -> FilePath -- ^Build location- -> (FilePath, FilePath) -- ^Executable (prefix,suffix)- -> PackageDescription- -> Executable- -> IO ()-installExe verbosity lbi binDir buildPref- (progprefix, progsuffix) _pkg exe = do- createDirectoryIfMissingVerbose verbosity True binDir- let exeName' = unUnqualComponentName $ exeName exe- exeFileName = exeName'- fixedExeBaseName = progprefix ++ exeName' ++ progsuffix- installBinary dest = do- runDbProgram verbosity ghcjsProgram (withPrograms lbi) $- [ "--install-executable"- , buildPref </> exeName' </> exeFileName- , "-o", dest- ] ++- case (stripExes lbi, lookupProgram stripProgram $ withPrograms lbi) of- (True, Just strip) -> ["-strip-program", programPath strip]- _ -> []- installBinary (binDir </> fixedExeBaseName)----- |Install foreign library for GHC.-installFLib :: Verbosity- -> LocalBuildInfo- -> FilePath -- ^install location- -> FilePath -- ^Build location- -> PackageDescription- -> ForeignLib- -> IO ()-installFLib verbosity lbi targetDir builtDir _pkg flib =- install (foreignLibIsShared flib)- builtDir- targetDir- (flibTargetName lbi flib)- where- install _isShared srcDir dstDir name = do- let src = srcDir </> name- dst = dstDir </> name- createDirectoryIfMissingVerbose verbosity True targetDir- installOrdinaryFile verbosity src dst----- |Install for ghc, .hi, .a and, if --with-ghci given, .o-installLib :: Verbosity- -> LocalBuildInfo- -> FilePath -- ^install location- -> FilePath -- ^install location for dynamic libraries- -> FilePath -- ^Build location- -> PackageDescription- -> Library- -> ComponentLocalBuildInfo- -> IO ()-installLib verbosity lbi targetDir dynlibTargetDir _builtDir _pkg lib clbi = do- whenVanilla $ copyModuleFiles "js_hi"- whenProf $ copyModuleFiles "js_p_hi"- whenShared $ copyModuleFiles "js_dyn_hi"-- -- whenVanilla $ installOrdinary builtDir targetDir $ toJSLibName vanillaLibName- -- whenProf $ installOrdinary builtDir targetDir $ toJSLibName profileLibName- -- whenShared $ installShared builtDir dynlibTargetDir $ toJSLibName sharedLibName- -- fixme do these make the correct lib names?- whenHasCode $ do- whenVanilla $ do- sequence_ [ installOrdinary builtDir' targetDir (toJSLibName $ mkGenericStaticLibName (l ++ f))- | l <- getHSLibraryName (componentUnitId clbi):(extraBundledLibs (libBuildInfo lib))- , f <- "":extraLibFlavours (libBuildInfo lib)- ]- -- whenGHCi $ installOrdinary builtDir targetDir (toJSLibName ghciLibName)- whenProf $ do- installOrdinary builtDir' targetDir (toJSLibName profileLibName)- -- whenGHCi $ installOrdinary builtDir targetDir (toJSLibName ghciProfLibName)- whenShared $- sequence_ [ installShared builtDir' dynlibTargetDir- (toJSLibName $ mkGenericSharedLibName platform compiler_id (l ++ f))- | l <- getHSLibraryName uid : extraBundledLibs (libBuildInfo lib)- , f <- "":extraDynLibFlavours (libBuildInfo lib)- ]- where- builtDir' = componentBuildDir lbi clbi-- install isShared isJS srcDir dstDir name = do- let src = srcDir </> name- dst = dstDir </> name- createDirectoryIfMissingVerbose verbosity True dstDir-- if isShared- then installExecutableFile verbosity src dst- else installOrdinaryFile verbosity src dst-- when (stripLibs lbi && not isJS) $- Strip.stripLib verbosity- (hostPlatform lbi) (withPrograms lbi) dst-- installOrdinary = install False True- installShared = install True True-- copyModuleFiles ext =- findModuleFilesEx verbosity [builtDir'] [ext] (allLibModules lib clbi)- >>= installOrdinaryFiles verbosity targetDir-- compiler_id = compilerId (compiler lbi)- platform = hostPlatform lbi- uid = componentUnitId clbi- -- vanillaLibName = mkLibName uid- profileLibName = mkProfLibName uid- -- sharedLibName = (mkSharedLibName (hostPlatform lbi) compiler_id) uid-- hasLib = not $ null (allLibModules lib clbi)- && null (cSources (libBuildInfo lib))- && null (cxxSources (libBuildInfo lib))- && null (jsSources (libBuildInfo lib))- has_code = not (componentIsIndefinite clbi)- whenHasCode = when has_code- whenVanilla = when (hasLib && withVanillaLib lbi)- whenProf = when (hasLib && withProfLib lbi && has_code)- -- whenGHCi = when (hasLib && withGHCiLib lbi && has_code)- whenShared = when (hasLib && withSharedLib lbi && has_code)---adjustExts :: String -> String -> GhcOptions -> GhcOptions-adjustExts hiSuf objSuf opts =- opts `mappend` mempty {- ghcOptHiSuffix = toFlag hiSuf,- ghcOptObjSuffix = toFlag objSuf- }--isDynamic :: Compiler -> Bool-isDynamic = Internal.ghcLookupProperty "GHC Dynamic"--supportsDynamicToo :: Compiler -> Bool-supportsDynamicToo = Internal.ghcLookupProperty "Support dynamic-too"--withExt :: FilePath -> String -> FilePath-withExt fp ext = fp <.> if takeExtension fp /= ('.':ext) then ext else ""--findGhcjsGhcVersion :: Verbosity -> FilePath -> IO (Maybe Version)-findGhcjsGhcVersion verbosity pgm =- findProgramVersion "--numeric-ghc-version" id verbosity pgm--findGhcjsPkgGhcjsVersion :: Verbosity -> FilePath -> IO (Maybe Version)-findGhcjsPkgGhcjsVersion verbosity pgm =- findProgramVersion "--numeric-ghcjs-version" id verbosity pgm---- -------------------------------------------------------------------------------- Registering--hcPkgInfo :: ProgramDb -> HcPkg.HcPkgInfo-hcPkgInfo progdb = HcPkg.HcPkgInfo { HcPkg.hcPkgProgram = ghcjsPkgProg- , HcPkg.noPkgDbStack = False- , HcPkg.noVerboseFlag = False- , HcPkg.flagPackageConf = False- , HcPkg.supportsDirDbs = True- , HcPkg.requiresDirDbs = ver >= v7_10- , HcPkg.nativeMultiInstance = ver >= v7_10- , HcPkg.recacheMultiInstance = True- , HcPkg.suppressFilesCheck = True- }- where- v7_10 = mkVersion [7,10]- ghcjsPkgProg = fromMaybe (error "GHCJS.hcPkgInfo no ghcjs program") $ lookupProgram ghcjsPkgProgram progdb- ver = fromMaybe (error "GHCJS.hcPkgInfo no ghcjs version") $ programVersion ghcjsPkgProg--registerPackage- :: Verbosity- -> ProgramDb- -> PackageDBStack- -> InstalledPackageInfo- -> HcPkg.RegisterOptions- -> IO ()-registerPackage verbosity progdb packageDbs installedPkgInfo registerOptions =- HcPkg.register (hcPkgInfo progdb) verbosity packageDbs- installedPkgInfo registerOptions--pkgRoot :: Verbosity -> LocalBuildInfo -> PackageDB -> IO FilePath-pkgRoot verbosity lbi = pkgRoot'- where- pkgRoot' GlobalPackageDB =- let ghcjsProg = fromMaybe (error "GHCJS.pkgRoot: no ghcjs program") $ lookupProgram ghcjsProgram (withPrograms lbi)- in fmap takeDirectory (getGlobalPackageDB verbosity ghcjsProg)- pkgRoot' UserPackageDB = do- appDir <- getAppUserDataDirectory "ghcjs"- -- fixme correct this version- let ver = compilerVersion (compiler lbi)- subdir = System.Info.arch ++ '-':System.Info.os- ++ '-':prettyShow ver- rootDir = appDir </> subdir- -- We must create the root directory for the user package database if it- -- does not yet exists. Otherwise '${pkgroot}' will resolve to a- -- directory at the time of 'ghc-pkg register', and registration will- -- fail.- createDirectoryIfMissing True rootDir- return rootDir- pkgRoot' (SpecificPackageDB fp) = return (takeDirectory fp)----- | Get the JavaScript file name and command and arguments to run a--- program compiled by GHCJS--- the exe should be the base program name without exe extension-runCmd :: ProgramDb -> FilePath- -> (FilePath, FilePath, [String])+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}++module Distribution.Simple.GHCJS+ ( getGhcInfo+ , configure+ , configureCompiler+ , compilerProgramDb+ , getInstalledPackages+ , getInstalledPackagesMonitorFiles+ , getPackageDBContents+ , buildLib+ , buildFLib+ , buildExe+ , replLib+ , replFLib+ , replExe+ , startInterpreter+ , installLib+ , installFLib+ , installExe+ , libAbiHash+ , hcPkgInfo+ , registerPackage+ , componentGhcOptions+ , Internal.componentCcGhcOptions+ , getLibDir+ , isDynamic+ , getGlobalPackageDB+ , pkgRoot+ , runCmd++ -- * Constructing and deconstructing GHC environment files+ , Internal.GhcEnvironmentFileEntry (..)+ , Internal.simpleGhcEnvironmentFile+ , Internal.renderGhcEnvironmentFile+ , Internal.writeGhcEnvironmentFile+ , Internal.ghcPlatformAndVersionString+ , readGhcEnvironmentFile+ , parseGhcEnvironmentFile+ , ParseErrorExc (..)++ -- * Version-specific implementation quirks+ , getImplInfo+ , GhcImplInfo (..)+ ) where++import Distribution.Compat.Prelude+import Prelude ()++import Distribution.CabalSpecVersion+import Distribution.InstalledPackageInfo (InstalledPackageInfo)+import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo+import Distribution.ModuleName (ModuleName)+import qualified Distribution.ModuleName as ModuleName+import Distribution.Package+import Distribution.PackageDescription as PD+import Distribution.PackageDescription.Utils (cabalBug)+import Distribution.Pretty+import Distribution.Simple.BuildPaths+import Distribution.Simple.Compiler+import Distribution.Simple.Errors+import Distribution.Simple.Flag+import Distribution.Simple.GHC.EnvironmentParser+import Distribution.Simple.GHC.ImplInfo+import qualified Distribution.Simple.GHC.Internal as Internal+import qualified Distribution.Simple.Hpc as Hpc+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.PackageIndex (InstalledPackageIndex)+import qualified Distribution.Simple.PackageIndex as PackageIndex+import Distribution.Simple.PreProcess.Types+import Distribution.Simple.Program+import Distribution.Simple.Program.GHC+import qualified Distribution.Simple.Program.HcPkg as HcPkg+import qualified Distribution.Simple.Program.Strip as Strip+import Distribution.Simple.Setup.Common+import Distribution.Simple.Utils+import Distribution.System+import Distribution.Types.ComponentLocalBuildInfo+import Distribution.Types.PackageName.Magic+import Distribution.Types.ParStrat+import Distribution.Utils.NubList+import Distribution.Utils.Path+import Distribution.Verbosity (Verbosity)+import Distribution.Version++import Control.Arrow ((***))+import Control.Monad (msum)+import Data.Char (isLower)+import qualified Data.Map as Map+import Data.Maybe (fromJust)+import System.Directory+ ( canonicalizePath+ , createDirectoryIfMissing+ , doesFileExist+ , getAppUserDataDirectory+ , removeFile+ , renameFile+ )+import System.FilePath+ ( isRelative+ , replaceExtension+ , takeDirectory+ , takeExtension+ )+import qualified System.Info++-- -----------------------------------------------------------------------------+-- Configuring++-- | Configure GHCJS, and then auxiliary programs such as @ghc-pkg@, @haddock@+-- as well as toolchain programs such as @ar@, @ld.+configure+ :: Verbosity+ -> Maybe FilePath+ -- ^ user-specified @ghcjs@ path (optional)+ -> Maybe FilePath+ -- ^ user-specified @ghcjs-pkg@ path (optional)+ -> ProgramDb+ -> IO (Compiler, Maybe Platform, ProgramDb)+configure verbosity hcPath hcPkgPath conf0 = do+ (comp, compPlatform, progdb1) <- configureCompiler verbosity hcPath conf0+ compProgDb <- compilerProgramDb verbosity comp progdb1 hcPkgPath+ return (comp, compPlatform, compProgDb)++-- | Configure GHCJS.+configureCompiler+ :: Verbosity+ -> Maybe FilePath+ -- ^ user-specified @ghc@ path (optional)+ -> ProgramDb+ -> IO (Compiler, Maybe Platform, ProgramDb)+configureCompiler verbosity hcPath conf0 = do+ (ghcjsProg, ghcjsVersion, progdb1) <-+ requireProgramVersion+ verbosity+ ghcjsProgram+ (orLaterVersion (mkVersion [0, 1]))+ (userMaybeSpecifyPath "ghcjs" hcPath conf0)++ Just ghcjsGhcVersion <- findGhcjsGhcVersion verbosity (programPath ghcjsProg)+ unless (ghcjsGhcVersion < mkVersion [8, 8]) $+ warn verbosity $+ "Unknown/unsupported 'ghc' version detected "+ ++ "(Cabal "+ ++ prettyShow cabalVersion+ ++ " supports 'ghc' version < 8.8): "+ ++ programPath ghcjsProg+ ++ " is based on GHC version "+ ++ prettyShow ghcjsGhcVersion++ let implInfo = ghcjsVersionImplInfo ghcjsVersion ghcjsGhcVersion++ languages <- Internal.getLanguages verbosity implInfo ghcjsProg+ extensions <- Internal.getExtensions verbosity implInfo ghcjsProg++ ghcjsInfo <- Internal.getGhcInfo verbosity implInfo ghcjsProg+ let ghcInfoMap = Map.fromList ghcjsInfo++ let comp =+ Compiler+ { compilerId = CompilerId GHCJS ghcjsVersion+ , compilerAbiTag =+ AbiTag $+ "ghc" ++ intercalate "_" (map show . versionNumbers $ ghcjsGhcVersion)+ , compilerCompat = [CompilerId GHC ghcjsGhcVersion]+ , compilerLanguages = languages+ , compilerExtensions = extensions+ , compilerProperties = ghcInfoMap+ }+ compPlatform = Internal.targetPlatform ghcjsInfo+ return (comp, compPlatform, progdb1)++-- | Given a configured @ghcjs@ program, configure auxiliary programs such+-- as @ghcjs-pkg@ or @haddock@, based on the location of the @ghcjs@ executable.+compilerProgramDb+ :: Verbosity+ -> Compiler+ -> ProgramDb+ -> Maybe FilePath+ -- ^ user-specified @ghc-pkg@ path (optional)+ -> IO ProgramDb+compilerProgramDb verbosity comp progdb1 hcPkgPath = do+ let+ ghcjsProg = fromJust $ lookupProgram ghcjsProgram progdb1+ ghcjsVersion = compilerVersion comp+ ghcjsGhcVersion = case compilerCompat comp of+ [CompilerId GHC ghcjsGhcVer] -> ghcjsGhcVer+ compat -> error $ "could not parse ghcjsGhcVersion:" ++ show compat++ -- This is slightly tricky, we have to configure ghc first, then we use the+ -- location of ghc to help find ghc-pkg in the case that the user did not+ -- specify the location of ghc-pkg directly:+ (ghcjsPkgProg, ghcjsPkgVersion, progdb2) <-+ requireProgramVersion+ verbosity+ ghcjsPkgProgram+ { programFindLocation = guessGhcjsPkgFromGhcjsPath ghcjsProg+ }+ anyVersion+ (userMaybeSpecifyPath "ghcjs-pkg" hcPkgPath progdb1)++ Just ghcjsPkgGhcjsVersion <-+ findGhcjsPkgGhcjsVersion+ verbosity+ (programPath ghcjsPkgProg)++ when (ghcjsVersion /= ghcjsPkgGhcjsVersion) $+ dieWithException verbosity $+ VersionMismatchJS+ (programPath ghcjsProg)+ ghcjsVersion+ (programPath ghcjsPkgProg)+ ghcjsPkgGhcjsVersion++ when (ghcjsGhcVersion /= ghcjsPkgVersion) $+ dieWithException verbosity $+ VersionMismatchGHCJS (programPath ghcjsProg) ghcjsGhcVersion (programPath ghcjsPkgProg) ghcjsPkgVersion++ -- Likewise we try to find the matching hsc2hs and haddock programs.+ let hsc2hsProgram' =+ hsc2hsProgram+ { programFindLocation =+ guessHsc2hsFromGhcjsPath ghcjsProg+ }+ haddockProgram' =+ haddockProgram+ { programFindLocation =+ guessHaddockFromGhcjsPath ghcjsProg+ }+ hpcProgram' =+ hpcProgram+ { programFindLocation = guessHpcFromGhcjsPath ghcjsProg+ }+ {-+ runghcProgram' = runghcProgram {+ programFindLocation = guessRunghcFromGhcjsPath ghcjsProg+ } -}+ progdb3 =+ addKnownProgram haddockProgram' $+ addKnownProgram hsc2hsProgram' $+ addKnownProgram hpcProgram' $+ {- addKnownProgram runghcProgram' -} progdb2++ return progdb3++guessGhcjsPkgFromGhcjsPath+ :: ConfiguredProgram+ -> Verbosity+ -> ProgramSearchPath+ -> IO (Maybe (FilePath, [FilePath]))+guessGhcjsPkgFromGhcjsPath = guessToolFromGhcjsPath ghcjsPkgProgram++guessHsc2hsFromGhcjsPath+ :: ConfiguredProgram+ -> Verbosity+ -> ProgramSearchPath+ -> IO (Maybe (FilePath, [FilePath]))+guessHsc2hsFromGhcjsPath = guessToolFromGhcjsPath hsc2hsProgram++guessHaddockFromGhcjsPath+ :: ConfiguredProgram+ -> Verbosity+ -> ProgramSearchPath+ -> IO (Maybe (FilePath, [FilePath]))+guessHaddockFromGhcjsPath = guessToolFromGhcjsPath haddockProgram++guessHpcFromGhcjsPath+ :: ConfiguredProgram+ -> Verbosity+ -> ProgramSearchPath+ -> IO (Maybe (FilePath, [FilePath]))+guessHpcFromGhcjsPath = guessToolFromGhcjsPath hpcProgram++guessToolFromGhcjsPath+ :: Program+ -> ConfiguredProgram+ -> Verbosity+ -> ProgramSearchPath+ -> IO (Maybe (FilePath, [FilePath]))+guessToolFromGhcjsPath tool ghcjsProg verbosity searchpath =+ do+ let toolname = programName tool+ given_path = programPath ghcjsProg+ given_dir = takeDirectory given_path+ real_path <- canonicalizePath given_path+ let real_dir = takeDirectory real_path+ versionSuffix path = takeVersionSuffix (dropExeExtension path)+ given_suf = versionSuffix given_path+ real_suf = versionSuffix real_path+ guessNormal dir = dir </> toolname <.> exeExtension buildPlatform+ guessGhcjs dir =+ dir+ </> (toolname ++ "-ghcjs")+ <.> exeExtension buildPlatform+ guessGhcjsVersioned dir suf =+ dir+ </> (toolname ++ "-ghcjs" ++ suf)+ <.> exeExtension buildPlatform+ guessVersioned dir suf =+ dir+ </> (toolname ++ suf)+ <.> exeExtension buildPlatform+ mkGuesses dir suf+ | null suf = [guessGhcjs dir, guessNormal dir]+ | otherwise =+ [ guessGhcjsVersioned dir suf+ , guessVersioned dir suf+ , guessGhcjs dir+ , guessNormal dir+ ]+ guesses =+ mkGuesses given_dir given_suf+ ++ if real_path == given_path+ then []+ else mkGuesses real_dir real_suf+ info verbosity $+ "looking for tool "+ ++ toolname+ ++ " near compiler in "+ ++ given_dir+ debug verbosity $ "candidate locations: " ++ show guesses+ exists <- traverse doesFileExist guesses+ case [file | (file, True) <- zip guesses exists] of+ -- If we can't find it near ghc, fall back to the usual+ -- method.+ [] -> programFindLocation tool verbosity searchpath+ (fp : _) -> do+ info verbosity $ "found " ++ toolname ++ " in " ++ fp+ let lookedAt =+ map fst+ . takeWhile (\(_file, exist) -> not exist)+ $ zip guesses exists+ return (Just (fp, lookedAt))+ where+ takeVersionSuffix :: FilePath -> String+ takeVersionSuffix = takeWhileEndLE isSuffixChar++ isSuffixChar :: Char -> Bool+ isSuffixChar c = isDigit c || c == '.' || c == '-'++getGhcInfo :: Verbosity -> ConfiguredProgram -> IO [(String, String)]+getGhcInfo verbosity ghcjsProg = Internal.getGhcInfo verbosity implInfo ghcjsProg+ where+ version = fromMaybe (error "GHCJS.getGhcInfo: no version") $ programVersion ghcjsProg+ implInfo = ghcVersionImplInfo version++-- | Given a single package DB, return all installed packages.+getPackageDBContents+ :: Verbosity+ -> Maybe (SymbolicPath CWD (Dir from))+ -> PackageDBX (SymbolicPath from (Dir PkgDB))+ -> ProgramDb+ -> IO InstalledPackageIndex+getPackageDBContents verbosity mbWorkDir packagedb progdb = do+ pkgss <- getInstalledPackages' verbosity mbWorkDir [packagedb] progdb+ toPackageIndex verbosity pkgss progdb++-- | Given a package DB stack, return all installed packages.+getInstalledPackages+ :: Verbosity+ -> Maybe (SymbolicPath CWD (Dir from))+ -> PackageDBStackX (SymbolicPath from (Dir PkgDB))+ -> ProgramDb+ -> IO InstalledPackageIndex+getInstalledPackages verbosity mbWorkDir packagedbs progdb = do+ checkPackageDbEnvVar verbosity+ checkPackageDbStack verbosity packagedbs+ pkgss <- getInstalledPackages' verbosity mbWorkDir packagedbs progdb+ index <- toPackageIndex verbosity pkgss progdb+ return $! index++toPackageIndex+ :: Verbosity+ -> [(PackageDBX a, [InstalledPackageInfo])]+ -> ProgramDb+ -> IO InstalledPackageIndex+toPackageIndex verbosity pkgss progdb = do+ -- On Windows, various fields have $topdir/foo rather than full+ -- paths. We need to substitute the right value in so that when+ -- we, for example, call gcc, we have proper paths to give it.+ topDir <- getLibDir' verbosity ghcjsProg+ let indices =+ [ PackageIndex.fromList (map (Internal.substTopDir topDir) pkgs)+ | (_, pkgs) <- pkgss+ ]+ return $! (mconcat indices)+ where+ ghcjsProg = fromMaybe (error "GHCJS.toPackageIndex no ghcjs program") $ lookupProgram ghcjsProgram progdb++getLibDir :: Verbosity -> LocalBuildInfo -> IO FilePath+getLibDir verbosity lbi =+ dropWhileEndLE isSpace+ `fmap` getDbProgramOutput+ verbosity+ ghcjsProgram+ (withPrograms lbi)+ ["--print-libdir"]++getLibDir' :: Verbosity -> ConfiguredProgram -> IO FilePath+getLibDir' verbosity ghcjsProg =+ dropWhileEndLE isSpace+ `fmap` getProgramOutput verbosity ghcjsProg ["--print-libdir"]++-- | Return the 'FilePath' to the global GHC package database.+getGlobalPackageDB :: Verbosity -> ConfiguredProgram -> IO FilePath+getGlobalPackageDB verbosity ghcProg =+ dropWhileEndLE isSpace+ `fmap` getProgramOutput verbosity ghcProg ["--print-global-package-db"]++-- | Return the 'FilePath' to the per-user GHC package database.+getUserPackageDB :: Verbosity -> ConfiguredProgram -> Platform -> IO FilePath+getUserPackageDB _verbosity ghcjsProg platform = do+ -- It's rather annoying that we have to reconstruct this, because ghc+ -- hides this information from us otherwise. But for certain use cases+ -- like change monitoring it really can't remain hidden.+ appdir <- getAppUserDataDirectory "ghcjs"+ return (appdir </> platformAndVersion </> packageConfFileName)+ where+ platformAndVersion =+ Internal.ghcPlatformAndVersionString+ platform+ ghcjsVersion+ packageConfFileName = "package.conf.d"+ ghcjsVersion = fromMaybe (error "GHCJS.getUserPackageDB: no version") $ programVersion ghcjsProg++checkPackageDbEnvVar :: Verbosity -> IO ()+checkPackageDbEnvVar verbosity =+ Internal.checkPackageDbEnvVar verbosity "GHCJS" "GHCJS_PACKAGE_PATH"++checkPackageDbStack :: Eq fp => Verbosity -> PackageDBStackX fp -> IO ()+checkPackageDbStack _ (GlobalPackageDB : rest)+ | GlobalPackageDB `notElem` rest = return ()+checkPackageDbStack verbosity rest+ | GlobalPackageDB `notElem` rest =+ dieWithException verbosity GlobalPackageDBLimitation+checkPackageDbStack verbosity _ =+ dieWithException verbosity GlobalPackageDBSpecifiedFirst++getInstalledPackages'+ :: Verbosity+ -> Maybe (SymbolicPath CWD (Dir from))+ -> [PackageDBX (SymbolicPath from (Dir PkgDB))]+ -> ProgramDb+ -> IO [(PackageDBX (SymbolicPath from (Dir PkgDB)), [InstalledPackageInfo])]+getInstalledPackages' verbosity mbWorkDir packagedbs progdb =+ sequenceA+ [ do+ pkgs <- HcPkg.dump (hcPkgInfo progdb) verbosity mbWorkDir packagedb+ return (packagedb, pkgs)+ | packagedb <- packagedbs+ ]++-- | Get the packages from specific PackageDBs, not cumulative.+getInstalledPackagesMonitorFiles+ :: Verbosity+ -> Platform+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> ProgramDb+ -> [PackageDB]+ -> IO [FilePath]+getInstalledPackagesMonitorFiles verbosity platform mbWorkDir progdb =+ traverse getPackageDBPath+ where+ getPackageDBPath :: PackageDB -> IO FilePath+ getPackageDBPath GlobalPackageDB =+ selectMonitorFile =<< getGlobalPackageDB verbosity ghcjsProg+ getPackageDBPath UserPackageDB =+ selectMonitorFile =<< getUserPackageDB verbosity ghcjsProg platform+ getPackageDBPath (SpecificPackageDB path) = selectMonitorFile (interpretSymbolicPath mbWorkDir path)++ -- GHC has old style file dbs, and new style directory dbs.+ -- Note that for dir style dbs, we only need to monitor the cache file, not+ -- the whole directory. The ghc program itself only reads the cache file+ -- so it's safe to only monitor this one file.+ selectMonitorFile path0 = do+ let path =+ if isRelative path0+ then interpretSymbolicPath mbWorkDir (makeRelativePathEx path0)+ else path0+ isFileStyle <- doesFileExist path+ if isFileStyle+ then return path+ else return (path </> "package.cache")++ ghcjsProg = fromMaybe (error "GHCJS.toPackageIndex no ghcjs program") $ lookupProgram ghcjsProgram progdb++toJSLibName :: String -> String+toJSLibName lib+ | takeExtension lib `elem` [".dll", ".dylib", ".so"] =+ replaceExtension lib "js_so"+ | takeExtension lib == ".a" = replaceExtension lib "js_a"+ | otherwise = lib <.> "js_a"++-- -----------------------------------------------------------------------------+-- Building a library++buildLib+ :: Verbosity+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> Library+ -> ComponentLocalBuildInfo+ -> IO ()+buildLib = buildOrReplLib Nothing++replLib+ :: [String]+ -> Verbosity+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> Library+ -> ComponentLocalBuildInfo+ -> IO ()+replLib = buildOrReplLib . Just++buildOrReplLib+ :: Maybe [String]+ -> Verbosity+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> Library+ -> ComponentLocalBuildInfo+ -> IO ()+buildOrReplLib mReplFlags verbosity numJobs _pkg_descr lbi lib clbi = do+ let uid = componentUnitId clbi+ libTargetDir = componentBuildDir lbi clbi+ whenVanillaLib forceVanilla =+ when (forceVanilla || withVanillaLib lbi)+ whenProfLib = when (withProfLib lbi)+ whenSharedLib forceShared =+ when (forceShared || withSharedLib lbi)+ whenStaticLib forceStatic =+ when (forceStatic || withStaticLib lbi)+ -- whenGHCiLib = when (withGHCiLib lbi)+ forRepl = maybe False (const True) mReplFlags+ -- ifReplLib = when forRepl+ comp = compiler lbi+ implInfo = getImplInfo comp+ platform@(Platform _hostArch _hostOS) = hostPlatform lbi+ has_code = not (componentIsIndefinite clbi)+ mbWorkDir = mbWorkDirLBI lbi++ -- See Note [Symbolic paths] in Distribution.Utils.Path+ i = interpretSymbolicPathLBI lbi+ u :: SymbolicPathX allowAbs Pkg to -> FilePath+ u = getSymbolicPath++ (ghcjsProg, _) <- requireProgram verbosity ghcjsProgram (withPrograms lbi)+ let runGhcjsProg = runGHC verbosity ghcjsProg comp platform mbWorkDir++ let libBi = libBuildInfo lib++ -- fixme flags shouldn't depend on ghcjs being dynamic or not+ let isGhcjsDynamic = isDynamic comp+ dynamicTooSupported = supportsDynamicToo comp+ doingTH = usesTemplateHaskellOrQQ libBi+ forceVanillaLib = doingTH && not isGhcjsDynamic+ forceSharedLib = doingTH && isGhcjsDynamic+ -- TH always needs default libs, even when building for profiling++ -- Determine if program coverage should be enabled and if so, what+ -- '-hpcdir' should be.+ let isCoverageEnabled = libCoverage lbi+ hpcdir way+ | forRepl = mempty -- HPC is not supported in ghci+ | isCoverageEnabled = toFlag $ Hpc.mixDir (coerceSymbolicPath libTargetDir </> coerceSymbolicPath extraCompilationArtifacts) way+ | otherwise = mempty++ createDirectoryIfMissingVerbose verbosity True $ i libTargetDir+ -- TODO: do we need to put hs-boot files into place for mutually recursive+ -- modules?+ let cLikeFiles = fromNubListR $ toNubListR (cSources libBi) <> toNubListR (cxxSources libBi)+ jsSrcs = jsSources libBi+ cObjs = map ((`replaceExtensionSymbolicPath` objExtension)) cLikeFiles+ baseOpts = componentGhcOptions verbosity lbi libBi clbi libTargetDir+ linkJsLibOpts =+ mempty+ { ghcOptExtra =+ [ "-link-js-lib"+ , getHSLibraryName uid+ , "-js-lib-outputdir"+ , u libTargetDir+ ]+ ++ map u jsSrcs+ }+ vanillaOptsNoJsLib =+ baseOpts+ `mappend` mempty+ { ghcOptMode = toFlag GhcModeMake+ , ghcOptNumJobs = numJobs+ , ghcOptInputModules = toNubListR $ allLibModules lib clbi+ , ghcOptHPCDir = hpcdir Hpc.Vanilla+ }+ vanillaOpts = vanillaOptsNoJsLib `mappend` linkJsLibOpts++ profOpts =+ adjustExts "p_hi" "p_o" vanillaOpts+ `mappend` mempty+ { ghcOptProfilingMode = toFlag True+ , ghcOptProfilingAuto =+ Internal.profDetailLevelFlag+ True+ (withProfLibDetail lbi)+ , -- ghcOptHiSuffix = toFlag "p_hi",+ -- ghcOptObjSuffix = toFlag "p_o",+ ghcOptExtra = hcProfOptions GHC libBi+ , ghcOptHPCDir = hpcdir Hpc.Prof+ }++ sharedOpts =+ adjustExts "dyn_hi" "dyn_o" vanillaOpts+ `mappend` mempty+ { ghcOptDynLinkMode = toFlag GhcDynamicOnly+ , ghcOptFPic = toFlag True+ , -- ghcOptHiSuffix = toFlag "dyn_hi",+ -- ghcOptObjSuffix = toFlag "dyn_o",+ ghcOptExtra = hcOptions GHC libBi ++ hcSharedOptions GHC libBi+ , ghcOptHPCDir = hpcdir Hpc.Dyn+ }++ vanillaSharedOpts =+ vanillaOpts+ `mappend` mempty+ { ghcOptDynLinkMode = toFlag GhcStaticAndDynamic+ , ghcOptDynHiSuffix = toFlag "js_dyn_hi"+ , ghcOptDynObjSuffix = toFlag "js_dyn_o"+ , ghcOptHPCDir = hpcdir Hpc.Dyn+ }++ unless (forRepl || null (allLibModules lib clbi) && null jsSrcs && null cObjs) $+ do+ let vanilla = whenVanillaLib forceVanillaLib (runGhcjsProg vanillaOpts)+ shared = whenSharedLib forceSharedLib (runGhcjsProg sharedOpts)+ useDynToo =+ dynamicTooSupported+ && (forceVanillaLib || withVanillaLib lbi)+ && (forceSharedLib || withSharedLib lbi)+ && null (hcSharedOptions GHC libBi)+ if not has_code+ then vanilla+ else+ if useDynToo+ then do+ runGhcjsProg vanillaSharedOpts+ case (hpcdir Hpc.Dyn, hpcdir Hpc.Vanilla) of+ (Flag dynDir, Flag vanillaDir) ->+ -- When the vanilla and shared library builds are done+ -- in one pass, only one set of HPC module interfaces+ -- are generated. This set should suffice for both+ -- static and dynamically linked executables. We copy+ -- the modules interfaces so they are available under+ -- both ways.+ copyDirectoryRecursive verbosity (i dynDir) (i vanillaDir)+ _ -> return ()+ else+ if isGhcjsDynamic+ then do shared; vanilla+ else do vanilla; shared+ whenProfLib (runGhcjsProg profOpts)++ -- Build any C++ sources separately.+ {-+ unless (not has_code || null (cxxSources libBi) || not nativeToo) $ do+ info verbosity "Building C++ Sources..."+ sequence_+ [ do let baseCxxOpts = Internal.componentCxxGhcOptions verbosity implInfo+ lbi libBi clbi libTargetDir filename+ vanillaCxxOpts = if isGhcjsDynamic+ then baseCxxOpts { ghcOptFPic = toFlag True }+ else baseCxxOpts+ profCxxOpts = vanillaCxxOpts `mappend` mempty {+ ghcOptProfilingMode = toFlag True,+ ghcOptObjSuffix = toFlag "p_o"+ }+ sharedCxxOpts = vanillaCxxOpts `mappend` mempty {+ ghcOptFPic = toFlag True,+ ghcOptDynLinkMode = toFlag GhcDynamicOnly,+ ghcOptObjSuffix = toFlag "dyn_o"+ }+ odir = fromFlag (ghcOptObjDir vanillaCxxOpts)+ createDirectoryIfMissingVerbose verbosity True odir+ let runGhcProgIfNeeded cxxOpts = do+ needsRecomp <- checkNeedsRecompilation filename cxxOpts+ when needsRecomp $ runGhcjsProg cxxOpts+ runGhcProgIfNeeded vanillaCxxOpts+ unless forRepl $+ whenSharedLib forceSharedLib (runGhcProgIfNeeded sharedCxxOpts)+ unless forRepl $ whenProfLib (runGhcProgIfNeeded profCxxOpts)+ | filename <- cxxSources libBi]++ ifReplLib $ do+ when (null (allLibModules lib clbi)) $ warn verbosity "No exposed modules"+ ifReplLib (runGhcjsProg replOpts)+ -}+ -- build any C sources+ -- TODO: Add support for S and CMM files.+ {-+ unless (not has_code || null (cSources libBi) || not nativeToo) $ do+ info verbosity "Building C Sources..."+ sequence_+ [ do let baseCcOpts = Internal.componentCcGhcOptions verbosity implInfo+ lbi libBi clbi libTargetDir filename+ vanillaCcOpts = if isGhcjsDynamic+ -- Dynamic GHC requires C sources to be built+ -- with -fPIC for REPL to work. See #2207.+ then baseCcOpts { ghcOptFPic = toFlag True }+ else baseCcOpts+ profCcOpts = vanillaCcOpts `mappend` mempty {+ ghcOptProfilingMode = toFlag True,+ ghcOptObjSuffix = toFlag "p_o"+ }+ sharedCcOpts = vanillaCcOpts `mappend` mempty {+ ghcOptFPic = toFlag True,+ ghcOptDynLinkMode = toFlag GhcDynamicOnly,+ ghcOptObjSuffix = toFlag "dyn_o"+ }+ odir = fromFlag (ghcOptObjDir vanillaCcOpts)+ createDirectoryIfMissingVerbose verbosity True odir+ let runGhcProgIfNeeded ccOpts = do+ needsRecomp <- checkNeedsRecompilation filename ccOpts+ when needsRecomp $ runGhcjsProg ccOpts+ runGhcProgIfNeeded vanillaCcOpts+ unless forRepl $+ whenSharedLib forceSharedLib (runGhcProgIfNeeded sharedCcOpts)+ unless forRepl $ whenProfLib (runGhcProgIfNeeded profCcOpts)+ | filename <- cSources libBi]+ -}+ -- TODO: problem here is we need the .c files built first, so we can load them+ -- with ghci, but .c files can depend on .h files generated by ghc by ffi+ -- exports.++ -- link:++ when has_code . when False {- fixme nativeToo -} . unless forRepl $ do+ info verbosity "Linking..."+ let cSharedObjs =+ map+ ((`replaceExtensionSymbolicPath` ("dyn_" ++ objExtension)))+ (cSources libBi ++ cxxSources libBi)+ compiler_id = compilerId (compiler lbi)+ sharedLibFilePath = libTargetDir </> makeRelativePathEx (mkSharedLibName (hostPlatform lbi) compiler_id uid)+ staticLibFilePath = libTargetDir </> makeRelativePathEx (mkStaticLibName (hostPlatform lbi) compiler_id uid)++ let stubObjs = []+ stubSharedObjs = []++ {-+ stubObjs <- catMaybes <$> sequenceA+ [ findFileWithExtension [objExtension] [libTargetDir]+ (ModuleName.toFilePath x ++"_stub")+ | ghcVersion < mkVersion [7,2] -- ghc-7.2+ does not make _stub.o files+ , x <- allLibModules lib clbi ]+ stubProfObjs <- catMaybes <$> sequenceA+ [ findFileWithExtension ["p_" ++ objExtension] [libTargetDir]+ (ModuleName.toFilePath x ++"_stub")+ | ghcVersion < mkVersion [7,2] -- ghc-7.2+ does not make _stub.o files+ , x <- allLibModules lib clbi ]+ stubSharedObjs <- catMaybes <$> sequenceA+ [ findFileWithExtension ["dyn_" ++ objExtension] [libTargetDir]+ (ModuleName.toFilePath x ++"_stub")+ | ghcVersion < mkVersion [7,2] -- ghc-7.2+ does not make _stub.o files+ , x <- allLibModules lib clbi ]+ -}+ hObjs <-+ Internal.getHaskellObjects+ implInfo+ lib+ lbi+ clbi+ (coerceSymbolicPath libTargetDir)+ objExtension+ True+ hSharedObjs <-+ if withSharedLib lbi+ then+ Internal.getHaskellObjects+ implInfo+ lib+ lbi+ clbi+ (coerceSymbolicPath libTargetDir)+ ("dyn_" ++ objExtension)+ False+ else return []++ unless (null hObjs && null cObjs && null stubObjs) $ do+ rpaths <- getRPaths lbi clbi++ let staticObjectFiles =+ hObjs+ ++ map (makeSymbolicPath . (getSymbolicPath libTargetDir </>) . getSymbolicPath) cObjs+ ++ stubObjs+ dynamicObjectFiles =+ hSharedObjs+ ++ map (makeSymbolicPath . (getSymbolicPath libTargetDir </>) . getSymbolicPath) cSharedObjs+ ++ stubSharedObjs+ -- After the relocation lib is created we invoke ghc -shared+ -- with the dependencies spelled out as -package arguments+ -- and ghc invokes the linker with the proper library paths+ ghcSharedLinkArgs =+ mempty+ { ghcOptShared = toFlag True+ , ghcOptDynLinkMode = toFlag GhcDynamicOnly+ , ghcOptInputFiles = toNubListR dynamicObjectFiles+ , ghcOptOutputFile = toFlag sharedLibFilePath+ , ghcOptExtra = hcOptions GHC libBi ++ hcSharedOptions GHC libBi+ , -- For dynamic libs, Mac OS/X needs to know the install location+ -- at build time. This only applies to GHC < 7.8 - see the+ -- discussion in #1660.+ {-+ ghcOptDylibName = if hostOS == OSX+ && ghcVersion < mkVersion [7,8]+ then toFlag sharedLibInstallPath+ else mempty, -}+ ghcOptHideAllPackages = toFlag True+ , ghcOptNoAutoLinkPackages = toFlag True+ , ghcOptPackageDBs = withPackageDB lbi+ , ghcOptThisUnitId = case clbi of+ LibComponentLocalBuildInfo{componentCompatPackageKey = pk} ->+ toFlag pk+ _ -> mempty+ , ghcOptThisComponentId = case clbi of+ LibComponentLocalBuildInfo{componentInstantiatedWith = insts} ->+ if null insts+ then mempty+ else toFlag (componentComponentId clbi)+ _ -> mempty+ , ghcOptInstantiatedWith = case clbi of+ LibComponentLocalBuildInfo{componentInstantiatedWith = insts} ->+ insts+ _ -> []+ , ghcOptPackages =+ toNubListR $+ Internal.mkGhcOptPackages mempty clbi+ , ghcOptLinkLibs = extraLibs libBi+ , ghcOptLinkLibPath = toNubListR $ extraLibDirs libBi+ , ghcOptLinkFrameworks = toNubListR $ map getSymbolicPath $ PD.frameworks libBi+ , ghcOptLinkFrameworkDirs =+ toNubListR $ PD.extraFrameworkDirs libBi+ , ghcOptRPaths = rpaths+ }+ ghcStaticLinkArgs =+ mempty+ { ghcOptStaticLib = toFlag True+ , ghcOptInputFiles = toNubListR staticObjectFiles+ , ghcOptOutputFile = toFlag staticLibFilePath+ , ghcOptExtra = hcStaticOptions GHC libBi+ , ghcOptHideAllPackages = toFlag True+ , ghcOptNoAutoLinkPackages = toFlag True+ , ghcOptPackageDBs = withPackageDB lbi+ , ghcOptThisUnitId = case clbi of+ LibComponentLocalBuildInfo{componentCompatPackageKey = pk} ->+ toFlag pk+ _ -> mempty+ , ghcOptThisComponentId = case clbi of+ LibComponentLocalBuildInfo{componentInstantiatedWith = insts} ->+ if null insts+ then mempty+ else toFlag (componentComponentId clbi)+ _ -> mempty+ , ghcOptInstantiatedWith = case clbi of+ LibComponentLocalBuildInfo{componentInstantiatedWith = insts} ->+ insts+ _ -> []+ , ghcOptPackages =+ toNubListR $+ Internal.mkGhcOptPackages mempty clbi+ , ghcOptLinkLibs = extraLibs libBi+ , ghcOptLinkLibPath = toNubListR $ extraLibDirs libBi+ }++ info verbosity (show (ghcOptPackages ghcSharedLinkArgs))+ {-+ whenVanillaLib False $ do+ Ar.createArLibArchive verbosity lbi vanillaLibFilePath staticObjectFiles+ whenGHCiLib $ do+ (ldProg, _) <- requireProgram verbosity ldProgram (withPrograms lbi)+ Ld.combineObjectFiles verbosity lbi ldProg+ ghciLibFilePath staticObjectFiles+ -}+ {-+ whenProfLib $ do+ Ar.createArLibArchive verbosity lbi profileLibFilePath profObjectFiles+ whenGHCiLib $ do+ (ldProg, _) <- requireProgram verbosity ldProgram (withPrograms lbi)+ Ld.combineObjectFiles verbosity lbi ldProg+ ghciProfLibFilePath profObjectFiles+ -}+ whenSharedLib False $+ runGhcjsProg ghcSharedLinkArgs++ whenStaticLib False $+ runGhcjsProg ghcStaticLinkArgs++-- | Start a REPL without loading any source files.+startInterpreter+ :: Verbosity+ -> ProgramDb+ -> Compiler+ -> Platform+ -> PackageDBStack+ -> IO ()+startInterpreter verbosity progdb comp platform packageDBs = do+ let replOpts =+ mempty+ { ghcOptMode = toFlag GhcModeInteractive+ , ghcOptPackageDBs = packageDBs+ }+ checkPackageDbStack verbosity packageDBs+ (ghcjsProg, _) <- requireProgram verbosity ghcjsProgram progdb+ runGHC verbosity ghcjsProg comp platform Nothing replOpts++-- -----------------------------------------------------------------------------+-- Building an executable or foreign library++-- | Build a foreign library+buildFLib+ :: Verbosity+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> ForeignLib+ -> ComponentLocalBuildInfo+ -> IO ()+buildFLib v njobs pkg lbi = gbuild v njobs pkg lbi . GBuildFLib++replFLib+ :: [String]+ -> Verbosity+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> ForeignLib+ -> ComponentLocalBuildInfo+ -> IO ()+replFLib replFlags v njobs pkg lbi =+ gbuild v njobs pkg lbi . GReplFLib replFlags++-- | Build an executable with GHC.+buildExe+ :: Verbosity+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> Executable+ -> ComponentLocalBuildInfo+ -> IO ()+buildExe v njobs pkg lbi = gbuild v njobs pkg lbi . GBuildExe++replExe+ :: [String]+ -> Verbosity+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> Executable+ -> ComponentLocalBuildInfo+ -> IO ()+replExe replFlags v njobs pkg lbi =+ gbuild v njobs pkg lbi . GReplExe replFlags++-- | Building an executable, starting the REPL, and building foreign+-- libraries are all very similar and implemented in 'gbuild'. The+-- 'GBuildMode' distinguishes between the various kinds of operation.+data GBuildMode+ = GBuildExe Executable+ | GReplExe [String] Executable+ | GBuildFLib ForeignLib+ | GReplFLib [String] ForeignLib++gbuildInfo :: GBuildMode -> BuildInfo+gbuildInfo (GBuildExe exe) = buildInfo exe+gbuildInfo (GReplExe _ exe) = buildInfo exe+gbuildInfo (GBuildFLib flib) = foreignLibBuildInfo flib+gbuildInfo (GReplFLib _ flib) = foreignLibBuildInfo flib++gbuildName :: GBuildMode -> String+gbuildName (GBuildExe exe) = unUnqualComponentName $ exeName exe+gbuildName (GReplExe _ exe) = unUnqualComponentName $ exeName exe+gbuildName (GBuildFLib flib) = unUnqualComponentName $ foreignLibName flib+gbuildName (GReplFLib _ flib) = unUnqualComponentName $ foreignLibName flib++gbuildTargetName :: LocalBuildInfo -> GBuildMode -> String+gbuildTargetName lbi (GBuildExe exe) = exeTargetName (hostPlatform lbi) exe+gbuildTargetName lbi (GReplExe _ exe) = exeTargetName (hostPlatform lbi) exe+gbuildTargetName lbi (GBuildFLib flib) = flibTargetName lbi flib+gbuildTargetName lbi (GReplFLib _ flib) = flibTargetName lbi flib++exeTargetName :: Platform -> Executable -> String+exeTargetName platform exe = unUnqualComponentName (exeName exe) `withExt` exeExtension platform++-- | Target name for a foreign library (the actual file name)+--+-- We do not use mkLibName and co here because the naming for foreign libraries+-- is slightly different (we don't use "_p" or compiler version suffices, and we+-- don't want the "lib" prefix on Windows).+--+-- TODO: We do use `dllExtension` and co here, but really that's wrong: they+-- use the OS used to build cabal to determine which extension to use, rather+-- than the target OS (but this is wrong elsewhere in Cabal as well).+flibTargetName :: LocalBuildInfo -> ForeignLib -> String+flibTargetName lbi flib =+ case (os, foreignLibType flib) of+ (Windows, ForeignLibNativeShared) -> nm <.> "dll"+ (Windows, ForeignLibNativeStatic) -> nm <.> "lib"+ (Linux, ForeignLibNativeShared) -> "lib" ++ nm <.> versionedExt+ (_other, ForeignLibNativeShared) -> "lib" ++ nm <.> dllExtension (hostPlatform lbi)+ (_other, ForeignLibNativeStatic) -> "lib" ++ nm <.> staticLibExtension (hostPlatform lbi)+ (_any, ForeignLibTypeUnknown) -> cabalBug "unknown foreign lib type"+ where+ nm :: String+ nm = unUnqualComponentName $ foreignLibName flib++ os :: OS+ os =+ let (Platform _ os') = hostPlatform lbi+ in os'++ -- If a foreign lib foo has lib-version-info 5:1:2 or+ -- lib-version-linux 3.2.1, it should be built as libfoo.so.3.2.1+ -- Libtool's version-info data is translated into library versions in a+ -- nontrivial way: so refer to libtool documentation.+ versionedExt :: String+ versionedExt =+ let nums = foreignLibVersion flib os+ in foldl (<.>) "so" (map show nums)++-- | Name for the library when building.+--+-- If the `lib-version-info` field or the `lib-version-linux` field of+-- a foreign library target is set, we need to incorporate that+-- version into the SONAME field.+--+-- If a foreign library foo has lib-version-info 5:1:2, it should be+-- built as libfoo.so.3.2.1. We want it to get soname libfoo.so.3.+-- However, GHC does not allow overriding soname by setting linker+-- options, as it sets a soname of its own (namely the output+-- filename), after the user-supplied linker options. Hence, we have+-- to compile the library with the soname as its filename. We rename+-- the compiled binary afterwards.+--+-- This method allows to adjust the name of the library at build time+-- such that the correct soname can be set.+flibBuildName :: LocalBuildInfo -> ForeignLib -> String+flibBuildName lbi flib+ -- On linux, if a foreign-library has version data, the first digit is used+ -- to produce the SONAME.+ | (os, foreignLibType flib)+ == (Linux, ForeignLibNativeShared) =+ let nums = foreignLibVersion flib os+ in "lib" ++ nm <.> foldl (<.>) "so" (map show (take 1 nums))+ | otherwise = flibTargetName lbi flib+ where+ os :: OS+ os =+ let (Platform _ os') = hostPlatform lbi+ in os'++ nm :: String+ nm = unUnqualComponentName $ foreignLibName flib++gbuildIsRepl :: GBuildMode -> Bool+gbuildIsRepl (GBuildExe _) = False+gbuildIsRepl (GReplExe _ _) = True+gbuildIsRepl (GBuildFLib _) = False+gbuildIsRepl (GReplFLib _ _) = True++gbuildNeedDynamic :: LocalBuildInfo -> GBuildMode -> Bool+gbuildNeedDynamic lbi bm =+ case bm of+ GBuildExe _ -> withDynExe lbi+ GReplExe _ _ -> withDynExe lbi+ GBuildFLib flib -> withDynFLib flib+ GReplFLib _ flib -> withDynFLib flib+ where+ withDynFLib flib =+ case foreignLibType flib of+ ForeignLibNativeShared ->+ ForeignLibStandalone `notElem` foreignLibOptions flib+ ForeignLibNativeStatic ->+ False+ ForeignLibTypeUnknown ->+ cabalBug "unknown foreign lib type"++gbuildModDefFiles :: GBuildMode -> [RelativePath Source File]+gbuildModDefFiles (GBuildExe _) = []+gbuildModDefFiles (GReplExe _ _) = []+gbuildModDefFiles (GBuildFLib flib) = foreignLibModDefFile flib+gbuildModDefFiles (GReplFLib _ flib) = foreignLibModDefFile flib++-- | "Main" module name when overridden by @ghc-options: -main-is ...@+-- or 'Nothing' if no @-main-is@ flag could be found.+--+-- In case of 'Nothing', 'Distribution.ModuleName.main' can be assumed.+exeMainModuleName :: Executable -> Maybe ModuleName+exeMainModuleName Executable{buildInfo = bnfo} =+ -- GHC honors the last occurrence of a module name updated via -main-is+ --+ -- Moreover, -main-is when parsed left-to-right can update either+ -- the "Main" module name, or the "main" function name, or both,+ -- see also 'decodeMainIsArg'.+ msum $ reverse $ map decodeMainIsArg $ findIsMainArgs ghcopts+ where+ ghcopts = hcOptions GHC bnfo++ findIsMainArgs [] = []+ findIsMainArgs ("-main-is" : arg : rest) = arg : findIsMainArgs rest+ findIsMainArgs (_ : rest) = findIsMainArgs rest++-- | Decode argument to '-main-is'+--+-- Returns 'Nothing' if argument set only the function name.+--+-- This code has been stolen/refactored from GHC's DynFlags.setMainIs+-- function. The logic here is deliberately imperfect as it is+-- intended to be bug-compatible with GHC's parser. See discussion in+-- https://github.com/haskell/cabal/pull/4539#discussion_r118981753.+decodeMainIsArg :: String -> Maybe ModuleName+decodeMainIsArg arg+ | headOf main_fn isLower =+ -- The arg looked like "Foo.Bar.baz"+ Just (ModuleName.fromString main_mod)+ | headOf arg isUpper -- The arg looked like "Foo" or "Foo.Bar"+ =+ Just (ModuleName.fromString arg)+ | otherwise -- The arg looked like "baz"+ =+ Nothing+ where+ headOf :: String -> (Char -> Bool) -> Bool+ headOf str pred' = any pred' (safeHead str)++ (main_mod, main_fn) = splitLongestPrefix arg (== '.')++ splitLongestPrefix :: String -> (Char -> Bool) -> (String, String)+ splitLongestPrefix str pred'+ | null r_pre = (str, [])+ | otherwise = (reverse (safeTail r_pre), reverse r_suf)+ where+ -- 'safeTail' drops the char satisfying 'pred'+ (r_suf, r_pre) = break pred' (reverse str)++-- | A collection of:+-- * C input files+-- * C++ input files+-- * GHC input files+-- * GHC input modules+--+-- Used to correctly build and link sources.+data BuildSources = BuildSources+ { cSourcesFiles :: [SymbolicPath Pkg File]+ , cxxSourceFiles :: [SymbolicPath Pkg File]+ , inputSourceFiles :: [SymbolicPath Pkg File]+ , inputSourceModules :: [ModuleName]+ }++-- | Locate and return the 'BuildSources' required to build and link.+gbuildSources+ :: Verbosity+ -> Maybe (SymbolicPath CWD ('Dir Pkg))+ -> PackageId+ -> CabalSpecVersion+ -> SymbolicPath Pkg (Dir Source)+ -> GBuildMode+ -> IO BuildSources+gbuildSources verbosity mbWorkDir pkgId specVer tmpDir bm =+ case bm of+ GBuildExe exe -> exeSources exe+ GReplExe _ exe -> exeSources exe+ GBuildFLib flib -> return $ flibSources flib+ GReplFLib _ flib -> return $ flibSources flib+ where+ exeSources :: Executable -> IO BuildSources+ exeSources exe@Executable{buildInfo = bnfo, modulePath = modPath} = do+ main <- findFileCwd verbosity mbWorkDir (tmpDir : hsSourceDirs bnfo) modPath+ let mainModName = fromMaybe ModuleName.main $ exeMainModuleName exe+ otherModNames = exeModules exe+ haskellMain = isHaskell (getSymbolicPath main)++ -- Scripts have fakePackageId and are always Haskell but can have any extension.+ if haskellMain || pkgId == fakePackageId+ then+ if specVer < CabalSpecV2_0 && (mainModName `elem` otherModNames)+ then do+ -- The cabal manual clearly states that `other-modules` is+ -- intended for non-main modules. However, there's at least one+ -- important package on Hackage (happy-1.19.5) which+ -- violates this. We workaround this here so that we don't+ -- invoke GHC with e.g. 'ghc --make Main src/Main.hs' which+ -- would result in GHC complaining about duplicate Main+ -- modules.+ --+ -- Finally, we only enable this workaround for+ -- specVersion < 2, as 'cabal-version:>=2.0' cabal files+ -- have no excuse anymore to keep doing it wrong... ;-)+ warn verbosity $+ "Enabling workaround for Main module '"+ ++ prettyShow mainModName+ ++ "' listed in 'other-modules' illegally!"++ return+ BuildSources+ { cSourcesFiles = cSources bnfo+ , cxxSourceFiles = cxxSources bnfo+ , inputSourceFiles = [main]+ , inputSourceModules = filter (/= mainModName) $ exeModules exe+ }+ else+ return+ BuildSources+ { cSourcesFiles = cSources bnfo+ , cxxSourceFiles = cxxSources bnfo+ , inputSourceFiles = [main]+ , inputSourceModules = exeModules exe+ }+ else+ let (csf, cxxsf)+ | isCxx (getSymbolicPath main) = (cSources bnfo, main : cxxSources bnfo)+ -- if main is not a Haskell source+ -- and main is not a C++ source+ -- then we assume that it is a C source+ | otherwise = (main : cSources bnfo, cxxSources bnfo)+ in return+ BuildSources+ { cSourcesFiles = csf+ , cxxSourceFiles = cxxsf+ , inputSourceFiles = []+ , inputSourceModules = exeModules exe+ }++ flibSources :: ForeignLib -> BuildSources+ flibSources flib@ForeignLib{foreignLibBuildInfo = bnfo} =+ BuildSources+ { cSourcesFiles = cSources bnfo+ , cxxSourceFiles = cxxSources bnfo+ , inputSourceFiles = []+ , inputSourceModules = foreignLibModules flib+ }++ isCxx :: FilePath -> Bool+ isCxx fp = elem (takeExtension fp) [".cpp", ".cxx", ".c++"]++-- | FilePath has a Haskell extension: .hs or .lhs+isHaskell :: FilePath -> Bool+isHaskell fp = elem (takeExtension fp) [".hs", ".lhs"]++-- | Generic build function. See comment for 'GBuildMode'.+gbuild+ :: Verbosity+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> GBuildMode+ -> ComponentLocalBuildInfo+ -> IO ()+gbuild verbosity numJobs pkg_descr lbi bm clbi = do+ (ghcjsProg, _) <- requireProgram verbosity ghcjsProgram (withPrograms lbi)+ let replFlags = case bm of+ GReplExe flags _ -> flags+ GReplFLib flags _ -> flags+ GBuildExe{} -> mempty+ GBuildFLib{} -> mempty+ comp = compiler lbi+ platform = hostPlatform lbi+ mbWorkDir = mbWorkDirLBI lbi+ runGhcProg = runGHC verbosity ghcjsProg comp platform mbWorkDir++ let (bnfo, threaded) = case bm of+ GBuildFLib _ -> popThreadedFlag (gbuildInfo bm)+ _ -> (gbuildInfo bm, False)++ -- the name that GHC really uses (e.g., with .exe on Windows for executables)+ let targetName = gbuildTargetName lbi bm+ targetDir = buildDir lbi </> makeRelativePathEx (gbuildName bm)+ tmpDir = targetDir </> makeRelativePathEx (gbuildName bm ++ "-tmp")++ -- See Note [Symbolic paths] in Distribution.Utils.Path+ i = interpretSymbolicPath mbWorkDir++ createDirectoryIfMissingVerbose verbosity True $ i targetDir+ createDirectoryIfMissingVerbose verbosity True $ i tmpDir++ -- TODO: do we need to put hs-boot files into place for mutually recursive+ -- modules? FIX: what about exeName.hi-boot?++ -- Determine if program coverage should be enabled and if so, what+ -- '-hpcdir' should be.+ let isCoverageEnabled = exeCoverage lbi+ hpcdir way+ | gbuildIsRepl bm = mempty -- HPC is not supported in ghci+ | isCoverageEnabled = toFlag $ Hpc.mixDir (tmpDir </> coerceSymbolicPath extraCompilationArtifacts) way+ | otherwise = mempty++ rpaths <- getRPaths lbi clbi+ buildSources <- gbuildSources verbosity mbWorkDir (package pkg_descr) (specVersion pkg_descr) tmpDir bm++ let cSrcs = cSourcesFiles buildSources+ cxxSrcs = cxxSourceFiles buildSources+ inputFiles = inputSourceFiles buildSources+ inputModules = inputSourceModules buildSources+ isGhcDynamic = isDynamic comp+ dynamicTooSupported = supportsDynamicToo comp+ cObjs = map ((`replaceExtensionSymbolicPath` objExtension)) cSrcs+ cxxObjs = map ((`replaceExtensionSymbolicPath` objExtension)) cxxSrcs+ needDynamic = gbuildNeedDynamic lbi bm+ needProfiling = withProfExe lbi++ -- build executables+ buildRunner = case clbi of+ LibComponentLocalBuildInfo{} -> False+ FLibComponentLocalBuildInfo{} -> False+ ExeComponentLocalBuildInfo{} -> True+ TestComponentLocalBuildInfo{} -> True+ BenchComponentLocalBuildInfo{} -> True+ baseOpts =+ (componentGhcOptions verbosity lbi bnfo clbi tmpDir)+ `mappend` mempty+ { ghcOptMode = toFlag GhcModeMake+ , ghcOptInputFiles =+ toNubListR $+ if package pkg_descr == fakePackageId+ then filter (isHaskell . getSymbolicPath) inputFiles+ else inputFiles+ , ghcOptInputScripts =+ toNubListR $+ if package pkg_descr == fakePackageId+ then filter (not . isHaskell . getSymbolicPath) inputFiles+ else []+ , ghcOptInputModules = toNubListR inputModules+ , -- for all executable components (exe/test/bench),+ -- GHCJS must be passed the "-build-runner" option+ ghcOptExtra =+ if buildRunner+ then ["-build-runner"]+ else mempty+ }+ staticOpts =+ baseOpts+ `mappend` mempty+ { ghcOptDynLinkMode = toFlag GhcStaticOnly+ , ghcOptHPCDir = hpcdir Hpc.Vanilla+ }+ profOpts =+ baseOpts+ `mappend` mempty+ { ghcOptProfilingMode = toFlag True+ , ghcOptProfilingAuto =+ Internal.profDetailLevelFlag+ False+ (withProfExeDetail lbi)+ , ghcOptHiSuffix = toFlag "p_hi"+ , ghcOptObjSuffix = toFlag "p_o"+ , ghcOptExtra = hcProfOptions GHC bnfo+ , ghcOptHPCDir = hpcdir Hpc.Prof+ }+ dynOpts =+ baseOpts+ `mappend` mempty+ { ghcOptDynLinkMode = toFlag GhcDynamicOnly+ , -- TODO: Does it hurt to set -fPIC for executables?+ ghcOptFPic = toFlag True+ , ghcOptHiSuffix = toFlag "dyn_hi"+ , ghcOptObjSuffix = toFlag "dyn_o"+ , ghcOptExtra = hcOptions GHC bnfo ++ hcSharedOptions GHC bnfo+ , ghcOptHPCDir = hpcdir Hpc.Dyn+ }+ dynTooOpts =+ staticOpts+ `mappend` mempty+ { ghcOptDynLinkMode = toFlag GhcStaticAndDynamic+ , ghcOptDynHiSuffix = toFlag "dyn_hi"+ , ghcOptDynObjSuffix = toFlag "dyn_o"+ , ghcOptHPCDir = hpcdir Hpc.Dyn+ }+ linkerOpts =+ mempty+ { ghcOptLinkOptions = PD.ldOptions bnfo+ , ghcOptLinkLibs = extraLibs bnfo+ , ghcOptLinkLibPath = toNubListR $ extraLibDirs bnfo+ , ghcOptLinkFrameworks =+ toNubListR $+ map getSymbolicPath $+ PD.frameworks bnfo+ , ghcOptLinkFrameworkDirs =+ toNubListR $+ PD.extraFrameworkDirs bnfo+ , ghcOptInputFiles =+ toNubListR+ [makeSymbolicPath $ getSymbolicPath tmpDir </> getSymbolicPath x | x <- cObjs ++ cxxObjs]+ }+ dynLinkerOpts =+ mempty+ { ghcOptRPaths = rpaths+ }+ replOpts =+ baseOpts+ { ghcOptExtra =+ Internal.filterGhciFlags+ (ghcOptExtra baseOpts)+ <> replFlags+ }+ -- For a normal compile we do separate invocations of ghc for+ -- compiling as for linking. But for repl we have to do just+ -- the one invocation, so that one has to include all the+ -- linker stuff too, like -l flags and any .o files from C+ -- files etc.+ `mappend` linkerOpts+ `mappend` mempty+ { ghcOptMode = toFlag GhcModeInteractive+ , ghcOptOptimisation = toFlag GhcNoOptimisation+ }+ commonOpts+ | needProfiling = profOpts+ | needDynamic = dynOpts+ | otherwise = staticOpts+ compileOpts+ | useDynToo = dynTooOpts+ | otherwise = commonOpts+ withStaticExe = not needProfiling && not needDynamic++ -- For building exe's that use TH with -prof or -dynamic we actually have+ -- to build twice, once without -prof/-dynamic and then again with+ -- -prof/-dynamic. This is because the code that TH needs to run at+ -- compile time needs to be the vanilla ABI so it can be loaded up and run+ -- by the compiler.+ -- With dynamic-by-default GHC the TH object files loaded at compile-time+ -- need to be .dyn_o instead of .o.+ doingTH = usesTemplateHaskellOrQQ bnfo+ -- Should we use -dynamic-too instead of compiling twice?+ useDynToo =+ dynamicTooSupported+ && isGhcDynamic+ && doingTH+ && withStaticExe+ && null (hcSharedOptions GHC bnfo)+ compileTHOpts+ | isGhcDynamic = dynOpts+ | otherwise = staticOpts+ compileForTH+ | gbuildIsRepl bm = False+ | useDynToo = False+ | isGhcDynamic = doingTH && (needProfiling || withStaticExe)+ | otherwise = doingTH && (needProfiling || needDynamic)++ -- Build static/dynamic object files for TH, if needed.+ when compileForTH $+ runGhcProg+ compileTHOpts+ { ghcOptNoLink = toFlag True+ , ghcOptNumJobs = numJobs+ }++ -- Do not try to build anything if there are no input files.+ -- This can happen if the cabal file ends up with only cSrcs+ -- but no Haskell modules.+ unless+ ( (null inputFiles && null inputModules)+ || gbuildIsRepl bm+ )+ $ runGhcProg+ compileOpts+ { ghcOptNoLink = toFlag True+ , ghcOptNumJobs = numJobs+ }++ -- build any C++ sources+ unless (null cxxSrcs) $ do+ info verbosity "Building C++ Sources..."+ sequence_+ [ do+ let baseCxxOpts =+ Internal.componentCxxGhcOptions+ verbosity+ lbi+ bnfo+ clbi+ tmpDir+ filename+ vanillaCxxOpts =+ if isGhcDynamic+ then -- Dynamic GHC requires C++ sources to be built+ -- with -fPIC for REPL to work. See #2207.+ baseCxxOpts{ghcOptFPic = toFlag True}+ else baseCxxOpts+ profCxxOpts =+ vanillaCxxOpts+ `mappend` mempty+ { ghcOptProfilingMode = toFlag True+ }+ sharedCxxOpts =+ vanillaCxxOpts+ `mappend` mempty+ { ghcOptFPic = toFlag True+ , ghcOptDynLinkMode = toFlag GhcDynamicOnly+ }+ opts+ | needProfiling = profCxxOpts+ | needDynamic = sharedCxxOpts+ | otherwise = vanillaCxxOpts+ -- TODO: Placing all Haskell, C, & C++ objects in a single directory+ -- Has the potential for file collisions. In general we would+ -- consider this a user error. However, we should strive to+ -- add a warning if this occurs.+ odir = fromFlag (ghcOptObjDir opts)+ createDirectoryIfMissingVerbose verbosity True (i odir)+ needsRecomp <- checkNeedsRecompilation mbWorkDir filename opts+ when needsRecomp $+ runGhcProg opts+ | filename <- cxxSrcs+ ]++ -- build any C sources+ unless (null cSrcs) $ do+ info verbosity "Building C Sources..."+ sequence_+ [ do+ let baseCcOpts =+ Internal.componentCcGhcOptions+ verbosity+ lbi+ bnfo+ clbi+ tmpDir+ filename+ vanillaCcOpts =+ if isGhcDynamic+ then -- Dynamic GHC requires C sources to be built+ -- with -fPIC for REPL to work. See #2207.+ baseCcOpts{ghcOptFPic = toFlag True}+ else baseCcOpts+ profCcOpts =+ vanillaCcOpts+ `mappend` mempty+ { ghcOptProfilingMode = toFlag True+ }+ sharedCcOpts =+ vanillaCcOpts+ `mappend` mempty+ { ghcOptFPic = toFlag True+ , ghcOptDynLinkMode = toFlag GhcDynamicOnly+ }+ opts+ | needProfiling = profCcOpts+ | needDynamic = sharedCcOpts+ | otherwise = vanillaCcOpts+ odir = fromFlag (ghcOptObjDir opts)+ createDirectoryIfMissingVerbose verbosity True (i odir)+ needsRecomp <- checkNeedsRecompilation mbWorkDir filename opts+ when needsRecomp $+ runGhcProg opts+ | filename <- cSrcs+ ]++ -- TODO: problem here is we need the .c files built first, so we can load them+ -- with ghci, but .c files can depend on .h files generated by ghc by ffi+ -- exports.+ case bm of+ GReplExe _ _ -> runGhcProg replOpts+ GReplFLib _ _ -> runGhcProg replOpts+ GBuildExe _ -> do+ let linkOpts =+ commonOpts+ `mappend` linkerOpts+ `mappend` mempty+ { ghcOptLinkNoHsMain = toFlag (null inputFiles)+ }+ `mappend` (if withDynExe lbi then dynLinkerOpts else mempty)++ info verbosity "Linking..."+ -- Work around old GHCs not relinking in this+ -- situation, see #3294+ let target = targetDir </> makeRelativePathEx targetName+ when (compilerVersion comp < mkVersion [7, 7]) $ do+ let targetPath = i target+ e <- doesFileExist targetPath+ when e (removeFile targetPath)+ runGhcProg linkOpts{ghcOptOutputFile = toFlag target}+ GBuildFLib flib -> do+ let rtsInfo = extractRtsInfo lbi+ rtsOptLinkLibs =+ [ if needDynamic+ then+ if threaded+ then dynRtsThreadedLib (rtsDynamicInfo rtsInfo)+ else dynRtsVanillaLib (rtsDynamicInfo rtsInfo)+ else+ if threaded+ then statRtsThreadedLib (rtsStaticInfo rtsInfo)+ else statRtsVanillaLib (rtsStaticInfo rtsInfo)+ ]+ linkOpts = case foreignLibType flib of+ ForeignLibNativeShared ->+ commonOpts+ `mappend` linkerOpts+ `mappend` dynLinkerOpts+ `mappend` mempty+ { ghcOptLinkNoHsMain = toFlag True+ , ghcOptShared = toFlag True+ , ghcOptLinkLibs = rtsOptLinkLibs+ , ghcOptLinkLibPath = toNubListR $ map makeSymbolicPath $ rtsLibPaths rtsInfo+ , ghcOptFPic = toFlag True+ , ghcOptLinkModDefFiles = toNubListR $ fmap getSymbolicPath $ gbuildModDefFiles bm+ }+ ForeignLibNativeStatic ->+ -- this should be caught by buildFLib+ -- (and if we do implement this, we probably don't even want to call+ -- ghc here, but rather Ar.createArLibArchive or something)+ cabalBug "static libraries not yet implemented"+ ForeignLibTypeUnknown ->+ cabalBug "unknown foreign lib type"+ -- We build under a (potentially) different filename to set a+ -- soname on supported platforms. See also the note for+ -- @flibBuildName@.+ info verbosity "Linking..."+ let buildName = flibBuildName lbi flib+ buildFile = targetDir </> makeRelativePathEx buildName+ runGhcProg linkOpts{ghcOptOutputFile = toFlag buildFile}+ renameFile (i buildFile) (i targetDir </> targetName)++data DynamicRtsInfo = DynamicRtsInfo+ { dynRtsVanillaLib :: FilePath+ , dynRtsThreadedLib :: FilePath+ , dynRtsDebugLib :: FilePath+ , dynRtsEventlogLib :: FilePath+ , dynRtsThreadedDebugLib :: FilePath+ , dynRtsThreadedEventlogLib :: FilePath+ }++data StaticRtsInfo = StaticRtsInfo+ { statRtsVanillaLib :: FilePath+ , statRtsThreadedLib :: FilePath+ , statRtsDebugLib :: FilePath+ , statRtsEventlogLib :: FilePath+ , statRtsThreadedDebugLib :: FilePath+ , statRtsThreadedEventlogLib :: FilePath+ , statRtsProfilingLib :: FilePath+ , statRtsThreadedProfilingLib :: FilePath+ }++data RtsInfo = RtsInfo+ { rtsDynamicInfo :: DynamicRtsInfo+ , rtsStaticInfo :: StaticRtsInfo+ , rtsLibPaths :: [FilePath]+ }++-- | Extract (and compute) information about the RTS library+--+-- TODO: This hardcodes the name as @HSrts-ghc<version>@. I don't know if we can+-- find this information somewhere. We can lookup the 'hsLibraries' field of+-- 'InstalledPackageInfo' but it will tell us @["HSrts", "Cffi"]@, which+-- doesn't really help.+extractRtsInfo :: LocalBuildInfo -> RtsInfo+extractRtsInfo lbi =+ case PackageIndex.lookupPackageName (installedPkgs lbi) (mkPackageName "rts") of+ [(_, [rts])] -> aux rts+ _otherwise -> error "No (or multiple) ghc rts package is registered"+ where+ aux :: InstalledPackageInfo -> RtsInfo+ aux rts =+ RtsInfo+ { rtsDynamicInfo =+ DynamicRtsInfo+ { dynRtsVanillaLib = withGhcVersion "HSrts"+ , dynRtsThreadedLib = withGhcVersion "HSrts_thr"+ , dynRtsDebugLib = withGhcVersion "HSrts_debug"+ , dynRtsEventlogLib = withGhcVersion "HSrts_l"+ , dynRtsThreadedDebugLib = withGhcVersion "HSrts_thr_debug"+ , dynRtsThreadedEventlogLib = withGhcVersion "HSrts_thr_l"+ }+ , rtsStaticInfo =+ StaticRtsInfo+ { statRtsVanillaLib = "HSrts"+ , statRtsThreadedLib = "HSrts_thr"+ , statRtsDebugLib = "HSrts_debug"+ , statRtsEventlogLib = "HSrts_l"+ , statRtsThreadedDebugLib = "HSrts_thr_debug"+ , statRtsThreadedEventlogLib = "HSrts_thr_l"+ , statRtsProfilingLib = "HSrts_p"+ , statRtsThreadedProfilingLib = "HSrts_thr_p"+ }+ , rtsLibPaths = InstalledPackageInfo.libraryDirs rts+ }+ withGhcVersion = (++ ("-ghc" ++ prettyShow (compilerVersion (compiler lbi))))++-- | Returns True if the modification date of the given source file is newer than+-- the object file we last compiled for it, or if no object file exists yet.+checkNeedsRecompilation+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -> SymbolicPath Pkg File+ -> GhcOptions+ -> IO Bool+checkNeedsRecompilation mbWorkDir filename opts =+ i filename `moreRecentFile` oname+ where+ oname = getObjectFileName mbWorkDir filename opts+ i = interpretSymbolicPath mbWorkDir -- See Note [Symbolic paths] in Distribution.Utils.Path++-- | Finds the object file name of the given source file+getObjectFileName+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -> SymbolicPath Pkg File+ -> GhcOptions+ -> FilePath+getObjectFileName mbWorkDir filename opts = oname+ where+ i = interpretSymbolicPath mbWorkDir -- See Note [Symbolic paths] in Distribution.Utils.Path+ odir = i $ fromFlag (ghcOptObjDir opts)+ oext = fromFlagOrDefault "o" (ghcOptObjSuffix opts)+ -- NB: the filepath might be absolute, e.g. if it is the path to+ -- an autogenerated .hs file.+ oname = odir </> replaceExtension (getSymbolicPath filename) oext++-- | Calculate the RPATHs for the component we are building.+--+-- Calculates relative RPATHs when 'relocatable' is set.+getRPaths+ :: LocalBuildInfo+ -> ComponentLocalBuildInfo+ -- ^ Component we are building+ -> IO (NubListR FilePath)+getRPaths lbi clbi | supportRPaths hostOS = do+ libraryPaths <- depLibraryPaths False (relocatable lbi) lbi clbi+ let hostPref = case hostOS of+ OSX -> "@loader_path"+ _ -> "$ORIGIN"+ relPath p = if isRelative p then hostPref </> p else p+ rpaths = toNubListR (map relPath libraryPaths)+ return rpaths+ where+ (Platform _ hostOS) = hostPlatform lbi+ compid = compilerId . compiler $ lbi++ -- The list of RPath-supported operating systems below reflects the+ -- platforms on which Cabal's RPATH handling is tested. It does _NOT_+ -- reflect whether the OS supports RPATH.++ -- E.g. when this comment was written, the *BSD operating systems were+ -- untested with regards to Cabal RPATH handling, and were hence set to+ -- 'False', while those operating systems themselves do support RPATH.+ supportRPaths Linux = True+ supportRPaths Windows = False+ supportRPaths OSX = True+ supportRPaths FreeBSD =+ case compid of+ CompilerId GHC ver | ver >= mkVersion [7, 10, 2] -> True+ _ -> False+ supportRPaths OpenBSD = False+ supportRPaths NetBSD = False+ supportRPaths DragonFly = False+ supportRPaths Solaris = False+ supportRPaths AIX = False+ supportRPaths HPUX = False+ supportRPaths IRIX = False+ supportRPaths HaLVM = False+ supportRPaths IOS = False+ supportRPaths Android = False+ supportRPaths Ghcjs = False+ supportRPaths Wasi = False+ supportRPaths Hurd = True+ supportRPaths Haiku = False+ supportRPaths (OtherOS _) = False+-- Do _not_ add a default case so that we get a warning here when a new OS+-- is added.++getRPaths _ _ = return mempty++-- | Remove the "-threaded" flag when building a foreign library, as it has no+-- effect when used with "-shared". Returns the updated 'BuildInfo', along+-- with whether or not the flag was present, so we can use it to link against+-- the appropriate RTS on our own.+popThreadedFlag :: BuildInfo -> (BuildInfo, Bool)+popThreadedFlag bi =+ ( bi{options = filterHcOptions (/= "-threaded") (options bi)}+ , hasThreaded (options bi)+ )+ where+ filterHcOptions+ :: (String -> Bool)+ -> PerCompilerFlavor [String]+ -> PerCompilerFlavor [String]+ filterHcOptions p (PerCompilerFlavor ghc ghcjs) =+ PerCompilerFlavor (filter p ghc) ghcjs++ hasThreaded :: PerCompilerFlavor [String] -> Bool+ hasThreaded (PerCompilerFlavor ghc _) = elem "-threaded" ghc++-- | Extracts a String representing a hash of the ABI of a built+-- library. It can fail if the library has not yet been built.+libAbiHash+ :: Verbosity+ -> PackageDescription+ -> LocalBuildInfo+ -> Library+ -> ComponentLocalBuildInfo+ -> IO String+libAbiHash verbosity _pkg_descr lbi lib clbi = do+ let+ libBi = libBuildInfo lib+ comp = compiler lbi+ platform = hostPlatform lbi+ mbWorkDir = mbWorkDirLBI lbi+ vanillaArgs =+ (componentGhcOptions verbosity lbi libBi clbi (componentBuildDir lbi clbi))+ `mappend` mempty+ { ghcOptMode = toFlag GhcModeAbiHash+ , ghcOptInputModules = toNubListR $ exposedModules lib+ }+ sharedArgs =+ vanillaArgs+ `mappend` mempty+ { ghcOptDynLinkMode = toFlag GhcDynamicOnly+ , ghcOptFPic = toFlag True+ , ghcOptHiSuffix = toFlag "js_dyn_hi"+ , ghcOptObjSuffix = toFlag "js_dyn_o"+ , ghcOptExtra = hcOptions GHC libBi ++ hcSharedOptions GHC libBi+ }+ profArgs =+ vanillaArgs+ `mappend` mempty+ { ghcOptProfilingMode = toFlag True+ , ghcOptProfilingAuto =+ Internal.profDetailLevelFlag+ True+ (withProfLibDetail lbi)+ , ghcOptHiSuffix = toFlag "js_p_hi"+ , ghcOptObjSuffix = toFlag "js_p_o"+ , ghcOptExtra = hcProfOptions GHC libBi+ }+ ghcArgs+ | withVanillaLib lbi = vanillaArgs+ | withSharedLib lbi = sharedArgs+ | withProfLib lbi = profArgs+ | otherwise = error "libAbiHash: Can't find an enabled library way"++ (ghcjsProg, _) <- requireProgram verbosity ghcjsProgram (withPrograms lbi)+ hash <-+ getProgramInvocationOutput+ verbosity+ =<< ghcInvocation verbosity ghcjsProg comp platform mbWorkDir ghcArgs+ return (takeWhile (not . isSpace) hash)++componentGhcOptions+ :: Verbosity+ -> LocalBuildInfo+ -> BuildInfo+ -> ComponentLocalBuildInfo+ -> SymbolicPath Pkg (Dir build)+ -> GhcOptions+componentGhcOptions verbosity lbi bi clbi odir =+ let opts = Internal.componentGhcOptions verbosity lbi bi clbi odir+ in opts+ { ghcOptExtra = ghcOptExtra opts `mappend` hcOptions GHCJS bi+ }++-- -----------------------------------------------------------------------------+-- Installing++-- | Install executables for GHCJS.+installExe+ :: Verbosity+ -> LocalBuildInfo+ -> FilePath+ -- ^ Where to copy the files to+ -> FilePath+ -- ^ Build location+ -> (FilePath, FilePath)+ -- ^ Executable (prefix,suffix)+ -> PackageDescription+ -> Executable+ -> IO ()+installExe+ verbosity+ lbi+ binDir+ buildPref+ (progprefix, progsuffix)+ _pkg+ exe = do+ createDirectoryIfMissingVerbose verbosity True binDir+ let exeName' = unUnqualComponentName $ exeName exe+ exeFileName = exeName'+ fixedExeBaseName = progprefix ++ exeName' ++ progsuffix+ installBinary dest = do+ runDbProgramCwd verbosity (mbWorkDirLBI lbi) ghcjsProgram (withPrograms lbi) $+ [ "--install-executable"+ , buildPref </> exeName' </> exeFileName+ , "-o"+ , dest+ ]+ ++ case (stripExes lbi, lookupProgram stripProgram $ withPrograms lbi) of+ (True, Just strip) -> ["-strip-program", programPath strip]+ _ -> []+ installBinary (binDir </> fixedExeBaseName)++-- | Install foreign library for GHC.+installFLib+ :: Verbosity+ -> LocalBuildInfo+ -> FilePath+ -- ^ install location+ -> FilePath+ -- ^ Build location+ -> PackageDescription+ -> ForeignLib+ -> IO ()+installFLib verbosity lbi targetDir builtDir _pkg flib =+ install+ (foreignLibIsShared flib)+ builtDir+ targetDir+ (flibTargetName lbi flib)+ where+ install _isShared srcDir dstDir name = do+ let src = srcDir </> name+ dst = dstDir </> name+ createDirectoryIfMissingVerbose verbosity True targetDir+ installOrdinaryFile verbosity src dst++-- | Install for ghc, .hi, .a and, if --with-ghci given, .o+installLib+ :: Verbosity+ -> LocalBuildInfo+ -> FilePath+ -- ^ install location+ -> FilePath+ -- ^ install location for dynamic libraries+ -> FilePath+ -- ^ Build location+ -> PackageDescription+ -> Library+ -> ComponentLocalBuildInfo+ -> IO ()+installLib verbosity lbi targetDir dynlibTargetDir _builtDir _pkg lib clbi = do+ whenVanilla $ copyModuleFiles $ Suffix "js_hi"+ whenProf $ copyModuleFiles $ Suffix "js_p_hi"+ whenShared $ copyModuleFiles $ Suffix "js_dyn_hi"++ -- whenVanilla $ installOrdinary builtDir targetDir $ toJSLibName vanillaLibName+ -- whenProf $ installOrdinary builtDir targetDir $ toJSLibName profileLibName+ -- whenShared $ installShared builtDir dynlibTargetDir $ toJSLibName sharedLibName+ -- fixme do these make the correct lib names?+ whenHasCode $ do+ whenVanilla $ do+ sequence_+ [ installOrdinary builtDir' targetDir (toJSLibName $ mkGenericStaticLibName (l ++ f))+ | l <- getHSLibraryName (componentUnitId clbi) : (extraBundledLibs (libBuildInfo lib))+ , f <- "" : extraLibFlavours (libBuildInfo lib)+ ]+ -- whenGHCi $ installOrdinary builtDir targetDir (toJSLibName ghciLibName)+ whenProf $ do+ installOrdinary builtDir' targetDir (toJSLibName profileLibName)+ -- whenGHCi $ installOrdinary builtDir targetDir (toJSLibName ghciProfLibName)+ whenShared $+ sequence_+ [ installShared+ builtDir'+ dynlibTargetDir+ (toJSLibName $ mkGenericSharedLibName platform compiler_id (l ++ f))+ | l <- getHSLibraryName uid : extraBundledLibs (libBuildInfo lib)+ , f <- "" : extraDynLibFlavours (libBuildInfo lib)+ ]+ where+ i = interpretSymbolicPathLBI lbi -- See Note [Symbolic paths] in Distribution.Utils.Path+ builtDir' = componentBuildDir lbi clbi+ mbWorkDir = mbWorkDirLBI lbi++ install isShared isJS srcDir dstDir name = do+ let src = i $ srcDir </> makeRelativePathEx name+ dst = dstDir </> name+ createDirectoryIfMissingVerbose verbosity True dstDir++ if isShared+ then installExecutableFile verbosity src dst+ else installOrdinaryFile verbosity src dst++ when (stripLibs lbi && not isJS) $+ Strip.stripLib+ verbosity+ (hostPlatform lbi)+ (withPrograms lbi)+ dst++ installOrdinary = install False True+ installShared = install True True++ copyModuleFiles ext = do+ files <- findModuleFilesCwd verbosity mbWorkDir [builtDir'] [ext] (allLibModules lib clbi)+ let files' = map (i *** getSymbolicPath) files+ installOrdinaryFiles verbosity targetDir files'++ compiler_id = compilerId (compiler lbi)+ platform = hostPlatform lbi+ uid = componentUnitId clbi+ -- vanillaLibName = mkLibName uid+ profileLibName = mkProfLibName uid+ -- sharedLibName = (mkSharedLibName (hostPlatform lbi) compiler_id) uid++ hasLib =+ not $+ null (allLibModules lib clbi)+ && null (cSources (libBuildInfo lib))+ && null (cxxSources (libBuildInfo lib))+ && null (jsSources (libBuildInfo lib))+ has_code = not (componentIsIndefinite clbi)+ whenHasCode = when has_code+ whenVanilla = when (hasLib && withVanillaLib lbi)+ whenProf = when (hasLib && withProfLib lbi && has_code)+ -- whenGHCi = when (hasLib && withGHCiLib lbi && has_code)+ whenShared = when (hasLib && withSharedLib lbi && has_code)++adjustExts :: String -> String -> GhcOptions -> GhcOptions+adjustExts hiSuf objSuf opts =+ opts+ `mappend` mempty+ { ghcOptHiSuffix = toFlag hiSuf+ , ghcOptObjSuffix = toFlag objSuf+ }++isDynamic :: Compiler -> Bool+isDynamic = Internal.ghcLookupProperty "GHC Dynamic"++supportsDynamicToo :: Compiler -> Bool+supportsDynamicToo = Internal.ghcLookupProperty "Support dynamic-too"++withExt :: FilePath -> String -> FilePath+withExt fp ext = fp <.> if takeExtension fp /= ('.' : ext) then ext else ""++findGhcjsGhcVersion :: Verbosity -> FilePath -> IO (Maybe Version)+findGhcjsGhcVersion verbosity pgm =+ findProgramVersion "--numeric-ghc-version" id verbosity pgm++findGhcjsPkgGhcjsVersion :: Verbosity -> FilePath -> IO (Maybe Version)+findGhcjsPkgGhcjsVersion verbosity pgm =+ findProgramVersion "--numeric-ghcjs-version" id verbosity pgm++-- -----------------------------------------------------------------------------+-- Registering++hcPkgInfo :: ProgramDb -> HcPkg.HcPkgInfo+hcPkgInfo progdb =+ HcPkg.HcPkgInfo+ { HcPkg.hcPkgProgram = ghcjsPkgProg+ , HcPkg.noPkgDbStack = False+ , HcPkg.noVerboseFlag = False+ , HcPkg.flagPackageConf = False+ , HcPkg.supportsDirDbs = True+ , HcPkg.requiresDirDbs = ver >= v7_10+ , HcPkg.nativeMultiInstance = ver >= v7_10+ , HcPkg.recacheMultiInstance = True+ , HcPkg.suppressFilesCheck = True+ }+ where+ v7_10 = mkVersion [7, 10]+ ghcjsPkgProg = fromMaybe (error "GHCJS.hcPkgInfo no ghcjs program") $ lookupProgram ghcjsPkgProgram progdb+ ver = fromMaybe (error "GHCJS.hcPkgInfo no ghcjs version") $ programVersion ghcjsPkgProg++registerPackage+ :: Verbosity+ -> ProgramDb+ -> Maybe (SymbolicPath CWD (Dir from))+ -> PackageDBStackS from+ -> InstalledPackageInfo+ -> HcPkg.RegisterOptions+ -> IO ()+registerPackage verbosity progdb mbWorkDir packageDbs installedPkgInfo registerOptions =+ HcPkg.register+ (hcPkgInfo progdb)+ verbosity+ mbWorkDir+ packageDbs+ installedPkgInfo+ registerOptions++pkgRoot :: Verbosity -> LocalBuildInfo -> PackageDB -> IO FilePath+pkgRoot verbosity lbi = pkgRoot'+ where+ pkgRoot' GlobalPackageDB =+ let ghcjsProg = fromMaybe (error "GHCJS.pkgRoot: no ghcjs program") $ lookupProgram ghcjsProgram (withPrograms lbi)+ in fmap takeDirectory (getGlobalPackageDB verbosity ghcjsProg)+ pkgRoot' UserPackageDB = do+ appDir <- getAppUserDataDirectory "ghcjs"+ -- fixme correct this version+ let ver = compilerVersion (compiler lbi)+ subdir =+ System.Info.arch+ ++ '-'+ : System.Info.os+ ++ '-'+ : prettyShow ver+ rootDir = appDir </> subdir+ -- We must create the root directory for the user package database if it+ -- does not yet exists. Otherwise '${pkgroot}' will resolve to a+ -- directory at the time of 'ghc-pkg register', and registration will+ -- fail.+ createDirectoryIfMissing True rootDir+ return rootDir+ pkgRoot' (SpecificPackageDB fp) =+ return $+ takeDirectory $+ interpretSymbolicPathLBI lbi fp++-- | Get the JavaScript file name and command and arguments to run a+-- program compiled by GHCJS+-- the exe should be the base program name without exe extension+runCmd+ :: ProgramDb+ -> FilePath+ -> (FilePath, FilePath, [String]) runCmd progdb exe = ( script , programPath ghcjsProg
src/Distribution/Simple/Glob.hs view
@@ -1,8 +1,10 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Glob -- Copyright : Isaac Jones, Simon Marlow 2003-2004@@ -13,246 +15,333 @@ -- Portability : portable -- -- Simple file globbing.+module Distribution.Simple.Glob+ ( -- * Globs+ Glob -module Distribution.Simple.Glob (- GlobSyntaxError(..),- GlobResult(..),- matchDirFileGlob,- matchDirFileGlobWithDie,- runDirFileGlob,- fileGlobMatches,- parseFileGlob,- explainGlobSyntaxError,- Glob,- ) where+ -- * Matching on globs+ , GlobResult (..)+ , globMatches+ , fileGlobMatches+ , matchGlob+ , matchGlobPieces+ , matchDirFileGlob+ , matchDirFileGlobWithDie+ , runDirFileGlob -import Prelude ()+ -- * Parsing globs+ , parseFileGlob+ , GlobSyntaxError (..)+ , explainGlobSyntaxError++ -- * Utility+ , isRecursiveInRoot+ )+where+ import Distribution.Compat.Prelude+import Prelude () import Distribution.CabalSpecVersion+ ( CabalSpecVersion (..)+ )+import Distribution.Pretty+import Distribution.Simple.Errors+ ( CabalException (MatchDirFileGlob, MatchDirFileGlobErrors)+ )+import Distribution.Simple.Glob.Internal import Distribution.Simple.Utils+ ( debug+ , dieWithException+ , getDirectoryContentsRecursive+ , warn+ )+import Distribution.Utils.Path import Distribution.Verbosity+ ( Verbosity+ , silent+ ) -import System.Directory (getDirectoryContents, doesDirectoryExist, doesFileExist)-import System.FilePath (joinPath, splitExtensions, splitDirectories, takeFileName, (</>), (<.>))+import Control.Monad (mapM)+import Data.List (stripPrefix)+import System.Directory+import System.FilePath hiding ((<.>), (</>)) -import qualified Data.List.NonEmpty as NE+------------------------------------------------------------------------------- --- Note throughout that we use splitDirectories, not splitPath. On--- Posix, this makes no difference, but, because Windows accepts both--- slash and backslash as its path separators, if we left in the--- separators from the glob we might not end up properly normalised.+-- * Matching -data GlobResult a- = GlobMatch a- -- ^ The glob matched the value supplied.- | GlobWarnMultiDot a- -- ^ The glob did not match the value supplied because the- -- cabal-version is too low and the extensions on the file did- -- not precisely match the glob's extensions, but rather the- -- glob was a proper suffix of the file's extensions; i.e., if- -- not for the low cabal-version, it would have matched.- | GlobMissingDirectory FilePath- -- ^ The glob couldn't match because the directory named doesn't- -- exist. The directory will be as it appears in the glob (i.e.,- -- relative to the directory passed to 'matchDirFileGlob', and,- -- for 'data-files', relative to 'data-dir').- deriving (Show, Eq, Ord, Functor)+-------------------------------------------------------------------------------- +-- | Match a 'Glob' against the file system, starting from a+-- given root directory. The results are all relative to the given root.+--+-- @since 3.12.0.0+matchGlob :: FilePath -> Glob -> IO [FilePath]+matchGlob root glob =+ -- For this function, which is the general globbing one (doesn't care about+ -- cabal spec, used e.g. for monitoring), we consider all matches.+ mapMaybe+ ( \case+ GlobMatch a -> Just a+ GlobWarnMultiDot a -> Just a+ GlobMatchesDirectory a -> Just a+ GlobMissingDirectory{} -> Nothing+ )+ <$> runDirFileGlob silent Nothing root glob++-- | Match a globbing pattern against a file path component+matchGlobPieces :: GlobPieces -> String -> Bool+matchGlobPieces = goStart+ where+ -- From the man page, glob(7):+ -- "If a filename starts with a '.', this character must be+ -- matched explicitly."++ go, goStart :: [GlobPiece] -> String -> Bool++ goStart (WildCard : _) ('.' : _) = False+ goStart (Union globs : rest) cs =+ any+ (\glob -> goStart (glob ++ rest) cs)+ globs+ goStart rest cs = go rest cs++ go [] "" = True+ go (Literal lit : rest) cs+ | Just cs' <- stripPrefix lit cs =+ go rest cs'+ | otherwise = False+ go [WildCard] "" = True+ go (WildCard : rest) (c : cs) = go rest (c : cs) || go (WildCard : rest) cs+ go (Union globs : rest) cs = any (\glob -> go (glob ++ rest) cs) globs+ go [] (_ : _) = False+ go (_ : _) "" = False+ -- | Extract the matches from a list of 'GlobResult's. -- -- Note: throws away the 'GlobMissingDirectory' results; chances are -- that you want to check for these and error out if any are present.+--+-- @since 3.12.0.0 globMatches :: [GlobResult a] -> [a]-globMatches input = [ a | GlobMatch a <- input ]--data GlobSyntaxError- = StarInDirectory- | StarInFileName- | StarInExtension- | NoExtensionOnStar- | EmptyGlob- | LiteralFileNameGlobStar- | VersionDoesNotSupportGlobStar- | VersionDoesNotSupportGlob- deriving (Eq, Show)+globMatches input = [a | GlobMatch a <- input] -explainGlobSyntaxError :: FilePath -> GlobSyntaxError -> String-explainGlobSyntaxError filepath StarInDirectory =- "invalid file glob '" ++ filepath- ++ "'. A wildcard '**' is only allowed as the final parent"- ++ " directory. Stars must not otherwise appear in the parent"- ++ " directories."-explainGlobSyntaxError filepath StarInExtension =- "invalid file glob '" ++ filepath- ++ "'. Wildcards '*' are only allowed as the"- ++ " file's base name, not in the file extension."-explainGlobSyntaxError filepath StarInFileName =- "invalid file glob '" ++ filepath- ++ "'. Wildcards '*' may only totally replace the"- ++ " file's base name, not only parts of it."-explainGlobSyntaxError filepath NoExtensionOnStar =- "invalid file glob '" ++ filepath- ++ "'. If a wildcard '*' is used it must be with an file extension."-explainGlobSyntaxError filepath LiteralFileNameGlobStar =- "invalid file glob '" ++ filepath- ++ "'. If a wildcard '**' is used as a parent directory, the"- ++ " file's base name must be a wildcard '*'."-explainGlobSyntaxError _ EmptyGlob =- "invalid file glob. A glob cannot be the empty string."-explainGlobSyntaxError filepath VersionDoesNotSupportGlobStar =- "invalid file glob '" ++ filepath- ++ "'. Using the double-star syntax requires 'cabal-version: 2.4'"- ++ " or greater. Alternatively, for compatibility with earlier Cabal"- ++ " versions, list the included directories explicitly."-explainGlobSyntaxError filepath VersionDoesNotSupportGlob =- "invalid file glob '" ++ filepath- ++ "'. Using star wildcards requires 'cabal-version: >= 1.6'. "- ++ "Alternatively if you require compatibility with earlier Cabal "- ++ "versions then list all the files explicitly."+-- | This will 'die'' when the glob matches no files, or if the glob+-- refers to a missing directory, or if the glob fails to parse.+--+-- The 'Version' argument must be the spec version of the package+-- description being processed, as globs behave slightly differently+-- in different spec versions.+--+-- The first 'FilePath' argument is the directory that the glob is+-- relative to. It must be a valid directory (and hence it can't be+-- the empty string). The returned values will not include this+-- prefix.+--+-- The second 'FilePath' is the glob itself.+matchDirFileGlob+ :: Verbosity+ -> CabalSpecVersion+ -> Maybe (SymbolicPath CWD (Dir dir))+ -> SymbolicPathX allowAbs dir file+ -> IO [SymbolicPathX allowAbs dir file]+matchDirFileGlob v = matchDirFileGlobWithDie v dieWithException -data IsRecursive = Recursive | NonRecursive+-- | Like 'matchDirFileGlob' but with customizable 'die'+--+-- @since 3.6.0.0+matchDirFileGlobWithDie+ :: Verbosity+ -> (forall res. Verbosity -> CabalException -> IO [res])+ -> CabalSpecVersion+ -> Maybe (SymbolicPath CWD (Dir dir))+ -> SymbolicPathX allowAbs dir file+ -> IO [SymbolicPathX allowAbs dir file]+matchDirFileGlobWithDie verbosity rip version mbWorkDir symPath =+ let rawFilePath = getSymbolicPath symPath+ dir = maybe "." getSymbolicPath mbWorkDir+ in case parseFileGlob version rawFilePath of+ Left err -> rip verbosity $ MatchDirFileGlob (explainGlobSyntaxError rawFilePath err)+ Right glob -> do+ results <- runDirFileGlob verbosity (Just version) dir glob+ let missingDirectories =+ [missingDir | GlobMissingDirectory missingDir <- results]+ matches = globMatches results+ directoryMatches = [a | GlobMatchesDirectory a <- results] -data MultiDot = MultiDotDisabled | MultiDotEnabled+ let errors :: [String]+ errors =+ [ "filepath wildcard '"+ ++ rawFilePath+ ++ "' refers to the directory"+ ++ " '"+ ++ missingDir+ ++ "', which does not exist or is not a directory."+ | missingDir <- missingDirectories+ ]+ ++ [ "filepath wildcard '" ++ rawFilePath ++ "' does not match any files."+ | null matches && null directoryMatches+ -- we don't error out on directory matches, simply warn about them and ignore.+ ] -data Glob- = GlobStem FilePath Glob- -- ^ A single subdirectory component + remainder.- | GlobFinal GlobFinal+ warns :: [String]+ warns =+ [ "Ignoring directory '" ++ path ++ "'" ++ " listed in a Cabal package field which should only include files (not directories)."+ | path <- directoryMatches+ ] -data GlobFinal- = FinalMatch IsRecursive MultiDot String- -- ^ First argument: Is this a @**/*.ext@ pattern?- -- Second argument: should we match against the exact extensions, or accept a suffix?- -- Third argument: the extensions to accept.- | FinalLit FilePath- -- ^ Literal file name.+ if null errors+ then do+ unless (null warns) $+ warn verbosity $+ unlines warns+ return $ map unsafeMakeSymbolicPath matches+ else rip verbosity $ MatchDirFileGlobErrors errors -reconstructGlob :: Glob -> FilePath-reconstructGlob (GlobStem dir glob) =- dir </> reconstructGlob glob-reconstructGlob (GlobFinal final) = case final of- FinalMatch Recursive _ exts -> "**" </> "*" <.> exts- FinalMatch NonRecursive _ exts -> "*" <.> exts- FinalLit path -> path+------------------------------------------------------------------------------- --- | Returns 'Nothing' if the glob didn't match at all, or 'Just' the--- result if the glob matched (or would have matched with a higher--- cabal-version).-fileGlobMatches :: Glob -> FilePath -> Maybe (GlobResult FilePath)-fileGlobMatches pat candidate = do- match <- fileGlobMatchesSegments pat (splitDirectories candidate)- return (candidate <$ match)+-- * Parsing & printing -fileGlobMatchesSegments :: Glob -> [FilePath] -> Maybe (GlobResult ())-fileGlobMatchesSegments _ [] = Nothing-fileGlobMatchesSegments pat (seg : segs) = case pat of- GlobStem dir pat' -> do- guard (dir == seg)- fileGlobMatchesSegments pat' segs- GlobFinal final -> case final of- FinalMatch Recursive multidot ext -> do- let (candidateBase, candidateExts) = splitExtensions (NE.last $ seg:|segs)- guard (not (null candidateBase))- checkExt multidot ext candidateExts- FinalMatch NonRecursive multidot ext -> do- let (candidateBase, candidateExts) = splitExtensions seg- guard (null segs && not (null candidateBase))- checkExt multidot ext candidateExts- FinalLit filename -> do- guard (null segs && filename == seg)- return (GlobMatch ())+--------------------------------------------------------------------------------+-- Filepaths with globs may be parsed in the special context is globbing in+-- cabal package fields, such as `data-files`. In that case, we restrict the+-- globbing syntax to that supported by the cabal spec version in use.+-- Otherwise, we parse the globs to the extent of our globbing features+-- (wildcards `*`, unions `{a,b,c}`, and directory-recursive wildcards `**`). -checkExt- :: MultiDot- -> String -- ^ The pattern's extension- -> String -- ^ The candidate file's extension- -> Maybe (GlobResult ())-checkExt multidot ext candidate- | ext == candidate = Just (GlobMatch ())- | ext `isSuffixOf` candidate = case multidot of- MultiDotDisabled -> Just (GlobWarnMultiDot ())- MultiDotEnabled -> Just (GlobMatch ())- | otherwise = Nothing+-- ** Parsing globs in a cabal package parseFileGlob :: CabalSpecVersion -> FilePath -> Either GlobSyntaxError Glob parseFileGlob version filepath = case reverse (splitDirectories filepath) of [] ->- Left EmptyGlob+ Left EmptyGlob (filename : "**" : segments) | allowGlobStar -> do- ext <- case splitExtensions filename of- ("*", ext) | '*' `elem` ext -> Left StarInExtension- | null ext -> Left NoExtensionOnStar- | otherwise -> Right ext- _ -> Left LiteralFileNameGlobStar- foldM addStem (GlobFinal $ FinalMatch Recursive multidot ext) segments+ finalSegment <- case splitExtensions filename of+ ("*", ext)+ | '*' `elem` ext -> Left StarInExtension+ | null ext -> Left NoExtensionOnStar+ | otherwise -> Right (GlobDirRecursive [WildCard, Literal ext])+ _+ | allowLiteralFilenameGlobStar ->+ Right (GlobDirRecursive [Literal filename])+ | otherwise ->+ Left LiteralFileNameGlobStar++ foldM addStem finalSegment segments | otherwise -> Left VersionDoesNotSupportGlobStar (filename : segments) -> do- pat <- case splitExtensions filename of- ("*", ext) | not allowGlob -> Left VersionDoesNotSupportGlob- | '*' `elem` ext -> Left StarInExtension- | null ext -> Left NoExtensionOnStar- | otherwise -> Right (FinalMatch NonRecursive multidot ext)- (_, ext) | '*' `elem` ext -> Left StarInExtension- | '*' `elem` filename -> Left StarInFileName- | otherwise -> Right (FinalLit filename)- foldM addStem (GlobFinal pat) segments+ pat <- case splitExtensions filename of+ ("*", ext)+ | not allowGlob -> Left VersionDoesNotSupportGlob+ | '*' `elem` ext -> Left StarInExtension+ | null ext -> Left NoExtensionOnStar+ | otherwise -> Right (GlobFile [WildCard, Literal ext])+ (_, ext)+ | '*' `elem` ext -> Left StarInExtension+ | '*' `elem` filename -> Left StarInFileName+ | otherwise -> Right (GlobFile [Literal filename])++ foldM addStem pat segments where- allowGlob = version >= CabalSpecV1_6- allowGlobStar = version >= CabalSpecV2_4 addStem pat seg | '*' `elem` seg = Left StarInDirectory- | otherwise = Right (GlobStem seg pat)- multidot- | version >= CabalSpecV2_4 = MultiDotEnabled- | otherwise = MultiDotDisabled+ | otherwise = Right (GlobDir [Literal seg] pat)+ allowGlob = version >= CabalSpecV1_6+ allowGlobStar = version >= CabalSpecV2_4+ allowLiteralFilenameGlobStar = version >= CabalSpecV3_8 --- | This will 'die'' when the glob matches no files, or if the glob--- refers to a missing directory, or if the glob fails to parse.------ The 'Version' argument must be the spec version of the package--- description being processed, as globs behave slightly differently--- in different spec versions.------ The first 'FilePath' argument is the directory that the glob is--- relative to. It must be a valid directory (and hence it can't be--- the empty string). The returned values will not include this--- prefix.------ The second 'FilePath' is the glob itself.----matchDirFileGlob :: Verbosity -> CabalSpecVersion -> FilePath -> FilePath -> IO [FilePath]-matchDirFileGlob v = matchDirFileGlobWithDie v die'+enableMultidot :: CabalSpecVersion -> Bool+enableMultidot version+ | version >= CabalSpecV2_4 = True+ | otherwise = False --- | Like 'matchDirFileGlob' but with customizable 'die'------ @since 3.6.0.0----matchDirFileGlobWithDie :: Verbosity -> (Verbosity -> String -> IO [FilePath]) -> CabalSpecVersion -> FilePath -> FilePath -> IO [FilePath]-matchDirFileGlobWithDie verbosity rip version dir filepath = case parseFileGlob version filepath of- Left err -> rip verbosity $ explainGlobSyntaxError filepath err- Right glob -> do- results <- runDirFileGlob verbosity dir glob- let missingDirectories =- [ missingDir | GlobMissingDirectory missingDir <- results ]- matches = globMatches results+--------------------------------------------------------------------------------+-- Parse and printing utils+-------------------------------------------------------------------------------- - let errors :: [String]- errors =- [ "filepath wildcard '" ++ filepath ++ "' refers to the directory"- ++ " '" ++ missingDir ++ "', which does not exist or is not a directory."- | missingDir <- missingDirectories- ]- ++- [ "filepath wildcard '" ++ filepath ++ "' does not match any files."- | null matches- ]+-- ** Cabal package globbing errors - if null errors- then return matches- else rip verbosity $ unlines errors+data GlobSyntaxError+ = StarInDirectory+ | StarInFileName+ | StarInExtension+ | NoExtensionOnStar+ | EmptyGlob+ | LiteralFileNameGlobStar+ | VersionDoesNotSupportGlobStar+ | VersionDoesNotSupportGlob+ deriving (Eq, Show) +explainGlobSyntaxError :: FilePath -> GlobSyntaxError -> String+explainGlobSyntaxError filepath StarInDirectory =+ "invalid file glob '"+ ++ filepath+ ++ "'. A wildcard '**' is only allowed as the final parent"+ ++ " directory. Stars must not otherwise appear in the parent"+ ++ " directories."+explainGlobSyntaxError filepath StarInExtension =+ "invalid file glob '"+ ++ filepath+ ++ "'. Wildcards '*' are only allowed as the"+ ++ " file's base name, not in the file extension."+explainGlobSyntaxError filepath StarInFileName =+ "invalid file glob '"+ ++ filepath+ ++ "'. Wildcards '*' may only totally replace the"+ ++ " file's base name, not only parts of it."+explainGlobSyntaxError filepath NoExtensionOnStar =+ "invalid file glob '"+ ++ filepath+ ++ "'. If a wildcard '*' is used it must be with an file extension."+explainGlobSyntaxError filepath LiteralFileNameGlobStar =+ "invalid file glob '"+ ++ filepath+ ++ "'. Prior to 'cabal-version: 3.8'"+ ++ " if a wildcard '**' is used as a parent directory, the"+ ++ " file's base name must be a wildcard '*'."+explainGlobSyntaxError _ EmptyGlob =+ "invalid file glob. A glob cannot be the empty string."+explainGlobSyntaxError filepath VersionDoesNotSupportGlobStar =+ "invalid file glob '"+ ++ filepath+ ++ "'. Using the double-star syntax requires 'cabal-version: 2.4'"+ ++ " or greater. Alternatively, for compatibility with earlier Cabal"+ ++ " versions, list the included directories explicitly."+explainGlobSyntaxError filepath VersionDoesNotSupportGlob =+ "invalid file glob '"+ ++ filepath+ ++ "'. Using star wildcards requires 'cabal-version: >= 1.6'. "+ ++ "Alternatively if you require compatibility with earlier Cabal "+ ++ "versions then list all the files explicitly."++-- Note throughout that we use splitDirectories, not splitPath. On+-- Posix, this makes no difference, but, because Windows accepts both+-- slash and backslash as its path separators, if we left in the+-- separators from the glob we might not end up properly normalised.++data GlobResult a+ = -- | The glob matched the value supplied.+ GlobMatch a+ | -- | The glob did not match the value supplied because the+ -- cabal-version is too low and the extensions on the file did+ -- not precisely match the glob's extensions, but rather the+ -- glob was a proper suffix of the file's extensions; i.e., if+ -- not for the low cabal-version, it would have matched.+ GlobWarnMultiDot a+ | -- | The glob couldn't match because the directory named doesn't+ -- exist. The directory will be as it appears in the glob (i.e.,+ -- relative to the directory passed to 'matchDirFileGlob', and,+ -- for 'data-files', relative to 'data-dir').+ GlobMissingDirectory a+ | -- | The glob matched a directory when we were looking for files only.+ -- It didn't match a file!+ --+ -- @since 3.12.0.0+ GlobMatchesDirectory a+ deriving (Show, Eq, Ord, Functor)+ -- | Match files against a pre-parsed glob, starting in a directory. -- -- The 'Version' argument must be the spec version of the package@@ -262,18 +351,24 @@ -- The 'FilePath' argument is the directory that the glob is relative -- to. It must be a valid directory (and hence it can't be the empty -- string). The returned values will not include this prefix.-runDirFileGlob :: Verbosity -> FilePath -> Glob -> IO [GlobResult FilePath]-runDirFileGlob verbosity rawDir pat = do+runDirFileGlob+ :: Verbosity+ -> Maybe CabalSpecVersion+ -- ^ If the glob we are running should care about the cabal spec, and warnings such as 'GlobWarnMultiDot', then this should be the version.+ -- If you want to run a glob but don't care about any of the cabal-spec restrictions on globs, use 'Nothing'!+ -> FilePath+ -> Glob+ -> IO [GlobResult FilePath]+runDirFileGlob verbosity mspec rawRoot pat = do -- The default data-dir is null. Our callers -should- be -- converting that to '.' themselves, but it's a certainty that -- some future call-site will forget and trigger a really -- hard-to-debug failure if we don't check for that here.- when (null rawDir) $+ when (null rawRoot) $ warn verbosity $- "Null dir passed to runDirFileGlob; interpreting it "- ++ "as '.'. This is probably an internal error."- let dir = if null rawDir then "." else rawDir- debug verbosity $ "Expanding glob '" ++ reconstructGlob pat ++ "' in directory '" ++ dir ++ "'."+ "Null dir passed to runDirFileGlob; interpreting it "+ ++ "as '.'. This is probably an internal error."+ let root = if null rawRoot then "." else rawRoot -- This function might be called from the project root with dir as -- ".". Walking the tree starting there involves going into .git/ -- and dist-newstyle/, which is a lot of work for no reward, so@@ -281,40 +376,147 @@ -- there, and only walk as much as we need to: recursively if **, -- the whole directory if *, and just the specific file if it's a -- literal.- let (prefixSegments, final) = splitConstantPrefix pat- joinedPrefix = joinPath prefixSegments- case final of- FinalMatch recursive multidot exts -> do- let prefix = dir </> joinedPrefix- directoryExists <- doesDirectoryExist prefix+ let+ (prefixSegments, pathOrVariablePattern) = splitConstantPrefix pat+ joinedPrefix = joinPath prefixSegments++ -- The glob matching function depends on whether we care about the cabal version or not+ doesGlobMatch :: GlobPieces -> String -> Maybe (GlobResult ())+ doesGlobMatch glob str = case mspec of+ Just spec -> checkNameMatches spec glob str+ Nothing -> if matchGlobPieces glob str then Just (GlobMatch ()) else Nothing++ go (GlobFile glob) dir = do+ entries <- getDirectoryContents (root </> dir)+ catMaybes+ <$> mapM+ ( \s -> do+ -- When running a glob from a Cabal package description (i.e.+ -- when a cabal spec version is passed as an argument), we+ -- disallow matching a @GlobFile@ against a directory, preferring+ -- @GlobDir dir GlobDirTrailing@ to specify a directory match.+ isFile <- maybe (return True) (const $ doesFileExist (root </> dir </> s)) mspec+ let match = (dir </> s <$) <$> doesGlobMatch glob s+ return $+ if isFile+ then match+ else case match of+ Just (GlobMatch x) -> Just $ GlobMatchesDirectory x+ Just (GlobWarnMultiDot x) -> Just $ GlobMatchesDirectory x+ Just (GlobMatchesDirectory x) -> Just $ GlobMatchesDirectory x+ Just (GlobMissingDirectory x) -> Just $ GlobMissingDirectory x -- this should never match, unless you are in a file-delete-heavy concurrent setting i guess+ Nothing -> Nothing+ )+ entries+ go (GlobDirRecursive glob) dir = do+ entries <- getDirectoryContentsRecursive (root </> dir)+ return $+ mapMaybe+ ( \s -> do+ globMatch <- doesGlobMatch glob (takeFileName s)+ pure ((dir </> s) <$ globMatch)+ )+ entries+ go (GlobDir glob globPath) dir = do+ entries <- getDirectoryContents (root </> dir)+ subdirs <-+ filterM+ ( \subdir ->+ doesDirectoryExist+ (root </> dir </> subdir)+ )+ $ filter (matchGlobPieces glob) entries+ concat <$> traverse (\subdir -> go globPath (dir </> subdir)) subdirs+ go GlobDirTrailing dir = return [GlobMatch dir]++ case pathOrVariablePattern of+ Left filename -> do+ let filepath = joinedPrefix </> filename+ debug verbosity $ "Treating glob as filepath literal '" ++ filepath ++ "' in directory '" ++ root ++ "'."+ directoryExists <- doesDirectoryExist (root </> filepath) if directoryExists- then do- candidates <- case recursive of- Recursive -> getDirectoryContentsRecursive prefix- NonRecursive -> filterM (doesFileExist . (prefix </>)) =<< getDirectoryContents prefix- let checkName candidate = do- let (candidateBase, candidateExts) = splitExtensions $ takeFileName candidate- guard (not (null candidateBase))- match <- checkExt multidot exts candidateExts- return (joinedPrefix </> candidate <$ match)- return $ mapMaybe checkName candidates- else- return [ GlobMissingDirectory joinedPrefix ]- FinalLit fn -> do- exists <- doesFileExist (dir </> joinedPrefix </> fn)- return [ GlobMatch (joinedPrefix </> fn) | exists ]+ then pure [GlobMatchesDirectory filepath]+ else do+ exist <- doesFileExist (root </> filepath)+ pure $+ if exist+ then [GlobMatch filepath]+ else []+ Right variablePattern -> do+ debug verbosity $ "Expanding glob '" ++ show (pretty pat) ++ "' in directory '" ++ root ++ "'."+ directoryExists <- doesDirectoryExist (root </> joinedPrefix)+ if directoryExists+ then go variablePattern joinedPrefix+ else return [GlobMissingDirectory joinedPrefix]+ where+ -- \| Extract the (possibly null) constant prefix from the pattern.+ -- This has the property that, if @(pref, final) = splitConstantPrefix pat@,+ -- then @pat === foldr GlobDir final pref@.+ splitConstantPrefix :: Glob -> ([FilePath], Either FilePath Glob)+ splitConstantPrefix = fmap literalize . unfoldr' step+ where+ literalize (GlobFile [Literal filename]) =+ Left filename+ literalize glob =+ Right glob -unfoldr' :: (a -> Either r (b, a)) -> a -> ([b], r)-unfoldr' f a = case f a of- Left r -> ([], r)- Right (b, a') -> case unfoldr' f a' of- (bs, r) -> (b : bs, r)+ step (GlobDir [Literal seg] pat') = Right (seg, pat')+ step pat' = Left pat' --- | Extract the (possibly null) constant prefix from the pattern.--- This has the property that, if @(pref, final) = splitConstantPrefix pat@,--- then @pat === foldr GlobStem (GlobFinal final) pref@.-splitConstantPrefix :: Glob -> ([FilePath], GlobFinal)-splitConstantPrefix = unfoldr' step+ unfoldr' :: (a -> Either r (b, a)) -> a -> ([b], r)+ unfoldr' f a = case f a of+ Left r -> ([], r)+ Right (b, a') -> case unfoldr' f a' of+ (bs, r) -> (b : bs, r)++-- | Is the root of this relative glob path a directory-recursive wildcard, e.g. @**/*.txt@ ?+isRecursiveInRoot :: Glob -> Bool+isRecursiveInRoot (GlobDirRecursive _) = True+isRecursiveInRoot _ = False++-- | Check how the string matches the glob under this cabal version+checkNameMatches :: CabalSpecVersion -> GlobPieces -> String -> Maybe (GlobResult ())+checkNameMatches spec glob candidate+ -- Check if glob matches in its general form+ | matchGlobPieces glob candidate =+ -- if multidot is supported, then this is a clean match+ if enableMultidot spec+ then pure (GlobMatch ())+ else -- if not, issue a warning saying multidot is needed for the match++ let (_, candidateExts) = splitExtensions $ takeFileName candidate+ extractExts :: GlobPieces -> Maybe String+ extractExts [] = Nothing+ extractExts [Literal lit]+ -- Any literal terminating a glob, and which does have an extension,+ -- returns that extension. Otherwise, recurse until Nothing is returned.+ | let ext = takeExtensions lit+ , ext /= "" =+ Just ext+ extractExts (_ : x) = extractExts x+ in case extractExts glob of+ Just exts+ | exts == candidateExts ->+ return (GlobMatch ())+ | exts `isSuffixOf` candidateExts ->+ return (GlobWarnMultiDot ())+ _ -> return (GlobMatch ())+ | otherwise = empty++-- | How/does the glob match the given filepath, according to the cabal version?+-- Since this is pure, we don't make a distinction between matching on+-- directories or files (i.e. this function won't return 'GlobMatchesDirectory')+fileGlobMatches :: CabalSpecVersion -> Glob -> FilePath -> Maybe (GlobResult ())+fileGlobMatches version g path = go g (splitDirectories path) where- step (GlobStem seg pat) = Right (seg, pat)- step (GlobFinal pat) = Left pat+ go GlobDirTrailing [] = Just (GlobMatch ())+ go (GlobFile glob) [file] = checkNameMatches version glob file+ go (GlobDirRecursive glob) dirs+ | [] <- reverse dirs =+ Nothing -- @dir/**/x.txt@ should not match @dir/hello@+ | file : _ <- reverse dirs =+ checkNameMatches version glob file+ go (GlobDir glob globPath) (dir : dirs) = do+ _ <- checkNameMatches version glob dir -- we only care if dir segment matches+ go globPath dirs+ go _ _ = Nothing
+ src/Distribution/Simple/Glob/Internal.hs view
@@ -0,0 +1,131 @@+{-# LANGUAGE DeriveGeneric #-}++-----------------------------------------------------------------------------++-- |+-- Module : Distribution.Simple.Glob.Internal+-- Copyright : Isaac Jones, Simon Marlow 2003-2004+-- License : BSD3+-- portions Copyright (c) 2007, Galois Inc.+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Internal module for simple file globbing.+-- Please import "Distribution.Simple.Glob" instead.+module Distribution.Simple.Glob.Internal where++import Distribution.Compat.Prelude+import Prelude ()++import qualified Distribution.Compat.CharParsing as P+import Distribution.Parsec+import Distribution.Pretty+import qualified Text.PrettyPrint as Disp++--------------------------------------------------------------------------------++-- | A filepath specified by globbing.+data Glob+ = -- | @<dirGlob>/<glob>@+ GlobDir !GlobPieces !Glob+ | -- | @**/<glob>@, where @**@ denotes recursively traversing+ -- all directories and matching filenames on <glob>.+ GlobDirRecursive !GlobPieces+ | -- | A file glob.+ GlobFile !GlobPieces+ | -- | Trailing dir; a glob ending in @/@.+ GlobDirTrailing+ deriving (Eq, Show, Generic)++instance Binary Glob+instance Structured Glob++-- | A single directory or file component of a globbed path+type GlobPieces = [GlobPiece]++-- | A piece of a globbing pattern+data GlobPiece+ = -- | A wildcard @*@+ WildCard+ | -- | A literal string @dirABC@+ Literal String+ | -- | A union of patterns, e.g. @dir/{a,*.txt,c}/...@+ Union [GlobPieces]+ deriving (Eq, Show, Generic)++instance Binary GlobPiece+instance Structured GlobPiece++--------------------------------------------------------------------------------+-- Parsing & pretty-printing++instance Pretty Glob where+ pretty (GlobDir glob pathglob) =+ dispGlobPieces glob+ Disp.<> Disp.char '/'+ Disp.<> pretty pathglob+ pretty (GlobDirRecursive glob) =+ Disp.text "**/"+ Disp.<> dispGlobPieces glob+ pretty (GlobFile glob) = dispGlobPieces glob+ pretty GlobDirTrailing = Disp.empty++instance Parsec Glob where+ parsec = parsecPath+ where+ parsecPath :: CabalParsing m => m Glob+ parsecPath = do+ glob <- parsecGlob+ dirSep *> (GlobDir glob <$> parsecPath <|> pure (GlobDir glob GlobDirTrailing)) <|> pure (GlobFile glob)+ -- We could support parsing recursive directory search syntax+ -- @**@ here too, rather than just in 'parseFileGlob'++ dirSep :: CabalParsing m => m ()+ dirSep =+ () <$ P.char '/'+ <|> P.try+ ( do+ _ <- P.char '\\'+ -- check this isn't an escape code+ P.notFollowedBy (P.satisfy isGlobEscapedChar)+ )++ parsecGlob :: CabalParsing m => m GlobPieces+ parsecGlob = some parsecPiece+ where+ parsecPiece = P.choice [literal, wildcard, union]++ wildcard = WildCard <$ P.char '*'+ union = Union . toList <$> P.between (P.char '{') (P.char '}') (P.sepByNonEmpty parsecGlob (P.char ','))+ literal = Literal <$> some litchar++ litchar = normal <|> escape++ normal = P.satisfy (\c -> not (isGlobEscapedChar c) && c /= '/' && c /= '\\')+ escape = P.try $ P.char '\\' >> P.satisfy isGlobEscapedChar++dispGlobPieces :: GlobPieces -> Disp.Doc+dispGlobPieces = Disp.hcat . map dispPiece+ where+ dispPiece WildCard = Disp.char '*'+ dispPiece (Literal str) = Disp.text (escape str)+ dispPiece (Union globs) =+ Disp.braces+ ( Disp.hcat+ ( Disp.punctuate+ (Disp.char ',')+ (map dispGlobPieces globs)+ )+ )+ escape [] = []+ escape (c : cs)+ | isGlobEscapedChar c = '\\' : c : escape cs+ | otherwise = c : escape cs++isGlobEscapedChar :: Char -> Bool+isGlobEscapedChar '*' = True+isGlobEscapedChar '{' = True+isGlobEscapedChar '}' = True+isGlobEscapedChar ',' = True+isGlobEscapedChar _ = False
src/Distribution/Simple/Haddock.hs view
@@ -1,926 +1,1634 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}---------------------------------------------------------------------------------- |--- Module : Distribution.Simple.Haddock--- Copyright : Isaac Jones 2003-2005--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ This module deals with the @haddock@ and @hscolour@ commands.--- It uses information about installed packages (from @ghc-pkg@) to find the--- locations of documentation for dependent packages, so it can create links.------ The @hscolour@ support allows generating HTML versions of the original--- source, with coloured syntax highlighting.--module Distribution.Simple.Haddock (- haddock, hscolour,-- haddockPackagePaths- ) where--import Prelude ()-import Distribution.Compat.Prelude--import qualified Distribution.Simple.GHC as GHC-import qualified Distribution.Simple.GHCJS as GHCJS---- local-import Distribution.Backpack.DescribeUnitId-import Distribution.Backpack (OpenModule)-import Distribution.Types.ForeignLib-import Distribution.Types.UnqualComponentName-import Distribution.Types.ComponentLocalBuildInfo-import Distribution.Types.ExecutableScope-import Distribution.Types.LocalBuildInfo-import Distribution.Types.TargetInfo-import Distribution.Types.ExposedModule-import Distribution.Package-import qualified Distribution.ModuleName as ModuleName-import Distribution.PackageDescription-import Distribution.Simple.Compiler-import Distribution.Simple.Glob-import Distribution.Simple.Program.GHC-import Distribution.Simple.Program.ResponseFile-import Distribution.Simple.Program-import Distribution.Simple.PreProcess-import Distribution.Simple.Setup-import Distribution.Simple.Build-import Distribution.Simple.BuildTarget-import Distribution.Simple.InstallDirs-import Distribution.Simple.LocalBuildInfo hiding (substPathTemplate)-import Distribution.Simple.BuildPaths-import Distribution.Simple.Register-import qualified Distribution.Simple.Program.HcPkg as HcPkg-import qualified Distribution.Simple.PackageIndex as PackageIndex-import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo-import Distribution.InstalledPackageInfo ( InstalledPackageInfo )-import Distribution.Simple.Utils-import Distribution.System-import Distribution.Pretty-import Distribution.Parsec (simpleParsec)-import Distribution.Utils.NubList-import Distribution.Version-import qualified Distribution.Utils.ShortText as ShortText--import Distribution.Verbosity-import Language.Haskell.Extension--import Distribution.Compat.Semigroup (All (..), Any (..))--import Control.Monad-import Data.Either ( rights )--import System.Directory (getCurrentDirectory, doesDirectoryExist, doesFileExist)-import System.FilePath ( (</>), (<.>), normalise, isAbsolute )-import System.IO (hClose, hPutStrLn, hSetEncoding, utf8)---- --------------------------------------------------------------------------------- Types---- | A record that represents the arguments to the haddock executable, a product--- monoid.-data HaddockArgs = HaddockArgs {- argInterfaceFile :: Flag FilePath,- -- ^ Path to the interface file, relative to argOutputDir, required.- argPackageName :: Flag PackageIdentifier,- -- ^ Package name, required.- argHideModules :: (All,[ModuleName.ModuleName]),- -- ^ (Hide modules ?, modules to hide)- argIgnoreExports :: Any,- -- ^ Ignore export lists in modules?- argLinkSource :: Flag (Template,Template,Template),- -- ^ (Template for modules, template for symbols, template for lines).- argLinkedSource :: Flag Bool,- -- ^ Generate hyperlinked sources- argQuickJump :: Flag Bool,- -- ^ Generate quickjump index- argCssFile :: Flag FilePath,- -- ^ Optional custom CSS file.- argContents :: Flag String,- -- ^ Optional URL to contents page.- argVerbose :: Any,- argOutput :: Flag [Output],- -- ^ HTML or Hoogle doc or both? Required.- argInterfaces :: [(FilePath, Maybe String, Maybe String)],- -- ^ [(Interface file, URL to the HTML docs and hyperlinked-source for links)].- argOutputDir :: Directory,- -- ^ Where to generate the documentation.- argTitle :: Flag String,- -- ^ Page title, required.- argPrologue :: Flag String,- -- ^ Prologue text, required.- argGhcOptions :: GhcOptions,- -- ^ Additional flags to pass to GHC.- argGhcLibDir :: Flag FilePath,- -- ^ To find the correct GHC, required.- argReexports :: [OpenModule],- -- ^ Re-exported modules- argTargets :: [FilePath]- -- ^ Modules to process.-} deriving Generic---- | The FilePath of a directory, it's a monoid under '(</>)'.-newtype Directory = Dir { unDir' :: FilePath } deriving (Read,Show,Eq,Ord)--unDir :: Directory -> FilePath-unDir = normalise . unDir'--type Template = String--data Output = Html | Hoogle---- --------------------------------------------------------------------------------- Haddock support--haddock :: PackageDescription- -> LocalBuildInfo- -> [PPSuffixHandler]- -> HaddockFlags- -> IO ()-haddock pkg_descr _ _ haddockFlags- | not (hasLibs pkg_descr)- && not (fromFlag $ haddockExecutables haddockFlags)- && not (fromFlag $ haddockTestSuites haddockFlags)- && not (fromFlag $ haddockBenchmarks haddockFlags)- && not (fromFlag $ haddockForeignLibs haddockFlags)- =- warn (fromFlag $ haddockVerbosity haddockFlags) $- "No documentation was generated as this package does not contain "- ++ "a library. Perhaps you want to use the --executables, --tests,"- ++ " --benchmarks or --foreign-libraries flags."--haddock pkg_descr lbi suffixes flags' = do- let verbosity = flag haddockVerbosity- comp = compiler lbi- platform = hostPlatform lbi-- quickJmpFlag = haddockQuickJump flags'- flags = case haddockTarget of- ForDevelopment -> flags'- ForHackage -> flags'- { haddockHoogle = Flag True- , haddockHtml = Flag True- , haddockHtmlLocation = Flag (pkg_url ++ "/docs")- , haddockContents = Flag (toPathTemplate pkg_url)- , haddockLinkedSource = Flag True- , haddockQuickJump = Flag True- }- pkg_url = "/package/$pkg-$version"- flag f = fromFlag $ f flags-- tmpFileOpts = defaultTempFileOptions- { optKeepTempFiles = flag haddockKeepTempFiles }- htmlTemplate = fmap toPathTemplate . flagToMaybe . haddockHtmlLocation- $ flags- haddockTarget =- fromFlagOrDefault ForDevelopment (haddockForHackage flags')-- (haddockProg, version, _) <-- requireProgramVersion verbosity haddockProgram- (orLaterVersion (mkVersion [2,0])) (withPrograms lbi)-- -- various sanity checks- when (flag haddockHoogle && version < mkVersion [2,2]) $- die' verbosity "Haddock 2.0 and 2.1 do not support the --hoogle flag."--- when (flag haddockQuickJump && version < mkVersion [2,19]) $ do- let msg = "Haddock prior to 2.19 does not support the --quickjump flag."- alt = "The generated documentation won't have the QuickJump feature."- if Flag True == quickJmpFlag- then die' verbosity msg- else warn verbosity (msg ++ "\n" ++ alt)-- haddockGhcVersionStr <- getProgramOutput verbosity haddockProg- ["--ghc-version"]- case (simpleParsec haddockGhcVersionStr, compilerCompatVersion GHC comp) of- (Nothing, _) -> die' verbosity "Could not get GHC version from Haddock"- (_, Nothing) -> die' verbosity "Could not get GHC version from compiler"- (Just haddockGhcVersion, Just ghcVersion)- | haddockGhcVersion == ghcVersion -> return ()- | otherwise -> die' verbosity $- "Haddock's internal GHC version must match the configured "- ++ "GHC version.\n"- ++ "The GHC version is " ++ prettyShow ghcVersion ++ " but "- ++ "haddock is using GHC version " ++ prettyShow haddockGhcVersion-- -- the tools match the requests, we can proceed-- -- We fall back to using HsColour only for versions of Haddock which don't- -- support '--hyperlinked-sources'.- when (flag haddockLinkedSource && version < mkVersion [2,17]) $- hscolour' (warn verbosity) haddockTarget pkg_descr lbi suffixes- (defaultHscolourFlags `mappend` haddockToHscolour flags)-- libdirArgs <- getGhcLibDir verbosity lbi- let commonArgs = mconcat- [ libdirArgs- , fromFlags (haddockTemplateEnv lbi (packageId pkg_descr)) flags- , fromPackageDescription haddockTarget pkg_descr ]-- targets <- readTargetInfos verbosity pkg_descr lbi (haddockArgs flags)-- let- targets' =- case targets of- [] -> allTargetsInBuildOrder' pkg_descr lbi- _ -> targets-- internalPackageDB <-- createInternalPackageDB verbosity lbi (flag haddockDistPref)-- (\f -> foldM_ f (installedPkgs lbi) targets') $ \index target -> do-- let component = targetComponent target- clbi = targetCLBI target-- componentInitialBuildSteps (flag haddockDistPref) pkg_descr lbi clbi verbosity-- let- lbi' = lbi {- withPackageDB = withPackageDB lbi ++ [internalPackageDB],- installedPkgs = index- }-- preprocessComponent pkg_descr component lbi' clbi False verbosity suffixes- let- doExe com = case (compToExe com) of- Just exe -> do- withTempDirectoryEx verbosity tmpFileOpts (buildDir lbi') "tmp" $- \tmp -> do- exeArgs <- fromExecutable verbosity tmp lbi' clbi htmlTemplate- version exe- let exeArgs' = commonArgs `mappend` exeArgs- runHaddock verbosity tmpFileOpts comp platform- haddockProg exeArgs'- Nothing -> do- warn (fromFlag $ haddockVerbosity flags)- "Unsupported component, skipping..."- return ()- -- We define 'smsg' once and then reuse it inside the case, so that- -- we don't say we are running Haddock when we actually aren't- -- (e.g., Haddock is not run on non-libraries)- smsg :: IO ()- smsg = setupMessage' verbosity "Running Haddock on" (packageId pkg_descr)- (componentLocalName clbi) (maybeComponentInstantiatedWith clbi)- case component of- CLib lib -> do- withTempDirectoryEx verbosity tmpFileOpts (buildDir lbi) "tmp" $- \tmp -> do- smsg- libArgs <- fromLibrary verbosity tmp lbi' clbi htmlTemplate- version lib- let libArgs' = commonArgs `mappend` libArgs- runHaddock verbosity tmpFileOpts comp platform haddockProg libArgs'-- case libName lib of- LMainLibName ->- pure index- LSubLibName _ -> do- pwd <- getCurrentDirectory-- let- ipi = inplaceInstalledPackageInfo- pwd (flag haddockDistPref) pkg_descr- (mkAbiHash "inplace") lib lbi' clbi-- debug verbosity $ "Registering inplace:\n"- ++ (InstalledPackageInfo.showInstalledPackageInfo ipi)-- registerPackage verbosity (compiler lbi') (withPrograms lbi')- (withPackageDB lbi') ipi- HcPkg.defaultRegisterOptions {- HcPkg.registerMultiInstance = True- }-- return $ PackageIndex.insert ipi index-- CFLib flib -> (when (flag haddockForeignLibs) $ do- withTempDirectoryEx verbosity tmpFileOpts (buildDir lbi') "tmp" $- \tmp -> do- smsg- flibArgs <- fromForeignLib verbosity tmp lbi' clbi htmlTemplate- version flib- let libArgs' = commonArgs `mappend` flibArgs- runHaddock verbosity tmpFileOpts comp platform haddockProg libArgs')-- >> return index-- CExe _ -> (when (flag haddockExecutables) $ smsg >> doExe component) >> return index- CTest _ -> (when (flag haddockTestSuites) $ smsg >> doExe component) >> return index- CBench _ -> (when (flag haddockBenchmarks) $ smsg >> doExe component) >> return index-- for_ (extraDocFiles pkg_descr) $ \ fpath -> do- files <- matchDirFileGlob verbosity (specVersion pkg_descr) "." fpath- for_ files $ copyFileTo verbosity (unDir $ argOutputDir commonArgs)---- --------------------------------------------------------------------------------- Contributions to HaddockArgs (see also Doctest.hs for very similar code).--fromFlags :: PathTemplateEnv -> HaddockFlags -> HaddockArgs-fromFlags env flags =- mempty {- argHideModules = (maybe mempty (All . not)- $ flagToMaybe (haddockInternal flags), mempty),- argLinkSource = if fromFlag (haddockLinkedSource flags)- then Flag ("src/%{MODULE/./-}.html"- ,"src/%{MODULE/./-}.html#%{NAME}"- ,"src/%{MODULE/./-}.html#line-%{LINE}")- else NoFlag,- argLinkedSource = haddockLinkedSource flags,- argQuickJump = haddockQuickJump flags,- argCssFile = haddockCss flags,- argContents = fmap (fromPathTemplate . substPathTemplate env)- (haddockContents flags),- argVerbose = maybe mempty (Any . (>= deafening))- . flagToMaybe $ haddockVerbosity flags,- argOutput =- Flag $ case [ Html | Flag True <- [haddockHtml flags] ] ++- [ Hoogle | Flag True <- [haddockHoogle flags] ]- of [] -> [ Html ]- os -> os,- argOutputDir = maybe mempty Dir . flagToMaybe $ haddockDistPref flags,-- argGhcOptions = mempty { ghcOptExtra = ghcArgs }- }- where- ghcArgs = fromMaybe [] . lookup "ghc" . haddockProgramArgs $ flags--fromPackageDescription :: HaddockTarget -> PackageDescription -> HaddockArgs-fromPackageDescription haddockTarget pkg_descr = mempty- { argInterfaceFile = Flag $ haddockName pkg_descr- , argPackageName = Flag $ packageId $ pkg_descr- , argOutputDir = Dir $- "doc" </> "html" </> haddockDirName haddockTarget pkg_descr- , argPrologue = Flag $ ShortText.fromShortText $- if ShortText.null desc- then synopsis pkg_descr- else desc- , argTitle = Flag $ showPkg ++ subtitle- }- where- desc = description pkg_descr- showPkg = prettyShow (packageId pkg_descr)- subtitle- | ShortText.null (synopsis pkg_descr) = ""- | otherwise = ": " ++ ShortText.fromShortText (synopsis pkg_descr)--componentGhcOptions :: Verbosity -> LocalBuildInfo- -> BuildInfo -> ComponentLocalBuildInfo -> FilePath- -> GhcOptions-componentGhcOptions verbosity lbi bi clbi odir =- let f = case compilerFlavor (compiler lbi) of- GHC -> GHC.componentGhcOptions- GHCJS -> GHCJS.componentGhcOptions- _ -> error $- "Distribution.Simple.Haddock.componentGhcOptions:" ++- "haddock only supports GHC and GHCJS"- in f verbosity lbi bi clbi odir--mkHaddockArgs :: Verbosity- -> FilePath- -> LocalBuildInfo- -> ComponentLocalBuildInfo- -> Maybe PathTemplate -- ^ template for HTML location- -> Version- -> [FilePath]- -> BuildInfo- -> IO HaddockArgs-mkHaddockArgs verbosity tmp lbi clbi htmlTemplate haddockVersion inFiles bi = do- ifaceArgs <- getInterfaces verbosity lbi clbi htmlTemplate- let vanillaOpts = (componentGhcOptions normal lbi bi clbi (buildDir lbi)) {- -- Noooooooooo!!!!!111- -- haddock stomps on our precious .hi- -- and .o files. Workaround by telling- -- haddock to write them elsewhere.- ghcOptObjDir = toFlag tmp,- ghcOptHiDir = toFlag tmp,- ghcOptStubDir = toFlag tmp- } `mappend` getGhcCppOpts haddockVersion bi- sharedOpts = vanillaOpts {- ghcOptDynLinkMode = toFlag GhcDynamicOnly,- ghcOptFPic = toFlag True,- ghcOptHiSuffix = toFlag "dyn_hi",- ghcOptObjSuffix = toFlag "dyn_o",- ghcOptExtra = hcSharedOptions GHC bi-- }- opts <- if withVanillaLib lbi- then return vanillaOpts- else if withSharedLib lbi- then return sharedOpts- else die' verbosity $ "Must have vanilla or shared libraries "- ++ "enabled in order to run haddock"-- return ifaceArgs- { argGhcOptions = opts- , argTargets = inFiles- , argReexports = getReexports clbi- }--fromLibrary :: Verbosity- -> FilePath- -> LocalBuildInfo- -> ComponentLocalBuildInfo- -> Maybe PathTemplate -- ^ template for HTML location- -> Version- -> Library- -> IO HaddockArgs-fromLibrary verbosity tmp lbi clbi htmlTemplate haddockVersion lib = do- inFiles <- map snd `fmap` getLibSourceFiles verbosity lbi lib clbi- args <- mkHaddockArgs verbosity tmp lbi clbi htmlTemplate haddockVersion- inFiles (libBuildInfo lib)- return args {- argHideModules = (mempty, otherModules (libBuildInfo lib))- }--fromExecutable :: Verbosity- -> FilePath- -> LocalBuildInfo- -> ComponentLocalBuildInfo- -> Maybe PathTemplate -- ^ template for HTML location- -> Version- -> Executable- -> IO HaddockArgs-fromExecutable verbosity tmp lbi clbi htmlTemplate haddockVersion exe = do- inFiles <- map snd `fmap` getExeSourceFiles verbosity lbi exe clbi- args <- mkHaddockArgs verbosity tmp lbi clbi htmlTemplate- haddockVersion inFiles (buildInfo exe)- return args {- argOutputDir = Dir $ unUnqualComponentName $ exeName exe,- argTitle = Flag $ unUnqualComponentName $ exeName exe- }--fromForeignLib :: Verbosity- -> FilePath- -> LocalBuildInfo- -> ComponentLocalBuildInfo- -> Maybe PathTemplate -- ^ template for HTML location- -> Version- -> ForeignLib- -> IO HaddockArgs-fromForeignLib verbosity tmp lbi clbi htmlTemplate haddockVersion flib = do- inFiles <- map snd `fmap` getFLibSourceFiles verbosity lbi flib clbi- args <- mkHaddockArgs verbosity tmp lbi clbi htmlTemplate- haddockVersion inFiles (foreignLibBuildInfo flib)- return args {- argOutputDir = Dir $ unUnqualComponentName $ foreignLibName flib,- argTitle = Flag $ unUnqualComponentName $ foreignLibName flib- }--compToExe :: Component -> Maybe Executable-compToExe comp =- case comp of- CTest test@TestSuite { testInterface = TestSuiteExeV10 _ f } ->- Just Executable {- exeName = testName test,- modulePath = f,- exeScope = ExecutablePublic,- buildInfo = testBuildInfo test- }- CBench bench@Benchmark { benchmarkInterface = BenchmarkExeV10 _ f } ->- Just Executable {- exeName = benchmarkName bench,- modulePath = f,- exeScope = ExecutablePublic,- buildInfo = benchmarkBuildInfo bench- }- CExe exe -> Just exe- _ -> Nothing--getInterfaces :: Verbosity- -> LocalBuildInfo- -> ComponentLocalBuildInfo- -> Maybe PathTemplate -- ^ template for HTML location- -> IO HaddockArgs-getInterfaces verbosity lbi clbi htmlTemplate = do- (packageFlags, warnings) <- haddockPackageFlags verbosity lbi clbi htmlTemplate- traverse_ (warn (verboseUnmarkOutput verbosity)) warnings- return $ mempty {- argInterfaces = packageFlags- }--getReexports :: ComponentLocalBuildInfo -> [OpenModule]-getReexports LibComponentLocalBuildInfo {componentExposedModules = mods } =- mapMaybe exposedReexport mods-getReexports _ = []--getGhcCppOpts :: Version- -> BuildInfo- -> GhcOptions-getGhcCppOpts haddockVersion bi =- mempty {- ghcOptExtensions = toNubListR [EnableExtension CPP | needsCpp],- ghcOptCppOptions = defines- }- where- needsCpp = EnableExtension CPP `elem` usedExtensions bi- defines = [haddockVersionMacro]- haddockVersionMacro = "-D__HADDOCK_VERSION__="- ++ show (v1 * 1000 + v2 * 10 + v3)- where- (v1, v2, v3) = case versionNumbers haddockVersion of- [] -> (0,0,0)- [x] -> (x,0,0)- [x,y] -> (x,y,0)- (x:y:z:_) -> (x,y,z)--getGhcLibDir :: Verbosity -> LocalBuildInfo- -> IO HaddockArgs-getGhcLibDir verbosity lbi = do- l <- case compilerFlavor (compiler lbi) of- GHC -> GHC.getLibDir verbosity lbi- GHCJS -> GHCJS.getLibDir verbosity lbi- _ -> error "haddock only supports GHC and GHCJS"- return $ mempty { argGhcLibDir = Flag l }---- --------------------------------------------------------------------------------- | Call haddock with the specified arguments.-runHaddock :: Verbosity- -> TempFileOptions- -> Compiler- -> Platform- -> ConfiguredProgram- -> HaddockArgs- -> IO ()-runHaddock verbosity tmpFileOpts comp platform haddockProg args- | null (argTargets args) = warn verbosity $- "Haddocks are being requested, but there aren't any modules given "- ++ "to create documentation for."- | otherwise = do- let haddockVersion = fromMaybe (error "unable to determine haddock version")- (programVersion haddockProg)- renderArgs verbosity tmpFileOpts haddockVersion comp platform args $- \(flags,result)-> do-- runProgram verbosity haddockProg flags-- notice verbosity $ "Documentation created: " ++ result---renderArgs :: Verbosity- -> TempFileOptions- -> Version- -> Compiler- -> Platform- -> HaddockArgs- -> (([String], FilePath) -> IO a)- -> IO a-renderArgs verbosity tmpFileOpts version comp platform args k = do- let haddockSupportsUTF8 = version >= mkVersion [2,14,4]- haddockSupportsResponseFiles = version > mkVersion [2,16,2]- createDirectoryIfMissingVerbose verbosity True outputDir- withTempFileEx tmpFileOpts outputDir "haddock-prologue.txt" $- \prologueFileName h -> do- do- when haddockSupportsUTF8 (hSetEncoding h utf8)- hPutStrLn h $ fromFlag $ argPrologue args- hClose h- let pflag = "--prologue=" ++ prologueFileName- renderedArgs = pflag : renderPureArgs version comp platform args- if haddockSupportsResponseFiles- then- withResponseFile- verbosity- tmpFileOpts- outputDir- "haddock-response.txt"- (if haddockSupportsUTF8 then Just utf8 else Nothing)- renderedArgs- (\responseFileName -> k (["@" ++ responseFileName], result))- else- k (renderedArgs, result)- where- outputDir = (unDir $ argOutputDir args)- result = intercalate ", "- . map (\o -> outputDir </>- case o of- Html -> "index.html"- Hoogle -> pkgstr <.> "txt")- $ arg argOutput- where- pkgstr = prettyShow $ packageName pkgid- pkgid = arg argPackageName- arg f = fromFlag $ f args--renderPureArgs :: Version -> Compiler -> Platform -> HaddockArgs -> [String]-renderPureArgs version comp platform args = concat- [ (:[]) . (\f -> "--dump-interface="++ unDir (argOutputDir args) </> f)- . fromFlag . argInterfaceFile $ args-- , if isVersion 2 16- then (\pkg -> [ "--package-name=" ++ prettyShow (pkgName pkg)- , "--package-version=" ++ prettyShow (pkgVersion pkg)- ])- . fromFlag . argPackageName $ args- else []-- , [ "--since-qual=external" | isVersion 2 20 ]-- , [ "--quickjump" | isVersion 2 19- , fromFlag . argQuickJump $ args ]-- , [ "--hyperlinked-source" | isVersion 2 17- , fromFlag . argLinkedSource $ args ]-- , (\(All b,xs) -> bool (map (("--hide=" ++) . prettyShow) xs) [] b)- . argHideModules $ args-- , bool ["--ignore-all-exports"] [] . getAny . argIgnoreExports $ args-- , maybe [] (\(m,e,l) ->- ["--source-module=" ++ m- ,"--source-entity=" ++ e]- ++ if isVersion 2 14 then ["--source-entity-line=" ++ l]- else []- ) . flagToMaybe . argLinkSource $ args-- , maybe [] ((:[]) . ("--css="++)) . flagToMaybe . argCssFile $ args-- , maybe [] ((:[]) . ("--use-contents="++)) . flagToMaybe . argContents $ args-- , bool [] [verbosityFlag] . getAny . argVerbose $ args-- , map (\o -> case o of Hoogle -> "--hoogle"; Html -> "--html")- . fromFlag . argOutput $ args-- , renderInterfaces . argInterfaces $ args-- , (:[]) . ("--odir="++) . unDir . argOutputDir $ args-- , (:[]) . ("--title="++)- . (bool (++" (internal documentation)")- id (getAny $ argIgnoreExports args))- . fromFlag . argTitle $ args-- , [ "--optghc=" ++ opt | let opts = argGhcOptions args- , opt <- renderGhcOptions comp platform opts ]-- , maybe [] (\l -> ["-B"++l]) $- flagToMaybe (argGhcLibDir args) -- error if Nothing?-- -- https://github.com/haskell/haddock/pull/547- , [ "--reexport=" ++ prettyShow r- | r <- argReexports args- , isVersion 2 19- ]-- , argTargets $ args- ]- where- renderInterfaces = map renderInterface-- renderInterface :: (FilePath, Maybe FilePath, Maybe FilePath) -> String- renderInterface (i, html, hypsrc) = "--read-interface=" ++- (intercalate "," $ concat [ [ x | Just x <- [html] ]- , [ x | Just _ <- [html]- -- only render hypsrc path if html path- -- is given and hyperlinked-source is- -- enabled- , Just x <- [hypsrc]- , isVersion 2 17- , fromFlag . argLinkedSource $ args- ]- , [ i ]- ])-- bool a b c = if c then a else b- isVersion major minor = version >= mkVersion [major,minor]- verbosityFlag- | isVersion 2 5 = "--verbosity=1"- | otherwise = "--verbose"--------------------------------------------------------------------------------------- | Given a list of 'InstalledPackageInfo's, return a list of interfaces and--- HTML paths, and an optional warning for packages with missing documentation.-haddockPackagePaths :: [InstalledPackageInfo]- -> Maybe (InstalledPackageInfo -> FilePath)- -> IO ([( FilePath -- path to interface- -- file-- , Maybe FilePath -- url to html- -- documentation-- , Maybe FilePath -- url to hyperlinked- -- source- )]- , Maybe String -- warning about- -- missing documentation- )-haddockPackagePaths ipkgs mkHtmlPath = do- interfaces <- sequenceA- [ case interfaceAndHtmlPath ipkg of- Nothing -> return (Left (packageId ipkg))- Just (interface, html) -> do-- (html', hypsrc') <-- case html of- Just htmlPath -> do- let hypSrcPath = htmlPath </> defaultHyperlinkedSourceDirectory- hypSrcExists <- doesDirectoryExist hypSrcPath- return $ ( Just (fixFileUrl htmlPath)- , if hypSrcExists- then Just (fixFileUrl hypSrcPath)- else Nothing- )- Nothing -> return (Nothing, Nothing)-- exists <- doesFileExist interface- if exists- then return (Right (interface, html', hypsrc'))- else return (Left pkgid)- | ipkg <- ipkgs, let pkgid = packageId ipkg- , pkgName pkgid `notElem` noHaddockWhitelist- ]-- let missing = [ pkgid | Left pkgid <- interfaces ]- warning = "The documentation for the following packages are not "- ++ "installed. No links will be generated to these packages: "- ++ intercalate ", " (map prettyShow missing)- flags = rights interfaces-- return (flags, if null missing then Nothing else Just warning)-- where- -- Don't warn about missing documentation for these packages. See #1231.- noHaddockWhitelist = map mkPackageName [ "rts" ]-- -- Actually extract interface and HTML paths from an 'InstalledPackageInfo'.- interfaceAndHtmlPath :: InstalledPackageInfo- -> Maybe (FilePath, Maybe FilePath)- interfaceAndHtmlPath pkg = do- interface <- listToMaybe (InstalledPackageInfo.haddockInterfaces pkg)- html <- case mkHtmlPath of- Nothing -> listToMaybe (InstalledPackageInfo.haddockHTMLs pkg)- Just mkPath -> Just (mkPath pkg)- return (interface, if null html then Nothing else Just html)-- -- The 'haddock-html' field in the hc-pkg output is often set as a- -- native path, but we need it as a URL. See #1064. Also don't "fix"- -- the path if it is an interpolated one.- fixFileUrl f | Nothing <- mkHtmlPath- , isAbsolute f = "file://" ++ f- | otherwise = f-- -- 'src' is the default hyperlinked source directory ever since. It is- -- not possible to configure that directory in any way in haddock.- defaultHyperlinkedSourceDirectory = "src"---haddockPackageFlags :: Verbosity- -> LocalBuildInfo- -> ComponentLocalBuildInfo- -> Maybe PathTemplate- -> IO ([( FilePath -- path to interface- -- file-- , Maybe FilePath -- url to html- -- documentation-- , Maybe FilePath -- url to hyperlinked- -- source- )]- , Maybe String -- warning about- -- missing documentation- )-haddockPackageFlags verbosity lbi clbi htmlTemplate = do- let allPkgs = installedPkgs lbi- directDeps = map fst (componentPackageDeps clbi)- transitiveDeps <- case PackageIndex.dependencyClosure allPkgs directDeps of- Left x -> return x- Right inf -> die' verbosity $ "internal error when calculating transitive "- ++ "package dependencies.\nDebug info: " ++ show inf- haddockPackagePaths (PackageIndex.allPackages transitiveDeps) mkHtmlPath- where- mkHtmlPath = fmap expandTemplateVars htmlTemplate- expandTemplateVars tmpl pkg =- fromPathTemplate . substPathTemplate (env pkg) $ tmpl- env pkg = haddockTemplateEnv lbi (packageId pkg)---haddockTemplateEnv :: LocalBuildInfo -> PackageIdentifier -> PathTemplateEnv-haddockTemplateEnv lbi pkg_id =- (PrefixVar, prefix (installDirTemplates lbi))- -- We want the legacy unit ID here, because it gives us nice paths- -- (Haddock people don't care about the dependencies)- : initialPathTemplateEnv- pkg_id- (mkLegacyUnitId pkg_id)- (compilerInfo (compiler lbi))- (hostPlatform lbi)---- --------------------------------------------------------------------------------- hscolour support.--hscolour :: PackageDescription- -> LocalBuildInfo- -> [PPSuffixHandler]- -> HscolourFlags- -> IO ()-hscolour = hscolour' dieNoVerbosity ForDevelopment--hscolour' :: (String -> IO ()) -- ^ Called when the 'hscolour' exe is not found.- -> HaddockTarget- -> PackageDescription- -> LocalBuildInfo- -> [PPSuffixHandler]- -> HscolourFlags- -> IO ()-hscolour' onNoHsColour haddockTarget pkg_descr lbi suffixes flags =- either onNoHsColour (\(hscolourProg, _, _) -> go hscolourProg) =<<- lookupProgramVersion verbosity hscolourProgram- (orLaterVersion (mkVersion [1,8])) (withPrograms lbi)- where- go :: ConfiguredProgram -> IO ()- go hscolourProg = do- warn verbosity $- "the 'cabal hscolour' command is deprecated in favour of 'cabal " ++- "haddock --hyperlink-source' and will be removed in the next major " ++- "release."-- setupMessage verbosity "Running hscolour for" (packageId pkg_descr)- createDirectoryIfMissingVerbose verbosity True $- hscolourPref haddockTarget distPref pkg_descr-- withAllComponentsInBuildOrder pkg_descr lbi $ \comp clbi -> do- componentInitialBuildSteps distPref pkg_descr lbi clbi verbosity- preprocessComponent pkg_descr comp lbi clbi False verbosity suffixes- let- doExe com = case (compToExe com) of- Just exe -> do- let outputDir = hscolourPref haddockTarget distPref pkg_descr- </> unUnqualComponentName (exeName exe) </> "src"- runHsColour hscolourProg outputDir =<< getExeSourceFiles verbosity lbi exe clbi- Nothing -> do- warn (fromFlag $ hscolourVerbosity flags)- "Unsupported component, skipping..."- return ()- case comp of- CLib lib -> do- let outputDir = hscolourPref haddockTarget distPref pkg_descr </> "src"- runHsColour hscolourProg outputDir =<< getLibSourceFiles verbosity lbi lib clbi- CFLib flib -> do- let outputDir = hscolourPref haddockTarget distPref pkg_descr- </> unUnqualComponentName (foreignLibName flib) </> "src"- runHsColour hscolourProg outputDir =<< getFLibSourceFiles verbosity lbi flib clbi- CExe _ -> when (fromFlag (hscolourExecutables flags)) $ doExe comp- CTest _ -> when (fromFlag (hscolourTestSuites flags)) $ doExe comp- CBench _ -> when (fromFlag (hscolourBenchmarks flags)) $ doExe comp-- stylesheet = flagToMaybe (hscolourCSS flags)-- verbosity = fromFlag (hscolourVerbosity flags)- distPref = fromFlag (hscolourDistPref flags)-- runHsColour prog outputDir moduleFiles = do- createDirectoryIfMissingVerbose verbosity True outputDir-- case stylesheet of -- copy the CSS file- Nothing | programVersion prog >= Just (mkVersion [1,9]) ->- runProgram verbosity prog- ["-print-css", "-o" ++ outputDir </> "hscolour.css"]- | otherwise -> return ()- Just s -> copyFileVerbose verbosity s (outputDir </> "hscolour.css")-- for_ moduleFiles $ \(m, inFile) ->- runProgram verbosity prog- ["-css", "-anchor", "-o" ++ outFile m, inFile]- where- outFile m = outputDir </>- intercalate "-" (ModuleName.components m) <.> "html"--haddockToHscolour :: HaddockFlags -> HscolourFlags-haddockToHscolour flags =- HscolourFlags {- hscolourCSS = haddockHscolourCss flags,- hscolourExecutables = haddockExecutables flags,- hscolourTestSuites = haddockTestSuites flags,- hscolourBenchmarks = haddockBenchmarks flags,- hscolourForeignLibs = haddockForeignLibs flags,- hscolourVerbosity = haddockVerbosity flags,- hscolourDistPref = haddockDistPref flags,- hscolourCabalFilePath = haddockCabalFilePath flags- }---- --------------------------------------------------------------------------------- Boilerplate Monoid instance.-instance Monoid HaddockArgs where- mempty = gmempty- mappend = (<>)--instance Semigroup HaddockArgs where- (<>) = gmappend--instance Monoid Directory where- mempty = Dir "."- mappend = (<>)--instance Semigroup Directory where- Dir m <> Dir n = Dir $ m </> n+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}++-----------------------------------------------------------------------------++-- |+-- Module : Distribution.Simple.Haddock+-- Copyright : Isaac Jones 2003-2005+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- This module deals with the @haddock@ and @hscolour@ commands.+-- It uses information about installed packages (from @ghc-pkg@) to find the+-- locations of documentation for dependent packages, so it can create links.+--+-- The @hscolour@ support allows generating HTML versions of the original+-- source, with coloured syntax highlighting.+module Distribution.Simple.Haddock+ ( haddock+ , haddock_setupHooks+ , createHaddockIndex+ , hscolour+ , hscolour_setupHooks+ , haddockPackagePaths+ , Visibility (..)+ ) where++import Distribution.Compat.Prelude+import Prelude ()++import qualified Distribution.Simple.GHC as GHC+import qualified Distribution.Simple.GHCJS as GHCJS++-- local++import Distribution.Backpack (OpenModule)+import Distribution.Backpack.DescribeUnitId+import Distribution.Compat.Semigroup (All (..), Any (..))+import Distribution.InstalledPackageInfo (InstalledPackageInfo)+import qualified Distribution.InstalledPackageInfo as InstalledPackageInfo+import qualified Distribution.ModuleName as ModuleName+import Distribution.Package+import Distribution.PackageDescription+import Distribution.Parsec (simpleParsec)+import Distribution.Pretty+import Distribution.Simple.Build+import Distribution.Simple.BuildPaths+import Distribution.Simple.BuildTarget+import Distribution.Simple.Compiler+import Distribution.Simple.Errors+import Distribution.Simple.Flag+import Distribution.Simple.Glob (matchDirFileGlob)+import Distribution.Simple.InstallDirs+import Distribution.Simple.LocalBuildInfo hiding (substPathTemplate)+import qualified Distribution.Simple.PackageIndex as PackageIndex+import Distribution.Simple.PreProcess+import Distribution.Simple.Program+import Distribution.Simple.Program.GHC+import qualified Distribution.Simple.Program.HcPkg as HcPkg+import Distribution.Simple.Program.ResponseFile+import Distribution.Simple.Register+import Distribution.Simple.Setup+import Distribution.Simple.SetupHooks.Internal+ ( BuildHooks (..)+ , noBuildHooks+ )+import qualified Distribution.Simple.SetupHooks.Internal as SetupHooks+import qualified Distribution.Simple.SetupHooks.Rule as SetupHooks+import Distribution.Simple.Utils+import Distribution.System+import Distribution.Types.ComponentLocalBuildInfo+import Distribution.Types.ExposedModule+import Distribution.Types.LocalBuildInfo+import Distribution.Types.TargetInfo+import Distribution.Utils.Path hiding+ ( Dir+ )+import qualified Distribution.Utils.Path as Path+import qualified Distribution.Utils.ShortText as ShortText+import Distribution.Verbosity+import Distribution.Version++import Control.Monad+import Data.Bool (bool)+import Data.Either (rights)+import System.Directory (doesDirectoryExist, doesFileExist)+import System.FilePath (isAbsolute, normalise)+import System.IO (hClose, hPutStrLn, hSetEncoding, utf8)++-- ------------------------------------------------------------------------------+-- Types++-- | A record that represents the arguments to the haddock executable, a product+-- monoid.+data HaddockArgs = HaddockArgs+ { argInterfaceFile :: Flag FilePath+ -- ^ Path to the interface file, relative to argOutputDir, required.+ , argPackageName :: Flag PackageIdentifier+ -- ^ Package name, required.+ , argComponentName :: Flag String+ -- ^ Optional name used to construct haddock's `--package-name` option for+ -- various components (tests suites, sublibriaries, etc).+ , argHideModules :: (All, [ModuleName.ModuleName])+ -- ^ (Hide modules ?, modules to hide)+ , argIgnoreExports :: Any+ -- ^ Ignore export lists in modules?+ , argLinkSource :: Flag (Template, Template, Template)+ -- ^ (Template for modules, template for symbols, template for lines).+ , argLinkedSource :: Flag Bool+ -- ^ Generate hyperlinked sources+ , argQuickJump :: Flag Bool+ -- ^ Generate quickjump index+ , argCssFile :: Flag FilePath+ -- ^ Optional custom CSS file.+ , argContents :: Flag String+ -- ^ Optional URL to contents page.+ , argGenContents :: Flag Bool+ -- ^ Generate contents+ , argIndex :: Flag String+ -- ^ Optional URL to index page.+ , argGenIndex :: Flag Bool+ -- ^ Generate index+ , argBaseUrl :: Flag String+ -- ^ Optional base url from which static files will be loaded.+ , argVerbose :: Any+ , argOutput :: Flag [Output]+ -- ^ HTML or Hoogle doc or both? Required.+ , argInterfaces :: [(FilePath, Maybe String, Maybe String, Visibility)]+ -- ^ [(Interface file, URL to the HTML docs and hyperlinked-source for links)].+ , argOutputDir :: Directory+ -- ^ Where to generate the documentation.+ , argTitle :: Flag String+ -- ^ Page title, required.+ , argPrologue :: Flag String+ -- ^ Prologue text, required for 'haddock', ignored by 'haddocks'.+ , argPrologueFile :: Flag FilePath+ -- ^ Prologue file name, ignored by 'haddock', optional for 'haddocks'.+ , argGhcOptions :: GhcOptions+ -- ^ Additional flags to pass to GHC.+ , argGhcLibDir :: Flag FilePath+ -- ^ To find the correct GHC, required.+ , argReexports :: [OpenModule]+ -- ^ Re-exported modules+ , argTargets :: [FilePath]+ -- ^ Modules to process.+ , argResourcesDir :: Flag String+ -- ^ haddock's static \/ auxiliary files.+ , argUseUnicode :: Flag Bool+ -- ^ haddock's `--use-unicode` flag+ }+ deriving (Generic)++-- | The FilePath of a directory, it's a monoid under '(</>)'.+newtype Directory = Dir {unDir' :: FilePath} deriving (Read, Show, Eq, Ord)++-- NB: only correct at the top-level, after we have combined monoidally+-- the top-level output directory with the component subdir.+unDir :: Directory -> SymbolicPath Pkg (Path.Dir Artifacts)+unDir = makeSymbolicPath . normalise . unDir'++type Template = String++data Output = Html | Hoogle+ deriving (Eq)++-- ------------------------------------------------------------------------------+-- Haddock support++-- | Get Haddock program and check if it matches the request+getHaddockProg+ :: Verbosity+ -> ProgramDb+ -> Compiler+ -> HaddockArgs+ -> Flag Bool+ -- ^ quickjump feature+ -> IO (ConfiguredProgram, Version)+getHaddockProg verbosity programDb comp args quickJumpFlag = do+ let HaddockArgs+ { argQuickJump+ , argOutput+ } = args+ hoogle = Hoogle `elem` fromFlagOrDefault [] argOutput++ (haddockProg, version, _) <-+ requireProgramVersion+ verbosity+ haddockProgram+ (orLaterVersion (mkVersion [2, 0]))+ programDb++ -- various sanity checks+ when (hoogle && version < mkVersion [2, 2]) $+ dieWithException verbosity NoSupportForHoogle++ when (fromFlag argQuickJump && version < mkVersion [2, 19]) $ do+ let msg = "Haddock prior to 2.19 does not support the --quickjump flag."+ alt = "The generated documentation won't have the QuickJump feature."+ if Flag True == quickJumpFlag+ then dieWithException verbosity NoSupportForQuickJumpFlag+ else warn verbosity (msg ++ "\n" ++ alt)++ haddockGhcVersionStr <-+ getProgramOutput+ verbosity+ haddockProg+ ["--ghc-version"]+ case (simpleParsec haddockGhcVersionStr, compilerCompatVersion GHC comp) of+ (Nothing, _) -> dieWithException verbosity NoGHCVersionFromHaddock+ (_, Nothing) -> dieWithException verbosity NoGHCVersionFromCompiler+ (Just haddockGhcVersion, Just ghcVersion)+ | haddockGhcVersion == ghcVersion -> return ()+ | otherwise -> dieWithException verbosity $ HaddockAndGHCVersionDoesntMatch ghcVersion haddockGhcVersion++ return (haddockProg, version)++haddock+ :: PackageDescription+ -> LocalBuildInfo+ -> [PPSuffixHandler]+ -> HaddockFlags+ -> IO ()+haddock = haddock_setupHooks noBuildHooks++haddock_setupHooks+ :: BuildHooks+ -> PackageDescription+ -> LocalBuildInfo+ -> [PPSuffixHandler]+ -> HaddockFlags+ -> IO ()+haddock_setupHooks+ _+ pkg_descr+ _+ _+ haddockFlags+ | not (hasLibs pkg_descr)+ && not (fromFlag $ haddockExecutables haddockFlags)+ && not (fromFlag $ haddockTestSuites haddockFlags)+ && not (fromFlag $ haddockBenchmarks haddockFlags)+ && not (fromFlag $ haddockForeignLibs haddockFlags) =+ warn (fromFlag $ setupVerbosity $ haddockCommonFlags haddockFlags) $+ "No documentation was generated as this package does not contain "+ ++ "a library. Perhaps you want to use the --executables, --tests,"+ ++ " --benchmarks or --foreign-libraries flags."+haddock_setupHooks+ (BuildHooks{preBuildComponentRules = mbPbcRules})+ pkg_descr+ lbi+ suffixes+ flags' = do+ let verbosity = fromFlag $ haddockVerbosity flags+ mbWorkDir = flagToMaybe $ haddockWorkingDir flags+ comp = compiler lbi+ platform = hostPlatform lbi+ config = configFlags lbi++ quickJmpFlag = haddockQuickJump flags'+ flags = case haddockTarget of+ ForDevelopment -> flags'+ ForHackage ->+ flags'+ { haddockHoogle = Flag True+ , haddockHtml = Flag True+ , haddockHtmlLocation = Flag (pkg_url ++ "/docs")+ , haddockContents = Flag (toPathTemplate pkg_url)+ , haddockLinkedSource = Flag True+ , haddockQuickJump = Flag True+ }+ pkg_url = "/package/$pkg-$version"+ flag f = fromFlag $ f flags++ tmpFileOpts =+ commonSetupTempFileOptions $ configCommonFlags config+ htmlTemplate =+ fmap toPathTemplate . flagToMaybe . haddockHtmlLocation $+ flags+ haddockTarget =+ fromFlagOrDefault ForDevelopment (haddockForHackage flags')++ libdirArgs <- getGhcLibDir verbosity lbi+ -- The haddock-output-dir flag overrides any other documentation placement concerns.+ -- The point is to give the user full freedom over the location if they need it.+ let overrideWithOutputDir args = case haddockOutputDir flags of+ NoFlag -> args+ Flag dir -> args{argOutputDir = Dir dir}+ let commonArgs =+ overrideWithOutputDir $+ mconcat+ [ libdirArgs+ , fromFlags (haddockTemplateEnv lbi (packageId pkg_descr)) flags+ , fromPackageDescription haddockTarget pkg_descr+ ]++ (haddockProg, version) <-+ getHaddockProg verbosity (withPrograms lbi) comp commonArgs quickJmpFlag++ -- We fall back to using HsColour only for versions of Haddock which don't+ -- support '--hyperlinked-sources'.+ let using_hscolour = flag haddockLinkedSource && version < mkVersion [2, 17]+ when using_hscolour $+ hscolour'+ noBuildHooks+ -- NB: we are not passing the user BuildHooks here,+ -- because we are already running the pre/post build hooks+ -- for Haddock.+ (warn verbosity)+ haddockTarget+ pkg_descr+ lbi+ suffixes+ (defaultHscolourFlags `mappend` haddockToHscolour flags)++ targets <- readTargetInfos verbosity pkg_descr lbi (haddockTargets flags)++ let+ targets' =+ case targets of+ [] -> allTargetsInBuildOrder' pkg_descr lbi+ _ -> targets++ internalPackageDB <-+ createInternalPackageDB verbosity lbi (flag $ setupDistPref . haddockCommonFlags)++ (\f -> foldM_ f (installedPkgs lbi) targets') $ \index target -> do+ curDir <- absoluteWorkingDirLBI lbi+ let+ component = targetComponent target+ clbi = targetCLBI target+ bi = componentBuildInfo component+ -- Include any build-tool-depends on build tools internal to the current package.+ progs' = addInternalBuildTools curDir pkg_descr lbi bi (withPrograms lbi)+ lbi' =+ lbi+ { withPrograms = progs'+ , withPackageDB = withPackageDB lbi ++ [internalPackageDB]+ , installedPkgs = index+ }++ runPreBuildHooks :: LocalBuildInfo -> TargetInfo -> IO ()+ runPreBuildHooks lbi2 tgt =+ let inputs =+ SetupHooks.PreBuildComponentInputs+ { SetupHooks.buildingWhat = BuildHaddock flags+ , SetupHooks.localBuildInfo = lbi2+ , SetupHooks.targetInfo = tgt+ }+ in for_ mbPbcRules $ \pbcRules -> do+ (ruleFromId, _mons) <- SetupHooks.computeRules verbosity inputs pbcRules+ SetupHooks.executeRules verbosity lbi2 tgt ruleFromId++ -- See Note [Hi Haddock Recompilation Avoidance]+ reusingGHCCompilationArtifacts verbosity tmpFileOpts mbWorkDir lbi bi clbi version $ \haddockArtifactsDirs -> do+ preBuildComponent runPreBuildHooks verbosity lbi' target+ preprocessComponent pkg_descr component lbi' clbi False verbosity suffixes+ let+ doExe com = case (compToExe com) of+ Just exe -> do+ exeArgs <-+ fromExecutable+ verbosity+ haddockArtifactsDirs+ lbi'+ clbi+ htmlTemplate+ haddockTarget+ pkg_descr+ exe+ commonArgs+ runHaddock+ verbosity+ mbWorkDir+ tmpFileOpts+ comp+ platform+ haddockProg+ True+ exeArgs+ Nothing -> do+ warn+ verbosity+ "Unsupported component, skipping..."+ return ()+ -- We define 'smsg' once and then reuse it inside the case, so that+ -- we don't say we are running Haddock when we actually aren't+ -- (e.g., Haddock is not run on non-libraries)+ smsg :: IO ()+ smsg =+ setupMessage'+ verbosity+ "Running Haddock on"+ (packageId pkg_descr)+ (componentLocalName clbi)+ (maybeComponentInstantiatedWith clbi)+ ipi <- case component of+ CLib lib -> do+ smsg+ libArgs <-+ fromLibrary+ verbosity+ haddockArtifactsDirs+ lbi'+ clbi+ htmlTemplate+ haddockTarget+ pkg_descr+ lib+ commonArgs+ runHaddock+ verbosity+ mbWorkDir+ tmpFileOpts+ comp+ platform+ haddockProg+ True+ libArgs+ inplaceDir <- absoluteWorkingDirLBI lbi++ let+ ipi =+ inplaceInstalledPackageInfo+ inplaceDir+ (flag $ setupDistPref . haddockCommonFlags)+ pkg_descr+ (mkAbiHash "inplace")+ lib+ lbi'+ clbi++ debug verbosity $+ "Registering inplace:\n"+ ++ (InstalledPackageInfo.showInstalledPackageInfo ipi)++ registerPackage+ verbosity+ (compiler lbi')+ (withPrograms lbi')+ mbWorkDir+ (withPackageDB lbi')+ ipi+ HcPkg.defaultRegisterOptions+ { HcPkg.registerMultiInstance = True+ }++ return $ PackageIndex.insert ipi index+ CFLib flib ->+ when+ (flag haddockForeignLibs)+ ( do+ smsg+ flibArgs <-+ fromForeignLib+ verbosity+ haddockArtifactsDirs+ lbi'+ clbi+ htmlTemplate+ haddockTarget+ pkg_descr+ flib+ commonArgs+ runHaddock+ verbosity+ mbWorkDir+ tmpFileOpts+ comp+ platform+ haddockProg+ True+ flibArgs+ )+ >> return index+ CExe _ -> when (flag haddockExecutables) (smsg >> doExe component) >> return index+ CTest test -> do+ when (flag haddockTestSuites) $ do+ smsg+ testArgs <-+ fromTest+ verbosity+ haddockArtifactsDirs+ lbi'+ clbi+ htmlTemplate+ haddockTarget+ pkg_descr+ test+ commonArgs+ runHaddock+ verbosity+ mbWorkDir+ tmpFileOpts+ comp+ platform+ haddockProg+ True+ testArgs+ return index+ CBench bench -> do+ when (flag haddockBenchmarks) $ do+ smsg+ benchArgs <-+ fromBenchmark+ verbosity+ haddockArtifactsDirs+ lbi'+ clbi+ htmlTemplate+ haddockTarget+ pkg_descr+ bench+ commonArgs+ runHaddock+ verbosity+ mbWorkDir+ tmpFileOpts+ comp+ platform+ haddockProg+ True+ benchArgs+ return index++ return ipi++ for_ (extraDocFiles pkg_descr) $ \fpath -> do+ files <- matchDirFileGlob verbosity (specVersion pkg_descr) mbWorkDir fpath+ let targetDir = Dir $ unDir' (argOutputDir commonArgs) </> haddockDirName haddockTarget pkg_descr+ for_ files $+ copyFileToCwd verbosity mbWorkDir (unDir targetDir)++-- | Execute 'Haddock' configured with 'HaddocksFlags'. It is used to build+-- index and contents for documentation of multiple packages.+createHaddockIndex+ :: Verbosity+ -> ProgramDb+ -> Compiler+ -> Platform+ -> Maybe (SymbolicPath CWD (Path.Dir Pkg))+ -> HaddockProjectFlags+ -> IO ()+createHaddockIndex verbosity programDb comp platform mbWorkDir flags = do+ let args = fromHaddockProjectFlags flags+ tmpFileOpts =+ commonSetupTempFileOptions $ haddockProjectCommonFlags $ flags+ (haddockProg, _version) <-+ getHaddockProg verbosity programDb comp args (Flag True)+ runHaddock verbosity mbWorkDir tmpFileOpts comp platform haddockProg False args++-- ------------------------------------------------------------------------------+-- Contributions to HaddockArgs (see also Doctest.hs for very similar code).++fromFlags :: PathTemplateEnv -> HaddockFlags -> HaddockArgs+fromFlags env flags =+ mempty+ { argHideModules =+ ( maybe mempty (All . not) $+ flagToMaybe (haddockInternal flags)+ , mempty+ )+ , argLinkSource =+ if fromFlag (haddockLinkedSource flags)+ then+ Flag+ ( "src/%{MODULE/./-}.html"+ , "src/%{MODULE/./-}.html#%{NAME}"+ , "src/%{MODULE/./-}.html#line-%{LINE}"+ )+ else NoFlag+ , argLinkedSource = haddockLinkedSource flags+ , argQuickJump = haddockQuickJump flags+ , argCssFile = haddockCss flags+ , argContents =+ fmap+ (fromPathTemplate . substPathTemplate env)+ (haddockContents flags)+ , argGenContents = Flag False+ , argIndex =+ fmap+ (fromPathTemplate . substPathTemplate env)+ (haddockIndex flags)+ , argGenIndex = Flag False+ , argBaseUrl = haddockBaseUrl flags+ , argResourcesDir = haddockResourcesDir flags+ , argVerbose =+ maybe mempty (Any . (>= deafening))+ . flagToMaybe+ $ setupVerbosity commonFlags+ , argOutput =+ Flag $ case [Html | Flag True <- [haddockHtml flags]]+ ++ [Hoogle | Flag True <- [haddockHoogle flags]] of+ [] -> [Html]+ os -> os+ , argOutputDir = maybe mempty (Dir . getSymbolicPath) . flagToMaybe $ setupDistPref commonFlags+ , argGhcOptions = mempty{ghcOptExtra = ghcArgs}+ , argUseUnicode = haddockUseUnicode flags+ }+ where+ ghcArgs = fromMaybe [] . lookup "ghc" . haddockProgramArgs $ flags+ commonFlags = haddockCommonFlags flags++fromHaddockProjectFlags :: HaddockProjectFlags -> HaddockArgs+fromHaddockProjectFlags flags =+ mempty+ { argOutputDir = Dir (fromFlag $ haddockProjectDir flags)+ , argQuickJump = Flag True+ , argGenContents = Flag True+ , argGenIndex = Flag True+ , argPrologueFile = haddockProjectPrologue flags+ , argInterfaces = fromFlagOrDefault [] (haddockProjectInterfaces flags)+ , argLinkedSource = Flag True+ , argResourcesDir = haddockProjectResourcesDir flags+ , argCssFile = haddockProjectCss flags+ }++fromPackageDescription :: HaddockTarget -> PackageDescription -> HaddockArgs+fromPackageDescription _haddockTarget pkg_descr =+ mempty+ { argInterfaceFile = Flag $ haddockPath pkg_descr+ , argPackageName = Flag $ packageId $ pkg_descr+ , argOutputDir = Dir $ "doc" </> "html"+ , argPrologue =+ Flag $+ ShortText.fromShortText $+ if ShortText.null desc+ then synopsis pkg_descr+ else desc+ , argTitle = Flag $ showPkg ++ subtitle+ }+ where+ desc = description pkg_descr+ showPkg = prettyShow (packageId pkg_descr)+ subtitle+ | ShortText.null (synopsis pkg_descr) = ""+ | otherwise = ": " ++ ShortText.fromShortText (synopsis pkg_descr)++componentGhcOptions+ :: Verbosity+ -> LocalBuildInfo+ -> BuildInfo+ -> ComponentLocalBuildInfo+ -> SymbolicPath Pkg (Path.Dir build)+ -> GhcOptions+componentGhcOptions verbosity lbi bi clbi odir =+ let f = case compilerFlavor (compiler lbi) of+ GHC -> GHC.componentGhcOptions+ GHCJS -> GHCJS.componentGhcOptions+ _ ->+ error $+ "Distribution.Simple.Haddock.componentGhcOptions:"+ ++ "haddock only supports GHC and GHCJS"+ in f verbosity lbi bi clbi odir++{-+Note [Hi Haddock Recompilation Avoidance]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+Starting with Haddock 2.28, we no longer want to run Haddock's GHC session in+an arbitrary temporary directory. Doing so always causes recompilation during+documentation generation, which can now be avoided thanks to Hi Haddock.++Instead, we want to re-use the interface and object files produced by GHC.+We copy these intermediate files produced by GHC to temporary directories and+point haddock to them.++The reason why we can't use the GHC files /inplace/ is that haddock may have to+recompile (e.g. because of `haddock-options`). In that case, we want to be sure+the files produced by GHC do not get overwritten.++See https://github.com/haskell/cabal/pull/9177 for discussion.++(W.1) As it turns out, -stubdir is included in GHC's recompilation fingerprint.+This means that if we use a temporary directory for stubfiles produced by GHC+for the haddock invocation, haddock will trigger full recompilation since the+stubdir would be different.++So we don't use a temporary stubdir, despite the tmp o-dir and hi-dir:++We want to avoid at all costs haddock accidentally overwriting o-files and+hi-files (e.g. if a user specified haddock-option triggers recompilation), and+thus copy them to a temporary directory to pass them on to haddock. However,+stub files are much less problematic since ABI-incompatibility isn't at play+here, that is, there doesn't seem to be a GHC flag that could accidentally make+a stub file incompatible with the one produced by GHC from the same module.+-}++mkHaddockArgs+ :: Verbosity+ -> (SymbolicPath Pkg (Path.Dir Artifacts), SymbolicPath Pkg (Path.Dir Artifacts), SymbolicPath Pkg (Path.Dir Artifacts))+ -- ^ Directories for -hidir, -odir, and -stubdir to GHC through Haddock.+ -- See Note [Hi Haddock Recompilation Avoidance]+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> Maybe PathTemplate+ -- ^ template for HTML location+ -> [SymbolicPath Pkg File]+ -> BuildInfo+ -> IO HaddockArgs+mkHaddockArgs verbosity (tmpObjDir, tmpHiDir, tmpStubDir) lbi clbi htmlTemplate inFiles bi = do+ let+ vanillaOpts' =+ componentGhcOptions normal lbi bi clbi (buildDir lbi)+ vanillaOpts =+ vanillaOpts'+ { -- See Note [Hi Haddock Recompilation Avoidance]+ ghcOptObjDir = toFlag tmpObjDir+ , ghcOptHiDir = toFlag tmpHiDir+ , ghcOptStubDir = toFlag tmpStubDir+ }+ sharedOpts =+ vanillaOpts+ { ghcOptDynLinkMode = toFlag GhcDynamicOnly+ , ghcOptFPic = toFlag True+ , ghcOptHiSuffix = toFlag "dyn_hi"+ , ghcOptObjSuffix = toFlag "dyn_o"+ , ghcOptExtra = hcSharedOptions GHC bi+ }+ ifaceArgs <- getInterfaces verbosity lbi clbi htmlTemplate+ opts <-+ if withVanillaLib lbi+ then return vanillaOpts+ else+ if withSharedLib lbi+ then return sharedOpts+ else dieWithException verbosity MustHaveSharedLibraries++ return+ ifaceArgs+ { argGhcOptions = opts+ , argTargets = map getSymbolicPath inFiles+ , argReexports = getReexports clbi+ }++fromLibrary+ :: Verbosity+ -> (SymbolicPath Pkg (Path.Dir Artifacts), SymbolicPath Pkg (Path.Dir Artifacts), SymbolicPath Pkg (Path.Dir Artifacts))+ -- ^ Directories for -hidir, -odir, and -stubdir to GHC through Haddock.+ -- See Note [Hi Haddock Recompilation Avoidance]+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> Maybe PathTemplate+ -- ^ template for HTML location+ -> HaddockTarget+ -> PackageDescription+ -> Library+ -> HaddockArgs+ -- ^ common args+ -> IO HaddockArgs+fromLibrary verbosity haddockArtifactsDirs lbi clbi htmlTemplate haddockTarget pkg_descr lib commonArgs = do+ inFiles <- map snd `fmap` getLibSourceFiles verbosity lbi lib clbi+ args <-+ mkHaddockArgs+ verbosity+ haddockArtifactsDirs+ lbi+ clbi+ htmlTemplate+ inFiles+ (libBuildInfo lib)+ let args' =+ commonArgs+ <> args+ { argOutputDir =+ Dir $ haddockLibraryDirPath haddockTarget pkg_descr lib+ , argInterfaceFile = Flag $ haddockLibraryPath pkg_descr lib+ }+ args'' =+ args'+ { argHideModules = (mempty, otherModules (libBuildInfo lib))+ , argTitle = Flag $ haddockPackageLibraryName pkg_descr lib+ , argComponentName = toFlag (haddockPackageLibraryName' (pkgName (package pkg_descr)) (libName lib))+ , -- we need to accommodate for `argOutputDir`, see `haddockLibraryPath`+ argBaseUrl = case (libName lib, argBaseUrl args') of+ (LSubLibName _, Flag url) -> Flag $ ".." </> url+ (_, a) -> a+ , argContents = case (libName lib, argContents args') of+ (LSubLibName _, Flag url) -> Flag $ ".." </> url+ (_, a) -> a+ , argIndex = case (libName lib, argIndex args') of+ (LSubLibName _, Flag url) -> Flag $ ".." </> url+ (_, a) -> a+ }+ return args''++fromExecutable+ :: Verbosity+ -> (SymbolicPath Pkg (Path.Dir Artifacts), SymbolicPath Pkg (Path.Dir Artifacts), SymbolicPath Pkg (Path.Dir Artifacts))+ -- ^ Directories for -hidir, -odir, and -stubdir to GHC through Haddock.+ -- See Note [Hi Haddock Recompilation Avoidance]+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> Maybe PathTemplate+ -- ^ template for HTML location+ -> HaddockTarget+ -> PackageDescription+ -> Executable+ -> HaddockArgs+ -- ^ common args+ -> IO HaddockArgs+fromExecutable verbosity haddockArtifactsDirs lbi clbi htmlTemplate haddockTarget pkg_descr exe commonArgs = do+ inFiles <- map snd `fmap` getExeSourceFiles verbosity lbi exe clbi+ args <-+ mkHaddockArgs+ verbosity+ haddockArtifactsDirs+ lbi+ clbi+ htmlTemplate+ inFiles+ (buildInfo exe)+ let args' =+ commonArgs+ <> args+ { argOutputDir =+ Dir $+ haddockDirName haddockTarget pkg_descr+ </> unUnqualComponentName (exeName exe)+ }+ return+ args'+ { argTitle = Flag $ unUnqualComponentName $ exeName exe+ , -- we need to accommodate `argOutputDir`+ argBaseUrl = case argBaseUrl args' of+ Flag url -> Flag $ ".." </> url+ NoFlag -> NoFlag+ , argContents = case argContents args' of+ Flag url -> Flag $ ".." </> url+ NoFlag -> NoFlag+ , argIndex = case argIndex args' of+ Flag url -> Flag $ ".." </> url+ NoFlag -> NoFlag+ }++fromTest+ :: Verbosity+ -> (SymbolicPath Pkg (Path.Dir Artifacts), SymbolicPath Pkg (Path.Dir Artifacts), SymbolicPath Pkg (Path.Dir Artifacts))+ -- ^ Directories for -hidir, -odir, and -stubdir to GHC through Haddock.+ -- See Note [Hi Haddock Recompilation Avoidance]+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> Maybe PathTemplate+ -- ^ template for HTML location+ -> HaddockTarget+ -> PackageDescription+ -> TestSuite+ -> HaddockArgs+ -- ^ common args+ -> IO HaddockArgs+fromTest verbosity haddockArtifactsDirs lbi clbi htmlTemplate haddockTarget pkg_descr test commonArgs = do+ inFiles <- map snd `fmap` getTestSourceFiles verbosity lbi test clbi+ args <-+ mkHaddockArgs+ verbosity+ haddockArtifactsDirs+ lbi+ clbi+ htmlTemplate+ inFiles+ (testBuildInfo test)+ let args' =+ commonArgs+ <> args+ { argOutputDir =+ Dir $+ haddockDirName haddockTarget pkg_descr+ </> unUnqualComponentName (testName test)+ }+ return+ args'+ { argTitle = Flag $ prettyShow (packageName pkg_descr)+ , argComponentName = Flag $ prettyShow (packageName pkg_descr) ++ ":" ++ unUnqualComponentName (testName test)+ , -- we need to accommodate `argOutputDir`+ argBaseUrl = case argBaseUrl args' of+ Flag url -> Flag $ ".." </> url+ NoFlag -> NoFlag+ , argContents = case argContents args' of+ Flag url -> Flag $ ".." </> url+ NoFlag -> NoFlag+ , argIndex = case argIndex args' of+ Flag url -> Flag $ ".." </> url+ NoFlag -> NoFlag+ }++fromBenchmark+ :: Verbosity+ -> (SymbolicPath Pkg (Path.Dir Artifacts), SymbolicPath Pkg (Path.Dir Artifacts), SymbolicPath Pkg (Path.Dir Artifacts))+ -- ^ Directories for -hidir, -odir, and -stubdir to GHC through Haddock.+ -- See Note [Hi Haddock Recompilation Avoidance]+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> Maybe PathTemplate+ -- ^ template for HTML location+ -> HaddockTarget+ -> PackageDescription+ -> Benchmark+ -> HaddockArgs+ -- ^ common args+ -> IO HaddockArgs+fromBenchmark verbosity haddockArtifactsDirs lbi clbi htmlTemplate haddockTarget pkg_descr bench commonArgs = do+ inFiles <- map snd `fmap` getBenchmarkSourceFiles verbosity lbi bench clbi+ args <-+ mkHaddockArgs+ verbosity+ haddockArtifactsDirs+ lbi+ clbi+ htmlTemplate+ inFiles+ (benchmarkBuildInfo bench)+ let args' =+ commonArgs+ <> args+ { argOutputDir =+ Dir $+ haddockDirName haddockTarget pkg_descr+ </> unUnqualComponentName (benchmarkName bench)+ }+ return+ args'+ { argTitle = Flag $ prettyShow (packageName pkg_descr)+ , argComponentName = Flag $ prettyShow (packageName pkg_descr) ++ ":" ++ unUnqualComponentName (benchmarkName bench)+ , -- we need to accommodate `argOutputDir`+ argBaseUrl = case argBaseUrl args' of+ Flag url -> Flag $ ".." </> url+ NoFlag -> NoFlag+ , argContents = case argContents args' of+ Flag url -> Flag $ ".." </> url+ NoFlag -> NoFlag+ , argIndex = case argIndex args' of+ Flag url -> Flag $ ".." </> url+ NoFlag -> NoFlag+ }++fromForeignLib+ :: Verbosity+ -> (SymbolicPath Pkg (Path.Dir Artifacts), SymbolicPath Pkg (Path.Dir Artifacts), SymbolicPath Pkg (Path.Dir Artifacts))+ -- ^ Directories for -hidir, -odir, and -stubdir to GHC through Haddock.+ -- See Note [Hi Haddock Recompilation Avoidance]+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> Maybe PathTemplate+ -- ^ template for HTML location+ -> HaddockTarget+ -> PackageDescription+ -> ForeignLib+ -> HaddockArgs+ -- ^ common args+ -> IO HaddockArgs+fromForeignLib verbosity haddockArtifactsDirs lbi clbi htmlTemplate haddockTarget pkg_descr flib commonArgs = do+ inFiles <- map snd `fmap` getFLibSourceFiles verbosity lbi flib clbi+ args <-+ mkHaddockArgs+ verbosity+ haddockArtifactsDirs+ lbi+ clbi+ htmlTemplate+ inFiles+ (foreignLibBuildInfo flib)+ let args' =+ commonArgs+ <> args+ { argOutputDir =+ Dir $+ haddockDirName haddockTarget pkg_descr+ </> unUnqualComponentName (foreignLibName flib)+ }+ return+ args'+ { argTitle = Flag $ unUnqualComponentName $ foreignLibName flib+ , -- we need to accommodate `argOutputDir`+ argBaseUrl = case argBaseUrl args' of+ Flag url -> Flag $ ".." </> url+ NoFlag -> NoFlag+ , argContents = case argContents args' of+ Flag url -> Flag $ ".." </> url+ NoFlag -> NoFlag+ , argIndex = case argIndex args' of+ Flag url -> Flag $ ".." </> url+ NoFlag -> NoFlag+ }++compToExe :: Component -> Maybe Executable+compToExe comp =+ case comp of+ CTest test@TestSuite{testInterface = TestSuiteExeV10 _ f} ->+ Just+ Executable+ { exeName = testName test+ , modulePath = f+ , exeScope = ExecutablePublic+ , buildInfo = testBuildInfo test+ }+ CBench bench@Benchmark{benchmarkInterface = BenchmarkExeV10 _ f} ->+ Just+ Executable+ { exeName = benchmarkName bench+ , modulePath = f+ , exeScope = ExecutablePublic+ , buildInfo = benchmarkBuildInfo bench+ }+ CExe exe -> Just exe+ _ -> Nothing++getInterfaces+ :: Verbosity+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> Maybe PathTemplate+ -- ^ template for HTML location+ -> IO HaddockArgs+getInterfaces verbosity lbi clbi htmlTemplate = do+ (packageFlags, warnings) <- haddockPackageFlags verbosity lbi clbi htmlTemplate+ traverse_ (warn (verboseUnmarkOutput verbosity)) warnings+ return $+ mempty+ { argInterfaces = packageFlags+ }++getReexports :: ComponentLocalBuildInfo -> [OpenModule]+getReexports LibComponentLocalBuildInfo{componentExposedModules = mods} =+ mapMaybe exposedReexport mods+getReexports _ = []++getGhcLibDir+ :: Verbosity+ -> LocalBuildInfo+ -> IO HaddockArgs+getGhcLibDir verbosity lbi = do+ l <- case compilerFlavor (compiler lbi) of+ GHC -> GHC.getLibDir verbosity lbi+ GHCJS -> GHCJS.getLibDir verbosity lbi+ _ -> error "haddock only supports GHC and GHCJS"+ return $ mempty{argGhcLibDir = Flag l}++-- | If Hi Haddock is supported, this function creates temporary directories+-- and copies existing interface and object files produced by GHC into them,+-- then passes them off to the given continuation.+--+-- If Hi Haddock is _not_ supported, we can't re-use GHC's compilation files.+-- Instead, we use a clean temporary directory to the continuation,+-- with no hope for recompilation avoidance.+--+-- See Note [Hi Haddock Recompilation Avoidance]+reusingGHCCompilationArtifacts+ :: Verbosity+ -> TempFileOptions+ -> Maybe (SymbolicPath CWD (Path.Dir Pkg))+ -- ^ Working directory+ -> LocalBuildInfo+ -> BuildInfo+ -> ComponentLocalBuildInfo+ -> Version+ -- ^ Haddock's version+ -> ((SymbolicPath Pkg (Path.Dir Artifacts), SymbolicPath Pkg (Path.Dir Artifacts), SymbolicPath Pkg (Path.Dir Artifacts)) -> IO r)+ -- ^ Continuation+ -> IO r+reusingGHCCompilationArtifacts verbosity tmpFileOpts mbWorkDir lbi bi clbi version act+ | version >= mkVersion [2, 28, 0] = do+ withTempDirectoryCwdEx verbosity tmpFileOpts mbWorkDir (distPrefLBI lbi) "haddock-objs" $ \tmpObjDir ->+ withTempDirectoryCwdEx verbosity tmpFileOpts mbWorkDir (distPrefLBI lbi) "haddock-his" $ \tmpHiDir -> do+ -- Re-use ghc's interface and obj files, but first copy them to+ -- somewhere where it is safe if haddock overwrites them+ let+ vanillaOpts = componentGhcOptions normal lbi bi clbi (buildDir lbi)+ i = interpretSymbolicPath mbWorkDir+ copyDir getGhcDir tmpDir = do+ let ghcDir = i $ fromFlag $ getGhcDir vanillaOpts+ ghcDirExists <- doesDirectoryExist ghcDir+ -- Don't try to copy artifacts if they don't exist, e.g. if+ -- we have not yet run the 'build' command.+ when ghcDirExists $+ copyDirectoryRecursive verbosity ghcDir (i tmpDir)+ copyDir ghcOptObjDir tmpObjDir+ copyDir ghcOptHiDir tmpHiDir+ -- copyDir ghcOptStubDir tmpStubDir -- (see W.1 in Note [Hi Haddock Recompilation Avoidance])++ act (tmpObjDir, tmpHiDir, fromFlag $ ghcOptHiDir vanillaOpts)+ | otherwise = do+ withTempDirectoryCwdEx verbosity tmpFileOpts mbWorkDir (distPrefLBI lbi) "tmp" $+ \tmpFallback -> act (tmpFallback, tmpFallback, tmpFallback)++-- ------------------------------------------------------------------------------++-- | Call haddock with the specified arguments.+runHaddock+ :: Verbosity+ -> Maybe (SymbolicPath CWD (Path.Dir Pkg))+ -> TempFileOptions+ -> Compiler+ -> Platform+ -> ConfiguredProgram+ -> Bool+ -- ^ require targets+ -> HaddockArgs+ -> IO ()+runHaddock verbosity mbWorkDir tmpFileOpts comp platform haddockProg requireTargets args+ | requireTargets && null (argTargets args) =+ warn verbosity $+ "Haddocks are being requested, but there aren't any modules given "+ ++ "to create documentation for."+ | otherwise = do+ let haddockVersion =+ fromMaybe+ (error "unable to determine haddock version")+ (programVersion haddockProg)+ renderArgs verbosity mbWorkDir tmpFileOpts haddockVersion comp platform args $+ \flags result -> do+ runProgramCwd verbosity mbWorkDir haddockProg flags+ notice verbosity $ "Documentation created: " ++ result++renderArgs+ :: forall a+ . Verbosity+ -> Maybe (SymbolicPath CWD (Path.Dir Pkg))+ -> TempFileOptions+ -> Version+ -> Compiler+ -> Platform+ -> HaddockArgs+ -> ([String] -> FilePath -> IO a)+ -> IO a+renderArgs verbosity mbWorkDir tmpFileOpts version comp platform args k = do+ let haddockSupportsUTF8 = version >= mkVersion [2, 14, 4]+ haddockSupportsResponseFiles = version > mkVersion [2, 16, 2]+ createDirectoryIfMissingVerbose verbosity True (i outputDir)+ let withPrologueArgs prologueArgs =+ let renderedArgs = prologueArgs <> renderPureArgs version comp platform args+ in if haddockSupportsResponseFiles+ then+ withResponseFile+ verbosity+ tmpFileOpts+ "haddock-response.txt"+ (if haddockSupportsUTF8 then Just utf8 else Nothing)+ renderedArgs+ (\responseFileName -> k ["@" ++ responseFileName] result)+ else k renderedArgs result+ case (argPrologueFile args, argPrologue args) of+ (Flag pfile, _) ->+ withPrologueArgs ["--prologue=" ++ pfile]+ (_, Flag prologueText) ->+ withTempFileEx tmpFileOpts "haddock-prologue.txt" $+ \prologueFileName h -> do+ when haddockSupportsUTF8 (hSetEncoding h utf8)+ hPutStrLn h prologueText+ hClose h+ withPrologueArgs ["--prologue=" ++ u prologueFileName]+ (NoFlag, NoFlag) ->+ withPrologueArgs []+ where+ -- See Note [Symbolic paths] in Distribution.Utils.Path+ i = interpretSymbolicPath mbWorkDir+ u :: SymbolicPath Pkg to -> FilePath+ u = interpretSymbolicPathCWD++ outputDir = coerceSymbolicPath $ unDir $ argOutputDir args+ isNotArgContents = isNothing (flagToMaybe $ argContents args)+ isNotArgIndex = isNothing (flagToMaybe $ argIndex args)+ isArgGenIndex = fromFlagOrDefault False (argGenIndex args)+ -- Haddock, when generating HTML, does not generate an index if the options+ -- --use-contents or --use-index are passed to it. See+ -- https://haskell-haddock.readthedocs.io/en/latest/invoking.html#cmdoption-use-contents+ isIndexGenerated = isArgGenIndex && isNotArgContents && isNotArgIndex+ result =+ intercalate ", "+ . map+ ( \o ->+ i outputDir+ </> case o of+ Html+ | isIndexGenerated ->+ "index.html"+ Html+ | otherwise ->+ mempty+ Hoogle -> pkgstr <.> "txt"+ )+ . fromFlagOrDefault [Html]+ . argOutput+ $ args+ where+ pkgstr = prettyShow $ packageName pkgid+ pkgid = arg argPackageName+ arg f = fromFlag $ f args++renderPureArgs :: Version -> Compiler -> Platform -> HaddockArgs -> [String]+renderPureArgs version comp platform args =+ concat+ [ map (\f -> "--dump-interface=" ++ u (unDir (argOutputDir args)) </> f)+ . flagToList+ . argInterfaceFile+ $ args+ , if haddockSupportsPackageName+ then+ maybe+ []+ ( \pkg ->+ [ "--package-name="+ ++ case argComponentName args of+ Flag name -> name+ _ -> prettyShow (pkgName pkg)+ , "--package-version=" ++ prettyShow (pkgVersion pkg)+ ]+ )+ . flagToMaybe+ . argPackageName+ $ args+ else []+ , ["--since-qual=external" | isVersion 2 20]+ , [ "--quickjump" | isVersion 2 19, True <- flagToList . argQuickJump $ args+ ]+ , ["--hyperlinked-source" | isHyperlinkedSource]+ , (\(All b, xs) -> bool [] (map (("--hide=" ++) . prettyShow) xs) b)+ . argHideModules+ $ args+ , bool [] ["--ignore-all-exports"] . getAny . argIgnoreExports $ args+ , -- Haddock's --source-* options are ignored once --hyperlinked-source is+ -- set.+ -- See https://haskell-haddock.readthedocs.io/en/latest/invoking.html#cmdoption-hyperlinked-source+ -- To avoid Haddock's warning, we only set --source-* options if+ -- --hyperlinked-source is not set.+ if isHyperlinkedSource+ then []+ else+ maybe+ []+ ( \(m, e, l) ->+ [ "--source-module=" ++ m+ , "--source-entity=" ++ e+ ]+ ++ if isVersion 2 14+ then ["--source-entity-line=" ++ l]+ else []+ )+ . flagToMaybe+ . argLinkSource+ $ args+ , maybe [] ((: []) . ("--css=" ++)) . flagToMaybe . argCssFile $ args+ , maybe [] ((: []) . ("--use-contents=" ++)) . flagToMaybe . argContents $ args+ , bool [] ["--gen-contents"] . fromFlagOrDefault False . argGenContents $ args+ , maybe [] ((: []) . ("--use-index=" ++)) . flagToMaybe . argIndex $ args+ , bool [] ["--gen-index"] . fromFlagOrDefault False . argGenIndex $ args+ , maybe [] ((: []) . ("--base-url=" ++)) . flagToMaybe . argBaseUrl $ args+ , bool [verbosityFlag] [] . getAny . argVerbose $ args+ , map (\o -> case o of Hoogle -> "--hoogle"; Html -> "--html")+ . fromFlagOrDefault []+ . argOutput+ $ args+ , renderInterfaces . argInterfaces $ args+ , (: []) . ("--odir=" ++) . u . unDir . argOutputDir $ args+ , maybe+ []+ ( (: [])+ . ("--title=" ++)+ . ( bool+ id+ (++ " (internal documentation)")+ (getAny $ argIgnoreExports args)+ )+ )+ . flagToMaybe+ . argTitle+ $ args+ , [ "--optghc=" ++ opt | let opts = argGhcOptions args, opt <- renderGhcOptions comp platform opts+ ]+ , maybe [] (\l -> ["-B" ++ l]) $+ flagToMaybe (argGhcLibDir args) -- error if Nothing?+ , -- https://github.com/haskell/haddock/pull/547+ [ "--reexport=" ++ prettyShow r+ | r <- argReexports args+ , isVersion 2 19+ ]+ , argTargets $ args+ , maybe [] ((: []) . (resourcesDirFlag ++)) . flagToMaybe . argResourcesDir $ args+ , -- Do not re-direct compilation output to a temporary directory (--no-tmp-comp-dir)+ -- We pass this option by default to haddock to avoid recompilation+ -- See Note [Hi Haddock Recompilation Avoidance]+ ["--no-tmp-comp-dir" | version >= mkVersion [2, 28, 0]]+ , bool [] ["--use-unicode"] . fromFlagOrDefault False . argUseUnicode $ args+ ]+ where+ -- See Note [Symbolic paths] in Distribution.Utils.Path+ u = interpretSymbolicPathCWD+ renderInterfaces = map renderInterface++ renderInterface :: (FilePath, Maybe FilePath, Maybe FilePath, Visibility) -> String+ renderInterface (i, html, hypsrc, visibility) =+ "--read-interface="+ ++ intercalate+ ","+ ( concat+ [ [fromMaybe "" html]+ , -- only render hypsrc path if html path+ -- is given and hyperlinked-source is+ -- enabled++ [ case (html, hypsrc) of+ (Nothing, _) -> ""+ (_, Nothing) -> ""+ (_, Just x)+ | isVersion 2 17+ , fromFlagOrDefault False . argLinkedSource $ args ->+ x+ | otherwise ->+ ""+ ]+ , if haddockSupportsVisibility+ then+ [ case visibility of+ Visible -> "visible"+ Hidden -> "hidden"+ ]+ else []+ , [i]+ ]+ )++ isVersion major minor = version >= mkVersion [major, minor]+ verbosityFlag+ | isVersion 2 5 = "--verbosity=1"+ | otherwise = "--verbose"+ resourcesDirFlag+ | isVersion 2 29 = "--resources-dir="+ | otherwise = "--lib="+ haddockSupportsVisibility = version >= mkVersion [2, 26, 1]+ haddockSupportsPackageName = version > mkVersion [2, 16]+ haddockSupportsHyperlinkedSource = isVersion 2 17+ isHyperlinkedSource =+ haddockSupportsHyperlinkedSource+ && fromFlagOrDefault False (argLinkedSource args)++---------------------------------------------------------------------------------++-- | Given a list of 'InstalledPackageInfo's, return a list of interfaces and+-- HTML paths, and an optional warning for packages with missing documentation.+haddockPackagePaths+ :: [InstalledPackageInfo]+ -> Maybe (InstalledPackageInfo -> FilePath)+ -> IO+ ( [ ( FilePath -- path to interface+ -- file+ , Maybe FilePath -- url to html+ -- documentation+ , Maybe FilePath -- url to hyperlinked+ -- source+ , Visibility+ )+ ]+ , Maybe String -- warning about+ -- missing documentation+ )+haddockPackagePaths ipkgs mkHtmlPath = do+ interfaces <-+ sequenceA+ [ case interfaceAndHtmlPath ipkg of+ Nothing -> do+ return (Left (packageId ipkg))+ Just (interface, html) -> do+ (html', hypsrc') <-+ case html of+ Just htmlPath -> do+ let hypSrcPath = htmlPath </> defaultHyperlinkedSourceDirectory+ hypSrcExists <- doesDirectoryExist hypSrcPath+ return $+ ( Just (fixFileUrl htmlPath)+ , if hypSrcExists+ then Just (fixFileUrl hypSrcPath)+ else Nothing+ )+ Nothing -> return (Nothing, Nothing)++ exists <- doesFileExist interface+ if exists+ then return (Right (interface, html', hypsrc', Visible))+ else return (Left pkgid)+ | ipkg <- ipkgs+ , let pkgid = packageId ipkg+ , pkgName pkgid `notElem` noHaddockWhitelist+ ]++ let missing = [pkgid | Left pkgid <- interfaces]+ warning =+ "The following packages have no Haddock documentation "+ ++ "installed. No links will be generated to these packages: "+ ++ intercalate ", " (map prettyShow missing)+ flags = rights interfaces++ return (flags, if null missing then Nothing else Just warning)+ where+ -- Don't warn about missing documentation for these packages. See #1231.+ noHaddockWhitelist = map mkPackageName ["rts"]++ -- Actually extract interface and HTML paths from an 'InstalledPackageInfo'.+ interfaceAndHtmlPath+ :: InstalledPackageInfo+ -> Maybe (FilePath, Maybe FilePath)+ interfaceAndHtmlPath pkg = do+ interface <- listToMaybe (InstalledPackageInfo.haddockInterfaces pkg)+ html <- case mkHtmlPath of+ Nothing -> listToMaybe (InstalledPackageInfo.haddockHTMLs pkg)+ Just mkPath -> Just (mkPath pkg)+ return (interface, if null html then Nothing else Just html)++ -- The 'haddock-html' field in the hc-pkg output is often set as a+ -- native path, but we need it as a URL. See #1064. Also don't "fix"+ -- the path if it is an interpolated one.+ fixFileUrl f+ | Nothing <- mkHtmlPath+ , isAbsolute f =+ "file://" ++ f+ | otherwise = f++ -- 'src' is the default hyperlinked source directory ever since. It is+ -- not possible to configure that directory in any way in haddock.+ defaultHyperlinkedSourceDirectory = "src"++haddockPackageFlags+ :: Verbosity+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> Maybe PathTemplate+ -> IO+ ( [ ( FilePath -- path to interface+ -- file+ , Maybe FilePath -- url to html+ -- documentation+ , Maybe FilePath -- url to hyperlinked+ -- source+ , Visibility+ )+ ]+ , Maybe String -- warning about+ -- missing documentation+ )+haddockPackageFlags verbosity lbi clbi htmlTemplate = do+ let allPkgs = installedPkgs lbi+ directDeps = map fst (componentPackageDeps clbi)+ transitiveDeps <- case PackageIndex.dependencyClosure allPkgs directDeps of+ Left x -> return x+ Right inf ->+ dieWithException verbosity $ HaddockPackageFlags inf++ haddockPackagePaths (PackageIndex.allPackages transitiveDeps) mkHtmlPath+ where+ mkHtmlPath = fmap expandTemplateVars htmlTemplate+ expandTemplateVars tmpl pkg =+ fromPathTemplate . substPathTemplate (env pkg) $ tmpl+ env pkg = haddockTemplateEnv lbi (packageId pkg)++haddockTemplateEnv :: LocalBuildInfo -> PackageIdentifier -> PathTemplateEnv+haddockTemplateEnv lbi pkg_id =+ (PrefixVar, prefix (installDirTemplates lbi))+ -- We want the legacy unit ID here, because it gives us nice paths+ -- (Haddock people don't care about the dependencies)+ : initialPathTemplateEnv+ pkg_id+ (mkLegacyUnitId pkg_id)+ (compilerInfo (compiler lbi))+ (hostPlatform lbi)++-- ------------------------------------------------------------------------------+-- hscolour support.++hscolour+ :: PackageDescription+ -> LocalBuildInfo+ -> [PPSuffixHandler]+ -> HscolourFlags+ -> IO ()+hscolour = hscolour_setupHooks noBuildHooks++hscolour_setupHooks+ :: BuildHooks+ -> PackageDescription+ -> LocalBuildInfo+ -> [PPSuffixHandler]+ -> HscolourFlags+ -> IO ()+hscolour_setupHooks setupHooks =+ hscolour' setupHooks dieNoVerbosity ForDevelopment++hscolour'+ :: BuildHooks+ -> (String -> IO ())+ -- ^ Called when the 'hscolour' exe is not found.+ -> HaddockTarget+ -> PackageDescription+ -> LocalBuildInfo+ -> [PPSuffixHandler]+ -> HscolourFlags+ -> IO ()+hscolour'+ (BuildHooks{preBuildComponentRules = mbPbcRules})+ onNoHsColour+ haddockTarget+ pkg_descr+ lbi+ suffixes+ flags =+ either (\excep -> onNoHsColour $ exceptionMessage excep) (\(hscolourProg, _, _) -> go hscolourProg)+ =<< lookupProgramVersion+ verbosity+ hscolourProgram+ (orLaterVersion (mkVersion [1, 8]))+ (withPrograms lbi)+ where+ common = hscolourCommonFlags flags+ verbosity = fromFlag $ setupVerbosity common+ distPref = fromFlag $ setupDistPref common+ mbWorkDir = mbWorkDirLBI lbi+ i = interpretSymbolicPathLBI lbi -- See Note [Symbolic paths] in Distribution.Utils.Path+ u :: SymbolicPath Pkg to -> FilePath+ u = interpretSymbolicPathCWD++ go :: ConfiguredProgram -> IO ()+ go hscolourProg = do+ warn verbosity $+ "the 'cabal hscolour' command is deprecated in favour of 'cabal "+ ++ "haddock --hyperlink-source' and will be removed in the next major "+ ++ "release."++ setupMessage verbosity "Running hscolour for" (packageId pkg_descr)+ createDirectoryIfMissingVerbose verbosity True $+ i $+ hscolourPref haddockTarget distPref pkg_descr++ withAllComponentsInBuildOrder pkg_descr lbi $ \comp clbi -> do+ let tgt = TargetInfo clbi comp+ runPreBuildHooks :: LocalBuildInfo -> TargetInfo -> IO ()+ runPreBuildHooks lbi2 target =+ let inputs =+ SetupHooks.PreBuildComponentInputs+ { SetupHooks.buildingWhat = BuildHscolour flags+ , SetupHooks.localBuildInfo = lbi2+ , SetupHooks.targetInfo = target+ }+ in for_ mbPbcRules $ \pbcRules -> do+ (ruleFromId, _mons) <- SetupHooks.computeRules verbosity inputs pbcRules+ SetupHooks.executeRules verbosity lbi2 tgt ruleFromId+ preBuildComponent runPreBuildHooks verbosity lbi tgt+ preprocessComponent pkg_descr comp lbi clbi False verbosity suffixes+ let+ doExe com = case (compToExe com) of+ Just exe -> do+ let outputDir =+ hscolourPref haddockTarget distPref pkg_descr+ </> makeRelativePathEx (unUnqualComponentName (exeName exe) </> "src")+ runHsColour hscolourProg outputDir =<< getExeSourceFiles verbosity lbi exe clbi+ Nothing -> do+ warn verbosity "Unsupported component, skipping..."+ return ()+ case comp of+ CLib lib -> do+ let outputDir = hscolourPref haddockTarget distPref pkg_descr </> makeRelativePathEx "src"+ runHsColour hscolourProg outputDir =<< getLibSourceFiles verbosity lbi lib clbi+ CFLib flib -> do+ let outputDir =+ hscolourPref haddockTarget distPref pkg_descr+ </> makeRelativePathEx+ ( unUnqualComponentName (foreignLibName flib)+ </> "src"+ )+ runHsColour hscolourProg outputDir =<< getFLibSourceFiles verbosity lbi flib clbi+ CExe _ -> when (fromFlag (hscolourExecutables flags)) $ doExe comp+ CTest _ -> when (fromFlag (hscolourTestSuites flags)) $ doExe comp+ CBench _ -> when (fromFlag (hscolourBenchmarks flags)) $ doExe comp++ stylesheet = flagToMaybe (hscolourCSS flags)++ runHsColour+ :: ConfiguredProgram+ -> SymbolicPath Pkg to+ -> [(ModuleName.ModuleName, SymbolicPath Pkg to1)]+ -> IO ()+ runHsColour prog outputDir moduleFiles = do+ createDirectoryIfMissingVerbose verbosity True (i outputDir)++ case stylesheet of -- copy the CSS file+ Nothing+ | programVersion prog >= Just (mkVersion [1, 9]) ->+ runProgramCwd+ verbosity+ mbWorkDir+ prog+ ["-print-css", "-o" ++ u outputDir </> "hscolour.css"]+ | otherwise -> return ()+ Just s -> copyFileVerbose verbosity s (i outputDir </> "hscolour.css")++ for_ moduleFiles $ \(m, inFile) ->+ runProgramCwd+ verbosity+ mbWorkDir+ prog+ ["-css", "-anchor", "-o" ++ outFile m, u inFile]+ where+ outFile m =+ i outputDir+ </> intercalate "-" (ModuleName.components m)+ <.> "html"++haddockToHscolour :: HaddockFlags -> HscolourFlags+haddockToHscolour flags =+ HscolourFlags+ { hscolourCommonFlags = haddockCommonFlags flags+ , hscolourCSS = haddockHscolourCss flags+ , hscolourExecutables = haddockExecutables flags+ , hscolourTestSuites = haddockTestSuites flags+ , hscolourBenchmarks = haddockBenchmarks flags+ , hscolourForeignLibs = haddockForeignLibs flags+ }++-- ------------------------------------------------------------------------------+-- Boilerplate Monoid instance.+instance Monoid HaddockArgs where+ mempty = gmempty+ mappend = (<>)++instance Semigroup HaddockArgs where+ (<>) = gmappend++instance Monoid Directory where+ mempty = Dir "."+ mappend = (<>)++instance Semigroup Directory where+ Dir m <> Dir n = Dir $ m </> n
− src/Distribution/Simple/HaskellSuite.hs
@@ -1,228 +0,0 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}--module Distribution.Simple.HaskellSuite where--import Prelude ()-import Distribution.Compat.Prelude--import qualified Data.List.NonEmpty as NE--import Distribution.Simple.Program-import Distribution.Simple.Compiler-import Distribution.Simple.Utils-import Distribution.Simple.BuildPaths-import Distribution.Verbosity-import Distribution.Version-import Distribution.Pretty-import Distribution.Parsec (simpleParsec)-import Distribution.Package-import Distribution.InstalledPackageInfo hiding (includeDirs)-import Distribution.Simple.PackageIndex as PackageIndex-import Distribution.PackageDescription-import Distribution.Simple.LocalBuildInfo-import Distribution.System (Platform)-import Distribution.Utils.Path-import Language.Haskell.Extension-import Distribution.Simple.Program.Builtin--configure- :: Verbosity -> Maybe FilePath -> Maybe FilePath- -> ProgramDb -> IO (Compiler, Maybe Platform, ProgramDb)-configure verbosity mbHcPath hcPkgPath progdb0 = do-- -- We have no idea how a haskell-suite tool is named, so we require at- -- least some information from the user.- hcPath <-- let msg = "You have to provide name or path of a haskell-suite tool (-w PATH)"- in maybe (die' verbosity msg) return mbHcPath-- when (isJust hcPkgPath) $- warn verbosity "--with-hc-pkg option is ignored for haskell-suite"-- (comp, confdCompiler, progdb1) <- configureCompiler hcPath progdb0-- -- Update our pkg tool. It uses the same executable as the compiler, but- -- all command start with "pkg"- (confdPkg, _) <- requireProgram verbosity haskellSuitePkgProgram progdb1- let progdb2 =- updateProgram- confdPkg- { programLocation = programLocation confdCompiler- , programDefaultArgs = ["pkg"]- }- progdb1-- return (comp, Nothing, progdb2)-- where- configureCompiler hcPath progdb0' = do- let- haskellSuiteProgram' =- haskellSuiteProgram- { programFindLocation = \v p -> findProgramOnSearchPath v p hcPath }-- -- NB: cannot call requireProgram right away — it'd think that- -- the program is already configured and won't reconfigure it again.- -- Instead, call configureProgram directly first.- progdb1 <- configureProgram verbosity haskellSuiteProgram' progdb0'- (confdCompiler, progdb2) <- requireProgram verbosity haskellSuiteProgram' progdb1-- extensions <- getExtensions verbosity confdCompiler- languages <- getLanguages verbosity confdCompiler- (compName, compVersion) <-- getCompilerVersion verbosity confdCompiler-- let- comp = Compiler {- compilerId = CompilerId (HaskellSuite compName) compVersion,- compilerAbiTag = NoAbiTag,- compilerCompat = [],- compilerLanguages = languages,- compilerExtensions = extensions,- compilerProperties = mempty- }-- return (comp, confdCompiler, progdb2)--hstoolVersion :: Verbosity -> FilePath -> IO (Maybe Version)-hstoolVersion = findProgramVersion "--hspkg-version" id--numericVersion :: Verbosity -> FilePath -> IO (Maybe Version)-numericVersion = findProgramVersion "--compiler-version" (fromMaybe "" . safeLast . words)--getCompilerVersion :: Verbosity -> ConfiguredProgram -> IO (String, Version)-getCompilerVersion verbosity prog = do- output <- rawSystemStdout verbosity (programPath prog) ["--compiler-version"]- let- parts = words output- name = concat $ safeInit parts -- there shouldn't be any spaces in the name anyway- versionStr = fromMaybe "" $ safeLast parts- version <-- maybe (die' verbosity "haskell-suite: couldn't determine compiler version") return $- simpleParsec versionStr- return (name, version)--getExtensions :: Verbosity -> ConfiguredProgram -> IO [(Extension, Maybe CompilerFlag)]-getExtensions verbosity prog = do- extStrs <-- lines `fmap`- rawSystemStdout verbosity (programPath prog) ["--supported-extensions"]- return- [ (ext, Just $ "-X" ++ prettyShow ext) | Just ext <- map simpleParsec extStrs ]--getLanguages :: Verbosity -> ConfiguredProgram -> IO [(Language, CompilerFlag)]-getLanguages verbosity prog = do- langStrs <-- lines `fmap`- rawSystemStdout verbosity (programPath prog) ["--supported-languages"]- return- [ (ext, "-G" ++ prettyShow ext) | Just ext <- map simpleParsec langStrs ]---- Other compilers do some kind of a packagedb stack check here. Not sure--- if we need something like that as well.-getInstalledPackages :: Verbosity -> PackageDBStack -> ProgramDb- -> IO InstalledPackageIndex-getInstalledPackages verbosity packagedbs progdb =- liftM (PackageIndex.fromList . concat) $ for packagedbs $ \packagedb ->- do str <-- getDbProgramOutput verbosity haskellSuitePkgProgram progdb- ["dump", packageDbOpt packagedb]- `catchExit` \_ -> die' verbosity $ "pkg dump failed"- case parsePackages str of- Right ok -> return ok- _ -> die' verbosity "failed to parse output of 'pkg dump'"-- where- parsePackages str =- case partitionEithers $ map (parseInstalledPackageInfo . toUTF8BS) (splitPkgs str) of- ([], ok) -> Right [ pkg | (_, pkg) <- ok ]- (msgss, _) -> Left (foldMap NE.toList msgss)-- splitPkgs :: String -> [String]- splitPkgs = map unlines . splitWith ("---" ==) . lines- where- splitWith :: (a -> Bool) -> [a] -> [[a]]- splitWith p xs = ys : case zs of- [] -> []- _:ws -> splitWith p ws- where (ys,zs) = break p xs--buildLib- :: Verbosity -> PackageDescription -> LocalBuildInfo- -> Library -> ComponentLocalBuildInfo -> IO ()-buildLib verbosity pkg_descr lbi lib clbi = do- -- In future, there should be a mechanism for the compiler to request any- -- number of the above parameters (or their parts) — in particular,- -- pieces of PackageDescription.- --- -- For now, we only pass those that we know are used.-- let odir = buildDir lbi- bi = libBuildInfo lib- srcDirs = map getSymbolicPath (hsSourceDirs bi) ++ [odir]- dbStack = withPackageDB lbi- language = fromMaybe Haskell98 (defaultLanguage bi)- progdb = withPrograms lbi- pkgid = packageId pkg_descr-- runDbProgram verbosity haskellSuiteProgram progdb $- [ "compile", "--build-dir", odir ] ++- concat [ ["-i", d] | d <- srcDirs ] ++- concat [ ["-I", d] | d <- [autogenComponentModulesDir lbi clbi- ,autogenPackageModulesDir lbi- ,odir] ++ includeDirs bi ] ++- [ packageDbOpt pkgDb | pkgDb <- dbStack ] ++- [ "--package-name", prettyShow pkgid ] ++- concat [ ["--package-id", prettyShow ipkgid ]- | (ipkgid, _) <- componentPackageDeps clbi ] ++- ["-G", prettyShow language] ++- concat [ ["-X", prettyShow ex] | ex <- usedExtensions bi ] ++- cppOptions (libBuildInfo lib) ++- [ prettyShow modu | modu <- allLibModules lib clbi ]----installLib- :: Verbosity- -> LocalBuildInfo- -> FilePath -- ^install location- -> FilePath -- ^install location for dynamic libraries- -> FilePath -- ^Build location- -> PackageDescription- -> Library- -> ComponentLocalBuildInfo- -> IO ()-installLib verbosity lbi targetDir dynlibTargetDir builtDir pkg lib clbi = do- let progdb = withPrograms lbi- runDbProgram verbosity haskellSuitePkgProgram progdb $- [ "install-library"- , "--build-dir", builtDir- , "--target-dir", targetDir- , "--dynlib-target-dir", dynlibTargetDir- , "--package-id", prettyShow $ packageId pkg- ] ++ map prettyShow (allLibModules lib clbi)--registerPackage- :: Verbosity- -> ProgramDb- -> PackageDBStack- -> InstalledPackageInfo- -> IO ()-registerPackage verbosity progdb packageDbs installedPkgInfo = do- (hspkg, _) <- requireProgram verbosity haskellSuitePkgProgram progdb-- runProgramInvocation verbosity $- (programInvocation hspkg- ["update", packageDbOpt $ registrationPackageDB packageDbs])- { progInvokeInput = Just $ IODataText $ showInstalledPackageInfo installedPkgInfo }--initPackageDB :: Verbosity -> ProgramDb -> FilePath -> IO ()-initPackageDB verbosity progdb dbPath =- runDbProgram verbosity haskellSuitePkgProgram progdb- ["init", dbPath]--packageDbOpt :: PackageDB -> String-packageDbOpt GlobalPackageDB = "--global"-packageDbOpt UserPackageDB = "--user"-packageDbOpt (SpecificPackageDB db) = "--package-db=" ++ db
src/Distribution/Simple/Hpc.hs view
@@ -1,7 +1,10 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Hpc -- Copyright : Thomas Tuegel 2011@@ -13,103 +16,99 @@ -- This module provides functions for locating various HPC-related paths and -- a function for adding the necessary options to a PackageDescription to -- build test suites with HPC enabled.- module Distribution.Simple.Hpc- ( Way(..), guessWay- , htmlDir- , mixDir- , tixDir- , tixFilePath- , markupPackage- , markupTest- ) where+ ( Way (..)+ , guessWay+ , htmlDir+ , mixDir+ , tixDir+ , tixFilePath+ , HPCMarkupInfo (..)+ , markupPackage+ ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -import Distribution.Types.UnqualComponentName-import Distribution.ModuleName ( main )-import qualified Distribution.PackageDescription as PD+import Distribution.ModuleName (ModuleName, main) import Distribution.PackageDescription- ( Library(..)- , TestSuite(..)- , testModules- )+ ( TestSuite (..)+ , testModules+ )+import qualified Distribution.PackageDescription as PD import Distribution.Pretty-import Distribution.Simple.LocalBuildInfo ( LocalBuildInfo(..) )+import Distribution.Simple.LocalBuildInfo+ ( LocalBuildInfo (..)+ , interpretSymbolicPathLBI+ , mbWorkDirLBI+ ) import Distribution.Simple.Program- ( hpcProgram- , requireProgramVersion- )-import Distribution.Simple.Program.Hpc ( markup, union )-import Distribution.Simple.Utils ( notice )-import Distribution.Version ( anyVersion )-import Distribution.Verbosity ( Verbosity() )-import System.Directory ( createDirectoryIfMissing, doesFileExist )-import System.FilePath+ ( hpcProgram+ , requireProgramVersion+ )+import Distribution.Simple.Program.Hpc (markup, union)+import Distribution.Simple.Utils (notice)+import Distribution.Types.UnqualComponentName+import Distribution.Utils.Path+import Distribution.Verbosity (Verbosity ())+import Distribution.Version (anyVersion) +import System.Directory (createDirectoryIfMissing, doesFileExist)+ -- ------------------------------------------------------------------------- -- Haskell Program Coverage -data Way = Vanilla | Prof | Dyn+data Way = Vanilla | Prof | Dyn | ProfDyn deriving (Bounded, Enum, Eq, Read, Show) -hpcDir :: FilePath -- ^ \"dist/\" prefix- -> Way- -> FilePath -- ^ Directory containing component's HPC .mix files-hpcDir distPref way = distPref </> "hpc" </> wayDir+hpcDir+ :: SymbolicPath Pkg (Dir Dist)+ -- ^ \"dist/\" prefix+ -> Way+ -> SymbolicPath Pkg (Dir Artifacts)+ -- ^ Directory containing component's HPC .mix files+hpcDir distPref way = distPref </> makeRelativePathEx ("hpc" </> wayDir) where wayDir = case way of Vanilla -> "vanilla" Prof -> "prof" Dyn -> "dyn"+ ProfDyn -> "prof_dyn" -mixDir :: FilePath -- ^ \"dist/\" prefix- -> Way- -> FilePath -- ^ Component name- -> FilePath -- ^ Directory containing test suite's .mix files-mixDir distPref way name = hpcDir distPrefBuild way </> "mix" </> name- where- -- This is a hack for HPC over test suites, needed to match the directory- -- where HPC saves and reads .mix files when the main library of the same- -- package is being processed, perhaps in a previous cabal run (#5213).- -- E.g., @distPref@ may be- -- @./dist-newstyle/build/x86_64-linux/ghc-9.0.1/cabal-gh5213-0.1/t/tests@- -- but the path where library mix files reside has two less components- -- at the end (@t/tests@) and this reduced path needs to be passed to- -- both @hpc@ and @ghc@. For non-default optimization levels, the path- -- suffix is one element longer and the extra path element needs- -- to be preserved.- distPrefElements = splitDirectories distPref- distPrefBuild = case drop (length distPrefElements - 3) distPrefElements of- ["t", _, "noopt"] ->- joinPath $ take (length distPrefElements - 3) distPrefElements- ++ ["noopt"]- ["t", _, "opt"] ->- joinPath $ take (length distPrefElements - 3) distPrefElements- ++ ["opt"]- [_, "t", _] ->- joinPath $ take (length distPrefElements - 2) distPrefElements- _ -> distPref+mixDir+ :: SymbolicPath Pkg (Dir Dist)+ -- ^ \"dist/\" prefix+ -> Way+ -> SymbolicPath Pkg (Dir Mix)+ -- ^ Directory containing test suite's .mix files+mixDir distPref way = hpcDir distPref way </> makeRelativePathEx "mix" -tixDir :: FilePath -- ^ \"dist/\" prefix- -> Way- -> FilePath -- ^ Component name- -> FilePath -- ^ Directory containing test suite's .tix files-tixDir distPref way name = hpcDir distPref way </> "tix" </> name+tixDir+ :: SymbolicPath Pkg (Dir Dist)+ -- ^ \"dist/\" prefix+ -> Way+ -> SymbolicPath Pkg (Dir Tix)+ -- ^ Directory containing test suite's .tix files+tixDir distPref way = hpcDir distPref way </> makeRelativePathEx "tix" -- | Path to the .tix file containing a test suite's sum statistics.-tixFilePath :: FilePath -- ^ \"dist/\" prefix- -> Way- -> FilePath -- ^ Component name- -> FilePath -- ^ Path to test suite's .tix file-tixFilePath distPref way name = tixDir distPref way name </> name <.> "tix"+tixFilePath+ :: SymbolicPath Pkg (Dir Dist)+ -- ^ \"dist/\" prefix+ -> Way+ -> FilePath+ -- ^ Component name+ -> SymbolicPath Pkg File+ -- ^ Path to test suite's .tix file+tixFilePath distPref way name = tixDir distPref way </> makeRelativePathEx (name <.> "tix") -htmlDir :: FilePath -- ^ \"dist/\" prefix- -> Way- -> FilePath -- ^ Component name- -> FilePath -- ^ Path to test suite's HTML markup directory-htmlDir distPref way name = hpcDir distPref way </> "html" </> name+htmlDir+ :: SymbolicPath Pkg (Dir Dist)+ -- ^ \"dist/\" prefix+ -> Way+ -> SymbolicPath Pkg (Dir Artifacts)+ -- ^ Path to test suite's HTML markup directory+htmlDir distPref way = hpcDir distPref way </> makeRelativePathEx "html" -- | Attempt to guess the way the test suites in this package were compiled -- and linked with the library so the correct module interfaces are found.@@ -119,59 +118,72 @@ | withDynExe lbi = Dyn | otherwise = Vanilla --- | Generate the HTML markup for a test suite.-markupTest :: Verbosity- -> LocalBuildInfo- -> FilePath -- ^ \"dist/\" prefix- -> String -- ^ Library name- -> TestSuite- -> Library- -> IO ()-markupTest verbosity lbi distPref libraryName suite library = do- tixFileExists <- doesFileExist $ tixFilePath distPref way $ testName'- when tixFileExists $ do- -- behaviour of 'markup' depends on version, so we need *a* version- -- but no particular one- (hpc, hpcVer, _) <- requireProgramVersion verbosity- hpcProgram anyVersion (withPrograms lbi)- let htmlDir_ = htmlDir distPref way testName'- markup hpc hpcVer verbosity- (tixFilePath distPref way testName') mixDirs- htmlDir_- (exposedModules library)- notice verbosity $ "Test coverage report written to "- ++ htmlDir_ </> "hpc_index" <.> "html"- where- way = guessWay lbi- testName' = unUnqualComponentName $ testName suite- mixDirs = map (mixDir distPref way) [ testName', libraryName ]+-- | Haskell Program Coverage information required to produce a valid HPC+-- report through the `hpc markup` call for the package libraries.+data HPCMarkupInfo = HPCMarkupInfo+ { pathsToLibsArtifacts :: [SymbolicPath Pkg (Dir Artifacts)]+ -- ^ The paths to the library components whose modules are included in the+ -- coverage report+ , libsModulesToInclude :: [ModuleName]+ -- ^ The modules to include in the coverage report+ } --- | Generate the HTML markup for all of a package's test suites.-markupPackage :: Verbosity- -> LocalBuildInfo- -> FilePath -- ^ \"dist/\" prefix- -> PD.PackageDescription- -> [TestSuite]- -> IO ()-markupPackage verbosity lbi distPref pkg_descr suites = do- let tixFiles = map (tixFilePath distPref way) testNames- tixFilesExist <- traverse doesFileExist tixFiles- when (and tixFilesExist) $ do- -- behaviour of 'markup' depends on version, so we need *a* version- -- but no particular one- (hpc, hpcVer, _) <- requireProgramVersion verbosity- hpcProgram anyVersion (withPrograms lbi)- let outFile = tixFilePath distPref way libraryName- htmlDir' = htmlDir distPref way libraryName- excluded = concatMap testModules suites ++ [ main ]- createDirectoryIfMissing True $ takeDirectory outFile- union hpc verbosity tixFiles outFile excluded- markup hpc hpcVer verbosity outFile mixDirs htmlDir' included- notice verbosity $ "Package coverage report written to "- ++ htmlDir' </> "hpc_index.html"+-- | Generate the HTML markup for a package's test suites.+markupPackage+ :: Verbosity+ -> HPCMarkupInfo+ -> LocalBuildInfo+ -> SymbolicPath Pkg (Dir Dist)+ -- ^ Testsuite \"dist/\" prefix+ -> PD.PackageDescription+ -> [TestSuite]+ -> IO ()+markupPackage verbosity HPCMarkupInfo{pathsToLibsArtifacts, libsModulesToInclude} lbi testDistPref pkg_descr suites = do+ let tixFiles = map (tixFilePath testDistPref way) testNames+ mbWorkDir = mbWorkDirLBI lbi+ i = interpretSymbolicPathLBI lbi -- See Note [Symbolic paths] in Distribution.Utils.Path+ tixFilesExist <- traverse (doesFileExist . i) tixFiles+ when (and tixFilesExist) $ do+ -- behaviour of 'markup' depends on version, so we need *a* version+ -- but no particular one+ (hpc, hpcVer, _) <-+ requireProgramVersion+ verbosity+ hpcProgram+ anyVersion+ (withPrograms lbi)+ let htmlDir' = htmlDir testDistPref way+ -- The tix file used to generate the report is either the testsuite's+ -- tix file, when there is only one testsuite, or the sum of the tix+ -- files of all testsuites in the package, which gets put under pkgName+ -- for this component (a bit weird)+ -- TODO: cabal-install should pass to Cabal where to put the summed tix+ -- and report, and perhaps even the testsuites from other packages in+ -- the project which are currently not accounted for in the summed+ -- report.+ tixFile <- case suites of+ -- We call 'markupPackage' once for each testsuite to run individually,+ -- to get the coverage report of just the one testsuite+ [oneTest] -> do+ let testName' = unUnqualComponentName $ testName oneTest+ return $+ tixFilePath testDistPref way testName'+ -- And call 'markupPackage' once per `test` invocation with all the+ -- testsuites to run, which results in multiple tix files being considered+ _ -> do+ let excluded = concatMap testModules suites ++ [main]+ pkgName = prettyShow $ PD.package pkg_descr+ summedTixFile = tixFilePath testDistPref way pkgName+ createDirectoryIfMissing True $ i $ takeDirectorySymbolicPath summedTixFile+ union mbWorkDir hpc verbosity tixFiles summedTixFile excluded+ return summedTixFile++ markup mbWorkDir hpc hpcVer verbosity tixFile mixDirs htmlDir' libsModulesToInclude+ notice verbosity $+ "Package coverage report written to "+ ++ i htmlDir'+ </> "hpc_index.html" where way = guessWay lbi testNames = fmap (unUnqualComponentName . testName) suites- mixDirs = map (mixDir distPref way) $ libraryName : testNames- included = concatMap (exposedModules) $ PD.allLibraries pkg_descr- libraryName = prettyShow $ PD.package pkg_descr+ mixDirs = map ((`mixDir` way) . coerceSymbolicPath) pathsToLibsArtifacts
src/Distribution/Simple/Install.hs view
@@ -1,7 +1,11 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Install -- Copyright : Isaac Jones 2003-2004@@ -14,247 +18,355 @@ -- \"@.\/setup install@\" and \"@.\/setup copy@\" actions. It moves files into -- place based on the prefix argument. It does the generic bits and then calls -- compiler-specific functions to do the rest.--module Distribution.Simple.Install (- install,+module Distribution.Simple.Install+ ( install+ , install_setupHooks+ , installFileGlob ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -import Distribution.Types.TargetInfo-import Distribution.Types.LocalBuildInfo+import Distribution.CabalSpecVersion (CabalSpecVersion)++import Distribution.Types.ExecutableScope import Distribution.Types.ForeignLib+import Distribution.Types.LocalBuildInfo import Distribution.Types.PackageDescription+import Distribution.Types.TargetInfo import Distribution.Types.UnqualComponentName-import Distribution.Types.ExecutableScope import Distribution.Package import Distribution.PackageDescription-import Distribution.Simple.LocalBuildInfo-import Distribution.Simple.BuildPaths (haddockName, haddockPref)+import Distribution.Simple.BuildPaths (haddockPath, haddockPref)+import Distribution.Simple.BuildTarget+import Distribution.Simple.Compiler+ ( CompilerFlavor (..)+ , compilerFlavor+ ) import Distribution.Simple.Glob (matchDirFileGlob)+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.Setup.Config+import Distribution.Simple.Setup.Copy+ ( CopyFlags (..)+ )+import Distribution.Simple.Setup.Haddock+ ( HaddockTarget (ForDevelopment)+ )+import Distribution.Simple.SetupHooks.Internal+ ( InstallHooks (..)+ )+import qualified Distribution.Simple.SetupHooks.Internal as SetupHooks import Distribution.Simple.Utils- ( createDirectoryIfMissingVerbose- , installDirectoryContents, installOrdinaryFile, isInSearchPath- , die', info, noticeNoWrap, warn )-import Distribution.Simple.Compiler- ( CompilerFlavor(..), compilerFlavor )-import Distribution.Simple.Setup- ( CopyFlags(..), fromFlag, HaddockTarget(ForDevelopment) )-import Distribution.Simple.BuildTarget-import Distribution.Utils.Path (getSymbolicPath)+ ( createDirectoryIfMissingVerbose+ , dieWithException+ , info+ , installDirectoryContents+ , installOrdinaryFile+ , isAbsoluteOnAnyPlatform+ , isInSearchPath+ , noticeNoWrap+ , warn+ )+import Distribution.Utils.Path -import qualified Distribution.Simple.GHC as GHC+import Distribution.Compat.Graph (IsNode (..))+import Distribution.Simple.Errors+import qualified Distribution.Simple.GHC as GHC import qualified Distribution.Simple.GHCJS as GHCJS-import qualified Distribution.Simple.UHC as UHC-import qualified Distribution.Simple.HaskellSuite as HaskellSuite-import Distribution.Compat.Graph (IsNode(..))+import Distribution.Simple.Setup.Common+import qualified Distribution.Simple.UHC as UHC import System.Directory- ( doesDirectoryExist, doesFileExist )+ ( doesDirectoryExist+ , doesFileExist+ ) import System.FilePath- ( takeFileName, takeDirectory, (</>), isRelative )+ ( takeDirectory+ , takeFileName+ ) -import Distribution.Verbosity import Distribution.Pretty- ( prettyShow )+ ( prettyShow+ )+import Distribution.Verbosity --- |Perform the \"@.\/setup install@\" and \"@.\/setup copy@\"--- actions. Move files into place based on the prefix argument.+-- | Perform the \"@.\/setup install@\" and \"@.\/setup copy@\"+-- actions. Move files into place based on the prefix argument. ----- This does NOT register libraries, you should call 'register'--- to do that.+-- This does NOT register libraries, you should call 'register'+-- to do that.+install+ :: PackageDescription+ -- ^ information from the .cabal file+ -> LocalBuildInfo+ -- ^ information from the configure step+ -> CopyFlags+ -- ^ flags sent to copy or install+ -> IO ()+install = install_setupHooks SetupHooks.noInstallHooks -install :: PackageDescription -- ^information from the .cabal file- -> LocalBuildInfo -- ^information from the configure step- -> CopyFlags -- ^flags sent to copy or install- -> IO ()-install pkg_descr lbi flags = do- checkHasLibsOrExes- targets <- readTargetInfos verbosity pkg_descr lbi (copyArgs flags)+install_setupHooks+ :: InstallHooks+ -> PackageDescription+ -- ^ information from the .cabal file+ -> LocalBuildInfo+ -- ^ information from the configure step+ -> CopyFlags+ -- ^ flags sent to copy or install+ -> IO ()+install_setupHooks+ (InstallHooks{installComponentHook})+ pkg_descr+ lbi+ flags = do+ checkHasLibsOrExes+ targets <- readTargetInfos verbosity pkg_descr lbi (copyTargets flags) - copyPackage verbosity pkg_descr lbi distPref copydest+ copyPackage verbosity pkg_descr lbi distPref copydest - -- It's not necessary to do these in build-order, but it's harmless- withNeededTargetsInBuildOrder' pkg_descr lbi (map nodeKey targets) $ \target ->- let comp = targetComponent target- clbi = targetCLBI target- in copyComponent verbosity pkg_descr lbi comp clbi copydest- where- distPref = fromFlag (copyDistPref flags)- verbosity = fromFlag (copyVerbosity flags)- copydest = fromFlag (copyDest flags)+ -- It's not necessary to do these in build-order, but it's harmless+ withNeededTargetsInBuildOrder' pkg_descr lbi (map nodeKey targets) $ \target -> do+ let comp = targetComponent target+ clbi = targetCLBI target+ copyComponent verbosity pkg_descr lbi comp clbi copydest+ for_ installComponentHook $ \instAction ->+ let inputs =+ SetupHooks.InstallComponentInputs+ { copyFlags = flags+ , localBuildInfo = lbi+ , targetInfo = target+ }+ in instAction inputs+ where+ common = copyCommonFlags flags+ distPref = fromFlag $ setupDistPref common+ verbosity = fromFlag $ setupVerbosity common+ copydest = fromFlag (copyDest flags) - checkHasLibsOrExes =- unless (hasLibs pkg_descr || hasForeignLibs pkg_descr || hasExes pkg_descr) $- die' verbosity "No executables and no library found. Nothing to do."+ checkHasLibsOrExes =+ unless (hasLibs pkg_descr || hasForeignLibs pkg_descr || hasExes pkg_descr) $+ warn verbosity "No executables and no library found. Nothing to do." -- | Copy package global files.-copyPackage :: Verbosity -> PackageDescription- -> LocalBuildInfo -> FilePath -> CopyDest -> IO ()+copyPackage+ :: Verbosity+ -> PackageDescription+ -> LocalBuildInfo+ -> SymbolicPath Pkg (Dir Dist)+ -> CopyDest+ -> IO () copyPackage verbosity pkg_descr lbi distPref copydest = do- let -- This is a bit of a hack, to handle files which are not- -- per-component (data files and Haddock files.)- InstallDirs {- datadir = dataPref,- docdir = docPref,- htmldir = htmlPref,- haddockdir = interfacePref+ let+ -- This is a bit of a hack, to handle files which are not+ -- per-component (data files and Haddock files.)+ InstallDirs+ { datadir = dataPref+ , docdir = docPref+ , htmldir = htmlPref+ , haddockdir = interfacePref } = absoluteInstallCommandDirs pkg_descr lbi (localUnitId lbi) copydest+ mbWorkDir = mbWorkDirLBI lbi+ i = interpretSymbolicPath mbWorkDir -- See Note [Symbolic paths] in Distribution.Utils.Path -- Install (package-global) data files- installDataFiles verbosity pkg_descr dataPref+ installDataFiles verbosity mbWorkDir pkg_descr $ makeSymbolicPath dataPref -- Install (package-global) Haddock files -- TODO: these should be done per-library- docExists <- doesDirectoryExist $ haddockPref ForDevelopment distPref pkg_descr- info verbosity ("directory " ++ haddockPref ForDevelopment distPref pkg_descr ++- " does exist: " ++ show docExists)+ docExists <- doesDirectoryExist $ i $ haddockPref ForDevelopment distPref pkg_descr+ info+ verbosity+ ( "directory "+ ++ getSymbolicPath (haddockPref ForDevelopment distPref pkg_descr)+ ++ " does exist: "+ ++ show docExists+ ) -- TODO: this is a bit questionable, Haddock files really should -- be per library (when there are convenience libraries.) when docExists $ do- createDirectoryIfMissingVerbose verbosity True htmlPref- installDirectoryContents verbosity- (haddockPref ForDevelopment distPref pkg_descr) htmlPref- -- setPermissionsRecursive [Read] htmlPref- -- The haddock interface file actually already got installed- -- in the recursive copy, but now we install it where we actually- -- want it to be (normally the same place). We could remove the- -- copy in htmlPref first.- let haddockInterfaceFileSrc = haddockPref ForDevelopment distPref pkg_descr- </> haddockName pkg_descr- haddockInterfaceFileDest = interfacePref </> haddockName pkg_descr- -- We only generate the haddock interface file for libs, So if the- -- package consists only of executables there will not be one:- exists <- doesFileExist haddockInterfaceFileSrc- when exists $ do- createDirectoryIfMissingVerbose verbosity True interfacePref- installOrdinaryFile verbosity haddockInterfaceFileSrc- haddockInterfaceFileDest+ createDirectoryIfMissingVerbose verbosity True htmlPref+ installDirectoryContents+ verbosity+ (i $ haddockPref ForDevelopment distPref pkg_descr)+ htmlPref+ -- setPermissionsRecursive [Read] htmlPref+ -- The haddock interface file actually already got installed+ -- in the recursive copy, but now we install it where we actually+ -- want it to be (normally the same place). We could remove the+ -- copy in htmlPref first.+ let haddockInterfaceFileSrc =+ haddockPref ForDevelopment distPref pkg_descr+ </> makeRelativePathEx (haddockPath pkg_descr)+ haddockInterfaceFileDest = interfacePref </> haddockPath pkg_descr+ -- We only generate the haddock interface file for libs, So if the+ -- package consists only of executables there will not be one:+ exists <- doesFileExist $ i haddockInterfaceFileSrc+ when exists $ do+ createDirectoryIfMissingVerbose verbosity True interfacePref+ installOrdinaryFile+ verbosity+ (i haddockInterfaceFileSrc)+ haddockInterfaceFileDest let lfiles = licenseFiles pkg_descr unless (null lfiles) $ do createDirectoryIfMissingVerbose verbosity True docPref- for_ lfiles $ \lfile' -> do- let lfile :: FilePath- lfile = getSymbolicPath lfile'- installOrdinaryFile verbosity lfile (docPref </> takeFileName lfile)+ for_ lfiles $ \lfile -> do+ installOrdinaryFile+ verbosity+ (i lfile)+ (docPref </> takeFileName (getSymbolicPath lfile)) -- | Copy files associated with a component.-copyComponent :: Verbosity -> PackageDescription- -> LocalBuildInfo -> Component -> ComponentLocalBuildInfo- -> CopyDest- -> IO ()+copyComponent+ :: Verbosity+ -> PackageDescription+ -> LocalBuildInfo+ -> Component+ -> ComponentLocalBuildInfo+ -> CopyDest+ -> IO () copyComponent verbosity pkg_descr lbi (CLib lib) clbi copydest = do- let InstallDirs{- libdir = libPref,- dynlibdir = dynlibPref,- includedir = incPref- } = absoluteInstallCommandDirs pkg_descr lbi (componentUnitId clbi) copydest- buildPref = componentBuildDir lbi clbi-- case libName lib of- LMainLibName -> noticeNoWrap verbosity ("Installing library in " ++ libPref)- LSubLibName n -> noticeNoWrap verbosity ("Installing internal library " ++ prettyShow n ++ " in " ++ libPref)+ let InstallDirs+ { libdir = libPref+ , dynlibdir = dynlibPref+ , includedir = incPref+ } = absoluteInstallCommandDirs pkg_descr lbi (componentUnitId clbi) copydest+ buildPref = interpretSymbolicPathLBI lbi $ componentBuildDir lbi clbi - -- install include files for all compilers - they may be needed to compile- -- haskell files (using the CPP extension)- installIncludeFiles verbosity (libBuildInfo lib) lbi buildPref incPref+ case libName lib of+ LMainLibName -> noticeNoWrap verbosity ("Installing library in " ++ libPref)+ LSubLibName n -> noticeNoWrap verbosity ("Installing internal library " ++ prettyShow n ++ " in " ++ libPref) - case compilerFlavor (compiler lbi) of- GHC -> GHC.installLib verbosity lbi libPref dynlibPref buildPref pkg_descr lib clbi- GHCJS -> GHCJS.installLib verbosity lbi libPref dynlibPref buildPref pkg_descr lib clbi- UHC -> UHC.installLib verbosity lbi libPref dynlibPref buildPref pkg_descr lib clbi- HaskellSuite _ -> HaskellSuite.installLib- verbosity lbi libPref dynlibPref buildPref pkg_descr lib clbi- _ -> die' verbosity $ "installing with "- ++ prettyShow (compilerFlavor (compiler lbi))- ++ " is not implemented"+ -- install include files for all compilers - they may be needed to compile+ -- haskell files (using the CPP extension)+ installIncludeFiles verbosity (libBuildInfo lib) lbi buildPref incPref + case compilerFlavor (compiler lbi) of+ GHC -> GHC.installLib verbosity lbi libPref dynlibPref buildPref pkg_descr lib clbi+ GHCJS -> GHCJS.installLib verbosity lbi libPref dynlibPref buildPref pkg_descr lib clbi+ UHC -> UHC.installLib verbosity lbi libPref dynlibPref buildPref pkg_descr lib clbi+ _ ->+ dieWithException verbosity $ CompilerNotInstalled (compilerFlavor (compiler lbi)) copyComponent verbosity pkg_descr lbi (CFLib flib) clbi copydest = do- let InstallDirs{- flibdir = flibPref,- includedir = incPref- } = absoluteComponentInstallDirs pkg_descr lbi (componentUnitId clbi) copydest- buildPref = componentBuildDir lbi clbi-- noticeNoWrap verbosity ("Installing foreign library " ++ unUnqualComponentName (foreignLibName flib) ++ " in " ++ flibPref)- installIncludeFiles verbosity (foreignLibBuildInfo flib) lbi buildPref incPref+ let InstallDirs+ { flibdir = flibPref+ , includedir = incPref+ } = absoluteComponentInstallDirs pkg_descr lbi (componentUnitId clbi) copydest+ buildPref = interpretSymbolicPathLBI lbi $ componentBuildDir lbi clbi - case compilerFlavor (compiler lbi) of- GHC -> GHC.installFLib verbosity lbi flibPref buildPref pkg_descr flib- GHCJS -> GHCJS.installFLib verbosity lbi flibPref buildPref pkg_descr flib- _ -> die' verbosity $ "installing foreign lib with "- ++ prettyShow (compilerFlavor (compiler lbi))- ++ " is not implemented"+ noticeNoWrap verbosity ("Installing foreign library " ++ unUnqualComponentName (foreignLibName flib) ++ " in " ++ flibPref)+ installIncludeFiles verbosity (foreignLibBuildInfo flib) lbi buildPref incPref + case compilerFlavor (compiler lbi) of+ GHC -> GHC.installFLib verbosity lbi flibPref buildPref pkg_descr flib+ GHCJS -> GHCJS.installFLib verbosity lbi flibPref buildPref pkg_descr flib+ _ -> dieWithException verbosity $ CompilerNotInstalled (compilerFlavor (compiler lbi)) copyComponent verbosity pkg_descr lbi (CExe exe) clbi copydest = do- let installDirs = absoluteComponentInstallDirs pkg_descr lbi (componentUnitId clbi) copydest- -- the installers know how to find the actual location of the- -- binaries- buildPref = buildDir lbi- uid = componentUnitId clbi- pkgid = packageId pkg_descr- binPref | ExecutablePrivate <- exeScope exe = libexecdir installDirs- | otherwise = bindir installDirs- progPrefixPref = substPathTemplate pkgid lbi uid (progPrefix lbi)- progSuffixPref = substPathTemplate pkgid lbi uid (progSuffix lbi)- progFix = (progPrefixPref, progSuffixPref)- noticeNoWrap verbosity ("Installing executable " ++ prettyShow (exeName exe)- ++ " in " ++ binPref)- inPath <- isInSearchPath binPref- when (not inPath) $- warn verbosity ("The directory " ++ binPref- ++ " is not in the system search path.")- case compilerFlavor (compiler lbi) of- GHC -> GHC.installExe verbosity lbi binPref buildPref progFix pkg_descr exe- GHCJS -> GHCJS.installExe verbosity lbi binPref buildPref progFix pkg_descr exe- UHC -> return ()- HaskellSuite {} -> return ()- _ -> die' verbosity $ "installing with "- ++ prettyShow (compilerFlavor (compiler lbi))- ++ " is not implemented"+ let installDirs = absoluteComponentInstallDirs pkg_descr lbi (componentUnitId clbi) copydest+ -- the installers know how to find the actual location of the+ -- binaries+ buildPref = interpretSymbolicPathLBI lbi $ buildDir lbi+ uid = componentUnitId clbi+ pkgid = packageId pkg_descr+ binPref+ | ExecutablePrivate <- exeScope exe = libexecdir installDirs+ | otherwise = bindir installDirs+ progPrefixPref = substPathTemplate pkgid lbi uid (progPrefix lbi)+ progSuffixPref = substPathTemplate pkgid lbi uid (progSuffix lbi)+ progFix = (progPrefixPref, progSuffixPref)+ noticeNoWrap+ verbosity+ ( "Installing executable "+ ++ prettyShow (exeName exe)+ ++ " in "+ ++ binPref+ )+ inPath <- isInSearchPath binPref+ when (not inPath) $+ warn+ verbosity+ ( "The directory "+ ++ binPref+ ++ " is not in the system search path."+ )+ case compilerFlavor (compiler lbi) of+ GHC -> GHC.installExe verbosity lbi binPref buildPref progFix pkg_descr exe+ GHCJS -> GHCJS.installExe verbosity lbi binPref buildPref progFix pkg_descr exe+ UHC -> return ()+ _ ->+ dieWithException verbosity $ CompilerNotInstalled (compilerFlavor (compiler lbi)) -- Nothing to do for benchmark/testsuite copyComponent _ _ _ (CBench _) _ _ = return () copyComponent _ _ _ (CTest _) _ _ = return () -- | Install the files listed in data-files----installDataFiles :: Verbosity -> PackageDescription -> FilePath -> IO ()-installDataFiles verbosity pkg_descr destDataDir =- flip traverse_ (dataFiles pkg_descr) $ \ glob -> do- let srcDataDirRaw = dataDir pkg_descr- srcDataDir = if null srcDataDirRaw- then "."- else srcDataDirRaw- files <- matchDirFileGlob verbosity (specVersion pkg_descr) srcDataDir glob- for_ files $ \ file' -> do- let src = srcDataDir </> file'- dst = destDataDir </> file'- createDirectoryIfMissingVerbose verbosity True (takeDirectory dst)- installOrdinaryFile verbosity src dst+installDataFiles+ :: Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> PackageDescription+ -> SymbolicPath Pkg (Dir DataDir)+ -> IO ()+installDataFiles verbosity mbWorkDir pkg_descr destDataDir =+ traverse_+ (installFileGlob verbosity (specVersion pkg_descr) mbWorkDir (srcDataDir, destDataDir))+ (dataFiles pkg_descr)+ where+ srcDataDirRaw = getSymbolicPath $ dataDir pkg_descr+ srcDataDir :: Maybe (SymbolicPath CWD (Dir DataDir))+ srcDataDir+ | null srcDataDirRaw =+ Nothing+ | isAbsoluteOnAnyPlatform srcDataDirRaw =+ Just $ makeSymbolicPath srcDataDirRaw+ | otherwise =+ Just $ fromMaybe sameDirectory mbWorkDir </> makeRelativePathEx srcDataDirRaw +-- | Install the files specified by the given glob pattern.+installFileGlob+ :: Verbosity+ -> CabalSpecVersion+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> (Maybe (SymbolicPath CWD (Dir DataDir)), SymbolicPath Pkg (Dir DataDir))+ -- ^ @(src_dir, dest_dir)@+ -> RelativePath DataDir File+ -- ^ file glob pattern+ -> IO ()+installFileGlob verbosity spec_version mbWorkDir (srcDir, destDir) glob = do+ files <- matchDirFileGlob verbosity spec_version srcDir glob+ for_ files $ \file' -> do+ let src = getSymbolicPath (fromMaybe sameDirectory srcDir </> file')+ dst = interpretSymbolicPath mbWorkDir (destDir </> file')+ createDirectoryIfMissingVerbose verbosity True (takeDirectory dst)+ installOrdinaryFile verbosity src dst+ -- | Install the files listed in install-includes for a library--- installIncludeFiles :: Verbosity -> BuildInfo -> LocalBuildInfo -> FilePath -> FilePath -> IO () installIncludeFiles verbosity libBi lbi buildPref destIncludeDir = do- let relincdirs = "." : filter isRelative (includeDirs libBi)- incdirs = [ baseDir lbi </> dir | dir <- relincdirs ]- ++ [ buildPref </> dir | dir <- relincdirs ]- incs <- traverse (findInc incdirs) (installIncludes libBi)- sequence_- [ do createDirectoryIfMissingVerbose verbosity True destDir- installOrdinaryFile verbosity srcFile destFile- | (relFile, srcFile) <- incs- , let destFile = destIncludeDir </> relFile- destDir = takeDirectory destFile ]+ let relincdirs = sameDirectory : mapMaybe symbolicPathRelative_maybe (includeDirs libBi)+ incdirs =+ [ root </> getSymbolicPath dir+ | -- NB: both baseDir and buildPref are already interpreted,+ -- so we don't need to interpret these paths in the call to findInc.+ dir <- relincdirs+ , root <- [baseDir lbi, buildPref]+ ]+ incs <- traverse (findInc incdirs . getSymbolicPath) (installIncludes libBi)+ sequence_+ [ do+ createDirectoryIfMissingVerbose verbosity True destDir+ installOrdinaryFile verbosity srcFile destFile+ | (relFile, srcFile) <- incs+ , let destFile = destIncludeDir </> relFile+ destDir = takeDirectory destFile+ ] where- baseDir lbi' = fromMaybe "" (takeDirectory <$> cabalFilePath lbi')- findInc [] file = die' verbosity ("can't find include file " ++ file)- findInc (dir:dirs) file = do- let path = dir </> file- exists <- doesFileExist path- if exists then return (file, path) else findInc dirs file+ baseDir lbi' = packageRoot $ configCommonFlags $ configFlags lbi'+ findInc [] file = dieWithException verbosity $ CantFindIncludeFile file+ findInc (dir : dirs) file = do+ let path = dir </> file+ exists <- doesFileExist path+ if exists then return (file, path) else findInc dirs file
src/Distribution/Simple/InstallDirs.hs view
@@ -1,11 +1,11 @@ {-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.InstallDirs -- Copyright : Isaac Jones 2003-2004@@ -22,48 +22,49 @@ -- changing the prefix all other dirs still end up changed appropriately. So it -- provides a 'PathTemplate' type and functions for substituting for these -- templates.--module Distribution.Simple.InstallDirs (- InstallDirs(..),- InstallDirTemplates,- defaultInstallDirs,- defaultInstallDirs',- combineInstallDirs,- absoluteInstallDirs,- CopyDest(..),- prefixRelativeInstallDirs,- substituteInstallDirTemplates,-- PathTemplate,- PathTemplateVariable(..),- PathTemplateEnv,- toPathTemplate,- fromPathTemplate,- combinePathTemplate,- substPathTemplate,- initialPathTemplateEnv,- platformTemplateEnv,- compilerTemplateEnv,- packageTemplateEnv,- abiTemplateEnv,- installDirsTemplateEnv,+module Distribution.Simple.InstallDirs+ ( InstallDirs (..)+ , InstallDirTemplates+ , defaultInstallDirs+ , defaultInstallDirs'+ , combineInstallDirs+ , absoluteInstallDirs+ , CopyDest (..)+ , prefixRelativeInstallDirs+ , substituteInstallDirTemplates+ , PathTemplate+ , PathTemplateVariable (..)+ , PathTemplateEnv+ , toPathTemplate+ , fromPathTemplate+ , combinePathTemplate+ , substPathTemplate+ , initialPathTemplateEnv+ , platformTemplateEnv+ , compilerTemplateEnv+ , packageTemplateEnv+ , abiTemplateEnv+ , installDirsTemplateEnv ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () import Distribution.Compat.Environment (lookupEnv)-import Distribution.Pretty-import Distribution.Package-import Distribution.System import Distribution.Compiler+import Distribution.Package+import Distribution.Pretty import Distribution.Simple.InstallDirs.Internal+import Distribution.System import System.Directory (getAppUserDataDirectory) import System.FilePath- ( (</>), isPathSeparator- , pathSeparator, dropDrive- , takeDirectory )+ ( dropDrive+ , isPathSeparator+ , pathSeparator+ , takeDirectory+ , (</>)+ ) #ifdef mingw32_HOST_OS import qualified Prelude@@ -74,32 +75,32 @@ -- --------------------------------------------------------------------------- -- Installation directories - -- | The directories where we will install files for packages. -- -- We have several different directories for different types of files since -- many systems have conventions whereby different types of files in a package -- are installed in different directories. This is particularly the case on -- Unix style systems.----data InstallDirs dir = InstallDirs {- prefix :: dir,- bindir :: dir,- libdir :: dir,- libsubdir :: dir,- dynlibdir :: dir,- flibdir :: dir, -- ^ foreign libraries- libexecdir :: dir,- libexecsubdir:: dir,- includedir :: dir,- datadir :: dir,- datasubdir :: dir,- docdir :: dir,- mandir :: dir,- htmldir :: dir,- haddockdir :: dir,- sysconfdir :: dir- } deriving (Eq, Read, Show, Functor, Generic, Typeable)+data InstallDirs dir = InstallDirs+ { prefix :: dir+ , bindir :: dir+ , libdir :: dir+ , libsubdir :: dir+ , dynlibdir :: dir+ , flibdir :: dir+ -- ^ foreign libraries+ , libexecdir :: dir+ , libexecsubdir :: dir+ , includedir :: dir+ , datadir :: dir+ , datasubdir :: dir+ , docdir :: dir+ , mandir :: dir+ , htmldir :: dir+ , haddockdir :: dir+ , sysconfdir :: dir+ }+ deriving (Eq, Read, Show, Functor, Generic) instance Binary dir => Binary (InstallDirs dir) instance Structured dir => Structured (InstallDirs dir)@@ -111,38 +112,41 @@ instance Semigroup dir => Semigroup (InstallDirs dir) where (<>) = gmappend -combineInstallDirs :: (a -> b -> c)- -> InstallDirs a- -> InstallDirs b- -> InstallDirs c-combineInstallDirs combine a b = InstallDirs {- prefix = prefix a `combine` prefix b,- bindir = bindir a `combine` bindir b,- libdir = libdir a `combine` libdir b,- libsubdir = libsubdir a `combine` libsubdir b,- dynlibdir = dynlibdir a `combine` dynlibdir b,- flibdir = flibdir a `combine` flibdir b,- libexecdir = libexecdir a `combine` libexecdir b,- libexecsubdir= libexecsubdir a `combine` libexecsubdir b,- includedir = includedir a `combine` includedir b,- datadir = datadir a `combine` datadir b,- datasubdir = datasubdir a `combine` datasubdir b,- docdir = docdir a `combine` docdir b,- mandir = mandir a `combine` mandir b,- htmldir = htmldir a `combine` htmldir b,- haddockdir = haddockdir a `combine` haddockdir b,- sysconfdir = sysconfdir a `combine` sysconfdir b- }+combineInstallDirs+ :: (a -> b -> c)+ -> InstallDirs a+ -> InstallDirs b+ -> InstallDirs c+combineInstallDirs combine a b =+ InstallDirs+ { prefix = prefix a `combine` prefix b+ , bindir = bindir a `combine` bindir b+ , libdir = libdir a `combine` libdir b+ , libsubdir = libsubdir a `combine` libsubdir b+ , dynlibdir = dynlibdir a `combine` dynlibdir b+ , flibdir = flibdir a `combine` flibdir b+ , libexecdir = libexecdir a `combine` libexecdir b+ , libexecsubdir = libexecsubdir a `combine` libexecsubdir b+ , includedir = includedir a `combine` includedir b+ , datadir = datadir a `combine` datadir b+ , datasubdir = datasubdir a `combine` datasubdir b+ , docdir = docdir a `combine` docdir b+ , mandir = mandir a `combine` mandir b+ , htmldir = htmldir a `combine` htmldir b+ , haddockdir = haddockdir a `combine` haddockdir b+ , sysconfdir = sysconfdir a `combine` sysconfdir b+ } appendSubdirs :: (a -> a -> a) -> InstallDirs a -> InstallDirs a-appendSubdirs append dirs = dirs {- libdir = libdir dirs `append` libsubdir dirs,- libexecdir = libexecdir dirs `append` libexecsubdir dirs,- datadir = datadir dirs `append` datasubdir dirs,- libsubdir = error "internal error InstallDirs.libsubdir",- libexecsubdir = error "internal error InstallDirs.libexecsubdir",- datasubdir = error "internal error InstallDirs.datasubdir"- }+appendSubdirs append dirs =+ dirs+ { libdir = libdir dirs `append` libsubdir dirs+ , libexecdir = libexecdir dirs `append` libexecsubdir dirs+ , datadir = datadir dirs `append` datasubdir dirs+ , libsubdir = error "internal error InstallDirs.libsubdir"+ , libexecsubdir = error "internal error InstallDirs.libexecsubdir"+ , datasubdir = error "internal error InstallDirs.datasubdir"+ } -- | The installation directories in terms of 'PathTemplate's that contain -- variables.@@ -165,7 +169,6 @@ -- -- An additional complication is the need to support relocatable packages on -- systems which support such things, like Windows.--- type InstallDirTemplates = InstallDirs PathTemplate -- ---------------------------------------------------------------------------@@ -174,56 +177,77 @@ defaultInstallDirs :: CompilerFlavor -> Bool -> Bool -> IO InstallDirTemplates defaultInstallDirs = defaultInstallDirs' False -defaultInstallDirs' :: Bool {- use external internal deps -}- -> CompilerFlavor -> Bool -> Bool -> IO InstallDirTemplates+defaultInstallDirs'+ :: Bool {- use external internal deps -}+ -> CompilerFlavor+ -> Bool+ -> Bool+ -> IO InstallDirTemplates defaultInstallDirs' True comp userInstall hasLibs = do dflt <- defaultInstallDirs' False comp userInstall hasLibs -- Be a bit more hermetic about per-component installs- return dflt { datasubdir = toPathTemplate $ "$abi" </> "$libname",- docdir = toPathTemplate $ "$datadir" </> "doc" </> "$abi" </> "$libname"- }+ return+ dflt+ { datasubdir = toPathTemplate $ "$abi" </> "$libname"+ , docdir = toPathTemplate $ "$datadir" </> "doc" </> "$abi" </> "$libname"+ } defaultInstallDirs' False comp userInstall _hasLibs = do installPrefix <-- if userInstall+ if userInstall then do mDir <- lookupEnv "CABAL_DIR" case mDir of Nothing -> getAppUserDataDirectory "cabal" Just dir -> return dir else case buildOS of- Windows -> do windowsProgramFilesDir <- getWindowsProgramFilesDir- return (windowsProgramFilesDir </> "Haskell")- _ -> return "/usr/local"+ Windows -> do+ windowsProgramFilesDir <- getWindowsProgramFilesDir+ return (windowsProgramFilesDir </> "Haskell")+ Haiku -> return "/boot/system/non-packaged"+ _ -> return "/usr/local" installLibDir <-- case buildOS of+ case buildOS of Windows -> return "$prefix"- _ -> return ("$prefix" </> "lib")- return $ fmap toPathTemplate $ InstallDirs {- prefix = installPrefix,- bindir = "$prefix" </> "bin",- libdir = installLibDir,- libsubdir = case comp of- UHC -> "$pkgid"- _other -> "$abi" </> "$libname",- dynlibdir = "$libdir" </> case comp of- UHC -> "$pkgid"- _other -> "$abi",- libexecsubdir= "$abi" </> "$pkgid",- flibdir = "$libdir",- libexecdir = case buildOS of- Windows -> "$prefix" </> "$libname"- _other -> "$prefix" </> "libexec",- includedir = "$libdir" </> "$libsubdir" </> "include",- datadir = case buildOS of- Windows -> "$prefix"- _other -> "$prefix" </> "share",- datasubdir = "$abi" </> "$pkgid",- docdir = "$datadir" </> "doc" </> "$abi" </> "$pkgid",- mandir = "$datadir" </> "man",- htmldir = "$docdir" </> "html",- haddockdir = "$htmldir",- sysconfdir = "$prefix" </> "etc"- }+ _ -> return ("$prefix" </> "lib")+ return $+ fmap toPathTemplate $+ InstallDirs+ { prefix = installPrefix+ , bindir = "$prefix" </> "bin"+ , libdir = installLibDir+ , libsubdir = case comp of+ UHC -> "$pkgid"+ _other -> "$abi" </> "$libname"+ , dynlibdir =+ "$libdir" </> case comp of+ UHC -> "$pkgid"+ _other -> "$abi"+ , libexecsubdir = "$abi" </> "$pkgid"+ , flibdir = "$libdir"+ , libexecdir = case buildOS of+ Windows -> "$prefix" </> "$libname"+ Haiku -> "$libdir"+ _other -> "$prefix" </> "libexec"+ , includedir = case buildOS of+ Haiku -> "$prefix" </> "develop" </> "headers"+ _other -> "$libdir" </> "$libsubdir" </> "include"+ , datadir = case buildOS of+ Windows -> "$prefix"+ Haiku -> "$prefix" </> "data"+ _other -> "$prefix" </> "share"+ , datasubdir = "$abi" </> "$pkgid"+ , docdir = case buildOS of+ Haiku -> "$prefix" </> "documentation"+ _other -> "$datadir" </> "doc" </> "$abi" </> "$pkgid"+ , mandir = case buildOS of+ Haiku -> "$docdir" </> "man"+ _other -> "$datadir" </> "man"+ , htmldir = "$docdir" </> "html"+ , haddockdir = "$htmldir"+ , sysconfdir = case buildOS of+ Haiku -> "boot" </> "system" </> "settings"+ _other -> "$prefix" </> "etc"+ } -- --------------------------------------------------------------------------- -- Converting directories, absolute or prefix-relative@@ -239,102 +263,113 @@ -- can replace 'prefix' with the 'PrefixVar' and get resulting -- 'PathTemplate's that still have the 'PrefixVar' in them. Doing this makes it -- each to check which paths are relative to the $prefix.----substituteInstallDirTemplates :: PathTemplateEnv- -> InstallDirTemplates -> InstallDirTemplates+substituteInstallDirTemplates+ :: PathTemplateEnv+ -> InstallDirTemplates+ -> InstallDirTemplates substituteInstallDirTemplates env dirs = dirs' where- dirs' = InstallDirs {- -- So this specifies exactly which vars are allowed in each template- prefix = subst prefix [],- bindir = subst bindir [prefixVar],- libdir = subst libdir [prefixVar, bindirVar],- libsubdir = subst libsubdir [],- dynlibdir = subst dynlibdir [prefixVar, bindirVar, libdirVar],- flibdir = subst flibdir [prefixVar, bindirVar, libdirVar],- libexecdir = subst libexecdir prefixBinLibVars,- libexecsubdir = subst libexecsubdir [],- includedir = subst includedir prefixBinLibVars,- datadir = subst datadir prefixBinLibVars,- datasubdir = subst datasubdir [],- docdir = subst docdir prefixBinLibDataVars,- mandir = subst mandir (prefixBinLibDataVars ++ [docdirVar]),- htmldir = subst htmldir (prefixBinLibDataVars ++ [docdirVar]),- haddockdir = subst haddockdir (prefixBinLibDataVars ++- [docdirVar, htmldirVar]),- sysconfdir = subst sysconfdir prefixBinLibVars- }- subst dir env' = substPathTemplate (env'++env) (dir dirs)+ dirs' =+ InstallDirs+ { -- So this specifies exactly which vars are allowed in each template+ prefix = subst prefix []+ , bindir = subst bindir [prefixVar]+ , libdir = subst libdir [prefixVar, bindirVar]+ , libsubdir = subst libsubdir []+ , dynlibdir = subst dynlibdir [prefixVar, bindirVar, libdirVar]+ , flibdir = subst flibdir [prefixVar, bindirVar, libdirVar]+ , libexecdir = subst libexecdir prefixBinLibVars+ , libexecsubdir = subst libexecsubdir []+ , includedir = subst includedir prefixBinLibVars+ , datadir = subst datadir prefixBinLibVars+ , datasubdir = subst datasubdir []+ , docdir = subst docdir prefixBinLibDataVars+ , mandir = subst mandir (prefixBinLibDataVars ++ [docdirVar])+ , htmldir = subst htmldir (prefixBinLibDataVars ++ [docdirVar])+ , haddockdir =+ subst+ haddockdir+ ( prefixBinLibDataVars+ ++ [docdirVar, htmldirVar]+ )+ , sysconfdir = subst sysconfdir prefixBinLibVars+ }+ subst dir env' = substPathTemplate (env' ++ env) (dir dirs) - prefixVar = (PrefixVar, prefix dirs')- bindirVar = (BindirVar, bindir dirs')- libdirVar = (LibdirVar, libdir dirs')- libsubdirVar = (LibsubdirVar, libsubdir dirs')- datadirVar = (DatadirVar, datadir dirs')- datasubdirVar = (DatasubdirVar, datasubdir dirs')- docdirVar = (DocdirVar, docdir dirs')- htmldirVar = (HtmldirVar, htmldir dirs')+ prefixVar = (PrefixVar, prefix dirs')+ bindirVar = (BindirVar, bindir dirs')+ libdirVar = (LibdirVar, libdir dirs')+ libsubdirVar = (LibsubdirVar, libsubdir dirs')+ datadirVar = (DatadirVar, datadir dirs')+ datasubdirVar = (DatasubdirVar, datasubdir dirs')+ docdirVar = (DocdirVar, docdir dirs')+ htmldirVar = (HtmldirVar, htmldir dirs') prefixBinLibVars = [prefixVar, bindirVar, libdirVar, libsubdirVar] prefixBinLibDataVars = prefixBinLibVars ++ [datadirVar, datasubdirVar] -- | Convert from abstract install directories to actual absolute ones by -- substituting for all the variables in the abstract paths, to get real -- absolute path.-absoluteInstallDirs :: PackageIdentifier- -> UnitId- -> CompilerInfo- -> CopyDest- -> Platform- -> InstallDirs PathTemplate- -> InstallDirs FilePath+absoluteInstallDirs+ :: PackageIdentifier+ -> UnitId+ -> CompilerInfo+ -> CopyDest+ -> Platform+ -> InstallDirs PathTemplate+ -> InstallDirs FilePath absoluteInstallDirs pkgId libname compilerId copydest platform dirs =- (case copydest of- CopyTo destdir -> fmap ((destdir </>) . dropDrive)- CopyToDb dbdir -> fmap (substPrefix "${pkgroot}" (takeDirectory dbdir))- _ -> id)- . appendSubdirs (</>)- . fmap fromPathTemplate- $ substituteInstallDirTemplates env dirs+ ( case copydest of+ CopyTo destdir -> fmap ((destdir </>) . dropDrive)+ CopyToDb dbdir -> fmap (substPrefix "${pkgroot}" (takeDirectory dbdir))+ _ -> id+ )+ . appendSubdirs (</>)+ . fmap fromPathTemplate+ $ substituteInstallDirTemplates env dirs where env = initialPathTemplateEnv pkgId libname compilerId platform substPrefix pre root path | pre `isPrefixOf` path = root ++ drop (length pre) path- | otherwise = path-+ | otherwise = path --- |The location prefix for the /copy/ command.+-- | The location prefix for the /copy/ command. data CopyDest = NoCopyDest | CopyTo FilePath- | CopyToDb FilePath- -- ^ when using the ${pkgroot} as prefix. The CopyToDb will- -- adjust the paths to be relative to the provided package- -- database when copying / installing.+ | -- | when using the ${pkgroot} as prefix. The CopyToDb will+ -- adjust the paths to be relative to the provided package+ -- database when copying / installing.+ CopyToDb FilePath deriving (Eq, Show, Generic) +-- TODO: are these paths absolute or relative? Relative to what?+ instance Binary CopyDest+instance Structured CopyDest -- | Check which of the paths are relative to the installation $prefix. -- -- If any of the paths are not relative, ie they are absolute paths, then it -- prevents us from making a relocatable package (also known as a \"prefix -- independent\" package).----prefixRelativeInstallDirs :: PackageIdentifier- -> UnitId- -> CompilerInfo- -> Platform- -> InstallDirTemplates- -> InstallDirs (Maybe FilePath)+prefixRelativeInstallDirs+ :: PackageIdentifier+ -> UnitId+ -> CompilerInfo+ -> Platform+ -> InstallDirTemplates+ -> InstallDirs (Maybe FilePath) prefixRelativeInstallDirs pkgId libname compilerId platform dirs =- fmap relative- . appendSubdirs combinePathTemplate- $ -- substitute the path template into each other, except that we map+ fmap relative+ . appendSubdirs combinePathTemplate+ $ substituteInstallDirTemplates -- substitute the path template into each other, except that we map -- \$prefix back to $prefix. We're trying to end up with templates that -- mention no vars except $prefix.- substituteInstallDirTemplates env dirs {- prefix = PathTemplate [Variable PrefixVar]- }+ env+ dirs+ { prefix = PathTemplate [Variable PrefixVar]+ } where env = initialPathTemplateEnv pkgId libname compilerId platform @@ -342,19 +377,18 @@ -- path by stripping off $prefix/ or $prefix relative dir = case dir of PathTemplate cs -> fmap (fromPathTemplate . PathTemplate) (relative' cs)- relative' (Variable PrefixVar : Ordinary (s:rest) : rest')- | isPathSeparator s = Just (Ordinary rest : rest')+ relative' (Variable PrefixVar : Ordinary (s : rest) : rest')+ | isPathSeparator s = Just (Ordinary rest : rest') relative' (Variable PrefixVar : rest) = Just rest- relative' _ = Nothing+ relative' _ = Nothing -- --------------------------------------------------------------------------- -- Path templates -- | An abstract path, possibly containing variables that need to be -- substituted for to get a real 'FilePath'.--- newtype PathTemplate = PathTemplate [PathComponent]- deriving (Eq, Ord, Generic, Typeable)+ deriving (Eq, Ord, Generic) instance Binary PathTemplate instance Structured PathTemplate@@ -362,15 +396,14 @@ type PathTemplateEnv = [(PathTemplateVariable, PathTemplate)] -- | Convert a 'FilePath' to a 'PathTemplate' including any template vars.--- toPathTemplate :: FilePath -> PathTemplate-toPathTemplate fp = PathTemplate+toPathTemplate fp =+ PathTemplate . fromMaybe (error $ "panic! toPathTemplate " ++ show fp) . readMaybe -- TODO: eradicateNoParse $ fp -- | Convert back to a path, any remaining vars are included--- fromPathTemplate :: PathTemplate -> FilePath fromPathTemplate (PathTemplate template) = show template @@ -380,71 +413,80 @@ substPathTemplate :: PathTemplateEnv -> PathTemplate -> PathTemplate substPathTemplate environment (PathTemplate template) =- PathTemplate (concatMap subst template)-- where subst component@(Ordinary _) = [component]- subst component@(Variable variable) =- case lookup variable environment of- Just (PathTemplate components) -> components- Nothing -> [component]+ PathTemplate (concatMap subst template)+ where+ subst component@(Ordinary _) = [component]+ subst component@(Variable variable) =+ case lookup variable environment of+ Just (PathTemplate components) -> components+ Nothing -> [component] -- | The initial environment has all the static stuff but no paths-initialPathTemplateEnv :: PackageIdentifier- -> UnitId- -> CompilerInfo- -> Platform- -> PathTemplateEnv+initialPathTemplateEnv+ :: PackageIdentifier+ -> UnitId+ -> CompilerInfo+ -> Platform+ -> PathTemplateEnv initialPathTemplateEnv pkgId libname compiler platform =- packageTemplateEnv pkgId libname- ++ compilerTemplateEnv compiler- ++ platformTemplateEnv platform- ++ abiTemplateEnv compiler platform+ packageTemplateEnv pkgId libname+ ++ compilerTemplateEnv compiler+ ++ platformTemplateEnv platform+ ++ abiTemplateEnv compiler platform packageTemplateEnv :: PackageIdentifier -> UnitId -> PathTemplateEnv packageTemplateEnv pkgId uid =- [(PkgNameVar, PathTemplate [Ordinary $ prettyShow (packageName pkgId)])- ,(PkgVerVar, PathTemplate [Ordinary $ prettyShow (packageVersion pkgId)])- -- Invariant: uid is actually a HashedUnitId. Hard to enforce because- -- it's an API change.- ,(LibNameVar, PathTemplate [Ordinary $ prettyShow uid])- ,(PkgIdVar, PathTemplate [Ordinary $ prettyShow pkgId])+ [ (PkgNameVar, PathTemplate [Ordinary $ prettyShow (packageName pkgId)])+ , (PkgVerVar, PathTemplate [Ordinary $ prettyShow (packageVersion pkgId)])+ , -- Invariant: uid is actually a HashedUnitId. Hard to enforce because+ -- it's an API change.+ (LibNameVar, PathTemplate [Ordinary $ prettyShow uid])+ , (PkgIdVar, PathTemplate [Ordinary $ prettyShow pkgId]) ] compilerTemplateEnv :: CompilerInfo -> PathTemplateEnv compilerTemplateEnv compiler =- [(CompilerVar, PathTemplate [Ordinary $ prettyShow (compilerInfoId compiler)])+ [ (CompilerVar, PathTemplate [Ordinary $ prettyShow (compilerInfoId compiler)]) ] platformTemplateEnv :: Platform -> PathTemplateEnv platformTemplateEnv (Platform arch os) =- [(OSVar, PathTemplate [Ordinary $ prettyShow os])- ,(ArchVar, PathTemplate [Ordinary $ prettyShow arch])+ [ (OSVar, PathTemplate [Ordinary $ prettyShow os])+ , (ArchVar, PathTemplate [Ordinary $ prettyShow arch]) ] abiTemplateEnv :: CompilerInfo -> Platform -> PathTemplateEnv abiTemplateEnv compiler (Platform arch os) =- [(AbiVar, PathTemplate [Ordinary $ prettyShow arch ++ '-':prettyShow os ++- '-':prettyShow (compilerInfoId compiler) ++- case compilerInfoAbiTag compiler of- NoAbiTag -> ""- AbiTag tag -> '-':tag])- ,(AbiTagVar, PathTemplate [Ordinary $ abiTagString (compilerInfoAbiTag compiler)])+ [+ ( AbiVar+ , PathTemplate+ [ Ordinary $+ prettyShow arch+ ++ '-'+ : prettyShow os+ ++ '-'+ : prettyShow (compilerInfoId compiler)+ ++ case compilerInfoAbiTag compiler of+ NoAbiTag -> ""+ AbiTag tag -> '-' : tag+ ]+ )+ , (AbiTagVar, PathTemplate [Ordinary $ abiTagString (compilerInfoAbiTag compiler)]) ] installDirsTemplateEnv :: InstallDirs PathTemplate -> PathTemplateEnv installDirsTemplateEnv dirs =- [(PrefixVar, prefix dirs)- ,(BindirVar, bindir dirs)- ,(LibdirVar, libdir dirs)- ,(LibsubdirVar, libsubdir dirs)- ,(DynlibdirVar, dynlibdir dirs)- ,(DatadirVar, datadir dirs)- ,(DatasubdirVar, datasubdir dirs)- ,(DocdirVar, docdir dirs)- ,(HtmldirVar, htmldir dirs)+ [ (PrefixVar, prefix dirs)+ , (BindirVar, bindir dirs)+ , (LibdirVar, libdir dirs)+ , (LibsubdirVar, libsubdir dirs)+ , (DynlibdirVar, dynlibdir dirs)+ , (DatadirVar, datadir dirs)+ , (DatasubdirVar, datasubdir dirs)+ , (DocdirVar, docdir dirs)+ , (HtmldirVar, htmldir dirs) ] - -- --------------------------------------------------------------------------- -- Parsing and showing path templates: @@ -458,13 +500,16 @@ show (PathTemplate template) = show (show template) instance Read PathTemplate where- readsPrec p s = [ (PathTemplate template, s')- | (path, s') <- readsPrec p s- , (template, "") <- reads path ]+ readsPrec p s =+ [ (PathTemplate template, s')+ | (path, s') <- readsPrec p s+ , (template, "") <- reads path+ ] -- --------------------------------------------------------------------------- -- Internal utilities +{- FOURMOLU_DISABLE -} getWindowsProgramFilesDir :: IO FilePath getWindowsProgramFilesDir = do #ifdef mingw32_HOST_OS@@ -473,6 +518,7 @@ let m = Nothing #endif return (fromMaybe "C:\\Program Files" m)+{- FOURMOLU_ENABLE -} #ifdef mingw32_HOST_OS shGetFolderPath :: CInt -> IO (Maybe FilePath)@@ -490,7 +536,8 @@ -- csidl_PROGRAM_FILES_COMMON :: CInt -- csidl_PROGRAM_FILES_COMMON = 0x002b -#ifdef x86_64_HOST_ARCH+{- FOURMOLU_DISABLE -}+#if defined(x86_64_HOST_ARCH) || defined(aarch64_HOST_ARCH) #define CALLCONV ccall #else #define CALLCONV stdcall@@ -504,3 +551,4 @@ -> CWString -> Prelude.IO CInt #endif+{- FOURMOLU_ENABLE -}
src/Distribution/Simple/InstallDirs/Internal.hs view
@@ -1,127 +1,159 @@-{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-}+ module Distribution.Simple.InstallDirs.Internal- ( PathComponent(..)- , PathTemplateVariable(..)+ ( PathComponent (..)+ , PathTemplateVariable (..) ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -data PathComponent =- Ordinary FilePath- | Variable PathTemplateVariable- deriving (Eq, Ord, Generic, Typeable)+data PathComponent+ = Ordinary FilePath+ | Variable PathTemplateVariable+ deriving (Eq, Ord, Generic) instance Binary PathComponent instance Structured PathComponent -data PathTemplateVariable =- PrefixVar -- ^ The @$prefix@ path variable- | BindirVar -- ^ The @$bindir@ path variable- | LibdirVar -- ^ The @$libdir@ path variable- | LibsubdirVar -- ^ The @$libsubdir@ path variable- | DynlibdirVar -- ^ The @$dynlibdir@ path variable- | DatadirVar -- ^ The @$datadir@ path variable- | DatasubdirVar -- ^ The @$datasubdir@ path variable- | DocdirVar -- ^ The @$docdir@ path variable- | HtmldirVar -- ^ The @$htmldir@ path variable- | PkgNameVar -- ^ The @$pkg@ package name path variable- | PkgVerVar -- ^ The @$version@ package version path variable- | PkgIdVar -- ^ The @$pkgid@ package Id path variable, eg @foo-1.0@- | LibNameVar -- ^ The @$libname@ path variable- | CompilerVar -- ^ The compiler name and version, eg @ghc-6.6.1@- | OSVar -- ^ The operating system name, eg @windows@ or @linux@- | ArchVar -- ^ The CPU architecture name, eg @i386@ or @x86_64@- | AbiVar -- ^ The compiler's ABI identifier,- --- $arch-$os-$compiler-$abitag- | AbiTagVar -- ^ The optional ABI tag for the compiler- | ExecutableNameVar -- ^ The executable name; used in shell wrappers- | TestSuiteNameVar -- ^ The name of the test suite being run- | TestSuiteResultVar -- ^ The result of the test suite being run, eg- -- @pass@, @fail@, or @error@.- | BenchmarkNameVar -- ^ The name of the benchmark being run- deriving (Eq, Ord, Generic, Typeable)+data PathTemplateVariable+ = -- | The @$prefix@ path variable+ PrefixVar+ | -- | The @$bindir@ path variable+ BindirVar+ | -- | The @$libdir@ path variable+ LibdirVar+ | -- | The @$libsubdir@ path variable+ LibsubdirVar+ | -- | The @$dynlibdir@ path variable+ DynlibdirVar+ | -- | The @$datadir@ path variable+ DatadirVar+ | -- | The @$datasubdir@ path variable+ DatasubdirVar+ | -- | The @$docdir@ path variable+ DocdirVar+ | -- | The @$htmldir@ path variable+ HtmldirVar+ | -- | The @$pkg@ package name path variable+ PkgNameVar+ | -- | The @$version@ package version path variable+ PkgVerVar+ | -- | The @$pkgid@ package Id path variable, eg @foo-1.0@+ PkgIdVar+ | -- | The @$libname@ path variable+ LibNameVar+ | -- | The compiler name and version, eg @ghc-6.6.1@+ CompilerVar+ | -- | The operating system name, eg @windows@ or @linux@+ OSVar+ | -- | The CPU architecture name, eg @i386@ or @x86_64@+ ArchVar+ | -- | The compiler's ABI identifier,+ AbiVar+ | --- $arch-$os-$compiler-$abitag + -- | The optional ABI tag for the compiler+ AbiTagVar+ | -- | The executable name; used in shell wrappers+ ExecutableNameVar+ | -- | The name of the test suite being run+ TestSuiteNameVar+ | -- | The result of the test suite being run, eg+ -- @pass@, @fail@, or @error@.+ TestSuiteResultVar+ | -- | The name of the benchmark being run+ BenchmarkNameVar+ deriving (Eq, Ord, Generic)+ instance Binary PathTemplateVariable instance Structured PathTemplateVariable instance Show PathTemplateVariable where- show PrefixVar = "prefix"- show LibNameVar = "libname"- show BindirVar = "bindir"- show LibdirVar = "libdir"- show LibsubdirVar = "libsubdir"- show DynlibdirVar = "dynlibdir"- show DatadirVar = "datadir"+ show PrefixVar = "prefix"+ show LibNameVar = "libname"+ show BindirVar = "bindir"+ show LibdirVar = "libdir"+ show LibsubdirVar = "libsubdir"+ show DynlibdirVar = "dynlibdir"+ show DatadirVar = "datadir" show DatasubdirVar = "datasubdir"- show DocdirVar = "docdir"- show HtmldirVar = "htmldir"- show PkgNameVar = "pkg"- show PkgVerVar = "version"- show PkgIdVar = "pkgid"- show CompilerVar = "compiler"- show OSVar = "os"- show ArchVar = "arch"- show AbiTagVar = "abitag"- show AbiVar = "abi"+ show DocdirVar = "docdir"+ show HtmldirVar = "htmldir"+ show PkgNameVar = "pkg"+ show PkgVerVar = "version"+ show PkgIdVar = "pkgid"+ show CompilerVar = "compiler"+ show OSVar = "os"+ show ArchVar = "arch"+ show AbiTagVar = "abitag"+ show AbiVar = "abi" show ExecutableNameVar = "executablename"- show TestSuiteNameVar = "test-suite"+ show TestSuiteNameVar = "test-suite" show TestSuiteResultVar = "result"- show BenchmarkNameVar = "benchmark"+ show BenchmarkNameVar = "benchmark" instance Read PathTemplateVariable where readsPrec _ s =- take 1- [ (var, drop (length varStr) s)- | (varStr, var) <- vars- , varStr `isPrefixOf` s ]- -- NB: order matters! Longer strings first- where vars = [("prefix", PrefixVar)- ,("bindir", BindirVar)- ,("libdir", LibdirVar)- ,("libsubdir", LibsubdirVar)- ,("dynlibdir", DynlibdirVar)- ,("datadir", DatadirVar)- ,("datasubdir", DatasubdirVar)- ,("docdir", DocdirVar)- ,("htmldir", HtmldirVar)- ,("pkgid", PkgIdVar)- ,("libname", LibNameVar)- ,("pkgkey", LibNameVar) -- backwards compatibility- ,("pkg", PkgNameVar)- ,("version", PkgVerVar)- ,("compiler", CompilerVar)- ,("os", OSVar)- ,("arch", ArchVar)- ,("abitag", AbiTagVar)- ,("abi", AbiVar)- ,("executablename", ExecutableNameVar)- ,("test-suite", TestSuiteNameVar)- ,("result", TestSuiteResultVar)- ,("benchmark", BenchmarkNameVar)]+ take+ 1+ [ (var, drop (length varStr) s)+ | (varStr, var) <- vars+ , varStr `isPrefixOf` s+ ]+ where+ -- NB: order matters! Longer strings first+ vars =+ [ ("prefix", PrefixVar)+ , ("bindir", BindirVar)+ , ("libdir", LibdirVar)+ , ("libsubdir", LibsubdirVar)+ , ("dynlibdir", DynlibdirVar)+ , ("datadir", DatadirVar)+ , ("datasubdir", DatasubdirVar)+ , ("docdir", DocdirVar)+ , ("htmldir", HtmldirVar)+ , ("pkgid", PkgIdVar)+ , ("libname", LibNameVar)+ , ("pkgkey", LibNameVar) -- backwards compatibility+ , ("pkg", PkgNameVar)+ , ("version", PkgVerVar)+ , ("compiler", CompilerVar)+ , ("os", OSVar)+ , ("arch", ArchVar)+ , ("abitag", AbiTagVar)+ , ("abi", AbiVar)+ , ("executablename", ExecutableNameVar)+ , ("test-suite", TestSuiteNameVar)+ , ("result", TestSuiteResultVar)+ , ("benchmark", BenchmarkNameVar)+ ] instance Show PathComponent where show (Ordinary path) = path- show (Variable var) = '$':show var+ show (Variable var) = '$' : show var showList = foldr (\x -> (shows x .)) id instance Read PathComponent where -- for some reason we collapse multiple $ symbols here readsPrec _ = lex0- where lex0 [] = []- lex0 ('$':'$':s') = lex0 ('$':s')- lex0 ('$':s') = case [ (Variable var, s'')- | (var, s'') <- reads s' ] of- [] -> lex1 "$" s'- ok -> ok- lex0 s' = lex1 [] s'- lex1 "" "" = []- lex1 acc "" = [(Ordinary (reverse acc), "")]- lex1 acc ('$':'$':s) = lex1 acc ('$':s)- lex1 acc ('$':s) = [(Ordinary (reverse acc), '$':s)]- lex1 acc (c:s) = lex1 (c:acc) s- readList [] = [([],"")]- readList s = [ (component:components, s'')- | (component, s') <- reads s- , (components, s'') <- readList s' ]+ where+ lex0 [] = []+ lex0 ('$' : '$' : s') = lex0 ('$' : s')+ lex0 ('$' : s') = case [ (Variable var, s'')+ | (var, s'') <- reads s'+ ] of+ [] -> lex1 "$" s'+ ok -> ok+ lex0 s' = lex1 [] s'+ lex1 "" "" = []+ lex1 acc "" = [(Ordinary (reverse acc), "")]+ lex1 acc ('$' : '$' : s) = lex1 acc ('$' : s)+ lex1 acc ('$' : s) = [(Ordinary (reverse acc), '$' : s)]+ lex1 acc (c : s) = lex1 (c : acc) s+ readList [] = [([], "")]+ readList s =+ [ (component : components, s'')+ | (component, s') <- reads s+ , (components, s'') <- readList s'+ ]
src/Distribution/Simple/LocalBuildInfo.hs view
@@ -1,8 +1,10 @@-{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.LocalBuildInfo -- Copyright : Isaac Jones 2003-2004@@ -18,166 +20,231 @@ -- programs, the package database to use and a bunch of miscellaneous configure -- flags. It gets saved and reloaded from a file (@dist\/setup-config@). It gets -- passed in to very many subsequent build actions.--module Distribution.Simple.LocalBuildInfo (- LocalBuildInfo(..),- localComponentId,- localUnitId,- localCompatPackageKey,+module Distribution.Simple.LocalBuildInfo+ ( LocalBuildInfo (..)+ , localComponentId+ , localUnitId+ , localCompatPackageKey - -- * Buildable package components- Component(..),- ComponentName(..),- LibraryName(..),- defaultLibName,- showComponentName,- componentNameString,- ComponentLocalBuildInfo(..),- componentBuildDir,- foldComponent,- componentName,- componentBuildInfo,- componentBuildable,- pkgComponents,- pkgBuildableComponents,- lookupComponent,- getComponent,- allComponentsInBuildOrder,- depLibraryPaths,- allLibModules,+ -- * Convenience accessors+ , buildDir+ , packageRoot+ , progPrefix+ , progSuffix+ , interpretSymbolicPathLBI+ , mbWorkDirLBI+ , absoluteWorkingDirLBI+ , buildWays - withAllComponentsInBuildOrder,- withLibLBI,- withExeLBI,- withBenchLBI,- withTestLBI,- enabledTestLBIs,- enabledBenchLBIs,+ -- * Buildable package components+ , Component (..)+ , ComponentName (..)+ , LibraryName (..)+ , defaultLibName+ , showComponentName+ , componentNameString+ , ComponentLocalBuildInfo (..)+ , componentBuildDir+ , foldComponent+ , componentName+ , componentBuildInfo+ , componentBuildable+ , pkgComponents+ , pkgBuildableComponents+ , lookupComponent+ , getComponent+ , allComponentsInBuildOrder+ , depLibraryPaths+ , allLibModules+ , withAllComponentsInBuildOrder+ , withLibLBI+ , withExeLBI+ , withBenchLBI+ , withTestLBI+ , enabledTestLBIs+ , enabledBenchLBIs - -- * Installation directories- module Distribution.Simple.InstallDirs,- absoluteInstallDirs, prefixRelativeInstallDirs,- absoluteInstallCommandDirs,- absoluteComponentInstallDirs, prefixRelativeComponentInstallDirs,- substPathTemplate,+ -- * Installation directories+ , module Distribution.Simple.InstallDirs+ , absoluteInstallDirs+ , prefixRelativeInstallDirs+ , absoluteInstallCommandDirs+ , absoluteComponentInstallDirs+ , prefixRelativeComponentInstallDirs+ , substPathTemplate ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () import Distribution.Types.Component-import Distribution.Types.PackageId-import Distribution.Types.UnitId-import Distribution.Types.ComponentName-import Distribution.Types.UnqualComponentName-import Distribution.Types.PackageDescription import Distribution.Types.ComponentLocalBuildInfo+import Distribution.Types.ComponentName import Distribution.Types.LocalBuildInfo+import Distribution.Types.PackageDescription+import Distribution.Types.PackageId import Distribution.Types.TargetInfo+import Distribution.Types.UnitId+import Distribution.Types.UnqualComponentName -import Distribution.Simple.InstallDirs hiding (absoluteInstallDirs,- prefixRelativeInstallDirs,- substPathTemplate, )-import qualified Distribution.Simple.InstallDirs as InstallDirs-import Distribution.PackageDescription+import qualified Distribution.Compat.Graph as Graph import qualified Distribution.InstalledPackageInfo as Installed-import Distribution.Package import Distribution.ModuleName+import Distribution.Package+import Distribution.PackageDescription+import Distribution.Pretty import Distribution.Simple.Compiler+import Distribution.Simple.Flag+import Distribution.Simple.InstallDirs hiding+ ( absoluteInstallDirs+ , prefixRelativeInstallDirs+ , substPathTemplate+ )+import qualified Distribution.Simple.InstallDirs as InstallDirs import Distribution.Simple.PackageIndex+import Distribution.Simple.Setup.Common+import Distribution.Simple.Setup.Config import Distribution.Simple.Utils-import Distribution.Pretty-import qualified Distribution.Compat.Graph as Graph+import Distribution.Utils.Path import Data.List (stripPrefix)-import System.FilePath--import System.Directory (doesDirectoryExist, canonicalizePath)+import qualified System.Directory as Directory+ ( canonicalizePath+ , doesDirectoryExist+ ) -- ----------------------------------------------------------------------------- -- Configuration information of buildable components -componentBuildDir :: LocalBuildInfo -> ComponentLocalBuildInfo -> FilePath+componentBuildDir :: LocalBuildInfo -> ComponentLocalBuildInfo -> SymbolicPath Pkg (Dir Build) -- For now, we assume that libraries/executables/test-suites/benchmarks -- are only ever built once. With Backpack, we need a special case for -- libraries so that we can handle building them multiple times.-componentBuildDir lbi clbi- = buildDir lbi </>- case componentLocalName clbi of- CLibName LMainLibName ->- if prettyShow (componentUnitId clbi) == prettyShow (componentComponentId clbi)- then ""- else prettyShow (componentUnitId clbi)- CLibName (LSubLibName s) ->- if prettyShow (componentUnitId clbi) == prettyShow (componentComponentId clbi)- then unUnqualComponentName s- else prettyShow (componentUnitId clbi)- CFLibName s -> unUnqualComponentName s- CExeName s -> unUnqualComponentName s- CTestName s -> unUnqualComponentName s- CBenchName s -> unUnqualComponentName s+componentBuildDir lbi clbi =+ (buildDir lbi </>) $+ makeRelativePathEx $+ case componentLocalName clbi of+ CLibName LMainLibName ->+ if prettyShow (componentUnitId clbi) == prettyShow (componentComponentId clbi)+ then ""+ else prettyShow (componentUnitId clbi)+ CLibName (LSubLibName s) ->+ if prettyShow (componentUnitId clbi) == prettyShow (componentComponentId clbi)+ then unUnqualComponentName s+ else prettyShow (componentUnitId clbi)+ CFLibName s -> unUnqualComponentName s+ CExeName s -> unUnqualComponentName s+ CTestName s -> unUnqualComponentName s+ CBenchName s -> unUnqualComponentName s +-- | Interpret a symbolic path with respect to the working directory+-- stored in 'LocalBuildInfo'.+--+-- Use this before directly interacting with the file system.+--+-- NB: when invoking external programs (such as @GHC@), it is preferable to set+-- the working directory of the process rather than calling this function, as+-- this function will turn relative paths into absolute paths if the working+-- directory is an absolute path. This can degrade error messages, or worse,+-- break the behaviour entirely (because the program might expect certain paths+-- to be relative).+--+-- See Note [Symbolic paths] in Distribution.Utils.Path+interpretSymbolicPathLBI :: LocalBuildInfo -> SymbolicPathX allowAbsolute Pkg to -> FilePath+interpretSymbolicPathLBI lbi =+ interpretSymbolicPath (mbWorkDirLBI lbi)++-- | Retrieve an optional working directory from 'LocalBuildInfo'.+mbWorkDirLBI :: LocalBuildInfo -> Maybe (SymbolicPath CWD (Dir Pkg))+mbWorkDirLBI =+ flagToMaybe . setupWorkingDir . configCommonFlags . configFlags++-- | Absolute path to the current working directory.+absoluteWorkingDirLBI :: LocalBuildInfo -> IO (AbsolutePath (Dir Pkg))+absoluteWorkingDirLBI lbi = absoluteWorkingDir (mbWorkDirLBI lbi)+ -- | Perform the action on each enabled 'library' in the package -- description with the 'ComponentLocalBuildInfo'.-withLibLBI :: PackageDescription -> LocalBuildInfo- -> (Library -> ComponentLocalBuildInfo -> IO ()) -> IO ()+withLibLBI+ :: PackageDescription+ -> LocalBuildInfo+ -> (Library -> ComponentLocalBuildInfo -> IO ())+ -> IO () withLibLBI pkg lbi f =- withAllTargetsInBuildOrder' pkg lbi $ \target ->- case targetComponent target of- CLib lib -> f lib (targetCLBI target)- _ -> return ()+ withAllTargetsInBuildOrder' pkg lbi $ \target ->+ case targetComponent target of+ CLib lib -> f lib (targetCLBI target)+ _ -> return () -- | Perform the action on each enabled 'Executable' in the package -- description. Extended version of 'withExe' that also gives corresponding -- build info.-withExeLBI :: PackageDescription -> LocalBuildInfo- -> (Executable -> ComponentLocalBuildInfo -> IO ()) -> IO ()+withExeLBI+ :: PackageDescription+ -> LocalBuildInfo+ -> (Executable -> ComponentLocalBuildInfo -> IO ())+ -> IO () withExeLBI pkg lbi f =- withAllTargetsInBuildOrder' pkg lbi $ \target ->- case targetComponent target of- CExe exe -> f exe (targetCLBI target)- _ -> return ()+ withAllTargetsInBuildOrder' pkg lbi $ \target ->+ case targetComponent target of+ CExe exe -> f exe (targetCLBI target)+ _ -> return () -- | Perform the action on each enabled 'Benchmark' in the package -- description.-withBenchLBI :: PackageDescription -> LocalBuildInfo- -> (Benchmark -> ComponentLocalBuildInfo -> IO ()) -> IO ()+withBenchLBI+ :: PackageDescription+ -> LocalBuildInfo+ -> (Benchmark -> ComponentLocalBuildInfo -> IO ())+ -> IO () withBenchLBI pkg lbi f =- sequence_ [ f bench clbi | (bench, clbi) <- enabledBenchLBIs pkg lbi ]+ sequence_ [f bench clbi | (bench, clbi) <- enabledBenchLBIs pkg lbi] -withTestLBI :: PackageDescription -> LocalBuildInfo- -> (TestSuite -> ComponentLocalBuildInfo -> IO ()) -> IO ()+withTestLBI+ :: PackageDescription+ -> LocalBuildInfo+ -> (TestSuite -> ComponentLocalBuildInfo -> IO ())+ -> IO () withTestLBI pkg lbi f =- sequence_ [ f test clbi | (test, clbi) <- enabledTestLBIs pkg lbi ]+ sequence_ [f test clbi | (test, clbi) <- enabledTestLBIs pkg lbi] -enabledTestLBIs :: PackageDescription -> LocalBuildInfo- -> [(TestSuite, ComponentLocalBuildInfo)]+enabledTestLBIs+ :: PackageDescription+ -> LocalBuildInfo+ -> [(TestSuite, ComponentLocalBuildInfo)] enabledTestLBIs pkg lbi =- [ (test, targetCLBI target)- | target <- allTargetsInBuildOrder' pkg lbi- , CTest test <- [targetComponent target] ]+ [ (test, targetCLBI target)+ | target <- allTargetsInBuildOrder' pkg lbi+ , CTest test <- [targetComponent target]+ ] -enabledBenchLBIs :: PackageDescription -> LocalBuildInfo- -> [(Benchmark, ComponentLocalBuildInfo)]+enabledBenchLBIs+ :: PackageDescription+ -> LocalBuildInfo+ -> [(Benchmark, ComponentLocalBuildInfo)] enabledBenchLBIs pkg lbi =- [ (bench, targetCLBI target)- | target <- allTargetsInBuildOrder' pkg lbi- , CBench bench <- [targetComponent target] ]+ [ (bench, targetCLBI target)+ | target <- allTargetsInBuildOrder' pkg lbi+ , CBench bench <- [targetComponent target]+ ] -- | Perform the action on each buildable 'Library' or 'Executable' (Component) -- in the PackageDescription, subject to the build order specified by the -- 'compBuildOrder' field of the given 'LocalBuildInfo'-withAllComponentsInBuildOrder :: PackageDescription -> LocalBuildInfo- -> (Component -> ComponentLocalBuildInfo -> IO ())- -> IO ()+withAllComponentsInBuildOrder+ :: PackageDescription+ -> LocalBuildInfo+ -> (Component -> ComponentLocalBuildInfo -> IO ())+ -> IO () withAllComponentsInBuildOrder pkg lbi f =- withAllTargetsInBuildOrder' pkg lbi $ \target ->- f (targetComponent target) (targetCLBI target)+ withAllTargetsInBuildOrder' pkg lbi $ \target ->+ f (targetComponent target) (targetCLBI target) -allComponentsInBuildOrder :: LocalBuildInfo- -> [ComponentLocalBuildInfo]-allComponentsInBuildOrder lbi =- Graph.topSort (componentGraph lbi)+allComponentsInBuildOrder+ :: LocalBuildInfo+ -> [ComponentLocalBuildInfo]+allComponentsInBuildOrder (LocalBuildInfo{componentGraph = compGraph}) =+ Graph.topSort compGraph -- ----------------------------------------------------------------------------- -- A random function that has no business in this module@@ -186,42 +253,62 @@ -- transitive dependencies of the component we are building. -- -- When wanted, and possible, returns paths relative to the installDirs 'prefix'-depLibraryPaths :: Bool -- ^ Building for inplace?- -> Bool -- ^ Generate prefix-relative library paths- -> LocalBuildInfo- -> ComponentLocalBuildInfo -- ^ Component that is being built- -> IO [FilePath]-depLibraryPaths inplace relative lbi clbi = do- let pkgDescr = localPkgDescr lbi- installDirs = absoluteComponentInstallDirs pkgDescr lbi (componentUnitId clbi) NoCopyDest- executable = case clbi of- ExeComponentLocalBuildInfo {} -> True- _ -> False- relDir | executable = bindir installDirs- | otherwise = libdir installDirs+depLibraryPaths+ :: Bool+ -- ^ Building for inplace?+ -> Bool+ -- ^ Generate prefix-relative library paths+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -- ^ Component that is being built+ -> IO [FilePath]+depLibraryPaths+ inplace+ relative+ lbi@( LocalBuildInfo+ { localPkgDescr = pkgDescr+ , installedPkgs = installed+ }+ )+ clbi = do+ let installDirs = absoluteComponentInstallDirs pkgDescr lbi (componentUnitId clbi) NoCopyDest+ executable = case clbi of+ ExeComponentLocalBuildInfo{} -> True+ _ -> False+ relDir+ | executable = bindir installDirs+ | otherwise = libdir installDirs - let -- TODO: this is kind of inefficient- internalDeps = [ uid- | (uid, _) <- componentPackageDeps clbi- -- Test that it's internal- , sub_target <- allTargetsInBuildOrder' pkgDescr lbi- , componentUnitId (targetCLBI (sub_target)) == uid ]- internalLibs = [ getLibDir (targetCLBI sub_target)- | sub_target <- neededTargetsInBuildOrder'- pkgDescr lbi internalDeps ]- {-- -- This is better, but it doesn't work, because we may be passed a- -- CLBI which doesn't actually exist, and was faked up when we- -- were building a test suite/benchmark. See #3599 for proposal- -- to fix this.- let internalCLBIs = filter ((/= componentUnitId clbi) . componentUnitId)- . map targetCLBI- $ neededTargetsInBuildOrder lbi [componentUnitId clbi]- internalLibs = map getLibDir internalCLBIs- -}- getLibDir sub_clbi- | inplace = componentBuildDir lbi sub_clbi- | otherwise = dynlibdir (absoluteComponentInstallDirs pkgDescr lbi (componentUnitId sub_clbi) NoCopyDest)+ let+ -- TODO: this is kind of inefficient+ internalDeps =+ [ uid+ | (uid, _) <- componentPackageDeps clbi+ , -- Test that it's internal+ sub_target <- allTargetsInBuildOrder' pkgDescr lbi+ , componentUnitId (targetCLBI (sub_target)) == uid+ ]+ internalLibs =+ [ getLibDir (targetCLBI sub_target)+ | sub_target <-+ neededTargetsInBuildOrder'+ pkgDescr+ lbi+ internalDeps+ ]+ {-+ -- This is better, but it doesn't work, because we may be passed a+ -- CLBI which doesn't actually exist, and was faked up when we+ -- were building a test suite/benchmark. See #3599 for proposal+ -- to fix this.+ let internalCLBIs = filter ((/= componentUnitId clbi) . componentUnitId)+ . map targetCLBI+ $ neededTargetsInBuildOrder lbi [componentUnitId clbi]+ internalLibs = map getLibDir internalCLBIs+ -}+ getLibDir sub_clbi+ | inplace = interpretSymbolicPathLBI lbi $ componentBuildDir lbi sub_clbi+ | otherwise = dynlibdir (absoluteComponentInstallDirs pkgDescr lbi (componentUnitId sub_clbi) NoCopyDest) -- Why do we go through all the trouble of a hand-crafting -- internalLibs, when 'installedPkgs' actually contains the@@ -232,48 +319,54 @@ -- is a moot point if you are using a per-component build, -- because you never have any internal libraries in this case; -- they're all external.- let external_ipkgs = filter is_external (allPackages (installedPkgs lbi))+ let external_ipkgs = filter is_external (allPackages installed) is_external ipkg = not (installedUnitId ipkg `elem` internalDeps) -- First look for dynamic libraries in `dynamic-library-dirs`, and use -- `library-dirs` as a fall back.- getDynDir pkg = case Installed.libraryDynDirs pkg of- [] -> Installed.libraryDirs pkg- d -> d- allDepLibDirs = concatMap getDynDir external_ipkgs+ getDynDir pkg = case Installed.libraryDynDirs pkg of+ [] -> Installed.libraryDirs pkg+ d -> d+ allDepLibDirs = concatMap getDynDir external_ipkgs allDepLibDirs' = internalLibs ++ allDepLibDirs allDepLibDirsC <- traverse canonicalizePathNoFail allDepLibDirs' - let p = prefix installDirs+ let p = prefix installDirs prefixRelative l = isJust (stripPrefix p l) libPaths- | relative &&- prefixRelative relDir = map (\l ->- if prefixRelative l- then shortRelativePath relDir l- else l- ) allDepLibDirsC- | otherwise = allDepLibDirsC+ | relative+ && prefixRelative relDir =+ map+ ( \l ->+ if prefixRelative l+ then shortRelativePath relDir l+ else l+ )+ allDepLibDirsC+ | otherwise = allDepLibDirsC - return libPaths- where- -- 'canonicalizePath' fails on UNIX when the directory does not exists.- -- So just don't canonicalize when it doesn't exist.- canonicalizePathNoFail p = do- exists <- doesDirectoryExist p- if exists- then canonicalizePath p- else return p+ -- For some reason, this function returns lots of duplicates. Avoid+ -- exceeding `ARG_MAX` (the result of this function is used to populate+ -- `LD_LIBRARY_PATH`) by deduplicating the list.+ return $ ordNub libPaths+ where+ -- 'canonicalizePath' fails on UNIX when the directory does not exists.+ -- So just don't canonicalize when it doesn't exist.+ canonicalizePathNoFail p = do+ exists <- Directory.doesDirectoryExist p+ if exists+ then Directory.canonicalizePath p+ else return p -- | Get all module names that needed to be built by GHC; i.e., all -- of these 'ModuleName's have interface files associated with them -- that need to be installed. allLibModules :: Library -> ComponentLocalBuildInfo -> [ModuleName] allLibModules lib clbi =- ordNub $- explicitLibModules lib ++- case clbi of- LibComponentLocalBuildInfo { componentInstantiatedWith = insts } -> map fst insts+ ordNub $+ explicitLibModules lib+ ++ case clbi of+ LibComponentLocalBuildInfo{componentInstantiatedWith = insts} -> map fst insts _ -> [] -- -----------------------------------------------------------------------------@@ -283,49 +376,59 @@ -- assuming that @$libname@ points to the public library (or some fake -- package identifier if there is no public library.) IF AT ALL -- POSSIBLE, please use 'absoluteComponentInstallDirs' instead.-absoluteInstallDirs :: PackageDescription -> LocalBuildInfo- -> CopyDest- -> InstallDirs FilePath+absoluteInstallDirs+ :: PackageDescription+ -> LocalBuildInfo+ -> CopyDest+ -> InstallDirs FilePath absoluteInstallDirs pkg lbi copydest =- absoluteComponentInstallDirs pkg lbi (localUnitId lbi) copydest+ absoluteComponentInstallDirs pkg lbi (localUnitId lbi) copydest -- | See 'InstallDirs.absoluteInstallDirs'.-absoluteComponentInstallDirs :: PackageDescription -> LocalBuildInfo- -> UnitId- -> CopyDest- -> InstallDirs FilePath-absoluteComponentInstallDirs pkg lbi uid copydest =- InstallDirs.absoluteInstallDirs- (packageId pkg)- uid- (compilerInfo (compiler lbi))- copydest- (hostPlatform lbi)- (installDirTemplates lbi)+absoluteComponentInstallDirs+ :: PackageDescription+ -> LocalBuildInfo+ -> UnitId+ -> CopyDest+ -> InstallDirs FilePath+absoluteComponentInstallDirs+ pkg+ (LocalBuildInfo{compiler = comp, hostPlatform = plat, installDirTemplates = installDirs})+ uid+ copydest =+ InstallDirs.absoluteInstallDirs+ (packageId pkg)+ uid+ (compilerInfo comp)+ copydest+ plat+ installDirs -absoluteInstallCommandDirs :: PackageDescription -> LocalBuildInfo- -> UnitId- -> CopyDest- -> InstallDirs FilePath+absoluteInstallCommandDirs+ :: PackageDescription+ -> LocalBuildInfo+ -> UnitId+ -> CopyDest+ -> InstallDirs FilePath absoluteInstallCommandDirs pkg lbi uid copydest =- dirs {- -- Handle files which are not- -- per-component (data files and Haddock files.)- datadir = datadir dirs',- -- NB: The situation with Haddock is a bit delicate. On the- -- one hand, the easiest to understand Haddock documentation- -- path is pkgname-0.1, which means it's per-package (not- -- per-component). But this means that it's impossible to- -- install Haddock documentation for internal libraries. We'll- -- keep this constraint for now; this means you can't use- -- Cabal to Haddock internal libraries. This does not seem- -- like a big problem.- docdir = docdir dirs',- htmldir = htmldir dirs',- haddockdir = haddockdir dirs'+ dirs+ { -- Handle files which are not+ -- per-component (data files and Haddock files.)+ datadir = datadir dirs'+ , -- NB: The situation with Haddock is a bit delicate. On the+ -- one hand, the easiest to understand Haddock documentation+ -- path is pkgname-0.1, which means it's per-package (not+ -- per-component). But this means that it's impossible to+ -- install Haddock documentation for internal libraries. We'll+ -- keep this constraint for now; this means you can't use+ -- Cabal to Haddock internal libraries. This does not seem+ -- like a big problem.+ docdir = docdir dirs'+ , htmldir = htmldir dirs'+ , haddockdir = haddockdir dirs' } where- dirs = absoluteComponentInstallDirs pkg lbi uid copydest+ dirs = absoluteComponentInstallDirs pkg lbi uid copydest -- Notice use of 'absoluteInstallDirs' (not the -- per-component variant). This means for non-library -- packages we'll just pick a nondescriptive foo-0.1@@ -335,31 +438,46 @@ -- assuming that @$libname@ points to the public library (or some fake -- package identifier if there is no public library.) IF AT ALL -- POSSIBLE, please use 'prefixRelativeComponentInstallDirs' instead.-prefixRelativeInstallDirs :: PackageId -> LocalBuildInfo- -> InstallDirs (Maybe FilePath)+prefixRelativeInstallDirs+ :: PackageId+ -> LocalBuildInfo+ -> InstallDirs (Maybe FilePath) prefixRelativeInstallDirs pkg_descr lbi =- prefixRelativeComponentInstallDirs pkg_descr lbi (localUnitId lbi)---- |See 'InstallDirs.prefixRelativeInstallDirs'-prefixRelativeComponentInstallDirs :: PackageId -> LocalBuildInfo- -> UnitId- -> InstallDirs (Maybe FilePath)-prefixRelativeComponentInstallDirs pkg_descr lbi uid =- InstallDirs.prefixRelativeInstallDirs- (packageId pkg_descr)- uid- (compilerInfo (compiler lbi))- (hostPlatform lbi)- (installDirTemplates lbi)+ prefixRelativeComponentInstallDirs pkg_descr lbi (localUnitId lbi) -substPathTemplate :: PackageId -> LocalBuildInfo- -> UnitId- -> PathTemplate -> FilePath-substPathTemplate pkgid lbi uid = fromPathTemplate- . ( InstallDirs.substPathTemplate env )- where env = initialPathTemplateEnv- pkgid- uid- (compilerInfo (compiler lbi))- (hostPlatform lbi)+-- | See 'InstallDirs.prefixRelativeInstallDirs'+prefixRelativeComponentInstallDirs+ :: PackageId+ -> LocalBuildInfo+ -> UnitId+ -> InstallDirs (Maybe FilePath)+prefixRelativeComponentInstallDirs+ pkg_descr+ (LocalBuildInfo{compiler = comp, hostPlatform = plat, installDirTemplates = installDirs})+ uid =+ InstallDirs.prefixRelativeInstallDirs+ (packageId pkg_descr)+ uid+ (compilerInfo comp)+ plat+ installDirs +substPathTemplate+ :: PackageId+ -> LocalBuildInfo+ -> UnitId+ -> PathTemplate+ -> FilePath+substPathTemplate+ pkgid+ (LocalBuildInfo{compiler = comp, hostPlatform = plat})+ uid =+ fromPathTemplate+ . (InstallDirs.substPathTemplate env)+ where+ env =+ initialPathTemplateEnv+ pkgid+ uid+ (compilerInfo comp)+ plat
+ src/Distribution/Simple/PackageDescription.hs view
@@ -0,0 +1,136 @@+{-# LANGUAGE DataKinds #-}++-----------------------------------------------------------------------------++-- |+-- Module : Distribution.Simple.PackageDescription+-- Copyright : Isaac Jones 2003-2005+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- This defines parsers for the @.cabal@ format+module Distribution.Simple.PackageDescription+ ( -- * Read and Parse files+ readGenericPackageDescription+ , readHookedBuildInfo++ -- * Utility Parsing function+ , parseString+ ) where++import Distribution.Compat.Prelude+import Prelude ()++import qualified Data.ByteString as BS+import Data.List (groupBy)+import Distribution.Fields.ParseResult+import Distribution.PackageDescription+import Distribution.PackageDescription.Parsec+ ( parseGenericPackageDescription+ , parseHookedBuildInfo+ )+import Distribution.Parsec.Error (showPError)+import Distribution.Parsec.Warning+ ( PWarnType (PWTExperimental)+ , PWarning (..)+ , showPWarning+ )+import Distribution.Simple.Errors+import Distribution.Simple.Utils (dieWithException, equating, warn)+import Distribution.Utils.Path+import Distribution.Verbosity (Verbosity, normal)+import GHC.Stack+import System.Directory (doesFileExist)+import Text.Printf (printf)++readGenericPackageDescription+ :: HasCallStack+ => Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> SymbolicPath Pkg File+ -> IO GenericPackageDescription+readGenericPackageDescription =+ readAndParseFile parseGenericPackageDescription++readHookedBuildInfo+ :: Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ working directory+ -> SymbolicPath Pkg File+ -> IO HookedBuildInfo+readHookedBuildInfo =+ readAndParseFile parseHookedBuildInfo++-- | Helper combinator to do parsing plumbing for files.+--+-- Given a parser and a filename, return the parse of the file,+-- after checking if the file exists.+--+-- Argument order is chosen to encourage partial application.+readAndParseFile+ :: (BS.ByteString -> ParseResult a)+ -- ^ File contents to final value parser+ -> Verbosity+ -- ^ Verbosity level+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ Working directory+ -> SymbolicPath Pkg File+ -- ^ File to read+ -> IO a+readAndParseFile parser verbosity mbWorkDir fpath = do+ let ipath = interpretSymbolicPath mbWorkDir fpath+ upath = getSymbolicPath fpath+ exists <- doesFileExist ipath+ unless exists $+ dieWithException verbosity $+ ErrorParsingFileDoesntExist upath+ bs <- BS.readFile ipath+ parseString parser verbosity upath bs++parseString+ :: (BS.ByteString -> ParseResult a)+ -- ^ File contents to final value parser+ -> Verbosity+ -- ^ Verbosity level+ -> String+ -- ^ File name+ -> BS.ByteString+ -> IO a+parseString parser verbosity name bs = do+ let (warnings, result) = runParseResult (parser bs)+ traverse_ (warn verbosity . showPWarning name) (flattenDups verbosity warnings)+ case result of+ Right x -> return x+ Left (_, errors) -> do+ traverse_ (warn verbosity . showPError name) errors+ dieWithException verbosity $ FailedParsing name++-- | Collapse duplicate experimental feature warnings into single warning, with+-- a count of further sites+flattenDups :: Verbosity -> [PWarning] -> [PWarning]+flattenDups verbosity ws+ | verbosity <= normal = rest ++ experimentals+ | otherwise = ws -- show all instances+ where+ (exps, rest) = partition (\(PWarning w _ _) -> w == PWTExperimental) ws+ experimentals =+ concatMap flatCount+ . groupBy (equating warningStr)+ . sortBy (comparing warningStr)+ $ exps++ warningStr (PWarning _ _ w) = w++ -- flatten if we have 3 or more examples+ flatCount :: [PWarning] -> [PWarning]+ flatCount w@[] = w+ flatCount w@[_] = w+ flatCount w@[_, _] = w+ flatCount (PWarning t pos w : xs) =+ [ PWarning+ t+ pos+ (w <> printf " (and %d more occurrences)" (length xs))+ ]
src/Distribution/Simple/PackageIndex.hs view
@@ -1,9 +1,9 @@-{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.PackageIndex -- Copyright : (c) David Himmelstrup 2005,@@ -43,80 +43,82 @@ -- 'Distribution.Client.PackageIndex', which indexes packages only by -- 'PackageName' (this makes it suitable for indexing source packages, -- for which we don't know 'UnitId's.)----module Distribution.Simple.PackageIndex (- -- * Package index data type- InstalledPackageIndex,- PackageIndex,-- -- * Creating an index- fromList,+module Distribution.Simple.PackageIndex+ ( -- * Package index data type+ InstalledPackageIndex+ , PackageIndex - -- * Updates- merge,+ -- * Creating an index+ , fromList - insert,+ -- * Updates+ , merge+ , insert+ , deleteUnitId+ , deleteSourcePackageId+ , deletePackageName+ -- deleteDependency, - deleteUnitId,- deleteSourcePackageId,- deletePackageName,--- deleteDependency,+ -- * Queries - -- * Queries+ -- ** Precise lookups+ , lookupUnitId+ , lookupComponentId+ , lookupSourcePackageId+ , lookupPackageId+ , lookupPackageName+ , lookupInternalPackageName+ , lookupDependency+ , lookupInternalDependency - -- ** Precise lookups- lookupUnitId,- lookupComponentId,- lookupSourcePackageId,- lookupPackageId,- lookupPackageName,- lookupDependency,- lookupInternalDependency,+ -- ** Case-insensitive searches+ , searchByName+ , SearchResult (..)+ , searchByNameSubstring+ , searchWithPredicate - -- ** Case-insensitive searches- searchByName,- SearchResult(..),- searchByNameSubstring,- searchWithPredicate,+ -- ** Bulk queries+ , allPackages+ , allPackagesByName+ , allPackagesBySourcePackageId+ , allPackagesBySourcePackageIdAndLibName - -- ** Bulk queries- allPackages,- allPackagesByName,- allPackagesBySourcePackageId,- allPackagesBySourcePackageIdAndLibName,+ -- ** Special queries+ , brokenPackages+ , dependencyClosure+ , reverseDependencyClosure+ , topologicalOrder+ , reverseTopologicalOrder+ , dependencyInconsistencies+ , dependencyCycles+ , dependencyGraph+ , moduleNameIndex - -- ** Special queries- brokenPackages,- dependencyClosure,- reverseDependencyClosure,- topologicalOrder,- reverseTopologicalOrder,- dependencyInconsistencies,- dependencyCycles,- dependencyGraph,- moduleNameIndex+ -- ** Filters on lookup results+ , eligibleDependencies+ , matchingDependencies ) where -import Prelude ()-import Distribution.Compat.Prelude hiding (lookup) import qualified Data.Map.Strict as Map+import Distribution.Compat.Prelude hiding (lookup)+import Prelude () -import Distribution.Package import Distribution.Backpack-import Distribution.ModuleName import qualified Distribution.InstalledPackageInfo as IPI-import Distribution.Version+import Distribution.ModuleName+import Distribution.Package import Distribution.Simple.Utils import Distribution.Types.LibraryName+import Distribution.Version import Control.Exception (assert)+import Control.Monad import Data.Array ((!)) import qualified Data.Array as Array import qualified Data.Graph as Graph-import Data.List as List ( groupBy, deleteBy, deleteFirstsBy )+import Data.List as List (deleteBy, deleteFirstsBy, groupBy) import qualified Data.List.NonEmpty as NE-import qualified Data.Tree as Tree-import Control.Monad+import qualified Data.Tree as Tree import Distribution.Compat.Stack import qualified Prelude (foldr1)@@ -126,27 +128,25 @@ -- -- Packages are uniquely identified in by their 'UnitId', they can -- also be efficiently looked up by package name or by name and version.----data PackageIndex a = PackageIndex {- -- The primary index. Each InstalledPackageInfo record is uniquely identified- -- by its UnitId.- --- unitIdIndex :: !(Map UnitId a),-- -- This auxiliary index maps package names (case-sensitively) to all the- -- versions and instances of that package. This allows us to find all- -- versions satisfying a dependency.- --- -- It is a three-level index. The first level is the package name,- -- the second is the package version and the final level is instances- -- of the same package version. These are unique by UnitId- -- and are kept in preference order.- --- -- FIXME: Clarify what "preference order" means. Check that this invariant is- -- preserved. See #1463 for discussion.- packageIdIndex :: !(Map (PackageName, LibraryName) (Map Version [a]))-- } deriving (Eq, Generic, Show, Read, Typeable)+data PackageIndex a = PackageIndex+ { -- The primary index. Each InstalledPackageInfo record is uniquely identified+ -- by its UnitId.+ --+ unitIdIndex :: !(Map UnitId a)+ , -- This auxiliary index maps package names (case-sensitively) to all the+ -- versions and instances of that package. This allows us to find all+ -- versions satisfying a dependency.+ --+ -- It is a three-level index. The first level is the package name,+ -- the second is the package version and the final level is instances+ -- of the same package version. These are unique by UnitId+ -- and are kept in preference order.+ --+ -- FIXME: Clarify what "preference order" means. Check that this invariant is+ -- preserved. See #1463 for discussion.+ packageIdIndex :: !(Map (PackageName, LibraryName) (Map Version [a]))+ }+ deriving (Eq, Generic, Show, Read) instance Binary a => Binary (PackageIndex a) instance Structured a => Structured (PackageIndex a)@@ -156,9 +156,10 @@ type InstalledPackageIndex = PackageIndex IPI.InstalledPackageInfo instance Monoid (PackageIndex IPI.InstalledPackageInfo) where- mempty = PackageIndex Map.empty Map.empty+ mempty = PackageIndex Map.empty Map.empty mappend = (<>)- --save one mappend with empty in the common case:++ -- save one mappend with empty in the common case: mconcat [] = mempty mconcat xs = Prelude.foldr1 mappend xs @@ -170,69 +171,84 @@ invariant (PackageIndex pids pnames) = -- trace (show pids' ++ "\n" ++ show pnames') $ pids' == pnames'- where- pids' = map installedUnitId (Map.elems pids)- pnames' = sort- [ assert pinstOk (installedUnitId pinst)- | ((pname, plib), pvers) <- Map.toList pnames- , let pversOk = not (Map.null pvers)- , (pver, pinsts) <- assert pversOk $ Map.toList pvers- , let pinsts' = sortBy (comparing installedUnitId) pinsts- pinstsOk = all (\g -> length g == 1)- (groupBy (equating installedUnitId) pinsts')- , pinst <- assert pinstsOk $ pinsts'- , let pinstOk = packageName pinst == pname+ where+ pids' = map installedUnitId (Map.elems pids)+ pnames' =+ sort+ [ assert pinstOk (installedUnitId pinst)+ | ((pname, plib), pvers) <- Map.toList pnames+ , let pversOk = not (Map.null pvers)+ , (pver, pinsts) <- assert pversOk $ Map.toList pvers+ , let pinsts' = sortBy (comparing installedUnitId) pinsts+ pinstsOk =+ all+ (\g -> length g == 1)+ (groupBy (equating installedUnitId) pinsts')+ , pinst <- assert pinstsOk $ pinsts'+ , let pinstOk =+ packageName pinst == pname && packageVersion pinst == pver- && IPI.sourceLibName pinst == plib- ]- -- If you see this invariant failing (ie the assert in mkPackageIndex below)- -- then one thing to check is if it is happening in fromList. Check if the- -- second list above (the sort [...] bit) is ending up with duplicates. This- -- has been observed in practice once due to a messed up ghc-pkg db. How/why- -- it became messed up was not discovered.+ && IPI.sourceLibName pinst == plib+ ] +-- If you see this invariant failing (ie the assert in mkPackageIndex below)+-- then one thing to check is if it is happening in fromList. Check if the+-- second list above (the sort [...] bit) is ending up with duplicates. This+-- has been observed in practice once due to a messed up ghc-pkg db. How/why+-- it became messed up was not discovered. --+ -- * Internal helpers+ -- -mkPackageIndex :: WithCallStack (Map UnitId IPI.InstalledPackageInfo- -> Map (PackageName, LibraryName)- (Map Version [IPI.InstalledPackageInfo])- -> InstalledPackageIndex)+mkPackageIndex+ :: WithCallStack+ ( Map UnitId IPI.InstalledPackageInfo+ -> Map+ (PackageName, LibraryName)+ (Map Version [IPI.InstalledPackageInfo])+ -> InstalledPackageIndex+ ) mkPackageIndex pids pnames = assert (invariant index) index- where index = PackageIndex pids pnames-+ where+ index = PackageIndex pids pnames --+ -- * Construction+ -- -- | Build an index out of a bunch of packages. -- -- If there are duplicates by 'UnitId' then later ones mask earlier -- ones.--- fromList :: [IPI.InstalledPackageInfo] -> InstalledPackageIndex fromList pkgs = mkPackageIndex pids ((fmap . fmap) toList pnames) where- pids = Map.fromList [ (installedUnitId pkg, pkg) | pkg <- pkgs ]- pnames =+ pids = Map.fromList [(installedUnitId pkg, pkg) | pkg <- pkgs]+ pnames = Map.fromList [ (liftM2 (,) packageName IPI.sourceLibName (NE.head pkgsN), pvers)- | pkgsN <- NE.groupBy (equating (liftM2 (,) packageName IPI.sourceLibName))- . sortBy (comparing (liftM3 (,,) packageName IPI.sourceLibName packageVersion))- $ pkgs+ | pkgsN <-+ NE.groupBy (equating (liftM2 (,) packageName IPI.sourceLibName))+ . sortBy (comparing (liftM3 (,,) packageName IPI.sourceLibName packageVersion))+ $ pkgs , let pvers = Map.fromList- [ (packageVersion (NE.head pkgsNV),- NE.nubBy (equating installedUnitId) (NE.reverse pkgsNV))- | pkgsNV <- NE.groupBy (equating packageVersion) pkgsN- ]+ [ ( packageVersion (NE.head pkgsNV)+ , NE.nubBy (equating installedUnitId) (NE.reverse pkgsNV)+ )+ | pkgsNV <- NE.groupBy (equating packageVersion) pkgsN+ ] ] --+ -- * Updates+ -- -- | Merge two indexes.@@ -244,101 +260,110 @@ -- \"preferred\" over those from the first. Being preferred means they are top -- result when we do a lookup by source 'PackageId'. This is the mechanism we -- use to prefer user packages over global packages.----merge :: InstalledPackageIndex -> InstalledPackageIndex- -> InstalledPackageIndex+merge+ :: InstalledPackageIndex+ -> InstalledPackageIndex+ -> InstalledPackageIndex merge (PackageIndex pids1 pnames1) (PackageIndex pids2 pnames2) =- mkPackageIndex (Map.unionWith (\_ y -> y) pids1 pids2)- (Map.unionWith (Map.unionWith mergeBuckets) pnames1 pnames2)+ mkPackageIndex+ (Map.unionWith (\_ y -> y) pids1 pids2)+ (Map.unionWith (Map.unionWith mergeBuckets) pnames1 pnames2) where -- Packages in the second list mask those in the first, however preferred -- packages go first in the list. mergeBuckets xs ys = ys ++ (xs \\ ys) (\\) = deleteFirstsBy (equating installedUnitId) - -- | Inserts a single package into the index. -- -- This is equivalent to (but slightly quicker than) using 'mappend' or -- 'merge' with a singleton index.--- insert :: IPI.InstalledPackageInfo -> InstalledPackageIndex -> InstalledPackageIndex insert pkg (PackageIndex pids pnames) =- mkPackageIndex pids' pnames'-+ mkPackageIndex pids' pnames' where- pids' = Map.insert (installedUnitId pkg) pkg pids+ pids' = Map.insert (installedUnitId pkg) pkg pids pnames' = insertPackageName pnames insertPackageName =- Map.insertWith (\_ -> insertPackageVersion)- (packageName pkg, IPI.sourceLibName pkg)- (Map.singleton (packageVersion pkg) [pkg])+ Map.insertWith+ (\_ -> insertPackageVersion)+ (packageName pkg, IPI.sourceLibName pkg)+ (Map.singleton (packageVersion pkg) [pkg]) insertPackageVersion =- Map.insertWith (\_ -> insertPackageInstance)- (packageVersion pkg) [pkg]+ Map.insertWith+ (\_ -> insertPackageInstance)+ (packageVersion pkg)+ [pkg] insertPackageInstance pkgs = pkg : deleteBy (equating installedUnitId) pkg pkgs - -- | Removes a single installed package from the index.----deleteUnitId :: UnitId -> InstalledPackageIndex- -> InstalledPackageIndex+deleteUnitId+ :: UnitId+ -> InstalledPackageIndex+ -> InstalledPackageIndex deleteUnitId ipkgid original@(PackageIndex pids pnames) = case Map.updateLookupWithKey (\_ _ -> Nothing) ipkgid pids of- (Nothing, _) -> original- (Just spkgid, pids') -> mkPackageIndex pids'- (deletePkgName spkgid pnames)-+ (Nothing, _) -> original+ (Just spkgid, pids') ->+ mkPackageIndex+ pids'+ (deletePkgName spkgid pnames) where deletePkgName spkgid = Map.update (deletePkgVersion spkgid) (packageName spkgid, IPI.sourceLibName spkgid) deletePkgVersion spkgid =- (\m -> if Map.null m then Nothing else Just m)- . Map.update deletePkgInstance (packageVersion spkgid)+ (\m -> if Map.null m then Nothing else Just m)+ . Map.update deletePkgInstance (packageVersion spkgid) deletePkgInstance =- (\xs -> if null xs then Nothing else Just xs)- . List.deleteBy (\_ pkg -> installedUnitId pkg == ipkgid) undefined+ (\xs -> if null xs then Nothing else Just xs)+ . List.deleteBy (\_ pkg -> installedUnitId pkg == ipkgid) undefined -- | Removes all packages with this source 'PackageId' from the index.----deleteSourcePackageId :: PackageId -> InstalledPackageIndex- -> InstalledPackageIndex+deleteSourcePackageId+ :: PackageId+ -> InstalledPackageIndex+ -> InstalledPackageIndex deleteSourcePackageId pkgid original@(PackageIndex pids pnames) = -- NB: Doesn't delete internal packages case Map.lookup (packageName pkgid, LMainLibName) pnames of- Nothing -> original- Just pvers -> case Map.lookup (packageVersion pkgid) pvers of- Nothing -> original- Just pkgs -> mkPackageIndex- (foldl' (flip (Map.delete . installedUnitId)) pids pkgs)- (deletePkgName pnames)+ Nothing -> original+ Just pvers -> case Map.lookup (packageVersion pkgid) pvers of+ Nothing -> original+ Just pkgs ->+ mkPackageIndex+ (foldl' (flip (Map.delete . installedUnitId)) pids pkgs)+ (deletePkgName pnames) where deletePkgName = Map.update deletePkgVersion (packageName pkgid, LMainLibName) deletePkgVersion =- (\m -> if Map.null m then Nothing else Just m)- . Map.delete (packageVersion pkgid)-+ (\m -> if Map.null m then Nothing else Just m)+ . Map.delete (packageVersion pkgid) -- | Removes all packages with this (case-sensitive) name from the index. -- -- NB: Does NOT delete internal libraries from this package.----deletePackageName :: PackageName -> InstalledPackageIndex- -> InstalledPackageIndex+deletePackageName+ :: PackageName+ -> InstalledPackageIndex+ -> InstalledPackageIndex deletePackageName name original@(PackageIndex pids pnames) = case Map.lookup (name, LMainLibName) pnames of- Nothing -> original- Just pvers -> mkPackageIndex- (foldl' (flip (Map.delete . installedUnitId)) pids- (concat (Map.elems pvers)))- (Map.delete (name, LMainLibName) pnames)+ Nothing -> original+ Just pvers ->+ mkPackageIndex+ ( foldl'+ (flip (Map.delete . installedUnitId))+ pids+ (concat (Map.elems pvers))+ )+ (Map.delete (name, LMainLibName) pnames) {- -- | Removes all packages satisfying this dependency from the index.@@ -349,11 +374,12 @@ -} --+ -- * Bulk queries+ -- -- | Get all the packages from the index.--- allPackages :: PackageIndex a -> [a] allPackages = Map.elems . unitIdIndex @@ -362,91 +388,110 @@ -- They are grouped by package name (case-sensitively). -- -- (Doesn't include private libraries.)--- allPackagesByName :: PackageIndex a -> [(PackageName, [a])] allPackagesByName index = [ (pkgname, concat (Map.elems pvers))- | ((pkgname, LMainLibName), pvers) <- Map.toList (packageIdIndex index) ]+ | ((pkgname, LMainLibName), pvers) <- Map.toList (packageIdIndex index)+ ] -- | Get all the packages from the index. -- -- They are grouped by source package id (package name and version). -- -- (Doesn't include private libraries)----allPackagesBySourcePackageId :: HasUnitId a => PackageIndex a- -> [(PackageId, [a])]+allPackagesBySourcePackageId+ :: HasUnitId a+ => PackageIndex a+ -> [(PackageId, [a])] allPackagesBySourcePackageId index = [ (packageId ipkg, ipkgs) | ((_, LMainLibName), pvers) <- Map.toList (packageIdIndex index)- , ipkgs@(ipkg:_) <- Map.elems pvers ]+ , ipkgs@(ipkg : _) <- Map.elems pvers+ ] -- | Get all the packages from the index. -- -- They are grouped by source package id and library name. -- -- This DOES include internal libraries.-allPackagesBySourcePackageIdAndLibName :: HasUnitId a => PackageIndex a- -> [((PackageId, LibraryName), [a])]+allPackagesBySourcePackageIdAndLibName+ :: HasUnitId a+ => PackageIndex a+ -> [((PackageId, LibraryName), [a])] allPackagesBySourcePackageIdAndLibName index = [ ((packageId ipkg, ln), ipkgs) | ((_, ln), pvers) <- Map.toList (packageIdIndex index)- , ipkgs@(ipkg:_) <- Map.elems pvers ]+ , ipkgs@(ipkg : _) <- Map.elems pvers+ ] --+ -- * Lookups+ -- -- | Does a lookup by unit identifier. -- -- Since multiple package DBs mask each other by 'UnitId', -- then we get back at most one package.----lookupUnitId :: PackageIndex a -> UnitId- -> Maybe a+lookupUnitId+ :: PackageIndex a+ -> UnitId+ -> Maybe a lookupUnitId index uid = Map.lookup uid (unitIdIndex index) -- | Does a lookup by component identifier. In the absence -- of Backpack, this is just a 'lookupUnitId'.----lookupComponentId :: PackageIndex a -> ComponentId- -> Maybe a+lookupComponentId+ :: PackageIndex a+ -> ComponentId+ -> Maybe a lookupComponentId index cid =- Map.lookup (newSimpleUnitId cid) (unitIdIndex index)+ Map.lookup (newSimpleUnitId cid) (unitIdIndex index) -- | Does a lookup by source package id (name & version). -- -- There can be multiple installed packages with the same source 'PackageId' -- but different 'UnitId'. They are returned in order of -- preference, with the most preferred first.--- lookupSourcePackageId :: PackageIndex a -> PackageId -> [a] lookupSourcePackageId index pkgid = -- Do not lookup internal libraries case Map.lookup (packageName pkgid, LMainLibName) (packageIdIndex index) of- Nothing -> []- Just pvers -> case Map.lookup (packageVersion pkgid) pvers of- Nothing -> []+ Nothing -> []+ Just pvers -> case Map.lookup (packageVersion pkgid) pvers of+ Nothing -> [] Just pkgs -> pkgs -- in preference order -- | Convenient alias of 'lookupSourcePackageId', but assuming only -- one package per package ID. lookupPackageId :: PackageIndex a -> PackageId -> Maybe a-lookupPackageId index pkgid = case lookupSourcePackageId index pkgid of- [] -> Nothing- [pkg] -> Just pkg- _ -> error "Distribution.Simple.PackageIndex: multiple matches found"+lookupPackageId index pkgid = case lookupSourcePackageId index pkgid of+ [] -> Nothing+ [pkg] -> Just pkg+ _ -> error "Distribution.Simple.PackageIndex: multiple matches found" -- | Does a lookup by source package name.----lookupPackageName :: PackageIndex a -> PackageName- -> [(Version, [a])]+lookupPackageName+ :: PackageIndex a+ -> PackageName+ -> [(Version, [a])] lookupPackageName index name = -- Do not match internal libraries- case Map.lookup (name, LMainLibName) (packageIdIndex index) of- Nothing -> []- Just pvers -> Map.toList pvers+ lookupInternalPackageName index name LMainLibName +-- | Does a lookup by source package name and library name.+--+-- Also looks up internal packages.+lookupInternalPackageName+ :: PackageIndex a+ -> PackageName+ -> LibraryName+ -> [(Version, [a])]+lookupInternalPackageName index name library =+ case Map.lookup (name, library) (packageIdIndex index) of+ Nothing -> []+ Just pvers -> Map.toList pvers -- | Does a lookup by source package name and a range of versions. --@@ -457,12 +502,14 @@ -- function on those; use 'lookupInternalDependency' instead. -- -- INVARIANT: List of eligible 'IPI.InstalledPackageInfo' is non-empty.----lookupDependency :: InstalledPackageIndex -> PackageName -> VersionRange- -> [(Version, [IPI.InstalledPackageInfo])]+lookupDependency+ :: InstalledPackageIndex+ -> PackageName+ -> VersionRange+ -> [(Version, [IPI.InstalledPackageInfo])] lookupDependency index pn vr =- -- Yes, a little bit of a misnomer here!- lookupInternalDependency index pn vr LMainLibName+ -- Yes, a little bit of a misnomer here!+ lookupInternalDependency index pn vr LMainLibName -- | Does a lookup by source package name and a range of versions. --@@ -470,30 +517,57 @@ -- satisfying the version range constraint. -- -- INVARIANT: List of eligible 'IPI.InstalledPackageInfo' is non-empty.----lookupInternalDependency :: InstalledPackageIndex -> PackageName -> VersionRange- -> LibraryName- -> [(Version, [IPI.InstalledPackageInfo])]+lookupInternalDependency+ :: InstalledPackageIndex+ -> PackageName+ -> VersionRange+ -> LibraryName+ -> [(Version, [IPI.InstalledPackageInfo])] lookupInternalDependency index name versionRange libn =- case Map.lookup (name, libn) (packageIdIndex index) of- Nothing -> []- Just pvers -> [ (ver, pkgs')- | (ver, pkgs) <- Map.toList pvers- , ver `withinRange` versionRange- , let pkgs' = filter eligible pkgs- -- Enforce the invariant- , not (null pkgs')- ]- where- -- When we select for dependencies, we ONLY want to pick up indefinite- -- packages, or packages with no instantiations. We'll do mix-in- -- linking to improve any such package into an instantiated one- -- later.- eligible pkg = IPI.indefinite pkg || null (IPI.instantiatedWith pkg)+ matchingDependencies versionRange $+ lookupInternalPackageName index name libn +-- | Filter a set of installed packages to ones eligible as dependencies.+--+-- When we select for dependencies, we ONLY want to pick up indefinite+-- packages, or packages with no instantiations. We'll do mix-in linking to+-- improve any such package into an instantiated one later.+--+-- INVARIANT: List of eligible 'IPI.InstalledPackageInfo' is non-empty.+eligibleDependencies+ :: [(Version, [IPI.InstalledPackageInfo])]+ -> [(Version, [IPI.InstalledPackageInfo])]+eligibleDependencies versions =+ [ (ver, pkgs')+ | (ver, pkgs) <- versions+ , let pkgs' = filter eligible pkgs+ , -- Enforce the invariant+ not (null pkgs')+ ]+ where+ eligible pkg = IPI.indefinite pkg || null (IPI.instantiatedWith pkg) +-- | Get eligible dependencies from a list of versions. --+-- This can be used to filter the output of 'lookupPackageName' or+-- 'lookupInternalPackageName'.+--+-- INVARIANT: List of eligible 'IPI.InstalledPackageInfo' is non-empty.+matchingDependencies+ :: VersionRange+ -> [(Version, [IPI.InstalledPackageInfo])]+ -> [(Version, [IPI.InstalledPackageInfo])]+matchingDependencies versionRange versions =+ let eligibleVersions = eligibleDependencies versions+ in [ (ver, pkgs)+ | (ver, pkgs) <- eligibleVersions+ , ver `withinRange` versionRange+ ]++--+ -- * Case insensitive name lookups+ -- -- | Does a case-insensitive search by package name.@@ -507,42 +581,45 @@ -- have an ambiguous result, and we get back all the versions of all the -- packages. The list of ambiguous results is split by exact package name. So -- it is a non-empty list of non-empty lists.--- searchByName :: PackageIndex a -> String -> SearchResult [a] searchByName index name = -- Don't match internal packages- case [ pkgs | pkgs@((pname, LMainLibName),_) <- Map.toList (packageIdIndex index)- , lowercase (unPackageName pname) == lname ] of- [] -> None- [(_,pvers)] -> Unambiguous (concat (Map.elems pvers))- pkgss -> case find ((mkPackageName name ==) . fst . fst) pkgss of- Just (_,pvers) -> Unambiguous (concat (Map.elems pvers))- Nothing -> Ambiguous (map (concat . Map.elems . snd) pkgss)- where lname = lowercase name+ case [ pkgs | pkgs@((pname, LMainLibName), _) <- Map.toList (packageIdIndex index), lowercase (unPackageName pname) == lname+ ] of+ [] -> None+ [(_, pvers)] -> Unambiguous (concat (Map.elems pvers))+ pkgss -> case find ((mkPackageName name ==) . fst . fst) pkgss of+ Just (_, pvers) -> Unambiguous (concat (Map.elems pvers))+ Nothing -> Ambiguous (map (concat . Map.elems . snd) pkgss)+ where+ lname = lowercase name data SearchResult a = None | Unambiguous a | Ambiguous [a] -- | Does a case-insensitive substring search by package name. -- -- That is, all packages that contain the given string in their name.--- searchByNameSubstring :: PackageIndex a -> String -> [a] searchByNameSubstring index searchterm = searchWithPredicate index (\n -> lsearchterm `isInfixOf` lowercase n)- where lsearchterm = lowercase searchterm+ where+ lsearchterm = lowercase searchterm -- | @since 3.4.0.0 searchWithPredicate :: PackageIndex a -> (String -> Bool) -> [a] searchWithPredicate index predicate = [ pkg- -- Don't match internal packages- | ((pname, LMainLibName), pvers) <- Map.toList (packageIdIndex index)+ | -- Don't match internal packages+ ((pname, LMainLibName), pvers) <- Map.toList (packageIdIndex index) , predicate (unPackageName pname) , pkgs <- Map.elems pvers- , pkg <- pkgs ]+ , pkg <- pkgs+ ] --+ -- * Special queries+ -- -- None of the stuff below depends on the internal representation of the index.@@ -554,27 +631,30 @@ -- This actually computes the strongly connected components. So it gives us a -- list of groups of packages where within each group they all depend on each -- other, directly or indirectly.--- dependencyCycles :: PackageInstalled a => PackageIndex a -> [[a]] dependencyCycles index =- [ vs | Graph.CyclicSCC vs <- Graph.stronglyConnComp adjacencyList ]+ [vs | Graph.CyclicSCC vs <- Graph.stronglyConnComp adjacencyList] where- adjacencyList = [ (pkg, installedUnitId pkg, installedDepends pkg)- | pkg <- allPackages index ]-+ adjacencyList =+ [ (pkg, installedUnitId pkg, installedDepends pkg)+ | pkg <- allPackages index+ ] -- | All packages that have immediate dependencies that are not in the index. -- -- Returns such packages along with the dependencies that they're missing.----brokenPackages :: PackageInstalled a => PackageIndex a- -> [(a, [UnitId])]+brokenPackages+ :: PackageInstalled a+ => PackageIndex a+ -> [(a, [UnitId])] brokenPackages index = [ (pkg, missing)- | pkg <- allPackages index- , let missing = [ pkg' | pkg' <- installedDepends pkg- , isNothing (lookupUnitId index pkg') ]- , not (null missing) ]+ | pkg <- allPackages index+ , let missing =+ [ pkg' | pkg' <- installedDepends pkg, isNothing (lookupUnitId index pkg')+ ]+ , not (null missing)+ ] -- | Tries to take the transitive closure of the package dependencies. --@@ -583,78 +663,89 @@ -- -- * Note that if the result is @Right []@ it is because at least one of -- the original given 'PackageId's do not occur in the index.----dependencyClosure :: InstalledPackageIndex- -> [UnitId]- -> Either (InstalledPackageIndex)- [(IPI.InstalledPackageInfo, [UnitId])]+dependencyClosure+ :: InstalledPackageIndex+ -> [UnitId]+ -> Either+ (InstalledPackageIndex)+ [(IPI.InstalledPackageInfo, [UnitId])] dependencyClosure index pkgids0 = case closure mempty [] pkgids0 of (completed, []) -> Left completed- (completed, _) -> Right (brokenPackages completed)- where- closure completed failed [] = (completed, failed)- closure completed failed (pkgid:pkgids) = case lookupUnitId index pkgid of- Nothing -> closure completed (pkgid:failed) pkgids- Just pkg -> case lookupUnitId completed (installedUnitId pkg) of- Just _ -> closure completed failed pkgids+ (completed, _) -> Right (brokenPackages completed)+ where+ closure completed failed [] = (completed, failed)+ closure completed failed (pkgid : pkgids) = case lookupUnitId index pkgid of+ Nothing -> closure completed (pkgid : failed) pkgids+ Just pkg -> case lookupUnitId completed (installedUnitId pkg) of+ Just _ -> closure completed failed pkgids Nothing -> closure completed' failed pkgids'- where completed' = insert pkg completed- pkgids' = installedDepends pkg ++ pkgids+ where+ completed' = insert pkg completed+ pkgids' = installedDepends pkg ++ pkgids -- | Takes the transitive closure of the packages reverse dependencies. -- -- * The given 'PackageId's must be in the index.----reverseDependencyClosure :: PackageInstalled a => PackageIndex a- -> [UnitId]- -> [a]+reverseDependencyClosure+ :: PackageInstalled a+ => PackageIndex a+ -> [UnitId]+ -> [a] reverseDependencyClosure index =- map vertexToPkg- . concatMap Tree.flatten- . Graph.dfs reverseDepGraph- . map (fromMaybe noSuchPkgId . pkgIdToVertex)-+ map vertexToPkg+ . concatMap Tree.flatten+ . Graph.dfs reverseDepGraph+ . map (fromMaybe noSuchPkgId . pkgIdToVertex) where (depGraph, vertexToPkg, pkgIdToVertex) = dependencyGraph index reverseDepGraph = Graph.transposeG depGraph noSuchPkgId = error "reverseDependencyClosure: package is not in the graph" topologicalOrder :: PackageInstalled a => PackageIndex a -> [a]-topologicalOrder index = map toPkgId- . Graph.topSort- $ graph- where (graph, toPkgId, _) = dependencyGraph index+topologicalOrder index =+ map toPkgId+ . Graph.topSort+ $ graph+ where+ (graph, toPkgId, _) = dependencyGraph index reverseTopologicalOrder :: PackageInstalled a => PackageIndex a -> [a]-reverseTopologicalOrder index = map toPkgId- . Graph.topSort- . Graph.transposeG- $ graph- where (graph, toPkgId, _) = dependencyGraph index+reverseTopologicalOrder index =+ map toPkgId+ . Graph.topSort+ . Graph.transposeG+ $ graph+ where+ (graph, toPkgId, _) = dependencyGraph index -- | Builds a graph of the package dependencies. -- -- Dependencies on other packages that are not in the index are discarded. -- You can check if there are any such dependencies with 'brokenPackages'.----dependencyGraph :: PackageInstalled a => PackageIndex a- -> (Graph.Graph,- Graph.Vertex -> a,- UnitId -> Maybe Graph.Vertex)+dependencyGraph+ :: PackageInstalled a+ => PackageIndex a+ -> ( Graph.Graph+ , Graph.Vertex -> a+ , UnitId -> Maybe Graph.Vertex+ ) dependencyGraph index = (graph, vertex_to_pkg, id_to_vertex) where- graph = Array.listArray bounds- [ [ v | Just v <- map id_to_vertex (installedDepends pkg) ]- | pkg <- pkgs ]+ graph =+ Array.listArray+ bounds+ [ [v | Just v <- map id_to_vertex (installedDepends pkg)]+ | pkg <- pkgs+ ] - pkgs = sortBy (comparing packageId) (allPackages index)- vertices = zip (map installedUnitId pkgs) [0..]- vertex_map = Map.fromList vertices+ pkgs = sortBy (comparing packageId) (allPackages index)+ vertices = zip (map installedUnitId pkgs) [0 ..]+ vertex_map = Map.fromList vertices id_to_vertex pid = Map.lookup pid vertex_map vertex_to_pkg vertex = pkgTable ! vertex - pkgTable = Array.listArray bounds pkgs+ pkgTable = Array.listArray bounds pkgs topBound = length pkgs - 1 bounds = (0, topBound) @@ -671,29 +762,36 @@ -- Each element in the result is a package name along with the packages that -- depend on it and the versions they require. These are guaranteed to be -- distinct.----dependencyInconsistencies :: InstalledPackageIndex- -- At DepUniqueKey...- -> [(DepUniqueKey,- -- There were multiple packages (BAD!)- [(UnitId,- -- And here are the packages which- -- immediately depended on it- [IPI.InstalledPackageInfo])])]+dependencyInconsistencies+ :: InstalledPackageIndex+ -- At DepUniqueKey...+ -> [ ( DepUniqueKey+ , -- There were multiple packages (BAD!)+ [ ( UnitId+ , -- And here are the packages which+ -- immediately depended on it+ [IPI.InstalledPackageInfo]+ )+ ]+ )+ ] dependencyInconsistencies index = do- (dep_key, insts_map) <- Map.toList inverseIndex- let insts = Map.toList insts_map- guard (length insts >= 2)- return (dep_key, insts)+ (dep_key, insts_map) <- Map.toList inverseIndex+ let insts = Map.toList insts_map+ guard (length insts >= 2)+ return (dep_key, insts) where inverseIndex :: Map DepUniqueKey (Map UnitId [IPI.InstalledPackageInfo]) inverseIndex = Map.fromListWith (Map.unionWith (++)) $ do- pkg <- allPackages index- dep_ipid <- installedDepends pkg- Just dep <- [lookupUnitId index dep_ipid]- let dep_key = (packageName dep, IPI.sourceLibName dep,- Map.fromList (IPI.instantiatedWith dep))- return (dep_key, Map.singleton dep_ipid [pkg])+ pkg <- allPackages index+ dep_ipid <- installedDepends pkg+ Just dep <- [lookupUnitId index dep_ipid]+ let dep_key =+ ( packageName dep+ , IPI.sourceLibName dep+ , Map.fromList (IPI.instantiatedWith dep)+ )+ return (dep_key, Map.singleton dep_ipid [pkg]) -- | A rough approximation of GHC's module finder, takes a -- 'InstalledPackageIndex' and turns it into a map from module names to their@@ -705,12 +803,14 @@ pkg <- allPackages index IPI.ExposedModule m reexport <- IPI.exposedModules pkg case reexport of- Nothing -> return (m, [pkg])- Just (OpenModuleVar _) -> []- Just (OpenModule _ m') | m == m' -> []- | otherwise -> return (m', [pkg])- -- The heuristic is this: we want to prefer the original package- -- which originally exported a module. However, if a reexport- -- also *renamed* the module (m /= m'), then we have to use the- -- downstream package, since the upstream package has the wrong- -- module name!+ Nothing -> return (m, [pkg])+ Just (OpenModuleVar _) -> []+ Just (OpenModule _ m')+ | m == m' -> []+ | otherwise -> return (m', [pkg])++-- The heuristic is this: we want to prefer the original package+-- which originally exported a module. However, if a reexport+-- also *renamed* the module (m /= m'), then we have to use the+-- downstream package, since the upstream package has the wrong+-- module name!
src/Distribution/Simple/PreProcess.hs view
@@ -1,779 +1,932 @@-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}---------------------------------------------------------------------------------- |--- Module : Distribution.Simple.PreProcess--- Copyright : (c) 2003-2005, Isaac Jones, Malcolm Wallace--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ This defines a 'PreProcessor' abstraction which represents a pre-processor--- that can transform one kind of file into another. There is also a--- 'PPSuffixHandler' which is a combination of a file extension and a function--- for configuring a 'PreProcessor'. It defines a bunch of known built-in--- preprocessors like @cpp@, @cpphs@, @c2hs@, @hsc2hs@, @happy@, @alex@ etc and--- lists them in 'knownSuffixHandlers'. On top of this it provides a function--- for actually preprocessing some sources given a bunch of known suffix--- handlers. This module is not as good as it could be, it could really do with--- a rewrite to address some of the problems we have with pre-processors.--module Distribution.Simple.PreProcess (preprocessComponent, preprocessExtras,- knownSuffixHandlers, ppSuffixes,- PPSuffixHandler, PreProcessor(..),- mkSimplePreProcessor, runSimplePreProcessor,- ppCpp, ppCpp', ppGreenCard, ppC2hs, ppHsc2hs,- ppHappy, ppAlex, ppUnlit, platformDefines- )- where--import Prelude ()-import Distribution.Compat.Prelude-import Distribution.Compat.Stack--import Distribution.Simple.PreProcess.Unlit-import Distribution.Backpack.DescribeUnitId-import Distribution.Package-import qualified Distribution.ModuleName as ModuleName-import Distribution.ModuleName (ModuleName)-import Distribution.PackageDescription as PD-import qualified Distribution.InstalledPackageInfo as Installed-import qualified Distribution.Simple.PackageIndex as PackageIndex-import Distribution.Simple.CCompiler-import Distribution.Simple.Compiler-import Distribution.Simple.LocalBuildInfo-import Distribution.Simple.BuildPaths-import Distribution.Simple.Utils-import Distribution.Simple.Program-import Distribution.Simple.Program.ResponseFile-import Distribution.Simple.Test.LibV09-import Distribution.System-import Distribution.Pretty-import Distribution.Version-import Distribution.Verbosity-import Distribution.Utils.Path--import System.Directory (doesFileExist)-import System.Info (os, arch)-import System.FilePath (splitExtension, dropExtensions, (</>), (<.>),- takeDirectory, normalise, replaceExtension,- takeExtensions)---- |The interface to a preprocessor, which may be implemented using an--- external program, but need not be. The arguments are the name of--- the input file, the name of the output file and a verbosity level.--- Here is a simple example that merely prepends a comment to the given--- source file:------ > ppTestHandler :: PreProcessor--- > ppTestHandler =--- > PreProcessor {--- > platformIndependent = True,--- > runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity ->--- > do info verbosity (inFile++" has been preprocessed to "++outFile)--- > stuff <- readFile inFile--- > writeFile outFile ("-- preprocessed as a test\n\n" ++ stuff)--- > return ExitSuccess------ We split the input and output file names into a base directory and the--- rest of the file name. The input base dir is the path in the list of search--- dirs that this file was found in. The output base dir is the build dir where--- all the generated source files are put.------ The reason for splitting it up this way is that some pre-processors don't--- simply generate one output .hs file from one input file but have--- dependencies on other generated files (notably c2hs, where building one--- .hs file may require reading other .chi files, and then compiling the .hs--- file may require reading a generated .h file). In these cases the generated--- files need to embed relative path names to each other (eg the generated .hs--- file mentions the .h file in the FFI imports). This path must be relative to--- the base directory where the generated files are located, it cannot be--- relative to the top level of the build tree because the compilers do not--- look for .h files relative to there, ie we do not use \"-I .\", instead we--- use \"-I dist\/build\" (or whatever dist dir has been set by the user)------ Most pre-processors do not care of course, so mkSimplePreProcessor and--- runSimplePreProcessor functions handle the simple case.----data PreProcessor = PreProcessor {-- -- Is the output of the pre-processor platform independent? eg happy output- -- is portable haskell but c2hs's output is platform dependent.- -- This matters since only platform independent generated code can be- -- included into a source tarball.- platformIndependent :: Bool,-- -- TODO: deal with pre-processors that have implementation dependent output- -- eg alex and happy have --ghc flags. However we can't really include- -- ghc-specific code into supposedly portable source tarballs.-- runPreProcessor :: (FilePath, FilePath) -- Location of the source file relative to a base dir- -> (FilePath, FilePath) -- Output file name, relative to an output base dir- -> Verbosity -- verbosity- -> IO () -- Should exit if the preprocessor fails- }---- | Function to determine paths to possible extra C sources for a--- preprocessor: just takes the path to the build directory and uses--- this to search for C sources with names that match the--- preprocessor's output name format.-type PreProcessorExtras = FilePath -> IO [FilePath]---mkSimplePreProcessor :: (FilePath -> FilePath -> Verbosity -> IO ())- -> (FilePath, FilePath)- -> (FilePath, FilePath) -> Verbosity -> IO ()-mkSimplePreProcessor simplePP- (inBaseDir, inRelativeFile)- (outBaseDir, outRelativeFile) verbosity = simplePP inFile outFile verbosity- where inFile = normalise (inBaseDir </> inRelativeFile)- outFile = normalise (outBaseDir </> outRelativeFile)--runSimplePreProcessor :: PreProcessor -> FilePath -> FilePath -> Verbosity- -> IO ()-runSimplePreProcessor pp inFile outFile verbosity =- runPreProcessor pp (".", inFile) (".", outFile) verbosity---- |A preprocessor for turning non-Haskell files with the given extension--- into plain Haskell source files.-type PPSuffixHandler- = (String, BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor)---- | Apply preprocessors to the sources from 'hsSourceDirs' for a given--- component (lib, exe, or test suite).------ XXX: This is terrible-preprocessComponent :: PackageDescription- -> Component- -> LocalBuildInfo- -> ComponentLocalBuildInfo- -> Bool- -> Verbosity- -> [PPSuffixHandler]- -> IO ()-preprocessComponent pd comp lbi clbi isSrcDist verbosity handlers = do- -- NB: never report instantiation here; we'll report it properly when- -- building.- setupMessage' verbosity "Preprocessing" (packageId pd)- (componentLocalName clbi) (Nothing :: Maybe [(ModuleName, Module)])- case comp of- (CLib lib@Library{ libBuildInfo = bi }) -> do- let dirs = map getSymbolicPath (hsSourceDirs bi) ++- [ autogenComponentModulesDir lbi clbi ,autogenPackageModulesDir lbi]- for_ (map ModuleName.toFilePath $ allLibModules lib clbi) $- pre dirs (componentBuildDir lbi clbi) (localHandlers bi)- (CFLib flib@ForeignLib { foreignLibBuildInfo = bi, foreignLibName = nm }) -> do- let nm' = unUnqualComponentName nm- let flibDir = buildDir lbi </> nm' </> nm' ++ "-tmp"- dirs = map getSymbolicPath (hsSourceDirs bi) ++ [autogenComponentModulesDir lbi clbi- ,autogenPackageModulesDir lbi]- for_ (map ModuleName.toFilePath $ foreignLibModules flib) $- pre dirs flibDir (localHandlers bi)- (CExe exe@Executable { buildInfo = bi, exeName = nm }) -> do- let nm' = unUnqualComponentName nm- let exeDir = buildDir lbi </> nm' </> nm' ++ "-tmp"- dirs = map getSymbolicPath (hsSourceDirs bi) ++ [autogenComponentModulesDir lbi clbi- ,autogenPackageModulesDir lbi]- for_ (map ModuleName.toFilePath $ otherModules bi) $- pre dirs exeDir (localHandlers bi)- pre (map getSymbolicPath (hsSourceDirs bi)) exeDir (localHandlers bi) $- dropExtensions (modulePath exe)- CTest test@TestSuite{ testName = nm } -> do- let nm' = unUnqualComponentName nm- case testInterface test of- TestSuiteExeV10 _ f ->- preProcessTest test f $ buildDir lbi </> nm' </> nm' ++ "-tmp"- TestSuiteLibV09 _ _ -> do- let testDir = buildDir lbi </> stubName test- </> stubName test ++ "-tmp"- writeSimpleTestStub test testDir- preProcessTest test (stubFilePath test) testDir- TestSuiteUnsupported tt ->- die' verbosity $ "No support for preprocessing test "- ++ "suite type " ++ prettyShow tt- CBench bm@Benchmark{ benchmarkName = nm } -> do- let nm' = unUnqualComponentName nm- case benchmarkInterface bm of- BenchmarkExeV10 _ f ->- preProcessBench bm f $ buildDir lbi </> nm' </> nm' ++ "-tmp"- BenchmarkUnsupported tt ->- die' verbosity $ "No support for preprocessing benchmark "- ++ "type " ++ prettyShow tt- where- builtinHaskellSuffixes = ["hs", "lhs", "hsig", "lhsig"]- builtinCSuffixes = cSourceExtensions- builtinSuffixes = builtinHaskellSuffixes ++ builtinCSuffixes- localHandlers bi = [(ext, h bi lbi clbi) | (ext, h) <- handlers]- pre dirs dir lhndlrs fp =- preprocessFile (map unsafeMakeSymbolicPath dirs) dir isSrcDist fp verbosity builtinSuffixes lhndlrs- preProcessTest test = preProcessComponent (testBuildInfo test)- (testModules test)- preProcessBench bm = preProcessComponent (benchmarkBuildInfo bm)- (benchmarkModules bm)-- preProcessComponent- :: BuildInfo- -> [ModuleName]- -> FilePath- -> FilePath- -> IO ()- preProcessComponent bi modules exePath dir = do- let biHandlers = localHandlers bi- sourceDirs = map getSymbolicPath (hsSourceDirs bi) ++ [ autogenComponentModulesDir lbi clbi- , autogenPackageModulesDir lbi ]- sequence_ [ preprocessFile (map unsafeMakeSymbolicPath sourceDirs) dir isSrcDist- (ModuleName.toFilePath modu) verbosity builtinSuffixes- biHandlers- | modu <- modules ]-- -- XXX: what we do here (re SymbolicPath dir)- -- XXX: 2020-10-15 do we rely here on CWD being the PackageDir?- preprocessFile (unsafeMakeSymbolicPath dir : hsSourceDirs bi) dir isSrcDist- (dropExtensions $ exePath) verbosity- builtinSuffixes biHandlers----TODO: try to list all the modules that could not be found--- not just the first one. It's annoying and slow due to the need--- to reconfigure after editing the .cabal file each time.---- |Find the first extension of the file that exists, and preprocess it--- if required.-preprocessFile- :: [SymbolicPath PackageDir SourceDir] -- ^ source directories-- -> FilePath -- ^build directory- -> Bool -- ^preprocess for sdist- -> FilePath -- ^module file name- -> Verbosity -- ^verbosity- -> [String] -- ^builtin suffixes- -> [(String, PreProcessor)] -- ^possible preprocessors- -> IO ()-preprocessFile searchLoc buildLoc forSDist baseFile verbosity builtinSuffixes handlers = do- -- look for files in the various source dirs with this module name- -- and a file extension of a known preprocessor- psrcFiles <- findFileWithExtension' (map fst handlers) (map getSymbolicPath searchLoc) baseFile- case psrcFiles of- -- no preprocessor file exists, look for an ordinary source file- -- just to make sure one actually exists at all for this module.- -- Note: by looking in the target/output build dir too, we allow- -- source files to appear magically in the target build dir without- -- any corresponding "real" source file. This lets custom Setup.hs- -- files generate source modules directly into the build dir without- -- the rest of the build system being aware of it (somewhat dodgy)- Nothing -> do- bsrcFiles <- findFileWithExtension builtinSuffixes (buildLoc : map getSymbolicPath searchLoc) baseFile- case bsrcFiles of- Nothing ->- die' verbosity $ "can't find source for " ++ baseFile- ++ " in " ++ intercalate ", " (map getSymbolicPath searchLoc)- _ -> return ()- -- found a pre-processable file in one of the source dirs- Just (psrcLoc, psrcRelFile) -> do- let (srcStem, ext) = splitExtension psrcRelFile- psrcFile = psrcLoc </> psrcRelFile- pp = fromMaybe (error "Distribution.Simple.PreProcess: Just expected")- (lookup (safeTail ext) handlers)- -- Preprocessing files for 'sdist' is different from preprocessing- -- for 'build'. When preprocessing for sdist we preprocess to- -- avoid that the user has to have the preprocessors available.- -- ATM, we don't have a way to specify which files are to be- -- preprocessed and which not, so for sdist we only process- -- platform independent files and put them into the 'buildLoc'- -- (which we assume is set to the temp. directory that will become- -- the tarball).- --TODO: eliminate sdist variant, just supply different handlers- when (not forSDist || forSDist && platformIndependent pp) $ do- -- look for existing pre-processed source file in the dest dir to- -- see if we really have to re-run the preprocessor.- ppsrcFiles <- findFileWithExtension builtinSuffixes [buildLoc] baseFile- recomp <- case ppsrcFiles of- Nothing -> return True- Just ppsrcFile ->- psrcFile `moreRecentFile` ppsrcFile- when recomp $ do- let destDir = buildLoc </> dirName srcStem- createDirectoryIfMissingVerbose verbosity True destDir- runPreProcessorWithHsBootHack pp- (psrcLoc, psrcRelFile)- (buildLoc, srcStem <.> "hs")-- where- dirName = takeDirectory-- -- FIXME: This is a somewhat nasty hack. GHC requires that hs-boot files- -- be in the same place as the hs files, so if we put the hs file in dist/- -- then we need to copy the hs-boot file there too. This should probably be- -- done another way. Possibly we should also be looking for .lhs-boot- -- files, but I think that preprocessors only produce .hs files.- runPreProcessorWithHsBootHack pp- (inBaseDir, inRelativeFile)- (outBaseDir, outRelativeFile) = do- runPreProcessor pp- (inBaseDir, inRelativeFile)- (outBaseDir, outRelativeFile) verbosity-- exists <- doesFileExist inBoot- when exists $ copyFileVerbose verbosity inBoot outBoot-- where- inBoot = replaceExtension inFile "hs-boot"- outBoot = replaceExtension outFile "hs-boot"-- inFile = normalise (inBaseDir </> inRelativeFile)- outFile = normalise (outBaseDir </> outRelativeFile)---- --------------------------------------------------------------- * known preprocessors--- --------------------------------------------------------------ppGreenCard :: BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor-ppGreenCard _ lbi _- = PreProcessor {- platformIndependent = False,- runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity ->- runDbProgram verbosity greencardProgram (withPrograms lbi)- (["-tffi", "-o" ++ outFile, inFile])- }---- This one is useful for preprocessors that can't handle literate source.--- We also need a way to chain preprocessors.-ppUnlit :: PreProcessor-ppUnlit =- PreProcessor {- platformIndependent = True,- runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity ->- withUTF8FileContents inFile $ \contents ->- either (writeUTF8File outFile) (die' verbosity) (unlit inFile contents)- }--ppCpp :: BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor-ppCpp = ppCpp' []--ppCpp' :: [String] -> BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor-ppCpp' extraArgs bi lbi clbi =- case compilerFlavor (compiler lbi) of- GHC -> ppGhcCpp ghcProgram (const True) args bi lbi clbi- GHCJS -> ppGhcCpp ghcjsProgram (const True) args bi lbi clbi- _ -> ppCpphs args bi lbi clbi- where cppArgs = getCppOptions bi lbi- args = cppArgs ++ extraArgs--ppGhcCpp :: Program -> (Version -> Bool)- -> [String] -> BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor-ppGhcCpp program xHs extraArgs _bi lbi clbi =- PreProcessor {- platformIndependent = False,- runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity -> do- (prog, version, _) <- requireProgramVersion verbosity- program anyVersion (withPrograms lbi)- runProgram verbosity prog $- ["-E", "-cpp"]- -- This is a bit of an ugly hack. We're going to- -- unlit the file ourselves later on if appropriate,- -- so we need GHC not to unlit it now or it'll get- -- double-unlitted. In the future we might switch to- -- using cpphs --unlit instead.- ++ (if xHs version then ["-x", "hs"] else [])- ++ [ "-optP-include", "-optP"++ (autogenComponentModulesDir lbi clbi </> cppHeaderName) ]- ++ ["-o", outFile, inFile]- ++ extraArgs- }--ppCpphs :: [String] -> BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor-ppCpphs extraArgs _bi lbi clbi =- PreProcessor {- platformIndependent = False,- runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity -> do- (cpphsProg, cpphsVersion, _) <- requireProgramVersion verbosity- cpphsProgram anyVersion (withPrograms lbi)- runProgram verbosity cpphsProg $- ("-O" ++ outFile) : inFile- : "--noline" : "--strip"- : (if cpphsVersion >= mkVersion [1,6]- then ["--include="++ (autogenComponentModulesDir lbi clbi </> cppHeaderName)]- else [])- ++ extraArgs- }--ppHsc2hs :: BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor-ppHsc2hs bi lbi clbi =- PreProcessor {- platformIndependent = False,- runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity -> do- (gccProg, _) <- requireProgram verbosity gccProgram (withPrograms lbi)- (hsc2hsProg, hsc2hsVersion, _) <- requireProgramVersion verbosity- hsc2hsProgram anyVersion (withPrograms lbi)- -- See Trac #13896 and https://github.com/haskell/cabal/issues/3122.- let isCross = hostPlatform lbi /= buildPlatform- prependCrossFlags = if isCross then ("-x":) else id- let hsc2hsSupportsResponseFiles = hsc2hsVersion >= mkVersion [0,68,4]- pureArgs = genPureArgs hsc2hsVersion gccProg inFile outFile- if hsc2hsSupportsResponseFiles- then withResponseFile- verbosity- defaultTempFileOptions- (takeDirectory outFile)- "hsc2hs-response.txt"- Nothing- pureArgs- (\responseFileName ->- runProgram verbosity hsc2hsProg (prependCrossFlags ["@"++ responseFileName]))- else runProgram verbosity hsc2hsProg (prependCrossFlags pureArgs)- }- where- -- Returns a list of command line arguments that can either be passed- -- directly, or via a response file.- genPureArgs :: Version -> ConfiguredProgram -> String -> String -> [String]- genPureArgs hsc2hsVersion gccProg inFile outFile =- -- Additional gcc options- [ "--cflag=" ++ opt | opt <- programDefaultArgs gccProg- ++ programOverrideArgs gccProg ]- ++ [ "--lflag=" ++ opt | opt <- programDefaultArgs gccProg- ++ programOverrideArgs gccProg ]-- -- OSX frameworks:- ++ [ what ++ "=-F" ++ opt- | isOSX- , opt <- nub (concatMap Installed.frameworkDirs pkgs)- , what <- ["--cflag", "--lflag"] ]- ++ [ "--lflag=" ++ arg- | isOSX- , opt <- PD.frameworks bi ++ concatMap Installed.frameworks pkgs- , arg <- ["-framework", opt] ]-- -- Note that on ELF systems, wherever we use -L, we must also use -R- -- because presumably that -L dir is not on the normal path for the- -- system's dynamic linker. This is needed because hsc2hs works by- -- compiling a C program and then running it.-- ++ [ "--cflag=" ++ opt | opt <- platformDefines lbi ]-- -- Options from the current package:- ++ [ "--cflag=-I" ++ dir | dir <- PD.includeDirs bi ]- ++ [ "--cflag=-I" ++ buildDir lbi </> dir | dir <- PD.includeDirs bi ]- ++ [ "--cflag=" ++ opt | opt <- PD.ccOptions bi- ++ PD.cppOptions bi- -- hsc2hs uses the C ABI- -- We assume that there are only C sources- -- and C++ functions are exported via a C- -- interface and wrapped in a C source file.- -- Therefore we do not supply C++ flags- -- because there will not be C++ sources.- --- -- DO NOT add PD.cxxOptions unless this changes!- ]- ++ [ "--cflag=" ++ opt | opt <-- [ "-I" ++ autogenComponentModulesDir lbi clbi,- "-I" ++ autogenPackageModulesDir lbi,- "-include", autogenComponentModulesDir lbi clbi </> cppHeaderName ] ]- ++ [ "--lflag=-L" ++ opt | opt <- PD.extraLibDirs bi ]- ++ [ "--lflag=-Wl,-R," ++ opt | isELF- , opt <- PD.extraLibDirs bi ]- ++ [ "--lflag=-l" ++ opt | opt <- PD.extraLibs bi ]- ++ [ "--lflag=" ++ opt | opt <- PD.ldOptions bi ]-- -- Options from dependent packages- ++ [ "--cflag=" ++ opt- | pkg <- pkgs- , opt <- [ "-I" ++ opt | opt <- Installed.includeDirs pkg ]- ++ [ opt | opt <- Installed.ccOptions pkg ] ]- ++ [ "--lflag=" ++ opt- | pkg <- pkgs- , opt <- [ "-L" ++ opt | opt <- Installed.libraryDirs pkg ]- ++ [ "-Wl,-R," ++ opt | isELF- , opt <- Installed.libraryDirs pkg ]- ++ [ "-l" ++ opt | opt <- Installed.extraLibraries pkg ]- ++ [ opt | opt <- Installed.ldOptions pkg ] ]- ++ preccldFlags- ++ hsc2hsOptions bi- ++ postccldFlags-- ++ ["-o", outFile, inFile]- where- -- hsc2hs flag parsing was wrong- -- (see -- https://github.com/haskell/hsc2hs/issues/35)- -- so we need to put -- --cc/--ld *after* hsc2hsOptions,- -- for older hsc2hs (pre 0.68.8) so that they can be overridden.- ccldFlags =- [ "--cc=" ++ programPath gccProg- , "--ld=" ++ programPath gccProg- ]-- (preccldFlags, postccldFlags)- | hsc2hsVersion >= mkVersion [0,68,8] = (ccldFlags, [])- | otherwise = ([], ccldFlags)-- hacked_index = packageHacks (installedPkgs lbi)- -- Look only at the dependencies of the current component- -- being built! This relies on 'installedPkgs' maintaining- -- 'InstalledPackageInfo' for internal deps too; see #2971.- pkgs = PackageIndex.topologicalOrder $- case PackageIndex.dependencyClosure hacked_index- (map fst (componentPackageDeps clbi)) of- Left index' -> index'- Right inf ->- error ("ppHsc2hs: broken closure: " ++ show inf)- isOSX = case buildOS of OSX -> True; _ -> False- isELF = case buildOS of OSX -> False; Windows -> False; AIX -> False; _ -> True;- packageHacks = case compilerFlavor (compiler lbi) of- GHC -> hackRtsPackage- GHCJS -> hackRtsPackage- _ -> id- -- We don't link in the actual Haskell libraries of our dependencies, so- -- the -u flags in the ldOptions of the rts package mean linking fails on- -- OS X (its ld is a tad stricter than gnu ld). Thus we remove the- -- ldOptions for GHC's rts package:- hackRtsPackage index =- case PackageIndex.lookupPackageName index (mkPackageName "rts") of- [(_, [rts])]- -> PackageIndex.insert rts { Installed.ldOptions = [] } index- _ -> error "No (or multiple) ghc rts package is registered!!"--ppHsc2hsExtras :: PreProcessorExtras-ppHsc2hsExtras buildBaseDir = filter ("_hsc.c" `isSuffixOf`) `fmap`- getDirectoryContentsRecursive buildBaseDir--ppC2hs :: BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor-ppC2hs bi lbi clbi =- PreProcessor {- platformIndependent = False,- runPreProcessor = \(inBaseDir, inRelativeFile)- (outBaseDir, outRelativeFile) verbosity -> do- (c2hsProg, _, _) <- requireProgramVersion verbosity- c2hsProgram (orLaterVersion (mkVersion [0,15]))- (withPrograms lbi)- (gccProg, _) <- requireProgram verbosity gccProgram (withPrograms lbi)- runProgram verbosity c2hsProg $-- -- Options from the current package:- [ "--cpp=" ++ programPath gccProg, "--cppopts=-E" ]- ++ [ "--cppopts=" ++ opt | opt <- getCppOptions bi lbi ]- ++ [ "--cppopts=-include" ++ (autogenComponentModulesDir lbi clbi </> cppHeaderName) ]- ++ [ "--include=" ++ outBaseDir ]-- -- Options from dependent packages- ++ [ "--cppopts=" ++ opt- | pkg <- pkgs- , opt <- [ "-I" ++ opt | opt <- Installed.includeDirs pkg ]- ++ [ opt | opt@('-':c:_) <- Installed.ccOptions pkg- -- c2hs uses the C ABI- -- We assume that there are only C sources- -- and C++ functions are exported via a C- -- interface and wrapped in a C source file.- -- Therefore we do not supply C++ flags- -- because there will not be C++ sources.- --- --- -- DO NOT add Installed.cxxOptions unless this changes!- , c `elem` "DIU" ] ]- --TODO: install .chi files for packages, so we can --include- -- those dirs here, for the dependencies-- -- input and output files- ++ [ "--output-dir=" ++ outBaseDir- , "--output=" ++ outRelativeFile- , inBaseDir </> inRelativeFile ]- }- where- pkgs = PackageIndex.topologicalOrder (installedPkgs lbi)--ppC2hsExtras :: PreProcessorExtras-ppC2hsExtras d = filter (\p -> takeExtensions p == ".chs.c") `fmap`- getDirectoryContentsRecursive d----TODO: perhaps use this with hsc2hs too---TODO: remove cc-options from cpphs for cabal-version: >= 1.10---TODO: Refactor and add separate getCppOptionsForHs, getCppOptionsForCxx, & getCppOptionsForC--- instead of combining all these cases in a single function. This blind combination can--- potentially lead to compilation inconsistencies.-getCppOptions :: BuildInfo -> LocalBuildInfo -> [String]-getCppOptions bi lbi- = platformDefines lbi- ++ cppOptions bi- ++ ["-I" ++ dir | dir <- PD.includeDirs bi]- ++ [opt | opt@('-':c:_) <- PD.ccOptions bi ++ PD.cxxOptions bi, c `elem` "DIU"]--platformDefines :: LocalBuildInfo -> [String]-platformDefines lbi =- case compilerFlavor comp of- GHC ->- ["-D__GLASGOW_HASKELL__=" ++ versionInt version] ++- ["-D" ++ os ++ "_BUILD_OS=1"] ++- ["-D" ++ arch ++ "_BUILD_ARCH=1"] ++- map (\os' -> "-D" ++ os' ++ "_HOST_OS=1") osStr ++- map (\arch' -> "-D" ++ arch' ++ "_HOST_ARCH=1") archStr- GHCJS ->- compatGlasgowHaskell ++- ["-D__GHCJS__=" ++ versionInt version] ++- ["-D" ++ os ++ "_BUILD_OS=1"] ++- ["-D" ++ arch ++ "_BUILD_ARCH=1"] ++- map (\os' -> "-D" ++ os' ++ "_HOST_OS=1") osStr ++- map (\arch' -> "-D" ++ arch' ++ "_HOST_ARCH=1") archStr- HaskellSuite {} ->- ["-D__HASKELL_SUITE__"] ++- map (\os' -> "-D" ++ os' ++ "_HOST_OS=1") osStr ++- map (\arch' -> "-D" ++ arch' ++ "_HOST_ARCH=1") archStr- _ -> []- where- comp = compiler lbi- Platform hostArch hostOS = hostPlatform lbi- version = compilerVersion comp- compatGlasgowHaskell =- maybe [] (\v -> ["-D__GLASGOW_HASKELL__=" ++ versionInt v])- (compilerCompatVersion GHC comp)- -- TODO: move this into the compiler abstraction- -- FIXME: this forces GHC's crazy 4.8.2 -> 408 convention on all- -- the other compilers. Check if that's really what they want.- versionInt :: Version -> String- versionInt v = case versionNumbers v of- [] -> "1"- [n] -> show n- n1:n2:_ ->- -- 6.8.x -> 608- -- 6.10.x -> 610- let s1 = show n1- s2 = show n2- middle = case s2 of- _ : _ : _ -> ""- _ -> "0"- in s1 ++ middle ++ s2-- osStr = case hostOS of- Linux -> ["linux"]- Windows -> ["mingw32"]- OSX -> ["darwin"]- FreeBSD -> ["freebsd"]- OpenBSD -> ["openbsd"]- NetBSD -> ["netbsd"]- DragonFly -> ["dragonfly"]- Solaris -> ["solaris2"]- AIX -> ["aix"]- HPUX -> ["hpux"]- IRIX -> ["irix"]- HaLVM -> []- IOS -> ["ios"]- Android -> ["android"]- Ghcjs -> ["ghcjs"]- Hurd -> ["hurd"]- OtherOS _ -> []- archStr = case hostArch of- I386 -> ["i386"]- X86_64 -> ["x86_64"]- PPC -> ["powerpc"]- PPC64 -> ["powerpc64"]- Sparc -> ["sparc"]- Arm -> ["arm"]- AArch64 -> ["aarch64"]- Mips -> ["mips"]- SH -> []- IA64 -> ["ia64"]- S390 -> ["s390"]- Alpha -> ["alpha"]- Hppa -> ["hppa"]- Rs6000 -> ["rs6000"]- M68k -> ["m68k"]- Vax -> ["vax"]- JavaScript -> ["javascript"]- OtherArch _ -> []--ppHappy :: BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor-ppHappy _ lbi _ = pp { platformIndependent = True }- where pp = standardPP lbi happyProgram (hcFlags hc)- hc = compilerFlavor (compiler lbi)- hcFlags GHC = ["-agc"]- hcFlags GHCJS = ["-agc"]- hcFlags _ = []--ppAlex :: BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor-ppAlex _ lbi _ = pp { platformIndependent = True }- where pp = standardPP lbi alexProgram (hcFlags hc)- hc = compilerFlavor (compiler lbi)- hcFlags GHC = ["-g"]- hcFlags GHCJS = ["-g"]- hcFlags _ = []--standardPP :: LocalBuildInfo -> Program -> [String] -> PreProcessor-standardPP lbi prog args =- PreProcessor {- platformIndependent = False,- runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity ->- runDbProgram verbosity prog (withPrograms lbi)- (args ++ ["-o", outFile, inFile])- }---- |Convenience function; get the suffixes of these preprocessors.-ppSuffixes :: [ PPSuffixHandler ] -> [String]-ppSuffixes = map fst---- |Standard preprocessors: GreenCard, c2hs, hsc2hs, happy, alex and cpphs.-knownSuffixHandlers :: [ PPSuffixHandler ]-knownSuffixHandlers =- [ ("gc", ppGreenCard)- , ("chs", ppC2hs)- , ("hsc", ppHsc2hs)- , ("x", ppAlex)- , ("y", ppHappy)- , ("ly", ppHappy)- , ("cpphs", ppCpp)- ]---- |Standard preprocessors with possible extra C sources: c2hs, hsc2hs.-knownExtrasHandlers :: [ PreProcessorExtras ]-knownExtrasHandlers = [ ppC2hsExtras, ppHsc2hsExtras ]---- | Find any extra C sources generated by preprocessing that need to--- be added to the component (addresses issue #238).-preprocessExtras :: Verbosity- -> Component- -> LocalBuildInfo- -> IO [FilePath]-preprocessExtras verbosity comp lbi = case comp of- CLib _ -> pp $ buildDir lbi- (CExe Executable { exeName = nm }) -> do- let nm' = unUnqualComponentName nm- pp $ buildDir lbi </> nm' </> nm' ++ "-tmp"- (CFLib ForeignLib { foreignLibName = nm }) -> do- let nm' = unUnqualComponentName nm- pp $ buildDir lbi </> nm' </> nm' ++ "-tmp"- CTest test -> do- let nm' = unUnqualComponentName $ testName test- case testInterface test of- TestSuiteExeV10 _ _ ->- pp $ buildDir lbi </> nm' </> nm' ++ "-tmp"- TestSuiteLibV09 _ _ ->- pp $ buildDir lbi </> stubName test </> stubName test ++ "-tmp"- TestSuiteUnsupported tt ->- die' verbosity $ "No support for preprocessing test suite type " ++- prettyShow tt- CBench bm -> do- let nm' = unUnqualComponentName $ benchmarkName bm- case benchmarkInterface bm of- BenchmarkExeV10 _ _ ->- pp $ buildDir lbi </> nm' </> nm' ++ "-tmp"- BenchmarkUnsupported tt ->- die' verbosity $ "No support for preprocessing benchmark "- ++ "type " ++ prettyShow tt- where- pp :: FilePath -> IO [FilePath]- pp dir = (map (dir </>) . filter not_sub . concat)- <$> for knownExtrasHandlers- (withLexicalCallStack (\f -> f dir))- -- TODO: This is a terrible hack to work around #3545 while we don't- -- reorganize the directory layout. Basically, for the main- -- library, we might accidentally pick up autogenerated sources for- -- our subcomponents, because they are all stored as subdirectories- -- in dist/build. This is a cheap and cheerful check to prevent- -- this from happening. It is not particularly correct; for example- -- if a user has a test suite named foobar and puts their C file in- -- foobar/foo.c, this test will incorrectly exclude it. But I- -- didn't want to break BC...- not_sub p = and [ not (pre `isPrefixOf` p) | pre <- component_dirs ]- component_dirs = component_names (localPkgDescr lbi)- -- TODO: libify me- component_names pkg_descr = fmap unUnqualComponentName $- mapMaybe (libraryNameString . libName) (subLibraries pkg_descr) ++- map exeName (executables pkg_descr) ++- map testName (testSuites pkg_descr) ++- map benchmarkName (benchmarks pkg_descr)+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}++-----------------------------------------------------------------------------++-- |+-- Module : Distribution.Simple.PreProcess+-- Copyright : (c) 2003-2005, Isaac Jones, Malcolm Wallace+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- This module defines 'PPSuffixHandler', which is a combination of a file+-- extension and a function for configuring a 'PreProcessor'. It also defines+-- a bunch of known built-in preprocessors like @cpp@, @cpphs@, @c2hs@,+-- @hsc2hs@, @happy@, @alex@ etc and lists them in 'knownSuffixHandlers'.+-- On top of this it provides a function for actually preprocessing some sources+-- given a bunch of known suffix handlers.+-- This module is not as good as it could be, it could really do with a rewrite+-- to address some of the problems we have with pre-processors.+module Distribution.Simple.PreProcess+ ( preprocessComponent+ , preprocessExtras+ , preprocessFile+ , knownSuffixHandlers+ , ppSuffixes+ , PPSuffixHandler+ , Suffix (..)+ , builtinHaskellSuffixes+ , builtinHaskellBootSuffixes+ , PreProcessor (..)+ , mkSimplePreProcessor+ , runSimplePreProcessor+ , ppCpp+ , ppCpp'+ , ppC2hs+ , ppHsc2hs+ , ppHappy+ , ppAlex+ , ppUnlit+ , platformDefines+ , unsorted+ )+where++import Distribution.Compat.Prelude+import Distribution.Compat.Stack+import Prelude ()++import Distribution.Backpack.DescribeUnitId+import qualified Distribution.InstalledPackageInfo as Installed+import Distribution.ModuleName (ModuleName)+import Distribution.Package+import Distribution.PackageDescription as PD+import Distribution.Simple.BuildPaths+import Distribution.Simple.CCompiler+import Distribution.Simple.Compiler+import Distribution.Simple.Errors+import Distribution.Simple.LocalBuildInfo+import qualified Distribution.Simple.PackageIndex as PackageIndex+import Distribution.Simple.PreProcess.Types+import Distribution.Simple.PreProcess.Unlit+import Distribution.Simple.Program+import Distribution.Simple.Program.ResponseFile+import Distribution.Simple.Test.LibV09+import Distribution.Simple.Utils+import Distribution.System+import Distribution.Types.PackageName.Magic+import Distribution.Utils.Path+import Distribution.Verbosity+import Distribution.Version++import System.Directory (doesDirectoryExist, doesFileExist)+import System.FilePath+ ( normalise+ , replaceExtension+ , splitExtension+ , takeDirectory+ , takeExtensions+ )+import System.Info (arch, os)++-- | Just present the modules in the order given; this is the default and it is+-- appropriate for preprocessors which do not have any sort of dependencies+-- between modules.+unsorted+ :: Verbosity+ -> [path]+ -> [ModuleName]+ -> IO [ModuleName]+unsorted _ _ ms = pure ms++-- | Function to determine paths to possible extra C sources for a+-- preprocessor: just takes the path to the build directory and uses+-- this to search for C sources with names that match the+-- preprocessor's output name format.+type PreProcessorExtras =+ Maybe (SymbolicPath CWD (Dir Pkg))+ -> SymbolicPath Pkg (Dir Source)+ -> IO [RelativePath Source File]++mkSimplePreProcessor+ :: (FilePath -> FilePath -> Verbosity -> IO ())+ -> (FilePath, FilePath)+ -> (FilePath, FilePath)+ -> Verbosity+ -> IO ()+mkSimplePreProcessor+ simplePP+ (inBaseDir, inRelativeFile)+ (outBaseDir, outRelativeFile)+ verbosity = simplePP inFile outFile verbosity+ where+ inFile = normalise (inBaseDir </> inRelativeFile)+ outFile = normalise (outBaseDir </> outRelativeFile)++runSimplePreProcessor+ :: PreProcessor+ -> FilePath+ -> FilePath+ -> Verbosity+ -> IO ()+runSimplePreProcessor pp inFile outFile verbosity =+ runPreProcessor pp (".", inFile) (".", outFile) verbosity++-- | A preprocessor for turning non-Haskell files with the given 'Suffix'+-- (i.e. file extension) into plain Haskell source files.+type PPSuffixHandler =+ (Suffix, BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor)++-- | Apply preprocessors to the sources from 'hsSourceDirs' for a given+-- component (lib, exe, or test suite).+--+-- XXX: This is terrible+preprocessComponent+ :: PackageDescription+ -> Component+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> Bool+ -> Verbosity+ -> [PPSuffixHandler]+ -> IO ()+preprocessComponent pd comp lbi clbi isSrcDist verbosity handlers =+ -- Skip preprocessing for scripts since they should be regular Haskell files,+ -- but may have no or unknown extensions.+ when (package pd /= fakePackageId) $ do+ -- NB: never report instantiation here; we'll report it properly when+ -- building.+ setupMessage'+ verbosity+ "Preprocessing"+ (packageId pd)+ (componentLocalName clbi)+ (Nothing :: Maybe [(ModuleName, Module)])+ case comp of+ (CLib lib@Library{libBuildInfo = bi}) -> do+ let dirs =+ hsSourceDirs bi+ ++ [autogenComponentModulesDir lbi clbi, autogenPackageModulesDir lbi]+ let hndlrs = localHandlers bi+ mods <- orderingFromHandlers verbosity dirs hndlrs (allLibModules lib clbi)+ for_ (map moduleNameSymbolicPath mods) $+ pre dirs (componentBuildDir lbi clbi) hndlrs+ (CFLib flib@ForeignLib{foreignLibBuildInfo = bi}) -> do+ let flibDir = flibBuildDir lbi flib+ dirs =+ hsSourceDirs bi+ ++ [ autogenComponentModulesDir lbi clbi+ , autogenPackageModulesDir lbi+ ]+ let hndlrs = localHandlers bi+ mods <- orderingFromHandlers verbosity dirs hndlrs (foreignLibModules flib)+ for_ (map moduleNameSymbolicPath mods) $+ pre dirs flibDir hndlrs+ (CExe exe@Executable{buildInfo = bi}) -> do+ let exeDir = exeBuildDir lbi exe+ dirs =+ hsSourceDirs bi+ ++ [ autogenComponentModulesDir lbi clbi+ , autogenPackageModulesDir lbi+ ]+ let hndlrs = localHandlers bi+ mods <- orderingFromHandlers verbosity dirs hndlrs (otherModules bi)+ for_ (map moduleNameSymbolicPath mods) $+ pre dirs exeDir hndlrs+ pre (hsSourceDirs bi) exeDir (localHandlers bi) $+ dropExtensionsSymbolicPath (modulePath exe)+ CTest test@TestSuite{} -> do+ let testDir = testBuildDir lbi test+ case testInterface test of+ TestSuiteExeV10 _ f ->+ preProcessTest test f testDir+ TestSuiteLibV09 _ _ -> do+ writeSimpleTestStub test (i testDir)+ preProcessTest test (makeRelativePathEx $ stubFilePath test) testDir+ TestSuiteUnsupported tt ->+ dieWithException verbosity $ NoSupportForPreProcessingTest tt+ CBench bm@Benchmark{} -> do+ let benchDir = benchmarkBuildDir lbi bm+ case benchmarkInterface bm of+ BenchmarkExeV10 _ f ->+ preProcessBench bm f benchDir+ BenchmarkUnsupported tt ->+ dieWithException verbosity $ NoSupportForPreProcessingBenchmark tt+ where+ orderingFromHandlers v d hndlrs mods =+ foldM (\acc (_, pp) -> ppOrdering pp v d acc) mods hndlrs+ builtinCSuffixes = map Suffix cSourceExtensions+ builtinSuffixes = builtinHaskellSuffixes ++ builtinCSuffixes+ localHandlers bi = [(ext, h bi lbi clbi) | (ext, h) <- handlers]+ mbWorkDir = mbWorkDirLBI lbi+ i = interpretSymbolicPathLBI lbi -- See Note [Symbolic paths] in Distribution.Utils.Path+ pre dirs dir lhndlrs fp =+ preprocessFile mbWorkDir dirs dir isSrcDist fp verbosity builtinSuffixes lhndlrs True+ preProcessTest test =+ preProcessComponent+ (testBuildInfo test)+ (testModules test)+ preProcessBench bm =+ preProcessComponent+ (benchmarkBuildInfo bm)+ (benchmarkModules bm)++ preProcessComponent+ :: BuildInfo+ -> [ModuleName]+ -> RelativePath Source File+ -> SymbolicPath Pkg (Dir Build)+ -> IO ()+ preProcessComponent bi modules exePath outputDir = do+ let biHandlers = localHandlers bi+ sourceDirs =+ hsSourceDirs bi+ ++ [ autogenComponentModulesDir lbi clbi+ , autogenPackageModulesDir lbi+ ]+ sequence_+ [ preprocessFile+ mbWorkDir+ sourceDirs+ outputDir+ isSrcDist+ (moduleNameSymbolicPath modu)+ verbosity+ builtinSuffixes+ biHandlers+ False+ | modu <- modules+ ]+ -- Note we don't fail on missing in this case, because the main file+ -- may be generated later (i.e. by a test code generator)+ preprocessFile+ mbWorkDir+ (coerceSymbolicPath outputDir : hsSourceDirs bi)+ outputDir+ isSrcDist+ (dropExtensionsSymbolicPath $ exePath)+ verbosity+ builtinSuffixes+ biHandlers+ False++-- TODO: try to list all the modules that could not be found+-- not just the first one. It's annoying and slow due to the need+-- to reconfigure after editing the .cabal file each time.++-- | Find the first extension of the file that exists, and preprocess it+-- if required.+preprocessFile+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ package directory location+ -> [SymbolicPath Pkg (Dir Source)]+ -- ^ source directories+ -> SymbolicPath Pkg (Dir Build)+ -- ^ build directory+ -> Bool+ -- ^ preprocess for sdist+ -> RelativePath Source File+ -- ^ module file name+ -> Verbosity+ -- ^ verbosity+ -> [Suffix]+ -- ^ builtin suffixes+ -> [(Suffix, PreProcessor)]+ -- ^ possible preprocessors+ -> Bool+ -- ^ fail on missing file+ -> IO ()+preprocessFile mbWorkDir searchLoc buildLoc forSDist baseFile verbosity builtinSuffixes handlers failOnMissing = do+ -- look for files in the various source dirs with this module name+ -- and a file extension of a known preprocessor+ psrcFiles <- findFileCwdWithExtension' mbWorkDir (map fst handlers) searchLoc baseFile+ case psrcFiles of+ -- no preprocessor file exists, look for an ordinary source file+ -- just to make sure one actually exists at all for this module.++ -- Note [Dodgy build dirs for preprocessors]+ -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+ -- By looking in the target/output build dir too, we allow+ -- source files to appear magically in the target build dir without+ -- any corresponding "real" source file. This lets custom Setup.hs+ -- files generate source modules directly into the build dir without+ -- the rest of the build system being aware of it (somewhat dodgy)+ Nothing -> do+ bsrcFiles <- findFileCwdWithExtension mbWorkDir builtinSuffixes (buildAsSrcLoc : searchLoc) baseFile+ case (bsrcFiles, failOnMissing) of+ (Nothing, True) ->+ dieWithException verbosity $+ CantFindSourceForPreProcessFile $+ "can't find source for "+ ++ getSymbolicPath baseFile+ ++ " in "+ ++ intercalate ", " (map getSymbolicPath searchLoc)+ _ -> return ()+ -- found a pre-processable file in one of the source dirs+ Just (psrcLoc, psrcRelFile) -> do+ let (srcStem, ext) = splitExtension $ getSymbolicPath psrcRelFile+ psrcFile = psrcLoc </> psrcRelFile+ pp =+ fromMaybe+ (error "Distribution.Simple.PreProcess: Just expected")+ (lookup (Suffix $ safeTail ext) handlers)+ -- Preprocessing files for 'sdist' is different from preprocessing+ -- for 'build'. When preprocessing for sdist we preprocess to+ -- avoid that the user has to have the preprocessors available.+ -- ATM, we don't have a way to specify which files are to be+ -- preprocessed and which not, so for sdist we only process+ -- platform independent files and put them into the 'buildLoc'+ -- (which we assume is set to the temp. directory that will become+ -- the tarball).+ -- TODO: eliminate sdist variant, just supply different handlers+ when (not forSDist || forSDist && platformIndependent pp) $ do+ -- look for existing pre-processed source file in the dest dir to+ -- see if we really have to re-run the preprocessor.+ ppsrcFiles <- findFileCwdWithExtension mbWorkDir builtinSuffixes [buildAsSrcLoc] baseFile+ recomp <- case ppsrcFiles of+ Nothing -> return True+ Just ppsrcFile ->+ i psrcFile `moreRecentFile` i ppsrcFile+ when recomp $ do+ let destDir = i buildLoc </> takeDirectory srcStem+ createDirectoryIfMissingVerbose verbosity True destDir+ runPreProcessorWithHsBootHack+ pp+ (psrcLoc, getSymbolicPath $ psrcRelFile)+ (buildLoc, srcStem <.> "hs")+ where+ i = interpretSymbolicPath mbWorkDir -- See Note [Symbolic paths] in Distribution.Utils.Path+ buildAsSrcLoc :: SymbolicPath Pkg (Dir Source)+ buildAsSrcLoc = coerceSymbolicPath buildLoc++ -- FIXME: This is a somewhat nasty hack. GHC requires that hs-boot files+ -- be in the same place as the hs files, so if we put the hs file in dist/+ -- then we need to copy the hs-boot file there too. This should probably be+ -- done another way. Possibly we should also be looking for .lhs-boot+ -- files, but I think that preprocessors only produce .hs files.+ runPreProcessorWithHsBootHack+ pp+ (inBaseDir, inRelativeFile)+ (outBaseDir, outRelativeFile) = do+ -- Preprocessors are expected to take into account the working+ -- directory, e.g. using runProgramCwd with a working directory+ -- computed with mbWorkDirLBI.+ -- Hence the use of 'getSymbolicPath' here.+ runPreProcessor+ pp+ (getSymbolicPath $ inBaseDir, inRelativeFile)+ (getSymbolicPath $ outBaseDir, outRelativeFile)+ verbosity++ -- Here we interact directly with the file system, so we must+ -- interpret symbolic paths with respect to the working directory.+ let+ inFile = normalise (i inBaseDir </> inRelativeFile)+ outFile = normalise (i outBaseDir </> outRelativeFile)+ inBoot = replaceExtension inFile "hs-boot"+ outBoot = replaceExtension outFile "hs-boot"+ exists <- doesFileExist inBoot+ when exists $ copyFileVerbose verbosity inBoot outBoot++-- ------------------------------------------------------------++-- * known preprocessors++-- ------------------------------------------------------------++-- This one is useful for preprocessors that can't handle literate source.+-- We also need a way to chain preprocessors.+ppUnlit :: PreProcessor+ppUnlit =+ PreProcessor+ { platformIndependent = True+ , ppOrdering = unsorted+ , runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity ->+ withUTF8FileContents inFile $ \contents ->+ either (writeUTF8File outFile) (dieWithException verbosity) (unlit inFile contents)+ }++ppCpp :: BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor+ppCpp = ppCpp' []++ppCpp' :: [String] -> BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor+ppCpp' extraArgs bi lbi clbi =+ case compilerFlavor (compiler lbi) of+ GHC -> ppGhcCpp ghcProgram (const True) args bi lbi clbi+ GHCJS -> ppGhcCpp ghcjsProgram (const True) args bi lbi clbi+ _ -> ppCpphs args bi lbi clbi+ where+ cppArgs = getCppOptions bi lbi+ args = cppArgs ++ extraArgs++ppGhcCpp+ :: Program+ -> (Version -> Bool)+ -> [String]+ -> BuildInfo+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> PreProcessor+ppGhcCpp program xHs extraArgs _bi lbi clbi =+ PreProcessor+ { platformIndependent = False+ , ppOrdering = unsorted+ , runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity -> do+ (prog, version, _) <-+ requireProgramVersion+ verbosity+ program+ anyVersion+ (withPrograms lbi)+ runProgramCwd verbosity (mbWorkDirLBI lbi) prog $+ ["-E", "-cpp"]+ -- This is a bit of an ugly hack. We're going to+ -- unlit the file ourselves later on if appropriate,+ -- so we need GHC not to unlit it now or it'll get+ -- double-unlitted. In the future we might switch to+ -- using cpphs --unlit instead.+ ++ (if xHs version then ["-x", "hs"] else [])+ ++ ["-optP-include", "-optP" ++ u (autogenComponentModulesDir lbi clbi </> makeRelativePathEx cppHeaderName)]+ ++ ["-o", outFile, inFile]+ ++ extraArgs+ }+ where+ -- See Note [Symbolic paths] in Distribution.Utils.Path+ u :: SymbolicPath Pkg to -> FilePath+ u = interpretSymbolicPathCWD++ppCpphs :: [String] -> BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor+ppCpphs extraArgs _bi lbi clbi =+ PreProcessor+ { platformIndependent = False+ , ppOrdering = unsorted+ , runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity -> do+ (cpphsProg, cpphsVersion, _) <-+ requireProgramVersion+ verbosity+ cpphsProgram+ anyVersion+ (withPrograms lbi)+ runProgramCwd verbosity (mbWorkDirLBI lbi) cpphsProg $+ ("-O" ++ outFile)+ : inFile+ : "--noline"+ : "--strip"+ : ( if cpphsVersion >= mkVersion [1, 6]+ then ["--include=" ++ u (autogenComponentModulesDir lbi clbi </> makeRelativePathEx cppHeaderName)]+ else []+ )+ ++ extraArgs+ }+ where+ -- See Note [Symbolic paths] in Distribution.Utils.Path+ u :: SymbolicPath Pkg to -> FilePath+ u = interpretSymbolicPathCWD++ppHsc2hs :: BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor+ppHsc2hs bi lbi clbi =+ PreProcessor+ { platformIndependent = False+ , ppOrdering = unsorted+ , runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity -> do+ (gccProg, _) <- requireProgram verbosity gccProgram (withPrograms lbi)+ (hsc2hsProg, hsc2hsVersion, _) <-+ requireProgramVersion+ verbosity+ hsc2hsProgram+ anyVersion+ (withPrograms lbi)+ let runHsc2hs = runProgramCwd verbosity mbWorkDir hsc2hsProg+ -- See Trac #13896 and https://github.com/haskell/cabal/issues/3122.+ let isCross = hostPlatform lbi /= buildPlatform+ prependCrossFlags = if isCross then ("-x" :) else id+ let hsc2hsSupportsResponseFiles = hsc2hsVersion >= mkVersion [0, 68, 4]+ pureArgs = genPureArgs hsc2hsVersion gccProg inFile outFile+ if hsc2hsSupportsResponseFiles+ then+ withResponseFile+ verbosity+ defaultTempFileOptions+ "hsc2hs-response.txt"+ Nothing+ pureArgs+ ( \responseFileName ->+ runHsc2hs (prependCrossFlags ["@" ++ responseFileName])+ )+ else runHsc2hs (prependCrossFlags pureArgs)+ }+ where+ -- See Note [Symbolic paths] in Distribution.Utils.Path+ u :: SymbolicPathX allowAbs Pkg to -> FilePath+ u = interpretSymbolicPathCWD+ mbWorkDir = mbWorkDirLBI lbi++ -- Returns a list of command line arguments that can either be passed+ -- directly, or via a response file.+ genPureArgs :: Version -> ConfiguredProgram -> String -> String -> [String]+ genPureArgs hsc2hsVersion gccProg inFile outFile =+ -- Additional gcc options+ [ "--cflag=" ++ opt+ | opt <-+ programDefaultArgs gccProg+ ++ programOverrideArgs gccProg+ ]+ ++ [ "--lflag=" ++ opt+ | opt <-+ programDefaultArgs gccProg+ ++ programOverrideArgs gccProg+ ]+ -- OSX frameworks:+ ++ [ what ++ "=-F" ++ opt+ | isOSX+ , opt <- nub (concatMap Installed.frameworkDirs pkgs)+ , what <- ["--cflag", "--lflag"]+ ]+ ++ [ "--lflag=" ++ arg+ | isOSX+ , opt <- map getSymbolicPath (PD.frameworks bi) ++ concatMap Installed.frameworks pkgs+ , arg <- ["-framework", opt]+ ]+ -- Note that on ELF systems, wherever we use -L, we must also use -R+ -- because presumably that -L dir is not on the normal path for the+ -- system's dynamic linker. This is needed because hsc2hs works by+ -- compiling a C program and then running it.++ ++ ["--cflag=" ++ opt | opt <- platformDefines lbi]+ -- Options from the current package:+ ++ ["--cflag=-I" ++ u dir | dir <- PD.includeDirs bi]+ ++ [ "--cflag=-I" ++ u (buildDir lbi </> unsafeCoerceSymbolicPath relDir)+ | relDir <- mapMaybe symbolicPathRelative_maybe $ PD.includeDirs bi+ ]+ ++ [ "--cflag=" ++ opt+ | opt <-+ PD.ccOptions bi+ ++ PD.cppOptions bi+ -- hsc2hs uses the C ABI+ -- We assume that there are only C sources+ -- and C++ functions are exported via a C+ -- interface and wrapped in a C source file.+ -- Therefore we do not supply C++ flags+ -- because there will not be C++ sources.+ --+ -- DO NOT add PD.cxxOptions unless this changes!+ ]+ ++ [ "--cflag=" ++ opt+ | opt <-+ [ "-I" ++ u (autogenComponentModulesDir lbi clbi)+ , "-I" ++ u (autogenPackageModulesDir lbi)+ , "-include"+ , u $ autogenComponentModulesDir lbi clbi </> makeRelativePathEx cppHeaderName+ ]+ ]+ ++ [ "--lflag=-L" ++ u opt+ | opt <-+ if withFullyStaticExe lbi+ then PD.extraLibDirsStatic bi+ else PD.extraLibDirs bi+ ]+ ++ [ "--lflag=-Wl,-R," ++ u opt+ | isELF+ , opt <-+ if withFullyStaticExe lbi+ then PD.extraLibDirsStatic bi+ else PD.extraLibDirs bi+ ]+ ++ ["--lflag=-l" ++ opt | opt <- PD.extraLibs bi]+ ++ ["--lflag=" ++ opt | opt <- PD.ldOptions bi]+ -- Options from dependent packages+ ++ [ "--cflag=" ++ opt+ | pkg <- pkgs+ , opt <-+ ["-I" ++ opt | opt <- Installed.includeDirs pkg]+ ++ Installed.ccOptions pkg+ ]+ ++ [ "--lflag=" ++ opt+ | pkg <- pkgs+ , opt <-+ ["-L" ++ opt | opt <- Installed.libraryDirs pkg]+ ++ [ "-Wl,-R," ++ opt | isELF, opt <- Installed.libraryDirs pkg+ ]+ ++ [ "-l" ++ opt+ | opt <-+ if withFullyStaticExe lbi+ then Installed.extraLibrariesStatic pkg+ else Installed.extraLibraries pkg+ ]+ ++ Installed.ldOptions pkg+ ]+ ++ preccldFlags+ ++ hsc2hsOptions bi+ ++ postccldFlags+ ++ ["-o", outFile, inFile]+ where+ -- hsc2hs flag parsing was wrong+ -- (see -- https://github.com/haskell/hsc2hs/issues/35)+ -- so we need to put -- --cc/--ld *after* hsc2hsOptions,+ -- for older hsc2hs (pre 0.68.8) so that they can be overridden.+ ccldFlags =+ [ "--cc=" ++ programPath gccProg+ , "--ld=" ++ programPath gccProg+ ]++ (preccldFlags, postccldFlags)+ | hsc2hsVersion >= mkVersion [0, 68, 8] = (ccldFlags, [])+ | otherwise = ([], ccldFlags)++ hacked_index = packageHacks (installedPkgs lbi)+ -- Look only at the dependencies of the current component+ -- being built! This relies on 'installedPkgs' maintaining+ -- 'InstalledPackageInfo' for internal deps too; see #2971.+ pkgs = PackageIndex.topologicalOrder $+ case PackageIndex.dependencyClosure+ hacked_index+ (map fst (componentPackageDeps clbi)) of+ Left index' -> index'+ Right inf ->+ error ("ppHsc2hs: broken closure: " ++ show inf)+ isOSX = case buildOS of OSX -> True; _ -> False+ isELF = case buildOS of OSX -> False; Windows -> False; AIX -> False; _ -> True+ packageHacks = case compilerFlavor (compiler lbi) of+ GHC -> hackRtsPackage+ GHCJS -> hackRtsPackage+ _ -> id+ -- We don't link in the actual Haskell libraries of our dependencies, so+ -- the -u flags in the ldOptions of the rts package mean linking fails on+ -- OS X (its ld is a tad stricter than gnu ld). Thus we remove the+ -- ldOptions for GHC's rts package:+ hackRtsPackage index =+ case PackageIndex.lookupPackageName index (mkPackageName "rts") of+ [(_, [rts])] ->+ PackageIndex.insert rts{Installed.ldOptions = []} index+ _ -> error "No (or multiple) ghc rts package is registered!!"++ppHsc2hsExtras :: PreProcessorExtras+ppHsc2hsExtras mbWorkDir buildBaseDir = do+ fs <- getDirectoryContentsRecursive $ interpretSymbolicPath mbWorkDir buildBaseDir+ let hscCFiles = filter ("_hsc.c" `isSuffixOf`) fs+ return $ map makeRelativePathEx hscCFiles++ppC2hs :: BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor+ppC2hs bi lbi clbi =+ PreProcessor+ { platformIndependent = False+ , ppOrdering = unsorted+ , runPreProcessor =+ \(inBaseDir, inRelativeFile)+ (outBaseDir, outRelativeFile)+ verbosity -> do+ (c2hsProg, _, _) <-+ requireProgramVersion+ verbosity+ c2hsProgram+ (orLaterVersion (mkVersion [0, 15]))+ (withPrograms lbi)+ (gccProg, _) <- requireProgram verbosity gccProgram (withPrograms lbi)+ runProgramCwd verbosity mbWorkDir c2hsProg $+ -- Options from the current package:+ ["--cpp=" ++ programPath gccProg, "--cppopts=-E"]+ ++ ["--cppopts=" ++ opt | opt <- getCppOptions bi lbi]+ ++ ["--cppopts=-include" ++ u (autogenComponentModulesDir lbi clbi </> makeRelativePathEx cppHeaderName)]+ ++ ["--include=" ++ outBaseDir]+ -- Options from dependent packages+ ++ [ "--cppopts=" ++ opt+ | pkg <- pkgs+ , opt <-+ ["-I" ++ opt | opt <- Installed.includeDirs pkg]+ ++ [ opt | opt@('-' : c : _) <- Installed.ccOptions pkg,+ -- c2hs uses the C ABI+ -- We assume that there are only C sources+ -- and C++ functions are exported via a C+ -- interface and wrapped in a C source file.+ -- Therefore we do not supply C++ flags+ -- because there will not be C++ sources.+ --+ --+ -- DO NOT add Installed.cxxOptions unless this changes!+ c `elem` "DIU"+ ]+ ]+ -- TODO: install .chi files for packages, so we can --include+ -- those dirs here, for the dependencies++ -- input and output files+ ++ [ "--output-dir=" ++ outBaseDir+ , "--output=" ++ outRelativeFile+ , inBaseDir </> inRelativeFile+ ]+ }+ where+ pkgs = PackageIndex.topologicalOrder (installedPkgs lbi)+ mbWorkDir = mbWorkDirLBI lbi+ -- See Note [Symbolic paths] in Distribution.Utils.Path+ u :: SymbolicPath Pkg to -> FilePath+ u = interpretSymbolicPathCWD++ppC2hsExtras :: PreProcessorExtras+ppC2hsExtras mbWorkDir buildBaseDir = do+ fs <- getDirectoryContentsRecursive $ interpretSymbolicPath mbWorkDir buildBaseDir+ return $+ map makeRelativePathEx $+ filter (\p -> takeExtensions p == ".chs.c") fs++-- TODO: perhaps use this with hsc2hs too+-- TODO: remove cc-options from cpphs for cabal-version: >= 1.10+-- TODO: Refactor and add separate getCppOptionsForHs, getCppOptionsForCxx, & getCppOptionsForC+-- instead of combining all these cases in a single function. This blind combination can+-- potentially lead to compilation inconsistencies.+getCppOptions :: BuildInfo -> LocalBuildInfo -> [String]+getCppOptions bi lbi =+ platformDefines lbi+ ++ cppOptions bi+ ++ ["-I" ++ getSymbolicPath dir | dir <- PD.includeDirs bi]+ ++ [opt | opt@('-' : c : _) <- PD.ccOptions bi ++ PD.cxxOptions bi, c `elem` "DIU"]++platformDefines :: LocalBuildInfo -> [String]+platformDefines lbi =+ case compilerFlavor comp of+ GHC ->+ ["-D__GLASGOW_HASKELL__=" ++ versionInt version]+ ++ ["-D" ++ os ++ "_BUILD_OS=1"]+ ++ ["-D" ++ arch ++ "_BUILD_ARCH=1"]+ ++ map (\os' -> "-D" ++ os' ++ "_HOST_OS=1") osStr+ ++ map (\arch' -> "-D" ++ arch' ++ "_HOST_ARCH=1") archStr+ GHCJS ->+ compatGlasgowHaskell+ ++ ["-D__GHCJS__=" ++ versionInt version]+ ++ ["-D" ++ os ++ "_BUILD_OS=1"]+ ++ ["-D" ++ arch ++ "_BUILD_ARCH=1"]+ ++ map (\os' -> "-D" ++ os' ++ "_HOST_OS=1") osStr+ ++ map (\arch' -> "-D" ++ arch' ++ "_HOST_ARCH=1") archStr+ _ -> []+ where+ comp = compiler lbi+ Platform hostArch hostOS = hostPlatform lbi+ version = compilerVersion comp+ compatGlasgowHaskell =+ maybe+ []+ (\v -> ["-D__GLASGOW_HASKELL__=" ++ versionInt v])+ (compilerCompatVersion GHC comp)+ -- TODO: move this into the compiler abstraction+ -- FIXME: this forces GHC's crazy 4.8.2 -> 408 convention on all+ -- the other compilers. Check if that's really what they want.+ versionInt :: Version -> String+ versionInt v = case versionNumbers v of+ [] -> "1"+ [n] -> show n+ n1 : n2 : _ ->+ -- 6.8.x -> 608+ -- 6.10.x -> 610+ let s1 = show n1+ s2 = show n2+ middle = case s2 of+ _ : _ : _ -> ""+ _ -> "0"+ in s1 ++ middle ++ s2++ osStr = case hostOS of+ Linux -> ["linux"]+ Windows -> ["mingw32"]+ OSX -> ["darwin"]+ FreeBSD -> ["freebsd"]+ OpenBSD -> ["openbsd"]+ NetBSD -> ["netbsd"]+ DragonFly -> ["dragonfly"]+ Solaris -> ["solaris2"]+ AIX -> ["aix"]+ HPUX -> ["hpux"]+ IRIX -> ["irix"]+ HaLVM -> []+ IOS -> ["ios"]+ Android -> ["android"]+ Ghcjs -> ["ghcjs"]+ Wasi -> ["wasi"]+ Hurd -> ["hurd"]+ Haiku -> ["haiku"]+ OtherOS _ -> []+ archStr = case hostArch of+ I386 -> ["i386"]+ X86_64 -> ["x86_64"]+ PPC -> ["powerpc"]+ PPC64 -> ["powerpc64"]+ PPC64LE -> ["powerpc64le"]+ Sparc -> ["sparc"]+ Sparc64 -> ["sparc64"]+ Arm -> ["arm"]+ AArch64 -> ["aarch64"]+ Mips -> ["mips"]+ SH -> []+ IA64 -> ["ia64"]+ S390 -> ["s390"]+ S390X -> ["s390x"]+ Alpha -> ["alpha"]+ Hppa -> ["hppa"]+ Rs6000 -> ["rs6000"]+ M68k -> ["m68k"]+ Vax -> ["vax"]+ RISCV64 -> ["riscv64"]+ LoongArch64 -> ["loongarch64"]+ JavaScript -> ["javascript"]+ Wasm32 -> ["wasm32"]+ OtherArch _ -> []++ppHappy :: BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor+ppHappy _ lbi _ = pp{platformIndependent = True}+ where+ pp = standardPP lbi happyProgram (hcFlags hc)+ hc = compilerFlavor (compiler lbi)+ hcFlags GHC = ["-agc"]+ hcFlags GHCJS = ["-agc"]+ hcFlags _ = []++ppAlex :: BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor+ppAlex _ lbi _ = pp{platformIndependent = True}+ where+ pp = standardPP lbi alexProgram (hcFlags hc)+ hc = compilerFlavor (compiler lbi)+ hcFlags GHC = ["-g"]+ hcFlags GHCJS = ["-g"]+ hcFlags _ = []++standardPP :: LocalBuildInfo -> Program -> [String] -> PreProcessor+standardPP lbi prog args =+ PreProcessor+ { platformIndependent = False+ , ppOrdering = unsorted+ , runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity ->+ runDbProgramCwd+ verbosity+ (mbWorkDirLBI lbi)+ prog+ (withPrograms lbi)+ (args ++ ["-o", outFile, inFile])+ }++-- | Convenience function; get the suffixes of these preprocessors.+ppSuffixes :: [PPSuffixHandler] -> [Suffix]+ppSuffixes = map fst++-- | Standard preprocessors: c2hs, hsc2hs, happy, alex and cpphs.+knownSuffixHandlers :: [PPSuffixHandler]+knownSuffixHandlers =+ [ (Suffix "chs", ppC2hs)+ , (Suffix "hsc", ppHsc2hs)+ , (Suffix "x", ppAlex)+ , (Suffix "y", ppHappy)+ , (Suffix "ly", ppHappy)+ , (Suffix "cpphs", ppCpp)+ ]++-- | Standard preprocessors with possible extra C sources: c2hs, hsc2hs.+knownExtrasHandlers :: [PreProcessorExtras]+knownExtrasHandlers = [ppC2hsExtras, ppHsc2hsExtras]++-- | Find any extra C sources generated by preprocessing that need to+-- be added to the component (addresses issue #238).+preprocessExtras+ :: Verbosity+ -> Component+ -> LocalBuildInfo+ -> IO [SymbolicPath Pkg File]+preprocessExtras verbosity comp lbi = case comp of+ CLib _ -> pp $ buildDir lbi+ (CExe exe@Executable{}) -> pp $ exeBuildDir lbi exe+ (CFLib flib@ForeignLib{}) -> pp $ flibBuildDir lbi flib+ CTest test ->+ case testInterface test of+ TestSuiteUnsupported tt ->+ dieWithException verbosity $ NoSupportPreProcessingTestExtras tt+ _ -> pp $ testBuildDir lbi test+ CBench bm ->+ case benchmarkInterface bm of+ BenchmarkUnsupported tt ->+ dieWithException verbosity $ NoSupportPreProcessingBenchmarkExtras tt+ _ -> pp $ benchmarkBuildDir lbi bm+ where+ pp :: SymbolicPath Pkg (Dir Build) -> IO [SymbolicPath Pkg File]+ pp builddir = do+ -- Use the build dir as a source dir.+ let dir :: SymbolicPath Pkg (Dir Source)+ dir = coerceSymbolicPath builddir+ mbWorkDir = mbWorkDirLBI lbi+ b <- doesDirectoryExist (interpretSymbolicPathLBI lbi dir)+ if b+ then do+ xs <- for knownExtrasHandlers $ withLexicalCallStack $ \f -> f mbWorkDir dir+ let not_subs =+ map (dir </>) $+ filter (not_sub . getSymbolicPath) $+ concat xs+ return not_subs+ else pure []+ -- TODO: This is a terrible hack to work around #3545 while we don't+ -- reorganize the directory layout. Basically, for the main+ -- library, we might accidentally pick up autogenerated sources for+ -- our subcomponents, because they are all stored as subdirectories+ -- in dist/build. This is a cheap and cheerful check to prevent+ -- this from happening. It is not particularly correct; for example+ -- if a user has a test suite named foobar and puts their C file in+ -- foobar/foo.c, this test will incorrectly exclude it. But I+ -- didn't want to break BC...+ not_sub p = and [not (pre `isPrefixOf` p) | pre <- component_dirs]+ component_dirs = component_names (localPkgDescr lbi)+ -- TODO: libify me+ component_names pkg_descr =+ fmap unUnqualComponentName $+ mapMaybe (libraryNameString . libName) (subLibraries pkg_descr)+ ++ map exeName (executables pkg_descr)+ ++ map testName (testSuites pkg_descr)+ ++ map benchmarkName (benchmarks pkg_descr)
+ src/Distribution/Simple/PreProcess/Types.hs view
@@ -0,0 +1,128 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE RankNTypes #-}++-----------------------------------------------------------------------------++-- |+-- Module : Distribution.Simple.PreProcess.Types+-- Copyright : (c) 2003-2005, Isaac Jones, Malcolm Wallace+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- This defines a 'PreProcessor' abstraction which represents a pre-processor+-- that can transform one kind of file into another.+module Distribution.Simple.PreProcess.Types+ ( Suffix (..)+ , PreProcessor (..)+ , PreProcessCommand+ , builtinHaskellSuffixes+ , builtinHaskellBootSuffixes+ )+where++import Distribution.Compat.Prelude+import Prelude ()++import Distribution.ModuleName (ModuleName)+import Distribution.Pretty+import Distribution.Utils.Path+import Distribution.Verbosity+import qualified Text.PrettyPrint as Disp++-- | The interface to a preprocessor, which may be implemented using an+-- external program, but need not be. The arguments are the name of+-- the input file, the name of the output file and a verbosity level.+-- Here is a simple example that merely prepends a comment to the given+-- source file:+--+-- > ppTestHandler :: PreProcessor+-- > ppTestHandler =+-- > PreProcessor {+-- > platformIndependent = True,+-- > ppOrdering = \_ _ -> return,+-- > runPreProcessor = mkSimplePreProcessor $ \inFile outFile verbosity ->+-- > do info verbosity (inFile++" has been preprocessed to "++outFile)+-- > stuff <- readFile inFile+-- > writeFile outFile ("-- preprocessed as a test\n\n" ++ stuff)+-- > return ()+--+-- We split the input and output file names into a base directory and the+-- rest of the file name. The input base dir is the path in the list of search+-- dirs that this file was found in. The output base dir is the build dir where+-- all the generated source files are put.+--+-- The reason for splitting it up this way is that some pre-processors don't+-- simply generate one output .hs file from one input file but have+-- dependencies on other generated files (notably c2hs, where building one+-- .hs file may require reading other .chi files, and then compiling the .hs+-- file may require reading a generated .h file). In these cases the generated+-- files need to embed relative path names to each other (eg the generated .hs+-- file mentions the .h file in the FFI imports). This path must be relative to+-- the base directory where the generated files are located, it cannot be+-- relative to the top level of the build tree because the compilers do not+-- look for .h files relative to there, ie we do not use \"-I .\", instead we+-- use \"-I dist\/build\" (or whatever dist dir has been set by the user)+--+-- Most pre-processors do not care of course, so mkSimplePreProcessor and+-- runSimplePreProcessor functions handle the simple case.+data PreProcessor = PreProcessor+ { -- Is the output of the pre-processor platform independent? eg happy output+ -- is portable haskell but c2hs's output is platform dependent.+ -- This matters since only platform independent generated code can be+ -- included into a source tarball.+ platformIndependent :: Bool+ , -- TODO: deal with pre-processors that have implementation dependent output+ -- eg alex and happy have --ghc flags. However we can't really include+ -- ghc-specific code into supposedly portable source tarballs.++ ppOrdering+ :: Verbosity+ -> [SymbolicPath Pkg (Dir Source)] -- Source directories+ -> [ModuleName] -- Module names+ -> IO [ModuleName] -- Sorted modules++ -- ^ This function can reorder /all/ modules, not just those that the+ -- require the preprocessor in question. As such, this function should be+ -- well-behaved and not reorder modules it doesn't have dominion over!+ --+ -- @since 3.8.1.0+ , runPreProcessor+ :: PreProcessCommand+ }++-- | A command to run a given preprocessor on a single source file.+--+-- The input and output file paths are passed in as arguments, as it is+-- the build system and not the package author which chooses the location of+-- source files.+type PreProcessCommand =+ (FilePath, FilePath)+ -- ^ Location of the source file relative to a base dir+ -> (FilePath, FilePath)+ -- ^ Output file name, relative to an output base dir+ -> Verbosity+ -> IO () -- Should exit if the preprocessor fails++-- | A suffix (or file extension).+--+-- Mostly used to decide which preprocessor to use, e.g. files with suffix @"y"@+-- are usually processed by the @"happy"@ build tool.+newtype Suffix = Suffix String+ deriving (Eq, Ord, Show, Generic, IsString)++instance Pretty Suffix where+ pretty (Suffix s) = Disp.text s++instance Binary Suffix+instance Structured Suffix++builtinHaskellSuffixes :: [Suffix]+builtinHaskellSuffixes = map Suffix ["hs", "lhs", "hsig", "lhsig"]++builtinHaskellBootSuffixes :: [Suffix]+builtinHaskellBootSuffixes = map Suffix ["hs-boot", "lhs-boot"]
src/Distribution/Simple/PreProcess/Unlit.hs view
@@ -1,4 +1,9 @@ -----------------------------------------------------------------------------++-- This version is interesting because instead of striping comment lines, it+-- turns them into "-- " style comments. This allows using haddock markup+-- in literate scripts without having to use "> --" prefix.+ -- | -- Module : Distribution.Simple.PreProcess.Unlit -- Copyright : ...@@ -8,73 +13,77 @@ -- -- Remove the \"literal\" markups from a Haskell source file, including -- \"@>@\", \"@\\begin{code}@\", \"@\\end{code}@\", and \"@#@\"---- This version is interesting because instead of striping comment lines, it--- turns them into "-- " style comments. This allows using haddock markup--- in literate scripts without having to use "> --" prefix.--module Distribution.Simple.PreProcess.Unlit (unlit,plain) where--import Prelude ()-import Distribution.Compat.Prelude-import Distribution.Utils.Generic (safeTail, safeLast, safeInit)+module Distribution.Simple.PreProcess.Unlit (unlit, plain) where import Data.List (mapAccumL)+import Distribution.Compat.Prelude+import Distribution.Simple.Errors+import Distribution.Utils.Generic (safeInit, safeLast, safeTail)+import Prelude () -data Classified = BirdTrack String | Blank String | Ordinary String- | Line !Int String | CPP String- | BeginCode | EndCode- -- output only:- | Error String | Comment String+data Classified+ = BirdTrack String+ | Blank String+ | Ordinary String+ | Line !Int String+ | CPP String+ | BeginCode+ | EndCode+ | -- output only:+ Error String+ | Comment String -- | No unliteration. plain :: String -> String -> String plain _ hs = hs classify :: String -> Classified-classify ('>':s) = BirdTrack s-classify ('#':s) = case tokens s of- (line:file@('"':_:_):_) | all isDigit line- && safeLast file == Just '"'- -- this shouldn't fail as we tested for 'all isDigit'- -> Line (fromMaybe (error $ "panic! read @Int " ++ show line) $ readMaybe line) (safeTail (safeInit file)) -- TODO:eradicateNoParse- _ -> CPP s- where tokens = unfoldr $ \str -> case lex str of- (t@(_:_), str'):_ -> Just (t, str')- _ -> Nothing-classify ('\\':s)+classify ('>' : s) = BirdTrack s+classify ('#' : s) = case tokens s of+ (line : file@('"' : _ : _) : _)+ | all isDigit line+ && safeLast file == Just '"' ->+ -- this shouldn't fail as we tested for 'all isDigit'+ Line (fromMaybe (error $ "panic! read @Int " ++ show line) $ readMaybe line) (safeTail (safeInit file)) -- TODO:eradicateNoParse+ _ -> CPP s+ where+ tokens = unfoldr $ \str -> case lex str of+ (t@(_ : _), str') : _ -> Just (t, str')+ _ -> Nothing+classify ('\\' : s) | "begin{code}" `isPrefixOf` s = BeginCode- | "end{code}" `isPrefixOf` s = EndCode-classify s | all isSpace s = Blank s-classify s = Ordinary s+ | "end{code}" `isPrefixOf` s = EndCode+classify s | all isSpace s = Blank s+classify s = Ordinary s -- So the weird exception for comment indenting is to make things work with -- haddock, see classifyAndCheckForBirdTracks below. unclassify :: Bool -> Classified -> String-unclassify _ (BirdTrack s) = ' ':s-unclassify _ (Blank s) = s-unclassify _ (Ordinary s) = s-unclassify _ (Line n file) = "# " ++ show n ++ " " ++ show file-unclassify _ (CPP s) = '#':s-unclassify True (Comment "") = " --"-unclassify True (Comment s) = " -- " ++ s-unclassify False (Comment "") = "--"-unclassify False (Comment s) = "-- " ++ s-unclassify _ _ = internalError+unclassify _ (BirdTrack s) = ' ' : s+unclassify _ (Blank s) = s+unclassify _ (Ordinary s) = s+unclassify _ (Line n file) = "# " ++ show n ++ " " ++ show file+unclassify _ (CPP s) = '#' : s+unclassify True (Comment "") = " --"+unclassify True (Comment s) = " -- " ++ s+unclassify False (Comment "") = "--"+unclassify False (Comment s) = "-- " ++ s+unclassify _ _ = internalError -- | 'unlit' takes a filename (for error reports), and transforms the -- given string, to eliminate the literate comments from the program text.-unlit :: FilePath -> String -> Either String String+unlit :: FilePath -> String -> Either String CabalException unlit file input =- let (usesBirdTracks, classified) = classifyAndCheckForBirdTracks- . inlines- $ input- in either (Left . unlines . map (unclassify usesBirdTracks))- Right- . checkErrors- . reclassify- $ classified-+ let (usesBirdTracks, classified) =+ classifyAndCheckForBirdTracks+ . inlines+ $ input+ in either+ (Left . unlines . map (unclassify usesBirdTracks))+ Right+ . checkErrors+ . reclassify+ $ classified where -- So haddock requires comments and code to align, since it treats comments -- as following the layout rule. This is a pain for us since bird track@@ -94,16 +103,17 @@ in (seenBirdTrack || isBirdTrack classification, classification) isBirdTrack (BirdTrack _) = True- isBirdTrack _ = False+ isBirdTrack _ = False - checkErrors ls = case [ e | Error e <- ls ] of- [] -> Left ls- (message:_) -> Right (f ++ ":" ++ show n ++ ": " ++ message)- where (f, n) = errorPos file 1 ls- errorPos f n [] = (f, n)- errorPos f n (Error _:_) = (f, n)- errorPos _ _ (Line n' f':ls) = errorPos f' n' ls- errorPos f n (_ :ls) = errorPos f (n+1) ls+ checkErrors ls = case [e | Error e <- ls] of+ [] -> Left ls+ (message : _) -> Right (UnlitException (f ++ ":" ++ show n ++ ": " ++ message))+ where+ (f, n) = errorPos file 1 ls+ errorPos f n [] = (f, n)+ errorPos f n (Error _ : _) = (f, n)+ errorPos _ _ (Line n' f' : ls) = errorPos f' n' ls+ errorPos f n (_ : ls) = errorPos f (n + 1) ls -- Here we model a state machine, with each state represented by -- a local function. We only have four states (well, five,@@ -117,51 +127,51 @@ reclassify :: [Classified] -> [Classified] reclassify = blank -- begin in blank state where- latex [] = []- latex (EndCode :ls) = Blank "" : comment ls- latex (BeginCode :_ ) = [Error "\\begin{code} in code section"]- latex (BirdTrack l:ls) = Ordinary ('>':l) : latex ls- latex ( l:ls) = l : latex ls+ latex [] = []+ latex (EndCode : ls) = Blank "" : comment ls+ latex (BeginCode : _) = [Error "\\begin{code} in code section"]+ latex (BirdTrack l : ls) = Ordinary ('>' : l) : latex ls+ latex (l : ls) = l : latex ls - blank [] = []- blank (EndCode :_ ) = [Error "\\end{code} without \\begin{code}"]- blank (BeginCode :ls) = Blank "" : latex ls- blank (BirdTrack l:ls) = BirdTrack l : bird ls- blank (Ordinary l:ls) = Comment l : comment ls- blank ( l:ls) = l : blank ls+ blank [] = []+ blank (EndCode : _) = [Error "\\end{code} without \\begin{code}"]+ blank (BeginCode : ls) = Blank "" : latex ls+ blank (BirdTrack l : ls) = BirdTrack l : bird ls+ blank (Ordinary l : ls) = Comment l : comment ls+ blank (l : ls) = l : blank ls - bird [] = []- bird (EndCode :_ ) = [Error "\\end{code} without \\begin{code}"]- bird (BeginCode :ls) = Blank "" : latex ls- bird (Blank l :ls) = Blank l : blank ls- bird (Ordinary _:_ ) = [Error "program line before comment line"]- bird ( l:ls) = l : bird ls+ bird [] = []+ bird (EndCode : _) = [Error "\\end{code} without \\begin{code}"]+ bird (BeginCode : ls) = Blank "" : latex ls+ bird (Blank l : ls) = Blank l : blank ls+ bird (Ordinary _ : _) = [Error "program line before comment line"]+ bird (l : ls) = l : bird ls - comment [] = []- comment (EndCode :_ ) = [Error "\\end{code} without \\begin{code}"]- comment (BeginCode :ls) = Blank "" : latex ls- comment (CPP l :ls) = CPP l : comment ls- comment (BirdTrack _:_ ) = [Error "comment line before program line"]+ comment [] = []+ comment (EndCode : _) = [Error "\\end{code} without \\begin{code}"]+ comment (BeginCode : ls) = Blank "" : latex ls+ comment (CPP l : ls) = CPP l : comment ls+ comment (BirdTrack _ : _) = [Error "comment line before program line"] -- a blank line and another ordinary line following a comment -- will be treated as continuing the comment. Otherwise it's -- then end of the comment, with a blank line.- comment (Blank l:ls@(Ordinary _:_)) = Comment l : comment ls- comment (Blank l:ls) = Blank l : blank ls- comment (Line n f :ls) = Line n f : comment ls- comment (Ordinary l:ls) = Comment l : comment ls- comment (Comment _: _) = internalError- comment (Error _: _) = internalError+ comment (Blank l : ls@(Ordinary _ : _)) = Comment l : comment ls+ comment (Blank l : ls) = Blank l : blank ls+ comment (Line n f : ls) = Line n f : comment ls+ comment (Ordinary l : ls) = Comment l : comment ls+ comment (Comment _ : _) = internalError+ comment (Error _ : _) = internalError -- Re-implementation of 'lines', for better efficiency (but decreased laziness). -- Also, importantly, accepts non-standard DOS and Mac line ending characters. inlines :: String -> [String] inlines xs = lines' xs id where- lines' [] acc = [acc []]- lines' ('\^M':'\n':s) acc = acc [] : lines' s id -- DOS- lines' ('\^M':s) acc = acc [] : lines' s id -- MacOS- lines' ('\n':s) acc = acc [] : lines' s id -- Unix- lines' (c:s) acc = lines' s (acc . (c:))+ lines' [] acc = [acc []]+ lines' ('\^M' : '\n' : s) acc = acc [] : lines' s id -- DOS+ lines' ('\^M' : s) acc = acc [] : lines' s id -- MacOS+ lines' ('\n' : s) acc = acc [] : lines' s id -- Unix+ lines' (c : s) acc = lines' s (acc . (c :)) internalError :: a internalError = error "unlit: internal error"
src/Distribution/Simple/Program.hs view
@@ -1,7 +1,16 @@+{- FUTUREWORK:+ -+ - Currently the logic in this module is not tested.+ -+ - Ideally, a set of unit tests that check whether certain+ - flags trigger recompilation should be added.+ - -}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program -- Copyright : Isaac Jones 2006, Duncan Coutts 2007-2009@@ -34,150 +43,209 @@ -- hookedPrograms in 'Distribution.Simple.UserHooks'. This gives a -- hook user the ability to get the above flags and such so that they -- don't have to write all the PATH logic inside Setup.lhs.--module Distribution.Simple.Program (- -- * Program and functions for constructing them- Program(..)- , ProgramSearchPath- , ProgramSearchPathEntry(..)- , simpleProgram- , findProgramOnSearchPath- , defaultProgramSearchPath- , findProgramVersion+module Distribution.Simple.Program+ ( -- * Program and functions for constructing them+ Program (..)+ , ProgramSearchPath+ , ProgramSearchPathEntry (..)+ , simpleProgram+ , findProgramOnSearchPath+ , defaultProgramSearchPath+ , findProgramVersion -- * Configured program and related functions- , ConfiguredProgram(..)- , programPath- , ProgArg- , ProgramLocation(..)- , runProgram- , getProgramOutput- , suppressOverrideArgs+ , ConfiguredProgram (..)+ , programPath+ , ProgArg+ , ProgramLocation (..)+ , runProgram+ , runProgramCwd+ , getProgramOutput+ , suppressOverrideArgs -- * Program invocations- , ProgramInvocation(..)- , emptyProgramInvocation- , simpleProgramInvocation- , programInvocation- , runProgramInvocation- , getProgramInvocationOutput- , getProgramInvocationLBS+ , ProgramInvocation (..)+ , emptyProgramInvocation+ , simpleProgramInvocation+ , programInvocation+ , runProgramInvocation+ , getProgramInvocationOutput+ , getProgramInvocationLBS -- * The collection of unconfigured and configured programs- , builtinPrograms+ , builtinPrograms -- * The collection of configured programs we can run- , ProgramDb- , defaultProgramDb- , emptyProgramDb- , restoreProgramDb- , addKnownProgram- , addKnownPrograms- , lookupKnownProgram- , knownPrograms- , getProgramSearchPath- , setProgramSearchPath- , userSpecifyPath- , userSpecifyPaths- , userMaybeSpecifyPath- , userSpecifyArgs- , userSpecifyArgss- , userSpecifiedArgs- , lookupProgram- , lookupProgramVersion- , updateProgram- , configureProgram- , configureAllKnownPrograms- , reconfigurePrograms- , requireProgram- , requireProgramVersion- , needProgram- , runDbProgram- , getDbProgramOutput+ , ProgramDb+ , defaultProgramDb+ , emptyProgramDb+ , restoreProgramDb+ , addKnownProgram+ , addKnownPrograms+ , lookupKnownProgram+ , knownPrograms+ , getProgramSearchPath+ , setProgramSearchPath+ , userSpecifyPath+ , userSpecifyPaths+ , userMaybeSpecifyPath+ , userSpecifyArgs+ , userSpecifyArgss+ , userSpecifiedArgs+ , lookupProgram+ , lookupProgramVersion+ , updateProgram+ , configureProgram+ , configureAllKnownPrograms+ , reconfigurePrograms+ , requireProgram+ , requireProgramVersion+ , needProgram+ , runDbProgram+ , runDbProgramCwd+ , getDbProgramOutput+ , getDbProgramOutputCwd -- * Programs that Cabal knows about- , ghcProgram- , ghcPkgProgram- , ghcjsProgram- , ghcjsPkgProgram- , hmakeProgram- , jhcProgram- , uhcProgram- , gccProgram- , arProgram- , stripProgram- , happyProgram- , alexProgram- , hsc2hsProgram- , c2hsProgram- , cpphsProgram- , hscolourProgram- , doctestProgram- , haddockProgram- , greencardProgram- , ldProgram- , tarProgram- , cppProgram- , pkgConfigProgram- , hpcProgram- ) where+ , ghcProgram+ , ghcPkgProgram+ , ghcjsProgram+ , ghcjsPkgProgram+ , jhcProgram+ , uhcProgram+ , gccProgram+ , gppProgram+ , arProgram+ , stripProgram+ , happyProgram+ , alexProgram+ , hsc2hsProgram+ , c2hsProgram+ , cpphsProgram+ , hscolourProgram+ , doctestProgram+ , haddockProgram+ , ldProgram+ , tarProgram+ , cppProgram+ , pkgConfigProgram+ , hpcProgram+ ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -import Distribution.Simple.Program.Types-import Distribution.Simple.Program.Run-import Distribution.Simple.Program.Db+import Distribution.Simple.Errors import Distribution.Simple.Program.Builtin+import Distribution.Simple.Program.Db import Distribution.Simple.Program.Find+import Distribution.Simple.Program.Run+import Distribution.Simple.Program.Types import Distribution.Simple.Utils+import Distribution.Utils.Path import Distribution.Verbosity -- | Runs the given configured program.-runProgram :: Verbosity -- ^Verbosity- -> ConfiguredProgram -- ^The program to run- -> [ProgArg] -- ^Any /extra/ arguments to add- -> IO ()+runProgram+ :: Verbosity+ -- ^ Verbosity+ -> ConfiguredProgram+ -- ^ The program to run+ -> [ProgArg]+ -- ^ Any /extra/ arguments to add+ -> IO () runProgram verbosity prog args = runProgramInvocation verbosity (programInvocation prog args) +-- | Runs the given configured program.+runProgramCwd+ :: Verbosity+ -- ^ Verbosity+ -> Maybe (SymbolicPath CWD (Dir to))+ -- ^ Working directory+ -> ConfiguredProgram+ -- ^ The program to run+ -> [ProgArg]+ -- ^ Any /extra/ arguments to add+ -> IO ()+runProgramCwd verbosity mbWorkDir prog args =+ runProgramInvocation verbosity (programInvocationCwd mbWorkDir prog args) -- | Runs the given configured program and gets the output.----getProgramOutput :: Verbosity -- ^Verbosity- -> ConfiguredProgram -- ^The program to run- -> [ProgArg] -- ^Any /extra/ arguments to add- -> IO String+getProgramOutput+ :: Verbosity+ -- ^ Verbosity+ -> ConfiguredProgram+ -- ^ The program to run+ -> [ProgArg]+ -- ^ Any /extra/ arguments to add+ -> IO String getProgramOutput verbosity prog args = getProgramInvocationOutput verbosity (programInvocation prog args) +-- | Looks up the given program in the program database and runs it.+runDbProgram+ :: Verbosity+ -- ^ verbosity+ -> Program+ -- ^ The program to run+ -> ProgramDb+ -- ^ look up the program here+ -> [ProgArg]+ -- ^ Any /extra/ arguments to add+ -> IO ()+runDbProgram verbosity prog progDb args =+ runDbProgramCwd verbosity Nothing prog progDb args -- | Looks up the given program in the program database and runs it.----runDbProgram :: Verbosity -- ^verbosity- -> Program -- ^The program to run- -> ProgramDb -- ^look up the program here- -> [ProgArg] -- ^Any /extra/ arguments to add- -> IO ()-runDbProgram verbosity prog programDb args =+runDbProgramCwd+ :: Verbosity+ -- ^ verbosity+ -> Maybe (SymbolicPath CWD (Dir to))+ -- ^ working directory+ -> Program+ -- ^ The program to run+ -> ProgramDb+ -- ^ look up the program here+ -> [ProgArg]+ -- ^ Any /extra/ arguments to add+ -> IO ()+runDbProgramCwd verbosity mbWorkDir prog programDb args = case lookupProgram prog programDb of- Nothing -> die' verbosity notFound- Just configuredProg -> runProgram verbosity configuredProg args- where- notFound = "The program '" ++ programName prog- ++ "' is required but it could not be found"+ Nothing ->+ dieWithException verbosity $ ProgramNotFound (programName prog)+ Just configuredProg -> runProgramCwd verbosity mbWorkDir configuredProg args -- | Looks up the given program in the program database and runs it.----getDbProgramOutput :: Verbosity -- ^verbosity- -> Program -- ^The program to run- -> ProgramDb -- ^look up the program here- -> [ProgArg] -- ^Any /extra/ arguments to add- -> IO String-getDbProgramOutput verbosity prog programDb args =+getDbProgramOutput+ :: Verbosity+ -- ^ verbosity+ -> Program+ -- ^ The program to run+ -> ProgramDb+ -- ^ look up the program here+ -> [ProgArg]+ -- ^ Any /extra/ arguments to add+ -> IO String+getDbProgramOutput verb prog progDb args =+ getDbProgramOutputCwd verb Nothing prog progDb args++-- | Looks up the given program in the program database and runs it.+getDbProgramOutputCwd+ :: Verbosity+ -- ^ verbosity+ -> Maybe (SymbolicPath CWD (Dir to))+ -- ^ working directory+ -> Program+ -- ^ The program to run+ -> ProgramDb+ -- ^ look up the program here+ -> [ProgArg]+ -- ^ Any /extra/ arguments to add+ -> IO String+getDbProgramOutputCwd verbosity mbWorkDir prog programDb args = case lookupProgram prog programDb of- Nothing -> die' verbosity notFound- Just configuredProg -> getProgramOutput verbosity configuredProg args- where- notFound = "The program '" ++ programName prog- ++ "' is required but it could not be found"+ Nothing -> dieWithException verbosity $ ProgramNotFound (programName prog)+ Just configuredProg ->+ getProgramInvocationOutput verbosity $+ programInvocationCwd mbWorkDir configuredProg args
src/Distribution/Simple/Program/Ar.hs view
@@ -1,9 +1,11 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NondecreasingIndentation #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-}-{-# LANGUAGE NondecreasingIndentation #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Ar -- Copyright : Duncan Coutts 2009@@ -12,115 +14,163 @@ -- Portability : portable -- -- This module provides an library interface to the @ar@ program.--module Distribution.Simple.Program.Ar (- createArLibArchive,- multiStageProgramInvocation+module Distribution.Simple.Program.Ar+ ( createArLibArchive+ , multiStageProgramInvocation ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as BS8 import Distribution.Compat.CopyFile (filesEqual)-import Distribution.Simple.Compiler (arResponseFilesSupported)-import Distribution.Simple.LocalBuildInfo (LocalBuildInfo(..))+import Distribution.Simple.Compiler (arDashLSupported, arResponseFilesSupported)+import Distribution.Simple.LocalBuildInfo (LocalBuildInfo (..), mbWorkDirLBI) import Distribution.Simple.Program- ( ProgramInvocation, arProgram, requireProgram )+ ( ProgramInvocation+ , arProgram+ , requireProgram+ ) import Distribution.Simple.Program.ResponseFile- ( withResponseFile )+ ( withResponseFile+ ) import Distribution.Simple.Program.Run- ( programInvocation, multiStageProgramInvocation- , runProgramInvocation )-import Distribution.Simple.Setup- ( fromFlagOrDefault, configUseResponseFiles )+ ( multiStageProgramInvocation+ , programInvocationCwd+ , runProgramInvocation+ )+import Distribution.Simple.Setup.Common+import Distribution.Simple.Setup.Config+ ( configUseResponseFiles+ ) import Distribution.Simple.Utils- ( defaultTempFileOptions, dieWithLocation', withTempDirectory )+ ( defaultTempFileOptions+ , dieWithLocation'+ , withTempDirectoryCwd+ ) import Distribution.System- ( Arch(..), OS(..), Platform(..) )+ ( Arch (..)+ , OS (..)+ , Platform (..)+ )+import Distribution.Utils.Path import Distribution.Verbosity- ( Verbosity, deafening, verbose )+ ( Verbosity+ , deafening+ , verbose+ )+ import System.Directory (doesFileExist, renameFile)-import System.FilePath ((</>), splitFileName)+import System.FilePath (splitFileName) import System.IO- ( Handle, IOMode(ReadWriteMode), SeekMode(AbsoluteSeek)- , hFileSize, hSeek, withBinaryFile )+ ( Handle+ , IOMode (ReadWriteMode)+ , SeekMode (AbsoluteSeek)+ , hFileSize+ , hSeek+ , withBinaryFile+ ) -- | Call @ar@ to create a library archive from a bunch of object files.----createArLibArchive :: Verbosity -> LocalBuildInfo- -> FilePath -> [FilePath] -> IO ()+createArLibArchive+ :: Verbosity+ -> LocalBuildInfo+ -> SymbolicPath Pkg File+ -> [SymbolicPath Pkg File]+ -> IO () createArLibArchive verbosity lbi targetPath files = do- (ar, _) <- requireProgram verbosity arProgram progDb-- let (targetDir, targetName) = splitFileName targetPath- withTempDirectory verbosity targetDir "objs" $ \ tmpDir -> do- let tmpPath = tmpDir </> targetName+ (arProg, _) <- requireProgram verbosity arProgram progDb - -- The args to use with "ar" are actually rather subtle and system-dependent.- -- In particular we have the following issues:- --- -- -- On OS X, "ar q" does not make an archive index. Archives with no- -- index cannot be used.- --- -- -- GNU "ar r" will not let us add duplicate objects, only "ar q" lets us- -- do that. We have duplicates because of modules like "A.M" and "B.M"- -- both make an object file "M.o" and ar does not consider the directory.- --- -- Our solution is to use "ar r" in the simple case when one call is enough.- -- When we need to call ar multiple times we use "ar q" and for the last- -- call on OSX we use "ar qs" so that it'll make the index.+ let (targetDir0, targetName0) = splitFileName $ getSymbolicPath targetPath+ targetDir = makeSymbolicPath targetDir0+ targetName = makeRelativePathEx targetName0+ mbWorkDir = mbWorkDirLBI lbi+ -- See Note [Symbolic paths] in Distribution.Utils.Path+ i = interpretSymbolicPath mbWorkDir+ u :: SymbolicPath Pkg to -> FilePath+ u = interpretSymbolicPathCWD+ withTempDirectoryCwd verbosity mbWorkDir targetDir "objs" $ \tmpDir -> do+ let tmpPath = tmpDir </> targetName - let simpleArgs = case hostOS of- OSX -> ["-r", "-s"]- _ -> ["-r"]+ -- The args to use with "ar" are actually rather subtle and system-dependent.+ -- In particular we have the following issues:+ --+ -- -- On OS X, "ar q" does not make an archive index. Archives with no+ -- index cannot be used.+ --+ -- -- GNU "ar r" will not let us add duplicate objects, only "ar q" lets us+ -- do that. We have duplicates because of modules like "A.M" and "B.M"+ -- both make an object file "M.o" and ar does not consider the directory.+ --+ -- -- llvm-ar, which GHC >=9.4 uses on Windows, supports a "L" modifier+ -- in "q" mode which compels the archiver to add the members of an input+ -- archive to the output, rather than the archive itself. This is+ -- necessary as GHC may produce .o files that are actually archives. See+ -- https://gitlab.haskell.org/ghc/ghc/-/issues/21068.+ --+ -- Our solution is to use "ar r" in the simple case when one call is enough.+ -- When we need to call ar multiple times we use "ar q" and for the last+ -- call on OSX we use "ar qs" so that it'll make the index. - initialArgs = ["-q"]- finalArgs = case hostOS of- OSX -> ["-q", "-s"]- _ -> ["-q"]+ let simpleArgs, initialArgs, finalArgs :: [String]+ simpleArgs = case hostOS of+ OSX -> ["-r", "-s"]+ _ | dashLSupported -> ["-qL"]+ _ -> ["-r"] - extraArgs = verbosityOpts verbosity ++ [tmpPath]+ initialArgs = ["-q"]+ finalArgs = case hostOS of+ OSX -> ["-q", "-s"]+ _ | dashLSupported -> ["-qL"]+ _ -> ["-q"] - simple = programInvocation ar (simpleArgs ++ extraArgs)- initial = programInvocation ar (initialArgs ++ extraArgs)- middle = initial- final = programInvocation ar (finalArgs ++ extraArgs)+ extraArgs = verbosityOpts verbosity ++ [u tmpPath] - oldVersionManualOverride =- fromFlagOrDefault False $ configUseResponseFiles $ configFlags lbi- responseArgumentsNotSupported =- not (arResponseFilesSupported (compiler lbi))+ ar = programInvocationCwd mbWorkDir arProg+ simple = ar (simpleArgs ++ extraArgs)+ initial = ar (initialArgs ++ extraArgs)+ middle = initial+ final = ar (finalArgs ++ extraArgs) - invokeWithResponesFile :: FilePath -> ProgramInvocation- invokeWithResponesFile atFile =- programInvocation ar $- simpleArgs ++ extraArgs ++ ['@' : atFile]+ oldVersionManualOverride =+ fromFlagOrDefault False $ configUseResponseFiles $ configFlags lbi+ responseArgumentsNotSupported =+ not (arResponseFilesSupported (compiler lbi))+ dashLSupported =+ arDashLSupported (compiler lbi) - if oldVersionManualOverride || responseArgumentsNotSupported- then- sequence_- [ runProgramInvocation verbosity inv- | inv <- multiStageProgramInvocation- simple (initial, middle, final) files ]- else- withResponseFile verbosity defaultTempFileOptions tmpDir "ar.rsp" Nothing files $- \path -> runProgramInvocation verbosity $ invokeWithResponesFile path+ invokeWithResponseFile :: FilePath -> ProgramInvocation+ invokeWithResponseFile atFile =+ (ar $ simpleArgs ++ extraArgs ++ ['@' : atFile]) - unless (hostArch == Arm -- See #1537- || hostOS == AIX) $ -- AIX uses its own "ar" format variant- wipeMetadata verbosity tmpPath- equal <- filesEqual tmpPath targetPath- unless equal $ renameFile tmpPath targetPath+ if oldVersionManualOverride || responseArgumentsNotSupported+ then+ sequence_+ [ runProgramInvocation verbosity inv+ | inv <-+ multiStageProgramInvocation+ simple+ (initial, middle, final)+ (map getSymbolicPath files)+ ]+ else withResponseFile verbosity defaultTempFileOptions "ar.rsp" Nothing (map getSymbolicPath files) $+ \path -> runProgramInvocation verbosity $ invokeWithResponseFile path + unless+ ( hostArch == Arm -- See #1537+ || hostOS == AIX+ )+ $ wipeMetadata verbosity (i tmpPath) -- AIX uses its own "ar" format variant+ equal <- filesEqual (i tmpPath) (i targetPath)+ unless equal $ renameFile (i tmpPath) (i targetPath) where progDb = withPrograms lbi Platform hostArch hostOS = hostPlatform lbi verbosityOpts v | v >= deafening = ["-v"]- | v >= verbose = []- | otherwise = ["-c"] -- Do not warn if library had to be created.+ | v >= verbose = []+ | otherwise = ["-c"] -- Do not warn if library had to be created. -- | @ar@ by default includes various metadata for each object file in their -- respective headers, so the output can differ for the same inputs, making@@ -130,17 +180,18 @@ -- rather harder than just re-implementing this feature. wipeMetadata :: Verbosity -> FilePath -> IO () wipeMetadata verbosity path = do- -- Check for existence first (ReadWriteMode would create one otherwise)- exists <- doesFileExist path- unless exists $ wipeError "Temporary file disappeared"- withBinaryFile path ReadWriteMode $ \ h -> hFileSize h >>= wipeArchive h-+ -- Check for existence first (ReadWriteMode would create one otherwise)+ exists <- doesFileExist path+ unless exists $ wipeError "Temporary file disappeared"+ withBinaryFile path ReadWriteMode $ \h -> hFileSize h >>= wipeArchive h where- wipeError msg = dieWithLocation' verbosity path Nothing $+ wipeError msg =+ dieWithLocation' verbosity path Nothing $ "Distribution.Simple.Program.Ar.wipeMetadata: " ++ msg archLF = "!<arch>\x0a" -- global magic, 8 bytes x60LF = "\x60\x0a" -- header magic, 2 bytes- metadata = BS.concat+ metadata =+ BS.concat [ "0 " -- mtime, 12 bytes , "0 " -- UID, 6 bytes , "0 " -- GID, 6 bytes@@ -152,40 +203,41 @@ -- http://en.wikipedia.org/wiki/Ar_(Unix)#File_format_details wipeArchive :: Handle -> Integer -> IO () wipeArchive h archiveSize = do- global <- BS.hGet h (BS.length archLF)- unless (global == archLF) $ wipeError "Bad global header"- wipeHeader (toInteger $ BS.length archLF)-+ global <- BS.hGet h (BS.length archLF)+ unless (global == archLF) $ wipeError "Bad global header"+ wipeHeader (toInteger $ BS.length archLF) where wipeHeader :: Integer -> IO () wipeHeader offset = case compare offset archiveSize of- EQ -> return ()- GT -> wipeError (atOffset "Archive truncated")- LT -> do- header <- BS.hGet h headerSize- unless (BS.length header == headerSize) $- wipeError (atOffset "Short header")- let magic = BS.drop 58 header- unless (magic == x60LF) . wipeError . atOffset $- "Bad magic " ++ show magic ++ " in header"-- let name = BS.take 16 header- let size = BS.take 10 $ BS.drop 48 header- objSize <- case reads (BS8.unpack size) of- [(n, s)] | all isSpace s -> return n- _ -> wipeError (atOffset "Bad file size in header")+ EQ -> return ()+ GT -> wipeError (atOffset "Archive truncated")+ LT -> do+ header <- BS.hGet h headerSize+ unless (BS.length header == headerSize) $+ wipeError (atOffset "Short header")+ let magic = BS.drop 58 header+ unless (magic == x60LF) . wipeError . atOffset $+ "Bad magic " ++ show magic ++ " in header" - let replacement = BS.concat [ name, metadata, size, magic ]- unless (BS.length replacement == headerSize) $- wipeError (atOffset "Something has gone terribly wrong")- hSeek h AbsoluteSeek offset- BS.hPut h replacement+ let name = BS.take 16 header+ let size = BS.take 10 $ BS.drop 48 header+ objSize <- case reads (BS8.unpack size) of+ [(n, s)] | all isSpace s -> return n+ _ -> wipeError (atOffset "Bad file size in header") - let nextHeader = offset + toInteger headerSize +- -- Odd objects are padded with an extra '\x0a'- if odd objSize then objSize + 1 else objSize- hSeek h AbsoluteSeek nextHeader- wipeHeader nextHeader+ let replacement = BS.concat [name, metadata, size, magic]+ unless (BS.length replacement == headerSize) $+ wipeError (atOffset "Something has gone terribly wrong")+ hSeek h AbsoluteSeek offset+ BS.hPut h replacement + let nextHeader =+ offset+ + toInteger headerSize+ ++ -- Odd objects are padded with an extra '\x0a'+ if odd objSize then objSize + 1 else objSize+ hSeek h AbsoluteSeek nextHeader+ wipeHeader nextHeader where atOffset msg = msg ++ " at offset " ++ show offset
src/Distribution/Simple/Program/Builtin.hs view
@@ -1,4 +1,5 @@ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Builtin -- Copyright : Isaac Jones 2006, Duncan Coutts 2007-2009@@ -9,47 +10,42 @@ -- The module defines all the known built-in 'Program's. -- -- Where possible we try to find their version numbers.----module Distribution.Simple.Program.Builtin (-- -- * The collection of unconfigured and configured programs- builtinPrograms,+module Distribution.Simple.Program.Builtin+ ( -- * The collection of unconfigured and configured programs+ builtinPrograms -- * Programs that Cabal knows about- ghcProgram,- ghcPkgProgram,- runghcProgram,- ghcjsProgram,- ghcjsPkgProgram,- hmakeProgram,- jhcProgram,- haskellSuiteProgram,- haskellSuitePkgProgram,- uhcProgram,- gccProgram,- arProgram,- stripProgram,- happyProgram,- alexProgram,- hsc2hsProgram,- c2hsProgram,- cpphsProgram,- hscolourProgram,- doctestProgram,- haddockProgram,- greencardProgram,- ldProgram,- tarProgram,- cppProgram,- pkgConfigProgram,- hpcProgram,+ , ghcProgram+ , ghcPkgProgram+ , runghcProgram+ , ghcjsProgram+ , ghcjsPkgProgram+ , jhcProgram+ , uhcProgram+ , gccProgram+ , gppProgram+ , arProgram+ , stripProgram+ , happyProgram+ , alexProgram+ , hsc2hsProgram+ , c2hsProgram+ , cpphsProgram+ , hscolourProgram+ , doctestProgram+ , haddockProgram+ , ldProgram+ , tarProgram+ , cppProgram+ , pkgConfigProgram+ , hpcProgram ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -import Distribution.Simple.Program.GHC import Distribution.Simple.Program.Find+import Distribution.Simple.Program.GHC import Distribution.Simple.Program.Internal import Distribution.Simple.Program.Run import Distribution.Simple.Program.Types@@ -60,289 +56,331 @@ import qualified Data.Map as Map -- ------------------------------------------------------------+ -- * Known programs+ -- ------------------------------------------------------------ -- | The default list of programs. -- These programs are typically used internally to Cabal. builtinPrograms :: [Program] builtinPrograms =- [- -- compilers and related progs- ghcProgram- , runghcProgram- , ghcPkgProgram- , ghcjsProgram- , ghcjsPkgProgram- , haskellSuiteProgram- , haskellSuitePkgProgram- , hmakeProgram- , jhcProgram- , uhcProgram- , hpcProgram- -- preprocessors- , hscolourProgram- , doctestProgram- , haddockProgram- , happyProgram- , alexProgram- , hsc2hsProgram- , c2hsProgram- , cpphsProgram- , greencardProgram- -- platform toolchain- , gccProgram- , arProgram- , stripProgram- , ldProgram- , tarProgram- -- configuration tools- , pkgConfigProgram- ]+ [ -- compilers and related progs+ ghcProgram+ , runghcProgram+ , ghcPkgProgram+ , ghcjsProgram+ , ghcjsPkgProgram+ , jhcProgram+ , uhcProgram+ , hpcProgram+ , -- preprocessors+ hscolourProgram+ , doctestProgram+ , haddockProgram+ , happyProgram+ , alexProgram+ , hsc2hsProgram+ , c2hsProgram+ , cpphsProgram+ , -- platform toolchain+ gccProgram+ , arProgram+ , stripProgram+ , ldProgram+ , tarProgram+ , -- configuration tools+ pkgConfigProgram+ ] ghcProgram :: Program-ghcProgram = (simpleProgram "ghc") {- programFindVersion = findProgramVersion "--numeric-version" id,+ghcProgram =+ (simpleProgram "ghc")+ { programFindVersion = findProgramVersion "--numeric-version" id+ , programPostConf = ghcPostConf+ , programNormaliseArgs = normaliseGhcArgs+ }+ where+ ghcPostConf _verbosity ghcProg = do+ let setLanguageEnv prog =+ prog+ { programOverrideEnv =+ ("LANGUAGE", Just "en")+ : programOverrideEnv ghcProg+ } - -- Workaround for https://gitlab.haskell.org/ghc/ghc/-/issues/8825- -- (spurious warning on non-english locales)- programPostConf = \_verbosity ghcProg ->- do let ghcProg' = ghcProg {- programOverrideEnv = ("LANGUAGE", Just "en")- : programOverrideEnv ghcProg- }- -- Only the 7.8 branch seems to be affected. Fixed in 7.8.4.- affectedVersionRange = intersectVersionRanges- (laterVersion $ mkVersion [7,8,0])- (earlierVersion $ mkVersion [7,8,4])- return $ maybe ghcProg- (\v -> if withinRange v affectedVersionRange- then ghcProg' else ghcProg)- (programVersion ghcProg),+ ignorePackageEnv prog = prog{programDefaultArgs = "-package-env=-" : programDefaultArgs prog} - programNormaliseArgs = normaliseGhcArgs- }+ -- Only the 7.8 branch seems to be affected. Fixed in 7.8.4.+ affectedVersionRange =+ intersectVersionRanges+ (laterVersion $ mkVersion [7, 8, 0])+ (earlierVersion $ mkVersion [7, 8, 4]) + canIgnorePackageEnv = orLaterVersion $ mkVersion [8, 4, 4]++ applyWhen cond f prog = if cond then f prog else prog++ return $+ maybe+ ghcProg+ ( \v ->+ -- By default, ignore GHC_ENVIRONMENT variable of any package environmnet+ -- files. See #10759+ applyWhen (withinRange v canIgnorePackageEnv) ignorePackageEnv+ -- Workaround for https://gitlab.haskell.org/ghc/ghc/-/issues/8825+ -- (spurious warning on non-english locales)+ $+ applyWhen (withinRange v affectedVersionRange) setLanguageEnv $+ ghcProg+ )+ (programVersion ghcProg)+ runghcProgram :: Program-runghcProgram = (simpleProgram "runghc") {- programFindVersion = findProgramVersion "--version" $ \str ->- case words str of- -- "runghc 7.10.3"- (_:ver:_) -> ver- _ -> ""- }+runghcProgram =+ (simpleProgram "runghc")+ { programFindVersion = findProgramVersion "--version" $ \str ->+ case words str of+ -- "runghc 7.10.3"+ (_ : ver : _) -> ver+ _ -> ""+ } ghcPkgProgram :: Program-ghcPkgProgram = (simpleProgram "ghc-pkg") {- programFindVersion = findProgramVersion "--version" $ \str ->- -- Invoking "ghc-pkg --version" gives a string like- -- "GHC package manager version 6.4.1"- case words str of- (_:_:_:_:ver:_) -> ver- _ -> ""- }+ghcPkgProgram =+ (simpleProgram "ghc-pkg")+ { programFindVersion = findProgramVersion "--version" $ \str ->+ -- Invoking "ghc-pkg --version" gives a string like+ -- "GHC package manager version 6.4.1"+ case words str of+ (_ : _ : _ : _ : ver : _) -> ver+ _ -> ""+ } ghcjsProgram :: Program-ghcjsProgram = (simpleProgram "ghcjs") {- programFindVersion = findProgramVersion "--numeric-ghcjs-version" id- }+ghcjsProgram =+ (simpleProgram "ghcjs")+ { programFindVersion = findProgramVersion "--numeric-ghcjs-version" id+ } -- note: version is the version number of the GHC version that ghcjs-pkg was built with ghcjsPkgProgram :: Program-ghcjsPkgProgram = (simpleProgram "ghcjs-pkg") {- programFindVersion = findProgramVersion "--version" $ \str ->- -- Invoking "ghcjs-pkg --version" gives a string like- -- "GHCJS package manager version 6.4.1"- case words str of- (_:_:_:_:ver:_) -> ver- _ -> ""- }--hmakeProgram :: Program-hmakeProgram = (simpleProgram "hmake") {- programFindVersion = findProgramVersion "--version" $ \str ->- -- Invoking "hmake --version" gives a string line- -- "/usr/local/bin/hmake: 3.13 (2006-11-01)"- case words str of- (_:ver:_) -> ver- _ -> ""- }+ghcjsPkgProgram =+ (simpleProgram "ghcjs-pkg")+ { programFindVersion = findProgramVersion "--version" $ \str ->+ -- Invoking "ghcjs-pkg --version" gives a string like+ -- "GHCJS package manager version 6.4.1"+ case words str of+ (_ : _ : _ : _ : ver : _) -> ver+ _ -> ""+ } jhcProgram :: Program-jhcProgram = (simpleProgram "jhc") {- programFindVersion = findProgramVersion "--version" $ \str ->- -- invoking "jhc --version" gives a string like- -- "jhc 0.3.20080208 (wubgipkamcep-2)- -- compiled by ghc-6.8 on a x86_64 running linux"- case words str of- (_:ver:_) -> ver- _ -> ""- }+jhcProgram =+ (simpleProgram "jhc")+ { programFindVersion = findProgramVersion "--version" $ \str ->+ -- invoking "jhc --version" gives a string like+ -- "jhc 0.3.20080208 (wubgipkamcep-2)+ -- compiled by ghc-6.8 on a x86_64 running linux"+ case words str of+ (_ : ver : _) -> ver+ _ -> ""+ } uhcProgram :: Program-uhcProgram = (simpleProgram "uhc") {- programFindVersion = findProgramVersion "--version-dotted" id- }+uhcProgram =+ (simpleProgram "uhc")+ { programFindVersion = findProgramVersion "--version-dotted" id+ } hpcProgram :: Program-hpcProgram = (simpleProgram "hpc")- {- programFindVersion = findProgramVersion "version" $ \str ->- case words str of- (_ : _ : _ : ver : _) -> ver- _ -> ""+hpcProgram =+ (simpleProgram "hpc")+ { programFindVersion = findProgramVersion "version" $ \str ->+ case words str of+ (_ : _ : _ : ver : _) -> ver+ _ -> "" } --- This represents a haskell-suite compiler. Of course, the compiler--- itself probably is not called "haskell-suite", so this is not a real--- program. (But we don't know statically the name of the actual compiler,--- so this is the best we can do.)------ Having this Program value serves two purposes:------ 1. We can accept options for the compiler in the form of------ --haskell-suite-option(s)=...------ 2. We can find a program later using this static id (with--- requireProgram).------ The path to the real compiler is found and recorded in the ProgramDb--- during the configure phase.-haskellSuiteProgram :: Program-haskellSuiteProgram = (simpleProgram "haskell-suite") {- -- pretend that the program exists, otherwise it won't be in the- -- "configured" state- programFindLocation = \_verbosity _searchPath ->- return $ Just ("haskell-suite-dummy-location", [])- }---- This represent a haskell-suite package manager. See the comments for--- haskellSuiteProgram.-haskellSuitePkgProgram :: Program-haskellSuitePkgProgram = (simpleProgram "haskell-suite-pkg") {- programFindLocation = \_verbosity _searchPath ->- return $ Just ("haskell-suite-pkg-dummy-location", [])- }-- happyProgram :: Program-happyProgram = (simpleProgram "happy") {- programFindVersion = findProgramVersion "--version" $ \str ->- -- Invoking "happy --version" gives a string like- -- "Happy Version 1.16 Copyright (c) ...."- case words str of- (_:_:ver:_) -> ver- _ -> ""- }+happyProgram =+ (simpleProgram "happy")+ { programFindVersion = findProgramVersion "--version" $ \str ->+ -- Invoking "happy --version" gives a string like+ -- "Happy Version 1.16 Copyright (c) ...."+ case words str of+ (_ : _ : ver : _) -> ver+ _ -> ""+ } alexProgram :: Program-alexProgram = (simpleProgram "alex") {- programFindVersion = findProgramVersion "--version" $ \str ->- -- Invoking "alex --version" gives a string like- -- "Alex version 2.1.0, (c) 2003 Chris Dornan and Simon Marlow"- case words str of- (_:_:ver:_) -> takeWhile (\x -> isDigit x || x == '.') ver- _ -> ""- }+alexProgram =+ (simpleProgram "alex")+ { programFindVersion = findProgramVersion "--version" $ \str ->+ -- Invoking "alex --version" gives a string like+ -- "Alex version 2.1.0, (c) 2003 Chris Dornan and Simon Marlow"+ case words str of+ (_ : _ : ver : _) -> takeWhile (\x -> isDigit x || x == '.') ver+ _ -> ""+ } gccProgram :: Program-gccProgram = (simpleProgram "gcc") {- programFindVersion = findProgramVersion "-dumpversion" id- }+gccProgram =+ (simpleProgram "gcc")+ { programFindVersion = findProgramVersion "-dumpversion" id+ } +gppProgram :: Program+gppProgram =+ (simpleProgram "gpp")+ { programFindVersion = findProgramVersion "-dumpversion" id+ , programFindLocation = \v p -> findProgramOnSearchPath v p "g++"+ }+ arProgram :: Program arProgram = simpleProgram "ar" stripProgram :: Program-stripProgram = (simpleProgram "strip") {- programFindVersion = \verbosity ->- findProgramVersion "--version" stripExtractVersion (lessVerbose verbosity)- }+stripProgram =+ (simpleProgram "strip")+ { programFindVersion = \verbosity ->+ findProgramVersion "--version" stripExtractVersion (lessVerbose verbosity)+ } hsc2hsProgram :: Program-hsc2hsProgram = (simpleProgram "hsc2hs") {- programFindVersion =- findProgramVersion "--version" $ \str ->- -- Invoking "hsc2hs --version" gives a string like "hsc2hs version 0.66"- case words str of- (_:_:ver:_) -> ver- _ -> ""- }+hsc2hsProgram =+ (simpleProgram "hsc2hs")+ { programFindVersion =+ findProgramVersion "--version" $ \str ->+ -- Invoking "hsc2hs --version" gives a string like "hsc2hs version 0.66"+ case words str of+ (_ : _ : ver : _) -> ver+ _ -> ""+ } c2hsProgram :: Program-c2hsProgram = (simpleProgram "c2hs") {- programFindVersion = findProgramVersion "--numeric-version" id- }+c2hsProgram =+ (simpleProgram "c2hs")+ { programFindVersion = findProgramVersion "--numeric-version" id+ } cpphsProgram :: Program-cpphsProgram = (simpleProgram "cpphs") {- programFindVersion = findProgramVersion "--version" $ \str ->- -- Invoking "cpphs --version" gives a string like "cpphs 1.3"- case words str of- (_:ver:_) -> ver- _ -> ""- }+cpphsProgram =+ (simpleProgram "cpphs")+ { programFindVersion = findProgramVersion "--version" $ \str ->+ -- Invoking "cpphs --version" gives a string like "cpphs 1.3"+ case words str of+ (_ : ver : _) -> ver+ _ -> ""+ } hscolourProgram :: Program-hscolourProgram = (simpleProgram "hscolour") {- programFindLocation = \v p -> findProgramOnSearchPath v p "HsColour",- programFindVersion = findProgramVersion "-version" $ \str ->- -- Invoking "HsColour -version" gives a string like "HsColour 1.7"- case words str of- (_:ver:_) -> ver- _ -> ""- }+hscolourProgram =+ (simpleProgram "hscolour")+ { programFindLocation = \v p -> findProgramOnSearchPath v p "HsColour"+ , programFindVersion = findProgramVersion "-version" $ \str ->+ -- Invoking "HsColour -version" gives a string like "HsColour 1.7"+ case words str of+ (_ : ver : _) -> ver+ _ -> ""+ } -- TODO: Ensure that doctest is built against the same GHC as the one -- that's being used. Same for haddock. @phadej pointed this out. doctestProgram :: Program-doctestProgram = (simpleProgram "doctest") {- programFindLocation = \v p -> findProgramOnSearchPath v p "doctest"- , programFindVersion = findProgramVersion "--version" $ \str ->- -- "doctest version 0.11.2"- case words str of- (_:_:ver:_) -> ver- _ -> ""- }+doctestProgram =+ (simpleProgram "doctest")+ { programFindLocation = \v p -> findProgramOnSearchPath v p "doctest"+ , programFindVersion = findProgramVersion "--version" $ \str ->+ -- "doctest version 0.11.2"+ case words str of+ (_ : _ : ver : _) -> ver+ _ -> ""+ } haddockProgram :: Program-haddockProgram = (simpleProgram "haddock") {- programFindVersion = findProgramVersion "--version" $ \str ->- -- Invoking "haddock --version" gives a string like- -- "Haddock version 0.8, (c) Simon Marlow 2006"- case words str of- (_:_:ver:_) -> takeWhile (`elem` ('.':['0'..'9'])) ver- _ -> "",-- programNormaliseArgs = \_ _ args -> args- }--greencardProgram :: Program-greencardProgram = simpleProgram "greencard"+haddockProgram =+ (simpleProgram "haddock")+ { programFindVersion = findProgramVersion "--version" $ \str ->+ -- Invoking "haddock --version" gives a string like+ -- "Haddock version 0.8, (c) Simon Marlow 2006"+ case words str of+ (_ : _ : ver : _) -> takeWhile (`elem` ('.' : ['0' .. '9'])) ver+ _ -> ""+ , programNormaliseArgs = \_ _ args -> args+ } ldProgram :: Program-ldProgram = simpleProgram "ld"+ldProgram =+ (simpleProgram "ld")+ { programPostConf = \verbosity ldProg -> do+ -- The `lld` linker cannot create merge (relocatable) objects so we+ -- want to detect this.+ -- If the linker does support relocatable objects, we want to use that+ -- to create partially pre-linked objects for GHCi, so we get much+ -- faster loading as we do not have to do the separate loading and+ -- in-memory linking the static linker in GHC does, but can offload+ -- parts of this process to a pre-linking step.+ -- However this requires the linker to support this features. Not all+ -- linkers do, and notably as of this writing `lld` which is a popular+ -- choice for windows linking does not support this feature. However+ -- if using binutils ld or another linker that supports --relocatable,+ -- we should still be good to generate pre-linked objects.+ ldHelpOutput <-+ getProgramInvocationOutput+ verbosity+ (programInvocation ldProg ["--help"])+ -- In case the linker does not support '--help'. Eg the LLVM linker,+ -- `lld` only accepts `-help`.+ `catchIO` (\_ -> return "")+ let k = "Supports relocatable output"+ -- Standard GNU `ld` uses `--relocatable` while `ld.gold` uses+ -- `-relocatable` (single `-`).+ v+ | "-relocatable" `isInfixOf` ldHelpOutput = "YES"+ -- ld64 on macOS has this lovely response for "--help"+ --+ -- ld64: For information on command line options please use 'man ld'.+ --+ -- it does however support -r, if you read the manpage+ -- (e.g. https://www.manpagez.com/man/1/ld64/)+ | "ld64:" `isPrefixOf` ldHelpOutput = "YES"+ | otherwise = "NO" + m = Map.insert k v (programProperties ldProg)+ return $ ldProg{programProperties = m}+ }+ tarProgram :: Program-tarProgram = (simpleProgram "tar") {- -- See #1901. Some versions of 'tar' (OpenBSD, NetBSD, ...) don't support the- -- '--format' option.- programPostConf = \verbosity tarProg -> do- tarHelpOutput <- getProgramInvocationOutput- verbosity (programInvocation tarProg ["--help"])- -- Some versions of tar don't support '--help'.- `catchIO` (\_ -> return "")- let k = "Supports --format"- v = if ("--format" `isInfixOf` tarHelpOutput) then "YES" else "NO"- m = Map.insert k v (programProperties tarProg)- return $ tarProg { programProperties = m }- }+tarProgram =+ (simpleProgram "tar")+ { -- See #1901. Some versions of 'tar' (OpenBSD, NetBSD, ...) don't support the+ -- '--format' option.+ programPostConf = \verbosity tarProg -> do+ tarHelpOutput <-+ getProgramInvocationOutput+ verbosity+ (programInvocation tarProg ["--help"])+ -- Some versions of tar don't support '--help'.+ `catchIO` (\_ -> return "")+ let k = "Supports --format"+ v = if ("--format" `isInfixOf` tarHelpOutput) then "YES" else "NO"+ m = Map.insert k v (programProperties tarProg)+ return $ tarProg{programProperties = m}+ } cppProgram :: Program cppProgram = simpleProgram "cpp" pkgConfigProgram :: Program-pkgConfigProgram = (simpleProgram "pkg-config") {- programFindVersion = findProgramVersion "--version" id- }+pkgConfigProgram =+ (simpleProgram "pkg-config")+ { programFindVersion = findProgramVersion "--version" id+ , programPostConf = \_ pkgConfProg ->+ let programOverrideEnv' =+ programOverrideEnv pkgConfProg+ ++ [ ("PKG_CONFIG_ALLOW_SYSTEM_CFLAGS", Just "1")+ , ("PKG_CONFIG_ALLOW_SYSTEM_LIBS", Just "1")+ ]+ in pure $ pkgConfProg{programOverrideEnv = programOverrideEnv'}+ }
src/Distribution/Simple/Program/Db.hs view
@@ -1,8 +1,8 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Db -- Copyright : Isaac Jones 2006, Duncan Coutts 2007-2009@@ -23,62 +23,72 @@ -- hookedPrograms in 'Distribution.Simple.UserHooks'. This gives a -- hook user the ability to get the above flags and such so that they -- don't have to write all the PATH logic inside Setup.lhs.--module Distribution.Simple.Program.Db (- -- * The collection of configured programs we can run- ProgramDb,- emptyProgramDb,- defaultProgramDb,- restoreProgramDb,+module Distribution.Simple.Program.Db+ ( -- * The collection of configured programs we can run+ ProgramDb (..)+ , emptyProgramDb+ , defaultProgramDb+ , restoreProgramDb -- ** Query and manipulate the program db- addKnownProgram,- addKnownPrograms,- lookupKnownProgram,- knownPrograms,- getProgramSearchPath,- setProgramSearchPath,- modifyProgramSearchPath,- userSpecifyPath,- userSpecifyPaths,- userMaybeSpecifyPath,- userSpecifyArgs,- userSpecifyArgss,- userSpecifiedArgs,- lookupProgram,- updateProgram,- configuredPrograms,+ , addKnownProgram+ , addKnownPrograms+ , prependProgramSearchPath+ , prependProgramSearchPathNoLogging+ , lookupKnownProgram+ , knownPrograms+ , getProgramSearchPath+ , setProgramSearchPath+ , modifyProgramSearchPath+ , userSpecifyPath+ , userSpecifyPaths+ , userMaybeSpecifyPath+ , userSpecifyArgs+ , userSpecifyArgss+ , userSpecifiedArgs+ , lookupProgram+ , lookupProgramByName+ , updateProgram+ , configuredPrograms -- ** Query and manipulate the program db- configureProgram,- configureAllKnownPrograms,- unconfigureProgram,- lookupProgramVersion,- reconfigurePrograms,- requireProgram,- requireProgramVersion,- needProgram,+ , configureProgram+ , configureUnconfiguredProgram+ , configureAllKnownPrograms+ , unconfigureProgram+ , lookupProgramVersion+ , reconfigurePrograms+ , requireProgram+ , requireProgramVersion+ , needProgram + -- * Internal functions+ , UnconfiguredProgs+ , ConfiguredProgs+ , updateUnconfiguredProgs+ , updateConfiguredProgs ) where import Distribution.Compat.Prelude import Prelude () -import Distribution.Pretty import Distribution.Simple.Program.Builtin import Distribution.Simple.Program.Find import Distribution.Simple.Program.Types import Distribution.Simple.Utils-import Distribution.Utils.Structured (Structure (..), Structured (..))+import Distribution.Utils.Structured (Structure (..), Structured (..)) import Distribution.Verbosity import Distribution.Version import Data.Tuple (swap) import qualified Data.Map as Map+import Distribution.Simple.Errors -- ------------------------------------------------------------+ -- * Programs database+ -- ------------------------------------------------------------ -- | The configuration is a collection of information about programs. It@@ -89,75 +99,82 @@ -- by one we try to configure them at which point we move them into the -- configured collection. For unconfigured programs we record not just the -- 'Program' but also any user-provided arguments and location for the program.-data ProgramDb = ProgramDb {- unconfiguredProgs :: UnconfiguredProgs,- progSearchPath :: ProgramSearchPath,- configuredProgs :: ConfiguredProgs- }- deriving (Typeable)+data ProgramDb = ProgramDb+ { unconfiguredProgs :: UnconfiguredProgs+ , progSearchPath :: ProgramSearchPath+ , progOverrideEnv :: [(String, Maybe String)]+ , configuredProgs :: ConfiguredProgs+ } type UnconfiguredProgram = (Program, Maybe FilePath, [ProgArg])-type UnconfiguredProgs = Map.Map String UnconfiguredProgram-type ConfiguredProgs = Map.Map String ConfiguredProgram-+type UnconfiguredProgs = Map.Map String UnconfiguredProgram+type ConfiguredProgs = Map.Map String ConfiguredProgram emptyProgramDb :: ProgramDb-emptyProgramDb = ProgramDb Map.empty defaultProgramSearchPath Map.empty+emptyProgramDb = ProgramDb Map.empty defaultProgramSearchPath [] Map.empty defaultProgramDb :: ProgramDb defaultProgramDb = restoreProgramDb builtinPrograms emptyProgramDb - -- internal helpers:-updateUnconfiguredProgs :: (UnconfiguredProgs -> UnconfiguredProgs)- -> ProgramDb -> ProgramDb+updateUnconfiguredProgs+ :: (UnconfiguredProgs -> UnconfiguredProgs)+ -> ProgramDb+ -> ProgramDb updateUnconfiguredProgs update progdb =- progdb { unconfiguredProgs = update (unconfiguredProgs progdb) }+ progdb{unconfiguredProgs = update (unconfiguredProgs progdb)} -updateConfiguredProgs :: (ConfiguredProgs -> ConfiguredProgs)- -> ProgramDb -> ProgramDb+updateConfiguredProgs+ :: (ConfiguredProgs -> ConfiguredProgs)+ -> ProgramDb+ -> ProgramDb updateConfiguredProgs update progdb =- progdb { configuredProgs = update (configuredProgs progdb) }-+ progdb{configuredProgs = update (configuredProgs progdb)} -- Read & Show instances are based on listToFM -- | Note that this instance does not preserve the known 'Program's. -- See 'restoreProgramDb' for details.--- instance Show ProgramDb where show = show . Map.toAscList . configuredProgs -- | Note that this instance does not preserve the known 'Program's. -- See 'restoreProgramDb' for details.--- instance Read ProgramDb where readsPrec p s =- [ (emptyProgramDb { configuredProgs = Map.fromList s' }, r)- | (s', r) <- readsPrec p s ]+ [ (emptyProgramDb{configuredProgs = Map.fromList s'}, r)+ | (s', r) <- readsPrec p s+ ] -- | Note that this instance does not preserve the known 'Program's. -- See 'restoreProgramDb' for details.--- instance Binary ProgramDb where put db = do put (progSearchPath db)+ put (progOverrideEnv db) put (configuredProgs db) get = do searchpath <- get- progs <- get- return $! emptyProgramDb {- progSearchPath = searchpath,- configuredProgs = progs- }+ overrides <- get+ progs <- get+ return $!+ emptyProgramDb+ { progSearchPath = searchpath+ , progOverrideEnv = overrides+ , configuredProgs = progs+ } instance Structured ProgramDb where- structure p = Nominal (typeRep p) 0 "ProgramDb"- [ structure (Proxy :: Proxy ProgramSearchPath)- , structure (Proxy :: Proxy ConfiguredProgs)- ]-+ structure p =+ Nominal+ (typeRep p)+ 0+ "ProgramDb"+ [ structure (Proxy :: Proxy ProgramSearchPath)+ , structure (Proxy :: Proxy [(String, Maybe String)])+ , structure (Proxy :: Proxy ConfiguredProgs)+ ] -- | The 'Read'\/'Show' and 'Binary' instances do not preserve all the -- unconfigured 'Programs' because 'Program' is not in 'Read'\/'Show' because@@ -166,142 +183,177 @@ -- -- * It does not add the default programs, but you probably want them, use -- 'builtinPrograms' in addition to any extra you might need.--- restoreProgramDb :: [Program] -> ProgramDb -> ProgramDb restoreProgramDb = addKnownPrograms - -- ------------------------------- -- Managing unconfigured programs -- | Add a known program that we may configure later--- addKnownProgram :: Program -> ProgramDb -> ProgramDb-addKnownProgram prog = updateUnconfiguredProgs $- Map.insertWith combine (programName prog) (prog, Nothing, [])- where combine _ (_, path, args) = (prog, path, args)-+addKnownProgram prog =+ updateUnconfiguredProgs $+ Map.insertWith combine (programName prog) (prog, Nothing, [])+ where+ combine _ (_, path, args) = (prog, path, args) addKnownPrograms :: [Program] -> ProgramDb -> ProgramDb addKnownPrograms progs progdb = foldl' (flip addKnownProgram) progdb progs - lookupKnownProgram :: String -> ProgramDb -> Maybe Program lookupKnownProgram name =- fmap (\(p,_,_)->p) . Map.lookup name . unconfiguredProgs-+ fmap (\(p, _, _) -> p) . Map.lookup name . unconfiguredProgs knownPrograms :: ProgramDb -> [(Program, Maybe ConfiguredProgram)] knownPrograms progdb =- [ (p,p') | (p,_,_) <- Map.elems (unconfiguredProgs progdb)- , let p' = Map.lookup (programName p) (configuredProgs progdb) ]+ [ (p, p') | (p, _, _) <- Map.elems (unconfiguredProgs progdb), let p' = Map.lookup (programName p) (configuredProgs progdb)+ ] -- | Get the current 'ProgramSearchPath' used by the 'ProgramDb'. -- This is the default list of locations where programs are looked for when -- configuring them. This can be overridden for specific programs (with -- 'userSpecifyPath'), and specific known programs can modify or ignore this -- search path in their own configuration code.--- getProgramSearchPath :: ProgramDb -> ProgramSearchPath getProgramSearchPath = progSearchPath -- | Change the current 'ProgramSearchPath' used by the 'ProgramDb'. -- This will affect programs that are configured from here on, so you -- should usually set it before configuring any programs.--- setProgramSearchPath :: ProgramSearchPath -> ProgramDb -> ProgramDb-setProgramSearchPath searchpath db = db { progSearchPath = searchpath }+setProgramSearchPath searchpath db = db{progSearchPath = searchpath} -- | Modify the current 'ProgramSearchPath' used by the 'ProgramDb'. -- This will affect programs that are configured from here on, so you -- should usually modify it before configuring any programs.----modifyProgramSearchPath :: (ProgramSearchPath -> ProgramSearchPath)- -> ProgramDb- -> ProgramDb+modifyProgramSearchPath+ :: (ProgramSearchPath -> ProgramSearchPath)+ -> ProgramDb+ -> ProgramDb modifyProgramSearchPath f db = setProgramSearchPath (f $ getProgramSearchPath db) db --- |User-specify this path. Basically override any path information--- for this program in the configuration. If it's not a known--- program ignore it.+-- | Modify the current 'ProgramSearchPath' used by the 'ProgramDb'+-- by prepending the provided extra paths. ---userSpecifyPath :: String -- ^Program name- -> FilePath -- ^user-specified path to the program- -> ProgramDb -> ProgramDb-userSpecifyPath name path = updateUnconfiguredProgs $- flip Map.update name $ \(prog, _, args) -> Just (prog, Just path, args)+-- - Logs the added paths in info verbosity.+-- - Prepends environment variable overrides.+prependProgramSearchPath+ :: Verbosity+ -> [FilePath]+ -> [(String, Maybe FilePath)]+ -> ProgramDb+ -> IO ProgramDb+prependProgramSearchPath verbosity extraPaths extraEnv db = do+ unless (null extraPaths) $+ logExtraProgramSearchPath verbosity extraPaths+ unless (null extraEnv) $+ logExtraProgramOverrideEnv verbosity extraEnv+ return $ prependProgramSearchPathNoLogging extraPaths extraEnv db +prependProgramSearchPathNoLogging+ :: [FilePath]+ -> [(String, Maybe String)]+ -> ProgramDb+ -> ProgramDb+prependProgramSearchPathNoLogging extraPaths extraEnv db =+ let db' = modifyProgramSearchPath (nub . (map ProgramSearchPathDir extraPaths ++)) db+ db'' = db'{progOverrideEnv = extraEnv ++ progOverrideEnv db'}+ in db'' -userMaybeSpecifyPath :: String -> Maybe FilePath- -> ProgramDb -> ProgramDb-userMaybeSpecifyPath _ Nothing progdb = progdb-userMaybeSpecifyPath name (Just path) progdb = userSpecifyPath name path progdb+-- | User-specify this path. Basically override any path information+-- for this program in the configuration. If it's not a known+-- program ignore it.+userSpecifyPath+ :: String+ -- ^ Program name+ -> FilePath+ -- ^ user-specified path to the program+ -> ProgramDb+ -> ProgramDb+userSpecifyPath name path = updateUnconfiguredProgs $+ flip Map.update name $+ \(prog, _, args) -> Just (prog, Just path, args) +userMaybeSpecifyPath+ :: String+ -> Maybe FilePath+ -> ProgramDb+ -> ProgramDb+userMaybeSpecifyPath _ Nothing progdb = progdb+userMaybeSpecifyPath name (Just path) progdb = userSpecifyPath name path progdb --- |User-specify the arguments for this program. Basically override--- any args information for this program in the configuration. If it's--- not a known program, ignore it..-userSpecifyArgs :: String -- ^Program name- -> [ProgArg] -- ^user-specified args- -> ProgramDb- -> ProgramDb+-- | User-specify the arguments for this program. Basically override+-- any args information for this program in the configuration. If it's+-- not a known program, ignore it..+userSpecifyArgs+ :: String+ -- ^ Program name+ -> [ProgArg]+ -- ^ user-specified args+ -> ProgramDb+ -> ProgramDb userSpecifyArgs name args' =- updateUnconfiguredProgs- (flip Map.update name $- \(prog, path, args) -> Just (prog, path, args ++ args'))- . updateConfiguredProgs- (flip Map.update name $- \prog -> Just prog { programOverrideArgs = programOverrideArgs prog- ++ args' })-+ updateUnconfiguredProgs+ ( flip Map.update name $+ \(prog, path, args) -> Just (prog, path, args ++ args')+ )+ . updateConfiguredProgs+ ( flip Map.update name $+ \prog ->+ Just+ prog+ { programOverrideArgs =+ programOverrideArgs prog+ ++ args'+ }+ ) -- | Like 'userSpecifyPath' but for a list of progs and their paths.----userSpecifyPaths :: [(String, FilePath)]- -> ProgramDb- -> ProgramDb+userSpecifyPaths+ :: [(String, FilePath)]+ -> ProgramDb+ -> ProgramDb userSpecifyPaths paths progdb = foldl' (\progdb' (prog, path) -> userSpecifyPath prog path progdb') progdb paths - -- | Like 'userSpecifyPath' but for a list of progs and their args.----userSpecifyArgss :: [(String, [ProgArg])]- -> ProgramDb- -> ProgramDb+userSpecifyArgss+ :: [(String, [ProgArg])]+ -> ProgramDb+ -> ProgramDb userSpecifyArgss argss progdb = foldl' (\progdb' (prog, args) -> userSpecifyArgs prog args progdb') progdb argss - -- | Get the path that has been previously specified for a program, if any.--- userSpecifiedPath :: Program -> ProgramDb -> Maybe FilePath userSpecifiedPath prog =- join . fmap (\(_,p,_)->p) . Map.lookup (programName prog) . unconfiguredProgs-+ join . fmap (\(_, p, _) -> p) . Map.lookup (programName prog) . unconfiguredProgs -- | Get any extra args that have been previously specified for a program.--- userSpecifiedArgs :: Program -> ProgramDb -> [ProgArg] userSpecifiedArgs prog =- maybe [] (\(_,_,as)->as) . Map.lookup (programName prog) . unconfiguredProgs-+ maybe [] (\(_, _, as) -> as) . Map.lookup (programName prog) . unconfiguredProgs -- ----------------------------- -- Managing configured programs -- | Try to find a configured program lookupProgram :: Program -> ProgramDb -> Maybe ConfiguredProgram-lookupProgram prog = Map.lookup (programName prog) . configuredProgs+lookupProgram = lookupProgramByName . programName +-- | Try to find a configured program+lookupProgramByName :: String -> ProgramDb -> Maybe ConfiguredProgram+lookupProgramByName name = Map.lookup name . configuredProgs -- | Update a configured program in the database.-updateProgram :: ConfiguredProgram -> ProgramDb- -> ProgramDb-updateProgram prog = updateConfiguredProgs $- Map.insert (programId prog) prog-+updateProgram+ :: ConfiguredProgram+ -> ProgramDb+ -> ProgramDb+updateProgram prog =+ updateConfiguredProgs $+ Map.insert (programId prog) prog -- | List all configured programs. configuredPrograms :: ProgramDb -> [ConfiguredProgram]@@ -310,11 +362,13 @@ -- --------------------------- -- Configuring known programs --- | Try to configure a specific program. If the program is already included in--- the collection of unconfigured programs then we use any user-supplied--- location and arguments. If the program gets configured successfully it gets--- added to the configured collection.+-- | Try to configure a specific program and add it to the program database. --+-- If the program is already included in the collection of unconfigured programs,+-- then we use any user-supplied location and arguments.+-- If the program gets configured successfully, it gets added to the configured+-- collection.+-- -- Note that it is not a failure if the program cannot be configured. It's only -- a failure if the user supplied a location and the program could not be found -- at that location.@@ -323,57 +377,73 @@ -- front all the programs we will need, so we try to configure them all. -- To verify that a program was actually successfully configured use -- 'requireProgram'.----configureProgram :: Verbosity- -> Program- -> ProgramDb- -> IO ProgramDb+configureProgram+ :: Verbosity+ -> Program+ -> ProgramDb+ -> IO ProgramDb configureProgram verbosity prog progdb = do+ mbConfiguredProg <- configureUnconfiguredProgram verbosity prog progdb+ case mbConfiguredProg of+ Nothing -> return progdb+ Just configuredProg -> do+ let progdb' =+ updateConfiguredProgs+ (Map.insert (programName prog) configuredProg)+ progdb+ return progdb'++-- | Try to configure a specific program. If the program is already included in+-- the collection of unconfigured programs then we use any user-supplied+-- location and arguments.+configureUnconfiguredProgram+ :: Verbosity+ -> Program+ -> ProgramDb+ -> IO (Maybe ConfiguredProgram)+configureUnconfiguredProgram verbosity prog progdb = do let name = programName prog maybeLocation <- case userSpecifiedPath prog progdb of- Nothing ->+ Nothing -> programFindLocation prog verbosity (progSearchPath progdb)- >>= return . fmap (swap . fmap FoundOnSystem . swap)+ >>= return . fmap (swap . fmap FoundOnSystem . swap) Just path -> do absolute <- doesExecutableExist path if absolute then return (Just (UserSpecified path, []))- else findProgramOnSearchPath verbosity (progSearchPath progdb) path- >>= maybe (die' verbosity notFound)- (return . Just . swap . fmap UserSpecified . swap)- where notFound = "Cannot find the program '" ++ name- ++ "'. User-specified path '"- ++ path ++ "' does not refer to an executable and "- ++ "the program is not on the system path."+ else+ findProgramOnSearchPath verbosity (progSearchPath progdb) path+ >>= maybe+ (dieWithException verbosity $ ConfigureProgram name path)+ (return . Just . swap . fmap UserSpecified . swap) case maybeLocation of- Nothing -> return progdb+ Nothing -> return Nothing Just (location, triedLocations) -> do version <- programFindVersion prog verbosity (locationPath location) newPath <- programSearchPathAsPATHVar (progSearchPath progdb)- let configuredProg = ConfiguredProgram {- programId = name,- programVersion = version,- programDefaultArgs = [],- programOverrideArgs = userSpecifiedArgs prog progdb,- programOverrideEnv = [("PATH", Just newPath)],- programProperties = Map.empty,- programLocation = location,- programMonitorFiles = triedLocations- }+ let configuredProg =+ ConfiguredProgram+ { programId = name+ , programVersion = version+ , programDefaultArgs = []+ , programOverrideArgs = userSpecifiedArgs prog progdb+ , programOverrideEnv = [("PATH", Just newPath)] ++ progOverrideEnv progdb+ , programProperties = Map.empty+ , programLocation = location+ , programMonitorFiles = triedLocations+ } configuredProg' <- programPostConf prog verbosity configuredProg- return (updateConfiguredProgs (Map.insert name configuredProg') progdb)-+ return $ Just configuredProg' -- | Configure a bunch of programs using 'configureProgram'. Just a 'foldM'.----configurePrograms :: Verbosity- -> [Program]- -> ProgramDb- -> IO ProgramDb+configurePrograms+ :: Verbosity+ -> [Program]+ -> ProgramDb+ -> IO ProgramDb configurePrograms verbosity progs progdb = foldM (flip (configureProgram verbosity)) progdb progs - -- | Unconfigure a program. This is basically a hack and you shouldn't -- use it, but it can be handy for making sure a 'requireProgram' -- actually reconfigures.@@ -382,51 +452,51 @@ updateConfiguredProgs $ Map.delete progname -- | Try to configure all the known programs that have not yet been configured.----configureAllKnownPrograms :: Verbosity- -> ProgramDb- -> IO ProgramDb+configureAllKnownPrograms+ :: Verbosity+ -> ProgramDb+ -> IO ProgramDb configureAllKnownPrograms verbosity progdb =- configurePrograms verbosity- [ prog | (prog,_,_) <- Map.elems notYetConfigured ] progdb+ configurePrograms+ verbosity+ [prog | (prog, _, _) <- Map.elems notYetConfigured]+ progdb where- notYetConfigured = unconfiguredProgs progdb- `Map.difference` configuredProgs progdb-+ notYetConfigured =+ unconfiguredProgs progdb+ `Map.difference` configuredProgs progdb -- | reconfigure a bunch of programs given new user-specified args. It takes -- the same inputs as 'userSpecifyPath' and 'userSpecifyArgs' and for all progs -- with a new path it calls 'configureProgram'.----reconfigurePrograms :: Verbosity- -> [(String, FilePath)]- -> [(String, [ProgArg])]- -> ProgramDb- -> IO ProgramDb+reconfigurePrograms+ :: Verbosity+ -> [(String, FilePath)]+ -> [(String, [ProgArg])]+ -> ProgramDb+ -> IO ProgramDb reconfigurePrograms verbosity paths argss progdb = do configurePrograms verbosity progs- . userSpecifyPaths paths- . userSpecifyArgss argss- $ progdb-+ . userSpecifyPaths paths+ . userSpecifyArgss argss+ $ progdb where- progs = catMaybes [ lookupKnownProgram name progdb | (name,_) <- paths ]-+ progs = catMaybes [lookupKnownProgram name progdb | (name, _) <- paths] -- | Check that a program is configured and available to be run. -- -- It raises an exception if the program could not be configured, otherwise -- it returns the configured program.----requireProgram :: Verbosity -> Program -> ProgramDb- -> IO (ConfiguredProgram, ProgramDb)+requireProgram+ :: Verbosity+ -> Program+ -> ProgramDb+ -> IO (ConfiguredProgram, ProgramDb) requireProgram verbosity prog progdb = do- mres <- needProgram verbosity prog progdb- case mres of- Nothing -> die' verbosity notFound- Just res -> return res- where- notFound = "The program '" ++ programName prog ++ "' is required but it could not be found."+ mres <- needProgram verbosity prog progdb+ case mres of+ Nothing -> dieWithException verbosity $ RequireProgram (programName prog)+ Just res -> return res -- | Check that a program is configured and available to be run. --@@ -434,17 +504,19 @@ -- or is not found. -- -- @since 3.0.1.0-needProgram :: Verbosity -> Program -> ProgramDb- -> IO (Maybe (ConfiguredProgram, ProgramDb))+needProgram+ :: Verbosity+ -> Program+ -> ProgramDb+ -> IO (Maybe (ConfiguredProgram, ProgramDb)) needProgram verbosity prog progdb = do- -- If it's not already been configured, try to configure it now progdb' <- case lookupProgram prog progdb of Nothing -> configureProgram verbosity prog progdb- Just _ -> return progdb+ Just _ -> return progdb case lookupProgram prog progdb' of- Nothing -> return Nothing+ Nothing -> return Nothing Just configuredProg -> return (Just (configuredProg, progdb')) -- | Check that a program is configured and available to be run.@@ -456,50 +528,39 @@ -- It returns the configured program, its version number and a possibly updated -- 'ProgramDb'. If the program could not be configured or the version is -- unsuitable, it returns an error value.--- lookupProgramVersion- :: Verbosity -> Program -> VersionRange -> ProgramDb- -> IO (Either String (ConfiguredProgram, Version, ProgramDb))+ :: Verbosity+ -> Program+ -> VersionRange+ -> ProgramDb+ -> IO (Either CabalException (ConfiguredProgram, Version, ProgramDb)) lookupProgramVersion verbosity prog range programDb = do- -- If it's not already been configured, try to configure it now programDb' <- case lookupProgram prog programDb of Nothing -> configureProgram verbosity prog programDb- Just _ -> return programDb+ Just _ -> return programDb case lookupProgram prog programDb' of- Nothing -> return $! Left notFound- Just configuredProg@ConfiguredProgram { programLocation = location } ->+ Nothing -> return $! Left $ NoProgramFound (programName prog) range+ Just configuredProg@ConfiguredProgram{programLocation = location} -> case programVersion configuredProg of Just version | withinRange version range ->- return $! Right (configuredProg, version ,programDb')- | otherwise ->- return $! Left (badVersion version location)- Nothing ->- return $! Left (unknownVersion location)-- where notFound = "The program '"- ++ programName prog ++ "'" ++ versionRequirement- ++ " is required but it could not be found."- badVersion v l = "The program '"- ++ programName prog ++ "'" ++ versionRequirement- ++ " is required but the version found at "- ++ locationPath l ++ " is version " ++ prettyShow v- unknownVersion l = "The program '"- ++ programName prog ++ "'" ++ versionRequirement- ++ " is required but the version of "- ++ locationPath l ++ " could not be determined."- versionRequirement- | isAnyVersion range = ""- | otherwise = " version " ++ prettyShow range+ return $! Right (configuredProg, version, programDb')+ | otherwise ->+ return $! Left $ BadVersionDb (programName prog) version range (locationPath location)+ Nothing ->+ return $! Left $ UnknownVersionDb (programName prog) range (locationPath location) -- | Like 'lookupProgramVersion', but raises an exception in case of error -- instead of returning 'Left errMsg'.----requireProgramVersion :: Verbosity -> Program -> VersionRange- -> ProgramDb- -> IO (ConfiguredProgram, Version, ProgramDb)+requireProgramVersion+ :: Verbosity+ -> Program+ -> VersionRange+ -> ProgramDb+ -> IO (ConfiguredProgram, Version, ProgramDb) requireProgramVersion verbosity prog range programDb =- join $ either (die' verbosity) return `fmap`- lookupProgramVersion verbosity prog range programDb+ join $+ either (dieWithException verbosity) return+ `fmap` lookupProgramVersion verbosity prog range programDb
src/Distribution/Simple/Program/Find.hs view
@@ -1,10 +1,9 @@ {-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Find -- Copyright : Duncan Coutts 2013@@ -24,139 +23,173 @@ -- So we need an extension of the usual 'findExecutable' that can look in -- additional locations, either before, after or instead of the normal OS -- locations.----module Distribution.Simple.Program.Find (- -- * Program search path- ProgramSearchPath,- ProgramSearchPathEntry(..),- defaultProgramSearchPath,- findProgramOnSearchPath,- programSearchPathAsPATHVar,- getSystemSearchPath,+module Distribution.Simple.Program.Find+ ( -- * Program search path+ ProgramSearchPath+ , ProgramSearchPathEntry (..)+ , defaultProgramSearchPath+ , findProgramOnSearchPath+ , programSearchPathAsPATHVar+ , logExtraProgramSearchPath+ , logExtraProgramOverrideEnv+ , getSystemSearchPath+ , getExtraPathEnv+ , simpleProgram ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -import Distribution.Verbosity+import Distribution.Compat.Environment+import Distribution.Simple.Program.Types import Distribution.Simple.Utils import Distribution.System-import Distribution.Compat.Environment+import Distribution.Verbosity import qualified System.Directory as Directory- ( findExecutable )+ ( findExecutable+ ) import System.FilePath as FilePath- ( (</>), (<.>), splitSearchPath, searchPathSeparator, getSearchPath- , takeDirectory )+ ( getSearchPath+ , searchPathSeparator+ , splitSearchPath+ , takeDirectory+ , (<.>)+ , (</>)+ ) #if defined(mingw32_HOST_OS) import qualified System.Win32 as Win32 #endif --- | A search path to use when locating executables. This is analogous--- to the unix @$PATH@ or win32 @%PATH%@ but with the ability to use--- the system default method for finding executables ('findExecutable' which--- on unix is simply looking on the @$PATH@ but on win32 is a bit more--- complicated).------ The default to use is @[ProgSearchPathDefault]@ but you can add extra dirs--- either before, after or instead of the default, e.g. here we add an extra--- dir to search after the usual ones.------ > ['ProgramSearchPathDefault', 'ProgramSearchPathDir' dir]----type ProgramSearchPath = [ProgramSearchPathEntry]-data ProgramSearchPathEntry =- ProgramSearchPathDir FilePath -- ^ A specific dir- | ProgramSearchPathDefault -- ^ The system default- deriving (Eq, Generic, Typeable)--instance Binary ProgramSearchPathEntry-instance Structured ProgramSearchPathEntry- defaultProgramSearchPath :: ProgramSearchPath defaultProgramSearchPath = [ProgramSearchPathDefault] -findProgramOnSearchPath :: Verbosity -> ProgramSearchPath- -> FilePath -> IO (Maybe (FilePath, [FilePath]))+logExtraProgramSearchPath+ :: Verbosity+ -> [FilePath]+ -> IO ()+logExtraProgramSearchPath verbosity extraPaths =+ info verbosity . unlines $+ "Including the following directories in PATH:"+ : map ("- " ++) extraPaths++logExtraProgramOverrideEnv+ :: Verbosity+ -> [(String, Maybe String)]+ -> IO ()+logExtraProgramOverrideEnv verbosity extraEnv =+ info verbosity . unlines $+ "Including the following environment variable overrides:"+ : [ "- " ++ case mbVal of+ Nothing -> "unset " ++ var+ Just val -> var ++ "=" ++ val+ | (var, mbVal) <- extraEnv+ ]++findProgramOnSearchPath+ :: Verbosity+ -> ProgramSearchPath+ -> FilePath+ -> IO (Maybe (FilePath, [FilePath])) findProgramOnSearchPath verbosity searchpath prog = do- debug verbosity $ "Searching for " ++ prog ++ " in path."- res <- tryPathElems [] searchpath- case res of- Nothing -> debug verbosity ("Cannot find " ++ prog ++ " on the path")- Just (path, _) -> debug verbosity ("Found " ++ prog ++ " at "++ path)- return res+ debug verbosity $ "Searching for " ++ prog ++ " in path."+ res <- tryPathElems [] searchpath+ case res of+ Nothing -> debug verbosity ("Cannot find " ++ prog ++ " on the path")+ Just (path, _) -> debug verbosity ("Found " ++ prog ++ " at " ++ path)+ return res where- tryPathElems :: [[FilePath]] -> [ProgramSearchPathEntry]- -> IO (Maybe (FilePath, [FilePath]))- tryPathElems _ [] = return Nothing- tryPathElems tried (pe:pes) = do+ tryPathElems+ :: [[FilePath]]+ -> [ProgramSearchPathEntry]+ -> IO (Maybe (FilePath, [FilePath]))+ tryPathElems _ [] = return Nothing+ tryPathElems tried (pe : pes) = do res <- tryPathElem pe case res of- (Nothing, notfoundat) -> tryPathElems (notfoundat : tried) pes+ (Nothing, notfoundat) -> tryPathElems (notfoundat : tried) pes (Just foundat, notfoundat) -> return (Just (foundat, alltried)) where alltried = concat (reverse (notfoundat : tried)) tryPathElem :: ProgramSearchPathEntry -> IO (Maybe FilePath, [FilePath]) tryPathElem (ProgramSearchPathDir dir) =- findFirstExe [ dir </> prog <.> ext | ext <- exeExtensions ]-+ findFirstExe [dir </> prog <.> ext | ext <- exeExtensions] -- On windows, getSystemSearchPath is not guaranteed 100% correct so we -- use findExecutable and then approximate the not-found-at locations. tryPathElem ProgramSearchPathDefault | buildOS == Windows = do- mExe <- firstJustM [ findExecutable (prog <.> ext) | ext <- exeExtensions ]+ mExe <- firstJustM [findExecutable (prog <.> ext) | ext <- exeExtensions] syspath <- getSystemSearchPath case mExe of Nothing ->- let notfoundat = [ dir </> prog | dir <- syspath ] in- return (Nothing, notfoundat)-+ let notfoundat = [dir </> prog | dir <- syspath]+ in return (Nothing, notfoundat) Just foundat -> do- let founddir = takeDirectory foundat- notfoundat = [ dir </> prog- | dir <- takeWhile (/= founddir) syspath ]+ let founddir = takeDirectory foundat+ notfoundat =+ [ dir </> prog+ | dir <- takeWhile (/= founddir) syspath+ ] return (Just foundat, notfoundat) -- On other OSs we can just do the simple thing tryPathElem ProgramSearchPathDefault = do dirs <- getSystemSearchPath- findFirstExe [ dir </> prog <.> ext | dir <- dirs, ext <- exeExtensions ]+ findFirstExe [dir </> prog <.> ext | dir <- dirs, ext <- exeExtensions] findFirstExe :: [FilePath] -> IO (Maybe FilePath, [FilePath]) findFirstExe = go [] where- go fs' [] = return (Nothing, reverse fs')- go fs' (f:fs) = do+ go fs' [] = return (Nothing, reverse fs')+ go fs' (f : fs) = do isExe <- doesExecutableExist f if isExe then return (Just f, reverse fs')- else go (f:fs') fs+ else go (f : fs') fs -- Helper for evaluating actions until the first one returns 'Just' firstJustM :: Monad m => [m (Maybe a)] -> m (Maybe a) firstJustM [] = return Nothing- firstJustM (ma:mas) = do+ firstJustM (ma : mas) = do a <- ma case a of- Just _ -> return a+ Just _ -> return a Nothing -> firstJustM mas +-- | Adds some paths to the "PATH" entry in the key-value environment provided+-- or if there is none, looks up @$PATH@ in the real environment.+getExtraPathEnv+ :: Verbosity+ -> [(String, Maybe String)]+ -> [FilePath]+ -> IO [(String, Maybe String)]+getExtraPathEnv _ _ [] = return []+getExtraPathEnv verbosity env extras = do+ mb_path <- case lookup "PATH" env of+ Just x -> return x+ Nothing -> lookupEnv "PATH"+ logExtraProgramSearchPath verbosity extras+ let extra = intercalate [searchPathSeparator] extras+ path' = case mb_path of+ Nothing -> extra+ Just path -> extra ++ searchPathSeparator : path+ return [("PATH", Just path')]+ -- | Interpret a 'ProgramSearchPath' to construct a new @$PATH@ env var. -- Note that this is close but not perfect because on Windows the search -- algorithm looks at more than just the @%PATH%@. programSearchPathAsPATHVar :: ProgramSearchPath -> IO String programSearchPathAsPATHVar searchpath = do- ess <- traverse getEntries searchpath- return (intercalate [searchPathSeparator] (concat ess))+ ess <- traverse getEntries searchpath+ return (intercalate [searchPathSeparator] (concat ess)) where getEntries (ProgramSearchPathDir dir) = return [dir]- getEntries ProgramSearchPathDefault = do+ getEntries ProgramSearchPathDefault = do env <- getEnvironment return (maybe [] splitSearchPath (lookup "PATH" env)) -- | Get the system search path. On Unix systems this is just the @$PATH@ env -- var, but on windows it's a bit more complicated.--- getSystemSearchPath :: IO [FilePath] getSystemSearchPath = fmap nub $ do #if defined(mingw32_HOST_OS)@@ -196,3 +229,18 @@ _ -> return mExe #endif +-- | Make a simple named program.+--+-- By default we'll just search for it in the path and not try to find the+-- version name. You can override these behaviours if necessary, eg:+--+-- > (simpleProgram "foo") { programFindLocation = ... , programFindVersion ... }+simpleProgram :: String -> Program+simpleProgram name =+ Program+ { programName = name+ , programFindLocation = \v p -> findProgramOnSearchPath v p name+ , programFindVersion = \_ _ -> return Nothing+ , programPostConf = \_ p -> return p+ , programNormaliseArgs = \_ _ -> id+ }
src/Distribution/Simple/Program/GHC.hs view
@@ -1,843 +1,1078 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE RecordWildCards #-}-{-# LANGUAGE ScopedTypeVariables #-}--module Distribution.Simple.Program.GHC (- GhcOptions(..),- GhcMode(..),- GhcOptimisation(..),- GhcDynLinkMode(..),- GhcProfAuto(..),-- ghcInvocation,- renderGhcOptions,-- runGHC,-- packageDbArgsDb,- normaliseGhcArgs-- ) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Backpack-import Distribution.Compat.Semigroup (First'(..), Last'(..), Option'(..))-import Distribution.Simple.GHC.ImplInfo-import Distribution.PackageDescription-import Distribution.ModuleName-import Distribution.Simple.Compiler-import Distribution.Simple.Flag-import Distribution.Simple.Program.Types-import Distribution.Simple.Program.Run-import Distribution.System-import Distribution.Pretty-import Distribution.Types.ComponentId-import Distribution.Verbosity-import Distribution.Version-import Distribution.Utils.NubList-import Language.Haskell.Extension--import Data.List (stripPrefix)-import qualified Data.Map as Map-import Data.Monoid (All(..), Any(..), Endo(..))-import qualified Data.Set as Set--normaliseGhcArgs :: Maybe Version -> PackageDescription -> [String] -> [String]-normaliseGhcArgs (Just ghcVersion) PackageDescription{..} ghcArgs- | ghcVersion `withinRange` supportedGHCVersions- = argumentFilters . filter simpleFilters . filterRtsOpts $ ghcArgs- where- supportedGHCVersions :: VersionRange- supportedGHCVersions = intersectVersionRanges- (orLaterVersion (mkVersion [8,0]))- (earlierVersion (mkVersion [9,1]))-- from :: Monoid m => [Int] -> m -> m- from version flags- | ghcVersion `withinRange` orLaterVersion (mkVersion version) = flags- | otherwise = mempty-- to :: Monoid m => [Int] -> m -> m- to version flags- | ghcVersion `withinRange` earlierVersion (mkVersion version) = flags- | otherwise = mempty-- checkGhcFlags :: forall m . Monoid m => ([String] -> m) -> m- checkGhcFlags fun = mconcat- [ fun ghcArgs- , checkComponentFlags libBuildInfo pkgLibs- , checkComponentFlags buildInfo executables- , checkComponentFlags testBuildInfo testSuites- , checkComponentFlags benchmarkBuildInfo benchmarks- ]- where- pkgLibs = maybeToList library ++ subLibraries-- checkComponentFlags :: (a -> BuildInfo) -> [a] -> m- checkComponentFlags getInfo = foldMap (checkComponent . getInfo)- where- checkComponent :: BuildInfo -> m- checkComponent = foldMap fun . filterGhcOptions . allGhcOptions-- allGhcOptions :: BuildInfo -> [(CompilerFlavor, [String])]- allGhcOptions = foldMap (perCompilerFlavorToList .)- [options, profOptions, sharedOptions, staticOptions]-- filterGhcOptions :: [(CompilerFlavor, [String])] -> [[String]]- filterGhcOptions l = [opts | (GHC, opts) <- l]-- safeToFilterWarnings :: Bool- safeToFilterWarnings = getAll $ checkGhcFlags checkWarnings- where- checkWarnings :: [String] -> All- checkWarnings = All . Set.null . foldr alter Set.empty-- alter :: String -> Set String -> Set String- alter flag = appEndo $ mconcat- [ \s -> Endo $ if s == "-Werror" then Set.insert s else id- , \s -> Endo $ if s == "-Wwarn" then const Set.empty else id- , \s -> from [8,6] . Endo $- if s == "-Werror=compat"- then Set.union compatWarningSet else id- , \s -> from [8,6] . Endo $- if s == "-Wno-error=compat"- then (`Set.difference` compatWarningSet) else id- , \s -> from [8,6] . Endo $- if s == "-Wwarn=compat"- then (`Set.difference` compatWarningSet) else id- , from [8,4] $ markFlag "-Werror=" Set.insert- , from [8,4] $ markFlag "-Wwarn=" Set.delete- , from [8,4] $ markFlag "-Wno-error=" Set.delete- ] flag-- markFlag- :: String- -> (String -> Set String -> Set String)- -> String- -> Endo (Set String)- markFlag name update flag = Endo $ case stripPrefix name flag of- Just rest | not (null rest) && rest /= "compat" -> update rest- _ -> id-- flagArgumentFilter :: [String] -> [String] -> [String]- flagArgumentFilter flags = go- where- makeFilter :: String -> String -> Option' (First' ([String] -> [String]))- makeFilter flag arg = Option' $ First' . filterRest <$> stripPrefix flag arg- where- filterRest leftOver = case dropEq leftOver of- [] -> drop 1- _ -> id-- checkFilter :: String -> Maybe ([String] -> [String])- checkFilter = fmap getFirst' . getOption' . foldMap makeFilter flags-- go :: [String] -> [String]- go [] = []- go (arg:args) = case checkFilter arg of- Just f -> go (f args)- Nothing -> arg : go args-- argumentFilters :: [String] -> [String]- argumentFilters = flagArgumentFilter- ["-ghci-script", "-H", "-interactive-print"]-- filterRtsOpts :: [String] -> [String]- filterRtsOpts = go False- where- go :: Bool -> [String] -> [String]- go _ [] = []- go _ ("+RTS":opts) = go True opts- go _ ("-RTS":opts) = go False opts- go isRTSopts (opt:opts) = addOpt $ go isRTSopts opts- where- addOpt | isRTSopts = id- | otherwise = (opt:)-- simpleFilters :: String -> Bool- simpleFilters = not . getAny . mconcat- [ flagIn simpleFlags- , Any . isPrefixOf "-ddump-"- , Any . isPrefixOf "-dsuppress-"- , Any . isPrefixOf "-dno-suppress-"- , flagIn $ invertibleFlagSet "-" ["ignore-dot-ghci"]- , flagIn . invertibleFlagSet "-f" . mconcat $- [ [ "reverse-errors", "warn-unused-binds", "break-on-error"- , "break-on-exception", "print-bind-result"- , "print-bind-contents", "print-evld-with-show"- , "implicit-import-qualified", "error-spans"- ]- , from [7,8]- [ "print-explicit-foralls" -- maybe also earlier, but GHC-7.6 doesn't have --show-options- , "print-explicit-kinds"- ]- , from [8,0]- [ "print-explicit-coercions"- , "print-explicit-runtime-reps"- , "print-equality-relations"- , "print-unicode-syntax"- , "print-expanded-synonyms"- , "print-potential-instances"- , "print-typechecker-elaboration"- ]- , from [8,2]- [ "diagnostics-show-caret", "local-ghci-history"- , "show-warning-groups", "hide-source-paths"- , "show-hole-constraints"- ]- , from [8,4] ["show-loaded-modules"]- , from [8,6] [ "ghci-leak-check", "no-it" ]- , from [8,10]- [ "defer-diagnostics" -- affects printing of diagnostics- , "keep-going" -- try harder, the build will still fail if it's erroneous- , "print-axiom-incomps" -- print more debug info for closed type families- ]- ]- , flagIn . invertibleFlagSet "-d" $ [ "ppr-case-as-let", "ppr-ticks" ]- , isOptIntFlag- , isIntFlag- , if safeToFilterWarnings- then isWarning <> (Any . ("-w"==))- else mempty- , from [8,6] $- if safeToFilterHoles- then isTypedHoleFlag- else mempty- ]-- flagIn :: Set String -> String -> Any- flagIn set flag = Any $ Set.member flag set-- isWarning :: String -> Any- isWarning = mconcat $ map ((Any .) . isPrefixOf)- ["-fwarn-", "-fno-warn-", "-W", "-Wno-"]-- simpleFlags :: Set String- simpleFlags = Set.fromList . mconcat $- [ [ "-n", "-#include", "-Rghc-timing", "-dstg-stats"- , "-dth-dec-file", "-dsource-stats", "-dverbose-core2core"- , "-dverbose-stg2stg", "-dcore-lint", "-dstg-lint", "-dcmm-lint"- , "-dasm-lint", "-dannot-lint", "-dshow-passes", "-dfaststring-stats"- , "-fno-max-relevant-binds", "-recomp", "-no-recomp", "-fforce-recomp"- , "-fno-force-recomp"- ]-- , from [8,2]- [ "-fno-max-errors", "-fdiagnostics-color=auto"- , "-fdiagnostics-color=always", "-fdiagnostics-color=never"- , "-dppr-debug", "-dno-debug-output"- ]-- , from [8,4] [ "-ddebug-output" ]- , from [8,4] $ to [8,6] [ "-fno-max-valid-substitutions" ]- , from [8,6] [ "-dhex-word-literals" ]- , from [8,8] [ "-fshow-docs-of-hole-fits", "-fno-show-docs-of-hole-fits" ]- , from [9,0] [ "-dlinear-core-lint" ]- ]-- isOptIntFlag :: String -> Any- isOptIntFlag = mconcat . map (dropIntFlag True) $ ["-v", "-j"]-- isIntFlag :: String -> Any- isIntFlag = mconcat . map (dropIntFlag False) . mconcat $- [ [ "-fmax-relevant-binds", "-ddpr-user-length", "-ddpr-cols"- , "-dtrace-level", "-fghci-hist-size" ]- , from [8,2] ["-fmax-uncovered-patterns", "-fmax-errors"]- , from [8,4] $ to [8,6] ["-fmax-valid-substitutions"]- ]-- dropIntFlag :: Bool -> String -> String -> Any- dropIntFlag isOpt flag input = Any $ case stripPrefix flag input of- Nothing -> False- Just rest | isOpt && null rest -> True- | otherwise -> case parseInt rest of- Just _ -> True- Nothing -> False- where- parseInt :: String -> Maybe Int- parseInt = readMaybe . dropEq-- dropEq :: String -> String- dropEq ('=':s) = s- dropEq s = s-- invertibleFlagSet :: String -> [String] -> Set String- invertibleFlagSet prefix flagNames =- Set.fromList $ (++) <$> [prefix, prefix ++ "no-"] <*> flagNames-- compatWarningSet :: Set String- compatWarningSet = Set.fromList $ mconcat- [ from [8,6]- [ "missing-monadfail-instances", "semigroup"- , "noncanonical-monoid-instances", "implicit-kind-vars" ]- ]-- safeToFilterHoles :: Bool- safeToFilterHoles = getAll . checkGhcFlags $- All . fromMaybe True . fmap getLast' . getOption' . foldMap notDeferred- where- notDeferred :: String -> Option' (Last' Bool)- notDeferred "-fdefer-typed-holes" = Option' . Just . Last' $ False- notDeferred "-fno-defer-typed-holes" = Option' . Just . Last' $ True- notDeferred _ = Option' Nothing-- isTypedHoleFlag :: String -> Any- isTypedHoleFlag = mconcat- [ flagIn . invertibleFlagSet "-f" $- [ "show-hole-constraints", "show-valid-substitutions"- , "show-valid-hole-fits", "sort-valid-hole-fits"- , "sort-by-size-hole-fits", "sort-by-subsumption-hole-fits"- , "abstract-refinement-hole-fits", "show-provenance-of-hole-fits"- , "show-hole-matches-of-hole-fits", "show-type-of-hole-fits"- , "show-type-app-of-hole-fits", "show-type-app-vars-of-hole-fits"- , "unclutter-valid-hole-fits"- ]- , flagIn . Set.fromList $- [ "-fno-max-valid-hole-fits", "-fno-max-refinement-hole-fits"- , "-fno-refinement-level-hole-fits" ]- , mconcat . map (dropIntFlag False) $- [ "-fmax-valid-hole-fits", "-fmax-refinement-hole-fits"- , "-frefinement-level-hole-fits" ]- ]--normaliseGhcArgs _ _ args = args---- | A structured set of GHC options/flags------ Note that options containing lists fall into two categories:------ * options that can be safely deduplicated, e.g. input modules or--- enabled extensions;--- * options that cannot be deduplicated in general without changing--- semantics, e.g. extra ghc options or linking options.-data GhcOptions = GhcOptions {-- -- | The major mode for the ghc invocation.- ghcOptMode :: Flag GhcMode,-- -- | Any extra options to pass directly to ghc. These go at the end and hence- -- override other stuff.- ghcOptExtra :: [String],-- -- | Extra default flags to pass directly to ghc. These go at the beginning- -- and so can be overridden by other stuff.- ghcOptExtraDefault :: [String],-- ------------------------ -- Inputs and outputs-- -- | The main input files; could be .hs, .hi, .c, .o, depending on mode.- ghcOptInputFiles :: NubListR FilePath,-- -- | The names of input Haskell modules, mainly for @--make@ mode.- ghcOptInputModules :: NubListR ModuleName,-- -- | Location for output file; the @ghc -o@ flag.- ghcOptOutputFile :: Flag FilePath,-- -- | Location for dynamic output file in 'GhcStaticAndDynamic' mode;- -- the @ghc -dyno@ flag.- ghcOptOutputDynFile :: Flag FilePath,-- -- | Start with an empty search path for Haskell source files;- -- the @ghc -i@ flag (@-i@ on its own with no path argument).- ghcOptSourcePathClear :: Flag Bool,-- -- | Search path for Haskell source files; the @ghc -i@ flag.- ghcOptSourcePath :: NubListR FilePath,-- -------------- -- Packages-- -- | The unit ID the modules will belong to; the @ghc -this-unit-id@- -- flag (or @-this-package-key@ or @-package-name@ on older- -- versions of GHC). This is a 'String' because we assume you've- -- already figured out what the correct format for this string is- -- (we need to handle backwards compatibility.)- ghcOptThisUnitId :: Flag String,-- -- | GHC doesn't make any assumptions about the format of- -- definite unit ids, so when we are instantiating a package it- -- needs to be told explicitly what the component being instantiated- -- is. This only gets set when 'ghcOptInstantiatedWith' is non-empty- ghcOptThisComponentId :: Flag ComponentId,-- -- | How the requirements of the package being compiled are to- -- be filled. When typechecking an indefinite package, the 'OpenModule'- -- is always a 'OpenModuleVar'; otherwise, it specifies the installed module- -- that instantiates a package.- ghcOptInstantiatedWith :: [(ModuleName, OpenModule)],-- -- | No code? (But we turn on interface writing- ghcOptNoCode :: Flag Bool,-- -- | GHC package databases to use, the @ghc -package-conf@ flag.- ghcOptPackageDBs :: PackageDBStack,-- -- | The GHC packages to bring into scope when compiling,- -- the @ghc -package-id@ flags.- ghcOptPackages ::- NubListR (OpenUnitId, ModuleRenaming),-- -- | Start with a clean package set; the @ghc -hide-all-packages@ flag- ghcOptHideAllPackages :: Flag Bool,-- -- | Warn about modules, not listed in command line- ghcOptWarnMissingHomeModules :: Flag Bool,-- -- | Don't automatically link in Haskell98 etc; the @ghc- -- -no-auto-link-packages@ flag.- ghcOptNoAutoLinkPackages :: Flag Bool,-- ------------------ -- Linker stuff-- -- | Names of libraries to link in; the @ghc -l@ flag.- ghcOptLinkLibs :: [FilePath],-- -- | Search path for libraries to link in; the @ghc -L@ flag.- ghcOptLinkLibPath :: NubListR FilePath,-- -- | Options to pass through to the linker; the @ghc -optl@ flag.- ghcOptLinkOptions :: [String],-- -- | OSX only: frameworks to link in; the @ghc -framework@ flag.- ghcOptLinkFrameworks :: NubListR String,-- -- | OSX only: Search path for frameworks to link in; the- -- @ghc -framework-path@ flag.- ghcOptLinkFrameworkDirs :: NubListR String,-- -- | Don't do the link step, useful in make mode; the @ghc -no-link@ flag.- ghcOptNoLink :: Flag Bool,-- -- | Don't link in the normal RTS @main@ entry point; the @ghc -no-hs-main@- -- flag.- ghcOptLinkNoHsMain :: Flag Bool,-- -- | Module definition files (Windows specific)- ghcOptLinkModDefFiles :: NubListR FilePath,-- --------------------- -- C and CPP stuff-- -- | Options to pass through to the C compiler; the @ghc -optc@ flag.- ghcOptCcOptions :: [String],-- -- | Options to pass through to the C++ compiler.- ghcOptCxxOptions :: [String],-- -- | Options to pass through to the Assembler.- ghcOptAsmOptions :: [String],-- -- | Options to pass through to CPP; the @ghc -optP@ flag.- ghcOptCppOptions :: [String],-- -- | Search path for CPP includes like header files; the @ghc -I@ flag.- ghcOptCppIncludePath :: NubListR FilePath,-- -- | Extra header files to include at CPP stage; the @ghc -optP-include@ flag.- ghcOptCppIncludes :: NubListR FilePath,-- -- | Extra header files to include for old-style FFI; the @ghc -#include@ flag.- ghcOptFfiIncludes :: NubListR FilePath,-- ----------------------------- -- Language and extensions-- -- | The base language; the @ghc -XHaskell98@ or @-XHaskell2010@ flag.- ghcOptLanguage :: Flag Language,-- -- | The language extensions; the @ghc -X@ flag.- ghcOptExtensions :: NubListR Extension,-- -- | A GHC version-dependent mapping of extensions to flags. This must be- -- set to be able to make use of the 'ghcOptExtensions'.- ghcOptExtensionMap :: Map Extension (Maybe CompilerFlag),-- ----------------- -- Compilation-- -- | What optimisation level to use; the @ghc -O@ flag.- ghcOptOptimisation :: Flag GhcOptimisation,-- -- | Emit debug info; the @ghc -g@ flag.- ghcOptDebugInfo :: Flag DebugInfoLevel,-- -- | Compile in profiling mode; the @ghc -prof@ flag.- ghcOptProfilingMode :: Flag Bool,-- -- | Automatically add profiling cost centers; the @ghc -fprof-auto*@ flags.- ghcOptProfilingAuto :: Flag GhcProfAuto,-- -- | Use the \"split sections\" feature; the @ghc -split-sections@ flag.- ghcOptSplitSections :: Flag Bool,-- -- | Use the \"split object files\" feature; the @ghc -split-objs@ flag.- ghcOptSplitObjs :: Flag Bool,-- -- | Run N jobs simultaneously (if possible).- ghcOptNumJobs :: Flag (Maybe Int),-- -- | Enable coverage analysis; the @ghc -fhpc -hpcdir@ flags.- ghcOptHPCDir :: Flag FilePath,-- ----------------- -- GHCi-- -- | Extra GHCi startup scripts; the @-ghci-script@ flag- ghcOptGHCiScripts :: [FilePath],-- ------------------------- -- Redirecting outputs-- ghcOptHiSuffix :: Flag String,- ghcOptObjSuffix :: Flag String,- ghcOptDynHiSuffix :: Flag String, -- ^ only in 'GhcStaticAndDynamic' mode- ghcOptDynObjSuffix :: Flag String, -- ^ only in 'GhcStaticAndDynamic' mode- ghcOptHiDir :: Flag FilePath,- ghcOptObjDir :: Flag FilePath,- ghcOptOutputDir :: Flag FilePath,- ghcOptStubDir :: Flag FilePath,-- --------------------- -- Creating libraries-- ghcOptDynLinkMode :: Flag GhcDynLinkMode,- ghcOptStaticLib :: Flag Bool,- ghcOptShared :: Flag Bool,- ghcOptFPic :: Flag Bool,- ghcOptDylibName :: Flag String,- ghcOptRPaths :: NubListR FilePath,-- ---------------- -- Misc flags-- -- | Get GHC to be quiet or verbose with what it's doing; the @ghc -v@ flag.- ghcOptVerbosity :: Flag Verbosity,-- -- | Put the extra folders in the PATH environment variable we invoke- -- GHC with- ghcOptExtraPath :: NubListR FilePath,-- -- | Let GHC know that it is Cabal that's calling it.- -- Modifies some of the GHC error messages.- ghcOptCabal :: Flag Bool--} deriving (Show, Generic)---data GhcMode = GhcModeCompile -- ^ @ghc -c@- | GhcModeLink -- ^ @ghc@- | GhcModeMake -- ^ @ghc --make@- | GhcModeInteractive -- ^ @ghci@ \/ @ghc --interactive@- | GhcModeAbiHash -- ^ @ghc --abi-hash@--- | GhcModeDepAnalysis -- ^ @ghc -M@--- | GhcModeEvaluate -- ^ @ghc -e@- deriving (Show, Eq)--data GhcOptimisation = GhcNoOptimisation -- ^ @-O0@- | GhcNormalOptimisation -- ^ @-O@- | GhcMaximumOptimisation -- ^ @-O2@- | GhcSpecialOptimisation String -- ^ e.g. @-Odph@- deriving (Show, Eq)--data GhcDynLinkMode = GhcStaticOnly -- ^ @-static@- | GhcDynamicOnly -- ^ @-dynamic@- | GhcStaticAndDynamic -- ^ @-static -dynamic-too@- deriving (Show, Eq)--data GhcProfAuto = GhcProfAutoAll -- ^ @-fprof-auto@- | GhcProfAutoToplevel -- ^ @-fprof-auto-top@- | GhcProfAutoExported -- ^ @-fprof-auto-exported@- deriving (Show, Eq)--runGHC :: Verbosity -> ConfiguredProgram -> Compiler -> Platform -> GhcOptions- -> IO ()-runGHC verbosity ghcProg comp platform opts = do- runProgramInvocation verbosity (ghcInvocation ghcProg comp platform opts)---ghcInvocation :: ConfiguredProgram -> Compiler -> Platform -> GhcOptions- -> ProgramInvocation-ghcInvocation prog comp platform opts =- (programInvocation prog (renderGhcOptions comp platform opts)) {- progInvokePathEnv = fromNubListR (ghcOptExtraPath opts)- }--renderGhcOptions :: Compiler -> Platform -> GhcOptions -> [String]-renderGhcOptions comp _platform@(Platform _arch os) opts- | compilerFlavor comp `notElem` [GHC, GHCJS] =- error $ "Distribution.Simple.Program.GHC.renderGhcOptions: "- ++ "compiler flavor must be 'GHC' or 'GHCJS'!"- | otherwise =- concat- [ case flagToMaybe (ghcOptMode opts) of- Nothing -> []- Just GhcModeCompile -> ["-c"]- Just GhcModeLink -> []- Just GhcModeMake -> ["--make"]- Just GhcModeInteractive -> ["--interactive"]- Just GhcModeAbiHash -> ["--abi-hash"]--- Just GhcModeDepAnalysis -> ["-M"]--- Just GhcModeEvaluate -> ["-e", expr]-- , ghcOptExtraDefault opts-- , [ "-no-link" | flagBool ghcOptNoLink ]-- ---------------- -- Misc flags-- , maybe [] verbosityOpts (flagToMaybe (ghcOptVerbosity opts))-- , [ "-fbuilding-cabal-package" | flagBool ghcOptCabal ]-- ----------------- -- Compilation-- , case flagToMaybe (ghcOptOptimisation opts) of- Nothing -> []- Just GhcNoOptimisation -> ["-O0"]- Just GhcNormalOptimisation -> ["-O"]- Just GhcMaximumOptimisation -> ["-O2"]- Just (GhcSpecialOptimisation s) -> ["-O" ++ s] -- eg -Odph-- , case flagToMaybe (ghcOptDebugInfo opts) of- Nothing -> []- Just NoDebugInfo -> []- Just MinimalDebugInfo -> ["-g1"]- Just NormalDebugInfo -> ["-g2"]- Just MaximalDebugInfo -> ["-g3"]-- , [ "-prof" | flagBool ghcOptProfilingMode ]-- , case flagToMaybe (ghcOptProfilingAuto opts) of- _ | not (flagBool ghcOptProfilingMode)- -> []- Nothing -> []- Just GhcProfAutoAll- | flagProfAuto implInfo -> ["-fprof-auto"]- | otherwise -> ["-auto-all"] -- not the same, but close- Just GhcProfAutoToplevel- | flagProfAuto implInfo -> ["-fprof-auto-top"]- | otherwise -> ["-auto-all"]- Just GhcProfAutoExported- | flagProfAuto implInfo -> ["-fprof-auto-exported"]- | otherwise -> ["-auto"]-- , [ "-split-sections" | flagBool ghcOptSplitSections ]- , [ "-split-objs" | flagBool ghcOptSplitObjs ]-- , case flagToMaybe (ghcOptHPCDir opts) of- Nothing -> []- Just hpcdir -> ["-fhpc", "-hpcdir", hpcdir]-- , if parmakeSupported comp- then case ghcOptNumJobs opts of- NoFlag -> []- Flag n -> ["-j" ++ maybe "" show n]- else []-- --------------------- -- Creating libraries-- , [ "-staticlib" | flagBool ghcOptStaticLib ]- , [ "-shared" | flagBool ghcOptShared ]- , case flagToMaybe (ghcOptDynLinkMode opts) of- Nothing -> []- Just GhcStaticOnly -> ["-static"]- Just GhcDynamicOnly -> ["-dynamic"]- Just GhcStaticAndDynamic -> ["-static", "-dynamic-too"]- , [ "-fPIC" | flagBool ghcOptFPic ]-- , concat [ ["-dylib-install-name", libname] | libname <- flag ghcOptDylibName ]-- ------------------------- -- Redirecting outputs-- , concat [ ["-osuf", suf] | suf <- flag ghcOptObjSuffix ]- , concat [ ["-hisuf", suf] | suf <- flag ghcOptHiSuffix ]- , concat [ ["-dynosuf", suf] | suf <- flag ghcOptDynObjSuffix ]- , concat [ ["-dynhisuf",suf] | suf <- flag ghcOptDynHiSuffix ]- , concat [ ["-outputdir", dir] | dir <- flag ghcOptOutputDir ]- , concat [ ["-odir", dir] | dir <- flag ghcOptObjDir ]- , concat [ ["-hidir", dir] | dir <- flag ghcOptHiDir ]- , concat [ ["-stubdir", dir] | dir <- flag ghcOptStubDir ]-- ------------------------ -- Source search path-- , [ "-i" | flagBool ghcOptSourcePathClear ]- , [ "-i" ++ dir | dir <- flags ghcOptSourcePath ]-- ---------------------- --------------------- -- CPP, C, and C++ stuff-- , [ "-I" ++ dir | dir <- flags ghcOptCppIncludePath ]- , [ "-optP" ++ opt | opt <- ghcOptCppOptions opts]- , concat [ [ "-optP-include", "-optP" ++ inc]- | inc <- flags ghcOptCppIncludes ]- , [ "-optc" ++ opt | opt <- ghcOptCcOptions opts]- , -- C++ compiler options: GHC >= 8.10 requires -optcxx, older requires -optc- let cxxflag = case compilerCompatVersion GHC comp of- Just v | v >= mkVersion [8, 10] -> "-optcxx"- _ -> "-optc"- in [ cxxflag ++ opt | opt <- ghcOptCxxOptions opts]- , [ "-opta" ++ opt | opt <- ghcOptAsmOptions opts]-- ------------------ -- Linker stuff-- , [ "-optl" ++ opt | opt <- ghcOptLinkOptions opts]- , ["-l" ++ lib | lib <- ghcOptLinkLibs opts]- , ["-L" ++ dir | dir <- flags ghcOptLinkLibPath ]- , if isOSX- then concat [ ["-framework", fmwk]- | fmwk <- flags ghcOptLinkFrameworks ]- else []- , if isOSX- then concat [ ["-framework-path", path]- | path <- flags ghcOptLinkFrameworkDirs ]- else []- , [ "-no-hs-main" | flagBool ghcOptLinkNoHsMain ]- , [ "-dynload deploy" | not (null (flags ghcOptRPaths)) ]- , concat [ [ "-optl-Wl,-rpath," ++ dir]- | dir <- flags ghcOptRPaths ]- , [ modDefFile | modDefFile <- flags ghcOptLinkModDefFiles ]-- -------------- -- Packages-- , concat [ [ case () of- _ | unitIdSupported comp -> "-this-unit-id"- | packageKeySupported comp -> "-this-package-key"- | otherwise -> "-package-name"- , this_arg ]- | this_arg <- flag ghcOptThisUnitId ]-- , concat [ ["-this-component-id", prettyShow this_cid ]- | this_cid <- flag ghcOptThisComponentId ]-- , if null (ghcOptInstantiatedWith opts)- then []- else "-instantiated-with"- : intercalate "," (map (\(n,m) -> prettyShow n ++ "="- ++ prettyShow m)- (ghcOptInstantiatedWith opts))- : []-- , concat [ ["-fno-code", "-fwrite-interface"] | flagBool ghcOptNoCode ]-- , [ "-hide-all-packages" | flagBool ghcOptHideAllPackages ]- , [ "-Wmissing-home-modules" | flagBool ghcOptWarnMissingHomeModules ]- , [ "-no-auto-link-packages" | flagBool ghcOptNoAutoLinkPackages ]-- , packageDbArgs implInfo (ghcOptPackageDBs opts)-- , concat $ let space "" = ""- space xs = ' ' : xs- in [ ["-package-id", prettyShow ipkgid ++ space (prettyShow rns)]- | (ipkgid,rns) <- flags ghcOptPackages ]-- ----------------------------- -- Language and extensions-- , if supportsHaskell2010 implInfo- then [ "-X" ++ prettyShow lang | lang <- flag ghcOptLanguage ]- else []-- , [ ext'- | ext <- flags ghcOptExtensions- , ext' <- case Map.lookup ext (ghcOptExtensionMap opts) of- Just (Just arg) -> [arg]- Just Nothing -> []- Nothing ->- error $ "Distribution.Simple.Program.GHC.renderGhcOptions: "- ++ prettyShow ext ++ " not present in ghcOptExtensionMap."- ]-- ----------------- -- GHCi-- , concat [ [ "-ghci-script", script ] | script <- ghcOptGHCiScripts opts- , flagGhciScript implInfo ]-- ---------------- -- Inputs-- -- Specify the input file(s) first, so that in ghci the `main-is` module is- -- in scope instead of the first module defined in `other-modules`.- , flags ghcOptInputFiles- , [ prettyShow modu | modu <- flags ghcOptInputModules ]-- , concat [ [ "-o", out] | out <- flag ghcOptOutputFile ]- , concat [ [ "-dyno", out] | out <- flag ghcOptOutputDynFile ]-- ---------------- -- Extra-- , ghcOptExtra opts-- ]--- where- implInfo = getImplInfo comp- isOSX = os == OSX- flag flg = flagToList (flg opts)- flags flg = fromNubListR . flg $ opts- flagBool flg = fromFlagOrDefault False (flg opts)--verbosityOpts :: Verbosity -> [String]-verbosityOpts verbosity- | verbosity >= deafening = ["-v"]- | verbosity >= normal = []- | otherwise = ["-w", "-v0"]----- | GHC <7.6 uses '-package-conf' instead of '-package-db'.-packageDbArgsConf :: PackageDBStack -> [String]-packageDbArgsConf dbstack = case dbstack of- (GlobalPackageDB:UserPackageDB:dbs) -> concatMap specific dbs- (GlobalPackageDB:dbs) -> ("-no-user-package-conf")- : concatMap specific dbs- _ -> ierror- where- specific (SpecificPackageDB db) = [ "-package-conf", db ]- specific _ = ierror- ierror = error $ "internal error: unexpected package db stack: "- ++ show dbstack---- | GHC >= 7.6 uses the '-package-db' flag. See--- https://gitlab.haskell.org/ghc/ghc/-/issues/5977.-packageDbArgsDb :: PackageDBStack -> [String]--- special cases to make arguments prettier in common scenarios-packageDbArgsDb dbstack = case dbstack of- (GlobalPackageDB:UserPackageDB:dbs)- | all isSpecific dbs -> concatMap single dbs- (GlobalPackageDB:dbs)- | all isSpecific dbs -> "-no-user-package-db"- : concatMap single dbs- dbs -> "-clear-package-db"- : concatMap single dbs- where- single (SpecificPackageDB db) = [ "-package-db", db ]- single GlobalPackageDB = [ "-global-package-db" ]- single UserPackageDB = [ "-user-package-db" ]- isSpecific (SpecificPackageDB _) = True- isSpecific _ = False--packageDbArgs :: GhcImplInfo -> PackageDBStack -> [String]-packageDbArgs implInfo- | flagPackageConf implInfo = packageDbArgsConf- | otherwise = packageDbArgsDb+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}++module Distribution.Simple.Program.GHC+ ( GhcOptions (..)+ , GhcMode (..)+ , GhcOptimisation (..)+ , GhcDynLinkMode (..)+ , GhcProfAuto (..)+ , ghcInvocation+ , renderGhcOptions+ , runGHC+ , runGHCWithResponseFile+ , runReplProgram+ , packageDbArgsDb+ , normaliseGhcArgs+ ) where++import Distribution.Compat.Prelude+import Prelude ()++import Distribution.Backpack+import Distribution.Compat.Semigroup (First' (..), Last' (..), Option' (..))+import Distribution.ModuleName+import Distribution.PackageDescription+import Distribution.Pretty+import Distribution.Simple.Compiler+import Distribution.Simple.Flag+import Distribution.Simple.GHC.ImplInfo+import Distribution.Simple.Program.Find (getExtraPathEnv)+import Distribution.Simple.Program.ResponseFile+import Distribution.Simple.Program.Run+import Distribution.Simple.Program.Types+import Distribution.Simple.Utils (TempFileOptions, infoNoWrap)+import Distribution.System+import Distribution.Types.ComponentId+import Distribution.Types.ParStrat+import Distribution.Utils.NubList+import Distribution.Utils.Path+import Distribution.Verbosity+import Distribution.Version++import GHC.IO.Encoding (TextEncoding)+import Language.Haskell.Extension++import Data.List (stripPrefix)+import qualified Data.Map as Map+import Data.Monoid (All (..), Any (..), Endo (..))+import qualified Data.Set as Set+import qualified System.Process as Process++normaliseGhcArgs :: Maybe Version -> PackageDescription -> [String] -> [String]+normaliseGhcArgs (Just ghcVersion) PackageDescription{..} ghcArgs+ | ghcVersion `withinRange` supportedGHCVersions =+ argumentFilters . filter simpleFilters . filterRtsArgs $ ghcArgs+ where+ supportedGHCVersions :: VersionRange+ supportedGHCVersions = orLaterVersion (mkVersion [8, 0])+ -- we (weakly) support unknown future GHC versions for the purpose+ -- of filtering GHC arguments++ from :: Monoid m => [Int] -> m -> m+ from version flags+ | ghcVersion `withinRange` orLaterVersion (mkVersion version) = flags+ | otherwise = mempty++ to :: Monoid m => [Int] -> m -> m+ to version flags+ | ghcVersion `withinRange` earlierVersion (mkVersion version) = flags+ | otherwise = mempty++ checkGhcFlags :: forall m. Monoid m => ([String] -> m) -> m+ checkGhcFlags fun =+ mconcat+ [ fun ghcArgs+ , checkComponentFlags libBuildInfo pkgLibs+ , checkComponentFlags buildInfo executables+ , checkComponentFlags testBuildInfo testSuites+ , checkComponentFlags benchmarkBuildInfo benchmarks+ ]+ where+ pkgLibs = maybeToList library ++ subLibraries++ checkComponentFlags :: (a -> BuildInfo) -> [a] -> m+ checkComponentFlags getInfo = foldMap (checkComponent . getInfo)+ where+ checkComponent :: BuildInfo -> m+ checkComponent = foldMap fun . filterGhcOptions . allGhcOptions++ allGhcOptions :: BuildInfo -> [(CompilerFlavor, [String])]+ allGhcOptions =+ foldMap+ (perCompilerFlavorToList .)+ [options, profOptions, sharedOptions, staticOptions]++ filterGhcOptions :: [(CompilerFlavor, [String])] -> [[String]]+ filterGhcOptions l = [opts | (GHC, opts) <- l]++ safeToFilterWarnings :: Bool+ safeToFilterWarnings = getAll $ checkGhcFlags checkWarnings+ where+ checkWarnings :: [String] -> All+ checkWarnings = All . Set.null . foldr alter Set.empty++ alter :: String -> Set String -> Set String+ alter flag =+ appEndo $+ mconcat+ [ \s -> Endo $ if s == "-Werror" then Set.insert s else id+ , \s -> Endo $ if s == "-Wwarn" then const Set.empty else id+ , \s ->+ from [8, 6] . Endo $+ if s == "-Werror=compat"+ then Set.union compatWarningSet+ else id+ , \s ->+ from [8, 6] . Endo $+ if s == "-Wno-error=compat"+ then (`Set.difference` compatWarningSet)+ else id+ , \s ->+ from [8, 6] . Endo $+ if s == "-Wwarn=compat"+ then (`Set.difference` compatWarningSet)+ else id+ , from [8, 4] $ markFlag "-Werror=" Set.insert+ , from [8, 4] $ markFlag "-Wwarn=" Set.delete+ , from [8, 4] $ markFlag "-Wno-error=" Set.delete+ ]+ flag++ markFlag+ :: String+ -> (String -> Set String -> Set String)+ -> String+ -> Endo (Set String)+ markFlag name update flag = Endo $ case stripPrefix name flag of+ Just rest | not (null rest) && rest /= "compat" -> update rest+ _ -> id++ flagArgumentFilter :: [String] -> [String] -> [String]+ flagArgumentFilter flags = go+ where+ makeFilter :: String -> String -> Option' (First' ([String] -> [String]))+ makeFilter flag arg = Option' $ First' . filterRest <$> stripPrefix flag arg+ where+ filterRest leftOver = case dropEq leftOver of+ [] -> drop 1+ _ -> id++ checkFilter :: String -> Maybe ([String] -> [String])+ checkFilter = fmap getFirst' . getOption' . foldMap makeFilter flags++ go :: [String] -> [String]+ go [] = []+ go (arg : args) = case checkFilter arg of+ Just f -> go (f args)+ Nothing -> arg : go args++ argumentFilters :: [String] -> [String]+ argumentFilters =+ flagArgumentFilter+ ["-ghci-script", "-H", "-interactive-print"]++ -- \| Remove RTS arguments from a list.+ filterRtsArgs :: [String] -> [String]+ filterRtsArgs = snd . splitRTSArgs++ simpleFilters :: String -> Bool+ simpleFilters =+ not+ . getAny+ . mconcat+ [ flagIn simpleFlags+ , Any . isPrefixOf "-ddump-"+ , Any . isPrefixOf "-dsuppress-"+ , Any . isPrefixOf "-dno-suppress-"+ , flagIn $ invertibleFlagSet "-" ["ignore-dot-ghci"]+ , flagIn . invertibleFlagSet "-f" . mconcat $+ [+ [ "reverse-errors"+ , "warn-unused-binds"+ , "break-on-error"+ , "break-on-exception"+ , "print-bind-result"+ , "print-bind-contents"+ , "print-evld-with-show"+ , "implicit-import-qualified"+ , "error-spans"+ ]+ , from+ [7, 8]+ [ "print-explicit-foralls" -- maybe also earlier, but GHC-7.6 doesn't have --show-options+ , "print-explicit-kinds"+ ]+ , from+ [8, 0]+ [ "print-explicit-coercions"+ , "print-explicit-runtime-reps"+ , "print-equality-relations"+ , "print-unicode-syntax"+ , "print-expanded-synonyms"+ , "print-potential-instances"+ , "print-typechecker-elaboration"+ ]+ , from+ [8, 2]+ [ "diagnostics-show-caret"+ , "local-ghci-history"+ , "show-warning-groups"+ , "hide-source-paths"+ , "show-hole-constraints"+ ]+ , from [8, 4] ["show-loaded-modules"]+ , from [8, 6] ["ghci-leak-check", "no-it"]+ , from+ [8, 10]+ [ "defer-diagnostics" -- affects printing of diagnostics+ , "keep-going" -- try harder, the build will still fail if it's erroneous+ , "print-axiom-incomps" -- print more debug info for closed type families+ ]+ , from+ [9, 2]+ [ "family-application-cache"+ ]+ , from+ [9, 6]+ [ "print-redundant-promotion-ticks"+ , "show-error-context"+ ]+ , from+ [9, 8]+ [ "unoptimized-core-for-interpreter"+ ]+ , from+ [9, 10]+ [ "diagnostics-as-json"+ , "print-error-index-links"+ , "break-points"+ ]+ ]+ , flagIn $ invertibleFlagSet "-d" ["ppr-case-as-let", "ppr-ticks"]+ , isOptIntFlag+ , isIntFlag+ , if safeToFilterWarnings+ then isWarning <> (Any . ("-w" ==))+ else mempty+ , from [8, 6] $+ if safeToFilterHoles+ then isTypedHoleFlag+ else mempty+ ]++ flagIn :: Set String -> String -> Any+ flagIn set flag = Any $ Set.member flag set++ isWarning :: String -> Any+ isWarning =+ mconcat $+ map+ ((Any .) . isPrefixOf)+ ["-fwarn-", "-fno-warn-", "-W", "-Wno-"]++ simpleFlags :: Set String+ simpleFlags =+ Set.fromList . mconcat $+ [+ [ "-n"+ , "-#include"+ , "-Rghc-timing"+ , "-dstg-stats"+ , "-dth-dec-file"+ , "-dsource-stats"+ , "-dverbose-core2core"+ , "-dverbose-stg2stg"+ , "-dcore-lint"+ , "-dstg-lint"+ , "-dcmm-lint"+ , "-dasm-lint"+ , "-dannot-lint"+ , "-dshow-passes"+ , "-dfaststring-stats"+ , "-fno-max-relevant-binds"+ , "-recomp"+ , "-no-recomp"+ , "-fforce-recomp"+ , "-fno-force-recomp"+ ]+ , from+ [8, 2]+ [ "-fno-max-errors"+ , "-fdiagnostics-color=auto"+ , "-fdiagnostics-color=always"+ , "-fdiagnostics-color=never"+ , "-dppr-debug"+ , "-dno-debug-output"+ ]+ , from [8, 4] ["-ddebug-output"]+ , from [8, 4] $ to [8, 6] ["-fno-max-valid-substitutions"]+ , from [8, 6] ["-dhex-word-literals"]+ , from [8, 8] ["-fshow-docs-of-hole-fits", "-fno-show-docs-of-hole-fits"]+ , from [9, 0] ["-dlinear-core-lint"]+ , from [9, 10] ["-dipe-stats"]+ ]++ isOptIntFlag :: String -> Any+ isOptIntFlag = mconcat . map (dropIntFlag True) $ ["-v", "-j"]++ isIntFlag :: String -> Any+ isIntFlag =+ mconcat . map (dropIntFlag False) . mconcat $+ [+ [ "-fmax-relevant-binds"+ , "-ddpr-user-length"+ , "-ddpr-cols"+ , "-dtrace-level"+ , "-fghci-hist-size"+ , "-dinitial-unique"+ , "-dunique-increment"+ ]+ , from [8, 2] ["-fmax-uncovered-patterns", "-fmax-errors"]+ , from [8, 4] $ to [8, 6] ["-fmax-valid-substitutions"]+ , from [9, 12] ["-fmax-forced-spec-args", "-fwrite-if-compression"]+ ]++ dropIntFlag :: Bool -> String -> String -> Any+ dropIntFlag isOpt flag input = Any $ case stripPrefix flag input of+ Nothing -> False+ Just rest+ | isOpt && null rest -> True+ | otherwise -> case parseInt rest of+ Just _ -> True+ Nothing -> False+ where+ parseInt :: String -> Maybe Int+ parseInt = readMaybe . dropEq++ dropEq :: String -> String+ dropEq ('=' : s) = s+ dropEq s = s++ invertibleFlagSet :: String -> [String] -> Set String+ invertibleFlagSet prefix flagNames =+ Set.fromList $ (++) <$> [prefix, prefix ++ "no-"] <*> flagNames++ compatWarningSet :: Set String+ compatWarningSet =+ Set.fromList $+ mconcat+ [ from+ [8, 6]+ [ "missing-monadfail-instances"+ , "semigroup"+ , "noncanonical-monoid-instances"+ , "implicit-kind-vars"+ ]+ ]++ safeToFilterHoles :: Bool+ safeToFilterHoles =+ getAll . checkGhcFlags $+ All . fromMaybe True . fmap getLast' . getOption' . foldMap notDeferred+ where+ notDeferred :: String -> Option' (Last' Bool)+ notDeferred "-fdefer-typed-holes" = Option' . Just . Last' $ False+ notDeferred "-fno-defer-typed-holes" = Option' . Just . Last' $ True+ notDeferred _ = Option' Nothing++ isTypedHoleFlag :: String -> Any+ isTypedHoleFlag =+ mconcat+ [ flagIn . invertibleFlagSet "-f" $+ [ "show-hole-constraints"+ , "show-valid-substitutions"+ , "show-valid-hole-fits"+ , "sort-valid-hole-fits"+ , "sort-by-size-hole-fits"+ , "sort-by-subsumption-hole-fits"+ , "abstract-refinement-hole-fits"+ , "show-provenance-of-hole-fits"+ , "show-hole-matches-of-hole-fits"+ , "show-type-of-hole-fits"+ , "show-type-app-of-hole-fits"+ , "show-type-app-vars-of-hole-fits"+ , "unclutter-valid-hole-fits"+ ]+ , flagIn . Set.fromList $+ [ "-fno-max-valid-hole-fits"+ , "-fno-max-refinement-hole-fits"+ , "-fno-refinement-level-hole-fits"+ ]+ , mconcat . map (dropIntFlag False) $+ [ "-fmax-valid-hole-fits"+ , "-fmax-refinement-hole-fits"+ , "-frefinement-level-hole-fits"+ ]+ ]+normaliseGhcArgs _ _ args = args++-- | A structured set of GHC options/flags+--+-- Note that options containing lists fall into two categories:+--+-- * options that can be safely deduplicated, e.g. input modules or+-- enabled extensions;+-- * options that cannot be deduplicated in general without changing+-- semantics, e.g. extra ghc options or linking options.+data GhcOptions = GhcOptions+ { ghcOptMode :: Flag GhcMode+ -- ^ The major mode for the ghc invocation.+ , ghcOptExtra :: [String]+ -- ^ Any extra options to pass directly to ghc. These go at the end and hence+ -- override other stuff.+ , ghcOptExtraDefault :: [String]+ -- ^ Extra default flags to pass directly to ghc. These go at the beginning+ -- and so can be overridden by other stuff.+ , -----------------------+ -- Inputs and outputs++ ghcOptInputFiles :: NubListR (SymbolicPath Pkg File)+ -- ^ The main input files; could be .hs, .hi, .c, .o, depending on mode.+ , ghcOptInputScripts :: NubListR (SymbolicPath Pkg File)+ -- ^ Script files with irregular extensions that need -x hs.+ , ghcOptInputModules :: NubListR ModuleName+ -- ^ The names of input Haskell modules, mainly for @--make@ mode.+ , ghcOptOutputFile :: Flag (SymbolicPath Pkg File)+ -- ^ Location for output file; the @ghc -o@ flag.+ , ghcOptOutputDynFile :: Flag FilePath+ -- ^ Location for dynamic output file in 'GhcStaticAndDynamic' mode;+ -- the @ghc -dyno@ flag.+ , ghcOptSourcePathClear :: Flag Bool+ -- ^ Start with an empty search path for Haskell source files;+ -- the @ghc -i@ flag (@-i@ on its own with no path argument).+ , ghcOptSourcePath :: NubListR (SymbolicPath Pkg (Dir Source))+ -- ^ Search path for Haskell source files; the @ghc -i@ flag.+ , ghcOptUnitFiles :: [FilePath]+ -- ^ Unit files to load; the @ghc -unit@ flag.+ , -------------+ -- Packages++ ghcOptThisUnitId :: Flag String+ -- ^ The unit ID the modules will belong to; the @ghc -this-unit-id@+ -- flag (or @-this-package-key@ or @-package-name@ on older+ -- versions of GHC). This is a 'String' because we assume you've+ -- already figured out what the correct format for this string is+ -- (we need to handle backwards compatibility.)+ , ghcOptThisComponentId :: Flag ComponentId+ -- ^ GHC doesn't make any assumptions about the format of+ -- definite unit ids, so when we are instantiating a package it+ -- needs to be told explicitly what the component being instantiated+ -- is. This only gets set when 'ghcOptInstantiatedWith' is non-empty+ , ghcOptInstantiatedWith :: [(ModuleName, OpenModule)]+ -- ^ How the requirements of the package being compiled are to+ -- be filled. When typechecking an indefinite package, the 'OpenModule'+ -- is always a 'OpenModuleVar'; otherwise, it specifies the installed module+ -- that instantiates a package.+ , ghcOptNoCode :: Flag Bool+ -- ^ No code? (But we turn on interface writing+ , ghcOptPackageDBs :: PackageDBStack+ -- ^ GHC package databases to use, the @ghc -package-conf@ flag.+ , ghcOptPackages+ :: NubListR (OpenUnitId, ModuleRenaming)+ -- ^ The GHC packages to bring into scope when compiling,+ -- the @ghc -package-id@ flags.+ , ghcOptHideAllPackages :: Flag Bool+ -- ^ Start with a clean package set; the @ghc -hide-all-packages@ flag+ , ghcOptWarnMissingHomeModules :: Flag Bool+ -- ^ Warn about modules, not listed in command line+ , ghcOptNoAutoLinkPackages :: Flag Bool+ -- ^ Don't automatically link in Haskell98 etc; the @ghc+ -- -no-auto-link-packages@ flag.+ , -----------------+ -- Linker stuff++ ghcOptLinkLibs :: [FilePath]+ -- ^ Names of libraries to link in; the @ghc -l@ flag.+ , ghcOptLinkLibPath :: NubListR (SymbolicPath Pkg (Dir Lib))+ -- ^ Search path for libraries to link in; the @ghc -L@ flag.+ , ghcOptLinkOptions :: [String]+ -- ^ Options to pass through to the linker; the @ghc -optl@ flag.+ , ghcOptLinkFrameworks :: NubListR String+ -- ^ OSX only: frameworks to link in; the @ghc -framework@ flag.+ , ghcOptLinkFrameworkDirs :: NubListR (SymbolicPath Pkg (Dir Framework))+ -- ^ OSX only: Search path for frameworks to link in; the+ -- @ghc -framework-path@ flag.+ , ghcOptLinkRts :: Flag Bool+ -- ^ Instruct GHC to link against @libHSrts@ when producing a shared library.+ , ghcOptNoLink :: Flag Bool+ -- ^ Don't do the link step, useful in make mode; the @ghc -no-link@ flag.+ , ghcOptLinkNoHsMain :: Flag Bool+ -- ^ Don't link in the normal RTS @main@ entry point; the @ghc -no-hs-main@+ -- flag.+ , ghcOptLinkModDefFiles :: NubListR FilePath+ -- ^ Module definition files (Windows specific)+ , --------------------+ -- C and CPP stuff++ ghcOptCcOptions :: [String]+ -- ^ Options to pass through to the C compiler; the @ghc -optc@ flag.+ , ghcOptCxxOptions :: [String]+ -- ^ Options to pass through to the C++ compiler.+ , ghcOptAsmOptions :: [String]+ -- ^ Options to pass through to the Assembler.+ , ghcOptCppOptions :: [String]+ -- ^ Options to pass through to CPP; the @ghc -optP@ flag.+ , ghcOptJSppOptions :: [String]+ -- ^ Options to pass through to CPP; the @ghc -optJSP@ flag. @since 3.16.0.0+ , ghcOptCppIncludePath :: NubListR (SymbolicPath Pkg (Dir Include))+ -- ^ Search path for CPP includes like header files; the @ghc -I@ flag.+ , ghcOptCppIncludes :: NubListR (SymbolicPath Pkg File)+ -- ^ Extra header files to include at CPP stage; the @ghc -optP-include@ flag.+ , ghcOptFfiIncludes :: NubListR FilePath+ -- ^ Extra header files to include for old-style FFI; the @ghc -#include@ flag.+ , ghcOptCcProgram :: Flag FilePath+ -- ^ Program to use for the C and C++ compiler; the @ghc -pgmc@ flag.+ , ----------------------------+ -- Language and extensions++ ghcOptLanguage :: Flag Language+ -- ^ The base language; the @ghc -XHaskell98@ or @-XHaskell2010@ flag.+ , ghcOptExtensions :: NubListR Extension+ -- ^ The language extensions; the @ghc -X@ flag.+ , ghcOptExtensionMap :: Map Extension (Maybe CompilerFlag)+ -- ^ A GHC version-dependent mapping of extensions to flags. This must be+ -- set to be able to make use of the 'ghcOptExtensions'.+ , ----------------+ -- Compilation++ ghcOptOptimisation :: Flag GhcOptimisation+ -- ^ What optimisation level to use; the @ghc -O@ flag.+ , ghcOptDebugInfo :: Flag DebugInfoLevel+ -- ^ Emit debug info; the @ghc -g@ flag.+ , ghcOptProfilingMode :: Flag Bool+ -- ^ Compile in profiling mode; the @ghc -prof@ flag.+ , ghcOptProfilingAuto :: Flag GhcProfAuto+ -- ^ Automatically add profiling cost centers; the @ghc -fprof-auto*@ flags.+ , ghcOptSplitSections :: Flag Bool+ -- ^ Use the \"split sections\" feature; the @ghc -split-sections@ flag.+ , ghcOptSplitObjs :: Flag Bool+ -- ^ Use the \"split object files\" feature; the @ghc -split-objs@ flag.+ , ghcOptNumJobs :: Flag ParStrat+ -- ^ Run N jobs simultaneously (if possible).+ , ghcOptHPCDir :: Flag (SymbolicPath Pkg (Dir Mix))+ -- ^ Enable coverage analysis; the @ghc -fhpc -hpcdir@ flags.+ , ----------------+ -- GHCi++ ghcOptGHCiScripts :: [FilePath]+ -- ^ Extra GHCi startup scripts; the @-ghci-script@ flag+ , ------------------------+ -- Redirecting outputs++ ghcOptHiSuffix :: Flag String+ , ghcOptObjSuffix :: Flag String+ , ghcOptDynHiSuffix :: Flag String+ -- ^ only in 'GhcStaticAndDynamic' mode+ , ghcOptDynObjSuffix :: Flag String+ -- ^ only in 'GhcStaticAndDynamic' mode+ , ghcOptHiDir :: Flag (SymbolicPath Pkg (Dir Artifacts))+ , ghcOptHieDir :: Flag (SymbolicPath Pkg (Dir Artifacts))+ , ghcOptObjDir :: Flag (SymbolicPath Pkg (Dir Artifacts))+ , ghcOptOutputDir :: Flag (SymbolicPath Pkg (Dir Artifacts))+ , ghcOptStubDir :: Flag (SymbolicPath Pkg (Dir Artifacts))+ , --------------------+ -- Creating libraries++ ghcOptDynLinkMode :: Flag GhcDynLinkMode+ , ghcOptStaticLib :: Flag Bool+ , ghcOptShared :: Flag Bool+ , ghcOptFPic :: Flag Bool+ , ghcOptDylibName :: Flag String+ , ghcOptRPaths :: NubListR FilePath+ , ---------------+ -- Misc flags++ ghcOptVerbosity :: Flag Verbosity+ -- ^ Get GHC to be quiet or verbose with what it's doing; the @ghc -v@ flag.+ , ghcOptExtraPath :: NubListR (SymbolicPath Pkg (Dir Build))+ -- ^ Put the extra folders in the PATH environment variable we invoke+ -- GHC with+ , ghcOptCabal :: Flag Bool+ -- ^ Let GHC know that it is Cabal that's calling it.+ -- Modifies some of the GHC error messages.+ }+ deriving (Show, Generic)++data GhcMode+ = -- | @ghc -c@+ GhcModeCompile+ | -- | @ghc@+ GhcModeLink+ | -- | @ghc --make@+ GhcModeMake+ | -- | @ghci@ \/ @ghc --interactive@+ GhcModeInteractive+ | -- | @ghc --abi-hash@+ -- | GhcModeDepAnalysis -- ^ @ghc -M@+ -- | GhcModeEvaluate -- ^ @ghc -e@+ GhcModeAbiHash+ deriving (Show, Eq)++data GhcOptimisation+ = -- | @-O0@+ GhcNoOptimisation+ | -- | @-O@+ GhcNormalOptimisation+ | -- | @-O2@+ GhcMaximumOptimisation+ | -- | e.g. @-Odph@+ GhcSpecialOptimisation String+ deriving (Show, Eq)++data GhcDynLinkMode+ = -- | @-static@+ GhcStaticOnly+ | -- | @-dynamic@+ GhcDynamicOnly+ | -- | @-static -dynamic-too@+ GhcStaticAndDynamic+ deriving (Show, Eq)++data GhcProfAuto+ = -- | @-fprof-auto@+ GhcProfAutoAll+ | -- | @-fprof-auto-top@+ GhcProfAutoToplevel+ | -- | @-fprof-auto-exported@+ GhcProfAutoExported+ | -- | @-fprof-late+ GhcProfLate+ deriving (Show, Eq)++runGHC+ :: Verbosity+ -> ConfiguredProgram+ -> Compiler+ -> Platform+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> GhcOptions+ -> IO ()+runGHC verbosity ghcProg comp platform mbWorkDir opts = do+ runProgramInvocation verbosity+ =<< ghcInvocation verbosity ghcProg comp platform mbWorkDir opts++runGHCWithResponseFile+ :: FilePath+ -> Maybe TextEncoding+ -> TempFileOptions+ -> Verbosity+ -> ConfiguredProgram+ -> Compiler+ -> Platform+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> GhcOptions+ -> IO ()+runGHCWithResponseFile fileNameTemplate encoding tempFileOptions verbosity ghcProg comp platform maybeWorkDir opts = do+ invocation <- ghcInvocation verbosity ghcProg comp platform maybeWorkDir opts++ let compilerSupportsResponseFiles =+ case compilerCompatVersion GHC comp of+ -- GHC 9.4 is the first version which supports response files.+ Just version -> version >= mkVersion [9, 4]+ Nothing -> False++ args = progInvokeArgs invocation++ if not compilerSupportsResponseFiles+ then runProgramInvocation verbosity invocation+ else do+ let (rtsArgs, otherArgs) = splitRTSArgs args++ withResponseFile+ verbosity+ tempFileOptions+ fileNameTemplate+ encoding+ otherArgs+ $ \responseFile -> do+ let newInvocation =+ invocation{progInvokeArgs = ('@' : responseFile) : rtsArgs}++ infoNoWrap verbosity $+ "GHC response file arguments: "+ <> case otherArgs of+ [] -> ""+ arg : args' -> Process.showCommandForUser arg args'++ runProgramInvocation verbosity newInvocation++-- Note [Make --interactive the first argument to GHC]+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+-- The ghc argument @--interactive@ needs to be the first argument to the+-- ghc invocation, because Haskell Language Server used to rely on this.+-- This was initially changed for Cabal 3.16, but it broke all existing Haskell+-- Language Server prebuilt binaries.+-- To avoid this, we uphold this assumption in Haskell Language Server until the next+-- Cabal release (3.18).+--+-- The solution is to make sure that @--interactive@ is not passed as an argument in+-- the response file that is usually passed to ghc.+-- Instead, we filter out @--interactive@ and always pass it as the first argument,+-- if it exists.+--+-- We plan to remove this Hack in Cabal 3.18.++-- Start the repl. Either use `ghc`, or the program specified by the --with-repl flag.+runReplProgram+ :: Maybe FilePath+ -- ^ --with-repl argument+ -> TempFileOptions+ -> Verbosity+ -> ConfiguredProgram+ -> Compiler+ -> Platform+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> GhcOptions+ -> IO ()+runReplProgram withReplProg _tempFileOptions verbosity ghcProg comp platform mbWorkDir ghcOpts =+ let replProg = case withReplProg of+ Just path -> ghcProg{programLocation = FoundOnSystem path}+ Nothing -> ghcProg+ in do+ -- in runGHCWithResponseFile "ghci.rsp" Nothing tempFileOptions verbosity replProg comp platform mbWorkDir ghcOpts+ -- See Note [Make --interactive the first argument to GHC]+ -- In Cabal 3.18, restore the line above.+ invocation <- ghcInvocation verbosity replProg comp platform mbWorkDir ghcOpts+ let invocation' =+ let+ argsWithoutInteractive = filter (/= "--interactive") (progInvokeArgs invocation)+ in+ invocation+ { progInvokeArgs = ["--interactive"] <> argsWithoutInteractive+ }+ runProgramInvocation verbosity invocation'++ghcInvocation+ :: Verbosity+ -> ConfiguredProgram+ -> Compiler+ -> Platform+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> GhcOptions+ -> IO ProgramInvocation+ghcInvocation verbosity ghcProg comp platform mbWorkDir opts = do+ -- NOTE: GHC is the only program whose path we modify with more values than+ -- the standard @extra-prog-path@, namely the folders of the executables in+ -- the components, see @componentGhcOptions@.+ let envOverrides = programOverrideEnv ghcProg+ extraPath <-+ getExtraPathEnv verbosity envOverrides $+ map getSymbolicPath $+ fromNubListR $+ ghcOptExtraPath opts+ let ghcProg' = ghcProg{programOverrideEnv = envOverrides ++ extraPath}+ return $+ programInvocationCwd mbWorkDir ghcProg' $+ renderGhcOptions comp platform opts++-- TODO: use the -working-dir GHC flag instead of setting the process+-- working directory, as this improves error messages.++renderGhcOptions :: Compiler -> Platform -> GhcOptions -> [String]+renderGhcOptions comp _platform@(Platform _arch os) opts+ | compilerFlavor comp `notElem` [GHC, GHCJS] =+ error $+ "Distribution.Simple.Program.GHC.renderGhcOptions: "+ ++ "compiler flavor must be 'GHC' or 'GHCJS'!"+ | otherwise =+ concat+ [ case flagToMaybe (ghcOptMode opts) of+ Nothing -> []+ Just GhcModeCompile -> ["-c"]+ Just GhcModeLink -> []+ Just GhcModeMake -> ["--make"]+ Just GhcModeInteractive -> ["--interactive"]+ Just GhcModeAbiHash -> ["--abi-hash"]+ , -- Just GhcModeDepAnalysis -> ["-M"]+ -- Just GhcModeEvaluate -> ["-e", expr]++ ghcOptExtraDefault opts+ , ["-no-link" | flagBool ghcOptNoLink]+ , ["-flink-rts" | flagBool ghcOptLinkRts]+ , ---------------+ -- Misc flags++ maybe [] verbosityOpts (flagToMaybe (ghcOptVerbosity opts))+ , ["-fbuilding-cabal-package" | flagBool ghcOptCabal]+ , ----------------+ -- Compilation++ case flagToMaybe (ghcOptOptimisation opts) of+ Nothing -> []+ Just GhcNoOptimisation -> ["-O0"]+ Just GhcNormalOptimisation -> ["-O"]+ Just GhcMaximumOptimisation -> ["-O2"]+ Just (GhcSpecialOptimisation s) -> ["-O" ++ s] -- eg -Odph+ , case flagToMaybe (ghcOptDebugInfo opts) of+ Nothing -> []+ Just NoDebugInfo -> []+ Just MinimalDebugInfo -> ["-g1"]+ Just NormalDebugInfo -> ["-g2"]+ Just MaximalDebugInfo -> ["-g3"]+ , ["-prof" | flagBool ghcOptProfilingMode]+ , case flagToMaybe (ghcOptProfilingAuto opts) of+ _+ | not (flagBool ghcOptProfilingMode) ->+ []+ Nothing -> []+ Just GhcProfAutoAll+ | flagProfAuto implInfo -> ["-fprof-auto"]+ | otherwise -> ["-auto-all"] -- not the same, but close+ Just GhcProfLate+ | flagProfLate implInfo -> ["-fprof-late"]+ | otherwise -> ["-fprof-auto-top"] -- not the same, not very close, but what we have.+ Just GhcProfAutoToplevel+ | flagProfAuto implInfo -> ["-fprof-auto-top"]+ | otherwise -> ["-auto-all"]+ Just GhcProfAutoExported+ | flagProfAuto implInfo -> ["-fprof-auto-exported"]+ | otherwise -> ["-auto"]+ , ["-split-sections" | flagBool ghcOptSplitSections]+ , case compilerCompatVersion GHC comp of+ -- the -split-objs flag was removed in GHC 9.8+ Just ver | ver >= mkVersion [9, 8] -> []+ _ -> ["-split-objs" | flagBool ghcOptSplitObjs]+ , case flagToMaybe (ghcOptHPCDir opts) of+ Nothing -> []+ Just hpcdir -> ["-fhpc", "-hpcdir", u hpcdir]+ , if parmakeSupported comp+ then case ghcOptNumJobs opts of+ NoFlag -> []+ Flag Serial -> []+ Flag (UseSem name) ->+ if jsemSupported comp+ then ["-jsem " ++ name]+ else []+ Flag (NumJobs n) -> ["-j" ++ maybe "" show n]+ else []+ , --------------------+ -- Creating libraries++ ["-staticlib" | flagBool ghcOptStaticLib]+ , ["-shared" | flagBool ghcOptShared]+ , case flagToMaybe (ghcOptDynLinkMode opts) of+ Nothing -> []+ Just GhcStaticOnly -> ["-static"]+ Just GhcDynamicOnly -> ["-dynamic"]+ Just GhcStaticAndDynamic -> ["-static", "-dynamic-too"]+ , ["-fPIC" | flagBool ghcOptFPic]+ , concat [["-dylib-install-name", libname] | libname <- flag ghcOptDylibName]+ , ------------------------+ -- Redirecting outputs++ concat [["-osuf", suf] | suf <- flag ghcOptObjSuffix]+ , concat [["-hisuf", suf] | suf <- flag ghcOptHiSuffix]+ , concat [["-dynosuf", suf] | suf <- flag ghcOptDynObjSuffix]+ , concat [["-dynhisuf", suf] | suf <- flag ghcOptDynHiSuffix]+ , concat [["-outputdir", u dir] | dir <- flag ghcOptOutputDir]+ , concat [["-odir", u dir] | dir <- flag ghcOptObjDir]+ , concat [["-hidir", u dir] | dir <- flag ghcOptHiDir]+ , concat [["-hiedir", u dir] | dir <- flag ghcOptHieDir]+ , concat [["-stubdir", u dir] | dir <- flag ghcOptStubDir]+ , -----------------------+ -- Source search path++ ["-i" | flagBool ghcOptSourcePathClear]+ , ["-i" ++ u dir | dir <- flags ghcOptSourcePath]+ , --------------------++ --------------------+ -- CPP, C, and C++ stuff++ ["-I" ++ u dir | dir <- flags ghcOptCppIncludePath]+ , ["-optP" ++ opt | opt <- ghcOptCppOptions opts]+ , ["-optJSP" ++ opt | opt <- ghcOptJSppOptions opts]+ , concat+ [ ["-optP-include", "-optP" ++ u inc]+ | inc <- flags ghcOptCppIncludes+ ]+ , ["-optc" ++ opt | opt <- ghcOptCcOptions opts]+ , -- C++ compiler options: GHC >= 8.10 requires -optcxx, older requires -optc+ let cxxflag = case compilerCompatVersion GHC comp of+ Just v | v >= mkVersion [8, 10] -> "-optcxx"+ _ -> "-optc"+ in [cxxflag ++ opt | opt <- ghcOptCxxOptions opts]+ , ["-opta" ++ opt | opt <- ghcOptAsmOptions opts]+ , concat [["-pgmc", cc] | cc <- flag ghcOptCcProgram]+ , -----------------+ -- Linker stuff++ ["-optl" ++ opt | opt <- ghcOptLinkOptions opts]+ , ["-l" ++ lib | lib <- ghcOptLinkLibs opts]+ , ["-L" ++ u dir | dir <- flags ghcOptLinkLibPath]+ , if isOSX+ then+ concat+ [ ["-framework", fmwk]+ | fmwk <- flags ghcOptLinkFrameworks+ ]+ else []+ , if isOSX+ then+ concat+ [ ["-framework-path", u path]+ | path <- flags ghcOptLinkFrameworkDirs+ ]+ else []+ , ["-no-hs-main" | flagBool ghcOptLinkNoHsMain]+ , ["-dynload deploy" | not (null (flags ghcOptRPaths))]+ , ["-optl-Wl,-rpath," ++ dir | dir <- flags ghcOptRPaths]+ , flags ghcOptLinkModDefFiles+ , -------------+ -- Packages++ concat+ [ [ if+ | unitIdSupported comp -> "-this-unit-id"+ | packageKeySupported comp -> "-this-package-key"+ | otherwise -> "-package-name"+ , this_arg+ ]+ | this_arg <- flag ghcOptThisUnitId+ ]+ , concat+ [ ["-this-component-id", prettyShow this_cid]+ | this_cid <- flag ghcOptThisComponentId+ ]+ , if null (ghcOptInstantiatedWith opts)+ then []+ else+ "-instantiated-with"+ : intercalate+ ","+ ( map+ ( \(n, m) ->+ prettyShow n+ ++ "="+ ++ prettyShow m+ )+ (ghcOptInstantiatedWith opts)+ )+ : []+ , concat [["-fno-code", "-fwrite-interface"] | flagBool ghcOptNoCode]+ , ["-hide-all-packages" | flagBool ghcOptHideAllPackages]+ , ["-Wmissing-home-modules" | flagBool ghcOptWarnMissingHomeModules]+ , ["-no-auto-link-packages" | flagBool ghcOptNoAutoLinkPackages]+ , packageDbArgs implInfo (interpretPackageDBStack Nothing (ghcOptPackageDBs opts))+ , concat $+ let space "" = ""+ space xs = ' ' : xs+ in [ ["-package-id", prettyShow ipkgid ++ space (prettyShow rns)]+ | (ipkgid, rns) <- flags ghcOptPackages+ ]+ , ----------------------------+ -- Language and extensions++ if supportsHaskell2010 implInfo+ then ["-X" ++ prettyShow lang | lang <- flag ghcOptLanguage]+ else []+ , [ ext'+ | ext <- flags ghcOptExtensions+ , ext' <- case Map.lookup ext (ghcOptExtensionMap opts) of+ Just (Just arg) -> [arg]+ Just Nothing -> []+ Nothing ->+ error $+ "Distribution.Simple.Program.GHC.renderGhcOptions: "+ ++ prettyShow ext+ ++ " not present in ghcOptExtensionMap."+ ]+ , ----------------+ -- GHCi++ concat+ [ ["-ghci-script", script] | script <- ghcOptGHCiScripts opts, flagGhciScript implInfo+ ]+ , ---------------+ -- Inputs++ -- Specify the input file(s) first, so that in ghci the `main-is` module is+ -- in scope instead of the first module defined in `other-modules`.+ map u $ flags ghcOptInputFiles+ , concat [["-x", "hs", u script] | script <- flags ghcOptInputScripts]+ , [prettyShow modu | modu <- flags ghcOptInputModules]+ , concat [["-o", u out] | out <- flag ghcOptOutputFile]+ , concat [["-dyno", out] | out <- flag ghcOptOutputDynFile]+ , -- unit files+ concat [["-unit", "@" ++ unit] | unit <- ghcOptUnitFiles opts]+ , ---------------+ -- Extra++ ghcOptExtra opts+ ]+ where+ -- See Note [Symbolic paths] in Distribution.Utils.Path+ u :: SymbolicPath Pkg to -> FilePath+ u = interpretSymbolicPathCWD+ implInfo = getImplInfo comp+ isOSX = os == OSX+ flag flg = flagToList (flg opts)+ flags flg = fromNubListR . flg $ opts+ flagBool flg = fromFlagOrDefault False (flg opts)++verbosityOpts :: Verbosity -> [String]+verbosityOpts verbosity+ | verbosity >= deafening = ["-v"]+ | verbosity >= normal = []+ | otherwise = ["-w", "-v0"]++-- | GHC <7.6 uses '-package-conf' instead of '-package-db'.+packageDbArgsConf :: PackageDBStackCWD -> [String]+packageDbArgsConf dbstack = case dbstack of+ (GlobalPackageDB : UserPackageDB : dbs) -> concatMap specific dbs+ (GlobalPackageDB : dbs) ->+ ("-no-user-package-conf")+ : concatMap specific dbs+ _ -> ierror+ where+ specific (SpecificPackageDB db) = ["-package-conf", db]+ specific _ = ierror+ ierror =+ error $+ "internal error: unexpected package db stack: "+ ++ show dbstack++-- | GHC >= 7.6 uses the '-package-db' flag. See+-- https://gitlab.haskell.org/ghc/ghc/-/issues/5977.+packageDbArgsDb :: PackageDBStackCWD -> [String]+-- special cases to make arguments prettier in common scenarios+packageDbArgsDb dbstack = case dbstack of+ (GlobalPackageDB : UserPackageDB : dbs)+ | all isSpecific dbs -> concatMap single dbs+ (GlobalPackageDB : dbs)+ | all isSpecific dbs ->+ "-no-user-package-db"+ : concatMap single dbs+ dbs ->+ "-clear-package-db"+ : concatMap single dbs+ where+ single (SpecificPackageDB db) = ["-package-db", db]+ single GlobalPackageDB = ["-global-package-db"]+ single UserPackageDB = ["-user-package-db"]+ isSpecific (SpecificPackageDB _) = True+ isSpecific _ = False++packageDbArgs :: GhcImplInfo -> PackageDBStackCWD -> [String]+packageDbArgs implInfo+ | flagPackageConf implInfo = packageDbArgsConf+ | otherwise = packageDbArgsDb++-- | Split a list of command-line arguments into RTS arguments and non-RTS+-- arguments.+splitRTSArgs :: [String] -> ([String], [String])+splitRTSArgs args =+ let addRTSArg arg ~(rtsArgs, nonRTSArgs) = (arg : rtsArgs, nonRTSArgs)+ addNonRTSArg arg ~(rtsArgs, nonRTSArgs) = (rtsArgs, arg : nonRTSArgs)++ go _ [] = ([], [])+ go isRTSArg (arg : rest) =+ case arg of+ "+RTS" -> addRTSArg arg $ go True rest+ "-RTS" -> addRTSArg arg $ go False rest+ "--RTS" -> ([arg], rest)+ "--" -> ([], arg : rest)+ _ ->+ if isRTSArg+ then addRTSArg arg $ go isRTSArg rest+ else addNonRTSArg arg $ go isRTSArg rest+ in go False args -- ----------------------------------------------------------------------------- -- Boilerplate Monoid instance for GhcOptions
src/Distribution/Simple/Program/HcPkg.hs view
@@ -1,9 +1,11 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.HcPkg -- Copyright : Duncan Coutts 2009, 2013@@ -13,35 +15,34 @@ -- -- This module provides an library interface to the @hc-pkg@ program. -- Currently only GHC and GHCJS have hc-pkg programs.--module Distribution.Simple.Program.HcPkg (- -- * Types- HcPkgInfo(..),- RegisterOptions(..),- defaultRegisterOptions,+module Distribution.Simple.Program.HcPkg+ ( -- * Types+ HcPkgInfo (..)+ , RegisterOptions (..)+ , defaultRegisterOptions -- * Actions- init,- invoke,- register,- unregister,- recache,- expose,- hide,- dump,- describe,- list,+ , init+ , invoke+ , register+ , unregister+ , recache+ , expose+ , hide+ , dump+ , describe+ , list -- * Program invocations- initInvocation,- registerInvocation,- unregisterInvocation,- recacheInvocation,- exposeInvocation,- hideInvocation,- dumpInvocation,- describeInvocation,- listInvocation,+ , initInvocation+ , registerInvocation+ , unregisterInvocation+ , recacheInvocation+ , exposeInvocation+ , hideInvocation+ , dumpInvocation+ , describeInvocation+ , listInvocation ) where import Distribution.Compat.Prelude hiding (init)@@ -51,245 +52,278 @@ import Distribution.Parsec import Distribution.Pretty import Distribution.Simple.Compiler+import Distribution.Simple.Errors import Distribution.Simple.Program.Run import Distribution.Simple.Program.Types import Distribution.Simple.Utils import Distribution.Types.ComponentId import Distribution.Types.PackageId import Distribution.Types.UnitId+import Distribution.Utils.Path import Distribution.Verbosity -import Data.List (stripPrefix)-import System.FilePath as FilePath (isPathSeparator, joinPath, splitDirectories, splitPath, (<.>), (</>))+import Data.List (stripPrefix)+import System.FilePath as FilePath+ ( isPathSeparator+ , joinPath+ , splitDirectories+ , splitPath+ ) -import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-import qualified Data.List.NonEmpty as NE+import qualified Data.ByteString as BS+import qualified Data.ByteString.Lazy as LBS+import qualified Data.List.NonEmpty as NE import qualified System.FilePath.Posix as FilePath.Posix -- | Information about the features and capabilities of an @hc-pkg@ -- program.--- data HcPkgInfo = HcPkgInfo- { hcPkgProgram :: ConfiguredProgram- , noPkgDbStack :: Bool -- ^ no package DB stack supported- , noVerboseFlag :: Bool -- ^ hc-pkg does not support verbosity flags- , flagPackageConf :: Bool -- ^ use package-conf option instead of package-db- , supportsDirDbs :: Bool -- ^ supports directory style package databases- , requiresDirDbs :: Bool -- ^ requires directory style package databases- , nativeMultiInstance :: Bool -- ^ supports --enable-multi-instance flag- , recacheMultiInstance :: Bool -- ^ supports multi-instance via recache- , suppressFilesCheck :: Bool -- ^ supports --force-files or equivalent+ { hcPkgProgram :: ConfiguredProgram+ , noPkgDbStack :: Bool+ -- ^ no package DB stack supported+ , noVerboseFlag :: Bool+ -- ^ hc-pkg does not support verbosity flags+ , flagPackageConf :: Bool+ -- ^ use package-conf option instead of package-db+ , supportsDirDbs :: Bool+ -- ^ supports directory style package databases+ , requiresDirDbs :: Bool+ -- ^ requires directory style package databases+ , nativeMultiInstance :: Bool+ -- ^ supports --enable-multi-instance flag+ , recacheMultiInstance :: Bool+ -- ^ supports multi-instance via recache+ , suppressFilesCheck :: Bool+ -- ^ supports --force-files or equivalent } - -- | Call @hc-pkg@ to initialise a package database at the location {path}. -- -- > hc-pkg init {path}--- init :: HcPkgInfo -> Verbosity -> Bool -> FilePath -> IO () init hpi verbosity preferCompat path- | not (supportsDirDbs hpi)- || (not (requiresDirDbs hpi) && preferCompat)- = writeFile path "[]"-- | otherwise- = runProgramInvocation verbosity (initInvocation hpi verbosity path)+ | not (supportsDirDbs hpi)+ || (not (requiresDirDbs hpi) && preferCompat) =+ writeFile path "[]"+ | otherwise =+ runProgramInvocation verbosity (initInvocation hpi verbosity path) -- | Run @hc-pkg@ using a given package DB stack, directly forwarding the -- provided command-line arguments to it.-invoke :: HcPkgInfo -> Verbosity -> PackageDBStack -> [String] -> IO ()-invoke hpi verbosity dbStack extraArgs =+invoke+ :: HcPkgInfo+ -> Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> PackageDBStack+ -> [String]+ -> IO ()+invoke hpi verbosity mbWorkDir dbStack extraArgs = runProgramInvocation verbosity invocation where- args = packageDbStackOpts hpi dbStack ++ extraArgs- invocation = programInvocation (hcPkgProgram hpi) args+ args = packageDbStackOpts hpi dbStack ++ extraArgs+ invocation = programInvocationCwd mbWorkDir (hcPkgProgram hpi) args -- | Additional variations in the behaviour for 'register'.-data RegisterOptions = RegisterOptions {- -- | Allows re-registering \/ overwriting an existing package- registerAllowOverwrite :: Bool,-- -- | Insist on the ability to register multiple instances of a- -- single version of a single package. This will fail if the @hc-pkg@- -- does not support it, see 'nativeMultiInstance' and- -- 'recacheMultiInstance'.- registerMultiInstance :: Bool,-- -- | Require that no checks are performed on the existence of package- -- files mentioned in the registration info. This must be used if- -- registering prior to putting the files in their final place. This will- -- fail if the @hc-pkg@ does not support it, see 'suppressFilesCheck'.- registerSuppressFilesCheck :: Bool- }+data RegisterOptions = RegisterOptions+ { registerAllowOverwrite :: Bool+ -- ^ Allows re-registering \/ overwriting an existing package+ , registerMultiInstance :: Bool+ -- ^ Insist on the ability to register multiple instances of a+ -- single version of a single package. This will fail if the @hc-pkg@+ -- does not support it, see 'nativeMultiInstance' and+ -- 'recacheMultiInstance'.+ , registerSuppressFilesCheck :: Bool+ -- ^ Require that no checks are performed on the existence of package+ -- files mentioned in the registration info. This must be used if+ -- registering prior to putting the files in their final place. This will+ -- fail if the @hc-pkg@ does not support it, see 'suppressFilesCheck'.+ } -- | Defaults are @True@, @False@ and @False@ defaultRegisterOptions :: RegisterOptions-defaultRegisterOptions = RegisterOptions {- registerAllowOverwrite = True,- registerMultiInstance = False,- registerSuppressFilesCheck = False- }+defaultRegisterOptions =+ RegisterOptions+ { registerAllowOverwrite = True+ , registerMultiInstance = False+ , registerSuppressFilesCheck = False+ } -- | Call @hc-pkg@ to register a package. -- -- > hc-pkg register {filename | -} [--user | --global | --package-db]----register :: HcPkgInfo -> Verbosity -> PackageDBStack- -> InstalledPackageInfo- -> RegisterOptions- -> IO ()-register hpi verbosity packagedbs pkgInfo registerOptions+register+ :: HcPkgInfo+ -> Verbosity+ -> Maybe (SymbolicPath CWD (Dir from))+ -> PackageDBStackS from+ -> InstalledPackageInfo+ -> RegisterOptions+ -> IO ()+register hpi verbosity mbWorkDir packagedbs pkgInfo registerOptions | registerMultiInstance registerOptions- , not (nativeMultiInstance hpi || recacheMultiInstance hpi)- = die' verbosity $ "HcPkg.register: the compiler does not support "- ++ "registering multiple instances of packages."-+ , not (nativeMultiInstance hpi || recacheMultiInstance hpi) =+ dieWithException verbosity RegMultipleInstancePkg | registerSuppressFilesCheck registerOptions- , not (suppressFilesCheck hpi)- = die' verbosity $ "HcPkg.register: the compiler does not support "- ++ "suppressing checks on files."-- -- This is a trick. Older versions of GHC do not support the- -- --enable-multi-instance flag for ghc-pkg register but it turns out that- -- the same ability is available by using ghc-pkg recache. The recache- -- command is there to support distro package managers that like to work- -- by just installing files and running update commands, rather than- -- special add/remove commands. So the way to register by this method is- -- to write the package registration file directly into the package db and- -- then call hc-pkg recache.- --+ , not (suppressFilesCheck hpi) =+ dieWithException verbosity SuppressingChecksOnFile+ -- This is a trick. Older versions of GHC do not support the+ -- --enable-multi-instance flag for ghc-pkg register but it turns out that+ -- the same ability is available by using ghc-pkg recache. The recache+ -- command is there to support distro package managers that like to work+ -- by just installing files and running update commands, rather than+ -- special add/remove commands. So the way to register by this method is+ -- to write the package registration file directly into the package db and+ -- then call hc-pkg recache.+ -- | registerMultiInstance registerOptions- , recacheMultiInstance hpi- = do let pkgdb = registrationPackageDB packagedbs- writeRegistrationFileDirectly verbosity hpi pkgdb pkgInfo- recache hpi verbosity pkgdb-- | otherwise- = runProgramInvocation verbosity- (registerInvocation hpi verbosity packagedbs pkgInfo registerOptions)--writeRegistrationFileDirectly :: Verbosity- -> HcPkgInfo- -> PackageDB- -> InstalledPackageInfo- -> IO ()-writeRegistrationFileDirectly verbosity hpi (SpecificPackageDB dir) pkgInfo- | supportsDirDbs hpi- = do let pkgfile = dir </> prettyShow (installedUnitId pkgInfo) <.> "conf"- writeUTF8File pkgfile (showInstalledPackageInfo pkgInfo)-- | otherwise- = die' verbosity $ "HcPkg.writeRegistrationFileDirectly: compiler does not support dir style package dbs"--writeRegistrationFileDirectly verbosity _ _ _ =- -- We don't know here what the dir for the global or user dbs are,- -- if that's needed it'll require a bit more plumbing to support.- die' verbosity $ "HcPkg.writeRegistrationFileDirectly: only supports SpecificPackageDB for now"+ , recacheMultiInstance hpi =+ do+ let pkgdb = registrationPackageDB packagedbs+ writeRegistrationFileDirectly verbosity hpi mbWorkDir pkgdb pkgInfo+ recache hpi verbosity mbWorkDir pkgdb+ | otherwise =+ runProgramInvocation+ verbosity+ (registerInvocation hpi verbosity mbWorkDir packagedbs pkgInfo registerOptions) +writeRegistrationFileDirectly+ :: Verbosity+ -> HcPkgInfo+ -> Maybe (SymbolicPath CWD (Dir from))+ -> PackageDBS from+ -> InstalledPackageInfo+ -> IO ()+writeRegistrationFileDirectly verbosity hpi mbWorkDir (SpecificPackageDB dir) pkgInfo+ | supportsDirDbs hpi =+ do+ let pkgfile = interpretSymbolicPath mbWorkDir dir </> prettyShow (installedUnitId pkgInfo) <.> "conf"+ writeUTF8File pkgfile (showInstalledPackageInfo pkgInfo)+ | otherwise =+ dieWithException verbosity NoSupportDirStylePackageDb+writeRegistrationFileDirectly verbosity _ _ _ _ =+ -- We don't know here what the dir for the global or user dbs are,+ -- if that's needed it'll require a bit more plumbing to support.+ dieWithException verbosity OnlySupportSpecificPackageDb -- | Call @hc-pkg@ to unregister a package -- -- > hc-pkg unregister [pkgid] [--user | --global | --package-db]----unregister :: HcPkgInfo -> Verbosity -> PackageDB -> PackageId -> IO ()-unregister hpi verbosity packagedb pkgid =- runProgramInvocation verbosity- (unregisterInvocation hpi verbosity packagedb pkgid)-+unregister :: HcPkgInfo -> Verbosity -> Maybe (SymbolicPath CWD (Dir Pkg)) -> PackageDB -> PackageId -> IO ()+unregister hpi verbosity mbWorkDir packagedb pkgid =+ runProgramInvocation+ verbosity+ (unregisterInvocation hpi verbosity mbWorkDir packagedb pkgid) -- | Call @hc-pkg@ to recache the registered packages. -- -- > hc-pkg recache [--user | --global | --package-db]----recache :: HcPkgInfo -> Verbosity -> PackageDB -> IO ()-recache hpi verbosity packagedb =- runProgramInvocation verbosity- (recacheInvocation hpi verbosity packagedb)-+recache :: HcPkgInfo -> Verbosity -> Maybe (SymbolicPath CWD (Dir from)) -> PackageDBS from -> IO ()+recache hpi verbosity mbWorkDir packagedb =+ runProgramInvocation+ verbosity+ (recacheInvocation hpi verbosity mbWorkDir packagedb) -- | Call @hc-pkg@ to expose a package. -- -- > hc-pkg expose [pkgid] [--user | --global | --package-db]----expose :: HcPkgInfo -> Verbosity -> PackageDB -> PackageId -> IO ()-expose hpi verbosity packagedb pkgid =- runProgramInvocation verbosity- (exposeInvocation hpi verbosity packagedb pkgid)+expose+ :: HcPkgInfo+ -> Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> PackageDB+ -> PackageId+ -> IO ()+expose hpi verbosity mbWorkDir packagedb pkgid =+ runProgramInvocation+ verbosity+ (exposeInvocation hpi verbosity mbWorkDir packagedb pkgid) -- | Call @hc-pkg@ to retrieve a specific package -- -- > hc-pkg describe [pkgid] [--user | --global | --package-db]----describe :: HcPkgInfo -> Verbosity -> PackageDBStack -> PackageId -> IO [InstalledPackageInfo]-describe hpi verbosity packagedb pid = do-- output <- getProgramInvocationLBS verbosity- (describeInvocation hpi verbosity packagedb pid)- `catchIO` \_ -> return mempty+describe+ :: HcPkgInfo+ -> Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> PackageDBStack+ -> PackageId+ -> IO [InstalledPackageInfo]+describe hpi verbosity mbWorkDir packagedb pid = do+ output <-+ getProgramInvocationLBS+ verbosity+ (describeInvocation hpi verbosity mbWorkDir packagedb pid)+ `catchIO` \_ -> return mempty case parsePackages output of Left ok -> return ok- _ -> die' verbosity $ "failed to parse output of '"- ++ programId (hcPkgProgram hpi) ++ " describe " ++ prettyShow pid ++ "'"+ _ -> dieWithException verbosity $ FailedToParseOutputDescribe (programId (hcPkgProgram hpi)) pid -- | Call @hc-pkg@ to hide a package. -- -- > hc-pkg hide [pkgid] [--user | --global | --package-db]----hide :: HcPkgInfo -> Verbosity -> PackageDB -> PackageId -> IO ()-hide hpi verbosity packagedb pkgid =- runProgramInvocation verbosity- (hideInvocation hpi verbosity packagedb pkgid)-+hide+ :: HcPkgInfo+ -> Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> PackageDB+ -> PackageId+ -> IO ()+hide hpi verbosity mbWorkDir packagedb pkgid =+ runProgramInvocation+ verbosity+ (hideInvocation hpi verbosity mbWorkDir packagedb pkgid) -- | Call @hc-pkg@ to get all the details of all the packages in the given -- package database.----dump :: HcPkgInfo -> Verbosity -> PackageDB -> IO [InstalledPackageInfo]-dump hpi verbosity packagedb = do-- output <- getProgramInvocationLBS verbosity- (dumpInvocation hpi verbosity packagedb)- `catchIO` \e -> die' verbosity $ programId (hcPkgProgram hpi) ++ " dump failed: "- ++ displayException e+dump+ :: HcPkgInfo+ -> Verbosity+ -> Maybe (SymbolicPath CWD (Dir from))+ -> PackageDBX (SymbolicPath from (Dir PkgDB))+ -> IO [InstalledPackageInfo]+dump hpi verbosity mbWorkDir packagedb = do+ output <-+ getProgramInvocationLBS+ verbosity+ (dumpInvocation hpi verbosity mbWorkDir packagedb)+ `catchIO` \e ->+ dieWithException verbosity $ DumpFailed (programId (hcPkgProgram hpi)) (displayException e) case parsePackages output of Left ok -> return ok- _ -> die' verbosity $ "failed to parse output of '"- ++ programId (hcPkgProgram hpi) ++ " dump'"-+ _ -> dieWithException verbosity $ FailedToParseOutputDump (programId (hcPkgProgram hpi)) parsePackages :: LBS.ByteString -> Either [InstalledPackageInfo] [String] parsePackages lbs0 =- case traverse parseInstalledPackageInfo $ splitPkgs lbs0 of- Right ok -> Left [ setUnitId . maybe id mungePackagePaths (pkgRoot pkg) $ pkg | (_, pkg) <- ok ]- Left msgs -> Right (NE.toList msgs)+ case traverse parseInstalledPackageInfo $ splitPkgs lbs0 of+ Right ok -> Left [setUnitId . maybe id mungePackagePaths (pkgRoot pkg) $ pkg | (_, pkg) <- ok]+ Left msgs -> Right (NE.toList msgs) where splitPkgs :: LBS.ByteString -> [BS.ByteString] splitPkgs = checkEmpty . doSplit where -- Handle the case of there being no packages at all. checkEmpty [s] | BS.all isSpace8 s = []- checkEmpty ss = ss+ checkEmpty ss = ss isSpace8 :: Word8 -> Bool- isSpace8 9 = True -- '\t'+ isSpace8 9 = True -- '\t' isSpace8 10 = True -- '\n' isSpace8 13 = True -- '\r' isSpace8 32 = True -- ' '- isSpace8 _ = False+ isSpace8 _ = False doSplit :: LBS.ByteString -> [BS.ByteString] doSplit lbs = go (LBS.findIndices (\w -> w == 10 || w == 13) lbs) where go :: [Int64] -> [BS.ByteString]- go [] = [ LBS.toStrict lbs ]- go (idx:idxs) =- let (pfx, sfx) = LBS.splitAt idx lbs- in case foldr (<|>) Nothing $ map (`lbsStripPrefix` sfx) separators of+ go [] = [LBS.toStrict lbs]+ go (idx : idxs) =+ let (pfx, sfx) = LBS.splitAt idx lbs+ in case foldr (<|>) Nothing $ map (`lbsStripPrefix` sfx) separators of Just sfx' -> LBS.toStrict pfx : doSplit sfx'- Nothing -> go idxs+ Nothing -> go idxs separators :: [LBS.ByteString] separators = ["\n---\n", "\r\n---\r\n", "\r---\r"]@@ -303,25 +337,25 @@ | otherwise = Nothing #endif - mungePackagePaths :: FilePath -> InstalledPackageInfo -> InstalledPackageInfo -- Perform path/URL variable substitution as per the Cabal ${pkgroot} spec -- (http://www.haskell.org/pipermail/libraries/2009-May/011772.html) -- Paths/URLs can be relative to ${pkgroot} or ${pkgrooturl}. -- The "pkgroot" is the directory containing the package database. mungePackagePaths pkgroot pkginfo =- pkginfo {- importDirs = mungePaths (importDirs pkginfo),- includeDirs = mungePaths (includeDirs pkginfo),- libraryDirs = mungePaths (libraryDirs pkginfo),- libraryDynDirs = mungePaths (libraryDynDirs pkginfo),- frameworkDirs = mungePaths (frameworkDirs pkginfo),- haddockInterfaces = mungePaths (haddockInterfaces pkginfo),- haddockHTMLs = mungeUrls (haddockHTMLs pkginfo)+ pkginfo+ { importDirs = mungePaths (importDirs pkginfo)+ , includeDirs = mungePaths (includeDirs pkginfo)+ , libraryDirs = mungePaths (libraryDirs pkginfo)+ , libraryDirsStatic = mungePaths (libraryDirsStatic pkginfo)+ , libraryDynDirs = mungePaths (libraryDynDirs pkginfo)+ , frameworkDirs = mungePaths (frameworkDirs pkginfo)+ , haddockInterfaces = mungePaths (haddockInterfaces pkginfo)+ , haddockHTMLs = mungeUrls (haddockHTMLs pkginfo) } where mungePaths = map mungePath- mungeUrls = map mungeUrl+ mungeUrls = map mungeUrl mungePath p = case stripVarPrefix "${pkgroot}" p of Just p' -> pkgroot </> p'@@ -331,53 +365,56 @@ Just p' -> toUrlPath pkgroot p' Nothing -> p - toUrlPath r p = "file:///"- -- URLs always use posix style '/' separators:- ++ FilePath.Posix.joinPath (r : FilePath.splitDirectories p)+ toUrlPath r p =+ "file:///"+ -- URLs always use posix style '/' separators:+ ++ FilePath.Posix.joinPath (r : FilePath.splitDirectories p) stripVarPrefix var p = case splitPath p of- (root:path') -> case stripPrefix var root of+ (root : path') -> case stripPrefix var root of Just [sep] | isPathSeparator sep -> Just (joinPath path')- _ -> Nothing- _ -> Nothing-+ _ -> Nothing+ _ -> Nothing -- Older installed package info files did not have the installedUnitId -- field, so if it is missing then we fill it as the source package ID. -- NB: Internal libraries not supported. setUnitId :: InstalledPackageInfo -> InstalledPackageInfo-setUnitId pkginfo@InstalledPackageInfo {- installedUnitId = uid,- sourcePackageId = pid- } | unUnitId uid == ""- = pkginfo {- installedUnitId = mkLegacyUnitId pid,- installedComponentId_ = mkComponentId (prettyShow pid)- }+setUnitId+ pkginfo@InstalledPackageInfo+ { installedUnitId = uid+ , sourcePackageId = pid+ }+ | unUnitId uid == "" =+ pkginfo+ { installedUnitId = mkLegacyUnitId pid+ , installedComponentId_ = mkComponentId (prettyShow pid)+ } setUnitId pkginfo = pkginfo - -- | Call @hc-pkg@ to get the source package Id of all the packages in the -- given package database. -- -- This is much less information than with 'dump', but also rather quicker. -- Note in particular that it does not include the 'UnitId', just -- the source 'PackageId' which is not necessarily unique in any package db.----list :: HcPkgInfo -> Verbosity -> PackageDB- -> IO [PackageId]-list hpi verbosity packagedb = do-- output <- getProgramInvocationOutput verbosity- (listInvocation hpi verbosity packagedb)- `catchIO` \_ -> die' verbosity $ programId (hcPkgProgram hpi) ++ " list failed"+list+ :: HcPkgInfo+ -> Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> PackageDB+ -> IO [PackageId]+list hpi verbosity mbWorkDir packagedb = do+ output <-+ getProgramInvocationOutput+ verbosity+ (listInvocation hpi verbosity mbWorkDir packagedb)+ `catchIO` \_ -> dieWithException verbosity $ ListFailed (programId (hcPkgProgram hpi)) case parsePackageIds output of Just ok -> return ok- _ -> die' verbosity $ "failed to parse output of '"- ++ programId (hcPkgProgram hpi) ++ " list'"-+ _ -> dieWithException verbosity $ FailedToParseOutputList (programId (hcPkgProgram hpi)) where parsePackageIds = traverse simpleParsec . words @@ -387,130 +424,173 @@ initInvocation :: HcPkgInfo -> Verbosity -> FilePath -> ProgramInvocation initInvocation hpi verbosity path =- programInvocation (hcPkgProgram hpi) args+ programInvocation (hcPkgProgram hpi) args where- args = ["init", path]+ args =+ ["init", path] ++ verbosityOpts hpi verbosity registerInvocation- :: HcPkgInfo -> Verbosity -> PackageDBStack+ :: HcPkgInfo+ -> Verbosity+ -> Maybe (SymbolicPath CWD (Dir from))+ -> PackageDBStackS from -> InstalledPackageInfo -> RegisterOptions -> ProgramInvocation-registerInvocation hpi verbosity packagedbs pkgInfo registerOptions =- (programInvocation (hcPkgProgram hpi) (args "-")) {- progInvokeInput = Just $ IODataText $ showInstalledPackageInfo pkgInfo,- progInvokeInputEncoding = IOEncodingUTF8+registerInvocation hpi verbosity mbWorkDir packagedbs pkgInfo registerOptions =+ (programInvocationCwd mbWorkDir (hcPkgProgram hpi) (args "-"))+ { progInvokeInput = Just $ IODataText $ showInstalledPackageInfo pkgInfo+ , progInvokeInputEncoding = IOEncodingUTF8 } where cmdname | registerAllowOverwrite registerOptions = "update"- | registerMultiInstance registerOptions = "update"- | otherwise = "register"-- args file = [cmdname, file]- ++ packageDbStackOpts hpi packagedbs- ++ [ "--enable-multi-instance"- | registerMultiInstance registerOptions ]- ++ [ "--force-files"- | registerSuppressFilesCheck registerOptions ]- ++ verbosityOpts hpi verbosity--unregisterInvocation :: HcPkgInfo -> Verbosity -> PackageDB -> PackageId- -> ProgramInvocation-unregisterInvocation hpi verbosity packagedb pkgid =- programInvocation (hcPkgProgram hpi) $- ["unregister", packageDbOpts hpi packagedb, prettyShow pkgid]- ++ verbosityOpts hpi verbosity-+ | registerMultiInstance registerOptions = "update"+ | otherwise = "register" -recacheInvocation :: HcPkgInfo -> Verbosity -> PackageDB- -> ProgramInvocation-recacheInvocation hpi verbosity packagedb =- programInvocation (hcPkgProgram hpi) $- ["recache", packageDbOpts hpi packagedb]- ++ verbosityOpts hpi verbosity+ args file =+ [cmdname, file]+ ++ packageDbStackOpts hpi packagedbs+ ++ [ "--enable-multi-instance"+ | registerMultiInstance registerOptions+ ]+ ++ [ "--force-files"+ | registerSuppressFilesCheck registerOptions+ ]+ ++ verbosityOpts hpi verbosity +unregisterInvocation+ :: HcPkgInfo+ -> Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> PackageDB+ -> PackageId+ -> ProgramInvocation+unregisterInvocation hpi verbosity mbWorkDir packagedb pkgid =+ programInvocationCwd mbWorkDir (hcPkgProgram hpi) $+ ["unregister", packageDbOpts hpi packagedb, prettyShow pkgid]+ ++ verbosityOpts hpi verbosity -exposeInvocation :: HcPkgInfo -> Verbosity -> PackageDB -> PackageId- -> ProgramInvocation-exposeInvocation hpi verbosity packagedb pkgid =- programInvocation (hcPkgProgram hpi) $- ["expose", packageDbOpts hpi packagedb, prettyShow pkgid]- ++ verbosityOpts hpi verbosity+recacheInvocation+ :: HcPkgInfo+ -> Verbosity+ -> Maybe (SymbolicPath CWD (Dir from))+ -> PackageDBS from+ -> ProgramInvocation+recacheInvocation hpi verbosity mbWorkDir packagedb =+ programInvocationCwd mbWorkDir (hcPkgProgram hpi) $+ ["recache", packageDbOpts hpi packagedb]+ ++ verbosityOpts hpi verbosity -describeInvocation :: HcPkgInfo -> Verbosity -> PackageDBStack -> PackageId- -> ProgramInvocation-describeInvocation hpi verbosity packagedbs pkgid =- programInvocation (hcPkgProgram hpi) $- ["describe", prettyShow pkgid]- ++ packageDbStackOpts hpi packagedbs- ++ verbosityOpts hpi verbosity+exposeInvocation+ :: HcPkgInfo+ -> Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> PackageDB+ -> PackageId+ -> ProgramInvocation+exposeInvocation hpi verbosity mbWorkDir packagedb pkgid =+ programInvocationCwd mbWorkDir (hcPkgProgram hpi) $+ ["expose", packageDbOpts hpi packagedb, prettyShow pkgid]+ ++ verbosityOpts hpi verbosity -hideInvocation :: HcPkgInfo -> Verbosity -> PackageDB -> PackageId- -> ProgramInvocation-hideInvocation hpi verbosity packagedb pkgid =- programInvocation (hcPkgProgram hpi) $- ["hide", packageDbOpts hpi packagedb, prettyShow pkgid]- ++ verbosityOpts hpi verbosity+describeInvocation+ :: HcPkgInfo+ -> Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> PackageDBStack+ -> PackageId+ -> ProgramInvocation+describeInvocation hpi verbosity mbWorkDir packagedbs pkgid =+ programInvocationCwd mbWorkDir (hcPkgProgram hpi) $+ ["describe", prettyShow pkgid]+ ++ packageDbStackOpts hpi packagedbs+ ++ verbosityOpts hpi verbosity +hideInvocation+ :: HcPkgInfo+ -> Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> PackageDB+ -> PackageId+ -> ProgramInvocation+hideInvocation hpi verbosity mbWorkDir packagedb pkgid =+ programInvocationCwd mbWorkDir (hcPkgProgram hpi) $+ ["hide", packageDbOpts hpi packagedb, prettyShow pkgid]+ ++ verbosityOpts hpi verbosity -dumpInvocation :: HcPkgInfo -> Verbosity -> PackageDB -> ProgramInvocation-dumpInvocation hpi _verbosity packagedb =- (programInvocation (hcPkgProgram hpi) args) {- progInvokeOutputEncoding = IOEncodingUTF8+dumpInvocation+ :: HcPkgInfo+ -> Verbosity+ -> Maybe (SymbolicPath CWD (Dir from))+ -> PackageDBX (SymbolicPath from (Dir PkgDB))+ -> ProgramInvocation+dumpInvocation hpi _verbosity mbWorkDir packagedb =+ (programInvocationCwd mbWorkDir (hcPkgProgram hpi) args)+ { progInvokeOutputEncoding = IOEncodingUTF8 } where- args = ["dump", packageDbOpts hpi packagedb]+ args =+ ["dump", packageDbOpts hpi packagedb] ++ verbosityOpts hpi silent- -- We use verbosity level 'silent' because it is important that we- -- do not contaminate the output with info/debug messages. -listInvocation :: HcPkgInfo -> Verbosity -> PackageDB -> ProgramInvocation-listInvocation hpi _verbosity packagedb =- (programInvocation (hcPkgProgram hpi) args) {- progInvokeOutputEncoding = IOEncodingUTF8+-- We use verbosity level 'silent' because it is important that we+-- do not contaminate the output with info/debug messages.++listInvocation+ :: HcPkgInfo+ -> Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> PackageDB+ -> ProgramInvocation+listInvocation hpi _verbosity mbWorkDir packagedb =+ (programInvocationCwd mbWorkDir (hcPkgProgram hpi) args)+ { progInvokeOutputEncoding = IOEncodingUTF8 } where- args = ["list", "--simple-output", packageDbOpts hpi packagedb]+ args =+ ["list", "--simple-output", packageDbOpts hpi packagedb] ++ verbosityOpts hpi silent- -- We use verbosity level 'silent' because it is important that we- -- do not contaminate the output with info/debug messages. +-- We use verbosity level 'silent' because it is important that we+-- do not contaminate the output with info/debug messages. -packageDbStackOpts :: HcPkgInfo -> PackageDBStack -> [String]+packageDbStackOpts :: HcPkgInfo -> PackageDBStackS from -> [String] packageDbStackOpts hpi dbstack | noPkgDbStack hpi = [packageDbOpts hpi (registrationPackageDB dbstack)]- | otherwise = case dbstack of- (GlobalPackageDB:UserPackageDB:dbs) -> "--global"- : "--user"- : map specific dbs- (GlobalPackageDB:dbs) -> "--global"- : ("--no-user-" ++ packageDbFlag hpi)- : map specific dbs- _ -> ierror- where- specific (SpecificPackageDB db) = "--" ++ packageDbFlag hpi ++ "=" ++ db- specific _ = ierror- ierror :: a- ierror = error ("internal error: unexpected package db stack: " ++ show dbstack)+ | otherwise = case dbstack of+ (GlobalPackageDB : UserPackageDB : dbs) ->+ "--global"+ : "--user"+ : map specific dbs+ (GlobalPackageDB : dbs) ->+ "--global"+ : ("--no-user-" ++ packageDbFlag hpi)+ : map specific dbs+ _ -> ierror+ where+ specific (SpecificPackageDB db) = "--" ++ packageDbFlag hpi ++ "=" ++ interpretSymbolicPathCWD db+ specific _ = ierror+ ierror :: a+ ierror = error ("internal error: unexpected package db stack: " ++ show dbstack) packageDbFlag :: HcPkgInfo -> String packageDbFlag hpi- | flagPackageConf hpi- = "package-conf"- | otherwise- = "package-db"+ | flagPackageConf hpi =+ "package-conf"+ | otherwise =+ "package-db" -packageDbOpts :: HcPkgInfo -> PackageDB -> String-packageDbOpts _ GlobalPackageDB = "--global"-packageDbOpts _ UserPackageDB = "--user"-packageDbOpts hpi (SpecificPackageDB db) = "--" ++ packageDbFlag hpi ++ "=" ++ db+packageDbOpts :: HcPkgInfo -> PackageDBX (SymbolicPath from (Dir PkgDB)) -> String+packageDbOpts _ GlobalPackageDB = "--global"+packageDbOpts _ UserPackageDB = "--user"+packageDbOpts hpi (SpecificPackageDB db) = "--" ++ packageDbFlag hpi ++ "=" ++ interpretSymbolicPathCWD db verbosityOpts :: HcPkgInfo -> Verbosity -> [String] verbosityOpts hpi v- | noVerboseFlag hpi- = []+ | noVerboseFlag hpi =+ [] | v >= deafening = ["-v2"]- | v == silent = ["-v0"]- | otherwise = []+ | v == silent = ["-v0"]+ | otherwise = []
src/Distribution/Simple/Program/Hpc.hs view
@@ -1,7 +1,9 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Hpc -- Copyright : Thomas Tuegel 2011@@ -10,22 +12,20 @@ -- Portability : portable -- -- This module provides an library interface to the @hpc@ program.- module Distribution.Simple.Program.Hpc- ( markup- , union- ) where+ ( markup+ , union+ ) where -import Prelude () import Distribution.Compat.Prelude--import System.Directory (makeRelativeToCurrentDirectory)+import Prelude () import Distribution.ModuleName+import Distribution.Pretty import Distribution.Simple.Program.Run import Distribution.Simple.Program.Types-import Distribution.Pretty import Distribution.Simple.Utils+import Distribution.Utils.Path import Distribution.Verbosity import Distribution.Version @@ -36,74 +36,107 @@ -- first path in the list. This means that e.g. test suites that import their -- library as a dependency can still work, but those that include the library -- modules directly (in other-modules) don't.-markup :: ConfiguredProgram- -> Version- -> Verbosity- -> FilePath -- ^ Path to .tix file- -> [FilePath] -- ^ Paths to .mix file directories- -> FilePath -- ^ Path where html output should be located- -> [ModuleName] -- ^ List of modules to include in the report- -> IO ()-markup hpc hpcVer verbosity tixFile hpcDirs destDir included = do- hpcDirs' <- if withinRange hpcVer (orLaterVersion version07)- then return hpcDirs- else do- warn verbosity $ "Your version of HPC (" ++ prettyShow hpcVer- ++ ") does not properly handle multiple search paths. "- ++ "Coverage report generation may fail unexpectedly. These "- ++ "issues are addressed in version 0.7 or later (GHC 7.8 or "- ++ "later)."- ++ if null droppedDirs- then ""- else " The following search paths have been abandoned: "- ++ show droppedDirs- return passedDirs+markup+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -> ConfiguredProgram+ -> Version+ -> Verbosity+ -> SymbolicPath Pkg File+ -- ^ Path to .tix file+ -> [SymbolicPath Pkg (Dir Mix)]+ -- ^ Paths to .mix file directories+ -> SymbolicPath Pkg (Dir Artifacts)+ -- ^ Path where html output should be located+ -> [ModuleName]+ -- ^ List of modules to include in the report+ -> IO ()+markup mbWorkDir hpc hpcVer verbosity tixFile hpcDirs destDir included = do+ hpcDirs' <-+ if withinRange hpcVer (orLaterVersion version07)+ then return hpcDirs+ else do+ warn verbosity $+ "Your version of HPC ("+ ++ prettyShow hpcVer+ ++ ") does not properly handle multiple search paths. "+ ++ "Coverage report generation may fail unexpectedly. These "+ ++ "issues are addressed in version 0.7 or later (GHC 7.8 or "+ ++ "later)."+ ++ if null droppedDirs+ then ""+ else+ " The following search paths have been abandoned: "+ ++ show droppedDirs+ return passedDirs - -- Prior to GHC 8.0, hpc assumes all .mix paths are relative.- hpcDirs'' <- traverse makeRelativeToCurrentDirectory hpcDirs'+ -- Prior to GHC 8.0, hpc assumes all .mix paths are relative.+ hpcDirs'' <- traverse (tryMakeRelative mbWorkDir) hpcDirs' - runProgramInvocation verbosity- (markupInvocation hpc tixFile hpcDirs'' destDir included)+ runProgramInvocation+ verbosity+ (markupInvocation mbWorkDir hpc tixFile hpcDirs'' destDir included) where version07 = mkVersion [0, 7] (passedDirs, droppedDirs) = splitAt 1 hpcDirs -markupInvocation :: ConfiguredProgram- -> FilePath -- ^ Path to .tix file- -> [FilePath] -- ^ Paths to .mix file directories- -> FilePath -- ^ Path where html output should be- -- located- -> [ModuleName] -- ^ List of modules to include- -> ProgramInvocation-markupInvocation hpc tixFile hpcDirs destDir included =- let args = [ "markup", tixFile- , "--destdir=" ++ destDir- ]- ++ map ("--hpcdir=" ++) hpcDirs- ++ ["--include=" ++ prettyShow moduleName- | moduleName <- included ]- in programInvocation hpc args+markupInvocation+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -> ConfiguredProgram+ -> SymbolicPath Pkg File+ -- ^ Path to .tix file+ -> [SymbolicPath Pkg (Dir Mix)]+ -- ^ Paths to .mix file directories+ -> SymbolicPath Pkg (Dir Artifacts)+ -- ^ Path where html output should be+ -- located+ -> [ModuleName]+ -- ^ List of modules to include+ -> ProgramInvocation+markupInvocation mbWorkDir hpc tixFile hpcDirs destDir included =+ let args =+ [ "markup"+ , getSymbolicPath tixFile+ , "--destdir=" ++ getSymbolicPath destDir+ ]+ ++ map (("--hpcdir=" ++) . getSymbolicPath) hpcDirs+ ++ [ "--include=" ++ prettyShow moduleName+ | moduleName <- included+ ]+ in programInvocationCwd mbWorkDir hpc args -union :: ConfiguredProgram- -> Verbosity- -> [FilePath] -- ^ Paths to .tix files- -> FilePath -- ^ Path to resultant .tix file- -> [ModuleName] -- ^ List of modules to exclude from union- -> IO ()-union hpc verbosity tixFiles outFile excluded =- runProgramInvocation verbosity- (unionInvocation hpc tixFiles outFile excluded)+union+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -> ConfiguredProgram+ -> Verbosity+ -> [SymbolicPath Pkg File]+ -- ^ Paths to .tix files+ -> SymbolicPath Pkg File+ -- ^ Path to resultant .tix file+ -> [ModuleName]+ -- ^ List of modules to exclude from union+ -> IO ()+union mbWorkDir hpc verbosity tixFiles outFile excluded =+ runProgramInvocation+ verbosity+ (unionInvocation mbWorkDir hpc tixFiles outFile excluded) -unionInvocation :: ConfiguredProgram- -> [FilePath] -- ^ Paths to .tix files- -> FilePath -- ^ Path to resultant .tix file- -> [ModuleName] -- ^ List of modules to exclude from union- -> ProgramInvocation-unionInvocation hpc tixFiles outFile excluded =- programInvocation hpc $ concat- [ ["sum", "--union"]- , tixFiles- , ["--output=" ++ outFile]- , ["--exclude=" ++ prettyShow moduleName- | moduleName <- excluded ]+unionInvocation+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -> ConfiguredProgram+ -> [SymbolicPath Pkg File]+ -- ^ Paths to .tix files+ -> SymbolicPath Pkg File+ -- ^ Path to resultant .tix file+ -> [ModuleName]+ -- ^ List of modules to exclude from union+ -> ProgramInvocation+unionInvocation mbWorkDir hpc tixFiles outFile excluded =+ programInvocationCwd mbWorkDir hpc $+ concat+ [ ["sum", "--union"]+ , map getSymbolicPath tixFiles+ , ["--output=" ++ getSymbolicPath outFile]+ , [ "--exclude=" ++ prettyShow moduleName+ | moduleName <- excluded ]+ ]
src/Distribution/Simple/Program/Internal.hs view
@@ -1,4 +1,5 @@ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Internal --@@ -6,42 +7,47 @@ -- Portability : portable -- -- Internal utilities used by Distribution.Simple.Program.*.--module Distribution.Simple.Program.Internal (- stripExtractVersion,+module Distribution.Simple.Program.Internal+ ( stripExtractVersion ) where -import Prelude () import Distribution.Compat.Prelude-import Distribution.Utils.Generic(safeTail)+import Distribution.Utils.Generic (safeTail)+import Prelude () -- | Extract the version number from the output of 'strip --version'. ----- Invoking "strip --version" gives very inconsistent results. We ignore--- everything in parentheses (see #2497), look for the first word that starts--- with a number, and try parsing out the first two components of it. Non-GNU--- 'strip' doesn't appear to have a version flag.+-- Invoking "strip --version" gives very inconsistent results. We+-- ignore everything in parentheses (see #2497), look for the first+-- word that starts with a number, and try parsing out the first two+-- components of it. Non-GNU, non-LLVM 'strip' doesn't appear to have+-- a version flag. stripExtractVersion :: String -> String stripExtractVersion str =- let numeric "" = False- numeric (x:_) = isDigit x+ let numeric "" = False+ numeric (x : _) = isDigit x + closingParentheses =+ [ ")"+ , -- LLVM strip outputs "llvm-strip, compatible with GNU strip\nLLVM (http://llvm.org/):\n..."+ "):"+ ]+ -- Filter out everything in parentheses. filterPar' :: Int -> [String] -> [String]- filterPar' _ [] = []- filterPar' n (x:xs)- | n >= 0 && "(" `isPrefixOf` x = filterPar' (n+1) ((safeTail x):xs)- | n > 0 && ")" `isSuffixOf` x = filterPar' (n-1) xs- | n > 0 = filterPar' n xs- | otherwise = x:filterPar' n xs+ filterPar' _ [] = []+ filterPar' n (x : xs)+ | n >= 0 && "(" `isPrefixOf` x = filterPar' (n + 1) ((safeTail x) : xs)+ | n > 0 && any (`isSuffixOf` x) closingParentheses = filterPar' (n - 1) xs+ | n > 0 = filterPar' n xs+ | otherwise = x : filterPar' n xs filterPar = filterPar' 0-- in case dropWhile (not . numeric) (filterPar . words $ str) of- (ver:_) ->- -- take the first two version components- let isDot = (== '.')- (major, rest) = break isDot ver- minor = takeWhile isDigit (dropWhile isDot rest)- in major ++ "." ++ minor- _ -> ""+ in case dropWhile (not . numeric) (filterPar . words $ str) of+ (ver : _) ->+ -- take the first two version components+ let isDot = (== '.')+ (major, rest) = break isDot ver+ minor = takeWhile isDigit (dropWhile isDot rest)+ in major ++ "." ++ minor+ _ -> ""
src/Distribution/Simple/Program/Ld.hs view
@@ -1,7 +1,9 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Ld -- Copyright : Duncan Coutts 2009@@ -10,83 +12,99 @@ -- Portability : portable -- -- This module provides an library interface to the @ld@ linker program.--module Distribution.Simple.Program.Ld (- combineObjectFiles,+module Distribution.Simple.Program.Ld+ ( combineObjectFiles ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () import Distribution.Simple.Compiler (arResponseFilesSupported)-import Distribution.Simple.LocalBuildInfo (LocalBuildInfo(..))+import Distribution.Simple.Flag+ ( fromFlagOrDefault+ )+import Distribution.Simple.LocalBuildInfo (LocalBuildInfo (..), mbWorkDirLBI) import Distribution.Simple.Program.ResponseFile- ( withResponseFile )+ ( withResponseFile+ ) import Distribution.Simple.Program.Run- ( ProgramInvocation, programInvocation, multiStageProgramInvocation- , runProgramInvocation )+ ( ProgramInvocation+ , multiStageProgramInvocation+ , programInvocationCwd+ , runProgramInvocation+ ) import Distribution.Simple.Program.Types- ( ConfiguredProgram(..) )-import Distribution.Simple.Setup- ( fromFlagOrDefault, configUseResponseFiles )+ ( ConfiguredProgram (..)+ )+import Distribution.Simple.Setup.Config+ ( configUseResponseFiles+ ) import Distribution.Simple.Utils- ( defaultTempFileOptions )+ ( defaultTempFileOptions+ )+import Distribution.Utils.Path import Distribution.Verbosity- ( Verbosity )+ ( Verbosity+ ) import System.Directory- ( renameFile )-import System.FilePath- ( (<.>), takeDirectory )+ ( renameFile+ ) -- | Call @ld -r@ to link a bunch of object files together.----combineObjectFiles :: Verbosity -> LocalBuildInfo -> ConfiguredProgram- -> FilePath -> [FilePath] -> IO ()-combineObjectFiles verbosity lbi ld target files = do-+combineObjectFiles+ :: Verbosity+ -> LocalBuildInfo+ -> ConfiguredProgram+ -> SymbolicPath Pkg File+ -> [SymbolicPath Pkg File]+ -> IO ()+combineObjectFiles verbosity lbi ldProg target files = do -- Unlike "ar", the "ld" tool is not designed to be used with xargs. That is, -- if we have more object files than fit on a single command line then we -- have a slight problem. What we have to do is link files in batches into -- a temp object file and then include that one in the next batch. - let simpleArgs = ["-r", "-o", target]+ let+ -- See Note [Symbolic paths] in Distribution.Utils.Path+ u :: SymbolicPath Pkg to -> FilePath+ u = interpretSymbolicPathCWD+ i = interpretSymbolicPath mbWorkDir+ mbWorkDir = mbWorkDirLBI lbi - initialArgs = ["-r", "-o", target]- middleArgs = ["-r", "-o", target, tmpfile]- finalArgs = middleArgs+ simpleArgs = ["-r", "-o", u target]+ initialArgs = ["-r", "-o", u target]+ middleArgs = ["-r", "-o", u target, u tmpfile]+ finalArgs = middleArgs - simple = programInvocation ld simpleArgs- initial = programInvocation ld initialArgs- middle = programInvocation ld middleArgs- final = programInvocation ld finalArgs+ ld = programInvocationCwd (mbWorkDirLBI lbi) ldProg+ simple = ld simpleArgs+ initial = ld initialArgs+ middle = ld middleArgs+ final = ld finalArgs - targetDir = takeDirectory target+ invokeWithResponseFile :: FilePath -> ProgramInvocation+ invokeWithResponseFile atFile =+ ld $ simpleArgs ++ ['@' : atFile] - invokeWithResponesFile :: FilePath -> ProgramInvocation- invokeWithResponesFile atFile =- programInvocation ld $ simpleArgs ++ ['@' : atFile]+ oldVersionManualOverride =+ fromFlagOrDefault False $ configUseResponseFiles $ configFlags lbi+ -- Whether ghc's ar supports response files is a good proxy for+ -- whether ghc's ld supports them as well.+ responseArgumentsNotSupported =+ not (arResponseFilesSupported (compiler lbi)) - oldVersionManualOverride =- fromFlagOrDefault False $ configUseResponseFiles $ configFlags lbi- -- Whether ghc's ar supports response files is a good proxy for- -- whether ghc's ld supports them as well.- responseArgumentsNotSupported =- not (arResponseFilesSupported (compiler lbi))+ run :: [ProgramInvocation] -> IO ()+ run [] = return ()+ run [inv] = runProgramInvocation verbosity inv+ run (inv : invs) = do+ runProgramInvocation verbosity inv+ renameFile (i target) (i tmpfile)+ run invs if oldVersionManualOverride || responseArgumentsNotSupported- then- run $ multiStageProgramInvocation simple (initial, middle, final) files- else- withResponseFile verbosity defaultTempFileOptions targetDir "ld.rsp" Nothing files $- \path -> runProgramInvocation verbosity $ invokeWithResponesFile path-+ then run $ multiStageProgramInvocation simple (initial, middle, final) (map getSymbolicPath files)+ else withResponseFile verbosity defaultTempFileOptions "ld.rsp" Nothing (map getSymbolicPath files) $+ \path -> runProgramInvocation verbosity $ invokeWithResponseFile path where- tmpfile = target <.> "tmp" -- perhaps should use a proper temp file-- run :: [ProgramInvocation] -> IO ()- run [] = return ()- run [inv] = runProgramInvocation verbosity inv- run (inv:invs) = do runProgramInvocation verbosity inv- renameFile target tmpfile- run invs+ tmpfile = target <.> "tmp" -- perhaps should use a proper temp file
src/Distribution/Simple/Program/ResponseFile.hs view
@@ -1,6 +1,11 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RankNTypes #-}+ ----------------------------------------------------------------------------++----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.ResponseFile -- Copyright : (c) Sergey Vinokurov 2017@@ -8,30 +13,35 @@ -- -- Maintainer : cabal-devel@haskell.org -- Created : 23 July 2017------------------------------------------------------------------------------ module Distribution.Simple.Program.ResponseFile (withResponseFile) where +import System.IO (TextEncoding, hClose, hPutStr, hSetEncoding) import Prelude ()-import System.IO (TextEncoding, hSetEncoding, hPutStr, hClose) import Distribution.Compat.Prelude-import Distribution.Simple.Utils (TempFileOptions, withTempFileEx, debug)+import Distribution.Simple.Utils (TempFileOptions, debug, withTempFileEx)+import Distribution.Utils.Path import Distribution.Verbosity withResponseFile :: Verbosity -> TempFileOptions- -> FilePath -- ^ Working directory to create response file in.- -> FilePath -- ^ Template for response file name.- -> Maybe TextEncoding -- ^ Encoding to use for response file contents.- -> [String] -- ^ Arguments to put into response file.+ -> String+ -- ^ Template for response file name.+ -> Maybe TextEncoding+ -- ^ Encoding to use for response file contents.+ -> [String]+ -- ^ Arguments to put into response file. -> (FilePath -> IO a) -> IO a-withResponseFile verbosity tmpFileOpts workDir fileNameTemplate encoding arguments f =- withTempFileEx tmpFileOpts workDir fileNameTemplate $ \responseFileName hf -> do+withResponseFile verbosity tmpFileOpts fileNameTemplate encoding arguments f =+ withTempFileEx tmpFileOpts fileNameTemplate $ \responsePath hf -> do+ let responseFileName = getSymbolicPath responsePath traverse_ (hSetEncoding hf) encoding- let responseContents = unlines $ map escapeResponseFileArg arguments+ let responseContents =+ unlines $+ map escapeResponseFileArg $+ arguments hPutStr hf responseContents hClose hf debug verbosity $ responseFileName ++ " contents: <<<"@@ -51,10 +61,9 @@ escape :: String -> Char -> String escape cs c = case c of- '\\' -> c:'\\':cs- '\'' -> c:'\\':cs- '"' -> c:'\\':cs- _ | isSpace c -> c:'\\':cs- | otherwise -> c:cs--+ '\\' -> c : '\\' : cs+ '\'' -> c : '\\' : cs+ '"' -> c : '\\' : cs+ _+ | isSpace c -> c : '\\' : cs+ | otherwise -> c : cs
src/Distribution/Simple/Program/Run.hs view
@@ -1,8 +1,11 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Run -- Copyright : Duncan Coutts 2009@@ -12,36 +15,36 @@ -- -- This module provides a data type for program invocations and functions to -- run them.--module Distribution.Simple.Program.Run (- ProgramInvocation(..),- IOEncoding(..),- emptyProgramInvocation,- simpleProgramInvocation,- programInvocation,- multiStageProgramInvocation,-- runProgramInvocation,- getProgramInvocationOutput,- getProgramInvocationLBS,- getProgramInvocationOutputAndErrors,-- getEffectiveEnvironment,+module Distribution.Simple.Program.Run+ ( ProgramInvocation (..)+ , IOEncoding (..)+ , emptyProgramInvocation+ , simpleProgramInvocation+ , programInvocation+ , programInvocationCwd+ , multiStageProgramInvocation+ , runProgramInvocation+ , getProgramInvocationOutput+ , getProgramInvocationLBS+ , getProgramInvocationOutputAndErrors+ , getProgramInvocationLBSAndErrors+ , getEffectiveEnvironment+ , getFullEnvironment ) where import Distribution.Compat.Prelude import Prelude () import Distribution.Compat.Environment+import Distribution.Simple.Errors import Distribution.Simple.Program.Types import Distribution.Simple.Utils import Distribution.Utils.Generic+import Distribution.Utils.Path import Distribution.Verbosity -import System.FilePath (searchPathSeparator)- import qualified Data.ByteString.Lazy as LBS-import qualified Data.Map as Map+import qualified Data.Map as Map -- | Represents a specific invocation of a specific program. --@@ -49,180 +52,220 @@ -- and actually doing it. This provides the opportunity to the caller to -- adjust how the program will be called. These invocations can either be run -- directly or turned into shell or batch scripts.----data ProgramInvocation = ProgramInvocation {- progInvokePath :: FilePath,- progInvokeArgs :: [String],- progInvokeEnv :: [(String, Maybe String)],- -- Extra paths to add to PATH- progInvokePathEnv :: [FilePath],- progInvokeCwd :: Maybe FilePath,- progInvokeInput :: Maybe IOData,- progInvokeInputEncoding :: IOEncoding, -- ^ TODO: remove this, make user decide when constructing 'progInvokeInput'.- progInvokeOutputEncoding :: IOEncoding- }+data ProgramInvocation = ProgramInvocation+ { progInvokePath :: FilePath+ , progInvokeArgs :: [String]+ , progInvokeEnv :: [(String, Maybe String)]+ , progInvokeCwd :: Maybe FilePath+ , progInvokeInput :: Maybe IOData+ , progInvokeInputEncoding :: IOEncoding+ -- ^ TODO: remove this, make user decide when constructing 'progInvokeInput'.+ , progInvokeOutputEncoding :: IOEncoding+ , progInvokeWhen :: IO Bool+ } -data IOEncoding = IOEncodingText -- locale mode text- | IOEncodingUTF8 -- always utf8+data IOEncoding+ = IOEncodingText -- locale mode text+ | IOEncodingUTF8 -- always utf8 encodeToIOData :: IOEncoding -> IOData -> IOData-encodeToIOData _ iod@(IODataBinary _) = iod-encodeToIOData IOEncodingText iod@(IODataText _) = iod-encodeToIOData IOEncodingUTF8 (IODataText str) = IODataBinary (toUTF8LBS str)+encodeToIOData _ iod@(IODataBinary _) = iod+encodeToIOData IOEncodingText iod@(IODataText _) = iod+encodeToIOData IOEncodingUTF8 (IODataText str) = IODataBinary (toUTF8LBS str) emptyProgramInvocation :: ProgramInvocation emptyProgramInvocation =- ProgramInvocation {- progInvokePath = "",- progInvokeArgs = [],- progInvokeEnv = [],- progInvokePathEnv = [],- progInvokeCwd = Nothing,- progInvokeInput = Nothing,- progInvokeInputEncoding = IOEncodingText,- progInvokeOutputEncoding = IOEncodingText- }+ ProgramInvocation+ { progInvokePath = ""+ , progInvokeArgs = []+ , progInvokeEnv = []+ , progInvokeCwd = Nothing+ , progInvokeInput = Nothing+ , progInvokeInputEncoding = IOEncodingText+ , progInvokeOutputEncoding = IOEncodingText+ , progInvokeWhen = pure True+ } -simpleProgramInvocation :: FilePath -> [String] -> ProgramInvocation+simpleProgramInvocation+ :: FilePath+ -> [String]+ -> ProgramInvocation simpleProgramInvocation path args =- emptyProgramInvocation {- progInvokePath = path,- progInvokeArgs = args- }+ emptyProgramInvocation+ { progInvokePath = path+ , progInvokeArgs = args+ } -programInvocation :: ConfiguredProgram -> [String] -> ProgramInvocation+programInvocation+ :: ConfiguredProgram+ -> [String]+ -> ProgramInvocation programInvocation prog args =- emptyProgramInvocation {- progInvokePath = programPath prog,- progInvokeArgs = programDefaultArgs prog- ++ args- ++ programOverrideArgs prog,- progInvokeEnv = programOverrideEnv prog- }+ emptyProgramInvocation+ { progInvokePath = programPath prog+ , progInvokeArgs =+ programDefaultArgs prog+ ++ args+ ++ programOverrideArgs prog+ , progInvokeEnv = programOverrideEnv prog+ } +programInvocationCwd+ :: forall to+ . Maybe (SymbolicPath CWD (Dir to))+ -> ConfiguredProgram+ -> [String]+ -> ProgramInvocation+programInvocationCwd mbWorkDir prog args =+ (programInvocation prog args)+ { progInvokeCwd = fmap getSymbolicPath mbWorkDir+ } runProgramInvocation :: Verbosity -> ProgramInvocation -> IO ()-runProgramInvocation verbosity- ProgramInvocation {- progInvokePath = path,- progInvokeArgs = args,- progInvokeEnv = [],- progInvokePathEnv = [],- progInvokeCwd = Nothing,- progInvokeInput = Nothing- } =- rawSystemExit verbosity path args--runProgramInvocation verbosity- ProgramInvocation {- progInvokePath = path,- progInvokeArgs = args,- progInvokeEnv = envOverrides,- progInvokePathEnv = extraPath,- progInvokeCwd = mcwd,- progInvokeInput = Nothing- } = do- pathOverride <- getExtraPathEnv envOverrides extraPath- menv <- getEffectiveEnvironment (envOverrides ++ pathOverride)- exitCode <- rawSystemIOWithEnv verbosity- path args- mcwd menv- Nothing Nothing Nothing- when (exitCode /= ExitSuccess) $- exitWith exitCode--runProgramInvocation verbosity- ProgramInvocation {- progInvokePath = path,- progInvokeArgs = args,- progInvokeEnv = envOverrides,- progInvokePathEnv = extraPath,- progInvokeCwd = mcwd,- progInvokeInput = Just inputStr,- progInvokeInputEncoding = encoding- } = do- pathOverride <- getExtraPathEnv envOverrides extraPath- menv <- getEffectiveEnvironment (envOverrides ++ pathOverride)- (_, errors, exitCode) <- rawSystemStdInOut verbosity- path args- mcwd menv- (Just input) IODataModeBinary+runProgramInvocation+ verbosity+ ProgramInvocation+ { progInvokePath = path+ , progInvokeArgs = args+ , progInvokeEnv = []+ , progInvokeCwd = Nothing+ , progInvokeInput = Nothing+ } =+ rawSystemExit verbosity Nothing path args+runProgramInvocation+ verbosity+ ProgramInvocation+ { progInvokePath = path+ , progInvokeArgs = args+ , progInvokeEnv = envOverrides+ , progInvokeCwd = mcwd+ , progInvokeInput = Nothing+ } = do+ menv <- getEffectiveEnvironment envOverrides+ maybeExit $+ rawSystemIOWithEnv+ verbosity+ path+ args+ mcwd+ menv+ Nothing+ Nothing+ Nothing+runProgramInvocation+ verbosity+ ProgramInvocation+ { progInvokePath = path+ , progInvokeArgs = args+ , progInvokeEnv = envOverrides+ , progInvokeCwd = mcwd+ , progInvokeInput = Just inputStr+ , progInvokeInputEncoding = encoding+ } = do+ menv <- getEffectiveEnvironment envOverrides+ (_, errors, exitCode) <-+ rawSystemStdInOut+ verbosity+ path+ args+ mcwd+ menv+ (Just input)+ IODataModeBinary when (exitCode /= ExitSuccess) $- die' verbosity $ "'" ++ path ++ "' exited with an error:\n" ++ errors- where- input = encodeToIOData encoding inputStr+ dieWithException verbosity $+ RunProgramInvocationException path errors+ where+ input = encodeToIOData encoding inputStr getProgramInvocationOutput :: Verbosity -> ProgramInvocation -> IO String getProgramInvocationOutput verbosity inv = do- (output, errors, exitCode) <- getProgramInvocationOutputAndErrors verbosity inv- when (exitCode /= ExitSuccess) $- die' verbosity $ "'" ++ progInvokePath inv ++ "' exited with an error:\n" ++ errors- return output+ (output, errors, exitCode) <- getProgramInvocationOutputAndErrors verbosity inv+ when (exitCode /= ExitSuccess) $+ die' verbosity $+ "'" ++ progInvokePath inv ++ "' exited with an error:\n" ++ errors+ return output getProgramInvocationLBS :: Verbosity -> ProgramInvocation -> IO LBS.ByteString getProgramInvocationLBS verbosity inv = do- (output, errors, exitCode) <- getProgramInvocationIODataAndErrors verbosity inv IODataModeBinary- when (exitCode /= ExitSuccess) $- die' verbosity $ "'" ++ progInvokePath inv ++ "' exited with an error:\n" ++ errors- return output+ (output, errors, exitCode) <- getProgramInvocationIODataAndErrors verbosity inv IODataModeBinary+ when (exitCode /= ExitSuccess) $+ dieWithException verbosity $+ GetProgramInvocationLBSException (progInvokePath inv) errors+ return output -getProgramInvocationOutputAndErrors :: Verbosity -> ProgramInvocation- -> IO (String, String, ExitCode)+getProgramInvocationOutputAndErrors+ :: Verbosity+ -> ProgramInvocation+ -> IO (String, String, ExitCode) getProgramInvocationOutputAndErrors verbosity inv = case progInvokeOutputEncoding inv of- IOEncodingText -> do- (output, errors, exitCode) <- getProgramInvocationIODataAndErrors verbosity inv IODataModeText- return (output, errors, exitCode)- IOEncodingUTF8 -> do- (output', errors, exitCode) <- getProgramInvocationIODataAndErrors verbosity inv IODataModeBinary- return (normaliseLineEndings (fromUTF8LBS output'), errors, exitCode)+ IOEncodingText -> do+ (output, errors, exitCode) <- getProgramInvocationIODataAndErrors verbosity inv IODataModeText+ return (output, errors, exitCode)+ IOEncodingUTF8 -> do+ (output', errors, exitCode) <- getProgramInvocationIODataAndErrors verbosity inv IODataModeBinary+ return (normaliseLineEndings (fromUTF8LBS output'), errors, exitCode) +getProgramInvocationLBSAndErrors+ :: Verbosity+ -> ProgramInvocation+ -> IO (LBS.ByteString, String, ExitCode)+getProgramInvocationLBSAndErrors verbosity inv =+ getProgramInvocationIODataAndErrors verbosity inv IODataModeBinary+ getProgramInvocationIODataAndErrors- :: KnownIODataMode mode => Verbosity -> ProgramInvocation -> IODataMode mode- -> IO (mode, String, ExitCode)+ :: KnownIODataMode mode+ => Verbosity+ -> ProgramInvocation+ -> IODataMode mode+ -> IO (mode, String, ExitCode) getProgramInvocationIODataAndErrors verbosity ProgramInvocation- { progInvokePath = path- , progInvokeArgs = args- , progInvokeEnv = envOverrides- , progInvokePathEnv = extraPath- , progInvokeCwd = mcwd- , progInvokeInput = minputStr+ { progInvokePath = path+ , progInvokeArgs = args+ , progInvokeEnv = envOverrides+ , progInvokeCwd = mcwd+ , progInvokeInput = minputStr , progInvokeInputEncoding = encoding } mode = do- pathOverride <- getExtraPathEnv envOverrides extraPath- menv <- getEffectiveEnvironment (envOverrides ++ pathOverride)+ menv <- getEffectiveEnvironment envOverrides rawSystemStdInOut verbosity path args mcwd menv input mode- where- input = encodeToIOData encoding <$> minputStr--getExtraPathEnv :: [(String, Maybe String)] -> [FilePath] -> IO [(String, Maybe String)]-getExtraPathEnv _ [] = return []-getExtraPathEnv env extras = do- mb_path <- case lookup "PATH" env of- Just x -> return x- Nothing -> lookupEnv "PATH"- let extra = intercalate [searchPathSeparator] extras- path' = case mb_path of- Nothing -> extra- Just path -> extra ++ searchPathSeparator : path- return [("PATH", Just path')]+ where+ input = encodeToIOData encoding <$> minputStr -- | Return the current environment extended with the given overrides. -- If an entry is specified twice in @overrides@, the second entry takes -- precedence. ---getEffectiveEnvironment :: [(String, Maybe String)]- -> IO (Maybe [(String, String)])-getEffectiveEnvironment [] = return Nothing+-- getEffectiveEnvironment returns 'Nothing' when there are no overrides.+-- It returns an argument that is suitable to pass directly to 'CreateProcess' to+-- override the environment.+-- If you need the full environment to manipulate further, even when there are no overrides,+-- then call 'getFullEnvironment'.+getEffectiveEnvironment+ :: [(String, Maybe String)]+ -> IO (Maybe [(String, String)])+getEffectiveEnvironment [] = return Nothing getEffectiveEnvironment overrides =- fmap (Just . Map.toList . apply overrides . Map.fromList) getEnvironment+ fmap (Just . Map.toList . apply overrides . Map.fromList) getEnvironment where apply os env = foldl' (flip update) env os- update (var, Nothing) = Map.delete var+ update (var, Nothing) = Map.delete var update (var, Just val) = Map.insert var val +-- | Like 'getEffectiveEnvironment', but when no overrides are specified,+-- returns the full environment instead of 'Nothing'.+getFullEnvironment+ :: [(String, Maybe String)]+ -> IO [(String, String)]+getFullEnvironment overrides = do+ menv <- getEffectiveEnvironment overrides+ case menv of+ Just env -> return env+ Nothing -> getEnvironment+ -- | Like the unix xargs program. Useful for when we've got very long command -- lines that might overflow an OS limit on command line length and so you -- need to invoke a command multiple times to get all the args in.@@ -242,53 +285,51 @@ -- > $ middle args_2 -- > ... -- > $ final args_n--- multiStageProgramInvocation :: ProgramInvocation -> (ProgramInvocation, ProgramInvocation, ProgramInvocation) -> [String] -> [ProgramInvocation] multiStageProgramInvocation simple (initial, middle, final) args =-- let argSize inv = length (progInvokePath inv)- + foldl' (\s a -> length a + 1 + s) 1 (progInvokeArgs inv)+ let argSize inv =+ length (progInvokePath inv)+ + foldl' (\s a -> length a + 1 + s) 1 (progInvokeArgs inv) fixedArgSize = maximum (map argSize [simple, initial, middle, final])- chunkSize = maxCommandLineSize - fixedArgSize-+ chunkSize = maxCommandLineSize - fixedArgSize in case splitChunks chunkSize args of- [] -> [ simple ]-- [c] -> [ simple `appendArgs` c ]-- (c:c2:cs) | (xs, x) <- unsnocNE (c2:|cs) ->- [ initial `appendArgs` c ]- ++ [ middle `appendArgs` c'| c' <- xs ]- ++ [ final `appendArgs` x ]-+ [] -> [simple]+ [c] -> [simple `appendArgs` c]+ (c : c2 : cs)+ | (xs, x) <- unsnocNE (c2 :| cs) ->+ [initial `appendArgs` c]+ ++ [middle `appendArgs` c' | c' <- xs]+ ++ [final `appendArgs` x] where appendArgs :: ProgramInvocation -> [String] -> ProgramInvocation- inv `appendArgs` as = inv { progInvokeArgs = progInvokeArgs inv ++ as }+ inv `appendArgs` as = inv{progInvokeArgs = progInvokeArgs inv ++ as} splitChunks :: Int -> [[a]] -> [[[a]]] splitChunks len = unfoldr $ \s ->- if null s then Nothing- else Just (chunk len s)+ if null s+ then Nothing+ else Just (chunk len s) chunk :: Int -> [[a]] -> ([[a]], [[a]])- chunk len (s:_) | length s >= len = error toolong- chunk len ss = chunk' [] len ss+ chunk len (s : _) | length s >= len = error toolong+ chunk len ss = chunk' [] len ss chunk' :: [[a]] -> Int -> [[a]] -> ([[a]], [[a]])- chunk' acc len (s:ss)- | len' < len = chunk' (s:acc) (len-len'-1) ss- where len' = length s- chunk' acc _ ss = (reverse acc, ss)-- toolong = "multiStageProgramInvocation: a single program arg is larger "- ++ "than the maximum command line length!"+ chunk' acc len (s : ss)+ | len' < len = chunk' (s : acc) (len - len' - 1) ss+ where+ len' = length s+ chunk' acc _ ss = (reverse acc, ss) + toolong =+ "multiStageProgramInvocation: a single program arg is larger "+ ++ "than the maximum command line length!" ---FIXME: discover this at configure time or runtime on unix+-- FIXME: discover this at configure time or runtime on unix -- The value is 32k on Windows and posix specifies a minimum of 4k -- but all sensible unixes use more than 4k. -- we could use getSysVar ArgumentLimit but that's in the unix lib
src/Distribution/Simple/Program/Script.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE GADTs #-}+ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Script -- Copyright : Duncan Coutts 2009@@ -9,16 +11,14 @@ -- -- This module provides an library interface to the @hc-pkg@ program. -- Currently only GHC and LHC have hc-pkg programs.--module Distribution.Simple.Program.Script (-- invocationAsSystemScript,- invocationAsShellScript,- invocationAsBatchFile,+module Distribution.Simple.Program.Script+ ( invocationAsSystemScript+ , invocationAsShellScript+ , invocationAsBatchFile ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () import Distribution.Simple.Program.Run import Distribution.Simple.Utils@@ -26,89 +26,89 @@ -- | Generate a system script, either POSIX shell script or Windows batch file -- as appropriate for the given system.--- invocationAsSystemScript :: OS -> ProgramInvocation -> String invocationAsSystemScript Windows = invocationAsBatchFile-invocationAsSystemScript _ = invocationAsShellScript-+invocationAsSystemScript _ = invocationAsShellScript -- | Generate a POSIX shell script that invokes a program.--- invocationAsShellScript :: ProgramInvocation -> String invocationAsShellScript- ProgramInvocation {- progInvokePath = path,- progInvokeArgs = args,- progInvokeEnv = envExtra,- progInvokeCwd = mcwd,- progInvokeInput = minput- } = unlines $- [ "#!/bin/sh" ]- ++ concatMap setEnv envExtra- ++ [ "cd " ++ quote cwd | cwd <- maybeToList mcwd ]- ++ [ (case minput of- Nothing -> ""- Just input -> "echo " ++ quote (iodataToText input) ++ " | ")- ++ unwords (map quote $ path : args) ++ " \"$@\""]-- where- setEnv (var, Nothing) = ["unset " ++ var, "export " ++ var]- setEnv (var, Just val) = ["export " ++ var ++ "=" ++ quote val]+ ProgramInvocation+ { progInvokePath = path+ , progInvokeArgs = args+ , progInvokeEnv = envExtra+ , progInvokeCwd = mcwd+ , progInvokeInput = minput+ } =+ unlines $+ ["#!/bin/sh"]+ ++ concatMap setEnv envExtra+ ++ ["cd " ++ quote cwd | cwd <- maybeToList mcwd]+ ++ [ ( case minput of+ Nothing -> ""+ Just input -> "printf '%s' " ++ quote (iodataToText input) ++ " | "+ )+ ++ unwords (map quote $ path : args)+ ++ " \"$@\""+ ]+ where+ setEnv (var, Nothing) = ["unset " ++ var, "export " ++ var]+ setEnv (var, Just val) = ["export " ++ var ++ "=" ++ quote val] - quote :: String -> String- quote s = "'" ++ escape s ++ "'"+ quote :: String -> String+ quote s = "'" ++ escape s ++ "'" - escape [] = []- escape ('\'':cs) = "'\\''" ++ escape cs- escape (c :cs) = c : escape cs+ escape [] = []+ escape ('\'' : cs) = "'\\''" ++ escape cs+ escape (c : cs) = c : escape cs iodataToText :: IOData -> String-iodataToText (IODataText str) = str+iodataToText (IODataText str) = str iodataToText (IODataBinary lbs) = fromUTF8LBS lbs - -- | Generate a Windows batch file that invokes a program.--- invocationAsBatchFile :: ProgramInvocation -> String invocationAsBatchFile- ProgramInvocation {- progInvokePath = path,- progInvokeArgs = args,- progInvokeEnv = envExtra,- progInvokeCwd = mcwd,- progInvokeInput = minput- } = unlines $- [ "@echo off" ]- ++ map setEnv envExtra- ++ [ "cd \"" ++ cwd ++ "\"" | cwd <- maybeToList mcwd ]- ++ case minput of- Nothing ->- [ path ++ concatMap (' ':) args ]-- Just input ->- [ "(" ]- ++ [ "echo " ++ escape line | line <- lines $ iodataToText input ]- ++ [ ") | "- ++ "\"" ++ path ++ "\""- ++ concatMap (\arg -> ' ':quote arg) args ]-- where- setEnv (var, Nothing) = "set " ++ var ++ "="- setEnv (var, Just val) = "set " ++ var ++ "=" ++ escape val+ ProgramInvocation+ { progInvokePath = path+ , progInvokeArgs = args+ , progInvokeEnv = envExtra+ , progInvokeCwd = mcwd+ , progInvokeInput = minput+ } =+ unlines $+ ["@echo off"]+ ++ map setEnv envExtra+ ++ ["cd \"" ++ cwd ++ "\"" | cwd <- maybeToList mcwd]+ ++ case minput of+ Nothing ->+ [path ++ concatMap (' ' :) args]+ Just input ->+ ["("]+ ++ ["echo " ++ escape line | line <- lines $ iodataToText input]+ ++ [ ") | "+ ++ "\""+ ++ path+ ++ "\""+ ++ concatMap (\arg -> ' ' : quote arg) args+ ]+ where+ setEnv (var, Nothing) = "set " ++ var ++ "="+ setEnv (var, Just val) = "set " ++ var ++ "=" ++ escape val - quote :: String -> String- quote s = "\"" ++ escapeQ s ++ "\""+ quote :: String -> String+ quote s = "\"" ++ escapeQ s ++ "\"" - escapeQ [] = []- escapeQ ('"':cs) = "\"\"\"" ++ escapeQ cs- escapeQ (c :cs) = c : escapeQ cs+ escapeQ [] = []+ escapeQ ('"' : cs) = "\"\"\"" ++ escapeQ cs+ escapeQ (c : cs) = c : escapeQ cs - escape [] = []- escape ('|':cs) = "^|" ++ escape cs- escape ('<':cs) = "^<" ++ escape cs- escape ('>':cs) = "^>" ++ escape cs- escape ('&':cs) = "^&" ++ escape cs- escape ('(':cs) = "^(" ++ escape cs- escape (')':cs) = "^)" ++ escape cs- escape ('^':cs) = "^^" ++ escape cs- escape (c :cs) = c : escape cs+ escape [] = []+ escape ('|' : cs) = "^|" ++ escape cs+ escape ('<' : cs) = "^<" ++ escape cs+ escape ('>' : cs) = "^>" ++ escape cs+ escape ('&' : cs) = "^&" ++ escape cs+ escape ('(' : cs) = "^(" ++ escape cs+ escape (')' : cs) = "^)" ++ escape cs+ escape ('^' : cs) = "^^" ++ escape cs+ escape (c : cs) = c : escape cs
src/Distribution/Simple/Program/Strip.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Strip --@@ -9,12 +10,11 @@ -- Portability : portable -- -- This module provides an library interface to the @strip@ program.- module Distribution.Simple.Program.Strip (stripLib, stripExe)- where+where -import Prelude () import Distribution.Compat.Prelude+import Prelude () import Distribution.Simple.Program import Distribution.Simple.Utils@@ -22,54 +22,66 @@ import Distribution.Verbosity import Distribution.Version -import System.FilePath (takeBaseName)+import System.FilePath (takeBaseName) runStrip :: Verbosity -> ProgramDb -> FilePath -> [String] -> IO () runStrip verbosity progDb path args = case lookupProgram stripProgram progDb of Just strip -> runProgram verbosity strip (args ++ [path])- Nothing -> unless (buildOS == Windows) $- -- Don't bother warning on windows, we don't expect them to- -- have the strip program anyway.- warn verbosity $ "Unable to strip executable or library '"- ++ (takeBaseName path)- ++ "' (missing the 'strip' program)"+ Nothing ->+ unless (buildOS == Windows) $+ -- Don't bother warning on windows, we don't expect them to+ -- have the strip program anyway.+ warn verbosity $+ "Unable to strip executable or library '"+ ++ (takeBaseName path)+ ++ "' (missing the 'strip' program)" stripExe :: Verbosity -> Platform -> ProgramDb -> FilePath -> IO () stripExe verbosity (Platform _arch os) progdb path = runStrip verbosity progdb path args where args = case os of- OSX -> ["-x"] -- By default, stripping the ghc binary on at least- -- some OS X installations causes:- -- HSbase-3.0.o: unknown symbol `_environ'"- -- The -x flag fixes that.- _ -> []+ OSX -> ["-x"] -- By default, stripping the ghc binary on at least+ -- some OS X installations causes:+ -- HSbase-3.0.o: unknown symbol `_environ'"+ -- The -x flag fixes that.+ _ -> [] stripLib :: Verbosity -> Platform -> ProgramDb -> FilePath -> IO () stripLib verbosity (Platform arch os) progdb path = do case os of- OSX -> -- '--strip-unneeded' is not supported on OS X, iOS, AIX, or- -- Solaris. See #1630.- return ()+ OSX ->+ -- '--strip-unneeded' is not supported on OS X, iOS, AIX, or+ -- Solaris. See #1630.+ return () IOS -> return () AIX -> return () Solaris -> return ()- Windows -> -- Stripping triggers a bug in 'strip.exe' for- -- libraries with lots identically named modules. See- -- #1784.- return()- Linux | arch == I386 ->- -- Versions of 'strip' on 32-bit Linux older than 2.18 are- -- broken. See #2339.- let okVersion = orLaterVersion (mkVersion [2,18])- in case programVersion =<< lookupProgram stripProgram progdb of- Just v | withinRange v okVersion ->- runStrip verbosity progdb path args- _ -> warn verbosity $ "Unable to strip library '"- ++ (takeBaseName path)- ++ "' (version of 'strip' too old; "- ++ "requires >= 2.18 on 32-bit Linux)"- _ -> runStrip verbosity progdb path args+ OpenBSD ->+ -- '--strip-unneeded' sometimes strips too much on OpenBSD.+ -- -- See https://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/ports/lang/ghc/patches/patch-libraries_Cabal_Cabal_Distribution_Simple_Program_Strip_hs+ return ()+ Windows ->+ -- Stripping triggers a bug in 'strip.exe' for+ -- libraries with lots identically named modules. See+ -- #1784.+ return ()+ Linux+ | arch == I386 ->+ -- Versions of 'strip' on 32-bit Linux older than 2.18 are+ -- broken. See #2339.+ let okVersion = orLaterVersion (mkVersion [2, 18])+ in case programVersion =<< lookupProgram stripProgram progdb of+ Just v+ | withinRange v okVersion ->+ runStrip verbosity progdb path args+ _ ->+ warn verbosity $+ "Unable to strip library '"+ ++ (takeBaseName path)+ ++ "' (version of 'strip' too old; "+ ++ "requires >= 2.18 on 32-bit Linux)"+ _ -> runStrip verbosity progdb path args where args = ["--strip-unneeded"]
src/Distribution/Simple/Program/Types.hs view
@@ -1,10 +1,9 @@-{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-}-{-# LANGUAGE DeriveDataTypeable #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Types -- Copyright : Isaac Jones 2006, Duncan Coutts 2007-2009@@ -19,30 +18,27 @@ -- args). Configuring a program involves finding its location and if necessary -- finding its version. There's reasonable default behavior for trying to find -- \"foo\" in PATH, being able to override its location, etc.----module Distribution.Simple.Program.Types (- -- * Program and functions for constructing them- Program(..),- ProgramSearchPath,- ProgramSearchPathEntry(..),- simpleProgram,+module Distribution.Simple.Program.Types+ ( -- * Program and functions for constructing them+ Program (..)+ , ProgramSearchPath+ , ProgramSearchPathEntry (..) -- * Configured program and related functions- ConfiguredProgram(..),- programPath,- suppressOverrideArgs,- ProgArg,- ProgramLocation(..),- simpleConfiguredProgram,+ , ConfiguredProgram (..)+ , programPath+ , suppressOverrideArgs+ , ProgArg+ , ProgramLocation (..)+ , simpleConfiguredProgram ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () import Distribution.PackageDescription-import Distribution.Simple.Program.Find-import Distribution.Version import Distribution.Verbosity+import Distribution.Version import qualified Data.Map as Map @@ -50,88 +46,107 @@ -- -- Note: rather than constructing this directly, start with 'simpleProgram' and -- override any extra fields.----data Program = Program {- -- | The simple name of the program, eg. ghc- programName :: String,-- -- | A function to search for the program if its location was not- -- specified by the user. Usually this will just be a call to- -- 'findProgramOnSearchPath'.- --- -- It is supplied with the prevailing search path which will typically- -- just be used as-is, but can be extended or ignored as needed.- --- -- For the purpose of change monitoring, in addition to the location- -- where the program was found, it returns all the other places that- -- were tried.- --- programFindLocation :: Verbosity -> ProgramSearchPath- -> IO (Maybe (FilePath, [FilePath])),-- -- | Try to find the version of the program. For many programs this is- -- not possible or is not necessary so it's OK to return Nothing.- programFindVersion :: Verbosity -> FilePath -> IO (Maybe Version),+data Program = Program+ { programName :: String+ -- ^ The simple name of the program, eg. ghc+ , programFindLocation+ :: Verbosity+ -> ProgramSearchPath+ -> IO (Maybe (FilePath, [FilePath]))+ -- ^ A function to search for the program if its location was not+ -- specified by the user. Usually this will just be a call to+ -- 'findProgramOnSearchPath'.+ --+ -- It is supplied with the prevailing search path which will typically+ -- just be used as-is, but can be extended or ignored as needed.+ --+ -- For the purpose of change monitoring, in addition to the location+ -- where the program was found, it returns all the other places that+ -- were tried.+ , programFindVersion :: Verbosity -> FilePath -> IO (Maybe Version)+ -- ^ Try to find the version of the program. For many programs this is+ -- not possible or is not necessary so it's OK to return Nothing.+ , programPostConf :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram+ -- ^ A function to do any additional configuration after we have+ -- located the program (and perhaps identified its version). For example+ -- it could add args, or environment vars.+ , programNormaliseArgs :: Maybe Version -> PackageDescription -> [String] -> [String]+ -- ^ A function that filters any arguments that don't impact the output+ -- from a commandline. Used to limit the volatility of dependency hashes+ -- when using new-build.+ } - -- | A function to do any additional configuration after we have- -- located the program (and perhaps identified its version). For example- -- it could add args, or environment vars.- programPostConf :: Verbosity -> ConfiguredProgram -> IO ConfiguredProgram,- -- | A function that filters any arguments that don't impact the output- -- from a commandline. Used to limit the volatility of dependency hashes- -- when using new-build.- programNormaliseArgs :: Maybe Version -> PackageDescription -> [String] -> [String]- } instance Show Program where show (Program name _ _ _ _) = "Program: " ++ name type ProgArg = String --- | Represents a program which has been configured and is thus ready to be run.+-- | A search path to use when locating executables. This is analogous+-- to the unix @$PATH@ or win32 @%PATH%@ but with the ability to use+-- the system default method for finding executables ('findExecutable' which+-- on unix is simply looking on the @$PATH@ but on win32 is a bit more+-- complicated). ----- These are usually made by configuring a 'Program', but if you have to--- construct one directly then start with 'simpleConfiguredProgram' and--- override any extra fields.+-- The default to use is @[ProgSearchPathDefault]@ but you can add extra dirs+-- either before, after or instead of the default, e.g. here we add an extra+-- dir to search after the usual ones. ---data ConfiguredProgram = ConfiguredProgram {- -- | Just the name again- programId :: String,-- -- | The version of this program, if it is known.- programVersion :: Maybe Version,-- -- | Default command-line args for this program.- -- These flags will appear first on the command line, so they can be- -- overridden by subsequent flags.- programDefaultArgs :: [String],-- -- | Override command-line args for this program.- -- These flags will appear last on the command line, so they override- -- all earlier flags.- programOverrideArgs :: [String],-- -- | Override environment variables for this program.- -- These env vars will extend\/override the prevailing environment of- -- the current to form the environment for the new process.- programOverrideEnv :: [(String, Maybe String)],+-- > ['ProgramSearchPathDefault', 'ProgramSearchPathDir' dir]+--+-- We also use this path to set the environment when running child processes.+--+-- The @ProgramDb@ is created with a @ProgramSearchPath@ to which we+-- @prependProgramSearchPath@ to add the ones that come from cli flags and from+-- configurations. Then each of the programs that are configured in the db+-- inherits the same path as part of @configureProgram@.+type ProgramSearchPath = [ProgramSearchPathEntry] - -- | A key-value map listing various properties of the program, useful- -- for feature detection. Populated during the configuration step, key- -- names depend on the specific program.- programProperties :: Map.Map String String,+data ProgramSearchPathEntry+ = -- | A specific dir+ ProgramSearchPathDir FilePath+ | -- | The system default+ ProgramSearchPathDefault+ deriving (Show, Eq, Generic) - -- | Location of the program. eg. @\/usr\/bin\/ghc-6.4@- programLocation :: ProgramLocation,+instance Binary ProgramSearchPathEntry+instance Structured ProgramSearchPathEntry - -- | In addition to the 'programLocation' where the program was found,- -- these are additional locations that were looked at. The combination- -- of ths found location and these not-found locations can be used to- -- monitor to detect when the re-configuring the program might give a- -- different result (e.g. found in a different location).- --- programMonitorFiles :: [FilePath]- }- deriving (Eq, Generic, Read, Show, Typeable)+-- | Represents a program which has been configured and is thus ready to be run.+--+-- These are usually made by configuring a 'Program', but if you have to+-- construct one directly then start with 'simpleConfiguredProgram' and+-- override any extra fields.+data ConfiguredProgram = ConfiguredProgram+ { programId :: String+ -- ^ Just the name again+ , programVersion :: Maybe Version+ -- ^ The version of this program, if it is known.+ , programDefaultArgs :: [String]+ -- ^ Default command-line args for this program.+ -- These flags will appear first on the command line, so they can be+ -- overridden by subsequent flags.+ , programOverrideArgs :: [String]+ -- ^ Override command-line args for this program.+ -- These flags will appear last on the command line, so they override+ -- all earlier flags.+ , programOverrideEnv :: [(String, Maybe String)]+ -- ^ Override environment variables for this program.+ -- These env vars will extend\/override the prevailing environment of+ -- the current to form the environment for the new process.+ , programProperties :: Map.Map String String+ -- ^ A key-value map listing various properties of the program, useful+ -- for feature detection. Populated during the configuration step, key+ -- names depend on the specific program.+ , programLocation :: ProgramLocation+ -- ^ Location of the program. eg. @\/usr\/bin\/ghc-6.4@+ , programMonitorFiles :: [FilePath]+ -- ^ In addition to the 'programLocation' where the program was found,+ -- these are additional locations that were looked at. The combination+ -- of this found location and these not-found locations can be used to+ -- monitor to detect when the re-configuring the program might give a+ -- different result (e.g. found in a different location).+ }+ deriving (Eq, Generic, Read, Show) instance Binary ConfiguredProgram instance Structured ConfiguredProgram@@ -139,12 +154,12 @@ -- | Where a program was found. Also tells us whether it's specified by user or -- not. This includes not just the path, but the program as well. data ProgramLocation- = UserSpecified { locationPath :: FilePath }- -- ^The user gave the path to this program,- -- eg. --ghc-path=\/usr\/bin\/ghc-6.6- | FoundOnSystem { locationPath :: FilePath }- -- ^The program was found automatically.- deriving (Eq, Generic, Read, Show, Typeable)+ = -- | The user gave the path to this program,+ -- eg. --ghc-path=\/usr\/bin\/ghc-6.6+ UserSpecified {locationPath :: FilePath}+ | -- | The program was found automatically.+ FoundOnSystem {locationPath :: FilePath}+ deriving (Eq, Generic, Read, Show) instance Binary ProgramLocation instance Structured ProgramLocation@@ -155,36 +170,20 @@ -- | Suppress any extra arguments added by the user. suppressOverrideArgs :: ConfiguredProgram -> ConfiguredProgram-suppressOverrideArgs prog = prog { programOverrideArgs = [] }---- | Make a simple named program.------ By default we'll just search for it in the path and not try to find the--- version name. You can override these behaviours if necessary, eg:------ > (simpleProgram "foo") { programFindLocation = ... , programFindVersion ... }----simpleProgram :: String -> Program-simpleProgram name = Program {- programName = name,- programFindLocation = \v p -> findProgramOnSearchPath v p name,- programFindVersion = \_ _ -> return Nothing,- programPostConf = \_ p -> return p,- programNormaliseArgs = \_ _ -> id- }+suppressOverrideArgs prog = prog{programOverrideArgs = []} -- | Make a simple 'ConfiguredProgram'. -- -- > simpleConfiguredProgram "foo" (FoundOnSystem path)--- simpleConfiguredProgram :: String -> ProgramLocation -> ConfiguredProgram-simpleConfiguredProgram name loc = ConfiguredProgram {- programId = name,- programVersion = Nothing,- programDefaultArgs = [],- programOverrideArgs = [],- programOverrideEnv = [],- programProperties = Map.empty,- programLocation = loc,- programMonitorFiles = [] -- did not look in any other locations- }+simpleConfiguredProgram name loc =+ ConfiguredProgram+ { programId = name+ , programVersion = Nothing+ , programDefaultArgs = []+ , programOverrideArgs = []+ , programOverrideEnv = []+ , programProperties = Map.empty+ , programLocation = loc+ , programMonitorFiles = [] -- did not look in any other locations+ }
src/Distribution/Simple/Register.hs view
@@ -1,7 +1,9 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Register -- Copyright : Isaac Jones 2003-2004@@ -25,164 +27,198 @@ -- This module does not delegate anything to the per-compiler modules but just -- mixes it all in this module, which is rather unsatisfactory. The script -- generation and the unregister feature are not well used or tested.--module Distribution.Simple.Register (- register,- unregister,-- internalPackageDBPath,-- initPackageDB,- doesPackageDBExist,- createPackageDB,- deletePackageDB,-- abiHash,- invokeHcPkg,- registerPackage,- HcPkg.RegisterOptions(..),- HcPkg.defaultRegisterOptions,- generateRegistrationInfo,- inplaceInstalledPackageInfo,- absoluteInstalledPackageInfo,- generalInstalledPackageInfo,+module Distribution.Simple.Register+ ( register+ , unregister+ , internalPackageDBPath+ , initPackageDB+ , doesPackageDBExist+ , createPackageDB+ , deletePackageDB+ , abiHash+ , invokeHcPkg+ , registerPackage+ , HcPkg.RegisterOptions (..)+ , HcPkg.defaultRegisterOptions+ , generateRegistrationInfo+ , inplaceInstalledPackageInfo+ , absoluteInstalledPackageInfo+ , generalInstalledPackageInfo ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -import Distribution.Types.TargetInfo-import Distribution.Types.LocalBuildInfo import Distribution.Types.ComponentLocalBuildInfo+import Distribution.Types.LocalBuildInfo+import Distribution.Types.TargetInfo -import Distribution.Simple.LocalBuildInfo import Distribution.Simple.BuildPaths import Distribution.Simple.BuildTarget+import Distribution.Simple.LocalBuildInfo -import qualified Distribution.Simple.GHC as GHC+import qualified Distribution.Simple.GHC as GHC import qualified Distribution.Simple.GHCJS as GHCJS-import qualified Distribution.Simple.UHC as UHC-import qualified Distribution.Simple.HaskellSuite as HaskellSuite import qualified Distribution.Simple.PackageIndex as Index+import qualified Distribution.Simple.UHC as UHC import Distribution.Backpack.DescribeUnitId+import Distribution.Compat.Graph (IsNode (nodeKey))+import Distribution.InstalledPackageInfo (InstalledPackageInfo)+import qualified Distribution.InstalledPackageInfo as IPI+import Distribution.License (licenseFromSPDX, licenseToSPDX)+import Distribution.Package+import Distribution.PackageDescription+import Distribution.Pretty import Distribution.Simple.Compiler+import Distribution.Simple.Errors+import Distribution.Simple.Flag import Distribution.Simple.Program-import Distribution.Simple.Program.Script import qualified Distribution.Simple.Program.HcPkg as HcPkg-import Distribution.Simple.Setup-import Distribution.PackageDescription-import Distribution.Package-import Distribution.License (licenseToSPDX, licenseFromSPDX)-import qualified Distribution.InstalledPackageInfo as IPI-import Distribution.InstalledPackageInfo (InstalledPackageInfo)+import Distribution.Simple.Program.Script+import Distribution.Simple.Setup.Common+import Distribution.Simple.Setup.Register import Distribution.Simple.Utils-import Distribution.Utils.MapAccum import Distribution.System-import Distribution.Pretty+import Distribution.Utils.MapAccum+import Distribution.Utils.Path import Distribution.Verbosity as Verbosity import Distribution.Version-import Distribution.Compat.Graph (IsNode(nodeKey))--import System.FilePath ((</>), (<.>), isAbsolute) import System.Directory+import System.FilePath (isAbsolute) import qualified Data.ByteString.Lazy.Char8 as BS.Char8 -- ----------------------------------------------------------------------------- -- Registration -register :: PackageDescription -> LocalBuildInfo- -> RegisterFlags -- ^Install in the user's database?; verbose- -> IO ()-register pkg_descr lbi0 flags =- -- Duncan originally asked for us to not register/install files- -- when there was no public library. But with per-component- -- configure, we legitimately need to install internal libraries- -- so that we can get them. So just unconditionally install.- doRegister- where- doRegister = do- targets <- readTargetInfos verbosity pkg_descr lbi0 (regArgs flags)+register+ :: PackageDescription+ -> LocalBuildInfo+ -> RegisterFlags+ -- ^ Install in the user's database?; verbose+ -> IO ()+register pkg_descr lbi0 flags = do+ -- Duncan originally asked for us to not register/install files+ -- when there was no public library. But with per-component+ -- configure, we legitimately need to install internal libraries+ -- so that we can get them. So just unconditionally install.+ let verbosity = fromFlag $ registerVerbosity flags+ targets <- readTargetInfos verbosity pkg_descr lbi0 $ registerTargets flags - -- It's important to register in build order, because ghc-pkg- -- will complain if a dependency is not registered.- let componentsToRegister- = neededTargetsInBuildOrder' pkg_descr lbi0 (map nodeKey targets)+ -- It's important to register in build order, because ghc-pkg+ -- will complain if a dependency is not registered.+ let componentsToRegister =+ neededTargetsInBuildOrder' pkg_descr lbi0 (map nodeKey targets) - (_, ipi_mbs) <-- mapAccumM `flip` installedPkgs lbi0 `flip` componentsToRegister $ \index tgt ->- case targetComponent tgt of- CLib lib -> do- let clbi = targetCLBI tgt- lbi = lbi0 { installedPkgs = index }- ipi <- generateOne pkg_descr lib lbi clbi flags- return (Index.insert ipi index, Just ipi)- _ -> return (index, Nothing)+ (_, ipi_mbs) <-+ mapAccumM `flip` installedPkgs lbi0 `flip` componentsToRegister $ \index tgt ->+ case targetComponent tgt of+ CLib lib -> do+ let clbi = targetCLBI tgt+ lbi = lbi0{installedPkgs = index}+ ipi <- generateOne pkg_descr lib lbi clbi flags+ return (Index.insert ipi index, Just ipi)+ _ -> return (index, Nothing) - registerAll pkg_descr lbi0 flags (catMaybes ipi_mbs)- where- verbosity = fromFlag (regVerbosity flags)+ registerAll pkg_descr lbi0 flags (catMaybes ipi_mbs) -generateOne :: PackageDescription -> Library -> LocalBuildInfo -> ComponentLocalBuildInfo- -> RegisterFlags- -> IO InstalledPackageInfo-generateOne pkg lib lbi clbi regFlags- = do- absPackageDBs <- absolutePackageDBPaths packageDbs- installedPkgInfo <- generateRegistrationInfo- verbosity pkg lib lbi clbi inplace reloc distPref- (registrationPackageDB absPackageDBs)+generateOne+ :: PackageDescription+ -> Library+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> RegisterFlags+ -> IO InstalledPackageInfo+generateOne pkg lib lbi clbi regFlags =+ do+ absPackageDBs <- absolutePackageDBPaths mbWorkDir packageDbs+ installedPkgInfo <-+ generateRegistrationInfo+ verbosity+ pkg+ lib+ lbi+ clbi+ inplace+ reloc+ distPref+ (registrationPackageDB absPackageDBs) info verbosity (IPI.showInstalledPackageInfo installedPkgInfo) return installedPkgInfo where- inplace = fromFlag (regInPlace regFlags)- reloc = relocatable lbi+ common = registerCommonFlags regFlags+ inplace = fromFlag (regInPlace regFlags)+ reloc = relocatable lbi -- FIXME: there's really no guarantee this will work. -- registering into a totally different db stack can -- fail if dependencies cannot be satisfied.- packageDbs = nub $ withPackageDB lbi- ++ maybeToList (flagToMaybe (regPackageDB regFlags))- distPref = fromFlag (regDistPref regFlags)- verbosity = fromFlag (regVerbosity regFlags)+ packageDbs =+ nub $+ withPackageDB lbi+ ++ maybeToList (flagToMaybe (regPackageDB regFlags))+ distPref = fromFlag $ setupDistPref common+ verbosity = fromFlag $ setupVerbosity common+ mbWorkDir = flagToMaybe $ setupWorkingDir common -registerAll :: PackageDescription -> LocalBuildInfo -> RegisterFlags- -> [InstalledPackageInfo]- -> IO ()-registerAll pkg lbi regFlags ipis- = do+registerAll+ :: PackageDescription+ -> LocalBuildInfo+ -> RegisterFlags+ -> [InstalledPackageInfo]+ -> IO ()+registerAll pkg lbi regFlags ipis =+ do when (fromFlag (regPrintId regFlags)) $ do for_ ipis $ \installedPkgInfo -> -- Only print the public library's IPI- when (packageId installedPkgInfo == packageId pkg- && IPI.sourceLibName installedPkgInfo == LMainLibName) $- putStrLn (prettyShow (IPI.installedUnitId installedPkgInfo))+ when+ ( packageId installedPkgInfo == packageId pkg+ && IPI.sourceLibName installedPkgInfo == LMainLibName+ )+ $ putStrLn (prettyShow (IPI.installedUnitId installedPkgInfo)) - -- Three different modes:+ -- Three different modes: case () of- _ | modeGenerateRegFile -> writeRegistrationFileOrDirectory- | modeGenerateRegScript -> writeRegisterScript- | otherwise -> do- for_ ipis $ \ipi -> do- setupMessage' verbosity "Registering" (packageId pkg)- (CLibName (IPI.sourceLibName ipi))- (Just (IPI.instantiatedWith ipi))- registerPackage verbosity (compiler lbi) (withPrograms lbi)- packageDbs ipi HcPkg.defaultRegisterOptions-+ _+ | modeGenerateRegFile -> writeRegistrationFileOrDirectory+ | modeGenerateRegScript -> writeRegisterScript+ | otherwise -> do+ for_ ipis $ \ipi -> do+ setupMessage'+ verbosity+ "Registering"+ (packageId pkg)+ (CLibName (IPI.sourceLibName ipi))+ (Just (IPI.instantiatedWith ipi))+ registerPackage+ verbosity+ (compiler lbi)+ (withPrograms lbi)+ (mbWorkDirLBI lbi)+ packageDbs+ ipi+ HcPkg.defaultRegisterOptions where modeGenerateRegFile = isJust (flagToMaybe (regGenPkgConf regFlags))- regFile = fromMaybe (prettyShow (packageId pkg) <.> "conf")- (fromFlag (regGenPkgConf regFlags))+ regFile =+ interpretSymbolicPathLBI lbi $+ fromMaybe+ (makeSymbolicPath (prettyShow (packageId pkg) <.> "conf"))+ (fromFlag (regGenPkgConf regFlags)) modeGenerateRegScript = fromFlag (regGenScript regFlags) -- FIXME: there's really no guarantee this will work. -- registering into a totally different db stack can -- fail if dependencies cannot be satisfied.- packageDbs = nub $ withPackageDB lbi- ++ maybeToList (flagToMaybe (regPackageDB regFlags))- verbosity = fromFlag (regVerbosity regFlags)+ packageDbs =+ nub $+ withPackageDB lbi+ ++ maybeToList (flagToMaybe (regPackageDB regFlags))+ common = registerCommonFlags regFlags+ verbosity = fromFlag (setupVerbosity common)+ mbWorkDir = mbWorkDirLBI lbi writeRegistrationFileOrDirectory = do -- Handles overwriting both directory and file@@ -196,195 +232,255 @@ createDirectory regFile let num_ipis = length ipis lpad m xs = replicate (m - length ys) '0' ++ ys- where ys = take m xs+ where+ ys = take m xs number i = lpad (length (show num_ipis)) (show i)- for_ (zip ([1..] :: [Int]) ipis) $ \(i, installedPkgInfo) ->- writeUTF8File (regFile </> (number i ++ "-" ++ prettyShow (IPI.installedUnitId installedPkgInfo)))- (IPI.showInstalledPackageInfo installedPkgInfo)+ for_ (zip ([1 ..] :: [Int]) ipis) $ \(i, installedPkgInfo) ->+ writeUTF8File+ (regFile </> (number i ++ "-" ++ prettyShow (IPI.installedUnitId installedPkgInfo)))+ (IPI.showInstalledPackageInfo installedPkgInfo) writeRegisterScript = case compilerFlavor (compiler lbi) of UHC -> notice verbosity "Registration scripts not needed for uhc"- _ -> withHcPkg verbosity- "Registration scripts are not implemented for this compiler"- (compiler lbi) (withPrograms lbi)- (writeHcPkgRegisterScript verbosity ipis packageDbs)-+ _ ->+ withHcPkg+ verbosity+ "Registration scripts are not implemented for this compiler"+ (compiler lbi)+ (withPrograms lbi)+ (writeHcPkgRegisterScript verbosity mbWorkDir ipis packageDbs) -generateRegistrationInfo :: Verbosity- -> PackageDescription- -> Library- -> LocalBuildInfo- -> ComponentLocalBuildInfo- -> Bool- -> Bool- -> FilePath- -> PackageDB- -> IO InstalledPackageInfo+generateRegistrationInfo+ :: Verbosity+ -> PackageDescription+ -> Library+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> Bool+ -> Bool+ -> SymbolicPath Pkg (Dir Dist)+ -> PackageDB+ -> IO InstalledPackageInfo generateRegistrationInfo verbosity pkg lib lbi clbi inplace reloc distPref packageDb = do- --TODO: eliminate pwd!- pwd <- getCurrentDirectory-+ inplaceDir <- absoluteWorkingDirLBI lbi installedPkgInfo <- if inplace- -- NB: With an inplace installation, the user may run './Setup+ then -- NB: With an inplace installation, the user may run './Setup -- build' to update the library files, without reregistering. -- In this case, it is critical that the ABI hash not flip.- then return (inplaceInstalledPackageInfo pwd distPref- pkg (mkAbiHash "inplace") lib lbi clbi)- else do++ return+ ( inplaceInstalledPackageInfo+ inplaceDir+ distPref+ pkg+ (mkAbiHash "inplace")+ lib+ lbi+ clbi+ )+ else do abi_hash <- abiHash verbosity pkg distPref lbi lib clbi if reloc- then relocRegistrationInfo verbosity- pkg lib lbi clbi abi_hash packageDb- else return (absoluteInstalledPackageInfo- pkg abi_hash lib lbi clbi)-+ then+ relocRegistrationInfo+ verbosity+ pkg+ lib+ lbi+ clbi+ abi_hash+ packageDb+ else+ return+ ( absoluteInstalledPackageInfo+ pkg+ abi_hash+ lib+ lbi+ clbi+ ) return installedPkgInfo -- | Compute the 'AbiHash' of a library that we built inplace.-abiHash :: Verbosity- -> PackageDescription- -> FilePath- -> LocalBuildInfo- -> Library- -> ComponentLocalBuildInfo- -> IO AbiHash+abiHash+ :: Verbosity+ -> PackageDescription+ -> SymbolicPath Pkg (Dir Dist)+ -> LocalBuildInfo+ -> Library+ -> ComponentLocalBuildInfo+ -> IO AbiHash abiHash verbosity pkg distPref lbi lib clbi =- case compilerFlavor comp of- GHC -> do- fmap mkAbiHash $ GHC.libAbiHash verbosity pkg lbi' lib clbi- GHCJS -> do- fmap mkAbiHash $ GHCJS.libAbiHash verbosity pkg lbi' lib clbi- _ -> return (mkAbiHash "")+ case compilerFlavor comp of+ GHC -> do+ fmap mkAbiHash $ GHC.libAbiHash verbosity pkg lbi' lib clbi+ GHCJS -> do+ fmap mkAbiHash $ GHCJS.libAbiHash verbosity pkg lbi' lib clbi+ _ -> return (mkAbiHash "") where comp = compiler lbi- lbi' = lbi {- withPackageDB = withPackageDB lbi- ++ [SpecificPackageDB (internalPackageDBPath lbi distPref)]- }+ lbi' =+ lbi+ { withPackageDB =+ withPackageDB lbi+ ++ [SpecificPackageDB (internalPackageDBPath lbi distPref)]+ } -relocRegistrationInfo :: Verbosity- -> PackageDescription- -> Library- -> LocalBuildInfo- -> ComponentLocalBuildInfo- -> AbiHash- -> PackageDB- -> IO InstalledPackageInfo+relocRegistrationInfo+ :: Verbosity+ -> PackageDescription+ -> Library+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> AbiHash+ -> PackageDB+ -> IO InstalledPackageInfo relocRegistrationInfo verbosity pkg lib lbi clbi abi_hash packageDb = case (compilerFlavor (compiler lbi)) of- GHC -> do fs <- GHC.pkgRoot verbosity lbi packageDb- return (relocatableInstalledPackageInfo- pkg abi_hash lib lbi clbi fs)- _ -> die' verbosity- "Distribution.Simple.Register.relocRegistrationInfo: \- \not implemented for this compiler"+ GHC -> do+ fs <- GHC.pkgRoot verbosity lbi packageDb+ return+ ( relocatableInstalledPackageInfo+ pkg+ abi_hash+ lib+ lbi+ clbi+ fs+ )+ _ -> dieWithException verbosity RelocRegistrationInfo initPackageDB :: Verbosity -> Compiler -> ProgramDb -> FilePath -> IO () initPackageDB verbosity comp progdb dbPath =- createPackageDB verbosity comp progdb False dbPath+ createPackageDB verbosity comp progdb False dbPath -- | Create an empty package DB at the specified location.-createPackageDB :: Verbosity -> Compiler -> ProgramDb -> Bool- -> FilePath -> IO ()+createPackageDB+ :: Verbosity+ -> Compiler+ -> ProgramDb+ -> Bool+ -> FilePath+ -> IO () createPackageDB verbosity comp progdb preferCompat dbPath =- case compilerFlavor comp of- GHC -> HcPkg.init (GHC.hcPkgInfo progdb) verbosity preferCompat dbPath- GHCJS -> HcPkg.init (GHCJS.hcPkgInfo progdb) verbosity False dbPath- UHC -> return ()- HaskellSuite _ -> HaskellSuite.initPackageDB verbosity progdb dbPath- _ -> die' verbosity $- "Distribution.Simple.Register.createPackageDB: "- ++ "not implemented for this compiler"+ case compilerFlavor comp of+ GHC -> HcPkg.init (GHC.hcPkgInfo progdb) verbosity preferCompat dbPath+ GHCJS -> HcPkg.init (GHCJS.hcPkgInfo progdb) verbosity False dbPath+ UHC -> return ()+ _ -> dieWithException verbosity CreatePackageDB doesPackageDBExist :: FilePath -> IO Bool doesPackageDBExist dbPath = do- -- currently one impl for all compiler flavours, but could change if needed- dir_exists <- doesDirectoryExist dbPath- if dir_exists- then return True- else doesFileExist dbPath+ -- currently one impl for all compiler flavours, but could change if needed+ dir_exists <- doesDirectoryExist dbPath+ if dir_exists+ then return True+ else doesFileExist dbPath deletePackageDB :: FilePath -> IO () deletePackageDB dbPath = do- -- currently one impl for all compiler flavours, but could change if needed- dir_exists <- doesDirectoryExist dbPath- if dir_exists- then removeDirectoryRecursive dbPath- else do file_exists <- doesFileExist dbPath- when file_exists $ removeFile dbPath+ -- currently one impl for all compiler flavours, but could change if needed+ dir_exists <- doesDirectoryExist dbPath+ if dir_exists+ then removeDirectoryRecursive dbPath+ else do+ file_exists <- doesFileExist dbPath+ when file_exists $ removeFile dbPath -- | Run @hc-pkg@ using a given package DB stack, directly forwarding the -- provided command-line arguments to it.-invokeHcPkg :: Verbosity -> Compiler -> ProgramDb -> PackageDBStack- -> [String] -> IO ()-invokeHcPkg verbosity comp progdb dbStack extraArgs =- withHcPkg verbosity "invokeHcPkg" comp progdb- (\hpi -> HcPkg.invoke hpi verbosity dbStack extraArgs)+invokeHcPkg+ :: Verbosity+ -> Compiler+ -> ProgramDb+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> PackageDBStack+ -> [String]+ -> IO ()+invokeHcPkg verbosity comp progdb mbWorkDir dbStack extraArgs =+ withHcPkg+ verbosity+ "invokeHcPkg"+ comp+ progdb+ (\hpi -> HcPkg.invoke hpi verbosity mbWorkDir dbStack extraArgs) -withHcPkg :: Verbosity -> String -> Compiler -> ProgramDb- -> (HcPkg.HcPkgInfo -> IO a) -> IO a+withHcPkg+ :: Verbosity+ -> String+ -> Compiler+ -> ProgramDb+ -> (HcPkg.HcPkgInfo -> IO a)+ -> IO a withHcPkg verbosity name comp progdb f = case compilerFlavor comp of- GHC -> f (GHC.hcPkgInfo progdb)+ GHC -> f (GHC.hcPkgInfo progdb) GHCJS -> f (GHCJS.hcPkgInfo progdb)- _ -> die' verbosity ("Distribution.Simple.Register." ++ name ++ ":\- \not implemented for this compiler")+ _ -> dieWithException verbosity $ WithHcPkg name -registerPackage :: Verbosity- -> Compiler- -> ProgramDb- -> PackageDBStack- -> InstalledPackageInfo- -> HcPkg.RegisterOptions- -> IO ()-registerPackage verbosity comp progdb packageDbs installedPkgInfo registerOptions =+registerPackage+ :: Verbosity+ -> Compiler+ -> ProgramDb+ -> Maybe (SymbolicPath CWD (Dir from))+ -> PackageDBStackS from+ -> InstalledPackageInfo+ -> HcPkg.RegisterOptions+ -> IO ()+registerPackage verbosity comp progdb mbWorkDir packageDbs installedPkgInfo registerOptions = case compilerFlavor comp of- GHC -> GHC.registerPackage verbosity progdb packageDbs installedPkgInfo registerOptions- GHCJS -> GHCJS.registerPackage verbosity progdb packageDbs installedPkgInfo registerOptions- HaskellSuite {} ->- HaskellSuite.registerPackage verbosity progdb packageDbs installedPkgInfo- _ | HcPkg.registerMultiInstance registerOptions- -> die' verbosity "Registering multiple package instances is not yet supported for this compiler"- UHC -> UHC.registerPackage verbosity comp progdb packageDbs installedPkgInfo- _ -> die' verbosity "Registering is not implemented for this compiler"+ GHC -> GHC.registerPackage verbosity progdb mbWorkDir packageDbs installedPkgInfo registerOptions+ GHCJS -> GHCJS.registerPackage verbosity progdb mbWorkDir packageDbs installedPkgInfo registerOptions+ _+ | HcPkg.registerMultiInstance registerOptions ->+ dieWithException verbosity RegisMultiplePkgNotSupported+ UHC -> UHC.registerPackage verbosity mbWorkDir comp progdb packageDbs installedPkgInfo+ _ -> dieWithException verbosity RegisteringNotImplemented -writeHcPkgRegisterScript :: Verbosity- -> [InstalledPackageInfo]- -> PackageDBStack- -> HcPkg.HcPkgInfo- -> IO ()-writeHcPkgRegisterScript verbosity ipis packageDbs hpi = do+writeHcPkgRegisterScript+ :: Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> [InstalledPackageInfo]+ -> PackageDBStack+ -> HcPkg.HcPkgInfo+ -> IO ()+writeHcPkgRegisterScript verbosity mbWorkDir ipis packageDbs hpi = do let genScript installedPkgInfo =- let invocation = HcPkg.registerInvocation hpi Verbosity.normal- packageDbs installedPkgInfo- HcPkg.defaultRegisterOptions- in invocationAsSystemScript buildOS invocation+ let invocation =+ HcPkg.registerInvocation+ hpi+ Verbosity.normal+ mbWorkDir+ packageDbs+ installedPkgInfo+ HcPkg.defaultRegisterOptions+ in invocationAsSystemScript buildOS invocation scripts = map genScript ipis -- TODO: Do something more robust here regScript = unlines scripts - info verbosity ("Creating package registration script: " ++ regScriptFileName)- writeUTF8File regScriptFileName regScript- setFileExecutable regScriptFileName+ let out_file = interpretSymbolicPath mbWorkDir regScriptFileName+ info verbosity ("Creating package registration script: " ++ out_file)+ writeUTF8File out_file regScript+ setFileExecutable out_file -regScriptFileName :: FilePath+regScriptFileName :: SymbolicPath Pkg File regScriptFileName = case buildOS of- Windows -> "register.bat"- _ -> "register.sh"-+ Windows -> makeSymbolicPath "register.bat"+ _ -> makeSymbolicPath "register.sh" -- ----------------------------------------------------------------------------- -- Making the InstalledPackageInfo -- | Construct 'InstalledPackageInfo' for a library in a package, given a set -- of installation directories.--- generalInstalledPackageInfo- :: ([FilePath] -> [FilePath]) -- ^ Translate relative include dir paths to- -- absolute paths.+ :: ([FilePath] -> [FilePath])+ -- ^ Translate relative include dir paths to+ -- absolute paths. -> PackageDescription -> AbiHash -> Library@@ -393,203 +489,267 @@ -> InstallDirs FilePath -> InstalledPackageInfo generalInstalledPackageInfo adjustRelIncDirs pkg abi_hash lib lbi clbi installDirs =- IPI.InstalledPackageInfo {- IPI.sourcePackageId = packageId pkg,- IPI.installedUnitId = componentUnitId clbi,- IPI.installedComponentId_ = componentComponentId clbi,- IPI.instantiatedWith = componentInstantiatedWith clbi,- IPI.sourceLibName = libName lib,- IPI.compatPackageKey = componentCompatPackageKey clbi,- -- If GHC >= 8.4 we register with SDPX, otherwise with legacy license- IPI.license =+ IPI.InstalledPackageInfo+ { IPI.sourcePackageId = packageId pkg+ , IPI.installedUnitId = componentUnitId clbi+ , IPI.installedComponentId_ = componentComponentId clbi+ , IPI.instantiatedWith = expectLibraryComponent (maybeComponentInstantiatedWith clbi)+ , IPI.sourceLibName = libName lib+ , IPI.compatPackageKey = expectLibraryComponent (maybeComponentCompatPackageKey clbi)+ , -- If GHC >= 8.4 we register with SDPX, otherwise with legacy license+ IPI.license = if ghc84- then Left $ either id licenseToSPDX $ licenseRaw pkg- else Right $ either licenseFromSPDX id $ licenseRaw pkg,- IPI.copyright = copyright pkg,- IPI.maintainer = maintainer pkg,- IPI.author = author pkg,- IPI.stability = stability pkg,- IPI.homepage = homepage pkg,- IPI.pkgUrl = pkgUrl pkg,- IPI.synopsis = synopsis pkg,- IPI.description = description pkg,- IPI.category = category pkg,- IPI.abiHash = abi_hash,- IPI.indefinite = componentIsIndefinite clbi,- IPI.exposed = libExposed lib,- IPI.exposedModules = componentExposedModules clbi- -- add virtual modules into the list of exposed modules for the- -- package database as well.- ++ map (\name -> IPI.ExposedModule name Nothing) (virtualModules bi),- IPI.hiddenModules = otherModules bi,- IPI.trusted = IPI.trusted IPI.emptyInstalledPackageInfo,- IPI.importDirs = [ libdir installDirs | hasModules ],- IPI.libraryDirs = libdirs,- IPI.libraryDynDirs = dynlibdirs,- IPI.dataDir = datadir installDirs,- IPI.hsLibraries = (if hasLibrary- then [getHSLibraryName (componentUnitId clbi)]- else []) ++ extraBundledLibs bi,- IPI.extraLibraries = extraLibs bi,- IPI.extraGHCiLibraries = extraGHCiLibs bi,- IPI.includeDirs = absinc ++ adjustRelIncDirs relinc,- IPI.includes = includes bi,- IPI.depends = depends,- IPI.abiDepends = [], -- due to #5465- IPI.ccOptions = [], -- Note. NOT ccOptions bi!- -- We don't want cc-options to be propagated- -- to C compilations in other packages.- IPI.cxxOptions = [], -- Also. NOT cxxOptions bi!- IPI.ldOptions = ldOptions bi,- IPI.frameworks = frameworks bi,- IPI.frameworkDirs = extraFrameworkDirs bi,- IPI.haddockInterfaces = [haddockdir installDirs </> haddockName pkg],- IPI.haddockHTMLs = [htmldir installDirs],- IPI.pkgRoot = Nothing,- IPI.libVisibility = libVisibility lib- }+ then Left $ either id licenseToSPDX $ licenseRaw pkg+ else Right $ either licenseFromSPDX id $ licenseRaw pkg+ , IPI.copyright = copyright pkg+ , IPI.maintainer = maintainer pkg+ , IPI.author = author pkg+ , IPI.stability = stability pkg+ , IPI.homepage = homepage pkg+ , IPI.pkgUrl = pkgUrl pkg+ , IPI.synopsis = synopsis pkg+ , IPI.description = description pkg+ , IPI.category = category pkg+ , IPI.abiHash = abi_hash+ , IPI.indefinite = componentIsIndefinite clbi+ , IPI.exposed = libExposed lib+ , IPI.exposedModules =+ expectLibraryComponent (maybeComponentExposedModules clbi)+ -- add virtual modules into the list of exposed modules for the+ -- package database as well.+ ++ map (\name -> IPI.ExposedModule name Nothing) (virtualModules bi)+ , IPI.hiddenModules = otherModules bi+ , IPI.trusted = IPI.trusted IPI.emptyInstalledPackageInfo+ , IPI.importDirs = [libdir installDirs | hasModules]+ , IPI.libraryDirs = libdirs+ , IPI.libraryDirsStatic = libdirsStatic+ , IPI.libraryDynDirs = dynlibdirs+ , IPI.dataDir = datadir installDirs+ , IPI.hsLibraries =+ ( if hasLibrary+ then [getHSLibraryName (componentUnitId clbi)]+ else []+ )+ ++ extraBundledLibs bi+ , IPI.extraLibraries = extraLibs bi+ , IPI.extraLibrariesStatic = extraLibsStatic bi+ , IPI.extraGHCiLibraries = extraGHCiLibs bi+ , IPI.includeDirs = absinc ++ adjustRelIncDirs relinc+ , IPI.includes = map getSymbolicPath $ includes bi+ , IPI.depends = depends+ , IPI.abiDepends = [] -- due to #5465+ , IPI.ccOptions = [] -- Note. NOT ccOptions bi!+ -- We don't want cc-options to be propagated+ -- to C compilations in other packages.+ , IPI.cxxOptions = [] -- Also. NOT cxxOptions bi!+ , IPI.ldOptions = ldOptions bi+ , IPI.frameworks = map getSymbolicPath $ frameworks bi+ , IPI.frameworkDirs = map getSymbolicPath $ extraFrameworkDirs bi+ , IPI.haddockInterfaces = [haddockdir installDirs </> haddockLibraryPath pkg lib | hasModules]+ , IPI.haddockHTMLs = [htmldir installDirs | hasModules]+ , IPI.pkgRoot = Nothing+ , IPI.libVisibility = libVisibility lib+ } where ghc84 = case compilerId $ compiler lbi of- CompilerId GHC v -> v >= mkVersion [8, 4]- _ -> False+ CompilerId GHC v -> v >= mkVersion [8, 4]+ _ -> False bi = libBuildInfo lib- --TODO: unclear what the root cause of the+ -- TODO: unclear what the root cause of the -- duplication is, but we nub it here for now: depends = ordNub $ map fst (componentPackageDeps clbi)- (absinc, relinc) = partition isAbsolute (includeDirs bi)+ (absinc, relinc) = partition isAbsolute (map getSymbolicPath $ includeDirs bi) hasModules = not $ null (allLibModules lib clbi) comp = compiler lbi- hasLibrary = (hasModules || not (null (cSources bi))- || not (null (asmSources bi))- || not (null (cmmSources bi))- || not (null (cxxSources bi))- || (not (null (jsSources bi)) &&- compilerFlavor comp == GHCJS))- && not (componentIsIndefinite clbi)+ hasLibrary =+ ( hasModules+ || not (null (cSources bi))+ || not (null (asmSources bi))+ || not (null (cmmSources bi))+ || not (null (cxxSources bi))+ || (not (null (jsSources bi)) && hasJsSupport)+ )+ && not (componentIsIndefinite clbi)+ hasJsSupport = case hostPlatform lbi of+ Platform JavaScript _ -> True+ _ -> False+ extraLibDirs' = map getSymbolicPath $ extraLibDirs bi+ libdirsStatic+ | hasLibrary = libdir installDirs : extraLibDirsStaticOrFallback+ | otherwise = extraLibDirsStaticOrFallback+ where+ -- If no static library dirs were given, the package likely makes no+ -- distinction between fully static linking and otherwise.+ -- Fall back to the normal library dirs in that case.+ extraLibDirsStaticOrFallback = case extraLibDirsStatic bi of+ [] -> extraLibDirs'+ dirs -> map getSymbolicPath dirs (libdirs, dynlibdirs)- | not hasLibrary- = (extraLibDirs bi, [])+ | not hasLibrary =+ (extraLibDirs', []) -- the dynamic-library-dirs defaults to the library-dirs if not specified, -- so this works whether the dynamic-library-dirs field is supported or not - | libraryDynDirSupported comp- = (libdir installDirs : extraLibDirs bi,- dynlibdir installDirs : extraLibDirs bi)+ | libraryDynDirSupported comp =+ ( libdir installDirs : extraLibDirs'+ , dynlibdir installDirs : extraLibDirs'+ )+ | otherwise =+ (libdir installDirs : dynlibdir installDirs : extraLibDirs', [])+ expectLibraryComponent (Just attribute) = attribute+ expectLibraryComponent Nothing = (error "generalInstalledPackageInfo: Expected a library component, got something else.") - | otherwise- = (libdir installDirs : dynlibdir installDirs : extraLibDirs bi, [])- -- the compiler doesn't understand the dynamic-library-dirs field so we- -- add the dyn directory to the "normal" list in the library-dirs field+-- the compiler doesn't understand the dynamic-library-dirs field so we+-- add the dyn directory to the "normal" list in the library-dirs field -- | Construct 'InstalledPackageInfo' for a library that is in place in the -- build tree. -- -- This function knows about the layout of in place packages.----inplaceInstalledPackageInfo :: FilePath -- ^ top of the build tree- -> FilePath -- ^ location of the dist tree- -> PackageDescription- -> AbiHash- -> Library- -> LocalBuildInfo- -> ComponentLocalBuildInfo- -> InstalledPackageInfo+inplaceInstalledPackageInfo+ :: AbsolutePath (Dir Pkg)+ -> SymbolicPath Pkg (Dir Dist)+ -- ^ location of the dist tree+ -> PackageDescription+ -> AbiHash+ -> Library+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> InstalledPackageInfo inplaceInstalledPackageInfo inplaceDir distPref pkg abi_hash lib lbi clbi =- generalInstalledPackageInfo adjustRelativeIncludeDirs- pkg abi_hash lib lbi clbi installDirs+ generalInstalledPackageInfo+ adjustRelativeIncludeDirs+ pkg+ abi_hash+ lib+ lbi+ clbi+ installDirs where+ i = interpretSymbolicPathAbsolute inplaceDir -- See Note [Symbolic paths] in Distribution.Utils.Path adjustRelativeIncludeDirs = concatMap $ \d ->- [ inplaceDir </> d -- local include-dir- , inplaceDir </> libTargetDir </> d -- autogen include-dir+ [ i $ makeRelativePathEx d -- local include-dir+ , i $ libTargetDir </> makeRelativePathEx d -- autogen include-dir ] libTargetDir = componentBuildDir lbi clbi installDirs =- (absoluteComponentInstallDirs pkg lbi (componentUnitId clbi) NoCopyDest) {- libdir = inplaceDir </> libTargetDir,- dynlibdir = inplaceDir </> libTargetDir,- datadir = inplaceDir </> dataDir pkg,- docdir = inplaceDocdir,- htmldir = inplaceHtmldir,- haddockdir = inplaceHtmldir- }- inplaceDocdir = inplaceDir </> distPref </> "doc"- inplaceHtmldir = inplaceDocdir </> "html" </> prettyShow (packageName pkg)-+ (absoluteComponentInstallDirs pkg lbi (componentUnitId clbi) NoCopyDest)+ { libdir = i libTargetDir+ , dynlibdir = i libTargetDir+ , datadir =+ let rawDataDir = dataDir pkg+ in if null $ getSymbolicPath rawDataDir+ then i sameDirectory+ else i rawDataDir+ , docdir = i inplaceDocdir+ , htmldir = inplaceHtmldir+ , haddockdir = inplaceHtmldir+ }+ inplaceDocdir = distPref </> makeRelativePathEx "doc"+ inplaceHtmldir = i $ inplaceDocdir </> makeRelativePathEx ("html" </> prettyShow (packageName pkg)) -- | Construct 'InstalledPackageInfo' for the final install location of a -- library package. -- -- This function knows about the layout of installed packages.----absoluteInstalledPackageInfo :: PackageDescription- -> AbiHash- -> Library- -> LocalBuildInfo- -> ComponentLocalBuildInfo- -> InstalledPackageInfo+absoluteInstalledPackageInfo+ :: PackageDescription+ -> AbiHash+ -> Library+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> InstalledPackageInfo absoluteInstalledPackageInfo pkg abi_hash lib lbi clbi =- generalInstalledPackageInfo adjustReativeIncludeDirs- pkg abi_hash lib lbi clbi installDirs+ generalInstalledPackageInfo+ adjustReativeIncludeDirs+ pkg+ abi_hash+ lib+ lbi+ clbi+ installDirs where -- For installed packages we install all include files into one dir, -- whereas in the build tree they may live in multiple local dirs. adjustReativeIncludeDirs _ | null (installIncludes bi) = []- | otherwise = [includedir installDirs]+ | otherwise = [includedir installDirs] bi = libBuildInfo lib installDirs = absoluteComponentInstallDirs pkg lbi (componentUnitId clbi) NoCopyDest --relocatableInstalledPackageInfo :: PackageDescription- -> AbiHash- -> Library- -> LocalBuildInfo- -> ComponentLocalBuildInfo- -> FilePath- -> InstalledPackageInfo+relocatableInstalledPackageInfo+ :: PackageDescription+ -> AbiHash+ -> Library+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> SymbolicPath CWD ('Dir Pkg)+ -> InstalledPackageInfo relocatableInstalledPackageInfo pkg abi_hash lib lbi clbi pkgroot =- generalInstalledPackageInfo adjustReativeIncludeDirs- pkg abi_hash lib lbi clbi installDirs+ generalInstalledPackageInfo+ adjustReativeIncludeDirs+ pkg+ abi_hash+ lib+ lbi+ clbi+ installDirs where -- For installed packages we install all include files into one dir, -- whereas in the build tree they may live in multiple local dirs. adjustReativeIncludeDirs _ | null (installIncludes bi) = []- | otherwise = [includedir installDirs]+ | otherwise = [includedir installDirs] bi = libBuildInfo lib - installDirs = fmap (("${pkgroot}" </>) . shortRelativePath pkgroot)- $ absoluteComponentInstallDirs pkg lbi (componentUnitId clbi) NoCopyDest+ installDirs =+ fmap (("${pkgroot}" </>) . shortRelativePath (getSymbolicPath pkgroot)) $+ absoluteComponentInstallDirs pkg lbi (componentUnitId clbi) NoCopyDest -- ----------------------------------------------------------------------------- -- Unregistration unregister :: PackageDescription -> LocalBuildInfo -> RegisterFlags -> IO () unregister pkg lbi regFlags = do- let pkgid = packageId pkg+ let pkgid = packageId pkg+ common = registerCommonFlags regFlags genScript = fromFlag (regGenScript regFlags)- verbosity = fromFlag (regVerbosity regFlags)- packageDb = fromFlagOrDefault (registrationPackageDB (withPackageDB lbi))- (regPackageDB regFlags)+ verbosity = fromFlag (setupVerbosity common)+ packageDb =+ fromFlagOrDefault+ (registrationPackageDB (withPackageDB lbi))+ (regPackageDB regFlags)+ mbWorkDir = mbWorkDirLBI lbi unreg hpi =- let invocation = HcPkg.unregisterInvocation- hpi Verbosity.normal packageDb pkgid- in if genScript- then writeFileAtomic unregScriptFileName- (BS.Char8.pack $ invocationAsSystemScript buildOS invocation)- else runProgramInvocation verbosity invocation+ let invocation =+ HcPkg.unregisterInvocation+ hpi+ Verbosity.normal+ mbWorkDir+ packageDb+ pkgid+ in if genScript+ then+ writeFileAtomic+ unregScriptFileName+ (BS.Char8.pack $ invocationAsSystemScript buildOS invocation)+ else runProgramInvocation verbosity invocation setupMessage verbosity "Unregistering" pkgid- withHcPkg verbosity "unregistering is only implemented for GHC and GHCJS"- (compiler lbi) (withPrograms lbi) unreg+ withHcPkg+ verbosity+ "unregistering is only implemented for GHC and GHCJS"+ (compiler lbi)+ (withPrograms lbi)+ unreg unregScriptFileName :: FilePath unregScriptFileName = case buildOS of- Windows -> "unregister.bat"- _ -> "unregister.sh"+ Windows -> "unregister.bat"+ _ -> "unregister.sh" -internalPackageDBPath :: LocalBuildInfo -> FilePath -> FilePath+internalPackageDBPath :: LocalBuildInfo -> SymbolicPath Pkg (Dir Dist) -> SymbolicPath Pkg (Dir PkgDB) internalPackageDBPath lbi distPref =- case compilerFlavor (compiler lbi) of- UHC -> UHC.inplacePackageDbPath lbi- _ -> distPref </> "package.conf.inplace"+ case compilerFlavor (compiler lbi) of+ UHC -> UHC.inplacePackageDbPath lbi+ _ -> distPref </> makeRelativePathEx "package.conf.inplace"
src/Distribution/Simple/Setup.hs view
@@ -1,2348 +1,251 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}---------------------------------------------------------------------------------- |--- Module : Distribution.Simple.Setup--- Copyright : Isaac Jones 2003-2004--- Duncan Coutts 2007--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ This is a big module, but not very complicated. The code is very regular--- and repetitive. It defines the command line interface for all the Cabal--- commands. For each command (like @configure@, @build@ etc) it defines a type--- that holds all the flags, the default set of flags and a 'CommandUI' that--- maps command line flags to and from the corresponding flags type.------ All the flags types are instances of 'Monoid', see--- <http://www.haskell.org/pipermail/cabal-devel/2007-December/001509.html>--- for an explanation.------ The types defined here get used in the front end and especially in--- @cabal-install@ which has to do quite a bit of manipulating sets of command--- line flags.------ This is actually relatively nice, it works quite well. The main change it--- needs is to unify it with the code for managing sets of fields that can be--- read and written from files. This would allow us to save configure flags in--- config files.--module Distribution.Simple.Setup (-- GlobalFlags(..), emptyGlobalFlags, defaultGlobalFlags, globalCommand,- ConfigFlags(..), emptyConfigFlags, defaultConfigFlags, configureCommand,- configPrograms,- configAbsolutePaths, readPackageDbList, showPackageDbList,- CopyFlags(..), emptyCopyFlags, defaultCopyFlags, copyCommand,- InstallFlags(..), emptyInstallFlags, defaultInstallFlags, installCommand,- HaddockTarget(..),- HaddockFlags(..), emptyHaddockFlags, defaultHaddockFlags, haddockCommand,- HscolourFlags(..), emptyHscolourFlags, defaultHscolourFlags, hscolourCommand,- BuildFlags(..), emptyBuildFlags, defaultBuildFlags, buildCommand,- ShowBuildInfoFlags(..), defaultShowBuildFlags, showBuildInfoCommand,- ReplFlags(..), defaultReplFlags, replCommand,- CleanFlags(..), emptyCleanFlags, defaultCleanFlags, cleanCommand,- RegisterFlags(..), emptyRegisterFlags, defaultRegisterFlags, registerCommand,- unregisterCommand,- SDistFlags(..), emptySDistFlags, defaultSDistFlags, sdistCommand,- TestFlags(..), emptyTestFlags, defaultTestFlags, testCommand,- TestShowDetails(..),- BenchmarkFlags(..), emptyBenchmarkFlags,- defaultBenchmarkFlags, benchmarkCommand,- CopyDest(..),- configureArgs, configureOptions, configureCCompiler, configureLinker,- buildOptions, haddockOptions, installDirsOptions,- testOptions', benchmarkOptions',- programDbOptions, programDbPaths',- programFlagsDescription,- replOptions,- splitArgs,-- defaultDistPref, optionDistPref,-- Flag(..),- toFlag,- fromFlag,- fromFlagOrDefault,- flagToMaybe,- flagToList,- maybeToFlag,- BooleanFlag(..),- boolOpt, boolOpt', trueArg, falseArg,- optionVerbosity, optionNumJobs) where--import Prelude ()-import Distribution.Compat.Prelude hiding (get)--import Distribution.Compiler-import Distribution.ReadE-import Distribution.Parsec-import Distribution.Pretty-import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp-import Distribution.ModuleName-import Distribution.PackageDescription-import Distribution.Simple.Command hiding (boolOpt, boolOpt')-import qualified Distribution.Simple.Command as Command-import Distribution.Simple.Compiler-import Distribution.Simple.Flag-import Distribution.Simple.Utils-import Distribution.Simple.Program-import Distribution.Simple.InstallDirs-import Distribution.Verbosity-import Distribution.Utils.NubList-import Distribution.Types.ComponentId-import Distribution.Types.GivenComponent-import Distribution.Types.Module-import Distribution.Types.PackageVersionConstraint--import Distribution.Compat.Stack-import Distribution.Compat.Semigroup (Last' (..), Option' (..))---- FIXME Not sure where this should live-defaultDistPref :: FilePath-defaultDistPref = "dist"---- --------------------------------------------------------------- * Global flags--- ---------------------------------------------------------------- In fact since individual flags types are monoids and these are just sets of--- flags then they are also monoids pointwise. This turns out to be really--- useful. The mempty is the set of empty flags and mappend allows us to--- override specific flags. For example we can start with default flags and--- override with the ones we get from a file or the command line, or both.---- | Flags that apply at the top level, not to any sub-command.-data GlobalFlags = GlobalFlags {- globalVersion :: Flag Bool,- globalNumericVersion :: Flag Bool- } deriving (Generic, Typeable)--defaultGlobalFlags :: GlobalFlags-defaultGlobalFlags = GlobalFlags {- globalVersion = Flag False,- globalNumericVersion = Flag False- }--globalCommand :: [Command action] -> CommandUI GlobalFlags-globalCommand commands = CommandUI- { commandName = ""- , commandSynopsis = ""- , commandUsage = \pname ->- "This Setup program uses the Haskell Cabal Infrastructure.\n"- ++ "See http://www.haskell.org/cabal/ for more information.\n"- ++ "\n"- ++ "Usage: " ++ pname ++ " [GLOBAL FLAGS] [COMMAND [FLAGS]]\n"- , commandDescription = Just $ \pname ->- let- commands' = commands ++ [commandAddAction helpCommandUI undefined]- cmdDescs = getNormalCommandDescriptions commands'- maxlen = maximum $ [length name | (name, _) <- cmdDescs]- align str = str ++ replicate (maxlen - length str) ' '- in- "Commands:\n"- ++ unlines [ " " ++ align name ++ " " ++ descr- | (name, descr) <- cmdDescs ]- ++ "\n"- ++ "For more information about a command use\n"- ++ " " ++ pname ++ " COMMAND --help\n\n"- ++ "Typical steps for installing Cabal packages:\n"- ++ concat [ " " ++ pname ++ " " ++ x ++ "\n"- | x <- ["configure", "build", "install"]]- , commandNotes = Nothing- , commandDefaultFlags = defaultGlobalFlags- , commandOptions = \_ ->- [option ['V'] ["version"]- "Print version information"- globalVersion (\v flags -> flags { globalVersion = v })- trueArg- ,option [] ["numeric-version"]- "Print just the version number"- globalNumericVersion (\v flags -> flags { globalNumericVersion = v })- trueArg- ]- }--emptyGlobalFlags :: GlobalFlags-emptyGlobalFlags = mempty--instance Monoid GlobalFlags where- mempty = gmempty- mappend = (<>)--instance Semigroup GlobalFlags where- (<>) = gmappend---- --------------------------------------------------------------- * Config flags--- ---------------------------------------------------------------- | Flags to @configure@ command.------ IMPORTANT: every time a new flag is added, 'D.C.Setup.filterConfigureFlags'--- should be updated.--- IMPORTANT: every time a new flag is added, it should be added to the Eq instance-data ConfigFlags = ConfigFlags {- -- This is the same hack as in 'buildArgs' and 'copyArgs'.- -- TODO: Stop using this eventually when 'UserHooks' gets changed- configArgs :: [String],-- --FIXME: the configPrograms is only here to pass info through to configure- -- because the type of configure is constrained by the UserHooks.- -- when we change UserHooks next we should pass the initial- -- ProgramDb directly and not via ConfigFlags- configPrograms_ :: Option' (Last' ProgramDb), -- ^All programs that- -- @cabal@ may run-- configProgramPaths :: [(String, FilePath)], -- ^user specified programs paths- configProgramArgs :: [(String, [String])], -- ^user specified programs args- configProgramPathExtra :: NubList FilePath, -- ^Extend the $PATH- configHcFlavor :: Flag CompilerFlavor, -- ^The \"flavor\" of the- -- compiler, e.g. GHC.- configHcPath :: Flag FilePath, -- ^given compiler location- configHcPkg :: Flag FilePath, -- ^given hc-pkg location- configVanillaLib :: Flag Bool, -- ^Enable vanilla library- configProfLib :: Flag Bool, -- ^Enable profiling in the library- configSharedLib :: Flag Bool, -- ^Build shared library- configStaticLib :: Flag Bool, -- ^Build static library- configDynExe :: Flag Bool, -- ^Enable dynamic linking of the- -- executables.- configFullyStaticExe :: Flag Bool, -- ^Enable fully static linking of the- -- executables.- configProfExe :: Flag Bool, -- ^Enable profiling in the- -- executables.- configProf :: Flag Bool, -- ^Enable profiling in the library- -- and executables.- configProfDetail :: Flag ProfDetailLevel, -- ^Profiling detail level- -- in the library and executables.- configProfLibDetail :: Flag ProfDetailLevel, -- ^Profiling detail level- -- in the library- configConfigureArgs :: [String], -- ^Extra arguments to @configure@- configOptimization :: Flag OptimisationLevel, -- ^Enable optimization.- configProgPrefix :: Flag PathTemplate, -- ^Installed executable prefix.- configProgSuffix :: Flag PathTemplate, -- ^Installed executable suffix.- configInstallDirs :: InstallDirs (Flag PathTemplate), -- ^Installation- -- paths- configScratchDir :: Flag FilePath,- configExtraLibDirs :: [FilePath], -- ^ path to search for extra libraries- configExtraFrameworkDirs :: [FilePath], -- ^ path to search for extra- -- frameworks (OS X only)- configExtraIncludeDirs :: [FilePath], -- ^ path to search for header files- configIPID :: Flag String, -- ^ explicit IPID to be used- configCID :: Flag ComponentId, -- ^ explicit CID to be used- configDeterministic :: Flag Bool, -- ^ be as deterministic as possible- -- (e.g., invariant over GHC, database,- -- etc). Used by the test suite-- configDistPref :: Flag FilePath, -- ^"dist" prefix- configCabalFilePath :: Flag FilePath, -- ^ Cabal file to use- configVerbosity :: Flag Verbosity, -- ^verbosity level- configUserInstall :: Flag Bool, -- ^The --user\/--global flag- configPackageDBs :: [Maybe PackageDB], -- ^Which package DBs to use- configGHCiLib :: Flag Bool, -- ^Enable compiling library for GHCi- configSplitSections :: Flag Bool, -- ^Enable -split-sections with GHC- configSplitObjs :: Flag Bool, -- ^Enable -split-objs with GHC- configStripExes :: Flag Bool, -- ^Enable executable stripping- configStripLibs :: Flag Bool, -- ^Enable library stripping- configConstraints :: [PackageVersionConstraint], -- ^Additional constraints for- -- dependencies.- configDependencies :: [GivenComponent],- -- ^The packages depended on.- configInstantiateWith :: [(ModuleName, Module)],- -- ^ The requested Backpack instantiation. If empty, either this- -- package does not use Backpack, or we just want to typecheck- -- the indefinite package.- configConfigurationsFlags :: FlagAssignment,- configTests :: Flag Bool, -- ^Enable test suite compilation- configBenchmarks :: Flag Bool, -- ^Enable benchmark compilation- configCoverage :: Flag Bool, -- ^Enable program coverage- configLibCoverage :: Flag Bool, -- ^Enable program coverage (deprecated)- configExactConfiguration :: Flag Bool,- -- ^All direct dependencies and flags are provided on the command line by- -- the user via the '--dependency' and '--flags' options.- configFlagError :: Flag String,- -- ^Halt and show an error message indicating an error in flag assignment- configRelocatable :: Flag Bool, -- ^ Enable relocatable package built- configDebugInfo :: Flag DebugInfoLevel, -- ^ Emit debug info.- configUseResponseFiles :: Flag Bool,- -- ^ Whether to use response files at all. They're used for such tools- -- as haddock, or ld.- configAllowDependingOnPrivateLibs :: Flag Bool- -- ^ Allow depending on private sublibraries. This is used by external- -- tools (like cabal-install) so they can add multiple-public-libraries- -- compatibility to older ghcs by checking visibility externally.- }- deriving (Generic, Read, Show, Typeable)--instance Binary ConfigFlags-instance Structured ConfigFlags---- | More convenient version of 'configPrograms'. Results in an--- 'error' if internal invariant is violated.-configPrograms :: WithCallStack (ConfigFlags -> ProgramDb)-configPrograms = fromMaybe (error "FIXME: remove configPrograms") . fmap getLast'- . getOption' . configPrograms_--instance Eq ConfigFlags where- (==) a b =- -- configPrograms skipped: not user specified, has no Eq instance- equal configProgramPaths- && equal configProgramArgs- && equal configProgramPathExtra- && equal configHcFlavor- && equal configHcPath- && equal configHcPkg- && equal configVanillaLib- && equal configProfLib- && equal configSharedLib- && equal configStaticLib- && equal configDynExe- && equal configFullyStaticExe- && equal configProfExe- && equal configProf- && equal configProfDetail- && equal configProfLibDetail- && equal configConfigureArgs- && equal configOptimization- && equal configProgPrefix- && equal configProgSuffix- && equal configInstallDirs- && equal configScratchDir- && equal configExtraLibDirs- && equal configExtraIncludeDirs- && equal configIPID- && equal configDeterministic- && equal configDistPref- && equal configVerbosity- && equal configUserInstall- && equal configPackageDBs- && equal configGHCiLib- && equal configSplitSections- && equal configSplitObjs- && equal configStripExes- && equal configStripLibs- && equal configConstraints- && equal configDependencies- && equal configConfigurationsFlags- && equal configTests- && equal configBenchmarks- && equal configCoverage- && equal configLibCoverage- && equal configExactConfiguration- && equal configFlagError- && equal configRelocatable- && equal configDebugInfo- && equal configUseResponseFiles- where- equal f = on (==) f a b--configAbsolutePaths :: ConfigFlags -> IO ConfigFlags-configAbsolutePaths f =- (\v -> f { configPackageDBs = v })- `liftM` traverse (maybe (return Nothing) (liftM Just . absolutePackageDBPath))- (configPackageDBs f)--defaultConfigFlags :: ProgramDb -> ConfigFlags-defaultConfigFlags progDb = emptyConfigFlags {- configArgs = [],- configPrograms_ = Option' (Just (Last' progDb)),- configHcFlavor = maybe NoFlag Flag defaultCompilerFlavor,- configVanillaLib = Flag True,- configProfLib = NoFlag,- configSharedLib = NoFlag,- configStaticLib = NoFlag,- configDynExe = Flag False,- configFullyStaticExe = Flag False,- configProfExe = NoFlag,- configProf = NoFlag,- configProfDetail = NoFlag,- configProfLibDetail= NoFlag,- configOptimization = Flag NormalOptimisation,- configProgPrefix = Flag (toPathTemplate ""),- configProgSuffix = Flag (toPathTemplate ""),- configDistPref = NoFlag,- configCabalFilePath = NoFlag,- configVerbosity = Flag normal,- configUserInstall = Flag False, --TODO: reverse this-#if defined(mingw32_HOST_OS)- -- See #1589.- configGHCiLib = Flag True,-#else- configGHCiLib = NoFlag,-#endif- configSplitSections = Flag False,- configSplitObjs = Flag False, -- takes longer, so turn off by default- configStripExes = NoFlag,- configStripLibs = NoFlag,- configTests = Flag False,- configBenchmarks = Flag False,- configCoverage = Flag False,- configLibCoverage = NoFlag,- configExactConfiguration = Flag False,- configFlagError = NoFlag,- configRelocatable = Flag False,- configDebugInfo = Flag NoDebugInfo,- configUseResponseFiles = NoFlag- }--configureCommand :: ProgramDb -> CommandUI ConfigFlags-configureCommand progDb = CommandUI- { commandName = "configure"- , commandSynopsis = "Prepare to build the package."- , commandDescription = Just $ \_ -> wrapText $- "Configure how the package is built by setting "- ++ "package (and other) flags.\n"- ++ "\n"- ++ "The configuration affects several other commands, "- ++ "including build, test, bench, run, repl.\n"- , commandNotes = Just $ \_pname -> programFlagsDescription progDb- , commandUsage = \pname ->- "Usage: " ++ pname ++ " configure [FLAGS]\n"- , commandDefaultFlags = defaultConfigFlags progDb- , commandOptions = \showOrParseArgs ->- configureOptions showOrParseArgs- ++ programDbPaths progDb showOrParseArgs- configProgramPaths (\v fs -> fs { configProgramPaths = v })- ++ programDbOption progDb showOrParseArgs- configProgramArgs (\v fs -> fs { configProgramArgs = v })- ++ programDbOptions progDb showOrParseArgs- configProgramArgs (\v fs -> fs { configProgramArgs = v })- }---- | Inverse to 'dispModSubstEntry'.-parsecModSubstEntry :: ParsecParser (ModuleName, Module)-parsecModSubstEntry = do- k <- parsec- _ <- P.char '='- v <- parsec- return (k, v)---- | Pretty-print a single entry of a module substitution.-dispModSubstEntry :: (ModuleName, Module) -> Disp.Doc-dispModSubstEntry (k, v) = pretty k <<>> Disp.char '=' <<>> pretty v--configureOptions :: ShowOrParseArgs -> [OptionField ConfigFlags]-configureOptions showOrParseArgs =- [optionVerbosity configVerbosity- (\v flags -> flags { configVerbosity = v })- ,optionDistPref- configDistPref (\d flags -> flags { configDistPref = d })- showOrParseArgs-- ,option [] ["compiler"] "compiler"- configHcFlavor (\v flags -> flags { configHcFlavor = v })- (choiceOpt [ (Flag GHC, ("g", ["ghc"]), "compile with GHC")- , (Flag GHCJS, ([] , ["ghcjs"]), "compile with GHCJS")- , (Flag UHC, ([] , ["uhc"]), "compile with UHC")- -- "haskell-suite" compiler id string will be replaced- -- by a more specific one during the configure stage- , (Flag (HaskellSuite "haskell-suite"), ([] , ["haskell-suite"]),- "compile with a haskell-suite compiler")])-- ,option "" ["cabal-file"]- "use this Cabal file"- configCabalFilePath (\v flags -> flags { configCabalFilePath = v })- (reqArgFlag "PATH")-- ,option "w" ["with-compiler"]- "give the path to a particular compiler"- configHcPath (\v flags -> flags { configHcPath = v })- (reqArgFlag "PATH")-- ,option "" ["with-hc-pkg"]- "give the path to the package tool"- configHcPkg (\v flags -> flags { configHcPkg = v })- (reqArgFlag "PATH")- ]- ++ map liftInstallDirs installDirsOptions- ++ [option "" ["program-prefix"]- "prefix to be applied to installed executables"- configProgPrefix- (\v flags -> flags { configProgPrefix = v })- (reqPathTemplateArgFlag "PREFIX")-- ,option "" ["program-suffix"]- "suffix to be applied to installed executables"- configProgSuffix (\v flags -> flags { configProgSuffix = v } )- (reqPathTemplateArgFlag "SUFFIX")-- ,option "" ["library-vanilla"]- "Vanilla libraries"- configVanillaLib (\v flags -> flags { configVanillaLib = v })- (boolOpt [] [])-- ,option "p" ["library-profiling"]- "Library profiling"- configProfLib (\v flags -> flags { configProfLib = v })- (boolOpt "p" [])-- ,option "" ["shared"]- "Shared library"- configSharedLib (\v flags -> flags { configSharedLib = v })- (boolOpt [] [])-- ,option "" ["static"]- "Static library"- configStaticLib (\v flags -> flags { configStaticLib = v })- (boolOpt [] [])-- ,option "" ["executable-dynamic"]- "Executable dynamic linking"- configDynExe (\v flags -> flags { configDynExe = v })- (boolOpt [] [])-- ,option "" ["executable-static"]- "Executable fully static linking"- configFullyStaticExe (\v flags -> flags { configFullyStaticExe = v })- (boolOpt [] [])-- ,option "" ["profiling"]- "Executable and library profiling"- configProf (\v flags -> flags { configProf = v })- (boolOpt [] [])-- ,option "" ["executable-profiling"]- "Executable profiling (DEPRECATED)"- configProfExe (\v flags -> flags { configProfExe = v })- (boolOpt [] [])-- ,option "" ["profiling-detail"]- ("Profiling detail level for executable and library (default, " ++- "none, exported-functions, toplevel-functions, all-functions).")- configProfDetail (\v flags -> flags { configProfDetail = v })- (reqArg' "level" (Flag . flagToProfDetailLevel)- showProfDetailLevelFlag)-- ,option "" ["library-profiling-detail"]- "Profiling detail level for libraries only."- configProfLibDetail (\v flags -> flags { configProfLibDetail = v })- (reqArg' "level" (Flag . flagToProfDetailLevel)- showProfDetailLevelFlag)-- ,multiOption "optimization"- configOptimization (\v flags -> flags { configOptimization = v })- [optArg' "n" (Flag . flagToOptimisationLevel)- (\f -> case f of- Flag NoOptimisation -> []- Flag NormalOptimisation -> [Nothing]- Flag MaximumOptimisation -> [Just "2"]- _ -> [])- "O" ["enable-optimization","enable-optimisation"]- "Build with optimization (n is 0--2, default is 1)",- noArg (Flag NoOptimisation) []- ["disable-optimization","disable-optimisation"]- "Build without optimization"- ]-- ,multiOption "debug-info"- configDebugInfo (\v flags -> flags { configDebugInfo = v })- [optArg' "n" (Flag . flagToDebugInfoLevel)- (\f -> case f of- Flag NoDebugInfo -> []- Flag MinimalDebugInfo -> [Just "1"]- Flag NormalDebugInfo -> [Nothing]- Flag MaximalDebugInfo -> [Just "3"]- _ -> [])- "" ["enable-debug-info"]- "Emit debug info (n is 0--3, default is 0)",- noArg (Flag NoDebugInfo) []- ["disable-debug-info"]- "Don't emit debug info"- ]-- ,option "" ["library-for-ghci"]- "compile library for use with GHCi"- configGHCiLib (\v flags -> flags { configGHCiLib = v })- (boolOpt [] [])-- ,option "" ["split-sections"]- "compile library code such that unneeded definitions can be dropped from the final executable (GHC 7.8+)"- configSplitSections (\v flags -> flags { configSplitSections = v })- (boolOpt [] [])-- ,option "" ["split-objs"]- "split library into smaller objects to reduce binary sizes (GHC 6.6+)"- configSplitObjs (\v flags -> flags { configSplitObjs = v })- (boolOpt [] [])-- ,option "" ["executable-stripping"]- "strip executables upon installation to reduce binary sizes"- configStripExes (\v flags -> flags { configStripExes = v })- (boolOpt [] [])-- ,option "" ["library-stripping"]- "strip libraries upon installation to reduce binary sizes"- configStripLibs (\v flags -> flags { configStripLibs = v })- (boolOpt [] [])-- ,option "" ["configure-option"]- "Extra option for configure"- configConfigureArgs (\v flags -> flags { configConfigureArgs = v })- (reqArg' "OPT" (\x -> [x]) id)-- ,option "" ["user-install"]- "doing a per-user installation"- configUserInstall (\v flags -> flags { configUserInstall = v })- (boolOpt' ([],["user"]) ([], ["global"]))-- ,option "" ["package-db"]- ( "Append the given package database to the list of package"- ++ " databases used (to satisfy dependencies and register into)."- ++ " May be a specific file, 'global' or 'user'. The initial list"- ++ " is ['global'], ['global', 'user'], or ['global', $sandbox],"- ++ " depending on context. Use 'clear' to reset the list to empty."- ++ " See the user guide for details.")- configPackageDBs (\v flags -> flags { configPackageDBs = v })- (reqArg' "DB" readPackageDbList showPackageDbList)-- ,option "f" ["flags"]- "Force values for the given flags in Cabal conditionals in the .cabal file. E.g., --flags=\"debug -usebytestrings\" forces the flag \"debug\" to true and \"usebytestrings\" to false."- configConfigurationsFlags (\v flags -> flags { configConfigurationsFlags = v })- (reqArg "FLAGS"- (parsecToReadE (\err -> "Invalid flag assignment: " ++ err) legacyParsecFlagAssignment)- legacyShowFlagAssignment')-- ,option "" ["extra-include-dirs"]- "A list of directories to search for header files"- configExtraIncludeDirs (\v flags -> flags {configExtraIncludeDirs = v})- (reqArg' "PATH" (\x -> [x]) id)-- ,option "" ["deterministic"]- "Try to be as deterministic as possible (used by the test suite)"- configDeterministic (\v flags -> flags {configDeterministic = v})- (boolOpt [] [])-- ,option "" ["ipid"]- "Installed package ID to compile this package as"- configIPID (\v flags -> flags {configIPID = v})- (reqArgFlag "IPID")-- ,option "" ["cid"]- "Installed component ID to compile this component as"- (fmap prettyShow . configCID) (\v flags -> flags {configCID = fmap mkComponentId v})- (reqArgFlag "CID")-- ,option "" ["extra-lib-dirs"]- "A list of directories to search for external libraries"- configExtraLibDirs (\v flags -> flags {configExtraLibDirs = v})- (reqArg' "PATH" (\x -> [x]) id)-- ,option "" ["extra-framework-dirs"]- "A list of directories to search for external frameworks (OS X only)"- configExtraFrameworkDirs- (\v flags -> flags {configExtraFrameworkDirs = v})- (reqArg' "PATH" (\x -> [x]) id)-- ,option "" ["extra-prog-path"]- "A list of directories to search for required programs (in addition to the normal search locations)"- configProgramPathExtra (\v flags -> flags {configProgramPathExtra = v})- (reqArg' "PATH" (\x -> toNubList [x]) fromNubList)-- ,option "" ["constraint"]- "A list of additional constraints on the dependencies."- configConstraints (\v flags -> flags { configConstraints = v})- (reqArg "DEPENDENCY"- (parsecToReadE (const "dependency expected") ((\x -> [x]) `fmap` parsec))- (map prettyShow))-- ,option "" ["dependency"]- "A list of exact dependencies. E.g., --dependency=\"void=void-0.5.8-177d5cdf20962d0581fe2e4932a6c309\""- configDependencies (\v flags -> flags { configDependencies = v})- (reqArg "NAME[:COMPONENT_NAME]=CID"- (parsecToReadE (const "dependency expected") ((\x -> [x]) `fmap` parsecGivenComponent))- (map (\(GivenComponent pn cn cid) ->- prettyShow pn- ++ case cn of LMainLibName -> ""- LSubLibName n -> ":" ++ prettyShow n- ++ "=" ++ prettyShow cid)))-- ,option "" ["instantiate-with"]- "A mapping of signature names to concrete module instantiations."- configInstantiateWith (\v flags -> flags { configInstantiateWith = v })- (reqArg "NAME=MOD"- (parsecToReadE ("Cannot parse module substitution: " ++) (fmap (:[]) parsecModSubstEntry))- (map (Disp.renderStyle defaultStyle . dispModSubstEntry)))-- ,option "" ["tests"]- "dependency checking and compilation for test suites listed in the package description file."- configTests (\v flags -> flags { configTests = v })- (boolOpt [] [])-- ,option "" ["coverage"]- "build package with Haskell Program Coverage. (GHC only)"- configCoverage (\v flags -> flags { configCoverage = v })- (boolOpt [] [])-- ,option "" ["library-coverage"]- "build package with Haskell Program Coverage. (GHC only) (DEPRECATED)"- configLibCoverage (\v flags -> flags { configLibCoverage = v })- (boolOpt [] [])-- ,option "" ["exact-configuration"]- "All direct dependencies and flags are provided on the command line."- configExactConfiguration- (\v flags -> flags { configExactConfiguration = v })- trueArg-- ,option "" ["benchmarks"]- "dependency checking and compilation for benchmarks listed in the package description file."- configBenchmarks (\v flags -> flags { configBenchmarks = v })- (boolOpt [] [])-- ,option "" ["relocatable"]- "building a package that is relocatable. (GHC only)"- configRelocatable (\v flags -> flags { configRelocatable = v})- (boolOpt [] [])-- ,option "" ["response-files"]- "enable workaround for old versions of programs like \"ar\" that do not support @file arguments"- configUseResponseFiles- (\v flags -> flags { configUseResponseFiles = v })- (boolOpt' ([], ["disable-response-files"]) ([], []))-- ,option "" ["allow-depending-on-private-libs"]- ( "Allow depending on private libraries. "- ++ "If set, the library visibility check MUST be done externally." )- configAllowDependingOnPrivateLibs- (\v flags -> flags { configAllowDependingOnPrivateLibs = v })- trueArg- ]- where- liftInstallDirs =- liftOption configInstallDirs (\v flags -> flags { configInstallDirs = v })-- reqPathTemplateArgFlag title _sf _lf d get set =- reqArgFlag title _sf _lf d- (fmap fromPathTemplate . get) (set . fmap toPathTemplate)--readPackageDbList :: String -> [Maybe PackageDB]-readPackageDbList "clear" = [Nothing]-readPackageDbList "global" = [Just GlobalPackageDB]-readPackageDbList "user" = [Just UserPackageDB]-readPackageDbList other = [Just (SpecificPackageDB other)]--showPackageDbList :: [Maybe PackageDB] -> [String]-showPackageDbList = map showPackageDb- where- showPackageDb Nothing = "clear"- showPackageDb (Just GlobalPackageDB) = "global"- showPackageDb (Just UserPackageDB) = "user"- showPackageDb (Just (SpecificPackageDB db)) = db--showProfDetailLevelFlag :: Flag ProfDetailLevel -> [String]-showProfDetailLevelFlag NoFlag = []-showProfDetailLevelFlag (Flag dl) = [showProfDetailLevel dl]--parsecGivenComponent :: ParsecParser GivenComponent-parsecGivenComponent = do- pn <- parsec- ln <- P.option LMainLibName $ do- _ <- P.char ':'- ucn <- parsec- return $ if unUnqualComponentName ucn == unPackageName pn- then LMainLibName- else LSubLibName ucn- _ <- P.char '='- cid <- parsec- return $ GivenComponent pn ln cid--installDirsOptions :: [OptionField (InstallDirs (Flag PathTemplate))]-installDirsOptions =- [ option "" ["prefix"]- "bake this prefix in preparation of installation"- prefix (\v flags -> flags { prefix = v })- installDirArg-- , option "" ["bindir"]- "installation directory for executables"- bindir (\v flags -> flags { bindir = v })- installDirArg-- , option "" ["libdir"]- "installation directory for libraries"- libdir (\v flags -> flags { libdir = v })- installDirArg-- , option "" ["libsubdir"]- "subdirectory of libdir in which libs are installed"- libsubdir (\v flags -> flags { libsubdir = v })- installDirArg-- , option "" ["dynlibdir"]- "installation directory for dynamic libraries"- dynlibdir (\v flags -> flags { dynlibdir = v })- installDirArg-- , option "" ["libexecdir"]- "installation directory for program executables"- libexecdir (\v flags -> flags { libexecdir = v })- installDirArg-- , option "" ["libexecsubdir"]- "subdirectory of libexecdir in which private executables are installed"- libexecsubdir (\v flags -> flags { libexecsubdir = v })- installDirArg-- , option "" ["datadir"]- "installation directory for read-only data"- datadir (\v flags -> flags { datadir = v })- installDirArg-- , option "" ["datasubdir"]- "subdirectory of datadir in which data files are installed"- datasubdir (\v flags -> flags { datasubdir = v })- installDirArg-- , option "" ["docdir"]- "installation directory for documentation"- docdir (\v flags -> flags { docdir = v })- installDirArg-- , option "" ["htmldir"]- "installation directory for HTML documentation"- htmldir (\v flags -> flags { htmldir = v })- installDirArg-- , option "" ["haddockdir"]- "installation directory for haddock interfaces"- haddockdir (\v flags -> flags { haddockdir = v })- installDirArg-- , option "" ["sysconfdir"]- "installation directory for configuration files"- sysconfdir (\v flags -> flags { sysconfdir = v })- installDirArg- ]- where- installDirArg _sf _lf d get set =- reqArgFlag "DIR" _sf _lf d- (fmap fromPathTemplate . get) (set . fmap toPathTemplate)--emptyConfigFlags :: ConfigFlags-emptyConfigFlags = mempty--instance Monoid ConfigFlags where- mempty = gmempty- mappend = (<>)--instance Semigroup ConfigFlags where- (<>) = gmappend---- --------------------------------------------------------------- * Copy flags--- ---------------------------------------------------------------- | Flags to @copy@: (destdir, copy-prefix (backwards compat), verbosity)-data CopyFlags = CopyFlags {- copyDest :: Flag CopyDest,- copyDistPref :: Flag FilePath,- copyVerbosity :: Flag Verbosity,- -- This is the same hack as in 'buildArgs'. But I (ezyang) don't- -- think it's a hack, it's the right way to make hooks more robust- -- TODO: Stop using this eventually when 'UserHooks' gets changed- copyArgs :: [String],- copyCabalFilePath :: Flag FilePath- }- deriving (Show, Generic)--defaultCopyFlags :: CopyFlags-defaultCopyFlags = CopyFlags {- copyDest = Flag NoCopyDest,- copyDistPref = NoFlag,- copyVerbosity = Flag normal,- copyArgs = [],- copyCabalFilePath = mempty- }--copyCommand :: CommandUI CopyFlags-copyCommand = CommandUI- { commandName = "copy"- , commandSynopsis = "Copy the files of all/specific components to install locations."- , commandDescription = Just $ \_ -> wrapText $- "Components encompass executables and libraries. "- ++ "Does not call register, and allows a prefix at install time. "- ++ "Without the --destdir flag, configure determines location.\n"- , commandNotes = Just $ \pname ->- "Examples:\n"- ++ " " ++ pname ++ " copy "- ++ " All the components in the package\n"- ++ " " ++ pname ++ " copy foo "- ++ " A component (i.e. lib, exe, test suite)"- , commandUsage = usageAlternatives "copy" $- [ "[FLAGS]"- , "COMPONENTS [FLAGS]"- ]- , commandDefaultFlags = defaultCopyFlags- , commandOptions = \showOrParseArgs -> case showOrParseArgs of- ShowArgs -> filter ((`notElem` ["target-package-db"])- . optionName) $ copyOptions ShowArgs- ParseArgs -> copyOptions ParseArgs-}--copyOptions :: ShowOrParseArgs -> [OptionField CopyFlags]-copyOptions showOrParseArgs =- [optionVerbosity copyVerbosity (\v flags -> flags { copyVerbosity = v })-- ,optionDistPref- copyDistPref (\d flags -> flags { copyDistPref = d })- showOrParseArgs-- ,option "" ["destdir"]- "directory to copy files to, prepended to installation directories"- copyDest (\v flags -> case copyDest flags of- Flag (CopyToDb _) -> error "Use either 'destdir' or 'target-package-db'."- _ -> flags { copyDest = v })- (reqArg "DIR" (succeedReadE (Flag . CopyTo))- (\f -> case f of Flag (CopyTo p) -> [p]; _ -> []))-- ,option "" ["target-package-db"]- "package database to copy files into. Required when using ${pkgroot} prefix."- copyDest (\v flags -> case copyDest flags of- NoFlag -> flags { copyDest = v }- Flag NoCopyDest -> flags { copyDest = v }- _ -> error "Use either 'destdir' or 'target-package-db'.")- (reqArg "DATABASE" (succeedReadE (Flag . CopyToDb))- (\f -> case f of Flag (CopyToDb p) -> [p]; _ -> []))- ]--emptyCopyFlags :: CopyFlags-emptyCopyFlags = mempty--instance Monoid CopyFlags where- mempty = gmempty- mappend = (<>)--instance Semigroup CopyFlags where- (<>) = gmappend---- --------------------------------------------------------------- * Install flags--- ---------------------------------------------------------------- | Flags to @install@: (package db, verbosity)-data InstallFlags = InstallFlags {- installPackageDB :: Flag PackageDB,- installDest :: Flag CopyDest,- installDistPref :: Flag FilePath,- installUseWrapper :: Flag Bool,- installInPlace :: Flag Bool,- installVerbosity :: Flag Verbosity,- -- this is only here, because we can not- -- change the hooks API.- installCabalFilePath :: Flag FilePath- }- deriving (Show, Generic)--defaultInstallFlags :: InstallFlags-defaultInstallFlags = InstallFlags {- installPackageDB = NoFlag,- installDest = Flag NoCopyDest,- installDistPref = NoFlag,- installUseWrapper = Flag False,- installInPlace = Flag False,- installVerbosity = Flag normal,- installCabalFilePath = mempty- }--installCommand :: CommandUI InstallFlags-installCommand = CommandUI- { commandName = "install"- , commandSynopsis =- "Copy the files into the install locations. Run register."- , commandDescription = Just $ \_ -> wrapText $- "Unlike the copy command, install calls the register command."- ++ "If you want to install into a location that is not what was"- ++ "specified in the configure step, use the copy command.\n"- , commandNotes = Nothing- , commandUsage = \pname ->- "Usage: " ++ pname ++ " install [FLAGS]\n"- , commandDefaultFlags = defaultInstallFlags- , commandOptions = \showOrParseArgs -> case showOrParseArgs of- ShowArgs -> filter ((`notElem` ["target-package-db"])- . optionName) $ installOptions ShowArgs- ParseArgs -> installOptions ParseArgs- }--installOptions :: ShowOrParseArgs -> [OptionField InstallFlags]-installOptions showOrParseArgs =- [optionVerbosity installVerbosity (\v flags -> flags { installVerbosity = v })- ,optionDistPref- installDistPref (\d flags -> flags { installDistPref = d })- showOrParseArgs-- ,option "" ["inplace"]- "install the package in the install subdirectory of the dist prefix, so it can be used without being installed"- installInPlace (\v flags -> flags { installInPlace = v })- trueArg-- ,option "" ["shell-wrappers"]- "using shell script wrappers around executables"- installUseWrapper (\v flags -> flags { installUseWrapper = v })- (boolOpt [] [])-- ,option "" ["package-db"] ""- installPackageDB (\v flags -> flags { installPackageDB = v })- (choiceOpt [ (Flag UserPackageDB, ([],["user"]),- "upon configuration register this package in the user's local package database")- , (Flag GlobalPackageDB, ([],["global"]),- "(default) upon configuration register this package in the system-wide package database")])- ,option "" ["target-package-db"]- "package database to install into. Required when using ${pkgroot} prefix."- installDest (\v flags -> flags { installDest = v })- (reqArg "DATABASE" (succeedReadE (Flag . CopyToDb))- (\f -> case f of Flag (CopyToDb p) -> [p]; _ -> []))- ]--emptyInstallFlags :: InstallFlags-emptyInstallFlags = mempty--instance Monoid InstallFlags where- mempty = gmempty- mappend = (<>)--instance Semigroup InstallFlags where- (<>) = gmappend---- --------------------------------------------------------------- * SDist flags--- ---------------------------------------------------------------- | Flags to @sdist@: (snapshot, verbosity)-data SDistFlags = SDistFlags {- sDistSnapshot :: Flag Bool,- sDistDirectory :: Flag FilePath,- sDistDistPref :: Flag FilePath,- sDistListSources :: Flag FilePath,- sDistVerbosity :: Flag Verbosity- }- deriving (Show, Generic, Typeable)--defaultSDistFlags :: SDistFlags-defaultSDistFlags = SDistFlags {- sDistSnapshot = Flag False,- sDistDirectory = mempty,- sDistDistPref = NoFlag,- sDistListSources = mempty,- sDistVerbosity = Flag normal- }--sdistCommand :: CommandUI SDistFlags-sdistCommand = CommandUI- { commandName = "sdist"- , commandSynopsis =- "Generate a source distribution file (.tar.gz)."- , commandDescription = Nothing- , commandNotes = Nothing- , commandUsage = \pname ->- "Usage: " ++ pname ++ " sdist [FLAGS]\n"- , commandDefaultFlags = defaultSDistFlags- , commandOptions = \showOrParseArgs ->- [optionVerbosity sDistVerbosity (\v flags -> flags { sDistVerbosity = v })- ,optionDistPref- sDistDistPref (\d flags -> flags { sDistDistPref = d })- showOrParseArgs-- ,option "" ["list-sources"]- "Just write a list of the package's sources to a file"- sDistListSources (\v flags -> flags { sDistListSources = v })- (reqArgFlag "FILE")-- ,option "" ["snapshot"]- "Produce a snapshot source distribution"- sDistSnapshot (\v flags -> flags { sDistSnapshot = v })- trueArg-- ,option "" ["output-directory"]- ("Generate a source distribution in the given directory, "- ++ "without creating a tarball")- sDistDirectory (\v flags -> flags { sDistDirectory = v })- (reqArgFlag "DIR")- ]- }--emptySDistFlags :: SDistFlags-emptySDistFlags = mempty--instance Monoid SDistFlags where- mempty = gmempty- mappend = (<>)--instance Semigroup SDistFlags where- (<>) = gmappend---- --------------------------------------------------------------- * Register flags--- ---------------------------------------------------------------- | Flags to @register@ and @unregister@: (user package, gen-script,--- in-place, verbosity)-data RegisterFlags = RegisterFlags {- regPackageDB :: Flag PackageDB,- regGenScript :: Flag Bool,- regGenPkgConf :: Flag (Maybe FilePath),- regInPlace :: Flag Bool,- regDistPref :: Flag FilePath,- regPrintId :: Flag Bool,- regVerbosity :: Flag Verbosity,- -- Same as in 'buildArgs' and 'copyArgs'- regArgs :: [String],- regCabalFilePath :: Flag FilePath- }- deriving (Show, Generic, Typeable)--defaultRegisterFlags :: RegisterFlags-defaultRegisterFlags = RegisterFlags {- regPackageDB = NoFlag,- regGenScript = Flag False,- regGenPkgConf = NoFlag,- regInPlace = Flag False,- regDistPref = NoFlag,- regPrintId = Flag False,- regArgs = [],- regCabalFilePath = mempty,- regVerbosity = Flag normal- }--registerCommand :: CommandUI RegisterFlags-registerCommand = CommandUI- { commandName = "register"- , commandSynopsis =- "Register this package with the compiler."- , commandDescription = Nothing- , commandNotes = Nothing- , commandUsage = \pname ->- "Usage: " ++ pname ++ " register [FLAGS]\n"- , commandDefaultFlags = defaultRegisterFlags- , commandOptions = \showOrParseArgs ->- [optionVerbosity regVerbosity (\v flags -> flags { regVerbosity = v })- ,optionDistPref- regDistPref (\d flags -> flags { regDistPref = d })- showOrParseArgs-- ,option "" ["packageDB"] ""- regPackageDB (\v flags -> flags { regPackageDB = v })- (choiceOpt [ (Flag UserPackageDB, ([],["user"]),- "upon registration, register this package in the user's local package database")- , (Flag GlobalPackageDB, ([],["global"]),- "(default)upon registration, register this package in the system-wide package database")])-- ,option "" ["inplace"]- "register the package in the build location, so it can be used without being installed"- regInPlace (\v flags -> flags { regInPlace = v })- trueArg-- ,option "" ["gen-script"]- "instead of registering, generate a script to register later"- regGenScript (\v flags -> flags { regGenScript = v })- trueArg-- ,option "" ["gen-pkg-config"]- "instead of registering, generate a package registration file/directory"- regGenPkgConf (\v flags -> flags { regGenPkgConf = v })- (optArg' "PKG" Flag flagToList)-- ,option "" ["print-ipid"]- "print the installed package ID calculated for this package"- regPrintId (\v flags -> flags { regPrintId = v })- trueArg- ]- }--unregisterCommand :: CommandUI RegisterFlags-unregisterCommand = CommandUI- { commandName = "unregister"- , commandSynopsis =- "Unregister this package with the compiler."- , commandDescription = Nothing- , commandNotes = Nothing- , commandUsage = \pname ->- "Usage: " ++ pname ++ " unregister [FLAGS]\n"- , commandDefaultFlags = defaultRegisterFlags- , commandOptions = \showOrParseArgs ->- [optionVerbosity regVerbosity (\v flags -> flags { regVerbosity = v })- ,optionDistPref- regDistPref (\d flags -> flags { regDistPref = d })- showOrParseArgs-- ,option "" ["user"] ""- regPackageDB (\v flags -> flags { regPackageDB = v })- (choiceOpt [ (Flag UserPackageDB, ([],["user"]),- "unregister this package in the user's local package database")- , (Flag GlobalPackageDB, ([],["global"]),- "(default) unregister this package in the system-wide package database")])-- ,option "" ["gen-script"]- "Instead of performing the unregister command, generate a script to unregister later"- regGenScript (\v flags -> flags { regGenScript = v })- trueArg- ]- }--emptyRegisterFlags :: RegisterFlags-emptyRegisterFlags = mempty--instance Monoid RegisterFlags where- mempty = gmempty- mappend = (<>)--instance Semigroup RegisterFlags where- (<>) = gmappend---- --------------------------------------------------------------- * HsColour flags--- --------------------------------------------------------------data HscolourFlags = HscolourFlags {- hscolourCSS :: Flag FilePath,- hscolourExecutables :: Flag Bool,- hscolourTestSuites :: Flag Bool,- hscolourBenchmarks :: Flag Bool,- hscolourForeignLibs :: Flag Bool,- hscolourDistPref :: Flag FilePath,- hscolourVerbosity :: Flag Verbosity,- hscolourCabalFilePath :: Flag FilePath- }- deriving (Show, Generic, Typeable)--emptyHscolourFlags :: HscolourFlags-emptyHscolourFlags = mempty--defaultHscolourFlags :: HscolourFlags-defaultHscolourFlags = HscolourFlags {- hscolourCSS = NoFlag,- hscolourExecutables = Flag False,- hscolourTestSuites = Flag False,- hscolourBenchmarks = Flag False,- hscolourDistPref = NoFlag,- hscolourForeignLibs = Flag False,- hscolourVerbosity = Flag normal,- hscolourCabalFilePath = mempty- }--instance Monoid HscolourFlags where- mempty = gmempty- mappend = (<>)--instance Semigroup HscolourFlags where- (<>) = gmappend--hscolourCommand :: CommandUI HscolourFlags-hscolourCommand = CommandUI- { commandName = "hscolour"- , commandSynopsis =- "Generate HsColour colourised code, in HTML format."- , commandDescription = Just (\_ -> "Requires the hscolour program.\n")- , commandNotes = Just $ \_ ->- "Deprecated in favour of 'cabal haddock --hyperlink-source'."- , commandUsage = \pname ->- "Usage: " ++ pname ++ " hscolour [FLAGS]\n"- , commandDefaultFlags = defaultHscolourFlags- , commandOptions = \showOrParseArgs ->- [optionVerbosity hscolourVerbosity- (\v flags -> flags { hscolourVerbosity = v })- ,optionDistPref- hscolourDistPref (\d flags -> flags { hscolourDistPref = d })- showOrParseArgs-- ,option "" ["executables"]- "Run hscolour for Executables targets"- hscolourExecutables (\v flags -> flags { hscolourExecutables = v })- trueArg-- ,option "" ["tests"]- "Run hscolour for Test Suite targets"- hscolourTestSuites (\v flags -> flags { hscolourTestSuites = v })- trueArg-- ,option "" ["benchmarks"]- "Run hscolour for Benchmark targets"- hscolourBenchmarks (\v flags -> flags { hscolourBenchmarks = v })- trueArg-- ,option "" ["foreign-libraries"]- "Run hscolour for Foreign Library targets"- hscolourForeignLibs (\v flags -> flags { hscolourForeignLibs = v })- trueArg-- ,option "" ["all"]- "Run hscolour for all targets"- (\f -> allFlags [ hscolourExecutables f- , hscolourTestSuites f- , hscolourBenchmarks f- , hscolourForeignLibs f- ])- (\v flags -> flags { hscolourExecutables = v- , hscolourTestSuites = v- , hscolourBenchmarks = v- , hscolourForeignLibs = v- })- trueArg-- ,option "" ["css"]- "Use a cascading style sheet"- hscolourCSS (\v flags -> flags { hscolourCSS = v })- (reqArgFlag "PATH")- ]- }---- --------------------------------------------------------------- * Haddock flags--- ----------------------------------------------------------------- | When we build haddock documentation, there are two cases:------ 1. We build haddocks only for the current development version,--- intended for local use and not for distribution. In this case,--- we store the generated documentation in @<dist>/doc/html/<package name>@.------ 2. We build haddocks for intended for uploading them to hackage.--- In this case, we need to follow the layout that hackage expects--- from documentation tarballs, and we might also want to use different--- flags than for development builds, so in this case we store the generated--- documentation in @<dist>/doc/html/<package id>-docs@.-data HaddockTarget = ForHackage | ForDevelopment deriving (Eq, Show, Generic, Typeable)--instance Binary HaddockTarget-instance Structured HaddockTarget--instance Pretty HaddockTarget where- pretty ForHackage = Disp.text "for-hackage"- pretty ForDevelopment = Disp.text "for-development"--instance Parsec HaddockTarget where- parsec = P.choice [ P.try $ P.string "for-hackage" >> return ForHackage- , P.string "for-development" >> return ForDevelopment]--data HaddockFlags = HaddockFlags {- haddockProgramPaths :: [(String, FilePath)],- haddockProgramArgs :: [(String, [String])],- haddockHoogle :: Flag Bool,- haddockHtml :: Flag Bool,- haddockHtmlLocation :: Flag String,- haddockForHackage :: Flag HaddockTarget,- haddockExecutables :: Flag Bool,- haddockTestSuites :: Flag Bool,- haddockBenchmarks :: Flag Bool,- haddockForeignLibs :: Flag Bool,- haddockInternal :: Flag Bool,- haddockCss :: Flag FilePath,- haddockLinkedSource :: Flag Bool,- haddockQuickJump :: Flag Bool,- haddockHscolourCss :: Flag FilePath,- haddockContents :: Flag PathTemplate,- haddockDistPref :: Flag FilePath,- haddockKeepTempFiles:: Flag Bool,- haddockVerbosity :: Flag Verbosity,- haddockCabalFilePath :: Flag FilePath,- haddockArgs :: [String]- }- deriving (Show, Generic, Typeable)--defaultHaddockFlags :: HaddockFlags-defaultHaddockFlags = HaddockFlags {- haddockProgramPaths = mempty,- haddockProgramArgs = [],- haddockHoogle = Flag False,- haddockHtml = Flag False,- haddockHtmlLocation = NoFlag,- haddockForHackage = NoFlag,- haddockExecutables = Flag False,- haddockTestSuites = Flag False,- haddockBenchmarks = Flag False,- haddockForeignLibs = Flag False,- haddockInternal = Flag False,- haddockCss = NoFlag,- haddockLinkedSource = Flag False,- haddockQuickJump = Flag False,- haddockHscolourCss = NoFlag,- haddockContents = NoFlag,- haddockDistPref = NoFlag,- haddockKeepTempFiles= Flag False,- haddockVerbosity = Flag normal,- haddockCabalFilePath = mempty,- haddockArgs = mempty- }--haddockCommand :: CommandUI HaddockFlags-haddockCommand = CommandUI- { commandName = "haddock"- , commandSynopsis = "Generate Haddock HTML documentation."- , commandDescription = Just $ \_ ->- "Requires the program haddock, version 2.x.\n"- , commandNotes = Nothing- , commandUsage = usageAlternatives "haddock" $- [ "[FLAGS]"- , "COMPONENTS [FLAGS]"- ]- , commandDefaultFlags = defaultHaddockFlags- , commandOptions = \showOrParseArgs ->- haddockOptions showOrParseArgs- ++ programDbPaths progDb ParseArgs- haddockProgramPaths (\v flags -> flags { haddockProgramPaths = v})- ++ programDbOption progDb showOrParseArgs- haddockProgramArgs (\v fs -> fs { haddockProgramArgs = v })- ++ programDbOptions progDb ParseArgs- haddockProgramArgs (\v flags -> flags { haddockProgramArgs = v})- }- where- progDb = addKnownProgram haddockProgram- $ addKnownProgram ghcProgram- $ emptyProgramDb--haddockOptions :: ShowOrParseArgs -> [OptionField HaddockFlags]-haddockOptions showOrParseArgs =- [optionVerbosity haddockVerbosity- (\v flags -> flags { haddockVerbosity = v })- ,optionDistPref- haddockDistPref (\d flags -> flags { haddockDistPref = d })- showOrParseArgs-- ,option "" ["keep-temp-files"]- "Keep temporary files"- haddockKeepTempFiles (\b flags -> flags { haddockKeepTempFiles = b })- trueArg-- ,option "" ["hoogle"]- "Generate a hoogle database"- haddockHoogle (\v flags -> flags { haddockHoogle = v })- trueArg-- ,option "" ["html"]- "Generate HTML documentation (the default)"- haddockHtml (\v flags -> flags { haddockHtml = v })- trueArg-- ,option "" ["html-location"]- "Location of HTML documentation for pre-requisite packages"- haddockHtmlLocation (\v flags -> flags { haddockHtmlLocation = v })- (reqArgFlag "URL")-- ,option "" ["for-hackage"]- "Collection of flags to generate documentation suitable for upload to hackage"- haddockForHackage (\v flags -> flags { haddockForHackage = v })- (noArg (Flag ForHackage))-- ,option "" ["executables"]- "Run haddock for Executables targets"- haddockExecutables (\v flags -> flags { haddockExecutables = v })- trueArg-- ,option "" ["tests"]- "Run haddock for Test Suite targets"- haddockTestSuites (\v flags -> flags { haddockTestSuites = v })- trueArg-- ,option "" ["benchmarks"]- "Run haddock for Benchmark targets"- haddockBenchmarks (\v flags -> flags { haddockBenchmarks = v })- trueArg-- ,option "" ["foreign-libraries"]- "Run haddock for Foreign Library targets"- haddockForeignLibs (\v flags -> flags { haddockForeignLibs = v })- trueArg-- ,option "" ["all"]- "Run haddock for all targets"- (\f -> allFlags [ haddockExecutables f- , haddockTestSuites f- , haddockBenchmarks f- , haddockForeignLibs f- ])- (\v flags -> flags { haddockExecutables = v- , haddockTestSuites = v- , haddockBenchmarks = v- , haddockForeignLibs = v- })- trueArg-- ,option "" ["internal"]- "Run haddock for internal modules and include all symbols"- haddockInternal (\v flags -> flags { haddockInternal = v })- trueArg-- ,option "" ["css"]- "Use PATH as the haddock stylesheet"- haddockCss (\v flags -> flags { haddockCss = v })- (reqArgFlag "PATH")-- ,option "" ["hyperlink-source","hyperlink-sources","hyperlinked-source"]- "Hyperlink the documentation to the source code"- haddockLinkedSource (\v flags -> flags { haddockLinkedSource = v })- trueArg-- ,option "" ["quickjump"]- "Generate an index for interactive documentation navigation"- haddockQuickJump (\v flags -> flags { haddockQuickJump = v })- trueArg-- ,option "" ["hscolour-css"]- "Use PATH as the HsColour stylesheet"- haddockHscolourCss (\v flags -> flags { haddockHscolourCss = v })- (reqArgFlag "PATH")-- ,option "" ["contents-location"]- "Bake URL in as the location for the contents page"- haddockContents (\v flags -> flags { haddockContents = v })- (reqArg' "URL"- (toFlag . toPathTemplate)- (flagToList . fmap fromPathTemplate))- ]--emptyHaddockFlags :: HaddockFlags-emptyHaddockFlags = mempty--instance Monoid HaddockFlags where- mempty = gmempty- mappend = (<>)--instance Semigroup HaddockFlags where- (<>) = gmappend---- --------------------------------------------------------------- * Clean flags--- --------------------------------------------------------------data CleanFlags = CleanFlags {- cleanSaveConf :: Flag Bool,- cleanDistPref :: Flag FilePath,- cleanVerbosity :: Flag Verbosity,- cleanCabalFilePath :: Flag FilePath- }- deriving (Show, Generic, Typeable)--defaultCleanFlags :: CleanFlags-defaultCleanFlags = CleanFlags {- cleanSaveConf = Flag False,- cleanDistPref = NoFlag,- cleanVerbosity = Flag normal,- cleanCabalFilePath = mempty- }--cleanCommand :: CommandUI CleanFlags-cleanCommand = CommandUI- { commandName = "clean"- , commandSynopsis = "Clean up after a build."- , commandDescription = Just $ \_ ->- "Removes .hi, .o, preprocessed sources, etc.\n"- , commandNotes = Nothing- , commandUsage = \pname ->- "Usage: " ++ pname ++ " clean [FLAGS]\n"- , commandDefaultFlags = defaultCleanFlags- , commandOptions = \showOrParseArgs ->- [optionVerbosity cleanVerbosity (\v flags -> flags { cleanVerbosity = v })- ,optionDistPref- cleanDistPref (\d flags -> flags { cleanDistPref = d })- showOrParseArgs-- ,option "s" ["save-configure"]- "Do not remove the configuration file (dist/setup-config) during cleaning. Saves need to reconfigure."- cleanSaveConf (\v flags -> flags { cleanSaveConf = v })- trueArg- ]- }--emptyCleanFlags :: CleanFlags-emptyCleanFlags = mempty--instance Monoid CleanFlags where- mempty = gmempty- mappend = (<>)--instance Semigroup CleanFlags where- (<>) = gmappend---- --------------------------------------------------------------- * Build flags--- --------------------------------------------------------------data BuildFlags = BuildFlags {- buildProgramPaths :: [(String, FilePath)],- buildProgramArgs :: [(String, [String])],- buildDistPref :: Flag FilePath,- buildVerbosity :: Flag Verbosity,- buildNumJobs :: Flag (Maybe Int),- -- TODO: this one should not be here, it's just that the silly- -- UserHooks stop us from passing extra info in other ways- buildArgs :: [String],- buildCabalFilePath :: Flag FilePath- }- deriving (Read, Show, Generic, Typeable)--defaultBuildFlags :: BuildFlags-defaultBuildFlags = BuildFlags {- buildProgramPaths = mempty,- buildProgramArgs = [],- buildDistPref = mempty,- buildVerbosity = Flag normal,- buildNumJobs = mempty,- buildArgs = [],- buildCabalFilePath = mempty- }--buildCommand :: ProgramDb -> CommandUI BuildFlags-buildCommand progDb = CommandUI- { commandName = "build"- , commandSynopsis = "Compile all/specific components."- , commandDescription = Just $ \_ -> wrapText $- "Components encompass executables, tests, and benchmarks.\n"- ++ "\n"- ++ "Affected by configuration options, see `configure`.\n"- , commandNotes = Just $ \pname ->- "Examples:\n"- ++ " " ++ pname ++ " build "- ++ " All the components in the package\n"- ++ " " ++ pname ++ " build foo "- ++ " A component (i.e. lib, exe, test suite)\n\n"- ++ programFlagsDescription progDb---TODO: re-enable once we have support for module/file targets--- ++ " " ++ pname ++ " build Foo.Bar "--- ++ " A module\n"--- ++ " " ++ pname ++ " build Foo/Bar.hs"--- ++ " A file\n\n"--- ++ "If a target is ambiguous it can be qualified with the component "--- ++ "name, e.g.\n"--- ++ " " ++ pname ++ " build foo:Foo.Bar\n"--- ++ " " ++ pname ++ " build testsuite1:Foo/Bar.hs\n"- , commandUsage = usageAlternatives "build" $- [ "[FLAGS]"- , "COMPONENTS [FLAGS]"- ]- , commandDefaultFlags = defaultBuildFlags- , commandOptions = \showOrParseArgs ->- [ optionVerbosity- buildVerbosity (\v flags -> flags { buildVerbosity = v })-- , optionDistPref- buildDistPref (\d flags -> flags { buildDistPref = d }) showOrParseArgs- ]- ++ buildOptions progDb showOrParseArgs- }--buildOptions :: ProgramDb -> ShowOrParseArgs- -> [OptionField BuildFlags]-buildOptions progDb showOrParseArgs =- [ optionNumJobs- buildNumJobs (\v flags -> flags { buildNumJobs = v })- ]-- ++ programDbPaths progDb showOrParseArgs- buildProgramPaths (\v flags -> flags { buildProgramPaths = v})-- ++ programDbOption progDb showOrParseArgs- buildProgramArgs (\v fs -> fs { buildProgramArgs = v })-- ++ programDbOptions progDb showOrParseArgs- buildProgramArgs (\v flags -> flags { buildProgramArgs = v})--emptyBuildFlags :: BuildFlags-emptyBuildFlags = mempty--instance Monoid BuildFlags where- mempty = gmempty- mappend = (<>)--instance Semigroup BuildFlags where- (<>) = gmappend---- --------------------------------------------------------------- * REPL Flags--- --------------------------------------------------------------data ReplFlags = ReplFlags {- replProgramPaths :: [(String, FilePath)],- replProgramArgs :: [(String, [String])],- replDistPref :: Flag FilePath,- replVerbosity :: Flag Verbosity,- replReload :: Flag Bool,- replReplOptions :: [String]- }- deriving (Show, Generic, Typeable)--defaultReplFlags :: ReplFlags-defaultReplFlags = ReplFlags {- replProgramPaths = mempty,- replProgramArgs = [],- replDistPref = NoFlag,- replVerbosity = Flag normal,- replReload = Flag False,- replReplOptions = []- }--instance Monoid ReplFlags where- mempty = gmempty- mappend = (<>)--instance Semigroup ReplFlags where- (<>) = gmappend--replCommand :: ProgramDb -> CommandUI ReplFlags-replCommand progDb = CommandUI- { commandName = "repl"- , commandSynopsis =- "Open an interpreter session for the given component."- , commandDescription = Just $ \pname -> wrapText $- "If the current directory contains no package, ignores COMPONENT "- ++ "parameters and opens an interactive interpreter session; if a "- ++ "sandbox is present, its package database will be used.\n"- ++ "\n"- ++ "Otherwise, (re)configures with the given or default flags, and "- ++ "loads the interpreter with the relevant modules. For executables, "- ++ "tests and benchmarks, loads the main module (and its "- ++ "dependencies); for libraries all exposed/other modules.\n"- ++ "\n"- ++ "The default component is the library itself, or the executable "- ++ "if that is the only component.\n"- ++ "\n"- ++ "Support for loading specific modules is planned but not "- ++ "implemented yet. For certain scenarios, `" ++ pname- ++ " exec -- ghci :l Foo` may be used instead. Note that `exec` will "- ++ "not (re)configure and you will have to specify the location of "- ++ "other modules, if required.\n"-- , commandNotes = Just $ \pname ->- "Examples:\n"- ++ " " ++ pname ++ " repl "- ++ " The first component in the package\n"- ++ " " ++ pname ++ " repl foo "- ++ " A named component (i.e. lib, exe, test suite)\n"- ++ " " ++ pname ++ " repl --repl-options=\"-lstdc++\""- ++ " Specifying flags for interpreter\n"---TODO: re-enable once we have support for module/file targets--- ++ " " ++ pname ++ " repl Foo.Bar "--- ++ " A module\n"--- ++ " " ++ pname ++ " repl Foo/Bar.hs"--- ++ " A file\n\n"--- ++ "If a target is ambiguous it can be qualified with the component "--- ++ "name, e.g.\n"--- ++ " " ++ pname ++ " repl foo:Foo.Bar\n"--- ++ " " ++ pname ++ " repl testsuite1:Foo/Bar.hs\n"- , commandUsage = \pname -> "Usage: " ++ pname ++ " repl [COMPONENT] [FLAGS]\n"- , commandDefaultFlags = defaultReplFlags- , commandOptions = \showOrParseArgs ->- optionVerbosity replVerbosity (\v flags -> flags { replVerbosity = v })- : optionDistPref- replDistPref (\d flags -> flags { replDistPref = d })- showOrParseArgs-- : programDbPaths progDb showOrParseArgs- replProgramPaths (\v flags -> flags { replProgramPaths = v})-- ++ programDbOption progDb showOrParseArgs- replProgramArgs (\v flags -> flags { replProgramArgs = v})-- ++ programDbOptions progDb showOrParseArgs- replProgramArgs (\v flags -> flags { replProgramArgs = v})-- ++ case showOrParseArgs of- ParseArgs ->- [ option "" ["reload"]- "Used from within an interpreter to update files."- replReload (\v flags -> flags { replReload = v })- trueArg- ]- _ -> []- ++ map liftReplOption (replOptions showOrParseArgs)- }- where- liftReplOption = liftOption replReplOptions (\v flags -> flags { replReplOptions = v })--replOptions :: ShowOrParseArgs -> [OptionField [String]]-replOptions _ = [ option [] ["repl-options"] "use this option for the repl" id- const (reqArg "FLAG" (succeedReadE (:[])) id) ]---- --------------------------------------------------------------- * Test flags--- --------------------------------------------------------------data TestShowDetails = Never | Failures | Always | Streaming | Direct- deriving (Eq, Ord, Enum, Bounded, Generic, Show, Typeable)--instance Binary TestShowDetails-instance Structured TestShowDetails--knownTestShowDetails :: [TestShowDetails]-knownTestShowDetails = [minBound..maxBound]--instance Pretty TestShowDetails where- pretty = Disp.text . lowercase . show--instance Parsec TestShowDetails where- parsec = maybe (fail "invalid TestShowDetails") return . classify =<< ident- where- ident = P.munch1 (\c -> isAlpha c || c == '_' || c == '-')- classify str = lookup (lowercase str) enumMap- enumMap :: [(String, TestShowDetails)]- enumMap = [ (prettyShow x, x)- | x <- knownTestShowDetails ]----TODO: do we need this instance?-instance Monoid TestShowDetails where- mempty = Never- mappend = (<>)--instance Semigroup TestShowDetails where- a <> b = if a < b then b else a--data TestFlags = TestFlags {- testDistPref :: Flag FilePath,- testVerbosity :: Flag Verbosity,- testHumanLog :: Flag PathTemplate,- testMachineLog :: Flag PathTemplate,- testShowDetails :: Flag TestShowDetails,- testKeepTix :: Flag Bool,- testWrapper :: Flag FilePath,- testFailWhenNoTestSuites :: Flag Bool,- -- TODO: think about if/how options are passed to test exes- testOptions :: [PathTemplate]- } deriving (Show, Generic, Typeable)--defaultTestFlags :: TestFlags-defaultTestFlags = TestFlags {- testDistPref = NoFlag,- testVerbosity = Flag normal,- testHumanLog = toFlag $ toPathTemplate $ "$pkgid-$test-suite.log",- testMachineLog = toFlag $ toPathTemplate $ "$pkgid.log",- testShowDetails = toFlag Failures,- testKeepTix = toFlag False,- testWrapper = NoFlag,- testFailWhenNoTestSuites = toFlag False,- testOptions = []- }--testCommand :: CommandUI TestFlags-testCommand = CommandUI- { commandName = "test"- , commandSynopsis =- "Run all/specific tests in the test suite."- , commandDescription = Just $ \pname -> wrapText $- "If necessary (re)configures with `--enable-tests` flag and builds"- ++ " the test suite.\n"- ++ "\n"- ++ "Remember that the tests' dependencies must be installed if there"- ++ " are additional ones; e.g. with `" ++ pname- ++ " install --only-dependencies --enable-tests`.\n"- ++ "\n"- ++ "By defining UserHooks in a custom Setup.hs, the package can"- ++ " define actions to be executed before and after running tests.\n"- , commandNotes = Nothing- , commandUsage = usageAlternatives "test"- [ "[FLAGS]"- , "TESTCOMPONENTS [FLAGS]"- ]- , commandDefaultFlags = defaultTestFlags- , commandOptions = testOptions'- }--testOptions' :: ShowOrParseArgs -> [OptionField TestFlags]-testOptions' showOrParseArgs =- [ optionVerbosity testVerbosity (\v flags -> flags { testVerbosity = v })- , optionDistPref- testDistPref (\d flags -> flags { testDistPref = d })- showOrParseArgs- , option [] ["log"]- ("Log all test suite results to file (name template can use "- ++ "$pkgid, $compiler, $os, $arch, $test-suite, $result)")- testHumanLog (\v flags -> flags { testHumanLog = v })- (reqArg' "TEMPLATE"- (toFlag . toPathTemplate)- (flagToList . fmap fromPathTemplate))- , option [] ["machine-log"]- ("Produce a machine-readable log file (name template can use "- ++ "$pkgid, $compiler, $os, $arch, $result)")- testMachineLog (\v flags -> flags { testMachineLog = v })- (reqArg' "TEMPLATE"- (toFlag . toPathTemplate)- (flagToList . fmap fromPathTemplate))- , option [] ["show-details"]- ("'always': always show results of individual test cases. "- ++ "'never': never show results of individual test cases. "- ++ "'failures': show results of failing test cases. "- ++ "'streaming': show results of test cases in real time."- ++ "'direct': send results of test cases in real time; no log file.")- testShowDetails (\v flags -> flags { testShowDetails = v })- (reqArg "FILTER"- (parsecToReadE (\_ -> "--show-details flag expects one of "- ++ intercalate ", "- (map prettyShow knownTestShowDetails))- (fmap toFlag parsec))- (flagToList . fmap prettyShow))- , option [] ["keep-tix-files"]- "keep .tix files for HPC between test runs"- testKeepTix (\v flags -> flags { testKeepTix = v})- trueArg- , option [] ["test-wrapper"]- "Run test through a wrapper."- testWrapper (\v flags -> flags { testWrapper = v })- (reqArg' "FILE" (toFlag :: FilePath -> Flag FilePath)- (flagToList :: Flag FilePath -> [FilePath]))- , option [] ["fail-when-no-test-suites"]- ("Exit with failure when no test suites are found.")- testFailWhenNoTestSuites (\v flags -> flags { testFailWhenNoTestSuites = v})- trueArg- , option [] ["test-options"]- ("give extra options to test executables "- ++ "(name templates can use $pkgid, $compiler, "- ++ "$os, $arch, $test-suite)")- testOptions (\v flags -> flags { testOptions = v })- (reqArg' "TEMPLATES" (map toPathTemplate . splitArgs)- (const []))- , option [] ["test-option"]- ("give extra option to test executables "- ++ "(no need to quote options containing spaces, "- ++ "name template can use $pkgid, $compiler, "- ++ "$os, $arch, $test-suite)")- testOptions (\v flags -> flags { testOptions = v })- (reqArg' "TEMPLATE" (\x -> [toPathTemplate x])- (map fromPathTemplate))- ]--emptyTestFlags :: TestFlags-emptyTestFlags = mempty--instance Monoid TestFlags where- mempty = gmempty- mappend = (<>)--instance Semigroup TestFlags where- (<>) = gmappend---- --------------------------------------------------------------- * Benchmark flags--- --------------------------------------------------------------data BenchmarkFlags = BenchmarkFlags {- benchmarkDistPref :: Flag FilePath,- benchmarkVerbosity :: Flag Verbosity,- benchmarkOptions :: [PathTemplate]- } deriving (Show, Generic, Typeable)--defaultBenchmarkFlags :: BenchmarkFlags-defaultBenchmarkFlags = BenchmarkFlags {- benchmarkDistPref = NoFlag,- benchmarkVerbosity = Flag normal,- benchmarkOptions = []- }--benchmarkCommand :: CommandUI BenchmarkFlags-benchmarkCommand = CommandUI- { commandName = "bench"- , commandSynopsis =- "Run all/specific benchmarks."- , commandDescription = Just $ \pname -> wrapText $- "If necessary (re)configures with `--enable-benchmarks` flag and"- ++ " builds the benchmarks.\n"- ++ "\n"- ++ "Remember that the benchmarks' dependencies must be installed if"- ++ " there are additional ones; e.g. with `" ++ pname- ++ " install --only-dependencies --enable-benchmarks`.\n"- ++ "\n"- ++ "By defining UserHooks in a custom Setup.hs, the package can"- ++ " define actions to be executed before and after running"- ++ " benchmarks.\n"- , commandNotes = Nothing- , commandUsage = usageAlternatives "bench"- [ "[FLAGS]"- , "BENCHCOMPONENTS [FLAGS]"- ]- , commandDefaultFlags = defaultBenchmarkFlags- , commandOptions = benchmarkOptions'- }--benchmarkOptions' :: ShowOrParseArgs -> [OptionField BenchmarkFlags]-benchmarkOptions' showOrParseArgs =- [ optionVerbosity benchmarkVerbosity- (\v flags -> flags { benchmarkVerbosity = v })- , optionDistPref- benchmarkDistPref (\d flags -> flags { benchmarkDistPref = d })- showOrParseArgs- , option [] ["benchmark-options"]- ("give extra options to benchmark executables "- ++ "(name templates can use $pkgid, $compiler, "- ++ "$os, $arch, $benchmark)")- benchmarkOptions (\v flags -> flags { benchmarkOptions = v })- (reqArg' "TEMPLATES" (map toPathTemplate . splitArgs)- (const []))- , option [] ["benchmark-option"]- ("give extra option to benchmark executables "- ++ "(no need to quote options containing spaces, "- ++ "name template can use $pkgid, $compiler, "- ++ "$os, $arch, $benchmark)")- benchmarkOptions (\v flags -> flags { benchmarkOptions = v })- (reqArg' "TEMPLATE" (\x -> [toPathTemplate x])- (map fromPathTemplate))- ]--emptyBenchmarkFlags :: BenchmarkFlags-emptyBenchmarkFlags = mempty--instance Monoid BenchmarkFlags where- mempty = gmempty- mappend = (<>)--instance Semigroup BenchmarkFlags where- (<>) = gmappend---- --------------------------------------------------------------- * Shared options utils--- --------------------------------------------------------------programFlagsDescription :: ProgramDb -> String-programFlagsDescription progDb =- "The flags --with-PROG and --PROG-option(s) can be used with"- ++ " the following programs:"- ++ (concatMap (\line -> "\n " ++ unwords line) . wrapLine 77 . sort)- [ programName prog | (prog, _) <- knownPrograms progDb ]- ++ "\n"---- | For each known program @PROG@ in 'progDb', produce a @with-PROG@--- 'OptionField'.-programDbPaths- :: ProgramDb- -> ShowOrParseArgs- -> (flags -> [(String, FilePath)])- -> ([(String, FilePath)] -> (flags -> flags))- -> [OptionField flags]-programDbPaths progDb showOrParseArgs get set =- programDbPaths' ("with-" ++) progDb showOrParseArgs get set---- | Like 'programDbPaths', but allows to customise the option name.-programDbPaths'- :: (String -> String)- -> ProgramDb- -> ShowOrParseArgs- -> (flags -> [(String, FilePath)])- -> ([(String, FilePath)] -> (flags -> flags))- -> [OptionField flags]-programDbPaths' mkName progDb showOrParseArgs get set =- case showOrParseArgs of- -- we don't want a verbose help text list so we just show a generic one:- ShowArgs -> [withProgramPath "PROG"]- ParseArgs -> map (withProgramPath . programName . fst)- (knownPrograms progDb)- where- withProgramPath prog =- option "" [mkName prog]- ("give the path to " ++ prog)- get set- (reqArg' "PATH" (\path -> [(prog, path)])- (\progPaths -> [ path | (prog', path) <- progPaths, prog==prog' ]))---- | For each known program @PROG@ in 'progDb', produce a @PROG-option@--- 'OptionField'.-programDbOption- :: ProgramDb- -> ShowOrParseArgs- -> (flags -> [(String, [String])])- -> ([(String, [String])] -> (flags -> flags))- -> [OptionField flags]-programDbOption progDb showOrParseArgs get set =- case showOrParseArgs of- -- we don't want a verbose help text list so we just show a generic one:- ShowArgs -> [programOption "PROG"]- ParseArgs -> map (programOption . programName . fst)- (knownPrograms progDb)- where- programOption prog =- option "" [prog ++ "-option"]- ("give an extra option to " ++ prog ++- " (no need to quote options containing spaces)")- get set- (reqArg' "OPT" (\arg -> [(prog, [arg])])- (\progArgs -> concat [ args- | (prog', args) <- progArgs, prog==prog' ]))----- | For each known program @PROG@ in 'progDb', produce a @PROG-options@--- 'OptionField'.-programDbOptions- :: ProgramDb- -> ShowOrParseArgs- -> (flags -> [(String, [String])])- -> ([(String, [String])] -> (flags -> flags))- -> [OptionField flags]-programDbOptions progDb showOrParseArgs get set =- case showOrParseArgs of- -- we don't want a verbose help text list so we just show a generic one:- ShowArgs -> [programOptions "PROG"]- ParseArgs -> map (programOptions . programName . fst)- (knownPrograms progDb)- where- programOptions prog =- option "" [prog ++ "-options"]- ("give extra options to " ++ prog)- get set- (reqArg' "OPTS" (\args -> [(prog, splitArgs args)]) (const []))---- --------------------------------------------------------------- * GetOpt Utils--- --------------------------------------------------------------boolOpt :: SFlags -> SFlags- -> MkOptDescr (a -> Flag Bool) (Flag Bool -> a -> a) a-boolOpt = Command.boolOpt flagToMaybe Flag--boolOpt' :: OptFlags -> OptFlags- -> MkOptDescr (a -> Flag Bool) (Flag Bool -> a -> a) a-boolOpt' = Command.boolOpt' flagToMaybe Flag--trueArg, falseArg :: MkOptDescr (a -> Flag Bool) (Flag Bool -> a -> a) a-trueArg sfT lfT = boolOpt' (sfT, lfT) ([], []) sfT lfT-falseArg sfF lfF = boolOpt' ([], []) (sfF, lfF) sfF lfF--reqArgFlag :: ArgPlaceHolder -> SFlags -> LFlags -> Description ->- (b -> Flag String) -> (Flag String -> b -> b) -> OptDescr b-reqArgFlag ad = reqArg ad (succeedReadE Flag) flagToList--optionDistPref :: (flags -> Flag FilePath)- -> (Flag FilePath -> flags -> flags)- -> ShowOrParseArgs- -> OptionField flags-optionDistPref get set = \showOrParseArgs ->- option "" (distPrefFlagName showOrParseArgs)- ( "The directory where Cabal puts generated build files "- ++ "(default " ++ defaultDistPref ++ ")")- get set- (reqArgFlag "DIR")- where- distPrefFlagName ShowArgs = ["builddir"]- distPrefFlagName ParseArgs = ["builddir", "distdir", "distpref"]--optionVerbosity :: (flags -> Flag Verbosity)- -> (Flag Verbosity -> flags -> flags)- -> OptionField flags-optionVerbosity get set =- option "v" ["verbose"]- "Control verbosity (n is 0--3, default verbosity level is 1)"- get set- (optArg "n" (fmap Flag flagToVerbosity)- (Flag verbose) -- default Value if no n is given- (fmap (Just . showForCabal) . flagToList))--optionNumJobs :: (flags -> Flag (Maybe Int))- -> (Flag (Maybe Int) -> flags -> flags)- -> OptionField flags-optionNumJobs get set =- option "j" ["jobs"]- "Run NUM jobs simultaneously (or '$ncpus' if no NUM is given)."- get set- (optArg "NUM" (fmap Flag numJobsParser)- (Flag Nothing)- (map (Just . maybe "$ncpus" show) . flagToList))- where- numJobsParser :: ReadE (Maybe Int)- numJobsParser = ReadE $ \s ->- case s of- "$ncpus" -> Right Nothing- _ -> case reads s of- [(n, "")]- | n < 1 -> Left "The number of jobs should be 1 or more."- | otherwise -> Right (Just n)- _ -> Left "The jobs value should be a number or '$ncpus'"----- --------------------------------------------------------------- * show-build-info command flags--- --------------------------------------------------------------data ShowBuildInfoFlags = ShowBuildInfoFlags- { buildInfoBuildFlags :: BuildFlags- , buildInfoOutputFile :: Maybe FilePath- } deriving (Show, Typeable)--defaultShowBuildFlags :: ShowBuildInfoFlags-defaultShowBuildFlags =- ShowBuildInfoFlags- { buildInfoBuildFlags = defaultBuildFlags- , buildInfoOutputFile = Nothing- }--showBuildInfoCommand :: ProgramDb -> CommandUI ShowBuildInfoFlags-showBuildInfoCommand progDb = CommandUI- { commandName = "show-build-info"- , commandSynopsis = "Emit details about how a package would be built."- , commandDescription = Just $ \_ -> wrapText $- "Components encompass executables, tests, and benchmarks.\n"- ++ "\n"- ++ "Affected by configuration options, see `configure`.\n"- , commandNotes = Just $ \pname ->- "Examples:\n"- ++ " " ++ pname ++ " show-build-info "- ++ " All the components in the package\n"- ++ " " ++ pname ++ " show-build-info foo "- ++ " A component (i.e. lib, exe, test suite)\n\n"- ++ programFlagsDescription progDb---TODO: re-enable once we have support for module/file targets--- ++ " " ++ pname ++ " show-build-info Foo.Bar "--- ++ " A module\n"--- ++ " " ++ pname ++ " show-build-info Foo/Bar.hs"--- ++ " A file\n\n"--- ++ "If a target is ambiguous it can be qualified with the component "--- ++ "name, e.g.\n"--- ++ " " ++ pname ++ " show-build-info foo:Foo.Bar\n"--- ++ " " ++ pname ++ " show-build-info testsuite1:Foo/Bar.hs\n"- , commandUsage = usageAlternatives "show-build-info" $- [ "[FLAGS]"- , "COMPONENTS [FLAGS]"- ]- , commandDefaultFlags = defaultShowBuildFlags- , commandOptions = \showOrParseArgs ->- parseBuildFlagsForShowBuildInfoFlags showOrParseArgs progDb- ++- [ option [] ["buildinfo-json-output"]- "Write the result to the given file instead of stdout"- buildInfoOutputFile (\pf flags -> flags { buildInfoOutputFile = pf })- (reqArg' "FILE" Just (maybe [] pure))- ]-- }--parseBuildFlagsForShowBuildInfoFlags :: ShowOrParseArgs -> ProgramDb -> [OptionField ShowBuildInfoFlags]-parseBuildFlagsForShowBuildInfoFlags showOrParseArgs progDb =- map- (liftOption- buildInfoBuildFlags- (\bf flags -> flags { buildInfoBuildFlags = bf } )- )- buildFlags- where- buildFlags = buildOptions progDb showOrParseArgs- ++- [ optionVerbosity- buildVerbosity (\v flags -> flags { buildVerbosity = v })-- , optionDistPref- buildDistPref (\d flags -> flags { buildDistPref = d }) showOrParseArgs- ]---- --------------------------------------------------------------- * Other Utils--- ---------------------------------------------------------------- | Arguments to pass to a @configure@ script, e.g. generated by--- @autoconf@.-configureArgs :: Bool -> ConfigFlags -> [String]-configureArgs bcHack flags- = hc_flag- ++ optFlag "with-hc-pkg" configHcPkg- ++ optFlag' "prefix" prefix- ++ optFlag' "bindir" bindir- ++ optFlag' "libdir" libdir- ++ optFlag' "libexecdir" libexecdir- ++ optFlag' "datadir" datadir- ++ optFlag' "sysconfdir" sysconfdir- ++ configConfigureArgs flags- where- hc_flag = case (configHcFlavor flags, configHcPath flags) of- (_, Flag hc_path) -> [hc_flag_name ++ hc_path]- (Flag hc, NoFlag) -> [hc_flag_name ++ prettyShow hc]- (NoFlag,NoFlag) -> []- hc_flag_name- --TODO kill off thic bc hack when defaultUserHooks is removed.- | bcHack = "--with-hc="- | otherwise = "--with-compiler="- optFlag name config_field = case config_field flags of- Flag p -> ["--" ++ name ++ "=" ++ p]- NoFlag -> []- optFlag' name config_field = optFlag name (fmap fromPathTemplate- . config_field- . configInstallDirs)--configureCCompiler :: Verbosity -> ProgramDb- -> IO (FilePath, [String])-configureCCompiler verbosity progdb = configureProg verbosity progdb gccProgram--configureLinker :: Verbosity -> ProgramDb -> IO (FilePath, [String])-configureLinker verbosity progdb = configureProg verbosity progdb ldProgram--configureProg :: Verbosity -> ProgramDb -> Program- -> IO (FilePath, [String])-configureProg verbosity programDb prog = do- (p, _) <- requireProgram verbosity prog programDb- let pInv = programInvocation p []- return (progInvokePath pInv, progInvokeArgs pInv)---- | Helper function to split a string into a list of arguments.--- It's supposed to handle quoted things sensibly, eg:------ > splitArgs "--foo=\"C:/Program Files/Bar/" --baz"--- > = ["--foo=C:/Program Files/Bar", "--baz"]------ > splitArgs "\"-DMSGSTR=\\\"foo bar\\\"\" --baz"--- > = ["-DMSGSTR=\"foo bar\"","--baz"]----splitArgs :: String -> [String]-splitArgs = space []- where- space :: String -> String -> [String]- space w [] = word w []- space w ( c :s)- | isSpace c = word w (space [] s)- space w ('"':s) = string w s- space w s = nonstring w s-- string :: String -> String -> [String]- string w [] = word w []- string w ('"':s) = space w s- string w ('\\':'"':s) = string ('"':w) s- string w ( c :s) = string (c:w) s-- nonstring :: String -> String -> [String]- nonstring w [] = word w []- nonstring w ('"':s) = string w s- nonstring w ( c :s) = space (c:w) s-- word [] s = s- word w s = reverse w : s---- The test cases kinda have to be rewritten from the ground up... :/---hunitTests :: [Test]---hunitTests =--- let m = [("ghc", GHC), ("nhc98", NHC), ("hugs", Hugs)]--- (flags, commands', unkFlags, ers)--- = getOpt Permute options ["configure", "foobar", "--prefix=/foo", "--ghc", "--nhc98", "--hugs", "--with-compiler=/comp", "--unknown1", "--unknown2", "--install-prefix=/foo", "--user", "--global"]--- in [TestLabel "very basic option parsing" $ TestList [--- "getOpt flags" ~: "failed" ~:--- [Prefix "/foo", GhcFlag, NhcFlag, HugsFlag,--- WithCompiler "/comp", InstPrefix "/foo", UserFlag, GlobalFlag]--- ~=? flags,--- "getOpt commands" ~: "failed" ~: ["configure", "foobar"] ~=? commands',--- "getOpt unknown opts" ~: "failed" ~:--- ["--unknown1", "--unknown2"] ~=? unkFlags,--- "getOpt errors" ~: "failed" ~: [] ~=? ers],------ TestLabel "test location of various compilers" $ TestList--- ["configure parsing for prefix and compiler flag" ~: "failed" ~:--- (Right (ConfigCmd (Just comp, Nothing, Just "/usr/local"), []))--- ~=? (parseArgs ["--prefix=/usr/local", "--"++name, "configure"])--- | (name, comp) <- m],------ TestLabel "find the package tool" $ TestList--- ["configure parsing for prefix comp flag, withcompiler" ~: "failed" ~:--- (Right (ConfigCmd (Just comp, Just "/foo/comp", Just "/usr/local"), []))--- ~=? (parseArgs ["--prefix=/usr/local", "--"++name,--- "--with-compiler=/foo/comp", "configure"])--- | (name, comp) <- m],------ TestLabel "simpler commands" $ TestList--- [flag ~: "failed" ~: (Right (flagCmd, [])) ~=? (parseArgs [flag])--- | (flag, flagCmd) <- [("build", BuildCmd),--- ("install", InstallCmd Nothing False),--- ("sdist", SDistCmd),--- ("register", RegisterCmd False)]--- ]--- ]--{- Testing ideas:- * IO to look for hugs and hugs-pkg (which hugs, etc)- * quickCheck to test permutations of arguments- * what other options can we over-ride with a command-line flag?--}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RankNTypes #-}++-- |+-- Module : Distribution.Simple.Setup+-- Copyright : Isaac Jones 2003-2004+-- Duncan Coutts 2007+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- This module defines the command line interface for all the Cabal+-- commands. For each command (like @configure@, @build@ etc) it defines a type+-- that holds all the flags, the default set of flags and a 'CommandUI' that+-- maps command line flags to and from the corresponding flags type.+--+-- All the flags types are instances of 'Monoid', see+-- <http://www.haskell.org/pipermail/cabal-devel/2007-December/001509.html>+-- for an explanation.+--+-- The types defined here get used in the front end and especially in+-- @cabal-install@ which has to do quite a bit of manipulating sets of command+-- line flags.+--+-- This is actually relatively nice, it works quite well. The main change it+-- needs is to unify it with the code for managing sets of fields that can be+-- read and written from files. This would allow us to save configure flags in+-- config files.+module Distribution.Simple.Setup+ ( GlobalFlags (..)+ , emptyGlobalFlags+ , defaultGlobalFlags+ , globalCommand+ , CommonSetupFlags (..)+ , defaultCommonSetupFlags+ , commonSetupTempFileOptions+ , ConfigFlags (..)+ , emptyConfigFlags+ , defaultConfigFlags+ , configureCommand+ , configPrograms+ , readPackageDb+ , readPackageDbList+ , showPackageDb+ , showPackageDbList+ , CopyFlags (..)+ , emptyCopyFlags+ , defaultCopyFlags+ , copyCommand+ , InstallFlags (..)+ , emptyInstallFlags+ , defaultInstallFlags+ , installCommand+ , HaddockTarget (..)+ , HaddockFlags (..)+ , emptyHaddockFlags+ , defaultHaddockFlags+ , haddockCommand+ , Visibility (..)+ , HaddockProjectFlags (..)+ , emptyHaddockProjectFlags+ , defaultHaddockProjectFlags+ , haddockProjectCommand+ , HscolourFlags (..)+ , emptyHscolourFlags+ , defaultHscolourFlags+ , hscolourCommand+ , BuildFlags (..)+ , emptyBuildFlags+ , defaultBuildFlags+ , buildCommand+ , DumpBuildInfo (..)+ , ReplFlags (..)+ , defaultReplFlags+ , replCommand+ , ReplOptions (..)+ , CleanFlags (..)+ , emptyCleanFlags+ , defaultCleanFlags+ , cleanCommand+ , RegisterFlags (..)+ , emptyRegisterFlags+ , defaultRegisterFlags+ , registerCommand+ , unregisterCommand+ , SDistFlags (..)+ , emptySDistFlags+ , defaultSDistFlags+ , sdistCommand+ , TestFlags (..)+ , emptyTestFlags+ , defaultTestFlags+ , testCommand+ , TestShowDetails (..)+ , BenchmarkFlags (..)+ , emptyBenchmarkFlags+ , defaultBenchmarkFlags+ , benchmarkCommand+ , CopyDest (..)+ , configureArgs+ , configureOptions+ , configureCCompiler+ , configureLinker+ , buildOptions+ , haddockOptions+ , haddockProjectOptions+ , installDirsOptions+ , testOptions'+ , benchmarkOptions'+ , programDbOptions+ , programDbPaths'+ , programFlagsDescription+ , replOptions+ , splitArgs+ , defaultDistPref+ , optionDistPref+ , Flag+ , pattern Flag+ , pattern NoFlag+ , toFlag+ , fromFlag+ , fromFlagOrDefault+ , flagToMaybe+ , flagToList+ , maybeToFlag+ , BooleanFlag (..)+ , boolOpt+ , boolOpt'+ , trueArg+ , falseArg+ , optionVerbosity+ , BuildingWhat (..)+ , buildingWhatCommonFlags+ , buildingWhatVerbosity+ , buildingWhatWorkingDir+ , buildingWhatDistPref+ ) where++import Distribution.Compat.Prelude+import Prelude ()++import Distribution.Simple.Flag+import Distribution.Simple.InstallDirs+import Distribution.Types.DumpBuildInfo++import Distribution.Simple.Setup.Benchmark+import Distribution.Simple.Setup.Build+import Distribution.Simple.Setup.Clean+import Distribution.Simple.Setup.Common+import Distribution.Simple.Setup.Config+import Distribution.Simple.Setup.Copy+import Distribution.Simple.Setup.Global+import Distribution.Simple.Setup.Haddock+import Distribution.Simple.Setup.Hscolour+import Distribution.Simple.Setup.Install+import Distribution.Simple.Setup.Register+ ( RegisterFlags (..)+ , defaultRegisterFlags+ , emptyRegisterFlags+ , registerCommand+ , unregisterCommand+ )+import Distribution.Simple.Setup.Repl+import Distribution.Simple.Setup.SDist+import Distribution.Simple.Setup.Test+import Distribution.Utils.Path++import Distribution.Verbosity (Verbosity)++-- | What kind of build phase are we doing/hooking into?+--+-- Is this a normal build, or is it perhaps for running an interactive+-- session or Haddock?+data BuildingWhat+ = -- | A normal build.+ BuildNormal BuildFlags+ | -- | Build steps for an interactive session.+ BuildRepl ReplFlags+ | -- | Build steps for generating documentation.+ BuildHaddock HaddockFlags+ | -- | Build steps for Hscolour.+ BuildHscolour HscolourFlags+ deriving (Generic, Show)++buildingWhatCommonFlags :: BuildingWhat -> CommonSetupFlags+buildingWhatCommonFlags = \case+ BuildNormal flags -> buildCommonFlags flags+ BuildRepl flags -> replCommonFlags flags+ BuildHaddock flags -> haddockCommonFlags flags+ BuildHscolour flags -> hscolourCommonFlags flags++buildingWhatVerbosity :: BuildingWhat -> Verbosity+buildingWhatVerbosity = fromFlag . setupVerbosity . buildingWhatCommonFlags++buildingWhatWorkingDir :: BuildingWhat -> Maybe (SymbolicPath CWD (Dir Pkg))+buildingWhatWorkingDir = flagToMaybe . setupWorkingDir . buildingWhatCommonFlags++buildingWhatDistPref :: BuildingWhat -> SymbolicPath Pkg (Dir Dist)+buildingWhatDistPref = fromFlag . setupDistPref . buildingWhatCommonFlags++-- The test cases kinda have to be rewritten from the ground up... :/+-- hunitTests :: [Test]+-- hunitTests =+-- let m = [("ghc", GHC), ("nhc98", NHC), ("hugs", Hugs)]+-- (flags, commands', unkFlags, ers)+-- = getOpt Permute options ["configure", "foobar", "--prefix=/foo", "--ghc", "--nhc98", "--hugs", "--with-compiler=/comp", "--unknown1", "--unknown2", "--install-prefix=/foo", "--user", "--global"]+-- in [TestLabel "very basic option parsing" $ TestList [+-- "getOpt flags" ~: "failed" ~:+-- [Prefix "/foo", GhcFlag, NhcFlag, HugsFlag,+-- WithCompiler "/comp", InstPrefix "/foo", UserFlag, GlobalFlag]+-- ~=? flags,+-- "getOpt commands" ~: "failed" ~: ["configure", "foobar"] ~=? commands',+-- "getOpt unknown opts" ~: "failed" ~:+-- ["--unknown1", "--unknown2"] ~=? unkFlags,+-- "getOpt errors" ~: "failed" ~: [] ~=? ers],+--+-- TestLabel "test location of various compilers" $ TestList+-- ["configure parsing for prefix and compiler flag" ~: "failed" ~:+-- (Right (ConfigCmd (Just comp, Nothing, Just "/usr/local"), []))+-- ~=? (parseArgs ["--prefix=/usr/local", "--"++name, "configure"])+-- | (name, comp) <- m],+--+-- TestLabel "find the package tool" $ TestList+-- ["configure parsing for prefix comp flag, withcompiler" ~: "failed" ~:+-- (Right (ConfigCmd (Just comp, Just "/foo/comp", Just "/usr/local"), []))+-- ~=? (parseArgs ["--prefix=/usr/local", "--"++name,+-- "--with-compiler=/foo/comp", "configure"])+-- | (name, comp) <- m],+--+-- TestLabel "simpler commands" $ TestList+-- [flag ~: "failed" ~: (Right (flagCmd, [])) ~=? (parseArgs [flag])+-- | (flag, flagCmd) <- [("build", BuildCmd),+-- ("install", InstallCmd Nothing False),+-- ("sdist", SDistCmd),+-- ("register", RegisterCmd False)]+-- ]+-- ]++{- Testing ideas:+ * IO to look for hugs and hugs-pkg (which hugs, etc)+ * quickCheck to test permutations of arguments+ * what other options can we over-ride with a command-line flag?+-}++instance Binary BuildingWhat+instance Structured BuildingWhat
+ src/Distribution/Simple/Setup/Benchmark.hs view
@@ -0,0 +1,157 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ViewPatterns #-}++-- |+-- Module : Distribution.Simple.Benchmark+-- Copyright : Isaac Jones 2003-2004+-- Duncan Coutts 2007+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Definition of the benchmarking command-line options.+-- See: @Distribution.Simple.Setup@+module Distribution.Simple.Setup.Benchmark+ ( BenchmarkFlags+ ( BenchmarkCommonFlags+ , benchmarkVerbosity+ , benchmarkDistPref+ , benchmarkCabalFilePath+ , benchmarkWorkingDir+ , benchmarkTargets+ , ..+ )+ , emptyBenchmarkFlags+ , defaultBenchmarkFlags+ , benchmarkCommand+ , benchmarkOptions'+ ) where++import Distribution.Compat.Prelude hiding (get)+import Prelude ()++import Distribution.Simple.Command hiding (boolOpt, boolOpt')+import Distribution.Simple.InstallDirs+import Distribution.Simple.Setup.Common+import Distribution.Simple.Utils+import Distribution.Utils.Path+import Distribution.Verbosity++-- ------------------------------------------------------------++-- * Benchmark flags++-- ------------------------------------------------------------++data BenchmarkFlags = BenchmarkFlags+ { benchmarkCommonFlags :: !CommonSetupFlags+ , benchmarkOptions :: [PathTemplate]+ }+ deriving (Show, Generic)++pattern BenchmarkCommonFlags+ :: Flag Verbosity+ -> Flag (SymbolicPath Pkg (Dir Dist))+ -> Flag (SymbolicPath CWD (Dir Pkg))+ -> Flag (SymbolicPath Pkg File)+ -> [String]+ -> BenchmarkFlags+pattern BenchmarkCommonFlags+ { benchmarkVerbosity+ , benchmarkDistPref+ , benchmarkWorkingDir+ , benchmarkCabalFilePath+ , benchmarkTargets+ } <-+ ( benchmarkCommonFlags ->+ CommonSetupFlags+ { setupVerbosity = benchmarkVerbosity+ , setupDistPref = benchmarkDistPref+ , setupWorkingDir = benchmarkWorkingDir+ , setupCabalFilePath = benchmarkCabalFilePath+ , setupTargets = benchmarkTargets+ }+ )++instance Binary BenchmarkFlags+instance Structured BenchmarkFlags++defaultBenchmarkFlags :: BenchmarkFlags+defaultBenchmarkFlags =+ BenchmarkFlags+ { benchmarkCommonFlags = defaultCommonSetupFlags+ , benchmarkOptions = []+ }++benchmarkCommand :: CommandUI BenchmarkFlags+benchmarkCommand =+ CommandUI+ { commandName = "bench"+ , commandSynopsis =+ "Run all/specific benchmarks."+ , commandDescription = Just $ \_pname ->+ wrapText $+ testOrBenchmarkHelpText "benchmark"+ , commandNotes = Nothing+ , commandUsage =+ usageAlternatives+ "bench"+ [ "[FLAGS]"+ , "BENCHCOMPONENTS [FLAGS]"+ ]+ , commandDefaultFlags = defaultBenchmarkFlags+ , commandOptions = benchmarkOptions'+ }++benchmarkOptions' :: ShowOrParseArgs -> [OptionField BenchmarkFlags]+benchmarkOptions' showOrParseArgs =+ withCommonSetupOptions+ benchmarkCommonFlags+ (\c f -> f{benchmarkCommonFlags = c})+ showOrParseArgs+ [ option+ []+ ["benchmark-options"]+ ( "give extra options to benchmark executables "+ ++ "(split on spaces, use \"\" to prevent splitting; "+ ++ "name templates can use $pkgid, $compiler, "+ ++ "$os, $arch, $benchmark)"+ )+ benchmarkOptions+ (\v flags -> flags{benchmarkOptions = v})+ ( reqArg'+ "TEMPLATES"+ (map toPathTemplate . splitArgs)+ (const [])+ )+ , option+ []+ ["benchmark-option"]+ ( "give extra option to benchmark executables "+ ++ "(passed directly as a single argument; "+ ++ "name template can use $pkgid, $compiler, "+ ++ "$os, $arch, $benchmark)"+ )+ benchmarkOptions+ (\v flags -> flags{benchmarkOptions = v})+ ( reqArg'+ "TEMPLATE"+ (\x -> [toPathTemplate x])+ (map fromPathTemplate)+ )+ ]++emptyBenchmarkFlags :: BenchmarkFlags+emptyBenchmarkFlags = mempty++instance Monoid BenchmarkFlags where+ mempty = gmempty+ mappend = (<>)++instance Semigroup BenchmarkFlags where+ (<>) = gmappend
+ src/Distribution/Simple/Setup/Build.hs view
@@ -0,0 +1,184 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ViewPatterns #-}++-- |+-- Module : Distribution.Simple.Setup.Build+-- Copyright : Isaac Jones 2003-2004+-- Duncan Coutts 2007+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Definition of the build command-line options.+-- See: @Distribution.Simple.Setup@+module Distribution.Simple.Setup.Build+ ( BuildFlags+ ( BuildCommonFlags+ , buildVerbosity+ , buildDistPref+ , buildCabalFilePath+ , buildWorkingDir+ , buildTargets+ , ..+ )+ , emptyBuildFlags+ , defaultBuildFlags+ , buildCommand+ , DumpBuildInfo (..)+ , buildOptions+ ) where++import Distribution.Compat.Prelude hiding (get)+import Prelude ()++import Distribution.Simple.Command hiding (boolOpt, boolOpt')+import Distribution.Simple.Flag+import Distribution.Simple.Program+import Distribution.Simple.Setup.Common+import Distribution.Simple.Utils+import Distribution.Types.DumpBuildInfo+import Distribution.Utils.Path+import Distribution.Verbosity++-- ------------------------------------------------------------++-- * Build flags++-- ------------------------------------------------------------++data BuildFlags = BuildFlags+ { buildCommonFlags :: !CommonSetupFlags+ , buildProgramPaths :: [(String, FilePath)]+ , buildProgramArgs :: [(String, [String])]+ , buildNumJobs :: Flag (Maybe Int)+ , buildUseSemaphore :: Flag String+ }+ deriving (Read, Show, Generic)++pattern BuildCommonFlags+ :: Flag Verbosity+ -> Flag (SymbolicPath Pkg (Dir Dist))+ -> Flag (SymbolicPath CWD (Dir Pkg))+ -> Flag (SymbolicPath Pkg File)+ -> [String]+ -> BuildFlags+pattern BuildCommonFlags+ { buildVerbosity+ , buildDistPref+ , buildWorkingDir+ , buildCabalFilePath+ , buildTargets+ } <-+ ( buildCommonFlags ->+ CommonSetupFlags+ { setupVerbosity = buildVerbosity+ , setupDistPref = buildDistPref+ , setupWorkingDir = buildWorkingDir+ , setupCabalFilePath = buildCabalFilePath+ , setupTargets = buildTargets+ }+ )++instance Binary BuildFlags+instance Structured BuildFlags++defaultBuildFlags :: BuildFlags+defaultBuildFlags =+ BuildFlags+ { buildCommonFlags = defaultCommonSetupFlags+ , buildProgramPaths = mempty+ , buildProgramArgs = []+ , buildNumJobs = mempty+ , buildUseSemaphore = NoFlag+ }++buildCommand :: ProgramDb -> CommandUI BuildFlags+buildCommand progDb =+ CommandUI+ { commandName = "build"+ , commandSynopsis = "Compile all/specific components."+ , commandDescription = Just $ \_ ->+ wrapText $+ "Components encompass executables, tests, and benchmarks.\n"+ ++ "\n"+ ++ "Affected by configuration options, see `configure`.\n"+ , commandNotes = Just $ \pname ->+ "Examples:\n"+ ++ " "+ ++ pname+ ++ " build "+ ++ " All the components in the package\n"+ ++ " "+ ++ pname+ ++ " build foo "+ ++ " A component (i.e. lib, exe, test suite)\n\n"+ ++ programFlagsDescription progDb+ , -- TODO: re-enable once we have support for module/file targets+ -- ++ " " ++ pname ++ " build Foo.Bar "+ -- ++ " A module\n"+ -- ++ " " ++ pname ++ " build Foo/Bar.hs"+ -- ++ " A file\n\n"+ -- ++ "If a target is ambiguous it can be qualified with the component "+ -- ++ "name, e.g.\n"+ -- ++ " " ++ pname ++ " build foo:Foo.Bar\n"+ -- ++ " " ++ pname ++ " build testsuite1:Foo/Bar.hs\n"+ commandUsage =+ usageAlternatives "build" $+ [ "[FLAGS]"+ , "COMPONENTS [FLAGS]"+ ]+ , commandDefaultFlags = defaultBuildFlags+ , commandOptions = buildOptions progDb+ }++buildOptions+ :: ProgramDb+ -> ShowOrParseArgs+ -> [OptionField BuildFlags]+buildOptions progDb showOrParseArgs =+ withCommonSetupOptions+ buildCommonFlags+ (\c f -> f{buildCommonFlags = c})+ showOrParseArgs+ ( [ optionNumJobs+ buildNumJobs+ (\v flags -> flags{buildNumJobs = v})+ , option+ []+ ["semaphore"]+ "semaphore"+ buildUseSemaphore+ (\v flags -> flags{buildUseSemaphore = v})+ (reqArg' "SEMAPHORE" Flag flagToList)+ ]+ )+ ++ programDbPaths+ progDb+ showOrParseArgs+ buildProgramPaths+ (\v flags -> flags{buildProgramPaths = v})+ ++ programDbOption+ progDb+ showOrParseArgs+ buildProgramArgs+ (\v fs -> fs{buildProgramArgs = v})+ ++ programDbOptions+ progDb+ showOrParseArgs+ buildProgramArgs+ (\v flags -> flags{buildProgramArgs = v})++emptyBuildFlags :: BuildFlags+emptyBuildFlags = mempty++instance Monoid BuildFlags where+ mempty = gmempty+ mappend = (<>)++instance Semigroup BuildFlags where+ (<>) = gmappend
+ src/Distribution/Simple/Setup/Clean.hs view
@@ -0,0 +1,123 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ViewPatterns #-}++-- |+-- Module : Distribution.Simple.Setup.Clean+-- Copyright : Isaac Jones 2003-2004+-- Duncan Coutts 2007+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Definition of the clean command-line options.+-- See: @Distribution.Simple.Setup@+module Distribution.Simple.Setup.Clean+ ( CleanFlags+ ( CleanCommonFlags+ , cleanVerbosity+ , cleanDistPref+ , cleanCabalFilePath+ , cleanWorkingDir+ , cleanTargets+ , ..+ )+ , emptyCleanFlags+ , defaultCleanFlags+ , cleanCommand+ ) where++import Distribution.Compat.Prelude hiding (get)+import Prelude ()++import Distribution.Simple.Command hiding (boolOpt, boolOpt')+import Distribution.Simple.Flag+import Distribution.Simple.Setup.Common+import Distribution.Utils.Path+import Distribution.Verbosity++-- ------------------------------------------------------------++-- * Clean flags++-- ------------------------------------------------------------++data CleanFlags = CleanFlags+ { cleanCommonFlags :: !CommonSetupFlags+ , cleanSaveConf :: Flag Bool+ }+ deriving (Show, Generic)++pattern CleanCommonFlags+ :: Flag Verbosity+ -> Flag (SymbolicPath Pkg (Dir Dist))+ -> Flag (SymbolicPath CWD (Dir Pkg))+ -> Flag (SymbolicPath Pkg File)+ -> [String]+ -> CleanFlags+pattern CleanCommonFlags+ { cleanVerbosity+ , cleanDistPref+ , cleanWorkingDir+ , cleanCabalFilePath+ , cleanTargets+ } <-+ ( cleanCommonFlags ->+ CommonSetupFlags+ { setupVerbosity = cleanVerbosity+ , setupDistPref = cleanDistPref+ , setupWorkingDir = cleanWorkingDir+ , setupCabalFilePath = cleanCabalFilePath+ , setupTargets = cleanTargets+ }+ )++instance Binary CleanFlags+instance Structured CleanFlags++defaultCleanFlags :: CleanFlags+defaultCleanFlags =+ CleanFlags+ { cleanCommonFlags = defaultCommonSetupFlags+ , cleanSaveConf = Flag False+ }++cleanCommand :: CommandUI CleanFlags+cleanCommand =+ CommandUI+ { commandName = "clean"+ , commandSynopsis = "Clean up after a build."+ , commandDescription = Just $ \_ ->+ "Removes .hi, .o, preprocessed sources, etc.\n"+ , commandNotes = Nothing+ , commandUsage = \pname ->+ "Usage: " ++ pname ++ " clean [FLAGS]\n"+ , commandDefaultFlags = defaultCleanFlags+ , commandOptions = \showOrParseArgs ->+ withCommonSetupOptions+ cleanCommonFlags+ (\c f -> f{cleanCommonFlags = c})+ showOrParseArgs+ [ option+ "s"+ ["save-configure"]+ "Do not remove the configuration file (dist/setup-config) during cleaning. Saves need to reconfigure."+ cleanSaveConf+ (\v flags -> flags{cleanSaveConf = v})+ trueArg+ ]+ }++emptyCleanFlags :: CleanFlags+emptyCleanFlags = mempty++instance Monoid CleanFlags where+ mempty = gmempty+ mappend = (<>)++instance Semigroup CleanFlags where+ (<>) = gmappend
+ src/Distribution/Simple/Setup/Common.hs view
@@ -0,0 +1,499 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RankNTypes #-}++-- |+-- Module : Distribution.Simple.Setup.Common+-- Copyright : Isaac Jones 2003-2004+-- Duncan Coutts 2007+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Common utilities for defining command-line options.+-- See: @Distribution.Simple.Setup@+module Distribution.Simple.Setup.Common+ ( CommonSetupFlags (..)+ , defaultCommonSetupFlags+ , withCommonSetupOptions+ , commonSetupTempFileOptions+ , CopyDest (..)+ , configureCCompiler+ , configureLinker+ , programDbOption+ , programDbOptions+ , programDbPaths+ , programDbPaths'+ , programFlagsDescription+ , splitArgs+ , testOrBenchmarkHelpText+ , defaultDistPref+ , extraCompilationArtifacts+ , optionDistPref+ , Flag+ , pattern Flag+ , pattern NoFlag+ , toFlag+ , fromFlag+ , fromFlagOrDefault+ , flagToMaybe+ , flagToList+ , maybeToFlag+ , BooleanFlag (..)+ , boolOpt+ , boolOpt'+ , trueArg+ , falseArg+ , reqArgFlag+ , reqSymbolicPathArgFlag+ , optionVerbosity+ , optionNumJobs+ ) where++import Distribution.Compat.Prelude hiding (get)+import Prelude ()++import Distribution.ReadE+import Distribution.Simple.Command hiding (boolOpt, boolOpt')+import qualified Distribution.Simple.Command as Command+import Distribution.Simple.Flag+import Distribution.Simple.InstallDirs+import Distribution.Simple.Program+import Distribution.Simple.Utils+import Distribution.Utils.Path+import Distribution.Verbosity++--------------------------------------------------------------------------------++-- | A datatype that stores common flags for different invocations+-- of a @Setup@ executable, e.g. configure, build, install.+data CommonSetupFlags = CommonSetupFlags+ { setupVerbosity :: !(Flag Verbosity)+ -- ^ Verbosity+ , setupWorkingDir :: !(Flag (SymbolicPath CWD (Dir Pkg)))+ -- ^ Working directory (optional)+ , setupDistPref :: !(Flag (SymbolicPath Pkg (Dir Dist)))+ -- ^ Build directory+ , setupCabalFilePath :: !(Flag (SymbolicPath Pkg File))+ -- ^ Which Cabal file to use (optional)+ , setupTargets :: [String]+ -- ^ Which targets is this Setup invocation relative to?+ --+ -- TODO: this one should not be here, it's just that the silly+ -- UserHooks stop us from passing extra info in other ways+ , setupKeepTempFiles :: Flag Bool+ -- ^ When this flag is set, temporary files will be kept after building.+ --+ -- Note: Keeping temporary files is important functionality for HLS, which+ -- runs @cabal repl@ with a fake GHC to get CLI arguments. It will need the+ -- temporary files (including multi unit repl response files) to stay, even+ -- after the @cabal repl@ command exits.+ }+ deriving (Eq, Show, Read, Generic)++instance Binary CommonSetupFlags+instance Structured CommonSetupFlags++instance Semigroup CommonSetupFlags where+ (<>) = gmappend++instance Monoid CommonSetupFlags where+ mempty = gmempty+ mappend = (<>)++defaultCommonSetupFlags :: CommonSetupFlags+defaultCommonSetupFlags =+ CommonSetupFlags+ { setupVerbosity = Flag normal+ , setupWorkingDir = NoFlag+ , setupDistPref = NoFlag+ , setupCabalFilePath = NoFlag+ , setupTargets = []+ , setupKeepTempFiles = NoFlag+ }++-- | Get `TempFileOptions` that respect the `setupKeepTempFiles` flag.+commonSetupTempFileOptions :: CommonSetupFlags -> TempFileOptions+commonSetupTempFileOptions options =+ TempFileOptions+ { optKeepTempFiles =+ fromFlagOrDefault False (setupKeepTempFiles options)+ }++commonSetupOptions :: ShowOrParseArgs -> [OptionField CommonSetupFlags]+commonSetupOptions showOrParseArgs =+ [ optionVerbosity+ setupVerbosity+ (\v flags -> flags{setupVerbosity = v})+ , optionDistPref+ setupDistPref+ (\d flags -> flags{setupDistPref = d})+ showOrParseArgs+ , option+ ""+ ["cabal-file"]+ "use this Cabal file"+ setupCabalFilePath+ (\v flags -> flags{setupCabalFilePath = v})+ (reqSymbolicPathArgFlag "PATH")+ , option+ ""+ ["keep-temp-files"]+ ( "Keep temporary files."+ )+ setupKeepTempFiles+ (\keepTempFiles flags -> flags{setupKeepTempFiles = keepTempFiles})+ trueArg+ -- NB: no --working-dir flag, as that value is populated using the+ -- global flag (see Distribution.Simple.Setup.Global.globalCommand).+ ]++withCommonSetupOptions+ :: (flags -> CommonSetupFlags)+ -> (CommonSetupFlags -> flags -> flags)+ -> ShowOrParseArgs+ -> [OptionField flags]+ -> [OptionField flags]+withCommonSetupOptions getCommon setCommon showOrParseArgs opts =+ map fmapOptionField (commonSetupOptions showOrParseArgs) ++ opts+ where+ fmapOptionField (OptionField nm descr) =+ OptionField nm (map (fmapOptDescr getCommon setCommon) descr)++--------------------------------------------------------------------------------++-- FIXME Not sure where this should live+defaultDistPref :: SymbolicPath Pkg (Dir Dist)+defaultDistPref = makeSymbolicPath "dist"++-- | The name of the directory where optional compilation artifacts+-- go, such as ghc plugins and .hie files.+extraCompilationArtifacts :: RelativePath Build (Dir Artifacts)+extraCompilationArtifacts = makeRelativePathEx "extra-compilation-artifacts"++-- | Help text for @test@ and @bench@ commands.+testOrBenchmarkHelpText+ :: String+ -- ^ Either @"test"@ or @"benchmark"@.+ -> String+ -- ^ Help text.+testOrBenchmarkHelpText s =+ unlines $+ map+ unwords+ [+ [ "The package must have been build with configuration"+ , concat ["flag `--enable-", s, "s`."]+ ]+ , [] -- blank line+ ,+ [ concat ["Note that additional dependencies of the ", s, "s"]+ , "must have already been installed."+ ]+ , []+ ,+ [ "By defining UserHooks in a custom Setup.hs, the package can define"+ , concat ["actions to be executed before and after running ", s, "s."]+ ]+ ]++-- ------------------------------------------------------------++-- * Shared options utils++-- ------------------------------------------------------------++programFlagsDescription :: ProgramDb -> String+programFlagsDescription progDb =+ "The flags --with-PROG and --PROG-option(s) can be used with"+ ++ " the following programs:"+ ++ (concatMap (\line -> "\n " ++ unwords line) . wrapLine 77 . sort)+ [programName prog | (prog, _) <- knownPrograms progDb]+ ++ "\n"++-- | For each known program @PROG@ in 'progDb', produce a @with-PROG@+-- 'OptionField'.+programDbPaths+ :: ProgramDb+ -> ShowOrParseArgs+ -> (flags -> [(String, FilePath)])+ -> ([(String, FilePath)] -> (flags -> flags))+ -> [OptionField flags]+programDbPaths progDb showOrParseArgs get set =+ programDbPaths' ("with-" ++) progDb showOrParseArgs get set++-- | Like 'programDbPaths', but allows to customise the option name.+programDbPaths'+ :: (String -> String)+ -> ProgramDb+ -> ShowOrParseArgs+ -> (flags -> [(String, FilePath)])+ -> ([(String, FilePath)] -> (flags -> flags))+ -> [OptionField flags]+programDbPaths' mkName progDb showOrParseArgs get set =+ case showOrParseArgs of+ -- we don't want a verbose help text list so we just show a generic one:+ ShowArgs -> [withProgramPath "PROG"]+ ParseArgs ->+ map+ (withProgramPath . programName . fst)+ (knownPrograms progDb)+ where+ withProgramPath prog =+ option+ ""+ [mkName prog]+ ("give the path to " ++ prog)+ get+ set+ ( reqArg'+ "PATH"+ (\path -> [(prog, path)])+ (\progPaths -> [path | (prog', path) <- progPaths, prog == prog'])+ )++-- | For each known program @PROG@ in 'progDb', produce a @PROG-option@+-- 'OptionField'.+programDbOption+ :: ProgramDb+ -> ShowOrParseArgs+ -> (flags -> [(String, [String])])+ -> ([(String, [String])] -> (flags -> flags))+ -> [OptionField flags]+programDbOption progDb showOrParseArgs get set =+ case showOrParseArgs of+ -- we don't want a verbose help text list so we just show a generic one:+ ShowArgs -> [programOption "PROG"]+ ParseArgs ->+ map+ (programOption . programName . fst)+ (knownPrograms progDb)+ where+ programOption prog =+ option+ ""+ [prog ++ "-option"]+ ( "give an extra option to "+ ++ prog+ ++ " (passed directly to "+ ++ prog+ ++ " as a single argument)"+ )+ get+ set+ ( reqArg'+ "OPT"+ (\arg -> [(prog, [arg])])+ ( \progArgs ->+ concat+ [ args+ | (prog', args) <- progArgs+ , prog == prog'+ ]+ )+ )++-- | For each known program @PROG@ in 'progDb', produce a @PROG-options@+-- 'OptionField'.+programDbOptions+ :: ProgramDb+ -> ShowOrParseArgs+ -> (flags -> [(String, [String])])+ -> ([(String, [String])] -> (flags -> flags))+ -> [OptionField flags]+programDbOptions progDb showOrParseArgs get set =+ case showOrParseArgs of+ -- we don't want a verbose help text list so we just show a generic one:+ ShowArgs -> [programOptions "PROG"]+ ParseArgs ->+ map+ (programOptions . programName . fst)+ (knownPrograms progDb)+ where+ programOptions prog =+ option+ ""+ [prog ++ "-options"]+ ( "give extra options to "+ ++ prog+ ++ " (split on spaces, use \"\" to prevent splitting)"+ )+ get+ set+ (reqArg' "OPTS" (\args -> [(prog, splitArgs args)]) (const []))++-- ------------------------------------------------------------++-- * GetOpt Utils++-- ------------------------------------------------------------++boolOpt+ :: SFlags+ -> SFlags+ -> MkOptDescr (a -> Flag Bool) (Flag Bool -> a -> a) a+boolOpt = Command.boolOpt flagToMaybe Flag++boolOpt'+ :: OptFlags+ -> OptFlags+ -> MkOptDescr (a -> Flag Bool) (Flag Bool -> a -> a) a+boolOpt' = Command.boolOpt' flagToMaybe Flag++trueArg, falseArg :: MkOptDescr (a -> Flag Bool) (Flag Bool -> a -> a) a+trueArg sfT lfT = boolOpt' (sfT, lfT) ([], []) sfT lfT+falseArg sfF lfF = boolOpt' ([], []) (sfF, lfF) sfF lfF++reqArgFlag+ :: ArgPlaceHolder+ -> SFlags+ -> LFlags+ -> Description+ -> (b -> Flag String)+ -> (Flag String -> b -> b)+ -> OptDescr b+reqArgFlag ad = reqArg ad (succeedReadE Flag) flagToList++optionDistPref+ :: (flags -> Flag (SymbolicPath Pkg (Dir Dist)))+ -> (Flag (SymbolicPath Pkg (Dir Dist)) -> flags -> flags)+ -> ShowOrParseArgs+ -> OptionField flags+optionDistPref get set = \showOrParseArgs ->+ option+ ""+ (distPrefFlagName showOrParseArgs)+ ( "The directory where Cabal puts generated build files "+ ++ "(default "+ ++ getSymbolicPath defaultDistPref+ ++ ")"+ )+ get+ set+ (reqSymbolicPathArgFlag "DIR")+ where+ distPrefFlagName ShowArgs = ["builddir"]+ distPrefFlagName ParseArgs = ["builddir", "distdir", "distpref"]++reqSymbolicPathArgFlag+ :: ArgPlaceHolder+ -> SFlags+ -> LFlags+ -> Description+ -> (b -> Flag (SymbolicPath from to))+ -> (Flag (SymbolicPath from to) -> b -> b)+ -> OptDescr b+reqSymbolicPathArgFlag title sf lf d get set =+ reqArgFlag+ title+ sf+ lf+ d+ (fmap getSymbolicPath . get)+ (set . fmap makeSymbolicPath)++optionVerbosity+ :: (flags -> Flag Verbosity)+ -> (Flag Verbosity -> flags -> flags)+ -> OptionField flags+optionVerbosity get set =+ option+ "v"+ ["verbose"]+ "Control verbosity (n is 0--3, default verbosity level is 1)"+ get+ set+ ( optArg+ "n"+ (fmap Flag flagToVerbosity)+ (show verbose, Flag verbose) -- default Value if no n is given+ (fmap (Just . showForCabal) . flagToList)+ )++optionNumJobs+ :: (flags -> Flag (Maybe Int))+ -> (Flag (Maybe Int) -> flags -> flags)+ -> OptionField flags+optionNumJobs get set =+ option+ "j"+ ["jobs"]+ "Run NUM jobs simultaneously (or '$ncpus' if no NUM is given)."+ get+ set+ ( optArg+ "NUM"+ (fmap Flag numJobsParser)+ ("$ncpus", Flag Nothing)+ (map (Just . maybe "$ncpus" show) . flagToList)+ )+ where+ numJobsParser :: ReadE (Maybe Int)+ numJobsParser = ReadE $ \s ->+ case s of+ "$ncpus" -> Right Nothing+ _ -> case reads s of+ [(n, "")]+ | n < 1 -> Left "The number of jobs should be 1 or more."+ | otherwise -> Right (Just n)+ _ -> Left "The jobs value should be a number or '$ncpus'"++-- ------------------------------------------------------------++-- * Other Utils++-- ------------------------------------------------------------++configureCCompiler+ :: Verbosity+ -> ProgramDb+ -> IO (FilePath, [String])+configureCCompiler verbosity progdb = configureProg verbosity progdb gccProgram++configureLinker :: Verbosity -> ProgramDb -> IO (FilePath, [String])+configureLinker verbosity progdb = configureProg verbosity progdb ldProgram++configureProg+ :: Verbosity+ -> ProgramDb+ -> Program+ -> IO (FilePath, [String])+configureProg verbosity programDb prog = do+ (p, _) <- requireProgram verbosity prog programDb+ let pInv = programInvocation p []+ return (progInvokePath pInv, progInvokeArgs pInv)++-- | Helper function to split a string into a list of arguments.+-- It's supposed to handle quoted things sensibly, eg:+--+-- > splitArgs "--foo=\"C:/Program Files/Bar/" --baz"+-- > = ["--foo=C:/Program Files/Bar", "--baz"]+--+-- > splitArgs "\"-DMSGSTR=\\\"foo bar\\\"\" --baz"+-- > = ["-DMSGSTR=\"foo bar\"","--baz"]+splitArgs :: String -> [String]+splitArgs = space []+ where+ space :: String -> String -> [String]+ space w [] = word w []+ space w (c : s)+ | isSpace c = word w (space [] s)+ space w ('"' : s) = string w s+ space w s = nonstring w s++ string :: String -> String -> [String]+ string w [] = word w []+ string w ('"' : s) = space w s+ string w ('\\' : '"' : s) = string ('"' : w) s+ string w (c : s) = string (c : w) s++ nonstring :: String -> String -> [String]+ nonstring w [] = word w []+ nonstring w ('"' : s) = string w s+ nonstring w (c : s) = space (c : w) s++ word [] s = s+ word w s = reverse w : s
+ src/Distribution/Simple/Setup/Config.hs view
@@ -0,0 +1,1109 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ViewPatterns #-}++-----------------------------------------------------------------------------++-- |+-- Module : Distribution.Simple.Setup.Config+-- Copyright : Isaac Jones 2003-2004+-- Duncan Coutts 2007+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Definition of the configure command-line options.+-- See: @Distribution.Simple.Setup@+module Distribution.Simple.Setup.Config+ ( ConfigFlags+ ( ConfigCommonFlags+ , configVerbosity+ , configDistPref+ , configCabalFilePath+ , configWorkingDir+ , configTargets+ , ..+ )+ , emptyConfigFlags+ , defaultConfigFlags+ , configureCommand+ , configPrograms+ , readPackageDb+ , readPackageDbList+ , showPackageDb+ , showPackageDbList+ , configureArgs+ , configureOptions+ , installDirsOptions+ ) where++import Distribution.Compat.Prelude hiding (get)+import Prelude ()++import qualified Distribution.Compat.CharParsing as P+import Distribution.Compat.Semigroup (Last' (..), Option' (..))+import Distribution.Compat.Stack+import Distribution.Compiler+import Distribution.ModuleName+import Distribution.PackageDescription+import Distribution.Parsec+import Distribution.Pretty+import Distribution.ReadE+import Distribution.Simple.Command hiding (boolOpt, boolOpt')+import Distribution.Simple.Compiler+import Distribution.Simple.Flag+import Distribution.Simple.InstallDirs+import Distribution.Simple.Program+import Distribution.Simple.Setup.Common+import Distribution.Simple.Utils+import Distribution.Types.ComponentId+import Distribution.Types.DumpBuildInfo+import Distribution.Types.GivenComponent+import Distribution.Types.Module+import Distribution.Types.PackageVersionConstraint+import Distribution.Types.UnitId+import Distribution.Utils.NubList+import Distribution.Utils.Path+import Distribution.Verbosity++import qualified Text.PrettyPrint as Disp++-- ------------------------------------------------------------++-- * Config flags++-- ------------------------------------------------------------++-- | Flags to @configure@ command.+--+-- IMPORTANT: every time a new flag is added, 'D.C.Setup.filterConfigureFlags'+-- should be updated.+-- IMPORTANT: every time a new flag is added, it should be added to the Eq instance+data ConfigFlags = ConfigFlags+ { configCommonFlags :: !CommonSetupFlags+ , -- FIXME: the configPrograms is only here to pass info through to configure+ -- because the type of configure is constrained by the UserHooks.+ -- when we change UserHooks next we should pass the initial+ -- ProgramDb directly and not via ConfigFlags+ configPrograms_ :: Option' (Last' ProgramDb)+ -- ^ All programs that+ -- @cabal@ may run+ , configProgramPaths :: [(String, FilePath)]+ -- ^ user specified programs paths+ , configProgramArgs :: [(String, [String])]+ -- ^ user specified programs args+ , configProgramPathExtra :: NubList FilePath+ -- ^ Extend the $PATH+ , configHcFlavor :: Flag CompilerFlavor+ -- ^ The \"flavor\" of the+ -- compiler, e.g. GHC.+ , configHcPath :: Flag FilePath+ -- ^ given compiler location+ , configHcPkg :: Flag FilePath+ -- ^ given hc-pkg location+ , configVanillaLib :: Flag Bool+ -- ^ Enable vanilla library+ , configProfLib :: Flag Bool+ -- ^ Enable profiling in the library+ , configSharedLib :: Flag Bool+ -- ^ Build shared library+ , configStaticLib :: Flag Bool+ -- ^ Build static library+ , configDynExe :: Flag Bool+ -- ^ Enable dynamic linking of the+ -- executables.+ , configFullyStaticExe :: Flag Bool+ -- ^ Enable fully static linking of the+ -- executables.+ , configProfExe :: Flag Bool+ -- ^ Enable profiling in the+ -- executables.+ , configProf :: Flag Bool+ -- ^ Enable profiling in the library+ -- and executables.+ , configProfShared :: Flag Bool+ -- ^ Enable shared profiling objects+ , configProfDetail :: Flag ProfDetailLevel+ -- ^ Profiling detail level+ -- in the library and executables.+ , configProfLibDetail :: Flag ProfDetailLevel+ -- ^ Profiling detail level+ -- in the library+ , configConfigureArgs :: [String]+ -- ^ Extra arguments to @configure@+ , configOptimization :: Flag OptimisationLevel+ -- ^ Enable optimization.+ , configProgPrefix :: Flag PathTemplate+ -- ^ Installed executable prefix.+ , configProgSuffix :: Flag PathTemplate+ -- ^ Installed executable suffix.+ , configInstallDirs :: InstallDirs (Flag PathTemplate)+ -- ^ Installation+ -- paths+ , configScratchDir :: Flag FilePath+ , configExtraLibDirs :: [SymbolicPath Pkg (Dir Lib)]+ -- ^ path to search for extra libraries+ , configExtraLibDirsStatic :: [SymbolicPath Pkg (Dir Lib)]+ -- ^ path to search for extra+ -- libraries when linking+ -- fully static executables+ , configExtraFrameworkDirs :: [SymbolicPath Pkg (Dir Framework)]+ -- ^ path to search for extra+ -- frameworks (OS X only)+ , configExtraIncludeDirs :: [SymbolicPath Pkg (Dir Include)]+ -- ^ path to search for header files+ , configIPID :: Flag String+ -- ^ explicit IPID to be used+ , configCID :: Flag ComponentId+ -- ^ explicit CID to be used+ , configDeterministic :: Flag Bool+ -- ^ be as deterministic as possible+ -- (e.g., invariant over GHC, database,+ -- etc). Used by the test suite+ , configUserInstall :: Flag Bool+ -- ^ The --user\/--global flag+ , configPackageDBs :: [Maybe PackageDB]+ -- ^ Which package DBs to use+ , configGHCiLib :: Flag Bool+ -- ^ Enable compiling library for GHCi+ , configSplitSections :: Flag Bool+ -- ^ Enable -split-sections with GHC+ , configSplitObjs :: Flag Bool+ -- ^ Enable -split-objs with GHC+ , configStripExes :: Flag Bool+ -- ^ Enable executable stripping+ , configStripLibs :: Flag Bool+ -- ^ Enable library stripping+ , configConstraints :: [PackageVersionConstraint]+ -- ^ Additional constraints for+ -- dependencies.+ , configDependencies :: [GivenComponent]+ -- ^ The packages depended on which already exist+ , configPromisedDependencies :: [PromisedComponent]+ -- ^ The packages depended on which doesn't yet exist (i.e. promised).+ -- Promising dependencies enables us to configure components in parallel,+ -- and avoids expensive builds if they are not necessary.+ -- For example, in multi-repl mode, we don't want to build dependencies that+ -- are loaded into the interactive session, since we have to build them again.+ , configInstantiateWith :: [(ModuleName, Module)]+ -- ^ The requested Backpack instantiation. If empty, either this+ -- package does not use Backpack, or we just want to typecheck+ -- the indefinite package.+ , configConfigurationsFlags :: FlagAssignment+ , configTests :: Flag Bool+ -- ^ Enable test suite compilation+ , configBenchmarks :: Flag Bool+ -- ^ Enable benchmark compilation+ , configCoverage :: Flag Bool+ -- ^ Enable program coverage+ , configLibCoverage :: Flag Bool+ -- ^ Enable program coverage (deprecated)+ , configExactConfiguration :: Flag Bool+ -- ^ All direct dependencies and flags are provided on the command line by+ -- the user via the '--dependency' and '--flags' options.+ , configFlagError :: Flag String+ -- ^ Halt and show an error message indicating an error in flag assignment+ , configRelocatable :: Flag Bool+ -- ^ Enable relocatable package built+ , configDebugInfo :: Flag DebugInfoLevel+ -- ^ Emit debug info.+ , configDumpBuildInfo :: Flag DumpBuildInfo+ -- ^ Should we dump available build information on build?+ -- Dump build information to disk before attempting to build,+ -- tooling can parse these files and use them to compile the+ -- source files themselves.+ , configUseResponseFiles :: Flag Bool+ -- ^ Whether to use response files at all. They're used for such tools+ -- as haddock, or ld.+ , configAllowDependingOnPrivateLibs :: Flag Bool+ -- ^ Allow depending on private sublibraries. This is used by external+ -- tools (like cabal-install) so they can add multiple-public-libraries+ -- compatibility to older ghcs by checking visibility externally.+ , configCoverageFor :: Flag [UnitId]+ -- ^ The list of libraries to be included in the hpc coverage report for+ -- testsuites run with @--enable-coverage@. Notably, this list must exclude+ -- indefinite libraries and instantiations because HPC does not support+ -- backpack (Nov. 2023).+ , configIgnoreBuildTools :: Flag Bool+ -- ^ When this flag is set, all tools declared in `build-tool`s and+ -- `build-tool-depends` will be ignored. This allows a Cabal package with+ -- build-tool-dependencies to be built even if the tool is not found.+ }+ deriving (Generic, Read, Show)++pattern ConfigCommonFlags+ :: Flag Verbosity+ -> Flag (SymbolicPath Pkg (Dir Dist))+ -> Flag (SymbolicPath CWD (Dir Pkg))+ -> Flag (SymbolicPath Pkg File)+ -> [String]+ -> ConfigFlags+pattern ConfigCommonFlags+ { configVerbosity+ , configDistPref+ , configWorkingDir+ , configCabalFilePath+ , configTargets+ } <-+ ( configCommonFlags ->+ CommonSetupFlags+ { setupVerbosity = configVerbosity+ , setupDistPref = configDistPref+ , setupWorkingDir = configWorkingDir+ , setupCabalFilePath = configCabalFilePath+ , setupTargets = configTargets+ }+ )++instance Binary ConfigFlags+instance Structured ConfigFlags++-- | More convenient version of 'configPrograms'. Results in an+-- 'error' if internal invariant is violated.+configPrograms :: WithCallStack (ConfigFlags -> ProgramDb)+configPrograms =+ fromMaybe (error "FIXME: remove configPrograms")+ . fmap getLast'+ . getOption'+ . configPrograms_++instance Eq ConfigFlags where+ (==) a b =+ -- configPrograms skipped: not user specified, has no Eq instance+ equal configCommonFlags+ && equal configProgramPaths+ && equal configProgramArgs+ && equal configProgramPathExtra+ && equal configHcFlavor+ && equal configHcPath+ && equal configHcPkg+ && equal configVanillaLib+ && equal configProfLib+ && equal configSharedLib+ && equal configStaticLib+ && equal configDynExe+ && equal configFullyStaticExe+ && equal configProfExe+ && equal configProf+ && equal configProfDetail+ && equal configProfShared+ && equal configProfLibDetail+ && equal configConfigureArgs+ && equal configOptimization+ && equal configProgPrefix+ && equal configProgSuffix+ && equal configInstallDirs+ && equal configScratchDir+ && equal configExtraLibDirs+ && equal configExtraLibDirsStatic+ && equal configExtraIncludeDirs+ && equal configIPID+ && equal configDeterministic+ && equal configUserInstall+ && equal configPackageDBs+ && equal configGHCiLib+ && equal configSplitSections+ && equal configSplitObjs+ && equal configStripExes+ && equal configStripLibs+ && equal configConstraints+ && equal configDependencies+ && equal configPromisedDependencies+ && equal configConfigurationsFlags+ && equal configTests+ && equal configBenchmarks+ && equal configCoverage+ && equal configLibCoverage+ && equal configExactConfiguration+ && equal configFlagError+ && equal configRelocatable+ && equal configDebugInfo+ && equal configDumpBuildInfo+ && equal configUseResponseFiles+ && equal configAllowDependingOnPrivateLibs+ && equal configCoverageFor+ && equal configIgnoreBuildTools+ where+ equal f = on (==) f a b++{- FOURMOLU_DISABLE -}+defaultConfigFlags :: ProgramDb -> ConfigFlags+defaultConfigFlags progDb =+ emptyConfigFlags+ { configCommonFlags = defaultCommonSetupFlags+ , configPrograms_ = Option' (Just (Last' progDb))+ , configHcFlavor = maybe NoFlag Flag defaultCompilerFlavor+ , configVanillaLib = Flag True+ , configProfLib = NoFlag+ , configSharedLib = NoFlag+ , configStaticLib = NoFlag+ , configDynExe = Flag False+ , configFullyStaticExe = Flag False+ , configProfExe = NoFlag+ , configProf = NoFlag+ , configProfDetail = NoFlag+ , configProfLibDetail = NoFlag+ , configOptimization = Flag NormalOptimisation+ , configProgPrefix = Flag (toPathTemplate "")+ , configProgSuffix = Flag (toPathTemplate "")+ , configUserInstall = Flag False -- TODO: reverse this+#if defined(mingw32_HOST_OS)+ -- See #8062 and GHC #21019.+ , configGHCiLib = Flag False+#else+ , configGHCiLib = NoFlag+#endif+ , configSplitSections = Flag False+ , configSplitObjs = Flag False -- takes longer, so turn off by default+ , configStripExes = NoFlag+ , configStripLibs = NoFlag+ , configTests = Flag False+ , configBenchmarks = Flag False+ , configCoverage = Flag False+ , configLibCoverage = NoFlag+ , configExactConfiguration = Flag False+ , configFlagError = NoFlag+ , configRelocatable = Flag False+ , configDebugInfo = Flag NoDebugInfo+ , configDumpBuildInfo = NoFlag+ , configUseResponseFiles = NoFlag+ }+{- FOURMOLU_ENABLE -}++configureCommand :: ProgramDb -> CommandUI ConfigFlags+configureCommand progDb =+ CommandUI+ { commandName = "configure"+ , commandSynopsis = "Prepare to build the package."+ , commandDescription = Just $ \_ ->+ wrapText $+ "Configure how the package is built by setting "+ ++ "package (and other) flags.\n"+ ++ "\n"+ ++ "The configuration affects several other commands, "+ ++ "including build, test, bench, run, repl.\n"+ , commandNotes = Just $ \_pname -> programFlagsDescription progDb+ , commandUsage = \pname ->+ "Usage: " ++ pname ++ " configure [FLAGS]\n"+ , commandDefaultFlags = defaultConfigFlags progDb+ , commandOptions = \showOrParseArgs ->+ configureOptions showOrParseArgs+ ++ programDbPaths+ progDb+ showOrParseArgs+ configProgramPaths+ (\v fs -> fs{configProgramPaths = v})+ ++ programDbOption+ progDb+ showOrParseArgs+ configProgramArgs+ (\v fs -> fs{configProgramArgs = v})+ ++ programDbOptions+ progDb+ showOrParseArgs+ configProgramArgs+ (\v fs -> fs{configProgramArgs = v})+ }++-- | Inverse to 'dispModSubstEntry'.+parsecModSubstEntry :: ParsecParser (ModuleName, Module)+parsecModSubstEntry = do+ k <- parsec+ _ <- P.char '='+ v <- parsec+ return (k, v)++-- | Pretty-print a single entry of a module substitution.+dispModSubstEntry :: (ModuleName, Module) -> Disp.Doc+dispModSubstEntry (k, v) = pretty k <<>> Disp.char '=' <<>> pretty v++configureOptions :: ShowOrParseArgs -> [OptionField ConfigFlags]+configureOptions showOrParseArgs =+ withCommonSetupOptions+ configCommonFlags+ (\c f -> f{configCommonFlags = c})+ showOrParseArgs+ [ option+ []+ ["compiler"]+ "compiler"+ configHcFlavor+ (\v flags -> flags{configHcFlavor = v})+ ( choiceOpt+ [ (Flag GHC, ("g", ["ghc"]), "compile with GHC")+ , (Flag GHCJS, ([], ["ghcjs"]), "compile with GHCJS")+ , (Flag UHC, ([], ["uhc"]), "compile with UHC")+ ]+ )+ , option+ "w"+ ["with-compiler"]+ "give the path to a particular compiler"+ configHcPath+ (\v flags -> flags{configHcPath = v})+ (reqArgFlag "PATH")+ , option+ ""+ ["with-hc-pkg"]+ "give the path to the package tool"+ configHcPkg+ (\v flags -> flags{configHcPkg = v})+ (reqArgFlag "PATH")+ ]+ ++ map liftInstallDirs installDirsOptions+ ++ [ option+ ""+ ["program-prefix"]+ "prefix to be applied to installed executables"+ configProgPrefix+ (\v flags -> flags{configProgPrefix = v})+ (reqPathTemplateArgFlag "PREFIX")+ , option+ ""+ ["program-suffix"]+ "suffix to be applied to installed executables"+ configProgSuffix+ (\v flags -> flags{configProgSuffix = v})+ (reqPathTemplateArgFlag "SUFFIX")+ , option+ ""+ ["library-vanilla"]+ "Vanilla libraries"+ configVanillaLib+ (\v flags -> flags{configVanillaLib = v})+ (boolOpt [] [])+ , option+ "p"+ ["library-profiling"]+ "Library profiling"+ configProfLib+ (\v flags -> flags{configProfLib = v})+ (boolOpt "p" [])+ , option+ ""+ ["shared"]+ "Shared library"+ configSharedLib+ (\v flags -> flags{configSharedLib = v})+ (boolOpt [] [])+ , option+ ""+ ["static"]+ "Static library"+ configStaticLib+ (\v flags -> flags{configStaticLib = v})+ (boolOpt [] [])+ , option+ ""+ ["executable-dynamic"]+ "Executable dynamic linking"+ configDynExe+ (\v flags -> flags{configDynExe = v})+ (boolOpt [] [])+ , option+ ""+ ["executable-static"]+ "Executable fully static linking"+ configFullyStaticExe+ (\v flags -> flags{configFullyStaticExe = v})+ (boolOpt [] [])+ , option+ ""+ ["profiling"]+ "Executable and library profiling"+ configProf+ (\v flags -> flags{configProf = v})+ (boolOpt [] [])+ , option+ ""+ ["profiling-shared"]+ "Build profiling shared libraries"+ configProfShared+ (\v flags -> flags{configProfShared = v})+ (boolOpt [] [])+ , option+ ""+ ["executable-profiling"]+ "Executable profiling (DEPRECATED)"+ configProfExe+ (\v flags -> flags{configProfExe = v})+ (boolOpt [] [])+ , option+ ""+ ["profiling-detail"]+ ( "Profiling detail level for executable and library (default, "+ ++ "none, exported-functions, toplevel-functions, all-functions, late)."+ )+ configProfDetail+ (\v flags -> flags{configProfDetail = v})+ ( reqArg'+ "level"+ (Flag . flagToProfDetailLevel)+ showProfDetailLevelFlag+ )+ , option+ ""+ ["library-profiling-detail"]+ "Profiling detail level for libraries only."+ configProfLibDetail+ (\v flags -> flags{configProfLibDetail = v})+ ( reqArg'+ "level"+ (Flag . flagToProfDetailLevel)+ showProfDetailLevelFlag+ )+ , multiOption+ "optimization"+ configOptimization+ (\v flags -> flags{configOptimization = v})+ [ optArgDef'+ "n"+ (show NoOptimisation, Flag . flagToOptimisationLevel)+ ( \f -> case f of+ Flag NoOptimisation -> []+ Flag NormalOptimisation -> [Nothing]+ Flag MaximumOptimisation -> [Just "2"]+ _ -> []+ )+ "O"+ ["enable-optimization", "enable-optimisation"]+ "Build with optimization (n is 0--2, default is 1)"+ , noArg+ (Flag NoOptimisation)+ []+ ["disable-optimization", "disable-optimisation"]+ "Build without optimization"+ ]+ , multiOption+ "debug-info"+ configDebugInfo+ (\v flags -> flags{configDebugInfo = v})+ [ optArg'+ "n"+ (Flag . flagToDebugInfoLevel)+ ( \f -> case f of+ Flag NoDebugInfo -> []+ Flag MinimalDebugInfo -> [Just "1"]+ Flag NormalDebugInfo -> [Nothing]+ Flag MaximalDebugInfo -> [Just "3"]+ _ -> []+ )+ ""+ ["enable-debug-info"]+ "Emit debug info (n is 0--3, default is 0)"+ , noArg+ (Flag NoDebugInfo)+ []+ ["disable-debug-info"]+ "Don't emit debug info"+ ]+ , multiOption+ "build-info"+ configDumpBuildInfo+ (\v flags -> flags{configDumpBuildInfo = v})+ [ noArg+ (Flag DumpBuildInfo)+ []+ ["enable-build-info"]+ "Enable build information generation during project building"+ , noArg+ (Flag NoDumpBuildInfo)+ []+ ["disable-build-info"]+ "Disable build information generation during project building"+ ]+ , option+ ""+ ["library-for-ghci"]+ "compile library for use with GHCi"+ configGHCiLib+ (\v flags -> flags{configGHCiLib = v})+ (boolOpt [] [])+ , option+ ""+ ["split-sections"]+ "compile library code such that unneeded definitions can be dropped from the final executable (GHC 7.8+)"+ configSplitSections+ (\v flags -> flags{configSplitSections = v})+ (boolOpt [] [])+ , option+ ""+ ["split-objs"]+ "split library into smaller objects to reduce binary sizes (GHC 6.6+)"+ configSplitObjs+ (\v flags -> flags{configSplitObjs = v})+ (boolOpt [] [])+ , option+ ""+ ["executable-stripping"]+ "strip executables upon installation to reduce binary sizes"+ configStripExes+ (\v flags -> flags{configStripExes = v})+ (boolOpt [] [])+ , option+ ""+ ["library-stripping"]+ "strip libraries upon installation to reduce binary sizes"+ configStripLibs+ (\v flags -> flags{configStripLibs = v})+ (boolOpt [] [])+ , option+ ""+ ["configure-option"]+ "Extra option for configure"+ configConfigureArgs+ (\v flags -> flags{configConfigureArgs = v})+ (reqArg' "OPT" (\x -> [x]) id)+ , option+ ""+ ["user-install"]+ "doing a per-user installation"+ configUserInstall+ (\v flags -> flags{configUserInstall = v})+ (boolOpt' ([], ["user"]) ([], ["global"]))+ , option+ ""+ ["package-db"]+ ( "Append the given package database to the list of package"+ ++ " databases used (to satisfy dependencies and register into)."+ ++ " May be a specific file, 'global' or 'user'. The initial list"+ ++ " is ['global'], ['global', 'user'], or ['global', $sandbox],"+ ++ " depending on context. Use 'clear' to reset the list to empty."+ ++ " See the user guide for details."+ )+ configPackageDBs+ (\v flags -> flags{configPackageDBs = v})+ (reqArg' "DB" readPackageDbList showPackageDbList)+ , option+ "f"+ ["flags"]+ "Force values for the given flags in Cabal conditionals in the .cabal file. E.g., --flags=\"debug -usebytestrings\" forces the flag \"debug\" to true and \"usebytestrings\" to false."+ configConfigurationsFlags+ (\v flags -> flags{configConfigurationsFlags = v})+ ( reqArg+ "FLAGS"+ (parsecToReadE (\err -> "Invalid flag assignment: " ++ err) legacyParsecFlagAssignment)+ legacyShowFlagAssignment'+ )+ , option+ ""+ ["extra-include-dirs"]+ "A list of directories to search for header files"+ configExtraIncludeDirs+ (\v flags -> flags{configExtraIncludeDirs = v})+ (reqArg' "PATH" (\x -> [makeSymbolicPath x]) (fmap getSymbolicPath))+ , option+ ""+ ["deterministic"]+ "Try to be as deterministic as possible (used by the test suite)"+ configDeterministic+ (\v flags -> flags{configDeterministic = v})+ (boolOpt [] [])+ , option+ ""+ ["ipid"]+ "Installed package ID to compile this package as"+ configIPID+ (\v flags -> flags{configIPID = v})+ (reqArgFlag "IPID")+ , option+ ""+ ["cid"]+ "Installed component ID to compile this component as"+ (fmap prettyShow . configCID)+ (\v flags -> flags{configCID = fmap mkComponentId v})+ (reqArgFlag "CID")+ , option+ ""+ ["extra-lib-dirs"]+ "A list of directories to search for external libraries"+ configExtraLibDirs+ (\v flags -> flags{configExtraLibDirs = v})+ (reqArg' "PATH" (\x -> [makeSymbolicPath x]) (fmap getSymbolicPath))+ , option+ ""+ ["extra-lib-dirs-static"]+ "A list of directories to search for external libraries when linking fully static executables"+ configExtraLibDirsStatic+ (\v flags -> flags{configExtraLibDirsStatic = v})+ (reqArg' "PATH" (\x -> [makeSymbolicPath x]) (fmap getSymbolicPath))+ , option+ ""+ ["extra-framework-dirs"]+ "A list of directories to search for external frameworks (OS X only)"+ configExtraFrameworkDirs+ (\v flags -> flags{configExtraFrameworkDirs = v})+ (reqArg' "PATH" (\x -> [makeSymbolicPath x]) (fmap getSymbolicPath))+ , option+ ""+ ["extra-prog-path"]+ "A list of directories to search for required programs (in addition to the normal search locations)"+ configProgramPathExtra+ (\v flags -> flags{configProgramPathExtra = v})+ (reqArg' "PATH" (\x -> toNubList [x]) fromNubList)+ , option+ ""+ ["constraint"]+ "A list of additional constraints on the dependencies."+ configConstraints+ (\v flags -> flags{configConstraints = v})+ ( reqArg+ "DEPENDENCY"+ (parsecToReadE (const "dependency expected") ((\x -> [x]) `fmap` parsec))+ (map prettyShow)+ )+ , option+ ""+ ["dependency"]+ "A list of exact dependencies. E.g., --dependency=\"void=void-0.5.8-177d5cdf20962d0581fe2e4932a6c309\""+ configDependencies+ (\v flags -> flags{configDependencies = v})+ ( reqArg+ "NAME[:COMPONENT_NAME]=CID"+ (parsecToReadE (const "dependency expected") ((\x -> [x]) `fmap` parsecGivenComponent))+ (map prettyGivenComponent)+ )+ , option+ ""+ ["promised-dependency"]+ "A list of promised dependencies. E.g., --promised-dependency=\"void-0.5.8=void-0.5.8-177d5cdf20962d0581fe2e4932a6c309\""+ configPromisedDependencies+ (\v flags -> flags{configPromisedDependencies = v})+ ( reqArg+ "NAME-VER[:COMPONENT_NAME]=CID"+ (parsecToReadE (const "dependency expected") ((\x -> [x]) `fmap` parsecPromisedComponent))+ (map prettyPromisedComponent)+ )+ , option+ ""+ ["instantiate-with"]+ "A mapping of signature names to concrete module instantiations."+ configInstantiateWith+ (\v flags -> flags{configInstantiateWith = v})+ ( reqArg+ "NAME=MOD"+ (parsecToReadE ("Cannot parse module substitution: " ++) (fmap (: []) parsecModSubstEntry))+ (map (Disp.renderStyle defaultStyle . dispModSubstEntry))+ )+ , option+ ""+ ["tests"]+ "dependency checking and compilation for test suites listed in the package description file."+ configTests+ (\v flags -> flags{configTests = v})+ (boolOpt [] [])+ , option+ ""+ ["coverage"]+ "build package with Haskell Program Coverage. (GHC only)"+ configCoverage+ (\v flags -> flags{configCoverage = v})+ (boolOpt [] [])+ , option+ ""+ ["library-coverage"]+ "build package with Haskell Program Coverage. (GHC only) (DEPRECATED)"+ configLibCoverage+ (\v flags -> flags{configLibCoverage = v})+ (boolOpt [] [])+ , option+ ""+ ["exact-configuration"]+ "All direct dependencies and flags are provided on the command line."+ configExactConfiguration+ (\v flags -> flags{configExactConfiguration = v})+ trueArg+ , option+ ""+ ["benchmarks"]+ "dependency checking and compilation for benchmarks listed in the package description file."+ configBenchmarks+ (\v flags -> flags{configBenchmarks = v})+ (boolOpt [] [])+ , option+ ""+ ["relocatable"]+ "building a package that is relocatable. (GHC only)"+ configRelocatable+ (\v flags -> flags{configRelocatable = v})+ (boolOpt [] [])+ , option+ ""+ ["response-files"]+ "enable workaround for old versions of programs like \"ar\" that do not support @file arguments"+ configUseResponseFiles+ (\v flags -> flags{configUseResponseFiles = v})+ (boolOpt' ([], ["disable-response-files"]) ([], []))+ , option+ ""+ ["allow-depending-on-private-libs"]+ ( "Allow depending on private libraries. "+ ++ "If set, the library visibility check MUST be done externally."+ )+ configAllowDependingOnPrivateLibs+ (\v flags -> flags{configAllowDependingOnPrivateLibs = v})+ trueArg+ , option+ ""+ ["coverage-for"]+ "A list of unit-ids of libraries to include in the Haskell Program Coverage report."+ configCoverageFor+ ( \v flags ->+ flags+ { configCoverageFor =+ mergeListFlag (configCoverageFor flags) v+ }+ )+ ( reqArg'+ "UNITID"+ (Flag . (: []) . fromString)+ (fmap prettyShow . fromFlagOrDefault [])+ )+ , option+ ""+ ["ignore-build-tools"]+ ( "Ignore build tool dependencies. "+ ++ "If set, declared build tools needn't be found for compilation to proceed."+ )+ configIgnoreBuildTools+ (\v flags -> flags{configIgnoreBuildTools = v})+ trueArg+ ]+ where+ liftInstallDirs =+ liftOption configInstallDirs (\v flags -> flags{configInstallDirs = v})++ reqPathTemplateArgFlag title _sf _lf d get set =+ reqArgFlag+ title+ _sf+ _lf+ d+ (fmap fromPathTemplate . get)+ (set . fmap toPathTemplate)++readPackageDbList :: String -> [Maybe PackageDB]+readPackageDbList str = [readPackageDb str]++-- | Parse a PackageDB stack entry+--+-- @since 3.7.0.0+readPackageDb :: String -> Maybe PackageDB+readPackageDb "clear" = Nothing+readPackageDb "global" = Just GlobalPackageDB+readPackageDb "user" = Just UserPackageDB+readPackageDb other = Just (SpecificPackageDB (makeSymbolicPath other))++showPackageDbList :: [Maybe PackageDB] -> [String]+showPackageDbList = map showPackageDb++-- | Show a PackageDB stack entry+--+-- @since 3.7.0.0+showPackageDb :: Maybe PackageDB -> String+showPackageDb Nothing = "clear"+showPackageDb (Just GlobalPackageDB) = "global"+showPackageDb (Just UserPackageDB) = "user"+showPackageDb (Just (SpecificPackageDB db)) = getSymbolicPath db++showProfDetailLevelFlag :: Flag ProfDetailLevel -> [String]+showProfDetailLevelFlag NoFlag = []+showProfDetailLevelFlag (Flag dl) = [showProfDetailLevel dl]++parsecPromisedComponent :: ParsecParser PromisedComponent+parsecPromisedComponent = do+ pn <- parsec+ ln <- P.option LMainLibName $ do+ _ <- P.char ':'+ ucn <- parsec+ return $+ if unUnqualComponentName ucn == unPackageName (pkgName pn)+ then LMainLibName+ else LSubLibName ucn+ _ <- P.char '='+ cid <- parsec+ return $ PromisedComponent pn ln cid++prettyPromisedComponent :: PromisedComponent -> String+prettyPromisedComponent (PromisedComponent pn cn cid) =+ prettyShow pn+ ++ case cn of+ LMainLibName -> ""+ LSubLibName n -> ":" ++ prettyShow n+ ++ "="+ ++ prettyShow cid++parsecGivenComponent :: ParsecParser GivenComponent+parsecGivenComponent = do+ pn <- parsec+ ln <- P.option LMainLibName $ do+ _ <- P.char ':'+ ucn <- parsec+ return $+ if unUnqualComponentName ucn == unPackageName pn+ then LMainLibName+ else LSubLibName ucn+ _ <- P.char '='+ cid <- parsec+ return $ GivenComponent pn ln cid++prettyGivenComponent :: GivenComponent -> String+prettyGivenComponent (GivenComponent pn cn cid) =+ prettyShow pn+ ++ case cn of+ LMainLibName -> ""+ LSubLibName n -> ":" ++ prettyShow n+ ++ "="+ ++ prettyShow cid++installDirsOptions :: [OptionField (InstallDirs (Flag PathTemplate))]+installDirsOptions =+ [ option+ ""+ ["prefix"]+ "bake this prefix in preparation of installation"+ prefix+ (\v flags -> flags{prefix = v})+ installDirArg+ , option+ ""+ ["bindir"]+ "installation directory for executables"+ bindir+ (\v flags -> flags{bindir = v})+ installDirArg+ , option+ ""+ ["libdir"]+ "installation directory for libraries"+ libdir+ (\v flags -> flags{libdir = v})+ installDirArg+ , option+ ""+ ["libsubdir"]+ "subdirectory of libdir in which libs are installed"+ libsubdir+ (\v flags -> flags{libsubdir = v})+ installDirArg+ , option+ ""+ ["dynlibdir"]+ "installation directory for dynamic libraries"+ dynlibdir+ (\v flags -> flags{dynlibdir = v})+ installDirArg+ , option+ ""+ ["libexecdir"]+ "installation directory for program executables"+ libexecdir+ (\v flags -> flags{libexecdir = v})+ installDirArg+ , option+ ""+ ["libexecsubdir"]+ "subdirectory of libexecdir in which private executables are installed"+ libexecsubdir+ (\v flags -> flags{libexecsubdir = v})+ installDirArg+ , option+ ""+ ["datadir"]+ "installation directory for read-only data"+ datadir+ (\v flags -> flags{datadir = v})+ installDirArg+ , option+ ""+ ["datasubdir"]+ "subdirectory of datadir in which data files are installed"+ datasubdir+ (\v flags -> flags{datasubdir = v})+ installDirArg+ , option+ ""+ ["docdir"]+ "installation directory for documentation"+ docdir+ (\v flags -> flags{docdir = v})+ installDirArg+ , option+ ""+ ["htmldir"]+ "installation directory for HTML documentation"+ htmldir+ (\v flags -> flags{htmldir = v})+ installDirArg+ , option+ ""+ ["haddockdir"]+ "installation directory for haddock interfaces"+ haddockdir+ (\v flags -> flags{haddockdir = v})+ installDirArg+ , option+ ""+ ["sysconfdir"]+ "installation directory for configuration files"+ sysconfdir+ (\v flags -> flags{sysconfdir = v})+ installDirArg+ ]+ where+ installDirArg _sf _lf d get set =+ reqArgFlag+ "DIR"+ _sf+ _lf+ d+ (fmap fromPathTemplate . get)+ (set . fmap toPathTemplate)++emptyConfigFlags :: ConfigFlags+emptyConfigFlags = mempty++instance Monoid ConfigFlags where+ mempty = gmempty+ mappend = (<>)++instance Semigroup ConfigFlags where+ (<>) = gmappend++-- | Arguments to pass to a @configure@ script, e.g. generated by+-- @autoconf@.+configureArgs :: Bool -> ConfigFlags -> [String]+configureArgs bcHack flags =+ hc_flag+ ++ optFlag "with-hc-pkg" configHcPkg+ ++ optFlag' "prefix" prefix+ ++ optFlag' "bindir" bindir+ ++ optFlag' "libdir" libdir+ ++ optFlag' "libexecdir" libexecdir+ ++ optFlag' "datadir" datadir+ ++ optFlag' "sysconfdir" sysconfdir+ ++ configConfigureArgs flags+ where+ hc_flag = case (configHcFlavor flags, configHcPath flags) of+ (_, Flag hc_path) -> [hc_flag_name ++ hc_path]+ (Flag hc, NoFlag) -> [hc_flag_name ++ prettyShow hc]+ (NoFlag, NoFlag) -> []+ hc_flag_name+ -- TODO kill off this bc hack when defaultUserHooks is removed.+ | bcHack = "--with-hc="+ | otherwise = "--with-compiler="+ optFlag name config_field = case config_field flags of+ Flag p -> ["--" ++ name ++ "=" ++ p]+ NoFlag -> []+ optFlag' name config_field =+ optFlag+ name+ ( fmap fromPathTemplate+ . config_field+ . configInstallDirs+ )
+ src/Distribution/Simple/Setup/Copy.hs view
@@ -0,0 +1,174 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ViewPatterns #-}++-- |+-- Module : Distribution.Simple.Setup.Copy+-- Copyright : Isaac Jones 2003-2004+-- Duncan Coutts 2007+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Definition of the copy command-line options.+-- See: @Distribution.Simple.Setup@+module Distribution.Simple.Setup.Copy+ ( CopyFlags+ ( CopyCommonFlags+ , copyVerbosity+ , copyDistPref+ , copyCabalFilePath+ , copyWorkingDir+ , copyTargets+ , ..+ )+ , emptyCopyFlags+ , defaultCopyFlags+ , copyCommand+ ) where++import Distribution.Compat.Prelude hiding (get)+import Prelude ()++import Distribution.ReadE+import Distribution.Simple.Command hiding (boolOpt, boolOpt')+import Distribution.Simple.Flag+import Distribution.Simple.InstallDirs+import Distribution.Simple.Setup.Common+import Distribution.Simple.Utils+import Distribution.Utils.Path+import Distribution.Verbosity++-- ------------------------------------------------------------++-- * Copy flags++-- ------------------------------------------------------------++-- | Flags to @copy@: (destdir, copy-prefix (backwards compat), verbosity)+data CopyFlags = CopyFlags+ { copyCommonFlags :: !CommonSetupFlags+ , copyDest :: Flag CopyDest+ }+ deriving (Show, Generic)++pattern CopyCommonFlags+ :: Flag Verbosity+ -> Flag (SymbolicPath Pkg (Dir Dist))+ -> Flag (SymbolicPath CWD (Dir Pkg))+ -> Flag (SymbolicPath Pkg File)+ -> [String]+ -> CopyFlags+pattern CopyCommonFlags+ { copyVerbosity+ , copyDistPref+ , copyWorkingDir+ , copyCabalFilePath+ , copyTargets+ } <-+ ( copyCommonFlags ->+ CommonSetupFlags+ { setupVerbosity = copyVerbosity+ , setupDistPref = copyDistPref+ , setupWorkingDir = copyWorkingDir+ , setupCabalFilePath = copyCabalFilePath+ , setupTargets = copyTargets+ }+ )++instance Binary CopyFlags+instance Structured CopyFlags++defaultCopyFlags :: CopyFlags+defaultCopyFlags =+ CopyFlags+ { copyCommonFlags = defaultCommonSetupFlags+ , copyDest = Flag NoCopyDest+ }++copyCommand :: CommandUI CopyFlags+copyCommand =+ CommandUI+ { commandName = "copy"+ , commandSynopsis = "Copy the files of all/specific components to install locations."+ , commandDescription = Just $ \_ ->+ wrapText $+ "Components encompass executables and libraries. "+ ++ "Does not call register, and allows a prefix at install time. "+ ++ "Without the --destdir flag, configure determines location.\n"+ , commandNotes = Just $ \pname ->+ "Examples:\n"+ ++ " "+ ++ pname+ ++ " copy "+ ++ " All the components in the package\n"+ ++ " "+ ++ pname+ ++ " copy foo "+ ++ " A component (i.e. lib, exe, test suite)"+ , commandUsage =+ usageAlternatives "copy" $+ [ "[FLAGS]"+ , "COMPONENTS [FLAGS]"+ ]+ , commandDefaultFlags = defaultCopyFlags+ , commandOptions = \showOrParseArgs -> case showOrParseArgs of+ ShowArgs ->+ filter+ ( (`notElem` ["target-package-db"])+ . optionName+ )+ $ copyOptions ShowArgs+ ParseArgs -> copyOptions ParseArgs+ }++copyOptions :: ShowOrParseArgs -> [OptionField CopyFlags]+copyOptions showOrParseArgs =+ withCommonSetupOptions+ copyCommonFlags+ (\c f -> f{copyCommonFlags = c})+ showOrParseArgs+ [ option+ ""+ ["destdir"]+ "directory to copy files to, prepended to installation directories"+ copyDest+ ( \v flags -> case copyDest flags of+ Flag (CopyToDb _) -> error "Use either 'destdir' or 'target-package-db'."+ _ -> flags{copyDest = v}+ )+ ( reqArg+ "DIR"+ (succeedReadE (Flag . CopyTo))+ (\f -> case f of Flag (CopyTo p) -> [p]; _ -> [])+ )+ , option+ ""+ ["target-package-db"]+ "package database to copy files into. Required when using ${pkgroot} prefix."+ copyDest+ ( \v flags -> case copyDest flags of+ NoFlag -> flags{copyDest = v}+ Flag NoCopyDest -> flags{copyDest = v}+ _ -> error "Use either 'destdir' or 'target-package-db'."+ )+ ( reqArg+ "DATABASE"+ (succeedReadE (Flag . CopyToDb))+ (\f -> case f of Flag (CopyToDb p) -> [p]; _ -> [])+ )+ ]++emptyCopyFlags :: CopyFlags+emptyCopyFlags = mempty++instance Monoid CopyFlags where+ mempty = gmempty+ mappend = (<>)++instance Semigroup CopyFlags where+ (<>) = gmappend
+ src/Distribution/Simple/Setup/Global.hs view
@@ -0,0 +1,129 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}++-- |+-- Module : Distribution.Simple.Setup.Global+-- Copyright : Isaac Jones 2003-2004+-- Duncan Coutts 2007+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Definition of the global command-line options.+-- See: @Distribution.Simple.Setup@+module Distribution.Simple.Setup.Global+ ( GlobalFlags (..)+ , emptyGlobalFlags+ , defaultGlobalFlags+ , globalCommand+ ) where++import Distribution.Compat.Prelude hiding (get)+import Prelude ()++import Distribution.Simple.Command hiding (boolOpt, boolOpt')+import Distribution.Simple.Flag+import Distribution.Simple.Setup.Common+import Distribution.Utils.Path++-- ------------------------------------------------------------++-- * Global flags++-- ------------------------------------------------------------++-- In fact since individual flags types are monoids and these are just sets of+-- flags then they are also monoids pointwise. This turns out to be really+-- useful. The mempty is the set of empty flags and mappend allows us to+-- override specific flags. For example we can start with default flags and+-- override with the ones we get from a file or the command line, or both.++-- | Flags that apply at the top level, not to any sub-command.+data GlobalFlags = GlobalFlags+ { globalVersion :: Flag Bool+ , globalNumericVersion :: Flag Bool+ , globalWorkingDir :: Flag (SymbolicPath CWD (Dir Pkg))+ }+ deriving (Generic)++defaultGlobalFlags :: GlobalFlags+defaultGlobalFlags =+ GlobalFlags+ { globalVersion = Flag False+ , globalNumericVersion = Flag False+ , globalWorkingDir = NoFlag+ }++globalCommand :: [Command action] -> CommandUI GlobalFlags+globalCommand commands =+ CommandUI+ { commandName = ""+ , commandSynopsis = ""+ , commandUsage = \pname ->+ "This Setup program uses the Haskell Cabal Infrastructure.\n"+ ++ "See http://www.haskell.org/cabal/ for more information.\n"+ ++ "\n"+ ++ "Usage: "+ ++ pname+ ++ " [GLOBAL FLAGS] [COMMAND [FLAGS]]\n"+ , commandDescription = Just $ \pname ->+ let+ commands' = commands ++ [commandAddAction helpCommandUI undefined]+ cmdDescs = getNormalCommandDescriptions commands'+ maxlen = maximum $ [length name | (name, _) <- cmdDescs]+ align str = str ++ replicate (maxlen - length str) ' '+ in+ "Commands:\n"+ ++ unlines+ [ " " ++ align name ++ " " ++ descr+ | (name, descr) <- cmdDescs+ ]+ ++ "\n"+ ++ "For more information about a command use\n"+ ++ " "+ ++ pname+ ++ " COMMAND --help\n\n"+ ++ "Typical steps for installing Cabal packages:\n"+ ++ concat+ [ " " ++ pname ++ " " ++ x ++ "\n"+ | x <- ["configure", "build", "install"]+ ]+ , commandNotes = Nothing+ , commandDefaultFlags = defaultGlobalFlags+ , commandOptions = \_ ->+ [ option+ ['V']+ ["version"]+ "Print version information"+ globalVersion+ (\v flags -> flags{globalVersion = v})+ trueArg+ , option+ []+ ["numeric-version"]+ "Print just the version number"+ globalNumericVersion+ (\v flags -> flags{globalNumericVersion = v})+ trueArg+ , option+ ""+ ["working-dir"]+ "Set working directory"+ globalWorkingDir+ (\v flags -> flags{globalWorkingDir = v})+ (reqSymbolicPathArgFlag "DIR")+ ]+ }++emptyGlobalFlags :: GlobalFlags+emptyGlobalFlags = mempty++instance Monoid GlobalFlags where+ mempty = gmempty+ mappend = (<>)++instance Semigroup GlobalFlags where+ (<>) = gmappend
+ src/Distribution/Simple/Setup/Haddock.hs view
@@ -0,0 +1,642 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ViewPatterns #-}++-- |+-- Module : Distribution.Simple.Setup.Haddock+-- Copyright : Isaac Jones 2003-2004+-- Duncan Coutts 2007+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Definition of the haddock command-line options.+-- See: @Distribution.Simple.Setup@+module Distribution.Simple.Setup.Haddock+ ( HaddockTarget (..)+ , HaddockFlags+ ( HaddockCommonFlags+ , haddockVerbosity+ , haddockDistPref+ , haddockCabalFilePath+ , haddockWorkingDir+ , haddockTargets+ , ..+ )+ , emptyHaddockFlags+ , defaultHaddockFlags+ , haddockCommand+ , Visibility (..)+ , HaddockProjectFlags (..)+ , emptyHaddockProjectFlags+ , defaultHaddockProjectFlags+ , haddockProjectCommand+ , haddockOptions+ , haddockProjectOptions+ ) where++import Distribution.Compat.Prelude hiding (get)+import Prelude ()++import qualified Distribution.Compat.CharParsing as P+import Distribution.Parsec+import Distribution.Pretty+import Distribution.Simple.Command hiding (boolOpt, boolOpt')+import Distribution.Simple.Flag+import Distribution.Simple.InstallDirs+import Distribution.Simple.Program+import Distribution.Simple.Setup.Common+import Distribution.Utils.Path+import Distribution.Verbosity++import qualified Text.PrettyPrint as Disp++-- ------------------------------------------------------------++-- * Haddock flags++-- ------------------------------------------------------------++-- | When we build haddock documentation, there are two cases:+--+-- 1. We build haddocks only for the current development version,+-- intended for local use and not for distribution. In this case,+-- we store the generated documentation in @<dist>/doc/html/<package name>@.+--+-- 2. We build haddocks for intended for uploading them to hackage.+-- In this case, we need to follow the layout that hackage expects+-- from documentation tarballs, and we might also want to use different+-- flags than for development builds, so in this case we store the generated+-- documentation in @<dist>/doc/html/<package id>-docs@.+data HaddockTarget = ForHackage | ForDevelopment deriving (Eq, Show, Generic)++instance Binary HaddockTarget+instance Structured HaddockTarget++instance Pretty HaddockTarget where+ pretty ForHackage = Disp.text "for-hackage"+ pretty ForDevelopment = Disp.text "for-development"++instance Parsec HaddockTarget where+ parsec =+ P.choice+ [ P.try $ P.string "for-hackage" >> return ForHackage+ , P.string "for-development" >> return ForDevelopment+ ]++data HaddockFlags = HaddockFlags+ { haddockCommonFlags :: !CommonSetupFlags+ , haddockProgramPaths :: [(String, FilePath)]+ , haddockProgramArgs :: [(String, [String])]+ , haddockHoogle :: Flag Bool+ , haddockHtml :: Flag Bool+ , haddockHtmlLocation :: Flag String+ , haddockForHackage :: Flag HaddockTarget+ , haddockExecutables :: Flag Bool+ , haddockTestSuites :: Flag Bool+ , haddockBenchmarks :: Flag Bool+ , haddockForeignLibs :: Flag Bool+ , haddockInternal :: Flag Bool+ , haddockCss :: Flag FilePath+ , haddockLinkedSource :: Flag Bool+ , haddockQuickJump :: Flag Bool+ , haddockHscolourCss :: Flag FilePath+ , haddockContents :: Flag PathTemplate+ , haddockIndex :: Flag PathTemplate+ , haddockBaseUrl :: Flag String+ , haddockResourcesDir :: Flag String+ , haddockOutputDir :: Flag FilePath+ , haddockUseUnicode :: Flag Bool+ }+ deriving (Show, Generic)++pattern HaddockCommonFlags+ :: Flag Verbosity+ -> Flag (SymbolicPath Pkg (Dir Dist))+ -> Flag (SymbolicPath CWD (Dir Pkg))+ -> Flag (SymbolicPath Pkg File)+ -> [String]+ -> HaddockFlags+pattern HaddockCommonFlags+ { haddockVerbosity+ , haddockDistPref+ , haddockWorkingDir+ , haddockCabalFilePath+ , haddockTargets+ } <-+ ( haddockCommonFlags ->+ CommonSetupFlags+ { setupVerbosity = haddockVerbosity+ , setupDistPref = haddockDistPref+ , setupWorkingDir = haddockWorkingDir+ , setupCabalFilePath = haddockCabalFilePath+ , setupTargets = haddockTargets+ }+ )++instance Binary HaddockFlags+instance Structured HaddockFlags++defaultHaddockFlags :: HaddockFlags+defaultHaddockFlags =+ HaddockFlags+ { haddockCommonFlags = defaultCommonSetupFlags+ , haddockProgramPaths = mempty+ , haddockProgramArgs = []+ , haddockHoogle = Flag False+ , haddockHtml = Flag False+ , haddockHtmlLocation = NoFlag+ , haddockForHackage = NoFlag+ , haddockExecutables = Flag False+ , haddockTestSuites = Flag False+ , haddockBenchmarks = Flag False+ , haddockForeignLibs = Flag False+ , haddockInternal = Flag False+ , haddockCss = NoFlag+ , haddockLinkedSource = Flag False+ , haddockQuickJump = Flag False+ , haddockHscolourCss = NoFlag+ , haddockContents = NoFlag+ , haddockIndex = NoFlag+ , haddockBaseUrl = NoFlag+ , haddockResourcesDir = NoFlag+ , haddockOutputDir = NoFlag+ , haddockUseUnicode = Flag False+ }++haddockCommand :: CommandUI HaddockFlags+haddockCommand =+ CommandUI+ { commandName = "haddock"+ , commandSynopsis = "Generate Haddock HTML documentation."+ , commandDescription = Just $ \_ ->+ "Requires the program haddock, version 2.x.\n"+ , commandNotes = Nothing+ , commandUsage =+ usageAlternatives "haddock" $+ [ "[FLAGS]"+ , "COMPONENTS [FLAGS]"+ ]+ , commandDefaultFlags = defaultHaddockFlags+ , commandOptions = \showOrParseArgs ->+ haddockOptions showOrParseArgs+ ++ programDbPaths+ progDb+ ParseArgs+ haddockProgramPaths+ (\v flags -> flags{haddockProgramPaths = v})+ ++ programDbOption+ progDb+ showOrParseArgs+ haddockProgramArgs+ (\v fs -> fs{haddockProgramArgs = v})+ ++ programDbOptions+ progDb+ ParseArgs+ haddockProgramArgs+ (\v flags -> flags{haddockProgramArgs = v})+ }+ where+ progDb =+ addKnownProgram haddockProgram $+ addKnownProgram ghcProgram $+ emptyProgramDb++haddockOptions :: ShowOrParseArgs -> [OptionField HaddockFlags]+haddockOptions showOrParseArgs =+ withCommonSetupOptions+ haddockCommonFlags+ (\c f -> f{haddockCommonFlags = c})+ showOrParseArgs+ [ option+ ""+ ["hoogle"]+ "Generate a hoogle database"+ haddockHoogle+ (\v flags -> flags{haddockHoogle = v})+ trueArg+ , option+ ""+ ["html"]+ "Generate HTML documentation (the default)"+ haddockHtml+ (\v flags -> flags{haddockHtml = v})+ trueArg+ , option+ ""+ ["html-location"]+ "Location of HTML documentation for pre-requisite packages"+ haddockHtmlLocation+ (\v flags -> flags{haddockHtmlLocation = v})+ (reqArgFlag "URL")+ , option+ ""+ ["for-hackage"]+ "Collection of flags to generate documentation suitable for upload to hackage"+ haddockForHackage+ (\v flags -> flags{haddockForHackage = v})+ (noArg (Flag ForHackage))+ , option+ ""+ ["executables"]+ "Run haddock for Executables targets"+ haddockExecutables+ (\v flags -> flags{haddockExecutables = v})+ trueArg+ , option+ ""+ ["tests"]+ "Run haddock for Test Suite targets"+ haddockTestSuites+ (\v flags -> flags{haddockTestSuites = v})+ trueArg+ , option+ ""+ ["benchmarks"]+ "Run haddock for Benchmark targets"+ haddockBenchmarks+ (\v flags -> flags{haddockBenchmarks = v})+ trueArg+ , option+ ""+ ["foreign-libraries"]+ "Run haddock for Foreign Library targets"+ haddockForeignLibs+ (\v flags -> flags{haddockForeignLibs = v})+ trueArg+ , option+ ""+ ["all"]+ "Run haddock for all targets"+ ( \f ->+ allFlags+ [ haddockExecutables f+ , haddockTestSuites f+ , haddockBenchmarks f+ , haddockForeignLibs f+ ]+ )+ ( \v flags ->+ flags+ { haddockExecutables = v+ , haddockTestSuites = v+ , haddockBenchmarks = v+ , haddockForeignLibs = v+ }+ )+ trueArg+ , option+ ""+ ["internal"]+ "Run haddock for internal modules and include all symbols"+ haddockInternal+ (\v flags -> flags{haddockInternal = v})+ trueArg+ , option+ ""+ ["css"]+ "Use PATH as the haddock stylesheet"+ haddockCss+ (\v flags -> flags{haddockCss = v})+ (reqArgFlag "PATH")+ , option+ ""+ ["hyperlink-source", "hyperlink-sources", "hyperlinked-source"]+ "Hyperlink the documentation to the source code"+ haddockLinkedSource+ (\v flags -> flags{haddockLinkedSource = v})+ trueArg+ , option+ ""+ ["quickjump"]+ "Generate an index for interactive documentation navigation"+ haddockQuickJump+ (\v flags -> flags{haddockQuickJump = v})+ trueArg+ , option+ ""+ ["hscolour-css"]+ "Use PATH as the HsColour stylesheet"+ haddockHscolourCss+ (\v flags -> flags{haddockHscolourCss = v})+ (reqArgFlag "PATH")+ , option+ ""+ ["contents-location"]+ "Bake URL in as the location for the contents page"+ haddockContents+ (\v flags -> flags{haddockContents = v})+ ( reqArg'+ "URL"+ (toFlag . toPathTemplate)+ (flagToList . fmap fromPathTemplate)+ )+ , option+ ""+ ["index-location"]+ "Use a separately-generated HTML index"+ haddockIndex+ (\v flags -> flags{haddockIndex = v})+ ( reqArg'+ "URL"+ (toFlag . toPathTemplate)+ (flagToList . fmap fromPathTemplate)+ )+ , option+ ""+ ["base-url"]+ "Base URL for static files."+ haddockBaseUrl+ (\v flags -> flags{haddockBaseUrl = v})+ (reqArgFlag "URL")+ , option+ ""+ ["resources-dir"]+ "location of Haddocks static / auxiliary files"+ haddockResourcesDir+ (\v flags -> flags{haddockResourcesDir = v})+ (reqArgFlag "DIR")+ , option+ ""+ ["output-dir"]+ "Generate haddock documentation into this directory. This flag is provided as a technology preview and is subject to change in the next releases."+ haddockOutputDir+ (\v flags -> flags{haddockOutputDir = v})+ (reqArgFlag "DIR")+ , option+ ""+ ["use-unicode"]+ "Pass --use-unicode option to haddock"+ haddockUseUnicode+ (\v flags -> flags{haddockUseUnicode = v})+ trueArg+ ]++emptyHaddockFlags :: HaddockFlags+emptyHaddockFlags = mempty++instance Monoid HaddockFlags where+ mempty = gmempty+ mappend = (<>)++instance Semigroup HaddockFlags where+ (<>) = gmappend++-- ------------------------------------------------------------++-- * HaddocksFlags flags++-- ------------------------------------------------------------++-- | Governs whether modules from a given interface should be visible or+-- hidden in the Haddock generated content page. We don't expose this+-- functionality to the user, but simply use 'Visible' for only local packages.+-- Visibility of modules is available since @haddock-2.26.1@.+data Visibility = Visible | Hidden+ deriving (Eq, Show)++data HaddockProjectFlags = HaddockProjectFlags+ { haddockProjectCommonFlags :: !CommonSetupFlags+ , haddockProjectHackage :: Flag Bool+ -- ^ a shortcut option which builds documentation linked to hackage. It implies:+ -- * `--html-location='https://hackage.haskell.org/package/$prg-$version/docs'+ -- * `--quickjump`+ -- * `--gen-index`+ -- * `--gen-contents`+ -- * `--hyperlinked-source`+ , -- options passed to @haddock@ via 'createHaddockIndex'+ haddockProjectDir :: Flag String+ -- ^ output directory of combined haddocks, the default is './haddocks'+ , haddockProjectPrologue :: Flag String+ , haddockProjectInterfaces :: Flag [(FilePath, Maybe FilePath, Maybe FilePath, Visibility)]+ -- ^ 'haddocksInterfaces' is inferred by the 'haddocksAction'; currently not+ -- exposed to the user.+ , -- options passed to @haddock@ via 'HaddockFlags' when building+ -- documentation++ haddockProjectProgramPaths :: [(String, FilePath)]+ , haddockProjectProgramArgs :: [(String, [String])]+ , haddockProjectHoogle :: Flag Bool+ , -- haddockHtml is not supported+ haddockProjectHtmlLocation :: Flag String+ , -- haddockForHackage is not supported+ haddockProjectExecutables :: Flag Bool+ , haddockProjectTestSuites :: Flag Bool+ , haddockProjectBenchmarks :: Flag Bool+ , haddockProjectForeignLibs :: Flag Bool+ , haddockProjectInternal :: Flag Bool+ , haddockProjectCss :: Flag FilePath+ , haddockProjectHscolourCss :: Flag FilePath+ , -- haddockContent is not supported, a fixed value is provided+ -- haddockIndex is not supported, a fixed value is provided+ -- haddockDistPerf is not supported, note: it changes location of the haddocks+ -- haddockBaseUrl is not supported, a fixed value is provided+ haddockProjectResourcesDir :: Flag String+ , haddockProjectUseUnicode :: Flag Bool+ }+ deriving (Show, Generic)++defaultHaddockProjectFlags :: HaddockProjectFlags+defaultHaddockProjectFlags =+ HaddockProjectFlags+ { haddockProjectCommonFlags = defaultCommonSetupFlags+ , haddockProjectHackage = Flag False+ , haddockProjectDir = Flag "./haddocks"+ , haddockProjectPrologue = NoFlag+ , haddockProjectTestSuites = Flag False+ , haddockProjectProgramPaths = mempty+ , haddockProjectProgramArgs = mempty+ , haddockProjectHoogle = Flag False+ , haddockProjectHtmlLocation = NoFlag+ , haddockProjectExecutables = Flag False+ , haddockProjectBenchmarks = Flag False+ , haddockProjectForeignLibs = Flag False+ , haddockProjectInternal = Flag False+ , haddockProjectCss = NoFlag+ , haddockProjectHscolourCss = NoFlag+ , haddockProjectResourcesDir = NoFlag+ , haddockProjectInterfaces = NoFlag+ , haddockProjectUseUnicode = NoFlag+ }++haddockProjectCommand :: CommandUI HaddockProjectFlags+haddockProjectCommand =+ CommandUI+ { commandName = "v2-haddock-project"+ , commandSynopsis = "Generate Haddocks HTML documentation for the cabal project."+ , commandDescription = Just $ \_ ->+ "Requires the program haddock, version 2.26.\n"+ , commandNotes = Nothing+ , commandUsage =+ usageAlternatives "haddock-project" $+ [ "[FLAGS]"+ , "COMPONENTS [FLAGS]"+ ]+ , commandDefaultFlags = defaultHaddockProjectFlags+ , commandOptions = \showOrParseArgs ->+ haddockProjectOptions showOrParseArgs+ ++ programDbPaths+ progDb+ ParseArgs+ haddockProjectProgramPaths+ (\v flags -> flags{haddockProjectProgramPaths = v})+ ++ programDbOption+ progDb+ showOrParseArgs+ haddockProjectProgramArgs+ (\v fs -> fs{haddockProjectProgramArgs = v})+ ++ programDbOptions+ progDb+ ParseArgs+ haddockProjectProgramArgs+ (\v flags -> flags{haddockProjectProgramArgs = v})+ }+ where+ progDb =+ addKnownProgram haddockProgram $+ addKnownProgram ghcProgram $+ emptyProgramDb++haddockProjectOptions :: ShowOrParseArgs -> [OptionField HaddockProjectFlags]+haddockProjectOptions showOrParseArgs =+ withCommonSetupOptions+ haddockProjectCommonFlags+ (\c f -> f{haddockProjectCommonFlags = c})+ showOrParseArgs+ [ option+ ""+ ["hackage"]+ ( concat+ [ "A short-cut option to build documentation linked to hackage."+ ]+ )+ haddockProjectHackage+ (\v flags -> flags{haddockProjectHackage = v})+ trueArg+ , option+ ""+ ["output"]+ "Output directory"+ haddockProjectDir+ (\v flags -> flags{haddockProjectDir = v})+ (optArg' "DIRECTORY" maybeToFlag (fmap Just . flagToList))+ , option+ ""+ ["prologue"]+ "File path to a prologue file in haddock format"+ haddockProjectPrologue+ (\v flags -> flags{haddockProjectPrologue = v})+ (optArg' "PATH" maybeToFlag (fmap Just . flagToList))+ , option+ ""+ ["hoogle"]+ "Generate a hoogle database"+ haddockProjectHoogle+ (\v flags -> flags{haddockProjectHoogle = v})+ trueArg+ , option+ ""+ ["html-location"]+ "Location of HTML documentation for pre-requisite packages"+ haddockProjectHtmlLocation+ (\v flags -> flags{haddockProjectHtmlLocation = v})+ (reqArgFlag "URL")+ , option+ ""+ ["executables"]+ "Run haddock for Executables targets"+ haddockProjectExecutables+ (\v flags -> flags{haddockProjectExecutables = v})+ trueArg+ , option+ ""+ ["tests"]+ "Run haddock for Test Suite targets"+ haddockProjectTestSuites+ (\v flags -> flags{haddockProjectTestSuites = v})+ trueArg+ , option+ ""+ ["benchmarks"]+ "Run haddock for Benchmark targets"+ haddockProjectBenchmarks+ (\v flags -> flags{haddockProjectBenchmarks = v})+ trueArg+ , option+ ""+ ["foreign-libraries"]+ "Run haddock for Foreign Library targets"+ haddockProjectForeignLibs+ (\v flags -> flags{haddockProjectForeignLibs = v})+ trueArg+ , option+ ""+ ["all", "haddock-all"]+ "Run haddock for all targets"+ ( \f ->+ allFlags+ [ haddockProjectExecutables f+ , haddockProjectTestSuites f+ , haddockProjectBenchmarks f+ , haddockProjectForeignLibs f+ ]+ )+ ( \v flags ->+ flags+ { haddockProjectExecutables = v+ , haddockProjectTestSuites = v+ , haddockProjectBenchmarks = v+ , haddockProjectForeignLibs = v+ }+ )+ trueArg+ , option+ ""+ ["internal"]+ "Run haddock for internal modules and include all symbols"+ haddockProjectInternal+ (\v flags -> flags{haddockProjectInternal = v})+ trueArg+ , option+ ""+ ["css"]+ "Use PATH as the haddock stylesheet"+ haddockProjectCss+ (\v flags -> flags{haddockProjectCss = v})+ (reqArgFlag "PATH")+ , option+ ""+ ["hscolour-css"]+ "Use PATH as the HsColour stylesheet"+ haddockProjectHscolourCss+ (\v flags -> flags{haddockProjectHscolourCss = v})+ (reqArgFlag "PATH")+ , option+ ""+ ["resources-dir"]+ "location of Haddocks static / auxiliary files"+ haddockProjectResourcesDir+ (\v flags -> flags{haddockProjectResourcesDir = v})+ (reqArgFlag "DIR")+ , option+ ""+ ["use-unicode"]+ "Pass --use-unicode option to haddock"+ haddockProjectUseUnicode+ (\v flags -> flags{haddockProjectUseUnicode = v})+ trueArg+ ]++emptyHaddockProjectFlags :: HaddockProjectFlags+emptyHaddockProjectFlags = mempty++instance Monoid HaddockProjectFlags where+ mempty = gmempty+ mappend = (<>)++instance Semigroup HaddockProjectFlags where+ (<>) = gmappend
+ src/Distribution/Simple/Setup/Hscolour.hs view
@@ -0,0 +1,181 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ViewPatterns #-}++-- |+-- Module : Distribution.Simple.Setup.Hscolour+-- Copyright : Isaac Jones 2003-2004+-- Duncan Coutts 2007+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Definition of the hscolour command-line options.+-- See: @Distribution.Simple.Setup@+module Distribution.Simple.Setup.Hscolour+ ( HscolourFlags+ ( HscolourCommonFlags+ , hscolourVerbosity+ , hscolourDistPref+ , hscolourCabalFilePath+ , hscolourWorkingDir+ , hscolourTargets+ , ..+ )+ , emptyHscolourFlags+ , defaultHscolourFlags+ , hscolourCommand+ ) where++import Distribution.Compat.Prelude hiding (get)+import Prelude ()++import Distribution.Simple.Command hiding (boolOpt, boolOpt')+import Distribution.Simple.Flag+import Distribution.Simple.Setup.Common+import Distribution.Utils.Path+import Distribution.Verbosity++-- ------------------------------------------------------------++-- * HsColour flags++-- ------------------------------------------------------------++data HscolourFlags = HscolourFlags+ { hscolourCommonFlags :: !CommonSetupFlags+ , hscolourCSS :: Flag FilePath+ , hscolourExecutables :: Flag Bool+ , hscolourTestSuites :: Flag Bool+ , hscolourBenchmarks :: Flag Bool+ , hscolourForeignLibs :: Flag Bool+ }+ deriving (Show, Generic)++pattern HscolourCommonFlags+ :: Flag Verbosity+ -> Flag (SymbolicPath Pkg (Dir Dist))+ -> Flag (SymbolicPath CWD (Dir Pkg))+ -> Flag (SymbolicPath Pkg File)+ -> [String]+ -> HscolourFlags+pattern HscolourCommonFlags+ { hscolourVerbosity+ , hscolourDistPref+ , hscolourWorkingDir+ , hscolourCabalFilePath+ , hscolourTargets+ } <-+ ( hscolourCommonFlags ->+ CommonSetupFlags+ { setupVerbosity = hscolourVerbosity+ , setupDistPref = hscolourDistPref+ , setupWorkingDir = hscolourWorkingDir+ , setupCabalFilePath = hscolourCabalFilePath+ , setupTargets = hscolourTargets+ }+ )++instance Binary HscolourFlags+instance Structured HscolourFlags++emptyHscolourFlags :: HscolourFlags+emptyHscolourFlags = mempty++defaultHscolourFlags :: HscolourFlags+defaultHscolourFlags =+ HscolourFlags+ { hscolourCommonFlags = defaultCommonSetupFlags+ , hscolourCSS = NoFlag+ , hscolourExecutables = Flag False+ , hscolourTestSuites = Flag False+ , hscolourBenchmarks = Flag False+ , hscolourForeignLibs = Flag False+ }++instance Monoid HscolourFlags where+ mempty = gmempty+ mappend = (<>)++instance Semigroup HscolourFlags where+ (<>) = gmappend++hscolourCommand :: CommandUI HscolourFlags+hscolourCommand =+ CommandUI+ { commandName = "hscolour"+ , commandSynopsis =+ "Generate HsColour colourised code, in HTML format."+ , commandDescription = Just (\_ -> "Requires the hscolour program.\n")+ , commandNotes = Just $ \_ ->+ "Deprecated in favour of 'cabal haddock --hyperlink-source'."+ , commandUsage = \pname ->+ "Usage: " ++ pname ++ " hscolour [FLAGS]\n"+ , commandDefaultFlags = defaultHscolourFlags+ , commandOptions = \showOrParseArgs ->+ withCommonSetupOptions+ hscolourCommonFlags+ (\c f -> f{hscolourCommonFlags = c})+ showOrParseArgs+ [ option+ ""+ ["executables"]+ "Run hscolour for Executables targets"+ hscolourExecutables+ (\v flags -> flags{hscolourExecutables = v})+ trueArg+ , option+ ""+ ["tests"]+ "Run hscolour for Test Suite targets"+ hscolourTestSuites+ (\v flags -> flags{hscolourTestSuites = v})+ trueArg+ , option+ ""+ ["benchmarks"]+ "Run hscolour for Benchmark targets"+ hscolourBenchmarks+ (\v flags -> flags{hscolourBenchmarks = v})+ trueArg+ , option+ ""+ ["foreign-libraries"]+ "Run hscolour for Foreign Library targets"+ hscolourForeignLibs+ (\v flags -> flags{hscolourForeignLibs = v})+ trueArg+ , option+ ""+ ["all"]+ "Run hscolour for all targets"+ ( \f ->+ allFlags+ [ hscolourExecutables f+ , hscolourTestSuites f+ , hscolourBenchmarks f+ , hscolourForeignLibs f+ ]+ )+ ( \v flags ->+ flags+ { hscolourExecutables = v+ , hscolourTestSuites = v+ , hscolourBenchmarks = v+ , hscolourForeignLibs = v+ }+ )+ trueArg+ , option+ ""+ ["css"]+ "Use a cascading style sheet"+ hscolourCSS+ (\v flags -> flags{hscolourCSS = v})+ (reqArgFlag "PATH")+ ]+ }
+ src/Distribution/Simple/Setup/Install.hs view
@@ -0,0 +1,183 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ViewPatterns #-}++-- |+-- Module : Distribution.Simple.Setup.Install+-- Copyright : Isaac Jones 2003-2004+-- Duncan Coutts 2007+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Definition of the install command-line options.+-- See: @Distribution.Simple.Setup@+module Distribution.Simple.Setup.Install+ ( InstallFlags+ ( InstallCommonFlags+ , installVerbosity+ , installDistPref+ , installCabalFilePath+ , installWorkingDir+ , installTargets+ , ..+ )+ , emptyInstallFlags+ , defaultInstallFlags+ , installCommand+ ) where++import Distribution.Compat.Prelude hiding (get)+import Prelude ()++import Distribution.ReadE+import Distribution.Simple.Command hiding (boolOpt, boolOpt')+import Distribution.Simple.Compiler+import Distribution.Simple.Flag+import Distribution.Simple.InstallDirs+import Distribution.Simple.Setup.Common+import Distribution.Simple.Utils+import Distribution.Utils.Path+import Distribution.Verbosity++-- ------------------------------------------------------------++-- * Install flags++-- ------------------------------------------------------------++-- | Flags to @install@: (package db, verbosity)+data InstallFlags = InstallFlags+ { installCommonFlags :: !CommonSetupFlags+ , installPackageDB :: Flag PackageDB+ , installDest :: Flag CopyDest+ , installUseWrapper :: Flag Bool+ , installInPlace :: Flag Bool+ }+ deriving (Show, Generic)++pattern InstallCommonFlags+ :: Flag Verbosity+ -> Flag (SymbolicPath Pkg (Dir Dist))+ -> Flag (SymbolicPath CWD (Dir Pkg))+ -> Flag (SymbolicPath Pkg File)+ -> [String]+ -> InstallFlags+pattern InstallCommonFlags+ { installVerbosity+ , installDistPref+ , installWorkingDir+ , installCabalFilePath+ , installTargets+ } <-+ ( installCommonFlags ->+ CommonSetupFlags+ { setupVerbosity = installVerbosity+ , setupDistPref = installDistPref+ , setupWorkingDir = installWorkingDir+ , setupCabalFilePath = installCabalFilePath+ , setupTargets = installTargets+ }+ )++defaultInstallFlags :: InstallFlags+defaultInstallFlags =+ InstallFlags+ { installCommonFlags = defaultCommonSetupFlags+ , installPackageDB = NoFlag+ , installDest = Flag NoCopyDest+ , installUseWrapper = Flag False+ , installInPlace = Flag False+ }++installCommand :: CommandUI InstallFlags+installCommand =+ CommandUI+ { commandName = "install"+ , commandSynopsis =+ "Copy the files into the install locations. Run register."+ , commandDescription = Just $ \_ ->+ wrapText $+ "Unlike the copy command, install calls the register command. "+ ++ "If you want to install into a location that is not what was "+ ++ "specified in the configure step, use the copy command.\n"+ , commandNotes = Nothing+ , commandUsage = \pname ->+ "Usage: " ++ pname ++ " install [FLAGS]\n"+ , commandDefaultFlags = defaultInstallFlags+ , commandOptions = \showOrParseArgs ->+ withCommonSetupOptions+ installCommonFlags+ (\c f -> f{installCommonFlags = c})+ showOrParseArgs+ $ case showOrParseArgs of+ ShowArgs ->+ filter+ ( (`notElem` ["target-package-db"])+ . optionName+ )+ installOptions+ ParseArgs -> installOptions+ }++installOptions :: [OptionField InstallFlags]+installOptions =+ [ option+ ""+ ["inplace"]+ "install the package in the install subdirectory of the dist prefix, so it can be used without being installed"+ installInPlace+ (\v flags -> flags{installInPlace = v})+ trueArg+ , option+ ""+ ["shell-wrappers"]+ "using shell script wrappers around executables"+ installUseWrapper+ (\v flags -> flags{installUseWrapper = v})+ (boolOpt [] [])+ , option+ ""+ ["package-db"]+ ""+ installPackageDB+ (\v flags -> flags{installPackageDB = v})+ ( choiceOpt+ [+ ( Flag UserPackageDB+ , ([], ["user"])+ , "upon configuration register this package in the user's local package database"+ )+ ,+ ( Flag GlobalPackageDB+ , ([], ["global"])+ , "(default) upon configuration register this package in the system-wide package database"+ )+ ]+ )+ , option+ ""+ ["target-package-db"]+ "package database to install into. Required when using ${pkgroot} prefix."+ installDest+ (\v flags -> flags{installDest = v})+ ( reqArg+ "DATABASE"+ (succeedReadE (Flag . CopyToDb))+ (\f -> case f of Flag (CopyToDb p) -> [p]; _ -> [])+ )+ ]++emptyInstallFlags :: InstallFlags+emptyInstallFlags = mempty++instance Monoid InstallFlags where+ mempty = gmempty+ mappend = (<>)++instance Semigroup InstallFlags where+ (<>) = gmappend
+ src/Distribution/Simple/Setup/Register.hs view
@@ -0,0 +1,217 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ViewPatterns #-}++-- |+-- Module : Distribution.Simple.Setup.Register+-- Copyright : Isaac Jones 2003-2004+-- Duncan Coutts 2007+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Definition of the register command-line options.+-- See: @Distribution.Simple.Setup@+module Distribution.Simple.Setup.Register+ ( RegisterFlags+ ( RegisterCommonFlags+ , registerVerbosity+ , registerDistPref+ , registerCabalFilePath+ , registerWorkingDir+ , registerTargets+ , ..+ )+ , emptyRegisterFlags+ , defaultRegisterFlags+ , registerCommand+ , unregisterCommand+ ) where++import Distribution.Compat.Prelude hiding (get)+import Prelude ()++import Distribution.Simple.Command hiding (boolOpt, boolOpt')+import Distribution.Simple.Compiler+import Distribution.Simple.Flag+import Distribution.Simple.Setup.Common+import Distribution.Utils.Path+import Distribution.Verbosity++-- ------------------------------------------------------------++-- * Register flags++-- ------------------------------------------------------------++-- | Flags to @register@ and @unregister@: (user package, gen-script,+-- in-place, verbosity)+data RegisterFlags = RegisterFlags+ { registerCommonFlags :: !CommonSetupFlags+ , regPackageDB :: Flag PackageDB+ , regGenScript :: Flag Bool+ , regGenPkgConf :: Flag (Maybe (SymbolicPath Pkg (Dir PkgConf)))+ , regInPlace :: Flag Bool+ , regPrintId :: Flag Bool+ }+ deriving (Show, Generic)++pattern RegisterCommonFlags+ :: Flag Verbosity+ -> Flag (SymbolicPath Pkg (Dir Dist))+ -> Flag (SymbolicPath CWD (Dir Pkg))+ -> Flag (SymbolicPath Pkg File)+ -> [String]+ -> RegisterFlags+pattern RegisterCommonFlags+ { registerVerbosity+ , registerDistPref+ , registerWorkingDir+ , registerCabalFilePath+ , registerTargets+ } <-+ ( registerCommonFlags ->+ CommonSetupFlags+ { setupVerbosity = registerVerbosity+ , setupDistPref = registerDistPref+ , setupWorkingDir = registerWorkingDir+ , setupCabalFilePath = registerCabalFilePath+ , setupTargets = registerTargets+ }+ )++defaultRegisterFlags :: RegisterFlags+defaultRegisterFlags =+ RegisterFlags+ { registerCommonFlags = defaultCommonSetupFlags+ , regPackageDB = NoFlag+ , regGenScript = Flag False+ , regGenPkgConf = NoFlag+ , regInPlace = Flag False+ , regPrintId = Flag False+ }++registerCommand :: CommandUI RegisterFlags+registerCommand =+ CommandUI+ { commandName = "register"+ , commandSynopsis =+ "Register this package with the compiler."+ , commandDescription = Nothing+ , commandNotes = Nothing+ , commandUsage = \pname ->+ "Usage: " ++ pname ++ " register [FLAGS]\n"+ , commandDefaultFlags = defaultRegisterFlags+ , commandOptions = \showOrParseArgs ->+ withCommonSetupOptions+ registerCommonFlags+ (\c f -> f{registerCommonFlags = c})+ showOrParseArgs+ $ [ option+ ""+ ["packageDB"]+ ""+ regPackageDB+ (\v flags -> flags{regPackageDB = v})+ ( choiceOpt+ [+ ( Flag UserPackageDB+ , ([], ["user"])+ , "upon registration, register this package in the user's local package database"+ )+ ,+ ( Flag GlobalPackageDB+ , ([], ["global"])+ , "(default)upon registration, register this package in the system-wide package database"+ )+ ]+ )+ , option+ ""+ ["inplace"]+ "register the package in the build location, so it can be used without being installed"+ regInPlace+ (\v flags -> flags{regInPlace = v})+ trueArg+ , option+ ""+ ["gen-script"]+ "instead of registering, generate a script to register later"+ regGenScript+ (\v flags -> flags{regGenScript = v})+ trueArg+ , option+ ""+ ["gen-pkg-config"]+ "instead of registering, generate a package registration file/directory"+ regGenPkgConf+ (\v flags -> flags{regGenPkgConf = v})+ (optArg' "PKG" (Flag . fmap makeSymbolicPath) (flagToList . fmap (fmap getSymbolicPath)))+ , option+ ""+ ["print-ipid"]+ "print the installed package ID calculated for this package"+ regPrintId+ (\v flags -> flags{regPrintId = v})+ trueArg+ ]+ }++unregisterCommand :: CommandUI RegisterFlags+unregisterCommand =+ CommandUI+ { commandName = "unregister"+ , commandSynopsis =+ "Unregister this package with the compiler."+ , commandDescription = Nothing+ , commandNotes = Nothing+ , commandUsage = \pname ->+ "Usage: " ++ pname ++ " unregister [FLAGS]\n"+ , commandDefaultFlags = defaultRegisterFlags+ , commandOptions = \showOrParseArgs ->+ withCommonSetupOptions+ registerCommonFlags+ (\c f -> f{registerCommonFlags = c})+ showOrParseArgs+ $ [ option+ ""+ ["user"]+ ""+ regPackageDB+ (\v flags -> flags{regPackageDB = v})+ ( choiceOpt+ [+ ( Flag UserPackageDB+ , ([], ["user"])+ , "unregister this package in the user's local package database"+ )+ ,+ ( Flag GlobalPackageDB+ , ([], ["global"])+ , "(default) unregister this package in the system-wide package database"+ )+ ]+ )+ , option+ ""+ ["gen-script"]+ "Instead of performing the unregister command, generate a script to unregister later"+ regGenScript+ (\v flags -> flags{regGenScript = v})+ trueArg+ ]+ }++emptyRegisterFlags :: RegisterFlags+emptyRegisterFlags = mempty++instance Monoid RegisterFlags where+ mempty = gmempty+ mappend = (<>)++instance Semigroup RegisterFlags where+ (<>) = gmappend
+ src/Distribution/Simple/Setup/Repl.hs view
@@ -0,0 +1,240 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ViewPatterns #-}++-- |+-- Module : Distribution.Simple.Setup.Repl+-- Copyright : Isaac Jones 2003-2004+-- Duncan Coutts 2007+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Definition of the repl command-line options.+-- See: @Distribution.Simple.Setup@+module Distribution.Simple.Setup.Repl+ ( ReplFlags+ ( ReplCommonFlags+ , replVerbosity+ , replDistPref+ , replCabalFilePath+ , replWorkingDir+ , replTargets+ , ..+ )+ , defaultReplFlags+ , replCommand+ , ReplOptions (..)+ , replOptions+ ) where++import Distribution.Compat.Prelude hiding (get)+import Prelude ()++import Distribution.ReadE+import Distribution.Simple.Command hiding (boolOpt, boolOpt')+import Distribution.Simple.Flag+import Distribution.Simple.Program+import Distribution.Simple.Setup.Common+import Distribution.Simple.Utils+import Distribution.Utils.Path+import Distribution.Verbosity++-- ------------------------------------------------------------++-- * REPL Flags++-- ------------------------------------------------------------++data ReplOptions = ReplOptions+ { replOptionsFlags :: [String]+ , replOptionsNoLoad :: Flag Bool+ , replOptionsFlagOutput :: Flag FilePath+ , replWithRepl :: Flag FilePath+ }+ deriving (Show, Generic)++pattern ReplCommonFlags+ :: Flag Verbosity+ -> Flag (SymbolicPath Pkg (Dir Dist))+ -> Flag (SymbolicPath CWD (Dir Pkg))+ -> Flag (SymbolicPath Pkg File)+ -> [String]+ -> ReplFlags+pattern ReplCommonFlags+ { replVerbosity+ , replDistPref+ , replWorkingDir+ , replCabalFilePath+ , replTargets+ } <-+ ( replCommonFlags ->+ CommonSetupFlags+ { setupVerbosity = replVerbosity+ , setupDistPref = replDistPref+ , setupWorkingDir = replWorkingDir+ , setupCabalFilePath = replCabalFilePath+ , setupTargets = replTargets+ }+ )++instance Binary ReplOptions+instance Structured ReplOptions++instance Monoid ReplOptions where+ mempty = ReplOptions mempty (Flag False) NoFlag NoFlag+ mappend = (<>)++instance Semigroup ReplOptions where+ (<>) = gmappend++data ReplFlags = ReplFlags+ { replCommonFlags :: !CommonSetupFlags+ , replProgramPaths :: [(String, FilePath)]+ , replProgramArgs :: [(String, [String])]+ , replReload :: Flag Bool+ , replReplOptions :: ReplOptions+ }+ deriving (Show, Generic)++instance Binary ReplFlags+instance Structured ReplFlags++defaultReplFlags :: ReplFlags+defaultReplFlags =+ ReplFlags+ { replCommonFlags = defaultCommonSetupFlags+ , replProgramPaths = mempty+ , replProgramArgs = []+ , replReload = Flag False+ , replReplOptions = mempty+ }++instance Monoid ReplFlags where+ mempty = gmempty+ mappend = (<>)++instance Semigroup ReplFlags where+ (<>) = gmappend++replCommand :: ProgramDb -> CommandUI ReplFlags+replCommand progDb =+ CommandUI+ { commandName = "repl"+ , commandSynopsis =+ "Open an interpreter session for the given component."+ , commandDescription = Just $ \pname ->+ wrapText $+ "If the current directory contains no package, ignores COMPONENT "+ ++ "parameters and opens an interactive interpreter session.\n"+ ++ "\n"+ ++ "Otherwise, (re)configures with the given or default flags, and "+ ++ "loads the interpreter with the relevant modules. For executables, "+ ++ "tests and benchmarks, loads the main module (and its "+ ++ "dependencies); for libraries all exposed/other modules.\n"+ ++ "\n"+ ++ "The default component is the library itself, or the executable "+ ++ "if that is the only component.\n"+ ++ "\n"+ ++ "Support for loading specific modules is planned but not "+ ++ "implemented yet. For certain scenarios, `"+ ++ pname+ ++ " exec -- ghci :l Foo` may be used instead. Note that `exec` will "+ ++ "not (re)configure and you will have to specify the location of "+ ++ "other modules, if required.\n"+ , commandNotes = Just $ \pname ->+ "Examples:\n"+ ++ " "+ ++ pname+ ++ " repl "+ ++ " The first component in the package\n"+ ++ " "+ ++ pname+ ++ " repl foo "+ ++ " A named component (i.e. lib, exe, test suite)\n"+ ++ " "+ ++ pname+ ++ " repl --repl-options=\"-lstdc++\""+ ++ " Specifying flags for interpreter\n"+ , -- TODO: re-enable once we have support for module/file targets+ -- ++ " " ++ pname ++ " repl Foo.Bar "+ -- ++ " A module\n"+ -- ++ " " ++ pname ++ " repl Foo/Bar.hs"+ -- ++ " A file\n\n"+ -- ++ "If a target is ambiguous it can be qualified with the component "+ -- ++ "name, e.g.\n"+ -- ++ " " ++ pname ++ " repl foo:Foo.Bar\n"+ -- ++ " " ++ pname ++ " repl testsuite1:Foo/Bar.hs\n"+ commandUsage = \pname -> "Usage: " ++ pname ++ " repl [COMPONENT] [FLAGS]\n"+ , commandDefaultFlags = defaultReplFlags+ , commandOptions = \showOrParseArgs ->+ withCommonSetupOptions+ replCommonFlags+ (\c f -> f{replCommonFlags = c})+ showOrParseArgs+ $ programDbPaths+ progDb+ showOrParseArgs+ replProgramPaths+ (\v flags -> flags{replProgramPaths = v})+ ++ programDbOption+ progDb+ showOrParseArgs+ replProgramArgs+ (\v flags -> flags{replProgramArgs = v})+ ++ programDbOptions+ progDb+ showOrParseArgs+ replProgramArgs+ (\v flags -> flags{replProgramArgs = v})+ ++ case showOrParseArgs of+ ParseArgs ->+ [ option+ ""+ ["reload"]+ "Used from within an interpreter to update files."+ replReload+ (\v flags -> flags{replReload = v})+ trueArg+ ]+ _ -> []+ ++ map liftReplOption (replOptions showOrParseArgs)+ }+ where+ liftReplOption = liftOption replReplOptions (\v flags -> flags{replReplOptions = v})++replOptions :: ShowOrParseArgs -> [OptionField ReplOptions]+replOptions _ =+ [ option+ []+ ["repl-no-load"]+ "Disable loading of project modules at REPL startup."+ replOptionsNoLoad+ (\p flags -> flags{replOptionsNoLoad = p})+ trueArg+ , option+ []+ ["repl-options"]+ "Use the option(s) for the repl"+ replOptionsFlags+ (\p flags -> flags{replOptionsFlags = p})+ (reqArg "FLAG" (succeedReadE words) id)+ , option+ []+ ["repl-multi-file"]+ "Write repl options to this directory rather than starting repl mode"+ replOptionsFlagOutput+ (\p flags -> flags{replOptionsFlagOutput = p})+ (reqArg "DIR" (succeedReadE Flag) flagToList)+ , option+ []+ ["with-repl"]+ "Give the path to a program to use for REPL"+ replWithRepl+ (\v flags -> flags{replWithRepl = v})+ (reqArgFlag "PATH")+ ]
+ src/Distribution/Simple/Setup/SDist.hs view
@@ -0,0 +1,141 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ViewPatterns #-}++-- |+-- Module : Distribution.Simple.Setup.SDist+-- Copyright : Isaac Jones 2003-2004+-- Duncan Coutts 2007+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Definition of the sdist command-line options.+-- See: @Distribution.Simple.Setup@+module Distribution.Simple.Setup.SDist+ ( SDistFlags+ ( SDistCommonFlags+ , sDistVerbosity+ , sDistDistPref+ , sDistCabalFilePath+ , sDistWorkingDir+ , sDistTargets+ , ..+ )+ , emptySDistFlags+ , defaultSDistFlags+ , sdistCommand+ ) where++import Distribution.Compat.Prelude hiding (get)+import Prelude ()++import Distribution.Simple.Command hiding (boolOpt, boolOpt')+import Distribution.Simple.Flag+import Distribution.Simple.Setup.Common+import Distribution.Utils.Path+import Distribution.Verbosity++-- ------------------------------------------------------------++-- * SDist flags++-- ------------------------------------------------------------++-- | Flags to @sdist@: (snapshot, verbosity)+data SDistFlags = SDistFlags+ { sDistCommonFlags :: !CommonSetupFlags+ , sDistSnapshot :: Flag Bool+ , sDistDirectory :: Flag FilePath+ , sDistListSources :: Flag FilePath+ }+ deriving (Show, Generic)++pattern SDistCommonFlags+ :: Flag Verbosity+ -> Flag (SymbolicPath Pkg (Dir Dist))+ -> Flag (SymbolicPath CWD (Dir Pkg))+ -> Flag (SymbolicPath Pkg File)+ -> [String]+ -> SDistFlags+pattern SDistCommonFlags+ { sDistVerbosity+ , sDistDistPref+ , sDistWorkingDir+ , sDistCabalFilePath+ , sDistTargets+ } <-+ ( sDistCommonFlags ->+ CommonSetupFlags+ { setupVerbosity = sDistVerbosity+ , setupDistPref = sDistDistPref+ , setupWorkingDir = sDistWorkingDir+ , setupCabalFilePath = sDistCabalFilePath+ , setupTargets = sDistTargets+ }+ )++defaultSDistFlags :: SDistFlags+defaultSDistFlags =+ SDistFlags+ { sDistCommonFlags = defaultCommonSetupFlags+ , sDistSnapshot = Flag False+ , sDistDirectory = mempty+ , sDistListSources = mempty+ }++sdistCommand :: CommandUI SDistFlags+sdistCommand =+ CommandUI+ { commandName = "sdist"+ , commandSynopsis =+ "Generate a source distribution file (.tar.gz)."+ , commandDescription = Nothing+ , commandNotes = Nothing+ , commandUsage = \pname ->+ "Usage: " ++ pname ++ " sdist [FLAGS]\n"+ , commandDefaultFlags = defaultSDistFlags+ , commandOptions = \showOrParseArgs ->+ withCommonSetupOptions+ sDistCommonFlags+ (\c f -> f{sDistCommonFlags = c})+ showOrParseArgs+ [ option+ ""+ ["list-sources"]+ "Just write a list of the package's sources to a file"+ sDistListSources+ (\v flags -> flags{sDistListSources = v})+ (reqArgFlag "FILE")+ , option+ ""+ ["snapshot"]+ "Produce a snapshot source distribution"+ sDistSnapshot+ (\v flags -> flags{sDistSnapshot = v})+ trueArg+ , option+ ""+ ["output-directory"]+ ( "Generate a source distribution in the given directory, "+ ++ "without creating a tarball"+ )+ sDistDirectory+ (\v flags -> flags{sDistDirectory = v})+ (reqArgFlag "DIR")+ ]+ }++emptySDistFlags :: SDistFlags+emptySDistFlags = mempty++instance Monoid SDistFlags where+ mempty = gmempty+ mappend = (<>)++instance Semigroup SDistFlags where+ (<>) = gmappend
+ src/Distribution/Simple/Setup/Test.hs view
@@ -0,0 +1,284 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ViewPatterns #-}++-- |+-- Module : Distribution.Simple.Test+-- Copyright : Isaac Jones 2003-2004+-- Duncan Coutts 2007+-- License : BSD3+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- Definition of the testing command-line options.+-- See: @Distribution.Simple.Setup@+module Distribution.Simple.Setup.Test+ ( TestFlags+ ( TestCommonFlags+ , testVerbosity+ , testDistPref+ , testCabalFilePath+ , testWorkingDir+ , testTargets+ , ..+ )+ , emptyTestFlags+ , defaultTestFlags+ , testCommand+ , TestShowDetails (..)+ , testOptions'+ ) where++import Distribution.Compat.Prelude hiding (get)+import Prelude ()++import qualified Distribution.Compat.CharParsing as P+import Distribution.Parsec+import Distribution.Pretty+import Distribution.ReadE+import Distribution.Simple.Command hiding (boolOpt, boolOpt')+import Distribution.Simple.Flag+import Distribution.Simple.InstallDirs+import Distribution.Simple.Setup.Common+import Distribution.Simple.Utils+import Distribution.Utils.Path+import Distribution.Verbosity++import qualified Text.PrettyPrint as Disp++-- ------------------------------------------------------------++-- * Test flags++-- ------------------------------------------------------------++data TestShowDetails = Never | Failures | Always | Streaming | Direct+ deriving (Eq, Ord, Enum, Bounded, Generic, Show)++instance Binary TestShowDetails+instance Structured TestShowDetails++knownTestShowDetails :: [TestShowDetails]+knownTestShowDetails = [minBound .. maxBound]++instance Pretty TestShowDetails where+ pretty = Disp.text . lowercase . show++instance Parsec TestShowDetails where+ parsec = maybe (fail "invalid TestShowDetails") return . classify =<< ident+ where+ ident = P.munch1 (\c -> isAlpha c || c == '_' || c == '-')+ classify str = lookup (lowercase str) enumMap+ enumMap :: [(String, TestShowDetails)]+ enumMap =+ [ (prettyShow x, x)+ | x <- knownTestShowDetails+ ]++-- TODO: do we need this instance?+instance Monoid TestShowDetails where+ mempty = Never+ mappend = (<>)++instance Semigroup TestShowDetails where+ a <> b = if a < b then b else a++data TestFlags = TestFlags+ { testCommonFlags :: !CommonSetupFlags+ , testHumanLog :: Flag PathTemplate+ , testMachineLog :: Flag PathTemplate+ , testShowDetails :: Flag TestShowDetails+ , testKeepTix :: Flag Bool+ , testWrapper :: Flag FilePath+ , testFailWhenNoTestSuites :: Flag Bool+ , -- TODO: think about if/how options are passed to test exes+ testOptions :: [PathTemplate]+ }+ deriving (Show, Generic)++pattern TestCommonFlags+ :: Flag Verbosity+ -> Flag (SymbolicPath Pkg (Dir Dist))+ -> Flag (SymbolicPath CWD (Dir Pkg))+ -> Flag (SymbolicPath Pkg File)+ -> [String]+ -> TestFlags+pattern TestCommonFlags+ { testVerbosity+ , testDistPref+ , testWorkingDir+ , testCabalFilePath+ , testTargets+ } <-+ ( testCommonFlags ->+ CommonSetupFlags+ { setupVerbosity = testVerbosity+ , setupDistPref = testDistPref+ , setupWorkingDir = testWorkingDir+ , setupCabalFilePath = testCabalFilePath+ , setupTargets = testTargets+ }+ )++instance Binary TestFlags+instance Structured TestFlags++defaultTestFlags :: TestFlags+defaultTestFlags =+ TestFlags+ { testCommonFlags = defaultCommonSetupFlags+ , testHumanLog = toFlag $ toPathTemplate $ "$pkgid-$test-suite.log"+ , testMachineLog = toFlag $ toPathTemplate $ "$pkgid.log"+ , testShowDetails = toFlag Direct+ , testKeepTix = toFlag False+ , testWrapper = NoFlag+ , testFailWhenNoTestSuites = toFlag False+ , testOptions = []+ }++testCommand :: CommandUI TestFlags+testCommand =+ CommandUI+ { commandName = "test"+ , commandSynopsis =+ "Run all/specific tests in the test suite."+ , commandDescription = Just $ \_pname ->+ wrapText $+ testOrBenchmarkHelpText "test"+ , commandNotes = Nothing+ , commandUsage =+ usageAlternatives+ "test"+ [ "[FLAGS]"+ , "TESTCOMPONENTS [FLAGS]"+ ]+ , commandDefaultFlags = defaultTestFlags+ , commandOptions = testOptions'+ }++testOptions' :: ShowOrParseArgs -> [OptionField TestFlags]+testOptions' showOrParseArgs =+ withCommonSetupOptions+ testCommonFlags+ (\c f -> f{testCommonFlags = c})+ showOrParseArgs+ [ option+ []+ ["log"]+ ( "Log all test suite results to file (name template can use "+ ++ "$pkgid, $compiler, $os, $arch, $test-suite, $result)"+ )+ testHumanLog+ (\v flags -> flags{testHumanLog = v})+ ( reqArg'+ "TEMPLATE"+ (toFlag . toPathTemplate)+ (flagToList . fmap fromPathTemplate)+ )+ , option+ []+ ["machine-log"]+ ( "Produce a machine-readable log file (name template can use "+ ++ "$pkgid, $compiler, $os, $arch, $result)"+ )+ testMachineLog+ (\v flags -> flags{testMachineLog = v})+ ( reqArg'+ "TEMPLATE"+ (toFlag . toPathTemplate)+ (flagToList . fmap fromPathTemplate)+ )+ , option+ []+ ["show-details"]+ ( "'always': always show results of individual test cases. "+ ++ "'never': never show results of individual test cases. "+ ++ "'failures': show results of failing test cases. "+ ++ "'streaming': show results of test cases in real time."+ ++ "'direct': send results of test cases in real time; no log file."+ )+ testShowDetails+ (\v flags -> flags{testShowDetails = v})+ ( reqArg+ "FILTER"+ ( parsecToReadE+ ( \_ ->+ "--show-details flag expects one of "+ ++ intercalate+ ", "+ (map prettyShow knownTestShowDetails)+ )+ (fmap toFlag parsec)+ )+ (flagToList . fmap prettyShow)+ )+ , option+ []+ ["keep-tix-files"]+ "keep .tix files for HPC between test runs"+ testKeepTix+ (\v flags -> flags{testKeepTix = v})+ trueArg+ , option+ []+ ["test-wrapper"]+ "Run test through a wrapper."+ testWrapper+ (\v flags -> flags{testWrapper = v})+ ( reqArg'+ "FILE"+ (toFlag :: FilePath -> Flag FilePath)+ (flagToList :: Flag FilePath -> [FilePath])+ )+ , option+ []+ ["fail-when-no-test-suites"]+ ("Exit with failure when no test suites are found.")+ testFailWhenNoTestSuites+ (\v flags -> flags{testFailWhenNoTestSuites = v})+ trueArg+ , option+ []+ ["test-options"]+ ( "give extra options to test executables "+ ++ "(split on spaces, use \"\" to prevent splitting; "+ ++ "name templates can use $pkgid, $compiler, "+ ++ "$os, $arch, $test-suite)"+ )+ testOptions+ (\v flags -> flags{testOptions = v})+ ( reqArg'+ "TEMPLATES"+ (map toPathTemplate . splitArgs)+ (const [])+ )+ , option+ []+ ["test-option"]+ ( "give extra option to test executables "+ ++ "(passed directly as a single argument; "+ ++ "name template can use $pkgid, $compiler, "+ ++ "$os, $arch, $test-suite)"+ )+ testOptions+ (\v flags -> flags{testOptions = v})+ ( reqArg'+ "TEMPLATE"+ (\x -> [toPathTemplate x])+ (map fromPathTemplate)+ )+ ]++emptyTestFlags :: TestFlags+emptyTestFlags = mempty++instance Monoid TestFlags where+ mempty = gmempty+ mappend = (<>)++instance Semigroup TestFlags where+ (<>) = gmappend
+ src/Distribution/Simple/SetupHooks/Errors.hs view
@@ -0,0 +1,231 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE StandaloneDeriving #-}++-----------------------------------------------------------------------------++-- Module : Distribution.Simple.SetupHooks.Errors+-- Copyright :+-- License :+--+-- Maintainer :+-- Portability :+--+-- Exceptions for the Hooks build-type.++module Distribution.Simple.SetupHooks.Errors+ ( SetupHooksException (..)+ , CannotApplyComponentDiffReason (..)+ , IllegalComponentDiffReason (..)+ , RulesException (..)+ , setupHooksExceptionCode+ , setupHooksExceptionMessage+ ) where++import Distribution.PackageDescription+import Distribution.Simple.SetupHooks.Rule+import qualified Distribution.Simple.SetupHooks.Rule as Rule+import Distribution.Types.Component++import qualified Data.Graph as Graph+import Data.List+ ( intercalate+ )+import qualified Data.List.NonEmpty as NE+import qualified Data.Tree as Tree++--------------------------------------------------------------------------------++-- | An error involving the @SetupHooks@ module of a package with+-- Hooks build-type.+data SetupHooksException+ = -- | Cannot apply a diff to a component in a per-component configure hook.+ CannotApplyComponentDiff CannotApplyComponentDiffReason+ | -- | An error with pre-build rules.+ RulesException RulesException+ deriving (Show)++-- | AN error involving the @Rules@ in the @SetupHooks@ module of a+-- package with the Hooks build-type.+data RulesException+ = -- | There are cycles in the dependency graph of fine-grained rules.+ CyclicRuleDependencies+ (NE.NonEmpty (RuleBinary, [Graph.Tree RuleBinary]))+ | -- | When executing fine-grained rules compiled into the external hooks+ -- executable, we failed to find dependencies of a rule.+ CantFindSourceForRuleDependencies+ RuleBinary+ (NE.NonEmpty Rule.Location)+ -- ^ missing dependencies+ | -- | When executing fine-grained rules compiled into the external hooks+ -- executable, a rule failed to generate the outputs it claimed it would.+ MissingRuleOutputs+ RuleBinary+ (NE.NonEmpty Rule.Location)+ -- ^ missing outputs+ | -- | An invalid reference to a rule output, e.g. an out-of-range+ -- index.+ InvalidRuleOutputIndex+ RuleId+ -- ^ rule+ RuleId+ -- ^ dependency+ (NE.NonEmpty Rule.Location)+ -- ^ outputs of dependency+ Word+ -- ^ the invalid index+ | -- | A duplicate 'RuleId' in the construction of pre-build rules.+ DuplicateRuleId !RuleId !Rule !Rule++deriving instance Show RulesException++data CannotApplyComponentDiffReason+ = MismatchedComponentTypes Component Component+ | IllegalComponentDiff Component (NE.NonEmpty IllegalComponentDiffReason)+ deriving (Show)++data IllegalComponentDiffReason+ = CannotChangeName+ | CannotChangeComponentField String+ | CannotChangeBuildInfoField String+ deriving (Show)++setupHooksExceptionCode :: SetupHooksException -> Int+setupHooksExceptionCode = \case+ CannotApplyComponentDiff rea ->+ cannotApplyComponentDiffCode rea+ RulesException rea ->+ rulesExceptionCode rea++rulesExceptionCode :: RulesException -> Int+rulesExceptionCode = \case+ CyclicRuleDependencies{} -> 9077+ CantFindSourceForRuleDependencies{} -> 1071+ MissingRuleOutputs{} -> 3498+ InvalidRuleOutputIndex{} -> 1173+ DuplicateRuleId{} -> 7717++setupHooksExceptionMessage :: SetupHooksException -> String+setupHooksExceptionMessage = \case+ CannotApplyComponentDiff reason ->+ cannotApplyComponentDiffMessage reason+ RulesException reason ->+ rulesExceptionMessage reason++rulesExceptionMessage :: RulesException -> String+rulesExceptionMessage = \case+ CyclicRuleDependencies cycles ->+ unlines $+ ("Hooks: cycle" ++ plural ++ " in dependency structure of rules:")+ : map showCycle (NE.toList cycles)+ where+ plural :: String+ plural+ | NE.length cycles >= 2 =+ "s"+ | otherwise =+ ""+ showCycle :: (RuleBinary, [Graph.Tree RuleBinary]) -> String+ showCycle (r, rs) =+ unlines . map (" " ++) . lines $+ Tree.drawTree $+ fmap showRule $+ Tree.Node r rs+ CantFindSourceForRuleDependencies _r deps ->+ unlines $+ ("Pre-build rules: can't find source for rule " ++ what ++ ":")+ : map (\d -> " - " <> show d) depsL+ where+ depsL = NE.toList deps+ what+ | length depsL == 1 =+ "dependency"+ | otherwise =+ "dependencies"+ MissingRuleOutputs _r reslts ->+ unlines $+ ("Pre-build rule did not generate expected result" <> plural <> ":")+ : map (\res -> " - " <> show res) resultsL+ where+ resultsL = NE.toList reslts+ plural+ | length resultsL == 1 =+ ""+ | otherwise =+ "s"+ InvalidRuleOutputIndex rId depRuleId outputs i -> unlines [header, body]+ where+ header = "Invalid index '" ++ show i ++ "' in dependency of " ++ show rId ++ "."+ nbOutputs = NE.length outputs+ body+ | (fromIntegral i :: Int) >= 0 =+ unwords+ [ "The dependency"+ , show depRuleId+ , "only has"+ , show nbOutputs+ , "output" ++ plural ++ "."+ ]+ | otherwise =+ "The index is too large."+ plural = if nbOutputs == 1 then "" else "s"+ DuplicateRuleId rId r1 r2 ->+ unlines $+ [ "Duplicate pre-build rule (" <> show rId <> ")"+ , " - " <> showRule (ruleBinary r1)+ , " - " <> showRule (ruleBinary r2)+ ]+ where+ showRule :: RuleBinary -> String+ showRule (Rule{staticDependencies = deps, results = reslts}) =+ "Rule: " ++ showDeps deps ++ " --> " ++ show (NE.toList reslts)++showDeps :: [Rule.Dependency] -> String+showDeps deps = "[" ++ intercalate ", " (map showDep deps) ++ "]"++showDep :: Rule.Dependency -> String+showDep = \case+ RuleDependency (RuleOutput{outputOfRule = rId, outputIndex = i}) ->+ "(" ++ show rId ++ ")[" ++ show i ++ "]"+ FileDependency loc -> show loc++cannotApplyComponentDiffCode :: CannotApplyComponentDiffReason -> Int+cannotApplyComponentDiffCode = \case+ MismatchedComponentTypes{} -> 9491+ IllegalComponentDiff{} -> 7634++cannotApplyComponentDiffMessage :: CannotApplyComponentDiffReason -> String+cannotApplyComponentDiffMessage = \case+ MismatchedComponentTypes comp diff ->+ unlines+ [ "Hooks: mismatched component types in per-component configure hook."+ , "Trying to apply " ++ what ++ " diff to " ++ to ++ "."+ ]+ where+ what = case diff of+ CLib{} -> "a library"+ CFLib{} -> "a foreign library"+ CExe{} -> "an executable"+ CTest{} -> "a testsuite"+ CBench{} -> "a benchmark"+ to = case componentName comp of+ nm@(CExeName{}) -> "an " ++ showComponentName nm+ nm -> "a " ++ showComponentName nm+ IllegalComponentDiff comp reasons ->+ unlines $+ ("Hooks: illegal component diff in per-component pre-configure hook for " ++ what ++ ":")+ : map mk_rea (NE.toList reasons)+ where+ mk_rea err = " - " ++ illegalComponentDiffMessage err ++ "."+ what = case componentName comp of+ CLibName LMainLibName -> "main library"+ nm -> showComponentName nm++illegalComponentDiffMessage :: IllegalComponentDiffReason -> String+illegalComponentDiffMessage = \case+ CannotChangeName ->+ "cannot change the name of a component"+ CannotChangeComponentField fld ->+ "cannot change component field '" ++ fld ++ "'"+ CannotChangeBuildInfoField fld ->+ "cannot change BuildInfo field '" ++ fld ++ "'"
+ src/Distribution/Simple/SetupHooks/Internal.hs view
@@ -0,0 +1,1095 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeApplications #-}++-- |+-- Module: Distribution.Simple.SetupHooks.Internal+--+-- Internal implementation module.+-- Users of @build-type: Hooks@ should import "Distribution.Simple.SetupHooks"+-- instead.+module Distribution.Simple.SetupHooks.Internal+ ( -- * The setup hooks datatype+ SetupHooks (..)+ , noSetupHooks++ -- * Configure hooks+ , ConfigureHooks (..)+ , noConfigureHooks++ -- ** Per-package configure hooks+ , PreConfPackageInputs (..)+ , PreConfPackageOutputs (..)+ , noPreConfPackageOutputs+ , PreConfPackageHook+ , PostConfPackageInputs (..)+ , PostConfPackageHook++ -- ** Per-component configure hooks+ , PreConfComponentInputs (..)+ , PreConfComponentOutputs (..)+ , noPreConfComponentOutputs+ , PreConfComponentHook+ , ComponentDiff (..)+ , emptyComponentDiff+ , buildInfoComponentDiff+ , LibraryDiff+ , ForeignLibDiff+ , ExecutableDiff+ , TestSuiteDiff+ , BenchmarkDiff+ , BuildInfoDiff++ -- * Build hooks+ , BuildHooks (..)+ , noBuildHooks+ , BuildingWhat (..)+ , buildingWhatVerbosity+ , buildingWhatWorkingDir+ , buildingWhatDistPref++ -- ** Pre-build rules+ , PreBuildComponentInputs (..)+ , PreBuildComponentRules++ -- ** Post-build hook+ , PostBuildComponentInputs (..)+ , PostBuildComponentHook++ -- * Install hooks+ , InstallHooks (..)+ , noInstallHooks+ , InstallComponentInputs (..)+ , InstallComponentHook++ -- * Internals++ -- ** Per-component hook utilities+ , applyComponentDiffs+ , forComponents_++ -- ** Executing build rules+ , executeRules++ -- ** HookedBuildInfo compatibility code+ , hookedBuildInfoComponents+ , hookedBuildInfoComponentDiff_maybe+ )+where++import Distribution.Compat.Prelude+import Prelude ()++import Distribution.Compat.Lens ((.~))+import Distribution.PackageDescription+import Distribution.Simple.BuildPaths+import Distribution.Simple.Compiler (Compiler (..))+import Distribution.Simple.Errors+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.Program.Db+import Distribution.Simple.Setup+ ( BuildingWhat (..)+ , buildingWhatDistPref+ , buildingWhatVerbosity+ , buildingWhatWorkingDir+ )+import Distribution.Simple.Setup.Build (BuildFlags (..))+import Distribution.Simple.Setup.Config (ConfigFlags (..))+import Distribution.Simple.Setup.Copy (CopyFlags (..))+import Distribution.Simple.SetupHooks.Errors+import Distribution.Simple.SetupHooks.Rule+import qualified Distribution.Simple.SetupHooks.Rule as Rule+import Distribution.Simple.Utils+import Distribution.System (Platform (..))+import Distribution.Utils.Path++import qualified Distribution.Types.BuildInfo.Lens as BI (buildInfo)+import Distribution.Types.LocalBuildConfig as LBC+import Distribution.Types.TargetInfo+import Distribution.Verbosity++import qualified Data.ByteString.Lazy as LBS+import Data.Coerce (coerce)+import qualified Data.Graph as Graph+import qualified Data.List.NonEmpty as NE+import qualified Data.Map as Map+import qualified Data.Set as Set++import System.Directory (doesFileExist)++--------------------------------------------------------------------------------+-- SetupHooks++-- | Hooks into the @cabal@ build phases.+--+-- Usage:+--+-- - In your @.cabal@ file, declare @build-type: Hooks@+-- (with a @cabal-version@ greater than or equal to @3.14@),+-- - In your @.cabal@ file, include a @custom-setup@ stanza+-- which declares the dependencies of your @SetupHooks@ module;+-- this will usually contain a dependency on the @Cabal-hooks@ package.+-- - Provide a @SetupHooks.hs@ module next to your @.cabal@ file;+-- it must export @setupHooks :: SetupHooks@.+data SetupHooks = SetupHooks+ { configureHooks :: ConfigureHooks+ -- ^ Hooks into the configure phase.+ , buildHooks :: BuildHooks+ -- ^ Hooks into the build phase.+ --+ -- These hooks are relevant to any build-like phase,+ -- such as repl or haddock.+ , installHooks :: InstallHooks+ -- ^ Hooks into the copy/install phase.+ }++-- | 'SetupHooks' can be combined monoidally. This is useful to combine+-- setup hooks defined by another package with your own package-specific+-- hooks.+--+-- __Warning__: this 'Semigroup' instance is not commutative.+instance Semigroup SetupHooks where+ SetupHooks+ { configureHooks = conf1+ , buildHooks = build1+ , installHooks = inst1+ }+ <> SetupHooks+ { configureHooks = conf2+ , buildHooks = build2+ , installHooks = inst2+ } =+ SetupHooks+ { configureHooks = conf1 <> conf2+ , buildHooks = build1 <> build2+ , installHooks = inst1 <> inst2+ }++instance Monoid SetupHooks where+ mempty = noSetupHooks++-- | Empty hooks.+noSetupHooks :: SetupHooks+noSetupHooks =+ SetupHooks+ { configureHooks = noConfigureHooks+ , buildHooks = noBuildHooks+ , installHooks = noInstallHooks+ }++--------------------------------------------------------------------------------+-- Configure hooks.++type PreConfPackageHook = PreConfPackageInputs -> IO PreConfPackageOutputs++-- | Inputs to the package-wide pre-configure step.+data PreConfPackageInputs = PreConfPackageInputs+ { configFlags :: ConfigFlags+ , localBuildConfig :: LocalBuildConfig+ -- ^ Warning: the 'ProgramDb' in the 'withPrograms' field+ -- will not contain any unconfigured programs.+ , compiler :: Compiler+ , platform :: Platform+ }+ deriving (Generic, Show)++-- | Outputs of the package-wide pre-configure step.+--+-- Prefer using 'noPreConfPackageOutputs' and overriding the fields+-- you care about, to avoid depending on implementation details+-- of this datatype.+data PreConfPackageOutputs = PreConfPackageOutputs+ { buildOptions :: BuildOptions+ , extraConfiguredProgs :: ConfiguredProgs+ }+ deriving (Generic, Show)++-- | Use this smart constructor to declare an empty set of changes+-- by the package-wide pre-configure hook, and override the fields you+-- care about.+--+-- Use this rather than v'PreConfPackageOutputs' to avoid relying on+-- internal implementation details of the latter.+noPreConfPackageOutputs :: PreConfPackageInputs -> PreConfPackageOutputs+noPreConfPackageOutputs (PreConfPackageInputs{localBuildConfig = lbc}) =+ PreConfPackageOutputs+ { buildOptions = LBC.withBuildOptions lbc+ , extraConfiguredProgs = Map.empty+ }++-- | Package-wide post-configure step.+--+-- Perform side effects. Last opportunity for any package-wide logic;+-- any subsequent hooks work per-component.+type PostConfPackageHook = PostConfPackageInputs -> IO ()++-- | Inputs to the package-wide post-configure step.+data PostConfPackageInputs = PostConfPackageInputs+ { localBuildConfig :: LocalBuildConfig+ , packageBuildDescr :: PackageBuildDescr+ }+ deriving (Generic, Show)++-- | Per-component pre-configure step.+--+-- For each component of the package, this hook can perform side effects,+-- and return a diff to the passed in component, e.g. to declare additional+-- autogenerated modules.+type PreConfComponentHook = PreConfComponentInputs -> IO PreConfComponentOutputs++-- | Inputs to the per-component pre-configure step.+data PreConfComponentInputs = PreConfComponentInputs+ { localBuildConfig :: LocalBuildConfig+ , packageBuildDescr :: PackageBuildDescr+ , component :: Component+ }+ deriving (Generic, Show)++-- | Outputs of the per-component pre-configure step.+--+-- Prefer using 'noPreComponentOutputs' and overriding the fields+-- you care about, to avoid depending on implementation details+-- of this datatype.+data PreConfComponentOutputs = PreConfComponentOutputs+ { componentDiff :: ComponentDiff+ }+ deriving (Generic, Show)++-- | Use this smart constructor to declare an empty set of changes+-- by a per-component pre-configure hook, and override the fields you+-- care about.+--+-- Use this rather than v'PreConfComponentOutputs' to avoid relying on+-- internal implementation details of the latter.+noPreConfComponentOutputs :: PreConfComponentInputs -> PreConfComponentOutputs+noPreConfComponentOutputs (PreConfComponentInputs{component = comp}) =+ PreConfComponentOutputs+ { componentDiff = emptyComponentDiff (componentName comp)+ }++-- | Configure-time hooks.+--+-- Order of execution:+--+-- - 'preConfPackageHook',+-- - configure the package,+-- - 'postConfPackageHook',+-- - 'preConfComponentHook',+-- - configure the components.+data ConfigureHooks = ConfigureHooks+ { preConfPackageHook :: Maybe PreConfPackageHook+ -- ^ Package-wide pre-configure hook. See 'PreConfPackageHook'.+ , postConfPackageHook :: Maybe PostConfPackageHook+ -- ^ Package-wide post-configure hook. See 'PostConfPackageHook'.+ , preConfComponentHook :: Maybe PreConfComponentHook+ -- ^ Per-component pre-configure hook. See 'PreConfComponentHook'.+ }++-- Note: these configure hooks don't track any kind of dependency information,+-- so we won't know when the configuration is out of date and should be re-done.+-- This seems okay: it should only matter while developing the package, in which+-- case it seems acceptable to rely on the user re-configuring.++instance Semigroup ConfigureHooks where+ ConfigureHooks+ { preConfPackageHook = prePkg1+ , postConfPackageHook = postPkg1+ , preConfComponentHook = preComp1+ }+ <> ConfigureHooks+ { preConfPackageHook = prePkg2+ , postConfPackageHook = postPkg2+ , preConfComponentHook = preComp2+ } =+ ConfigureHooks+ { preConfPackageHook =+ coerce+ ((<>) @(Maybe PreConfPkgSemigroup))+ prePkg1+ prePkg2+ , postConfPackageHook =+ postPkg1 <> postPkg2+ , preConfComponentHook =+ coerce+ ((<>) @(Maybe PreConfComponentSemigroup))+ preComp1+ preComp2+ }++instance Monoid ConfigureHooks where+ mempty = noConfigureHooks++-- | Empty configure phase hooks.+noConfigureHooks :: ConfigureHooks+noConfigureHooks =+ ConfigureHooks+ { preConfPackageHook = Nothing+ , postConfPackageHook = Nothing+ , preConfComponentHook = Nothing+ }++-- | A newtype to hang off the @Semigroup PreConfPackageHook@ instance.+newtype PreConfPkgSemigroup = PreConfPkgSemigroup PreConfPackageHook++instance Semigroup PreConfPkgSemigroup where+ PreConfPkgSemigroup f1 <> PreConfPkgSemigroup f2 =+ PreConfPkgSemigroup $+ \inputs@( PreConfPackageInputs+ { configFlags = cfg+ , compiler = comp+ , platform = plat+ , localBuildConfig = lbc0+ }+ ) ->+ do+ PreConfPackageOutputs+ { buildOptions = opts1+ , extraConfiguredProgs = progs1+ } <-+ f1 inputs+ PreConfPackageOutputs+ { buildOptions = opts2+ , extraConfiguredProgs = progs2+ } <-+ f2 $+ PreConfPackageInputs+ { configFlags = cfg+ , compiler = comp+ , platform = plat+ , localBuildConfig =+ lbc0+ { LBC.withPrograms =+ updateConfiguredProgs (`Map.union` progs1) $+ LBC.withPrograms lbc0+ , LBC.withBuildOptions = opts1+ }+ }+ return $+ PreConfPackageOutputs+ { buildOptions = opts2+ , extraConfiguredProgs = progs1 <> progs2+ }++-- | A newtype to hang off the @Semigroup PreConfComponentHook@ instance.+newtype PreConfComponentSemigroup = PreConfComponentSemigroup PreConfComponentHook++instance Semigroup PreConfComponentSemigroup where+ PreConfComponentSemigroup f1 <> PreConfComponentSemigroup f2 =+ PreConfComponentSemigroup $ \inputs ->+ do+ PreConfComponentOutputs+ { componentDiff = diff1+ } <-+ f1 inputs+ PreConfComponentOutputs+ { componentDiff = diff2+ } <-+ f2 inputs+ return $+ PreConfComponentOutputs+ { componentDiff = diff1 <> diff2+ }++--------------------------------------------------------------------------------+-- Build setup hooks.++data PreBuildComponentInputs = PreBuildComponentInputs+ { buildingWhat :: BuildingWhat+ -- ^ what kind of build phase are we hooking into?+ , localBuildInfo :: LocalBuildInfo+ -- ^ information about the package+ , targetInfo :: TargetInfo+ -- ^ information about an individual component+ }+ deriving (Generic, Show)++type PreBuildComponentRules = Rules PreBuildComponentInputs++data PostBuildComponentInputs = PostBuildComponentInputs+ { buildFlags :: BuildFlags+ , localBuildInfo :: LocalBuildInfo+ , targetInfo :: TargetInfo+ }+ deriving (Generic, Show)++type PostBuildComponentHook = PostBuildComponentInputs -> IO ()++-- | Build-time hooks.+data BuildHooks = BuildHooks+ { preBuildComponentRules :: Maybe PreBuildComponentRules+ -- ^ Per-component fine-grained pre-build rules.+ , postBuildComponentHook :: Maybe PostBuildComponentHook+ -- ^ Per-component post-build hook.+ }++-- Note that the pre-build hook consists of a function which takes a component+-- as an argument (as part of the targetInfo field) and returns a collection of+-- pre-build rules.+--+-- One might wonder why it isn't instead a collection of pre-build rules, one+-- for each component. The reason is that Backpack creates components on-the-fly+-- through instantiation, which means e.g. that a single component name can+-- resolve to multiple components. This means we really need to pass in the+-- components to the function, as we don't know the full details (e.g. their+-- unit ids) ahead of time.++instance Semigroup BuildHooks where+ BuildHooks+ { preBuildComponentRules = rs1+ , postBuildComponentHook = post1+ }+ <> BuildHooks+ { preBuildComponentRules = rs2+ , postBuildComponentHook = post2+ } =+ BuildHooks+ { preBuildComponentRules = rs1 <> rs2+ , postBuildComponentHook = post1 <> post2+ }++instance Monoid BuildHooks where+ mempty = noBuildHooks++-- | Empty build hooks.+noBuildHooks :: BuildHooks+noBuildHooks =+ BuildHooks+ { preBuildComponentRules = Nothing+ , postBuildComponentHook = Nothing+ }++--------------------------------------------------------------------------------+-- Install setup hooks.++data InstallComponentInputs = InstallComponentInputs+ { copyFlags :: CopyFlags+ , localBuildInfo :: LocalBuildInfo+ , targetInfo :: TargetInfo+ }+ deriving (Generic, Show)++-- | A per-component install hook,+-- which can only perform side effects (e.g. copying files).+type InstallComponentHook = InstallComponentInputs -> IO ()++-- | Copy/install hooks.+data InstallHooks = InstallHooks+ { installComponentHook :: Maybe InstallComponentHook+ -- ^ Per-component install hook.+ }++instance Semigroup InstallHooks where+ InstallHooks+ { installComponentHook = inst1+ }+ <> InstallHooks+ { installComponentHook = inst2+ } =+ InstallHooks+ { installComponentHook = inst1 <> inst2+ }++instance Monoid InstallHooks where+ mempty = noInstallHooks++-- | Empty copy/install hooks.+noInstallHooks :: InstallHooks+noInstallHooks =+ InstallHooks+ { installComponentHook = Nothing+ }++--------------------------------------------------------------------------------+-- Per-component configure hook implementation details.++type LibraryDiff = Library+type ForeignLibDiff = ForeignLib+type ExecutableDiff = Executable+type TestSuiteDiff = TestSuite+type BenchmarkDiff = Benchmark+type BuildInfoDiff = BuildInfo++-- | A diff to a Cabal 'Component', that gets combined monoidally into+-- an existing 'Component'.+newtype ComponentDiff = ComponentDiff {componentDiff :: Component}+ deriving (Semigroup, Show)++emptyComponentDiff :: ComponentName -> ComponentDiff+emptyComponentDiff name = ComponentDiff $+ case name of+ CLibName{} -> CLib emptyLibrary+ CFLibName{} -> CFLib emptyForeignLib+ CExeName{} -> CExe emptyExecutable+ CTestName{} -> CTest emptyTestSuite+ CBenchName{} -> CBench emptyBenchmark++buildInfoComponentDiff :: ComponentName -> BuildInfo -> ComponentDiff+buildInfoComponentDiff name bi = ComponentDiff $+ BI.buildInfo .~ bi $+ case name of+ CLibName{} -> CLib emptyLibrary+ CFLibName{} -> CFLib emptyForeignLib+ CExeName{} -> CExe emptyExecutable+ CTestName{} -> CTest emptyTestSuite+ CBenchName{} -> CBench emptyBenchmark++applyLibraryDiff :: Verbosity -> Library -> LibraryDiff -> IO Library+applyLibraryDiff verbosity lib diff =+ case illegalLibraryDiffReasons lib diff of+ [] -> return $ lib <> diff+ (r : rs) ->+ dieWithException verbosity $+ SetupHooksException $+ CannotApplyComponentDiff $+ IllegalComponentDiff (CLib lib) (r NE.:| rs)++illegalLibraryDiffReasons :: Library -> LibraryDiff -> [IllegalComponentDiffReason]+illegalLibraryDiffReasons+ lib+ Library+ { libName = nm+ , libExposed = e+ , libVisibility = vis+ , libBuildInfo = bi+ } =+ [ CannotChangeName+ | not $ nm == libName emptyLibrary || nm == libName lib+ ]+ ++ [ CannotChangeComponentField "libExposed"+ | not $ e == libExposed emptyLibrary || e == libExposed lib+ ]+ ++ [ CannotChangeComponentField "libVisibility"+ | not $ vis == libVisibility emptyLibrary || vis == libVisibility lib+ ]+ ++ illegalBuildInfoDiffReasons (libBuildInfo lib) bi++applyForeignLibDiff :: Verbosity -> ForeignLib -> ForeignLibDiff -> IO ForeignLib+applyForeignLibDiff verbosity flib diff =+ case illegalForeignLibDiffReasons flib diff of+ [] -> return $ flib <> diff+ (r : rs) ->+ dieWithException verbosity $+ SetupHooksException $+ CannotApplyComponentDiff $+ IllegalComponentDiff (CFLib flib) (r NE.:| rs)++illegalForeignLibDiffReasons :: ForeignLib -> ForeignLibDiff -> [IllegalComponentDiffReason]+illegalForeignLibDiffReasons+ flib+ ForeignLib+ { foreignLibName = nm+ , foreignLibType = ty+ , foreignLibOptions = opts+ , foreignLibVersionInfo = vi+ , foreignLibVersionLinux = linux+ , foreignLibModDefFile = defs+ , foreignLibBuildInfo = bi+ } =+ [ CannotChangeName+ | not $ nm == foreignLibName emptyForeignLib || nm == foreignLibName flib+ ]+ ++ [ CannotChangeComponentField "foreignLibType"+ | not $ ty == foreignLibType emptyForeignLib || ty == foreignLibType flib+ ]+ ++ [ CannotChangeComponentField "foreignLibOptions"+ | not $ opts == foreignLibOptions emptyForeignLib || opts == foreignLibOptions flib+ ]+ ++ [ CannotChangeComponentField "foreignLibVersionInfo"+ | not $ vi == foreignLibVersionInfo emptyForeignLib || vi == foreignLibVersionInfo flib+ ]+ ++ [ CannotChangeComponentField "foreignLibVersionLinux"+ | not $ linux == foreignLibVersionLinux emptyForeignLib || linux == foreignLibVersionLinux flib+ ]+ ++ [ CannotChangeComponentField "foreignLibModDefFile"+ | not $ defs == foreignLibModDefFile emptyForeignLib || defs == foreignLibModDefFile flib+ ]+ ++ illegalBuildInfoDiffReasons (foreignLibBuildInfo flib) bi++applyExecutableDiff :: Verbosity -> Executable -> ExecutableDiff -> IO Executable+applyExecutableDiff verbosity exe diff =+ case illegalExecutableDiffReasons exe diff of+ [] -> return $ exe <> diff+ (r : rs) ->+ dieWithException verbosity $+ SetupHooksException $+ CannotApplyComponentDiff $+ IllegalComponentDiff (CExe exe) (r NE.:| rs)++illegalExecutableDiffReasons :: Executable -> ExecutableDiff -> [IllegalComponentDiffReason]+illegalExecutableDiffReasons+ exe+ Executable+ { exeName = nm+ , modulePath = path+ , exeScope = scope+ , buildInfo = bi+ } =+ [ CannotChangeName+ | not $ nm == exeName emptyExecutable || nm == exeName exe+ ]+ ++ [ CannotChangeComponentField "modulePath"+ | not $ path == modulePath emptyExecutable || path == modulePath exe+ ]+ ++ [ CannotChangeComponentField "exeScope"+ | not $ scope == exeScope emptyExecutable || scope == exeScope exe+ ]+ ++ illegalBuildInfoDiffReasons (buildInfo exe) bi++applyTestSuiteDiff :: Verbosity -> TestSuite -> TestSuiteDiff -> IO TestSuite+applyTestSuiteDiff verbosity test diff =+ case illegalTestSuiteDiffReasons test diff of+ [] -> return $ test <> diff+ (r : rs) ->+ dieWithException verbosity $+ SetupHooksException $+ CannotApplyComponentDiff $+ IllegalComponentDiff (CTest test) (r NE.:| rs)++illegalTestSuiteDiffReasons :: TestSuite -> TestSuiteDiff -> [IllegalComponentDiffReason]+illegalTestSuiteDiffReasons+ test+ TestSuite+ { testName = nm+ , testInterface = iface+ , testCodeGenerators = gens+ , testBuildInfo = bi+ } =+ [ CannotChangeName+ | not $ nm == testName emptyTestSuite || nm == testName test+ ]+ ++ [ CannotChangeComponentField "testInterface"+ | not $ iface == testInterface emptyTestSuite || iface == testInterface test+ ]+ ++ [ CannotChangeComponentField "testCodeGenerators"+ | not $ gens == testCodeGenerators emptyTestSuite || gens == testCodeGenerators test+ ]+ ++ illegalBuildInfoDiffReasons (testBuildInfo test) bi++applyBenchmarkDiff :: Verbosity -> Benchmark -> BenchmarkDiff -> IO Benchmark+applyBenchmarkDiff verbosity bench diff =+ case illegalBenchmarkDiffReasons bench diff of+ [] -> return $ bench <> diff+ (r : rs) ->+ dieWithException verbosity $+ SetupHooksException $+ CannotApplyComponentDiff $+ IllegalComponentDiff (CBench bench) (r NE.:| rs)++illegalBenchmarkDiffReasons :: Benchmark -> BenchmarkDiff -> [IllegalComponentDiffReason]+illegalBenchmarkDiffReasons+ bench+ Benchmark+ { benchmarkName = nm+ , benchmarkInterface = iface+ , benchmarkBuildInfo = bi+ } =+ [ CannotChangeName+ | not $ nm == benchmarkName emptyBenchmark || nm == benchmarkName bench+ ]+ ++ [ CannotChangeComponentField "benchmarkInterface"+ | not $ iface == benchmarkInterface emptyBenchmark || iface == benchmarkInterface bench+ ]+ ++ illegalBuildInfoDiffReasons (benchmarkBuildInfo bench) bi++illegalBuildInfoDiffReasons :: BuildInfo -> BuildInfoDiff -> [IllegalComponentDiffReason]+illegalBuildInfoDiffReasons+ bi+ BuildInfo+ { buildable = can_build+ , buildTools = build_tools+ , buildToolDepends = build_tools_depends+ , pkgconfigDepends = pkgconfig_depends+ , frameworks = fworks+ , targetBuildDepends = target_build_depends+ } =+ map CannotChangeBuildInfoField $+ [ "buildable"+ | not $ can_build == buildable bi || can_build == buildable emptyBuildInfo+ ]+ ++ [ "buildTools"+ | not $ build_tools == buildTools bi || build_tools == buildTools emptyBuildInfo+ ]+ ++ [ "buildToolsDepends"+ | not $ build_tools_depends == buildToolDepends bi || build_tools_depends == buildToolDepends emptyBuildInfo+ ]+ ++ [ "pkgconfigDepends"+ | not $ pkgconfig_depends == pkgconfigDepends bi || pkgconfig_depends == pkgconfigDepends emptyBuildInfo+ ]+ ++ [ "frameworks"+ | not $ fworks == frameworks bi || fworks == frameworks emptyBuildInfo+ ]+ ++ [ "targetBuildDepends"+ | not $ target_build_depends == targetBuildDepends bi || target_build_depends == targetBuildDepends emptyBuildInfo+ ]++-- | Traverse the components of a 'PackageDescription'.+--+-- The function must preserve the component type, i.e. map a 'CLib' to a 'CLib',+-- a 'CExe' to a 'CExe', etc.+traverseComponents+ :: Applicative m+ => (Component -> m Component)+ -> PackageDescription+ -> m PackageDescription+traverseComponents f pd =+ upd_pd+ <$> traverse f_lib (library pd)+ <*> traverse f_lib (subLibraries pd)+ <*> traverse f_flib (foreignLibs pd)+ <*> traverse f_exe (executables pd)+ <*> traverse f_test (testSuites pd)+ <*> traverse f_bench (benchmarks pd)+ where+ f_lib lib = \case { CLib lib' -> lib'; c -> mismatch (CLib lib) c } <$> f (CLib lib)+ f_flib flib = \case { CFLib flib' -> flib'; c -> mismatch (CFLib flib) c } <$> f (CFLib flib)+ f_exe exe = \case { CExe exe' -> exe'; c -> mismatch (CExe exe) c } <$> f (CExe exe)+ f_test test = \case { CTest test' -> test'; c -> mismatch (CTest test) c } <$> f (CTest test)+ f_bench bench = \case { CBench bench' -> bench'; c -> mismatch (CBench bench) c } <$> f (CBench bench)++ upd_pd lib sublibs flibs exes tests benchs =+ pd+ { library = lib+ , subLibraries = sublibs+ , foreignLibs = flibs+ , executables = exes+ , testSuites = tests+ , benchmarks = benchs+ }++ -- This is a panic, because we maintain this invariant elsewhere:+ -- see 'componentDiffError' in 'applyComponentDiff', which catches an+ -- invalid per-component configure hook.+ mismatch c1 c2 =+ error $+ "Mismatched component types: "+ ++ showComponentName (componentName c1)+ ++ " "+ ++ showComponentName (componentName c2)+ ++ "."+{-# INLINEABLE traverseComponents #-}++applyComponentDiffs+ :: Verbosity+ -> (Component -> IO (Maybe ComponentDiff))+ -> PackageDescription+ -> IO PackageDescription+applyComponentDiffs verbosity f = traverseComponents apply_diff+ where+ apply_diff :: Component -> IO Component+ apply_diff c = do+ mbDiff <- f c+ case mbDiff of+ Just diff -> applyComponentDiff verbosity c diff+ Nothing -> return c++forComponents_ :: PackageDescription -> (Component -> IO ()) -> IO ()+forComponents_ pd f = getConst $ traverseComponents (Const . f) pd++applyComponentDiff+ :: Verbosity+ -> Component+ -> ComponentDiff+ -> IO Component+applyComponentDiff verbosity comp (ComponentDiff diff)+ | CLib lib <- comp+ , CLib lib_diff <- diff =+ CLib <$> applyLibraryDiff verbosity lib lib_diff+ | CFLib flib <- comp+ , CFLib flib_diff <- diff =+ CFLib <$> applyForeignLibDiff verbosity flib flib_diff+ | CExe exe <- comp+ , CExe exe_diff <- diff =+ CExe <$> applyExecutableDiff verbosity exe exe_diff+ | CTest test <- comp+ , CTest test_diff <- diff =+ CTest <$> applyTestSuiteDiff verbosity test test_diff+ | CBench bench <- comp+ , CBench bench_diff <- diff =+ CBench <$> applyBenchmarkDiff verbosity bench bench_diff+ | otherwise =+ componentDiffError $ MismatchedComponentTypes comp diff+ where+ -- The per-component configure hook specified a diff of the wrong type,+ -- e.g. tried to apply an executable diff to a library.+ componentDiffError err =+ dieWithException verbosity $+ SetupHooksException $+ CannotApplyComponentDiff err++--------------------------------------------------------------------------------+-- Running pre-build rules++-- | Run all pre-build rules.+--+-- This function should only be called internally within @Cabal@, as it is used+-- to implement the (legacy) Setup.hs interface. The build tool+-- (e.g. @cabal-install@ or @hls@) should instead go through the separate+-- hooks executable, which allows us to only rerun the out-of-date rules+-- (instead of running all of these rules at once).+executeRules+ :: Verbosity+ -> LocalBuildInfo+ -> TargetInfo+ -> Map RuleId Rule+ -> IO ()+executeRules =+ executeRulesUserOrSystem+ SUser+ (\_rId cmd -> sequenceA $ runRuleDynDepsCmd cmd)+ (\_rId cmd -> runRuleExecCmd cmd)++-- | Like 'executeRules', except it can be used when communicating with+-- an external hooks executable.+executeRulesUserOrSystem+ :: forall userOrSystem+ . SScope userOrSystem+ -> (RuleId -> RuleDynDepsCmd userOrSystem -> IO (Maybe ([Rule.Dependency], LBS.ByteString)))+ -> (RuleId -> RuleExecCmd userOrSystem -> IO ())+ -> Verbosity+ -> LocalBuildInfo+ -> TargetInfo+ -> Map RuleId (RuleData userOrSystem)+ -> IO ()+executeRulesUserOrSystem scope runDepsCmdData runCmdData verbosity lbi tgtInfo allRules = do+ -- Compute all extra dynamic dependency edges.+ dynDepsEdges <-+ flip Map.traverseMaybeWithKey allRules $+ \rId (Rule{ruleCommands = cmds}) ->+ runDepsCmdData rId (ruleDepsCmd cmds)++ -- Create a build graph of all the rules, with static and dynamic dependencies+ -- as edges.+ let+ (ruleGraph, ruleFromVertex, vertexFromRuleId) =+ Graph.graphFromEdges+ [ (rule, rId, nub $ mapMaybe directRuleDependencyMaybe allDeps)+ | (rId, rule) <- Map.toList allRules+ , let dynDeps = fromMaybe [] (fst <$> Map.lookup rId dynDepsEdges)+ allDeps = staticDependencies rule ++ dynDeps+ ]++ -- Topologically sort the graph of rules.+ sccs = Graph.scc ruleGraph+ cycles = mapMaybe $ \(Graph.Node v0 subforest) ->+ case subforest of+ []+ | r@(_, rId, deps) <- ruleFromVertex v0 ->+ if rId `elem` deps+ then Just (r, [])+ else Nothing+ v : vs ->+ Just+ ( ruleFromVertex v0+ , map (fmap ruleFromVertex) (v : vs)+ )++ -- Compute demanded rules.+ --+ -- SetupHooks TODO: maybe requiring all generated modules to appear+ -- in autogen-modules is excessive; we can look through all modules instead.+ autogenModPaths =+ map (\m -> moduleNameSymbolicPath m <.> "hs") $+ autogenModules $+ componentBuildInfo $+ targetComponent tgtInfo+ leafRule_maybe (rId, r) =+ if any ((r `ruleOutputsLocation`) . (Location compAutogenDir)) autogenModPaths+ then vertexFromRuleId rId+ else Nothing+ leafRules = mapMaybe leafRule_maybe $ Map.toList allRules+ demandedRuleVerts = Set.fromList $ concatMap (Graph.reachable ruleGraph) leafRules+ nonDemandedRuleVerts = Set.fromList (Graph.vertices ruleGraph) Set.\\ demandedRuleVerts++ case cycles sccs of+ -- If there are cycles in the dependency structure, don't execute+ -- any rules at all; just throw an error right off the bat.+ r : rs ->+ let getRule ((ru, _, _), js) = (toRuleBinary ru, fmap (fmap (\(rv, _, _) -> toRuleBinary rv)) js)+ in errorOut $+ CyclicRuleDependencies $+ fmap getRule (r NE.:| rs)+ -- Otherwise, run all the demanded rules in dependency order (in one go).+ -- (Fine-grained running of rules should happen in cabal-install or HLS,+ -- not in the Cabal library.)+ [] -> do+ -- Emit a warning if there are non-demanded rules.+ unless (null nonDemandedRuleVerts) $+ warn verbosity $+ unlines $+ "The following rules are not demanded and will not be run:"+ : concat+ [ [ " - " ++ show rId ++ ","+ , " generating " ++ show (NE.toList $ results r)+ ]+ | v <- Set.toList nonDemandedRuleVerts+ , let (r, rId, _) = ruleFromVertex v+ ]+ ++ [ "Possible reasons for this error:"+ , " - Some autogenerated modules were not declared"+ , " (in the package description or in the pre-configure hooks)"+ , " - The output location for an autogenerated module is incorrect,"+ , " (e.g. the file extension is incorrect, or"+ , " it is not in the appropriate 'autogenComponentModules' directory)"+ ]++ -- Run all the demanded rules, in dependency order.+ for_ sccs $ \(Graph.Node ruleVertex _) ->+ -- Don't run a rule unless it is demanded.+ unless (ruleVertex `Set.member` nonDemandedRuleVerts) $ do+ let ( r@Rule+ { ruleCommands = cmds+ , staticDependencies = staticDeps+ , results = reslts+ }+ , rId+ , _staticRuleDepIds+ ) =+ ruleFromVertex ruleVertex+ mbDyn = Map.lookup rId dynDepsEdges+ allDeps = staticDeps ++ fromMaybe [] (fst <$> mbDyn)+ -- Check that the dependencies the rule expects are indeed present.+ resolvedDeps <- traverse (resolveDependency verbosity rId allRules) allDeps+ missingRuleDeps <- filterM (missingDep mbWorkDir) resolvedDeps+ case NE.nonEmpty missingRuleDeps of+ Just missingDeps ->+ errorOut $ CantFindSourceForRuleDependencies (toRuleBinary r) missingDeps+ -- Dependencies OK: run the associated action.+ Nothing -> do+ let execCmd = ruleExecCmd scope cmds (snd <$> mbDyn)+ runCmdData rId execCmd+ -- Throw an error if running the action did not result in+ -- the generation of outputs that we expected it to.+ missingRuleResults <- filterM (missingDep mbWorkDir) $ NE.toList reslts+ for_ (NE.nonEmpty missingRuleResults) $ \missingResults ->+ errorOut $ MissingRuleOutputs (toRuleBinary r) missingResults+ return ()+ where+ toRuleBinary :: RuleData userOrSystem -> RuleBinary+ toRuleBinary = case scope of+ SUser -> ruleBinary+ SSystem -> id+ clbi = targetCLBI tgtInfo+ mbWorkDir = mbWorkDirLBI lbi+ compAutogenDir = autogenComponentModulesDir lbi clbi+ errorOut e =+ dieWithException verbosity $+ SetupHooksException $+ RulesException e++directRuleDependencyMaybe :: Rule.Dependency -> Maybe RuleId+directRuleDependencyMaybe (RuleDependency dep) = Just $ outputOfRule dep+directRuleDependencyMaybe (FileDependency{}) = Nothing++resolveDependency :: Verbosity -> RuleId -> Map RuleId (RuleData scope) -> Rule.Dependency -> IO Location+resolveDependency verbosity rId allRules = \case+ FileDependency l -> return l+ RuleDependency (RuleOutput{outputOfRule = depId, outputIndex = i}) ->+ case Map.lookup depId allRules of+ Nothing ->+ error $+ unlines $+ [ "Internal error: missing rule dependency."+ , "Rule: " ++ show rId+ , "Dependency: " ++ show depId+ ]+ Just (Rule{results = os}) ->+ let j :: Int+ j = fromIntegral i+ in case listToMaybe $ drop j $ NE.toList os of+ Just o+ | j >= 0 ->+ return o+ _ ->+ dieWithException verbosity $+ SetupHooksException $+ RulesException $+ InvalidRuleOutputIndex rId depId os i++-- | Does the rule output the given location?+ruleOutputsLocation :: RuleData scope -> Location -> Bool+ruleOutputsLocation (Rule{results = rs}) fp =+ any (\out -> normaliseLocation out == normaliseLocation fp) rs++normaliseLocation :: Location -> Location+normaliseLocation (Location base rel) =+ Location (normaliseSymbolicPath base) (normaliseSymbolicPath rel)++-- | Is the file we depend on missing?+missingDep :: Maybe (SymbolicPath CWD (Dir Pkg)) -> Location -> IO Bool+missingDep mbWorkDir loc = not <$> doesFileExist fp+ where+ fp = interpretSymbolicPath mbWorkDir (location loc)++--------------------------------------------------------------------------------+-- Compatibility with HookedBuildInfo.+--+-- NB: assumes that the components in HookedBuildInfo are:+-- - an (optional) main library,+-- - executables.+--+-- No support for named sublibraries, foreign libraries, tests or benchmarks,+-- because the HookedBuildInfo datatype doesn't specify what type of component+-- each component name is (so we assume they are executables).++hookedBuildInfoComponents :: HookedBuildInfo -> Set ComponentName+hookedBuildInfoComponents (mb_mainlib, exes) =+ Set.fromList $+ (case mb_mainlib of Nothing -> id; Just{} -> (CLibName LMainLibName :))+ [CExeName exe_nm | (exe_nm, _) <- exes]++hookedBuildInfoComponentDiff_maybe :: HookedBuildInfo -> ComponentName -> Maybe (IO ComponentDiff)+hookedBuildInfoComponentDiff_maybe (mb_mainlib, exes) comp_nm =+ case comp_nm of+ CLibName lib_nm ->+ case lib_nm of+ LMainLibName -> return . ComponentDiff . CLib . buildInfoLibraryDiff <$> mb_mainlib+ LSubLibName{} -> Nothing+ CExeName exe_nm ->+ let mb_exe = lookup exe_nm exes+ in return . ComponentDiff . CExe . buildInfoExecutableDiff <$> mb_exe+ CFLibName{} -> Nothing+ CTestName{} -> Nothing+ CBenchName{} -> Nothing++buildInfoLibraryDiff :: BuildInfo -> LibraryDiff+buildInfoLibraryDiff bi = emptyLibrary{libBuildInfo = bi}++buildInfoExecutableDiff :: BuildInfo -> ExecutableDiff+buildInfoExecutableDiff bi = emptyExecutable{buildInfo = bi}++--------------------------------------------------------------------------------+-- Instances for serialisation++deriving newtype instance Binary ComponentDiff+deriving newtype instance Structured ComponentDiff++instance Binary PreConfPackageInputs+instance Structured PreConfPackageInputs+instance Binary PreConfPackageOutputs+instance Structured PreConfPackageOutputs++instance Binary PostConfPackageInputs+instance Structured PostConfPackageInputs++instance Binary PreConfComponentInputs+instance Structured PreConfComponentInputs+instance Binary PreConfComponentOutputs+instance Structured PreConfComponentOutputs++instance Binary PreBuildComponentInputs+instance Structured PreBuildComponentInputs++instance Binary PostBuildComponentInputs+instance Structured PostBuildComponentInputs++instance Binary InstallComponentInputs+instance Structured InstallComponentInputs++--------------------------------------------------------------------------------
+ src/Distribution/Simple/SetupHooks/Rule.hs view
@@ -0,0 +1,1140 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ConstraintKinds #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DerivingStrategies #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE PatternSynonyms #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE QuantifiedConstraints #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE TypeFamilyDependencies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}++-- |+-- Module: Distribution.Simple.SetupHooks.Rule+--+-- Internal module that defines fine-grained rules for setup hooks.+-- Users should import 'Distribution.Simple.SetupHooks' instead.+module Distribution.Simple.SetupHooks.Rule+ ( -- * Rules++ -- ** Rule+ Rule+ , RuleData (..)+ , RuleId (..)+ , staticRule+ , dynamicRule++ -- ** Commands+ , RuleCommands (..)+ , Command+ , CommandData (..)+ , runCommand+ , mkCommand+ , Dict (..)++ -- *** Helpers for executing commands+ , RuleCmds+ , RuleDynDepsCmd+ , RuleExecCmd+ , DynDepsCmd (..)+ , DepsRes (..)+ , ruleDepsCmd+ , runRuleDynDepsCmd+ , ruleExecCmd+ , runRuleExecCmd++ -- ** Collections of rules+ , Rules (..)+ , Dependency (..)+ , RuleOutput (..)+ , rules+ , noRules++ -- ** Rule inputs/outputs+ , Location (..)+ , location++ -- ** File/directory monitoring+ , MonitorFilePath (..)+ , MonitorKindFile (..)+ , MonitorKindDir (..)++ -- *** Monadic API for generation of 'ActionId'+ , RulesM+ , RulesT (..)+ , RulesEnv (..)+ , computeRules++ -- * Internals+ , Scope (..)+ , SScope (..)+ , Static (..)+ , RuleBinary+ , ruleBinary+ )+where++import qualified Distribution.Compat.Binary as Binary+import Distribution.Compat.Prelude++import Distribution.ModuleName+ ( ModuleName+ )+import Distribution.Simple.FileMonitor.Types+import Distribution.Types.UnitId+import Distribution.Utils.Path+ ( FileOrDir (..)+ , Pkg+ , RelativePath+ , SymbolicPath+ , getSymbolicPath+ , (</>)+ )+import Distribution.Utils.ShortText+ ( ShortText+ )+import Distribution.Utils.Structured+ ( Structure (..)+ , Structured (..)+ , nominalStructure+ )+import Distribution.Verbosity+ ( Verbosity+ )++import Control.Monad.Fix+ ( MonadFix+ )+import Control.Monad.Trans+ ( MonadIO+ , MonadTrans (..)+ )+import qualified Control.Monad.Trans.Reader as Reader+import qualified Control.Monad.Trans.State as State+#if MIN_VERSION_transformers(0,5,6)+import qualified Control.Monad.Trans.Writer.CPS as Writer+#else+import qualified Control.Monad.Trans.Writer.Strict as Writer+#endif+import qualified Data.ByteString.Lazy as LBS+import qualified Data.List.NonEmpty as NE+import qualified Data.Map.Strict as Map+ ( empty+ )++import qualified Data.Kind as Hs+import Data.Type.Bool+ ( If+ )+import Data.Type.Equality+ ( (:~~:) (HRefl)+ , type (==)+ )+import GHC.Show+ ( showCommaSpace+ )+import GHC.StaticPtr+import GHC.TypeLits+ ( Symbol+ )+import System.IO.Unsafe+ ( unsafePerformIO+ )+import qualified Type.Reflection as Typeable+ ( SomeTypeRep (..)+ , TypeRep+ , eqTypeRep+ , typeRep+ , typeRepKind+ , withTypeable+ , pattern App+ )++import System.FilePath+ ( normalise+ )++--------------------------------------------------------------------------------++{- Note [Fine-grained hooks]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~+To best understand how the framework of fine-grained build rules+fits into Cabal and the greater Haskell ecosystem, it is helpful to think+that we want build tools (such as cabal-install or HLS) to be able to call+individual build rules on-demand, so that e.g. when a user modifies a .xyz file+the associated preprocessor is re-run.++To do this, we need to perform two different kinds of invocations:++ Query: query the package for the rules that it provides, with their+ dependency information. This allows one to determine when each+ rule should be rerun.++ (For example, if one rule preprocesses *.xyz into *.hs, we need to+ re-run the rule whenever *.xyz is modified.)++ Run: run the relevant action, once one has determined that the rule+ has gone stale.++To do this, any Cabal package with Hooks build-type provides a SetupHooks+module which supports these queries; for example it can be compiled into+a separate executable which can be invoked in the manner described above.+-}++---------+-- Rules++-- | A unique identifier for a t'Rule'.+data RuleId = RuleId+ { ruleNameSpace :: !RulesNameSpace+ , ruleName :: !ShortText+ }+ deriving stock (Show, Eq, Ord, Generic)+ deriving anyclass (Binary, Structured)++data RulesNameSpace = RulesNameSpace+ { rulesUnitId :: !UnitId+ , rulesModuleName :: !ModuleName+ , rulesSrcLoc :: !(Int, Int)+ }+ deriving stock (Show, Eq, Ord, Generic)+ deriving anyclass (Binary, Structured)++-- | Internal function: create a 'RulesNameSpace' from a 'StaticPtrInfo'.+staticPtrNameSpace :: StaticPtrInfo -> RulesNameSpace+staticPtrNameSpace+ StaticPtrInfo+ { spInfoUnitId = unitId+ , spInfoModuleName = modName+ , spInfoSrcLoc = srcLoc+ } =+ RulesNameSpace+ { rulesUnitId = mkUnitId unitId+ , rulesModuleName = fromString modName+ , rulesSrcLoc = srcLoc+ }++-- | 'Rule's are defined with rich types by the package.+--+-- The build system only has a limited view of these; most data consists of+-- opaque 'ByteString's.+--+-- The 'Scope' data-type describes which side of this divide we are on.+data Scope+ = -- | User space (with rich types).+ User+ | -- | Build-system space (manipulation of raw data).+ System++data SScope (scope :: Scope) where+ SUser :: SScope User+ SSystem :: SScope System++type Rule = RuleData User+type RuleBinary = RuleData System++-- | A rule consists of:+--+-- - an action to run to execute the rule,+-- - a description of the rule inputs and outputs.+--+-- Use 'staticRule' or 'dynamicRule' to construct a rule, overriding specific+-- fields, rather than directly using the 'Rule' constructor.+data RuleData (scope :: Scope)+ = -- | Please use the 'staticRule' or 'dynamicRule' smart constructors+ -- instead of this constructor, in order to avoid relying on internal+ -- implementation details.+ Rule+ { ruleCommands :: !(RuleCmds scope)+ -- ^ To run this rule, which t'Command's should we execute?+ , staticDependencies :: ![Dependency]+ -- ^ Static dependencies of this rule.+ , results :: !(NE.NonEmpty Location)+ -- ^ Results of this rule.+ }+ deriving stock (Generic)++deriving stock instance Show (RuleData User)+deriving stock instance Eq (RuleData User)+deriving stock instance Eq (RuleData System)+deriving anyclass instance Binary (RuleData User)+deriving anyclass instance Binary (RuleData System)++-- | Trimmed down 'Show' instance, mostly for error messages.+instance Show RuleBinary where+ show (Rule{staticDependencies = deps, results = reslts, ruleCommands = cmds}) =+ what ++ ": " ++ showDeps deps ++ " --> " ++ show (NE.toList reslts)+ where+ what = case cmds of+ StaticRuleCommand{} -> "Rule"+ DynamicRuleCommands{} -> "Rule (dyn-deps)"+ showDeps :: [Dependency] -> String+ showDeps ds = "[" ++ intercalate ", " (map showDep ds) ++ "]"+ showDep :: Dependency -> String+ showDep = \case+ RuleDependency (RuleOutput{outputOfRule = rId, outputIndex = i}) ->+ "(" ++ show rId ++ ")[" ++ show i ++ "]"+ FileDependency loc -> show loc++-- | A rule with static dependencies.+--+-- Prefer using this smart constructor instead of v'Rule' whenever possible.+staticRule+ :: forall arg+ . Typeable arg+ => Command arg (IO ())+ -> [Dependency]+ -> NE.NonEmpty Location+ -> Rule+staticRule cmd dep res =+ Rule+ { ruleCommands =+ StaticRuleCommand+ { staticRuleCommand = cmd+ , staticRuleArgRep = Typeable.typeRep @arg+ }+ , staticDependencies = dep+ , results = res+ }++-- | A rule with dynamic dependencies.+--+-- Prefer using this smart constructor instead of v'Rule' whenever possible.+dynamicRule+ :: forall depsArg depsRes arg+ . (Typeable depsArg, Typeable depsRes, Typeable arg)+ => StaticPtr (Dict (Binary depsRes, Show depsRes, Eq depsRes))+ -> Command depsArg (IO ([Dependency], depsRes))+ -> Command arg (depsRes -> IO ())+ -> [Dependency]+ -> NE.NonEmpty Location+ -> Rule+dynamicRule dict depsCmd action dep res =+ Rule+ { ruleCommands =+ DynamicRuleCommands+ { dynamicRuleInstances = UserStatic dict+ , dynamicDeps = DynDepsCmd{dynDepsCmd = depsCmd}+ , dynamicRuleCommand = action+ , dynamicRuleTypeRep = Typeable.typeRep @(depsArg, depsRes, arg)+ }+ , staticDependencies = dep+ , results = res+ }++-----------------------+-- Rule inputs/outputs++-- | A (fully resolved) location of a dependency or result of a rule,+-- consisting of a base directory and of a file path relative to that base+-- directory path.+--+-- In practice, this will be something like @'Location' dir ('moduleNameSymbolicPath' mod <.> "hs")@,+-- where:+--+-- - for a file dependency, @dir@ is one of the Cabal search directories,+-- - for an output, @dir@ is a directory such as @autogenComponentModulesDir@+-- or @componentBuildDir@.+data Location where+ Location+ :: { locationBaseDir :: !(SymbolicPath Pkg (Dir baseDir))+ -- ^ Base directory.+ , locationRelPath :: !(RelativePath baseDir File)+ -- ^ File path relative to base directory (including file extension).+ }+ -> Location++instance Eq Location where+ Location b1 l1 == Location b2 l2 =+ (getSymbolicPath b1 == getSymbolicPath b2)+ && (getSymbolicPath l1 == getSymbolicPath l2)+instance Ord Location where+ compare (Location b1 l1) (Location b2 l2) =+ compare+ (getSymbolicPath b1, getSymbolicPath l1)+ (getSymbolicPath b2, getSymbolicPath l2)+instance Binary Location where+ put (Location base loc) = put (base, loc)+ get = Location <$> get <*> get+instance Structured Location where+ structure _ =+ Structure+ tr+ 0+ (show tr)+ [+ ( "Location"+ ,+ [ nominalStructure $ Proxy @(SymbolicPath Pkg (Dir (Tok "baseDir")))+ , nominalStructure $ Proxy @(RelativePath (Tok "baseDir") File)+ ]+ )+ ]+ where+ tr = Typeable.SomeTypeRep $ Typeable.typeRep @Location++-- | Get a (relative or absolute) un-interpreted path to a 'Location'.+location :: Location -> SymbolicPath Pkg File+location (Location base rel) = base </> rel++instance Show Location where+ showsPrec p (Location base rel) =+ showParen (p > 5) $+ showString (normalise $ getSymbolicPath base)+ . showString " </> "+ . showString (normalise $ getSymbolicPath rel)++-- The reason for splitting it up this way is that some pre-processors don't+-- simply generate one output @.hs@ file from one input file, but have+-- dependencies on other generated files (notably @c2hs@, where building one+-- @.hs@ file may require reading other @.chi@ files, and then compiling the+-- @.hs@ file may require reading a generated @.h@ file).+-- In these cases, the generated files need to embed relative path names to each+-- other (eg the generated @.hs@ file mentions the @.h@ file in the FFI imports).+-- This path must be relative to the base directory where the generated files+-- are located; it cannot be relative to the top level of the build tree because+-- the compilers do not look for @.h@ files relative to there, ie we do not use+-- @-I .@, instead we use @-I dist/build@ (or whatever dist dir has been set+-- by the user).++-- | A dependency of a rule.+data Dependency+ = -- | A dependency on an output of another rule.+ RuleDependency !RuleOutput+ | -- | A direct dependency on a file at a particular location on disk.+ --+ -- This should not be used for files that are generated by other rules;+ -- use 'RuleDependency' instead.+ FileDependency !Location+ deriving stock (Show, Eq, Ord, Generic)+ deriving anyclass (Binary, Structured)++-- | A reference to an output of another rule.+data RuleOutput = RuleOutput+ { outputOfRule :: !RuleId+ -- ^ which rule's outputs are we referring to?+ , outputIndex :: !Word+ -- ^ which particular output of that rule?+ }+ deriving stock (Show, Eq, Ord, Generic)+ deriving anyclass (Binary, Structured)++---------+-- Rules++-- | Monad for constructing rules.+type RulesM a = RulesT IO a++-- | The environment within the monadic API.+data RulesEnv = RulesEnv+ { rulesEnvVerbosity :: !Verbosity+ , rulesEnvNameSpace :: !RulesNameSpace+ }++-- | Monad transformer for defining rules. Usually wraps the 'IO' monad,+-- allowing @IO@ actions to be performed using @liftIO@.+newtype RulesT m a = RulesT+ { runRulesT+ :: Reader.ReaderT+ RulesEnv+ ( State.StateT+ (Map RuleId Rule)+ (Writer.WriterT [MonitorFilePath] m)+ )+ a+ }+ deriving newtype (Functor, Applicative, Monad, MonadIO, MonadFix)++instance MonadTrans RulesT where+ lift = RulesT . lift . lift . lift++-- | A collection of t'Rule's.+--+-- Use the 'rules' smart constructor instead of directly using the v'Rules'+-- constructor.+--+-- - Rules are registered using 'registerRule',+-- - Monitored files or directories are declared using 'addRuleMonitors';+-- a change in these will trigger the recomputation of all rules.+--+-- The @env@ type parameter represents an extra argument, which usually+-- consists of information known to Cabal such as 'LocalBuildInfo' and+-- 'ComponentLocalBuildInfo'.+newtype Rules env = Rules {runRules :: env -> RulesM ()}++-- | __Warning__: this 'Semigroup' instance is not commutative.+instance Semigroup (Rules env) where+ (Rules rs1) <> (Rules rs2) =+ Rules $ \inputs -> do+ y1 <- rs1 inputs+ y2 <- rs2 inputs+ return $ y1 <> y2++instance Monoid (Rules env) where+ mempty = Rules $ const noRules++-- | An empty collection of rules.+noRules :: RulesM ()+noRules = return ()++-- | Construct a collection of rules with a given label.+--+-- A label for the rules can be constructed using the @static@ keyword,+-- using the @StaticPointers@ extension.+-- NB: separate calls to 'rules' should have different labels.+--+-- Example usage:+--+-- > myRules :: Rules env+-- > myRules = rules (static ()) $ \ env -> do { .. } -- use the monadic API here+rules+ :: StaticPtr label+ -- ^ unique label for this collection of rules+ -> (env -> RulesM ())+ -- ^ the computation of rules+ -> Rules env+rules label = rulesInNameSpace (staticPtrNameSpace $ staticPtrInfo label)++-- | Internal function to create a collection of rules.+--+-- API users should go through the 'rules' function instead.+rulesInNameSpace+ :: RulesNameSpace+ -- ^ rule namespace+ -> (env -> RulesM ())+ -- ^ the computation of rules+ -> Rules env+rulesInNameSpace nameSpace f =+ Rules $ \env -> RulesT $ do+ Reader.withReaderT (\rulesEnv -> rulesEnv{rulesEnvNameSpace = nameSpace}) $+ runRulesT $+ f env++-- | Internal function: run the monadic 'Rules' computations in order+-- to obtain all the 'Rule's with their 'RuleId's.+computeRules+ :: Verbosity+ -> env+ -> Rules env+ -> IO (Map RuleId Rule, [MonitorFilePath])+computeRules verbosity inputs (Rules rs) = do+ -- Bogus namespace to start with. This will be the first thing+ -- to be set when users use the 'rules' smart constructor.+ let noNameSpace =+ RulesNameSpace+ { rulesUnitId = mkUnitId ""+ , rulesModuleName = fromString ""+ , rulesSrcLoc = (0, 0)+ }+ env0 =+ RulesEnv+ { rulesEnvVerbosity = verbosity+ , rulesEnvNameSpace = noNameSpace+ }+ Writer.runWriterT $+ (`State.execStateT` Map.empty) $+ (`Reader.runReaderT` env0) $+ runRulesT $+ rs inputs++------------+-- Commands++-- | A static pointer (in user scope) or its key (in system scope).+data family Static (scope :: Scope) :: Hs.Type -> Hs.Type++newtype instance Static User fnTy = UserStatic {userStaticPtr :: StaticPtr fnTy}+newtype instance Static System fnTy = SystemStatic {userStaticKey :: StaticKey}+ deriving newtype (Eq, Ord, Show, Binary)++systemStatic :: Static User fnTy -> Static System fnTy+systemStatic (UserStatic ptr) = SystemStatic (staticKey ptr)++instance Show (Static User fnTy) where+ showsPrec p ptr = showsPrec p (systemStatic ptr)+instance Eq (Static User fnTy) where+ (==) = (==) `on` systemStatic+instance Ord (Static User fnTy) where+ compare = compare `on` systemStatic+instance Binary (Static User fnTy) where+ put = put . systemStatic+ get = do+ ptrKey <- get @StaticKey+ case unsafePerformIO $ unsafeLookupStaticPtr ptrKey of+ Just ptr -> return $ UserStatic ptr+ Nothing ->+ fail $+ unlines+ [ "Failed to look up static pointer key for action."+ , "NB: Binary instances for 'User' types cannot be used in external executables."+ ]++-- | A command consists of a statically-known action together with a+-- (possibly dynamic) argument to that action.+--+-- For example, the action can consist of running an executable+-- (such as @happy@ or @c2hs@), while the argument consists of the variable+-- component of the command, e.g. the specific file to run @happy@ on.+type Command = CommandData User++-- | Internal datatype used for commands, both for the Hooks API ('Command')+-- and for the build system.+data CommandData (scope :: Scope) (arg :: Hs.Type) (res :: Hs.Type) = Command+ { actionPtr :: !(Static scope (arg -> res))+ -- ^ The (statically-known) action to execute.+ , actionArg :: !(ScopedArgument scope arg)+ -- ^ The (possibly dynamic) argument to pass to the action.+ , cmdInstances :: !(Static scope (Dict (Binary arg, Show arg)))+ -- ^ Static evidence that the argument can be serialised and deserialised.+ }++-- | Construct a command.+--+-- Prefer using this smart constructor instead of v'Command' whenever possible.+mkCommand+ :: forall arg res+ . StaticPtr (Dict (Binary arg, Show arg))+ -> StaticPtr (arg -> res)+ -> arg+ -> Command arg res+mkCommand dict action arg =+ Command+ { actionPtr = UserStatic action+ , actionArg = ScopedArgument arg+ , cmdInstances = UserStatic dict+ }++-- | Run a 'Command'.+runCommand :: Command args res -> res+runCommand (Command{actionPtr = UserStatic ptr, actionArg = ScopedArgument arg}) =+ deRefStaticPtr ptr arg++-- | Commands to execute a rule:+--+-- - for a rule with static dependencies, a single command,+-- - for a rule with dynamic dependencies, a command for computing dynamic+-- dependencies, and a command for executing the rule.+data+ RuleCommands+ (scope :: Scope)+ (deps :: Scope -> Hs.Type -> Hs.Type -> Hs.Type)+ (ruleCmd :: Scope -> Hs.Type -> Hs.Type -> Hs.Type)+ where+ -- | A rule with statically-known dependencies.+ StaticRuleCommand+ :: forall arg deps ruleCmd scope+ . If+ (scope == System)+ (arg ~ LBS.ByteString)+ (() :: Hs.Constraint)+ => { staticRuleCommand :: !(ruleCmd scope arg (IO ()))+ -- ^ The command to execute the rule.+ , staticRuleArgRep :: !(If (scope == System) Typeable.SomeTypeRep (Typeable.TypeRep arg))+ -- ^ A 'TypeRep' for 'arg'.+ }+ -> RuleCommands scope deps ruleCmd+ DynamicRuleCommands+ :: forall depsArg depsRes arg deps ruleCmd scope+ . If+ (scope == System)+ (depsArg ~ LBS.ByteString, depsRes ~ LBS.ByteString, arg ~ LBS.ByteString)+ (() :: Hs.Constraint)+ => { dynamicRuleInstances :: !(Static scope (Dict (Binary depsRes, Show depsRes, Eq depsRes)))+ -- ^ A rule with dynamic dependencies, which consists of two parts:+ --+ -- - a dynamic dependency computation, that returns additional edges to+ -- be added to the build graph together with an additional piece of data,+ -- - the command to execute the rule itself, which receives the additional+ -- piece of data returned by the dependency computation.+ , -- \^ Static evidence used for serialisation, in order to pass the result+ -- of the dependency computation to the main rule action.+ dynamicDeps :: !(deps scope depsArg depsRes)+ -- ^ A dynamic dependency computation. The resulting dependencies+ -- will be injected into the build graph, and the result of the computation+ -- will be passed on to the command that executes the rule.+ , dynamicRuleCommand :: !(ruleCmd scope arg (depsRes -> IO ()))+ -- ^ The command to execute the rule. It will receive the result+ -- of the dynamic dependency computation.+ , dynamicRuleTypeRep+ :: !( If+ (scope == System)+ Typeable.SomeTypeRep+ (Typeable.TypeRep (depsArg, depsRes, arg))+ )+ -- ^ A 'TypeRep' for the triple @(depsArg,depsRes,arg)@.+ }+ -> RuleCommands scope deps ruleCmd++{- Note [Hooks Binary instances]+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+The Hooks API is strongly typed: users can declare rule commands with varying+types, e.g.++ staticRule+ :: forall arg+ . Typeable arg+ => Command arg (IO ())+ -> [Dependency]+ -> NE.NonEmpty Location+ -> Rule++allows a user to declare a 'Command' that receives an argument of type 'arg'+of their choosing.++This all makes sense within the Hooks API, but when communicating with an+external build system (such as cabal-install or HLS), these arguments are+treated as opaque blobs of data (in particular if the Hooks are compiled into+a separate executable, then the static pointers that contain the relevant+instances for these user-chosen types can only be dereferenced from within that+executable, and not on the side of the build system).++This means that, to enable Hooks to be communicated between the package and the+build system, we need:++ 1. Two representations of rules: one for the package author using the Hooks API,+ and one for the build system.+ 2. Compatibility in the 'Binary' instances for these two types. One needs to be+ able to serialise a 'User'-side 'Rule', and de-serialise it on the build system+ into a 'System'-side 'Rule' which contains some opaque bits of data, and+ vice-versa.++(1) is achieved using the 'Scope' parameter to the 'RuleData' datatype.+@Rule = RuleData User@ is the API-side representation, whereas+@RuleBinary = RuleData System@ is the build-system-side representation.++For (2), note that when we serialise a value of known type and known size, e.g.+an 'Int64', we are nevertheless required to also serialise its size. This is because,+on the build-system side, we don't have access to any of the types, and thus don't know+how much to read in order to reconstruct the associated opaque 'ByteString'.+To ensure we always serialise/deserialise including the length of the data,+the 'ScopedArgument' newtype is used, with a custom 'Binary' instance that always+includes the length. We use this newtype:++ - in the definition of 'CommandData', for arguments to rules,+ - in the definition of 'DepsRes', for the result of dynamic dependency computations.+-}++newtype ScopedArgument (scope :: Scope) arg = ScopedArgument {getArg :: arg}+ deriving newtype (Eq, Ord, Show)++-- | Serialise/deserialise, always including the length of the payload.+instance Binary arg => Binary (ScopedArgument User arg) where+ put (ScopedArgument arg) = put @LBS.ByteString (Binary.encode arg)+ get = do+ dat <- get @LBS.ByteString+ case Binary.decodeOrFail dat of+ Left (_, _, err) -> fail err+ Right (_, _, res) -> return $ ScopedArgument res++-- | Serialise and deserialise a raw ByteString, leaving it untouched.+instance arg ~ LBS.ByteString => Binary (ScopedArgument System arg) where+ put (ScopedArgument arg) = put arg+ get = ScopedArgument <$> get++-- | A placeholder for a command that has been omitted, e.g. when we don't+-- care about serialising/deserialising one particular command in a datatype.+data NoCmd (scope :: Scope) arg res = CmdOmitted+ deriving stock (Generic, Eq, Ord, Show)+ deriving anyclass (Binary)++-- | A dynamic dependency command.+newtype DynDepsCmd scope depsArg depsRes = DynDepsCmd+ { dynDepsCmd+ :: CommandData scope depsArg (IO ([Dependency], depsRes))+ }++deriving newtype instance Show (DynDepsCmd User depsArg depsRes)+deriving newtype instance Eq (DynDepsCmd User depsArg depsRes)+deriving newtype instance Binary (DynDepsCmd User depsArg depsRes)+deriving newtype instance+ (arg ~ LBS.ByteString, depsRes ~ LBS.ByteString)+ => Eq (DynDepsCmd System arg depsRes)+deriving newtype instance+ (arg ~ LBS.ByteString, depsRes ~ LBS.ByteString)+ => Binary (DynDepsCmd System arg depsRes)++-- | The result of a dynamic dependency computation.+newtype DepsRes (scope :: Scope) depsArg depsRes = DepsRes+ { depsRes+ :: ScopedArgument scope depsRes -- See Note [Hooks Binary instances]+ }+ deriving newtype (Show, Eq, Ord)++deriving newtype instance+ Binary (ScopedArgument scope depsRes)+ => Binary (DepsRes scope depsArg depsRes)++-- | Both the rule command and the (optional) dynamic dependency command.+type RuleCmds scope = RuleCommands scope DynDepsCmd CommandData++-- | Only the (optional) dynamic dependency command.+type RuleDynDepsCmd scope = RuleCommands scope DynDepsCmd NoCmd++-- | The rule command together with the result of the (optional) dynamic+-- dependency computation.+type RuleExecCmd scope = RuleCommands scope DepsRes CommandData++-- | Project out the (optional) dependency computation command, so that+-- it can be serialised without serialising anything else.+ruleDepsCmd :: RuleCmds scope -> RuleDynDepsCmd scope+ruleDepsCmd = \case+ StaticRuleCommand+ { staticRuleCommand = _ :: CommandData scope args (IO ())+ , staticRuleArgRep = tr+ } ->+ StaticRuleCommand+ { staticRuleCommand = CmdOmitted :: NoCmd scope args (IO ())+ , staticRuleArgRep = tr+ }+ DynamicRuleCommands+ { dynamicRuleCommand = _ :: CommandData scope args (depsRes -> IO ())+ , dynamicRuleInstances = instsPtr+ , dynamicDeps = deps+ , dynamicRuleTypeRep = tr+ } ->+ DynamicRuleCommands+ { dynamicRuleInstances = instsPtr+ , dynamicDeps = deps+ , dynamicRuleCommand = CmdOmitted :: NoCmd scope args (depsRes -> IO ())+ , dynamicRuleTypeRep = tr+ }++-- | Obtain the (optional) 'IO' action that computes dynamic dependencies.+runRuleDynDepsCmd :: RuleDynDepsCmd User -> Maybe (IO ([Dependency], LBS.ByteString))+runRuleDynDepsCmd = \case+ StaticRuleCommand{} -> Nothing+ DynamicRuleCommands+ { dynamicRuleInstances = UserStatic instsPtr+ , dynamicDeps = DynDepsCmd{dynDepsCmd = depsCmd}+ }+ | Dict <- deRefStaticPtr instsPtr ->+ Just $ do+ (deps, dynDeps) <- runCommand depsCmd+ -- See Note [Hooks Binary instances]+ return $ (deps, Binary.encode $ ScopedArgument @User dynDeps)++-- | Project out the command for running the rule, passing in the result of+-- the dependency computation if there was one.+ruleExecCmd :: SScope scope -> RuleCmds scope -> Maybe LBS.ByteString -> RuleExecCmd scope+ruleExecCmd+ _+ StaticRuleCommand{staticRuleCommand = cmd, staticRuleArgRep = tr}+ _ =+ StaticRuleCommand{staticRuleCommand = cmd, staticRuleArgRep = tr}+ruleExecCmd+ scope+ DynamicRuleCommands+ { dynamicRuleInstances = instsPtr+ , dynamicRuleCommand = cmd :: CommandData scope arg (depsRes -> IO ())+ , dynamicDeps = _ :: DynDepsCmd scope depsArg depsRes+ , dynamicRuleTypeRep = tr+ }+ mbDepsResBinary =+ case mbDepsResBinary of+ Nothing ->+ error $+ unlines+ [ "Missing ByteString argument in 'ruleExecCmd'."+ , "Run 'runRuleDynDepsCmd' on the rule to obtain this data."+ ]+ Just depsResBinary ->+ case scope of+ SUser+ | Dict <- deRefStaticPtr (userStaticPtr instsPtr) ->+ DynamicRuleCommands+ { dynamicRuleInstances = instsPtr+ , dynamicRuleCommand = cmd+ , dynamicDeps = Binary.decode depsResBinary :: DepsRes User depsArg depsRes+ , dynamicRuleTypeRep = tr+ }+ SSystem ->+ DynamicRuleCommands+ { dynamicRuleInstances = instsPtr+ , dynamicRuleCommand = cmd+ , dynamicDeps = DepsRes $ ScopedArgument depsResBinary+ , dynamicRuleTypeRep = tr+ }++-- | Obtain the 'IO' action that executes a rule.+runRuleExecCmd :: RuleExecCmd User -> IO ()+runRuleExecCmd = \case+ StaticRuleCommand{staticRuleCommand = cmd} -> runCommand cmd+ DynamicRuleCommands+ { dynamicDeps = DepsRes (ScopedArgument{getArg = res})+ , dynamicRuleCommand = cmd+ } ->+ runCommand cmd res++--------------------------------------------------------------------------------+-- Instances++-- | A wrapper used to pass evidence of a constraint as an explicit value.+data Dict c where+ Dict :: c => Dict c++instance Show (CommandData User arg res) where+ showsPrec prec (Command{actionPtr = cmdPtr, actionArg = arg, cmdInstances = insts})+ | Dict <- deRefStaticPtr (userStaticPtr insts) =+ showParen (prec >= 11) $+ showString "Command {"+ . showString "actionPtrKey = "+ . shows cmdPtr+ . showCommaSpace+ . showString "actionArg = "+ . shows arg+ . showString "}"++instance Eq (CommandData User arg res) where+ Command{actionPtr = cmdPtr1, actionArg = arg1, cmdInstances = insts1}+ == Command{actionPtr = cmdPtr2, actionArg = arg2, cmdInstances = insts2}+ | cmdPtr1 == cmdPtr2+ , insts1 == insts2+ , Dict <- deRefStaticPtr (userStaticPtr insts1) =+ Binary.encode arg1 == Binary.encode arg2+ | otherwise =+ False+instance arg ~ LBS.ByteString => Eq (CommandData System arg res) where+ Command a1 b1 c1 == Command a2 b2 c2 =+ a1 == a2 && b1 == b2 && c1 == c2++instance Binary (CommandData User arg res) where+ put (Command{actionPtr = cmdPtr, actionArg = arg, cmdInstances = insts})+ | Dict <- deRefStaticPtr (userStaticPtr insts) =+ do+ put cmdPtr+ put insts+ put arg+ get = do+ cmdPtr <- get+ instsPtr <- get+ case deRefStaticPtr @(Dict (Binary arg, Show arg)) $ userStaticPtr instsPtr of+ Dict -> do+ arg <- get+ return $+ Command+ { actionPtr = cmdPtr+ , actionArg = arg+ , cmdInstances = instsPtr+ }+instance arg ~ LBS.ByteString => Binary (CommandData System arg res) where+ put (Command{actionPtr = cmdPtr, actionArg = arg, cmdInstances = insts}) =+ do+ put cmdPtr+ put insts+ put arg+ get = do+ cmdKey <- get+ instsKey <- get+ arg <- get+ return $ Command{actionPtr = cmdKey, actionArg = arg, cmdInstances = instsKey}++instance+ ( forall arg res. Show (ruleCmd User arg res)+ , forall depsArg depsRes. Show depsRes => Show (deps User depsArg depsRes)+ )+ => Show (RuleCommands User deps ruleCmd)+ where+ showsPrec prec (StaticRuleCommand{staticRuleCommand = cmd}) =+ showParen (prec >= 11) $+ showString "StaticRuleCommand {"+ . showString "staticRuleCommand = "+ . shows cmd+ . showString "}"+ showsPrec+ prec+ ( DynamicRuleCommands+ { dynamicDeps = deps+ , dynamicRuleCommand = cmd+ , dynamicRuleInstances = UserStatic instsPtr+ }+ )+ | Dict <- deRefStaticPtr instsPtr =+ showParen (prec >= 11) $+ showString "DynamicRuleCommands {"+ . showString "dynamicDeps = "+ . shows deps+ . showCommaSpace+ . showString "dynamicRuleCommand = "+ . shows cmd+ . showString "}"++instance+ ( forall arg res. Eq (ruleCmd User arg res)+ , forall depsArg depsRes. Eq depsRes => Eq (deps User depsArg depsRes)+ )+ => Eq (RuleCommands User deps ruleCmd)+ where+ StaticRuleCommand{staticRuleCommand = ruleCmd1 :: ruleCmd User arg1 (IO ()), staticRuleArgRep = tr1}+ == StaticRuleCommand{staticRuleCommand = ruleCmd2 :: ruleCmd User arg2 (IO ()), staticRuleArgRep = tr2}+ | Just HRefl <- Typeable.eqTypeRep tr1 tr2 =+ ruleCmd1 == ruleCmd2+ DynamicRuleCommands+ { dynamicDeps = depsCmd1 :: deps User depsArg1 depsRes1+ , dynamicRuleCommand = ruleCmd1 :: ruleCmd User arg1 (depsRes1 -> IO ())+ , dynamicRuleInstances = UserStatic instsPtr1+ , dynamicRuleTypeRep = tr1+ }+ == DynamicRuleCommands+ { dynamicDeps = depsCmd2 :: deps User depsArg2 depsRes2+ , dynamicRuleCommand = ruleCmd2 :: ruleCmd User arg2 (depsRes2 -> IO ())+ , dynamicRuleInstances = UserStatic instsPtr2+ , dynamicRuleTypeRep = tr2+ }+ | Just HRefl <- Typeable.eqTypeRep tr1 tr2+ , Dict <- deRefStaticPtr instsPtr1 =+ depsCmd1 == depsCmd2+ && ruleCmd1 == ruleCmd2+ && staticKey instsPtr1 == staticKey instsPtr2+ _ == _ = False++instance+ ( forall res. Eq (ruleCmd System LBS.ByteString res)+ , Eq (deps System LBS.ByteString LBS.ByteString)+ )+ => Eq (RuleCommands System deps ruleCmd)+ where+ StaticRuleCommand c1 d1 == StaticRuleCommand c2 d2 = c1 == c2 && d1 == d2+ DynamicRuleCommands a1 b1 c1 d1 == DynamicRuleCommands a2 b2 c2 d2 =+ a1 == a2 && b1 == b2 && c1 == c2 && d1 == d2+ _ == _ = False++instance+ ( forall arg res. Binary (ruleCmd User arg res)+ , forall depsArg depsRes. Binary depsRes => Binary (deps User depsArg depsRes)+ )+ => Binary (RuleCommands User deps ruleCmd)+ where+ put = \case+ StaticRuleCommand+ { staticRuleCommand = ruleCmd :: ruleCmd User arg (IO ())+ , staticRuleArgRep = tr+ } -> do+ put @Word 0+ put (Typeable.SomeTypeRep tr)+ put ruleCmd+ DynamicRuleCommands+ { dynamicDeps = deps :: deps User depsArg depsRes+ , dynamicRuleCommand = ruleCmd :: ruleCmd User arg (depsRes -> IO ())+ , dynamicRuleInstances = instsPtr+ , dynamicRuleTypeRep = tr+ } | Dict <- deRefStaticPtr (userStaticPtr instsPtr) ->+ do+ put @Word 1+ put (Typeable.SomeTypeRep tr)+ put instsPtr+ put ruleCmd+ put deps+ get = do+ tag <- get @Word+ case tag of+ 0 -> do+ Typeable.SomeTypeRep (trArg :: Typeable.TypeRep arg) <- get+ if+ | Just HRefl <- Typeable.eqTypeRep (Typeable.typeRepKind trArg) (Typeable.typeRep @Hs.Type) ->+ do+ ruleCmd <- get @(ruleCmd User arg (IO ()))+ return $+ Typeable.withTypeable trArg $+ StaticRuleCommand+ { staticRuleCommand = ruleCmd+ , staticRuleArgRep = trArg+ }+ | otherwise ->+ error "internal error when decoding static rule command"+ _ -> do+ Typeable.SomeTypeRep (tr :: Typeable.TypeRep ty) <- get+ case tr of+ Typeable.App+ ( Typeable.App+ (Typeable.App (tup3Tr :: Typeable.TypeRep tup3) (trDepsArg :: Typeable.TypeRep depsArg))+ (trDepsRes :: Typeable.TypeRep depsRes)+ )+ (trArg :: Typeable.TypeRep arg)+ | Just HRefl <- Typeable.eqTypeRep tup3Tr (Typeable.typeRep @(,,)) -> do+ instsPtr <- get+ case deRefStaticPtr $ userStaticPtr instsPtr of+ (Dict :: Dict (Binary depsRes, Show depsRes, Eq depsRes)) ->+ do+ ruleCmd <- get @(ruleCmd User arg (depsRes -> IO ()))+ deps <- get @(deps User depsArg depsRes)+ return $+ Typeable.withTypeable trDepsArg $+ Typeable.withTypeable trDepsRes $+ Typeable.withTypeable trArg $+ DynamicRuleCommands+ { dynamicDeps = deps+ , dynamicRuleCommand = ruleCmd+ , dynamicRuleInstances = instsPtr+ , dynamicRuleTypeRep = tr+ }+ _ -> error "internal error when decoding dynamic rule commands"++-- | A token constructor used to define 'Structured' instances on types+-- that involve existential quantification.+data family Tok (arg :: Symbol) :: k++instance+ ( forall res. Binary (ruleCmd System LBS.ByteString res)+ , Binary (deps System LBS.ByteString LBS.ByteString)+ )+ => Binary (RuleCommands System deps ruleCmd)+ where+ put = \case+ StaticRuleCommand{staticRuleCommand = ruleCmd, staticRuleArgRep = sTr} -> do+ put @Word 0+ put sTr+ put ruleCmd+ DynamicRuleCommands+ { dynamicDeps = deps+ , dynamicRuleCommand = ruleCmd+ , dynamicRuleInstances = instsKey+ , dynamicRuleTypeRep = sTr+ } ->+ do+ put @Word 1+ put sTr+ put instsKey+ put ruleCmd+ put deps+ get = do+ tag <- get @Word+ case tag of+ 0 -> do+ sTr <- get @Typeable.SomeTypeRep+ ruleCmd <- get+ return $+ StaticRuleCommand+ { staticRuleCommand = ruleCmd+ , staticRuleArgRep = sTr+ }+ _ -> do+ sTr <- get @Typeable.SomeTypeRep+ instsKey <- get+ ruleCmd <- get+ deps <- get+ return $+ DynamicRuleCommands+ { dynamicDeps = deps+ , dynamicRuleCommand = ruleCmd+ , dynamicRuleInstances = instsKey+ , dynamicRuleTypeRep = sTr+ }++--------------------------------------------------------------------------------+-- Showing rules++ruleBinary :: Rule -> RuleBinary+ruleBinary = Binary.decode . Binary.encode
src/Distribution/Simple/ShowBuildInfo.hs view
@@ -1,8 +1,11 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE OverloadedStrings #-}+ -- | -- This module defines a simple JSON-based format for exporting basic -- information about a Cabal package and the compiler configuration Cabal -- would use to build it. This can be produced with the--- @cabal new-show-build-info@ command.+-- @cabal build --enable-build-info@ command. -- -- -- This format is intended for consumption by external tooling and should@@ -14,7 +17,7 @@ -- Below is an example of the output this module produces, -- -- @--- { "cabal-version": "1.23.0.0",+-- { "cabal-lib-version": "1.23.0.0", -- "compiler": { -- "flavour": "GHC", -- "compiler-id": "ghc-7.10.2",@@ -34,7 +37,10 @@ -- } -- @ ----- The @cabal-version@ property provides the version of the Cabal library+-- The output format needs to be validated against 'doc/json-schemas/build-info.schema.json'.+-- If the format changes, update the schema as well!+--+-- The @cabal-lib-version@ property provides the version of the Cabal library -- which generated the output. The @compiler@ property gives some basic -- information about the compiler Cabal would use to compile the package. --@@ -52,95 +58,138 @@ -- * @src-files@: any other Haskell sources needed by the component -- -- Note: At the moment this is only supported when using the GHC compiler.---+module Distribution.Simple.ShowBuildInfo+ ( mkBuildInfo+ , mkBuildInfo'+ , mkCompilerInfo+ , mkComponentInfo+ ) where -module Distribution.Simple.ShowBuildInfo (mkBuildInfo) where+import System.FilePath import Distribution.Compat.Prelude import Prelude () -import qualified Distribution.Simple.GHC as GHC+import qualified Distribution.Simple.GHC as GHC import qualified Distribution.Simple.Program.GHC as GHC -import Distribution.PackageDescription import Distribution.Compiler-import Distribution.Verbosity-import Distribution.Simple.Compiler+import Distribution.PackageDescription+import Distribution.Pretty+import Distribution.Simple.Compiler (Compiler, compilerFlavor, showCompilerId) import Distribution.Simple.LocalBuildInfo import Distribution.Simple.Program-import Distribution.Simple.Setup+import Distribution.Simple.Setup.Build (BuildFlags) import Distribution.Simple.Utils (cabalVersion)-import Distribution.Simple.Utils.Json-import Distribution.Types.TargetInfo import Distribution.Text-import Distribution.Pretty+import Distribution.Types.TargetInfo+import Distribution.Utils.Json import Distribution.Utils.Path+import Distribution.Verbosity -- | Construct a JSON document describing the build information for a -- package. mkBuildInfo- :: PackageDescription -- ^ Mostly information from the .cabal file- -> LocalBuildInfo -- ^ Configuration information- -> BuildFlags -- ^ Flags that the user passed to build+ :: AbsolutePath (Dir Pkg)+ -- ^ The source directory of the package+ -> PackageDescription+ -- ^ Mostly information from the .cabal file+ -> LocalBuildInfo+ -- ^ Configuration information+ -> BuildFlags+ -- ^ Flags that the user passed to build+ -> (ConfiguredProgram, Compiler)+ -- ^ Compiler information.+ -- Needs to be passed explicitly, as we can't extract that information here+ -- without some partial function. -> [TargetInfo]- -> Json-mkBuildInfo pkg_descr lbi _flags targetsToBuild = info+ -> ([String], Json)+ -- ^ Json representation of buildinfo alongside generated warnings+mkBuildInfo wdir pkg_descr lbi _flags compilerInfo targetsToBuild = (warnings, JsonObject buildInfoFields) where- targetToNameAndLBI target =- (componentLocalName $ targetCLBI target, targetCLBI target)- componentsToBuild = map targetToNameAndLBI targetsToBuild- (.=) :: String -> Json -> (String, Json)- k .= v = (k, v)-- info = JsonObject- [ "cabal-version" .= JsonString (display cabalVersion)- , "compiler" .= mkCompilerInfo- , "components" .= JsonArray (map mkComponentInfo componentsToBuild)- ]+ buildInfoFields = mkBuildInfo' (uncurry mkCompilerInfo compilerInfo) componentInfos+ componentInfosWithWarnings = map (mkComponentInfo wdir pkg_descr lbi . targetCLBI) targetsToBuild+ componentInfos = map snd componentInfosWithWarnings+ warnings = concatMap fst componentInfosWithWarnings - mkCompilerInfo = JsonObject- [ "flavour" .= JsonString (prettyShow $ compilerFlavor $ compiler lbi)- , "compiler-id" .= JsonString (showCompilerId $ compiler lbi)- , "path" .= path- ]- where- path = maybe JsonNull (JsonString . programPath)- $ (flavorToProgram . compilerFlavor $ compiler lbi)- >>= flip lookupProgram (withPrograms lbi)+-- | A variant of 'mkBuildInfo' if you need to call 'mkCompilerInfo' and+-- 'mkComponentInfo' yourself.+--+-- If you change the format or any name in the output json, don't forget to update+-- the schema at @\/doc\/json-schemas\/build-info.schema.json@ and the docs of+-- @--enable-build-info@\/@--disable-build-info@.+mkBuildInfo'+ :: Json+ -- ^ The 'Json' from 'mkCompilerInfo'+ -> [Json]+ -- ^ The 'Json' from 'mkComponentInfo'+ -> [(String, Json)]+mkBuildInfo' compilerInfo componentInfos =+ [ "cabal-lib-version" .= JsonString (display cabalVersion)+ , "compiler" .= compilerInfo+ , "components" .= JsonArray componentInfos+ ] - flavorToProgram :: CompilerFlavor -> Maybe Program- flavorToProgram GHC = Just ghcProgram- flavorToProgram GHCJS = Just ghcjsProgram- flavorToProgram UHC = Just uhcProgram- flavorToProgram JHC = Just jhcProgram- flavorToProgram _ = Nothing+mkCompilerInfo :: ConfiguredProgram -> Compiler -> Json+mkCompilerInfo compilerProgram compilerInfo =+ JsonObject+ [ "flavour" .= JsonString (prettyShow $ compilerFlavor compilerInfo)+ , "compiler-id" .= JsonString (showCompilerId compilerInfo)+ , "path" .= JsonString (programPath compilerProgram)+ ] - mkComponentInfo (name, clbi) = JsonObject- [ "type" .= JsonString compType- , "name" .= JsonString (prettyShow name)- , "unit-id" .= JsonString (prettyShow $ componentUnitId clbi)- , "compiler-args" .= JsonArray (map JsonString $ getCompilerArgs bi lbi clbi)- , "modules" .= JsonArray (map (JsonString . display) modules)- , "src-files" .= JsonArray (map JsonString sourceFiles)- , "src-dirs" .= JsonArray (map JsonString $ map getSymbolicPath $ hsSourceDirs bi)+mkComponentInfo :: AbsolutePath (Dir Pkg) -> PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> ([String], Json)+mkComponentInfo wdir pkg_descr lbi clbi =+ ( warnings+ , JsonObject $+ [ "type" .= JsonString compType+ , "name" .= JsonString (prettyShow name)+ , "unit-id" .= JsonString (prettyShow $ componentUnitId clbi)+ , "compiler-args" .= JsonArray (map JsonString compilerArgs)+ , "modules" .= JsonArray (map (JsonString . display) modules)+ , "src-files" .= JsonArray (map (JsonString . getSymbolicPath) sourceFiles)+ , "hs-src-dirs" .= JsonArray (map (JsonString . prettyShow) $ hsSourceDirs bi)+ , "src-dir" .= JsonString (addTrailingPathSeparator (getAbsolutePath wdir)) ]- where- bi = componentBuildInfo comp- comp = fromMaybe (error $ "mkBuildInfo: no component " ++ prettyShow name) $ lookupComponent pkg_descr name- compType = case comp of- CLib _ -> "lib"- CExe _ -> "exe"- CTest _ -> "test"- CBench _ -> "bench"- CFLib _ -> "flib"- modules = case comp of- CLib lib -> explicitLibModules lib- CExe exe -> exeModules exe- _ -> []- sourceFiles = case comp of- CLib _ -> []- CExe exe -> [modulePath exe]- _ -> []+ <> cabalFile+ )+ where+ (warnings, compilerArgs) = getCompilerArgs bi lbi clbi+ name = componentLocalName clbi+ bi = componentBuildInfo comp+ -- If this error happens, a cabal invariant has been violated+ comp = fromMaybe (error $ "mkBuildInfo: no component " ++ prettyShow name) $ lookupComponent pkg_descr name+ compType = case comp of+ CLib _ -> "lib"+ CExe _ -> "exe"+ CTest _ -> "test"+ CBench _ -> "bench"+ CFLib _ -> "flib"+ modules = case comp of+ CLib lib -> explicitLibModules lib+ CExe exe -> exeModules exe+ CTest test ->+ case testInterface test of+ TestSuiteExeV10 _ _ -> []+ TestSuiteLibV09 _ modName -> [modName]+ TestSuiteUnsupported _ -> []+ CBench bench -> benchmarkModules bench+ CFLib flib -> foreignLibModules flib+ sourceFiles = case comp of+ CLib _ -> []+ CExe exe -> [modulePath exe]+ CTest test ->+ case testInterface test of+ TestSuiteExeV10 _ fp -> [fp]+ TestSuiteLibV09 _ _ -> []+ TestSuiteUnsupported _ -> []+ CBench bench -> case benchmarkInterface bench of+ BenchmarkExeV10 _ fp -> [fp]+ BenchmarkUnsupported _ -> []+ CFLib _ -> []+ cabalFile+ | Just fp <- pkgDescrFile lbi = [("cabal-file", JsonString $ getSymbolicPath fp)]+ | otherwise = [] -- | Get the command-line arguments that would be passed -- to the compiler to build the given component.@@ -148,15 +197,23 @@ :: BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo- -> [String]+ -> ([String], [String]) getCompilerArgs bi lbi clbi = case compilerFlavor $ compiler lbi of- GHC -> ghc- GHCJS -> ghc- c -> error $ "ShowBuildInfo.getCompilerArgs: Don't know how to get "++- "build arguments for compiler "++show c+ GHC -> ([], ghcArgs)+ GHCJS -> ([], ghcArgs)+ c ->+ (+ [ "ShowBuildInfo.getCompilerArgs: Don't know how to get build "+ ++ " arguments for compiler "+ ++ show c+ ]+ , []+ ) where -- This is absolutely awful- ghc = GHC.renderGhcOptions (compiler lbi) (hostPlatform lbi) baseOpts- where- baseOpts = GHC.componentGhcOptions normal lbi bi clbi (buildDir lbi)+ ghcArgs =+ GHC.renderGhcOptions (compiler lbi) (hostPlatform lbi) baseOpts+ baseOpts =+ GHC.componentGhcOptions normal lbi bi clbi $+ buildDir lbi
src/Distribution/Simple/SrcDist.hs view
@@ -1,7 +1,12 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------++-- NOTE: FIX: we don't have a great way of testing this module, since+-- we can't easily look inside a tarball once its created.+ -- | -- Module : Distribution.Simple.SrcDist -- Copyright : Simon Marlow 2004@@ -19,89 +24,88 @@ -- The 'createArchive' action uses the external @tar@ program and assumes that -- it accepts the @-z@ flag. Neither of these assumptions are valid on Windows. -- The 'sdist' action now also does some distribution QA checks.---- NOTE: FIX: we don't have a great way of testing this module, since--- we can't easily look inside a tarball once its created.--module Distribution.Simple.SrcDist (- -- * The top level action- sdist,-- -- ** Parts of 'sdist'- printPackageProblems,- prepareTree,- createArchive,+module Distribution.Simple.SrcDist+ ( -- * The top level action+ sdist - -- ** Snapshots- prepareSnapshotTree,- snapshotPackage,- snapshotVersion,- dateToSnapshotNumber,+ -- ** Parts of 'sdist'+ , printPackageProblems+ , prepareTree+ , createArchive - -- * Extracting the source files- listPackageSources,- listPackageSourcesWithDie,+ -- ** Snapshots+ , prepareSnapshotTree+ , snapshotPackage+ , snapshotVersion+ , dateToSnapshotNumber - ) where+ -- * Extracting the source files+ , listPackageSources+ , listPackageSourcesWithDie+ ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () +import Distribution.ModuleName+import Distribution.Package import Distribution.PackageDescription import Distribution.PackageDescription.Check hiding (doesFileExist)-import Distribution.Package-import Distribution.ModuleName-import qualified Distribution.ModuleName as ModuleName-import Distribution.Version+import Distribution.Pretty+import Distribution.Simple.BuildPaths import Distribution.Simple.Configure (findDistPrefOrDefault)+import Distribution.Simple.Errors+import Distribution.Simple.Flag import Distribution.Simple.Glob (matchDirFileGlobWithDie)-import Distribution.Simple.Utils-import Distribution.Simple.Setup import Distribution.Simple.PreProcess-import Distribution.Simple.BuildPaths import Distribution.Simple.Program-import Distribution.Pretty-import Distribution.Verbosity+import Distribution.Simple.Setup.Common+import Distribution.Simple.Setup.SDist+import Distribution.Simple.Utils import Distribution.Utils.Path+import Distribution.Verbosity+import Distribution.Version import qualified Data.Map as Map import Data.Time (UTCTime, getCurrentTime, toGregorian, utctDay)-import System.Directory ( doesFileExist )-import System.IO (IOMode(WriteMode), hPutStrLn, withFile)-import System.FilePath ((</>), (<.>), dropExtension, isRelative)+import System.Directory (doesFileExist)+import System.IO (IOMode (WriteMode), hPutStrLn, withFile) --- |Create a source distribution.-sdist :: PackageDescription -- ^ information from the tarball- -> SDistFlags -- ^ verbosity & snapshot- -> (FilePath -> FilePath) -- ^ build prefix (temp dir)- -> [PPSuffixHandler] -- ^ extra preprocessors (includes suffixes)- -> IO ()+-- | Create a source distribution.+sdist+ :: PackageDescription+ -- ^ information from the tarball+ -> SDistFlags+ -- ^ verbosity & snapshot+ -> (FilePath -> FilePath)+ -- ^ build prefix (temp dir)+ -> [PPSuffixHandler]+ -- ^ extra preprocessors (includes suffixes)+ -> IO () sdist pkg flags mkTmpDir pps = do-- distPref <- findDistPrefOrDefault $ sDistDistPref flags- let targetPref = distPref- tmpTargetDir = mkTmpDir distPref+ distPref <- findDistPrefOrDefault $ setupDistPref common+ let targetPref = i distPref+ tmpTargetDir = mkTmpDir (i distPref) -- When given --list-sources, just output the list of sources to a file. case sDistListSources flags of Flag path -> withFile path WriteMode $ \outHandle -> do- ordinary <- listPackageSources verbosity "." pkg pps- traverse_ (hPutStrLn outHandle) ordinary+ ordinary <- listPackageSources verbosity mbWorkDir pkg pps+ traverse_ (hPutStrLn outHandle . getSymbolicPath) ordinary notice verbosity $ "List of package sources written to file '" ++ path ++ "'"-- NoFlag -> do+ NoFlag -> do -- do some QA printPackageProblems verbosity pkg date <- getCurrentTime- let pkg' | snapshot = snapshotPackage date pkg- | otherwise = pkg+ let pkg'+ | snapshot = snapshotPackage date pkg+ | otherwise = pkg case flagToMaybe (sDistDirectory flags) of Just targetDir -> do generateSourceDir targetDir pkg' info verbosity $ "Source directory created: " ++ targetDir- Nothing -> do createDirectoryIfMissingVerbose verbosity True tmpTargetDir withTempDirectory verbosity tmpTargetDir "sdist." $ \tmpDir -> do@@ -109,32 +113,37 @@ generateSourceDir targetDir pkg' targzFile <- createArchive verbosity pkg' tmpDir targetPref notice verbosity $ "Source tarball created: " ++ targzFile- where generateSourceDir :: FilePath -> PackageDescription -> IO () generateSourceDir targetDir pkg' = do setupMessage verbosity "Building source dist for" (packageId pkg')- prepareTree verbosity pkg' targetDir pps+ prepareTree verbosity mbWorkDir pkg' targetDir pps when snapshot $ overwriteSnapshotPackageDesc verbosity pkg' targetDir - verbosity = fromFlag (sDistVerbosity flags)- snapshot = fromFlag (sDistSnapshot flags)+ common = sDistCommonFlags flags+ verbosity = fromFlag $ setupVerbosity common+ mbWorkDir = flagToMaybe $ setupWorkingDir common+ i = interpretSymbolicPath mbWorkDir -- See Note [Symbolic paths] in Distribution.Utils.Path+ snapshot = fromFlag (sDistSnapshot flags) -- | List all source files of a package. -- -- Since @Cabal-3.4@ returns a single list. There shouldn't be any -- executable files, they are hardly portable.--- listPackageSources- :: Verbosity -- ^ verbosity- -> FilePath -- ^ directory with cabal file- -> PackageDescription -- ^ info from the cabal file- -> [PPSuffixHandler] -- ^ extra preprocessors (include suffixes)- -> IO [FilePath] -- ^ relative paths+ :: Verbosity+ -- ^ verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ directory with cabal file+ -> PackageDescription+ -- ^ info from the cabal file+ -> [PPSuffixHandler]+ -- ^ extra preprocessors (include suffixes)+ -> IO [SymbolicPath Pkg File] listPackageSources verbosity cwd pkg_descr0 pps = do- -- Call helpers that actually do all work.- listPackageSources' verbosity die' cwd pkg_descr pps+ -- Call helpers that actually do all work.+ listPackageSources' verbosity dieWithException cwd pkg_descr pps where pkg_descr = filterAutogenModules pkg_descr0 @@ -144,377 +153,476 @@ -- -- Since @3.4.0.0 listPackageSourcesWithDie- :: Verbosity -- ^ verbosity- -> (Verbosity -> String -> IO [FilePath]) -- ^ 'die'' alternative- -> FilePath -- ^ directory with cabal file- -> PackageDescription -- ^ info from the cabal file- -> [PPSuffixHandler] -- ^ extra preprocessors (include suffixes)- -> IO [FilePath] -- ^ relative paths+ :: Verbosity+ -- ^ verbosity+ -> (forall res. Verbosity -> CabalException -> IO [res])+ -- ^ 'die'' alternative.+ -- Since 'die'' prefixes the error message with 'errorPrefix',+ -- whatever is passed in here and wants to die should do the same.+ -- See issue #7331.+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ directory with cabal file+ -> PackageDescription+ -- ^ info from the cabal file+ -> [PPSuffixHandler]+ -- ^ extra preprocessors (include suffixes)+ -> IO [SymbolicPath Pkg File] listPackageSourcesWithDie verbosity rip cwd pkg_descr0 pps = do- -- Call helpers that actually do all work.- listPackageSources' verbosity rip cwd pkg_descr pps+ -- Call helpers that actually do all work.+ listPackageSources' verbosity rip cwd pkg_descr pps where pkg_descr = filterAutogenModules pkg_descr0 - listPackageSources' :: Verbosity- -> (Verbosity -> String -> IO [FilePath])- -> FilePath+ -- ^ verbosity+ -> (forall res. Verbosity -> CabalException -> IO [res])+ -- ^ 'die'' alternative.+ -- Since 'die'' prefixes the error message with 'errorPrefix',+ -- whatever is passed in here and wants to die should do the same.+ -- See issue #7331.+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ directory with cabal file -> PackageDescription+ -- ^ info from the cabal file -> [PPSuffixHandler]- -> IO [FilePath]-listPackageSources' verbosity rip cwd pkg_descr pps =+ -- ^ extra preprocessors (include suffixes)+ -> IO [SymbolicPath Pkg File]+listPackageSources' verbosity rip mbWorkDir pkg_descr pps = fmap concat . sequenceA $- [- -- Library sources.- fmap concat- . withAllLib $ \Library {- exposedModules = modules,- signatures = sigs,- libBuildInfo = libBi- } ->- allSourcesBuildInfo verbosity rip cwd libBi pps (modules ++ sigs)-- -- Executables sources.- , fmap concat- . withAllExe $ \Executable { modulePath = mainPath, buildInfo = exeBi } -> do- biSrcs <- allSourcesBuildInfo verbosity rip cwd exeBi pps []- mainSrc <- findMainExeFile verbosity cwd exeBi pps mainPath- return (mainSrc:biSrcs)-- -- Foreign library sources- , fmap concat- . withAllFLib $ \flib@(ForeignLib { foreignLibBuildInfo = flibBi }) -> do- biSrcs <- allSourcesBuildInfo verbosity rip cwd flibBi pps []- defFiles <- traverse (findModDefFile verbosity cwd flibBi pps)- (foreignLibModDefFile flib)- return (defFiles ++ biSrcs)-- -- Test suites sources.- , fmap concat- . withAllTest $ \t -> do- let bi = testBuildInfo t- case testInterface t of- TestSuiteExeV10 _ mainPath -> do- biSrcs <- allSourcesBuildInfo verbosity rip cwd bi pps []- srcMainFile <- findMainExeFile verbosity cwd bi pps mainPath- return (srcMainFile:biSrcs)- TestSuiteLibV09 _ m ->- allSourcesBuildInfo verbosity rip cwd bi pps [m]- TestSuiteUnsupported tp ->- rip verbosity $ "Unsupported test suite type: " ++ show tp-- -- Benchmarks sources.- , fmap concat- . withAllBenchmark $ \bm -> do- let bi = benchmarkBuildInfo bm- case benchmarkInterface bm of- BenchmarkExeV10 _ mainPath -> do- biSrcs <- allSourcesBuildInfo verbosity rip cwd bi pps []- srcMainFile <- findMainExeFile verbosity cwd bi pps mainPath- return (srcMainFile:biSrcs)- BenchmarkUnsupported tp ->- rip verbosity $ "Unsupported benchmark type: " ++ show tp-- -- Data files.- , fmap concat- . for (dataFiles pkg_descr) $ \filename -> do- let srcDataDirRaw = dataDir pkg_descr- srcDataDir | null srcDataDirRaw = "."- | otherwise = srcDataDirRaw- matchDirFileGlobWithDie verbosity rip (specVersion pkg_descr) cwd (srcDataDir </> filename)-- -- Extra source files.- , fmap concat . for (extraSrcFiles pkg_descr) $ \fpath ->- matchDirFileGlobWithDie verbosity rip (specVersion pkg_descr) cwd fpath-- -- Extra doc files.- , fmap concat- . for (extraDocFiles pkg_descr) $ \ filename ->- matchDirFileGlobWithDie verbosity rip (specVersion pkg_descr) cwd filename-- -- License file(s).- , return (map getSymbolicPath $ licenseFiles pkg_descr)-- -- Install-include files, without autogen-include files- , fmap concat- . withAllLib $ \ l -> do- let lbi = libBuildInfo l- incls = filter (`notElem` autogenIncludes lbi) (installIncludes lbi)- relincdirs = "." : filter isRelative (includeDirs lbi)- traverse (fmap snd . findIncludeFile verbosity cwd relincdirs) incls-- -- Setup script, if it exists.- , fmap (maybe [] (\f -> [f])) $ findSetupFile cwd-- -- The .cabal file itself.- , fmap (\d -> [d]) (tryFindPackageDescCwd verbosity cwd ".")-- ]+ [ -- Library sources.+ fmap concat+ . withAllLib+ $ \Library+ { exposedModules = modules+ , signatures = sigs+ , libBuildInfo = libBi+ } ->+ allSourcesBuildInfo verbosity rip mbWorkDir libBi pps (modules ++ sigs)+ , -- Executables sources.+ fmap concat+ . withAllExe+ $ \Executable{modulePath = mainPath, buildInfo = exeBi} -> do+ biSrcs <- allSourcesBuildInfo verbosity rip mbWorkDir exeBi pps []+ mainSrc <- findMainExeFile verbosity mbWorkDir exeBi pps mainPath+ return (mainSrc : biSrcs)+ , -- Foreign library sources+ fmap concat+ . withAllFLib+ $ \flib@(ForeignLib{foreignLibBuildInfo = flibBi}) -> do+ biSrcs <- allSourcesBuildInfo verbosity rip mbWorkDir flibBi pps []+ defFiles <-+ traverse+ (findModDefFile verbosity mbWorkDir flibBi pps)+ (foreignLibModDefFile flib)+ return (defFiles ++ biSrcs)+ , -- Test suites sources.+ fmap concat+ . withAllTest+ $ \t -> do+ let bi = testBuildInfo t+ case testInterface t of+ TestSuiteExeV10 _ mainPath -> do+ biSrcs <- allSourcesBuildInfo verbosity rip mbWorkDir bi pps []+ srcMainFile <- findMainExeFile verbosity mbWorkDir bi pps mainPath+ return (srcMainFile : biSrcs)+ TestSuiteLibV09 _ m ->+ allSourcesBuildInfo verbosity rip mbWorkDir bi pps [m]+ TestSuiteUnsupported tp ->+ rip verbosity $ UnsupportedTestSuite (show tp)+ , -- Benchmarks sources.+ fmap concat+ . withAllBenchmark+ $ \bm -> do+ let bi = benchmarkBuildInfo bm+ case benchmarkInterface bm of+ BenchmarkExeV10 _ mainPath -> do+ biSrcs <- allSourcesBuildInfo verbosity rip mbWorkDir bi pps []+ srcMainFile <- findMainExeFile verbosity mbWorkDir bi pps mainPath+ return (srcMainFile : biSrcs)+ BenchmarkUnsupported tp ->+ rip verbosity $ UnsupportedBenchMark (show tp)+ , -- Data files.+ fmap concat+ . for (dataFiles pkg_descr)+ $ \filename ->+ do+ let srcDataDirRaw = dataDir pkg_descr+ srcDataFile :: SymbolicPath Pkg File+ srcDataFile+ | null (getSymbolicPath srcDataDirRaw) = sameDirectory </> filename+ | otherwise = srcDataDirRaw </> filename+ fmap coerceSymbolicPath+ <$> matchDirFileGlobWithDie verbosity rip (specVersion pkg_descr) mbWorkDir srcDataFile+ , -- Extra source files.+ fmap concat . for (extraSrcFiles pkg_descr) $ \fpath ->+ fmap relativeSymbolicPath+ <$> matchDirFileGlobWithDie verbosity rip (specVersion pkg_descr) mbWorkDir fpath+ , -- Extra doc files.+ fmap concat+ . for (extraDocFiles pkg_descr)+ $ \filename ->+ fmap (coerceSymbolicPath . relativeSymbolicPath)+ <$> matchDirFileGlobWithDie verbosity rip (specVersion pkg_descr) mbWorkDir filename+ , -- Extra files.+ fmap concat . for (extraFiles pkg_descr) $ \fpath ->+ fmap relativeSymbolicPath+ <$> matchDirFileGlobWithDie verbosity rip (specVersion pkg_descr) mbWorkDir fpath+ , -- License file(s).+ return (map (relativeSymbolicPath . coerceSymbolicPath) $ licenseFiles pkg_descr)+ , -- Install-include files, without autogen-include files+ fmap concat+ . withAllLib+ $ \l -> do+ let lbi = libBuildInfo l+ incls = fmap getSymbolicPath $ filter (`notElem` autogenIncludes lbi) (installIncludes lbi)+ relincdirs = fmap getSymbolicPath $ sameDirectory : mapMaybe symbolicPathRelative_maybe (includeDirs lbi)+ traverse (fmap (makeSymbolicPath . snd) . findIncludeFile verbosity cwd relincdirs) incls+ , -- Setup script, if it exists.+ fmap (maybe [] (\f -> [makeSymbolicPath f])) $ findSetupFile cwd+ , -- SetupHooks script, if it exists.+ fmap (maybe [] (\f -> [makeSymbolicPath f])) $ findSetupHooksFile cwd+ , -- The .cabal file itself.+ fmap (\d -> [d]) (coerceSymbolicPath . relativeSymbolicPath <$> tryFindPackageDesc verbosity mbWorkDir)+ ] where+ cwd = maybe "." getSymbolicPath mbWorkDir -- We have to deal with all libs and executables, so we have local -- versions of these functions that ignore the 'buildable' attribute:- withAllLib action = traverse action (allLibraries pkg_descr)- withAllFLib action = traverse action (foreignLibs pkg_descr)- withAllExe action = traverse action (executables pkg_descr)- withAllTest action = traverse action (testSuites pkg_descr)+ withAllLib action = traverse action (allLibraries pkg_descr)+ withAllFLib action = traverse action (foreignLibs pkg_descr)+ withAllExe action = traverse action (executables pkg_descr)+ withAllTest action = traverse action (testSuites pkg_descr) withAllBenchmark action = traverse action (benchmarks pkg_descr) ---- |Prepare a directory tree of source files.-prepareTree :: Verbosity -- ^verbosity- -> PackageDescription -- ^info from the cabal file- -> FilePath -- ^source tree to populate- -> [PPSuffixHandler] -- ^extra preprocessors (includes suffixes)- -> IO ()-prepareTree verbosity pkg_descr0 targetDir pps = do- ordinary <- listPackageSources verbosity "." pkg_descr pps- installOrdinaryFiles verbosity targetDir (zip (repeat []) ordinary)- maybeCreateDefaultSetupScript targetDir+-- | Prepare a directory tree of source files.+prepareTree+ :: Verbosity+ -- ^ verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ working directory+ -> PackageDescription+ -- ^ info from the cabal file+ -> FilePath+ -- ^ source tree to populate+ -> [PPSuffixHandler]+ -- ^ extra preprocessors (includes suffixes)+ -> IO ()+prepareTree verbosity mbWorkDir pkg_descr0 targetDir pps = do+ ordinary <- listPackageSources verbosity mbWorkDir pkg_descr pps+ installOrdinaryFiles verbosity targetDir (zip (repeat []) $ map i ordinary)+ maybeCreateDefaultSetupScript targetDir where+ i = interpretSymbolicPath mbWorkDir -- See Note [Symbolic paths] in Distribution.Utils.Path pkg_descr = filterAutogenModules pkg_descr0 -- | Find the setup script file, if it exists. findSetupFile :: FilePath -> IO (Maybe FilePath) findSetupFile targetDir = do- hsExists <- doesFileExist (targetDir </> setupHs)+ hsExists <- doesFileExist (targetDir </> setupHs) lhsExists <- doesFileExist (targetDir </> setupLhs) if hsExists then return (Just setupHs)- else if lhsExists- then return (Just setupLhs)- else return Nothing- where- setupHs = "Setup.hs"- setupLhs = "Setup.lhs"+ else+ if lhsExists+ then return (Just setupLhs)+ else return Nothing+ where+ setupHs = "Setup.hs"+ setupLhs = "Setup.lhs" +-- | Find the setup hooks script file, if it exists.+findSetupHooksFile :: FilePath -> IO (Maybe FilePath)+findSetupHooksFile targetDir = do+ hsExists <- doesFileExist (targetDir </> setupHs)+ lhsExists <- doesFileExist (targetDir </> setupLhs)+ if hsExists+ then return (Just setupHs)+ else+ if lhsExists+ then return (Just setupLhs)+ else return Nothing+ where+ setupHs = "SetupHooks.hs"+ setupLhs = "SetupHooks.lhs"+ -- | Create a default setup script in the target directory, if it doesn't exist. maybeCreateDefaultSetupScript :: FilePath -> IO () maybeCreateDefaultSetupScript targetDir = do mSetupFile <- findSetupFile targetDir case mSetupFile of Just _setupFile -> return ()- Nothing -> do- writeUTF8File (targetDir </> "Setup.hs") $ unlines [- "import Distribution.Simple",- "main = defaultMain"]+ Nothing -> do+ writeUTF8File (targetDir </> "Setup.hs") $+ unlines+ [ "import Distribution.Simple"+ , "main = defaultMain"+ ] -- | Find the main executable file. findMainExeFile :: Verbosity- -> FilePath -- ^ cwd+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ working directory -> BuildInfo -> [PPSuffixHandler]- -> FilePath -- ^ main-is- -> IO FilePath+ -> RelativePath Source File+ -- ^ main-is+ -> IO (SymbolicPath Pkg File) findMainExeFile verbosity cwd exeBi pps mainPath = do- ppFile <- findFileCwdWithExtension cwd (ppSuffixes pps) (map getSymbolicPath (hsSourceDirs exeBi))- (dropExtension mainPath)+ ppFile <-+ findFileCwdWithExtension+ cwd+ (ppSuffixes pps)+ (hsSourceDirs exeBi)+ (dropExtensionsSymbolicPath mainPath) case ppFile of- Nothing -> findFileCwd verbosity cwd (map getSymbolicPath (hsSourceDirs exeBi)) mainPath+ Nothing -> findFileCwd verbosity cwd (hsSourceDirs exeBi) mainPath Just pp -> return pp -- | Find a module definition file -- -- TODO: I don't know if this is right findModDefFile- :: Verbosity -> FilePath -> BuildInfo -> [PPSuffixHandler] -> FilePath -> IO FilePath+ :: Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> BuildInfo+ -> [PPSuffixHandler]+ -> RelativePath Source File+ -> IO (SymbolicPath Pkg File) findModDefFile verbosity cwd flibBi _pps modDefPath =- findFileCwd verbosity cwd ("." : map getSymbolicPath (hsSourceDirs flibBi)) modDefPath+ findFileCwd verbosity cwd (sameDirectory : hsSourceDirs flibBi) modDefPath -- | Given a list of include paths, try to find the include file named -- @f@. Return the name of the file and the full path, or exit with error if -- there's no such file. findIncludeFile :: Verbosity -> FilePath -> [FilePath] -> String -> IO (String, FilePath)-findIncludeFile verbosity _ [] f = die' verbosity ("can't find include file " ++ f)-findIncludeFile verbosity cwd (d:ds) f = do- let path = (d </> f)+findIncludeFile verbosity _ [] f = dieWithException verbosity $ NoIncludeFileFound f+findIncludeFile verbosity cwd (d : ds) f = do+ let path = d </> f b <- doesFileExist (cwd </> path)- if b then return (f,path) else findIncludeFile verbosity cwd ds f+ if b then return (f, path) else findIncludeFile verbosity cwd ds f --- | Remove the auto-generated modules (like 'Paths_*') from 'exposed-modules' +-- | Remove the auto-generated modules (like 'Paths_*') from 'exposed-modules' -- and 'other-modules'. filterAutogenModules :: PackageDescription -> PackageDescription-filterAutogenModules pkg_descr0 = mapLib filterAutogenModuleLib $- mapAllBuildInfo filterAutogenModuleBI pkg_descr0+filterAutogenModules pkg_descr0 =+ mapLib filterAutogenModuleLib $+ mapAllBuildInfo filterAutogenModuleBI pkg_descr0 where- mapLib f pkg = pkg { library = fmap f (library pkg)- , subLibraries = map f (subLibraries pkg) }- filterAutogenModuleLib lib = lib {- exposedModules = filter (filterFunction (libBuildInfo lib)) (exposedModules lib)- }- filterAutogenModuleBI bi = bi {- otherModules = filter (filterFunction bi) (otherModules bi)- }+ mapLib f pkg =+ pkg+ { library = fmap f (library pkg)+ , subLibraries = map f (subLibraries pkg)+ }+ filterAutogenModuleLib lib =+ lib+ { exposedModules = filter (filterFunction (libBuildInfo lib)) (exposedModules lib)+ }+ filterAutogenModuleBI bi =+ bi+ { otherModules = filter (filterFunction bi) (otherModules bi)+ } pathsModule = autogenPathsModuleName pkg_descr0+ packageInfoModule = autogenPackageInfoModuleName pkg_descr0 filterFunction bi = \mn ->- mn /= pathsModule- && not (mn `elem` autogenModules bi)+ mn /= pathsModule+ && mn /= packageInfoModule+ && not (mn `elem` autogenModules bi) -- | Prepare a directory tree of source files for a snapshot version. -- It is expected that the appropriate snapshot version has already been set -- in the package description, eg using 'snapshotPackage' or 'snapshotVersion'.--- prepareSnapshotTree- :: Verbosity -- ^verbosity- -> PackageDescription -- ^info from the cabal file- -> FilePath -- ^source tree to populate- -> [PPSuffixHandler] -- ^extra preprocessors (includes suffixes)+ :: Verbosity+ -- ^ verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ working directory+ -> PackageDescription+ -- ^ info from the cabal file+ -> FilePath+ -- ^ source tree to populate+ -> [PPSuffixHandler]+ -- ^ extra preprocessors (includes suffixes) -> IO ()-prepareSnapshotTree verbosity pkg targetDir pps = do- prepareTree verbosity pkg targetDir pps+prepareSnapshotTree verbosity mbWorkDir pkg targetDir pps = do+ prepareTree verbosity mbWorkDir pkg targetDir pps overwriteSnapshotPackageDesc verbosity pkg targetDir -overwriteSnapshotPackageDesc :: Verbosity -- ^verbosity- -> PackageDescription -- ^info from the cabal file- -> FilePath -- ^source tree- -> IO ()+overwriteSnapshotPackageDesc+ :: Verbosity+ -- ^ verbosity+ -> PackageDescription+ -- ^ info from the cabal file+ -> FilePath+ -- ^ source tree+ -> IO () overwriteSnapshotPackageDesc verbosity pkg targetDir = do- -- We could just writePackageDescription targetDescFile pkg_descr,- -- but that would lose comments and formatting.- descFile <- defaultPackageDesc verbosity- withUTF8FileContents descFile $- writeUTF8File (targetDir </> descFile)- . unlines . map (replaceVersion (packageVersion pkg)) . lines-+ -- We could just writePackageDescription targetDescFile pkg_descr,+ -- but that would lose comments and formatting.+ descFile <- getSymbolicPath <$> defaultPackageDescCwd verbosity+ withUTF8FileContents descFile $+ writeUTF8File (targetDir </> descFile)+ . unlines+ . map (replaceVersion (packageVersion pkg))+ . lines where replaceVersion :: Version -> String -> String replaceVersion version line- | "version:" `isPrefixOf` map toLower line- = "version: " ++ prettyShow version+ | "version:" `isPrefixOf` map toLower line =+ "version: " ++ prettyShow version | otherwise = line -- | Modifies a 'PackageDescription' by appending a snapshot number -- corresponding to the given date.--- snapshotPackage :: UTCTime -> PackageDescription -> PackageDescription snapshotPackage date pkg =- pkg {- package = pkgid { pkgVersion = snapshotVersion date (pkgVersion pkgid) }- }- where pkgid = packageId pkg+ pkg+ { package = pkgid{pkgVersion = snapshotVersion date (pkgVersion pkgid)}+ }+ where+ pkgid = packageId pkg -- | Modifies a 'Version' by appending a snapshot number corresponding -- to the given date.--- snapshotVersion :: UTCTime -> Version -> Version snapshotVersion date = alterVersion (++ [dateToSnapshotNumber date]) -- | Given a date produce a corresponding integer representation. -- For example given a date @18/03/2008@ produce the number @20080318@.--- dateToSnapshotNumber :: UTCTime -> Int dateToSnapshotNumber date = case toGregorian (utctDay date) of- (year, month, day) ->- fromIntegral year * 10000- + month * 100- + day+ (year, month, day) ->+ fromIntegral year * 10000+ + month * 100+ + day -- | Create an archive from a tree of source files, and clean up the tree. createArchive- :: Verbosity -- ^ verbosity- -> PackageDescription -- ^ info from cabal file- -> FilePath -- ^ source tree to archive- -> FilePath -- ^ name of archive to create- -> IO FilePath+ :: Verbosity+ -- ^ verbosity+ -> PackageDescription+ -- ^ info from cabal file+ -> FilePath+ -- ^ source tree to archive+ -> FilePath+ -- ^ name of archive to create+ -> IO FilePath createArchive verbosity pkg_descr tmpDir targetPref = do let tarBallFilePath = targetPref </> tarBallName pkg_descr <.> "tar.gz" (tarProg, _) <- requireProgram verbosity tarProgram defaultProgramDb- let formatOptSupported = maybe False (== "YES") $- Map.lookup "Supports --format"- (programProperties tarProg)+ let formatOptSupported =+ maybe False (== "YES") $+ Map.lookup+ "Supports --format"+ (programProperties tarProg) runProgram verbosity tarProg $ -- Hmm: I could well be skating on thinner ice here by using the -C option -- (=> seems to be supported at least by GNU and *BSD tar) [The -- prev. solution used pipes and sub-command sequences to set up the paths -- correctly, which is problematic in a Windows setting.] ["-czf", tarBallFilePath, "-C", tmpDir]- ++ (if formatOptSupported then ["--format", "ustar"] else [])- ++ [tarBallName pkg_descr]+ ++ (if formatOptSupported then ["--format", "ustar"] else [])+ ++ [tarBallName pkg_descr] return tarBallFilePath -- | Given a buildinfo, return the names of all source files. allSourcesBuildInfo- :: Verbosity- -> (Verbosity -> String -> IO [FilePath])- -> FilePath -- ^ cwd -- change me to 'BuildPath Absolute PackageDir'- -> BuildInfo- -> [PPSuffixHandler] -- ^ Extra preprocessors- -> [ModuleName] -- ^ Exposed modules- -> IO [FilePath]-allSourcesBuildInfo verbosity rip cwd bi pps modules = do- let searchDirs = map getSymbolicPath (hsSourceDirs bi)- sources <- fmap concat $ sequenceA $- [ let file = ModuleName.toFilePath module_- -- NB: *Not* findFileWithExtension, because the same source- -- file may show up in multiple paths due to a conditional;- -- we need to package all of them. See #367.- in findAllFilesCwdWithExtension cwd suffixes searchDirs file- >>= nonEmpty' (notFound module_) return- | module_ <- modules ++ otherModules bi ]- bootFiles <- sequenceA- [ let file = ModuleName.toFilePath module_- fileExts = ["hs-boot", "lhs-boot"]- in findFileCwdWithExtension cwd fileExts (map getSymbolicPath (hsSourceDirs bi)) file- | module_ <- modules ++ otherModules bi ]-- return $ sources ++ catMaybes bootFiles ++ cSources bi ++ cxxSources bi ++- cmmSources bi ++ asmSources bi ++ jsSources bi+ :: Verbosity+ -> (Verbosity -> CabalException -> IO [SymbolicPath Pkg File])+ -- ^ 'die'' alternative.+ -- Since 'die'' prefixes the error message with 'errorPrefix',+ -- whatever is passed in here and wants to die should do the same.+ -- See issue #7331.+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ working directory+ -> BuildInfo+ -> [PPSuffixHandler]+ -- ^ Extra preprocessors+ -> [ModuleName]+ -- ^ Exposed modules+ -> IO [SymbolicPath Pkg File]+allSourcesBuildInfo verbosity rip mbWorkDir bi pps modules = do+ let searchDirs = hsSourceDirs bi+ sources <-+ fmap concat $+ sequenceA $+ [ let file = moduleNameSymbolicPath module_+ in -- NB: *Not* findFileWithExtension, because the same source+ -- file may show up in multiple paths due to a conditional;+ -- we need to package all of them. See #367.+ findAllFilesCwdWithExtension mbWorkDir suffixes searchDirs file+ >>= nonEmpty' (notFound module_) return+ | module_ <- modules ++ otherModules bi+ ]+ bootFiles <-+ sequenceA+ [ let file = moduleNameSymbolicPath module_+ fileExts = builtinHaskellBootSuffixes+ in findFileCwdWithExtension mbWorkDir fileExts (hsSourceDirs bi) file+ | module_ <- modules ++ otherModules bi+ ] + return $+ sources+ ++ catMaybes bootFiles+ ++ cSources bi+ ++ cxxSources bi+ ++ cmmSources bi+ ++ asmSources bi+ ++ jsSources bi where nonEmpty' :: b -> ([a] -> b) -> [a] -> b nonEmpty' x _ [] = x nonEmpty' _ f xs = f xs - suffixes = ppSuffixes pps ++ ["hs", "lhs", "hsig", "lhsig"]-- notFound :: ModuleName -> IO [FilePath]- notFound m = rip verbosity $ "Error: Could not find module: " ++ prettyShow m- ++ " with any suffix: " ++ show suffixes ++ ". If the module "- ++ "is autogenerated it should be added to 'autogen-modules'."+ suffixes = ppSuffixes pps ++ builtinHaskellSuffixes + notFound :: ModuleName -> IO [SymbolicPath Pkg File]+ notFound m =+ rip verbosity $ NoModuleFound m suffixes -- | Note: must be called with the CWD set to the directory containing -- the '.cabal' file. printPackageProblems :: Verbosity -> PackageDescription -> IO () printPackageProblems verbosity pkg_descr = do- ioChecks <- checkPackageFiles verbosity pkg_descr "."+ ioChecks <- checkPackageFiles verbosity pkg_descr "." let pureChecks = checkConfiguredPackage pkg_descr- isDistError (PackageDistSuspicious _) = False- isDistError (PackageDistSuspiciousWarn _) = False- isDistError _ = True- (errors, warnings) = partition isDistError (pureChecks ++ ioChecks)+ (errors, warnings) = partition isHackageDistError (pureChecks ++ ioChecks) unless (null errors) $- notice verbosity $ "Distribution quality errors:\n"- ++ unlines (map explanation errors)+ notice verbosity $+ "Distribution quality errors:\n"+ ++ unlines (map ppPackageCheck errors) unless (null warnings) $- notice verbosity $ "Distribution quality warnings:\n"- ++ unlines (map explanation warnings)+ notice verbosity $+ "Distribution quality warnings:\n"+ ++ unlines (map ppPackageCheck warnings) unless (null errors) $- notice verbosity- "Note: the public hackage server would reject this package."+ notice+ verbosity+ "Note: the public hackage server would reject this package." ------------------------------------------------------------ -- | The name of the tarball without extension--- tarBallName :: PackageDescription -> String tarBallName = prettyShow . packageId -mapAllBuildInfo :: (BuildInfo -> BuildInfo)- -> (PackageDescription -> PackageDescription)-mapAllBuildInfo f pkg = pkg {- library = fmap mapLibBi (library pkg),- subLibraries = fmap mapLibBi (subLibraries pkg),- foreignLibs = fmap mapFLibBi (foreignLibs pkg),- executables = fmap mapExeBi (executables pkg),- testSuites = fmap mapTestBi (testSuites pkg),- benchmarks = fmap mapBenchBi (benchmarks pkg)- }+mapAllBuildInfo+ :: (BuildInfo -> BuildInfo)+ -> (PackageDescription -> PackageDescription)+mapAllBuildInfo f pkg =+ pkg+ { library = fmap mapLibBi (library pkg)+ , subLibraries = fmap mapLibBi (subLibraries pkg)+ , foreignLibs = fmap mapFLibBi (foreignLibs pkg)+ , executables = fmap mapExeBi (executables pkg)+ , testSuites = fmap mapTestBi (testSuites pkg)+ , benchmarks = fmap mapBenchBi (benchmarks pkg)+ } where- mapLibBi lib = lib { libBuildInfo = f (libBuildInfo lib) }- mapFLibBi flib = flib { foreignLibBuildInfo = f (foreignLibBuildInfo flib) }- mapExeBi exe = exe { buildInfo = f (buildInfo exe) }- mapTestBi tst = tst { testBuildInfo = f (testBuildInfo tst) }- mapBenchBi bm = bm { benchmarkBuildInfo = f (benchmarkBuildInfo bm) }+ mapLibBi lib = lib{libBuildInfo = f (libBuildInfo lib)}+ mapFLibBi flib = flib{foreignLibBuildInfo = f (foreignLibBuildInfo flib)}+ mapExeBi exe = exe{buildInfo = f (buildInfo exe)}+ mapTestBi tst = tst{testBuildInfo = f (testBuildInfo tst)}+ mapBenchBi bm = bm{benchmarkBuildInfo = f (benchmarkBuildInfo bm)}
src/Distribution/Simple/Test.hs view
@@ -1,7 +1,12 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ViewPatterns #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Test -- Copyright : Thomas Tuegel 2010@@ -13,124 +18,191 @@ -- This is the entry point into testing a built package. It performs the -- \"@.\/setup test@\" action. It runs test suites designated in the package -- description and reports on the results.- module Distribution.Simple.Test- ( test- ) where+ ( test+ ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -import Distribution.Types.UnqualComponentName import qualified Distribution.PackageDescription as PD+import Distribution.Pretty+import Distribution.Simple.Build (addInternalBuildTools) import Distribution.Simple.Compiler import Distribution.Simple.Hpc import Distribution.Simple.InstallDirs import qualified Distribution.Simple.LocalBuildInfo as LBI-import qualified Distribution.Types.LocalBuildInfo as LBI-import Distribution.Simple.Setup-import Distribution.Simple.UserHooks+import Distribution.Simple.Setup.Test import qualified Distribution.Simple.Test.ExeV10 as ExeV10 import qualified Distribution.Simple.Test.LibV09 as LibV09 import Distribution.Simple.Test.Log+import Distribution.Simple.UserHooks import Distribution.Simple.Utils import Distribution.TestSuite-import Distribution.Pretty+import qualified Distribution.Types.LocalBuildInfo as LBI+import Distribution.Types.UnqualComponentName+import Distribution.Utils.Path +import Distribution.Simple.Configure (getInstalledPackagesById)+import Distribution.Simple.Errors+import Distribution.Simple.Register (internalPackageDBPath)+import Distribution.Simple.Setup.Common+import Distribution.Simple.Setup.Config+import Distribution.Types.ExposedModule+import Distribution.Types.InstalledPackageInfo (InstalledPackageInfo (libraryDirs), exposedModules)+import Distribution.Types.LocalBuildInfo (LocalBuildInfo (..)) import System.Directory- ( createDirectoryIfMissing, doesFileExist, getDirectoryContents- , removeFile )-import System.FilePath ( (</>) )---- |Perform the \"@.\/setup test@\" action.-test :: Args -- ^positional command-line arguments- -> PD.PackageDescription -- ^information from the .cabal file- -> LBI.LocalBuildInfo -- ^information from the configure step- -> TestFlags -- ^flags sent to test- -> IO ()-test args pkg_descr lbi flags = do- let verbosity = fromFlag $ testVerbosity flags- machineTemplate = fromFlag $ testMachineLog flags- distPref = fromFlag $ testDistPref flags- testLogDir = distPref </> "test"- testNames = args- pkgTests = PD.testSuites pkg_descr- enabledTests = LBI.enabledTestLBIs pkg_descr lbi-- doTest :: ((PD.TestSuite, LBI.ComponentLocalBuildInfo),- Maybe TestSuiteLog) -> IO TestSuiteLog- doTest ((suite, clbi), _) =- case PD.testInterface suite of- PD.TestSuiteExeV10 _ _ ->- ExeV10.runTest pkg_descr lbi clbi flags suite+ ( createDirectoryIfMissing+ , doesFileExist+ , getDirectoryContents+ , removeFile+ ) - PD.TestSuiteLibV09 _ _ ->- LibV09.runTest pkg_descr lbi clbi flags suite+-- | Perform the \"@.\/setup test@\" action.+test+ :: Args+ -- ^ positional command-line arguments+ -> PD.PackageDescription+ -- ^ information from the .cabal file+ -> LBI.LocalBuildInfo+ -- ^ information from the configure step+ -> TestFlags+ -- ^ flags sent to test+ -> IO ()+test args pkg_descr lbi0 flags = do+ curDir <- LBI.absoluteWorkingDirLBI lbi0+ let common = testCommonFlags flags+ verbosity = fromFlag $ setupVerbosity common+ distPref = fromFlag $ setupDistPref common+ i = LBI.interpretSymbolicPathLBI lbi -- See Note [Symbolic paths] in Distribution.Utils.Path+ machineTemplate = fromFlag $ testMachineLog flags+ testLogDir = distPref </> makeRelativePathEx "test"+ testNames = args+ pkgTests = PD.testSuites pkg_descr+ enabledTests = LBI.enabledTestLBIs pkg_descr lbi+ -- We must add the internalPkgDB to the package database stack to lookup+ -- the path to HPC dirs of libraries local to this package+ internalPkgDb = internalPackageDBPath lbi0 distPref+ lbi = lbi0{withPackageDB = withPackageDB lbi0 ++ [SpecificPackageDB internalPkgDb]} - _ -> return TestSuiteLog- { testSuiteName = PD.testName suite- , testLogs = TestLog+ doTest+ :: HPCMarkupInfo+ -> ( (PD.TestSuite, LBI.ComponentLocalBuildInfo)+ , Maybe TestSuiteLog+ )+ -> IO TestSuiteLog+ doTest hpcMarkupInfo ((suite, clbi), _) = do+ let lbiForTest =+ lbi+ { withPrograms =+ -- Include any build-tool-depends on build tools internal to the current package.+ addInternalBuildTools+ curDir+ pkg_descr+ lbi+ (PD.testBuildInfo suite)+ (withPrograms lbi)+ }+ case PD.testInterface suite of+ PD.TestSuiteExeV10 _ _ ->+ ExeV10.runTest pkg_descr lbiForTest clbi hpcMarkupInfo flags suite+ PD.TestSuiteLibV09 _ _ ->+ LibV09.runTest pkg_descr lbiForTest clbi hpcMarkupInfo flags suite+ _ ->+ return+ TestSuiteLog+ { testSuiteName = PD.testName suite+ , testLogs =+ TestLog { testName = unUnqualComponentName $ PD.testName suite , testOptionsReturned = [] , testResult =- Error $ "No support for running test suite type: "- ++ show (pretty $ PD.testType suite)+ Error $+ "No support for running test suite type: "+ ++ show (pretty $ PD.testType suite) }- , logFile = ""- }+ , logFile = ""+ } - unless (PD.hasTests pkg_descr) $ do- notice verbosity "Package has no test suites."- exitSuccess+ unless (PD.hasTests pkg_descr) $ do+ notice verbosity "Package has no test suites."+ exitSuccess - when (PD.hasTests pkg_descr && null enabledTests) $- die' verbosity $- "No test suites enabled. Did you remember to configure with "- ++ "\'--enable-tests\'?"+ when (PD.hasTests pkg_descr && null enabledTests) $+ dieWithException verbosity NoTestSuitesEnabled - testsToRun <- case testNames of- [] -> return $ zip enabledTests $ repeat Nothing- names -> for names $ \tName ->- let testMap = zip enabledNames enabledTests- enabledNames = map (PD.testName . fst) enabledTests- allNames = map PD.testName pkgTests- tCompName = mkUnqualComponentName tName- in case lookup tCompName testMap of- Just t -> return (t, Nothing)- _ | tCompName `elem` allNames ->- die' verbosity $ "Package configured with test suite "- ++ tName ++ " disabled."- | otherwise -> die' verbosity $ "no such test: " ++ tName+ testsToRun <- case testNames of+ [] -> return $ zip enabledTests $ repeat Nothing+ names -> for names $ \tName ->+ let testMap = zip enabledNames enabledTests+ enabledNames = map (PD.testName . fst) enabledTests+ allNames = map PD.testName pkgTests+ tCompName = mkUnqualComponentName tName+ in case lookup tCompName testMap of+ Just t -> return (t, Nothing)+ _+ | tCompName `elem` allNames ->+ dieWithException verbosity $ TestNameDisabled tName+ | otherwise -> dieWithException verbosity $ NoSuchTest tName - createDirectoryIfMissing True testLogDir+ createDirectoryIfMissing True $ i testLogDir - -- Delete ordinary files from test log directory.- getDirectoryContents testLogDir- >>= filterM doesFileExist . map (testLogDir </>)- >>= traverse_ removeFile+ -- Delete ordinary files from test log directory.+ getDirectoryContents (i testLogDir)+ >>= filterM doesFileExist . map (i testLogDir </>)+ >>= traverse_ removeFile - let totalSuites = length testsToRun- notice verbosity $ "Running " ++ show totalSuites ++ " test suites..."- suites <- traverse doTest testsToRun- let packageLog = (localPackageLog pkg_descr lbi) { testSuites = suites }- packageLogFile = (</>) testLogDir- $ packageLogPath machineTemplate pkg_descr lbi- allOk <- summarizePackage verbosity packageLog- writeFile packageLogFile $ show packageLog+ -- We configured the unit-ids of libraries we should cover in our coverage+ -- report at configure time into the local build info. At build time, we built+ -- the hpc artifacts into the extraCompilationArtifacts directory, which, at+ -- install time, is copied into the ghc-pkg database files.+ -- Now, we get the path to the HPC artifacts and exposed modules of each+ -- library by querying the package database keyed by unit-id:+ let coverageFor =+ nub $+ fromFlagOrDefault [] (configCoverageFor (configFlags lbi))+ <> extraCoverageFor lbi+ ipkginfos <- getInstalledPackagesById verbosity lbi MissingCoveredInstalledLibrary coverageFor+ let ( concat -> pathsToLibsArtifacts+ , concat -> libsModulesToInclude+ ) =+ unzip $+ map+ ( \ip ->+ ( map ((</> coerceSymbolicPath extraCompilationArtifacts) . makeSymbolicPath) $ libraryDirs ip+ , map exposedName $ exposedModules ip+ )+ )+ ipkginfos+ hpcMarkupInfo = HPCMarkupInfo{pathsToLibsArtifacts, libsModulesToInclude} - when (LBI.testCoverage lbi) $- markupPackage verbosity lbi distPref pkg_descr $- map (fst . fst) testsToRun+ let totalSuites = length testsToRun+ notice verbosity $ "Running " ++ show totalSuites ++ " test suites..."+ suites <- traverse (doTest hpcMarkupInfo) testsToRun+ let packageLog = (localPackageLog pkg_descr lbi){testSuites = suites}+ packageLogFile =+ i testLogDir+ </> packageLogPath machineTemplate pkg_descr lbi+ allOk <- summarizePackage verbosity packageLog+ writeFile packageLogFile $ show packageLog - unless allOk exitFailure+ when (LBI.testCoverage lbi) $+ markupPackage verbosity hpcMarkupInfo lbi distPref pkg_descr $+ map (fst . fst) testsToRun -packageLogPath :: PathTemplate- -> PD.PackageDescription- -> LBI.LocalBuildInfo- -> FilePath+ unless allOk exitFailure++packageLogPath+ :: PathTemplate+ -> PD.PackageDescription+ -> LBI.LocalBuildInfo+ -> FilePath packageLogPath template pkg_descr lbi =- fromPathTemplate $ substPathTemplate env template- where- env = initialPathTemplateEnv- (PD.package pkg_descr) (LBI.localUnitId lbi)- (compilerInfo $ LBI.compiler lbi) (LBI.hostPlatform lbi)+ fromPathTemplate $ substPathTemplate env template+ where+ env =+ initialPathTemplateEnv+ (PD.package pkg_descr)+ (LBI.localUnitId lbi)+ (compilerInfo $ LBI.compiler lbi)+ (LBI.hostPlatform lbi)
src/Distribution/Simple/Test/ExeV10.hs view
@@ -2,192 +2,253 @@ {-# LANGUAGE RankNTypes #-} module Distribution.Simple.Test.ExeV10- ( runTest- ) where+ ( runTest+ ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -import Distribution.Types.UnqualComponentName-import Distribution.Compat.CreatePipe-import Distribution.Compat.Environment import qualified Distribution.PackageDescription as PD-import Distribution.Simple.Build.PathsModule import Distribution.Simple.BuildPaths import Distribution.Simple.Compiler+import Distribution.Simple.Errors+import Distribution.Simple.Flag import Distribution.Simple.Hpc import Distribution.Simple.InstallDirs import qualified Distribution.Simple.LocalBuildInfo as LBI-import qualified Distribution.Types.LocalBuildInfo as LBI-import Distribution.Simple.Setup+ ( ComponentLocalBuildInfo (..)+ , buildDir+ , depLibraryPaths+ )+import Distribution.Simple.Program.Db+import Distribution.Simple.Program.Find+import Distribution.Simple.Program.Run+import Distribution.Simple.Setup.Common+import Distribution.Simple.Setup.Test import Distribution.Simple.Test.Log import Distribution.Simple.Utils import Distribution.System import Distribution.TestSuite-import Distribution.Pretty+import qualified Distribution.Types.LocalBuildInfo as LBI+ ( LocalBuildInfo (..)+ , localUnitId+ , testCoverage+ )+import Distribution.Types.UnqualComponentName import Distribution.Verbosity -import System.Directory- ( createDirectoryIfMissing, doesDirectoryExist, doesFileExist- , getCurrentDirectory, removeDirectoryRecursive )-import System.FilePath ( (</>), (<.>) )-import System.IO ( stdout, stderr )+import Distribution.Utils.Path import qualified Data.ByteString.Lazy as LBS--runTest :: PD.PackageDescription- -> LBI.LocalBuildInfo- -> LBI.ComponentLocalBuildInfo- -> TestFlags- -> PD.TestSuite- -> IO TestSuiteLog-runTest pkg_descr lbi clbi flags suite = do- let isCoverageEnabled = LBI.testCoverage lbi- way = guessWay lbi- tixDir_ = tixDir distPref way testName'+import Distribution.Simple.LocalBuildInfo (interpretSymbolicPathLBI, packageRoot)+import System.Directory+ ( createDirectoryIfMissing+ , doesDirectoryExist+ , doesFileExist+ , removeDirectoryRecursive+ )+import System.IO (stderr, stdout)+import System.Process (createPipe) - pwd <- getCurrentDirectory- existingEnv <- getEnvironment+runTest+ :: PD.PackageDescription+ -> LBI.LocalBuildInfo+ -> LBI.ComponentLocalBuildInfo+ -> HPCMarkupInfo+ -> TestFlags+ -> PD.TestSuite+ -> IO TestSuiteLog+runTest pkg_descr lbi clbi hpcMarkupInfo flags suite = do+ let isCoverageEnabled = LBI.testCoverage lbi+ way = guessWay lbi+ tixDir_ = i $ tixDir distPref way - let cmd = LBI.buildDir lbi </> testName'- </> testName' <.> exeExtension (LBI.hostPlatform lbi)- -- Check that the test executable exists.- exists <- doesFileExist cmd- unless exists $ die' verbosity $ "Error: Could not find test program \"" ++ cmd- ++ "\". Did you build the package first?"+ let cmd =+ i (LBI.buildDir lbi)+ </> testName'+ </> testName' <.> exeExtension (LBI.hostPlatform lbi)+ -- Check that the test executable exists.+ exists <- doesFileExist cmd+ unless exists $+ dieWithException verbosity $+ Couldn'tFindTestProgram cmd - -- Remove old .tix files if appropriate.- unless (fromFlag $ testKeepTix flags) $ do- exists' <- doesDirectoryExist tixDir_- when exists' $ removeDirectoryRecursive tixDir_+ -- Remove old .tix files if appropriate.+ unless (fromFlag $ testKeepTix flags) $ do+ exists' <- doesDirectoryExist tixDir_+ when exists' $ removeDirectoryRecursive tixDir_ - -- Create directory for HPC files.- createDirectoryIfMissing True tixDir_+ -- Create directory for HPC files.+ createDirectoryIfMissing True tixDir_ - -- Write summary notices indicating start of test suite- notice verbosity $ summarizeSuiteStart $ testName'+ -- Write summary notices indicating start of test suite+ notice verbosity $ summarizeSuiteStart $ testName' + -- Run the test executable (with the appropriate environment set)+ let progDb = LBI.withPrograms lbi+ pathVar = progSearchPath progDb+ envOverrides = progOverrideEnv progDb+ newPath <- programSearchPathAsPATHVar pathVar+ let opts =+ map+ (testOption pkg_descr lbi suite)+ (testOptions flags)+ tixFile = packageRoot (testCommonFlags flags) </> getSymbolicPath (tixFilePath distPref way (testName')) - -- Run the test executable- let opts = map (testOption pkg_descr lbi suite)- (testOptions flags)- dataDirPath = pwd </> PD.dataDir pkg_descr- tixFile = pwd </> tixFilePath distPref way (testName')- pkgPathEnv = (pkgPathEnvVar pkg_descr "datadir", dataDirPath)- : existingEnv- shellEnv = [("HPCTIXFILE", tixFile) | isCoverageEnabled] ++ pkgPathEnv+ shellEnv <-+ getFullEnvironment+ ( [("PATH", Just newPath)]+ ++ [("HPCTIXFILE", Just tixFile) | isCoverageEnabled]+ ++ envOverrides+ ) - -- Add (DY)LD_LIBRARY_PATH if needed- shellEnv' <- if LBI.withDynExe lbi- then do let (Platform _ os) = LBI.hostPlatform lbi- paths <- LBI.depLibraryPaths True False lbi clbi- return (addLibraryPath os paths shellEnv)- else return shellEnv+ -- Add (DY)LD_LIBRARY_PATH if needed+ shellEnv' <-+ if LBI.withDynExe lbi+ then do+ let (Platform _ os) = LBI.hostPlatform lbi+ paths <- LBI.depLibraryPaths True False lbi clbi+ return (addLibraryPath os paths shellEnv)+ else return shellEnv - -- Output logger- (wOut, wErr, getLogText) <- case details of- Direct -> return (stdout, stderr, return LBS.empty)- _ -> do- (rOut, wOut) <- createPipe+ -- Output logger+ (wOut, wErr, getLogText) <- case details of+ Direct -> return (stdout, stderr, return LBS.empty)+ _ -> do+ (rOut, wOut) <- createPipe - return $ (,,) wOut wOut $ do- -- Read test executables' output- logText <- LBS.hGetContents rOut+ return $ (,,) wOut wOut $ do+ -- Read test executables' output+ logText <- LBS.hGetContents rOut - -- '--show-details=streaming': print the log output in another thread- when (details == Streaming) $ LBS.putStr logText+ -- '--show-details=streaming': print the log output in another thread+ when (details == Streaming) $ LBS.putStr logText - -- drain the output.- evaluate (force logText)+ -- drain the output.+ evaluate (force logText) - (exit, logText) <- case testWrapper flags of- Flag path -> rawSystemIOWithEnvAndAction- verbosity path (cmd:opts) Nothing (Just shellEnv')- getLogText- -- these handles are automatically closed- Nothing (Just wOut) (Just wErr)+ let mbWorkDir =+ interpretSymbolicPathCWD+ <$> flagToMaybe (setupWorkingDir (testCommonFlags flags)) - NoFlag -> rawSystemIOWithEnvAndAction- verbosity cmd opts Nothing (Just shellEnv')- getLogText- -- these handles are automatically closed- Nothing (Just wOut) (Just wErr)+ (exit, logText) <- case testWrapper flags of+ Flag path ->+ rawSystemIOWithEnvAndAction+ verbosity+ path+ (cmd : opts)+ mbWorkDir+ (Just shellEnv')+ getLogText+ -- these handles are automatically closed+ Nothing+ (Just wOut)+ (Just wErr)+ NoFlag ->+ rawSystemIOWithEnvAndAction+ verbosity+ cmd+ opts+ mbWorkDir+ (Just shellEnv')+ getLogText+ -- these handles are automatically closed+ Nothing+ (Just wOut)+ (Just wErr) - -- Generate TestSuiteLog from executable exit code and a machine-- -- readable test log.- let suiteLog = buildLog exit+ -- Generate TestSuiteLog from executable exit code and a machine-+ -- readable test log.+ let suiteLog = buildLog exit - -- Write summary notice to log file indicating start of test suite- appendFile (logFile suiteLog) $ summarizeSuiteStart $ testName'+ -- Write summary notice to log file indicating start of test suite+ appendFile (logFile suiteLog) $ summarizeSuiteStart testName' - -- Append contents of temporary log file to the final human-- -- readable log file- LBS.appendFile (logFile suiteLog) logText+ -- Append contents of temporary log file to the final human-+ -- readable log file+ LBS.appendFile (logFile suiteLog) logText - -- Write end-of-suite summary notice to log file- appendFile (logFile suiteLog) $ summarizeSuiteFinish suiteLog+ -- Write end-of-suite summary notice to log file+ appendFile (logFile suiteLog) $ summarizeSuiteFinish suiteLog - -- Show the contents of the human-readable log file on the terminal- -- if there is a failure and/or detailed output is requested- let whenPrinting = when $- ( details == Always ||- details == Failures && not (suitePassed $ testLogs suiteLog))+ -- Show the contents of the human-readable log file on the terminal+ -- if there is a failure and/or detailed output is requested+ let whenPrinting =+ when $+ ( details == Always+ || details == Failures && not (suitePassed $ testLogs suiteLog)+ ) -- verbosity overrides show-details && verbosity >= normal- whenPrinting $ do- LBS.putStr logText- putChar '\n'+ whenPrinting $ do+ LBS.putStr logText+ putChar '\n' - -- Write summary notice to terminal indicating end of test suite- notice verbosity $ summarizeSuiteFinish suiteLog+ -- Write summary notice to terminal indicating end of test suite+ notice verbosity $ summarizeSuiteFinish suiteLog - when isCoverageEnabled $- case PD.library pkg_descr of- Nothing ->- die' verbosity $ "Error: test coverage is only supported for packages with a library component"+ when isCoverageEnabled $ do+ -- Until #9493 is fixed, we expect cabal-install to pass one dist dir per+ -- library and there being at least one library in the package with the+ -- testsuite. When it is fixed, we can remove this predicate and allow a+ -- testsuite without a library to cover libraries in other packages of the+ -- same project+ when (null $ PD.allLibraries pkg_descr) $+ dieWithException verbosity TestCoverageSupport - Just library ->- markupTest verbosity lbi distPref (prettyShow $ PD.package pkg_descr) suite library+ markupPackage verbosity hpcMarkupInfo lbi distPref pkg_descr [suite] - return suiteLog+ return suiteLog where+ i = interpretSymbolicPathLBI lbi -- See Note [Symbolic paths] in Distribution.Utils.Path+ commonFlags = testCommonFlags flags+ testName' = unUnqualComponentName $ PD.testName suite - distPref = fromFlag $ testDistPref flags- verbosity = fromFlag $ testVerbosity flags+ distPref = fromFlag $ setupDistPref commonFlags+ verbosity = fromFlag $ setupVerbosity commonFlags details = fromFlag $ testShowDetails flags- testLogDir = distPref </> "test"+ testLogDir = distPref </> makeRelativePathEx "test" buildLog exit =- let r = case exit of- ExitSuccess -> Pass- ExitFailure c -> Fail $ "exit code: " ++ show c- --n = unUnqualComponentName $ PD.testName suite- l = TestLog- { testName = testName'- , testOptionsReturned = []- , testResult = r- }- in TestSuiteLog- { testSuiteName = PD.testName suite- , testLogs = l- , logFile =- testLogDir- </> testSuiteLogPath (fromFlag $ testHumanLog flags)- pkg_descr lbi testName' l- }+ let r = case exit of+ ExitSuccess -> Pass+ ExitFailure c -> Fail $ "exit code: " ++ show c+ -- n = unUnqualComponentName $ PD.testName suite+ l =+ TestLog+ { testName = testName'+ , testOptionsReturned = []+ , testResult = r+ }+ in TestSuiteLog+ { testSuiteName = PD.testName suite+ , testLogs = l+ , logFile =+ i testLogDir+ </> testSuiteLogPath+ (fromFlag $ testHumanLog flags)+ pkg_descr+ lbi+ testName'+ l+ } -- TODO: This is abusing the notion of a 'PathTemplate'. The result isn't -- necessarily a path.-testOption :: PD.PackageDescription- -> LBI.LocalBuildInfo- -> PD.TestSuite- -> PathTemplate- -> String+testOption+ :: PD.PackageDescription+ -> LBI.LocalBuildInfo+ -> PD.TestSuite+ -> PathTemplate+ -> String testOption pkg_descr lbi suite template =- fromPathTemplate $ substPathTemplate env template+ fromPathTemplate $ substPathTemplate env template where- env = initialPathTemplateEnv- (PD.package pkg_descr) (LBI.localUnitId lbi)- (compilerInfo $ LBI.compiler lbi) (LBI.hostPlatform lbi) ++- [(TestSuiteNameVar, toPathTemplate $ unUnqualComponentName $ PD.testName suite)]+ env =+ initialPathTemplateEnv+ (PD.package pkg_descr)+ (LBI.localUnitId lbi)+ (compilerInfo $ LBI.compiler lbi)+ (LBI.hostPlatform lbi)+ ++ [(TestSuiteNameVar, toPathTemplate $ unUnqualComponentName $ PD.testName suite)]
src/Distribution/Simple/Test/LibV09.hs view
@@ -1,115 +1,140 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} module Distribution.Simple.Test.LibV09- ( runTest- -- Test stub- , simpleTestStub- , stubFilePath, stubMain, stubName, stubWriteLog- , writeSimpleTestStub- ) where+ ( runTest+ -- Test stub+ , simpleTestStub+ , stubFilePath+ , stubMain+ , stubName+ , stubWriteLog+ , writeSimpleTestStub+ ) where -import Prelude () import Distribution.Compat.Prelude import Distribution.Types.UnqualComponentName+import Prelude () -import Distribution.Compat.CreatePipe-import Distribution.Compat.Environment import Distribution.Compat.Internal.TempFile+import Distribution.Compat.Process (proc) import Distribution.ModuleName import qualified Distribution.PackageDescription as PD-import Distribution.Simple.Build.PathsModule+import Distribution.Pretty import Distribution.Simple.BuildPaths import Distribution.Simple.Compiler+import Distribution.Simple.Errors import Distribution.Simple.Hpc import Distribution.Simple.InstallDirs import qualified Distribution.Simple.LocalBuildInfo as LBI-import qualified Distribution.Types.LocalBuildInfo as LBI-import Distribution.Simple.Setup+import Distribution.Simple.Program.Db+import Distribution.Simple.Program.Find+import Distribution.Simple.Program.Run+import Distribution.Simple.Setup.Common+import Distribution.Simple.Setup.Test import Distribution.Simple.Test.Log import Distribution.Simple.Utils import Distribution.System import Distribution.TestSuite-import Distribution.Pretty+import qualified Distribution.Types.LocalBuildInfo as LBI+import Distribution.Utils.Path import Distribution.Verbosity import qualified Control.Exception as CE import qualified Data.ByteString.Lazy as LBS import System.Directory- ( createDirectoryIfMissing, canonicalizePath- , doesDirectoryExist, doesFileExist- , getCurrentDirectory, removeDirectoryRecursive, removeFile- , setCurrentDirectory )-import System.FilePath ( (</>), (<.>) )-import System.IO ( hClose, hPutStr )-import System.Process (StdStream(..), waitForProcess)--runTest :: PD.PackageDescription- -> LBI.LocalBuildInfo- -> LBI.ComponentLocalBuildInfo- -> TestFlags- -> PD.TestSuite- -> IO TestSuiteLog-runTest pkg_descr lbi clbi flags suite = do- let isCoverageEnabled = LBI.testCoverage lbi- way = guessWay lbi+ ( canonicalizePath+ , createDirectoryIfMissing+ , doesDirectoryExist+ , doesFileExist+ , getCurrentDirectory+ , removeDirectoryRecursive+ , removeFile+ , setCurrentDirectory+ )+import System.IO (hClose, hPutStr)+import qualified System.Process as Process - pwd <- getCurrentDirectory- existingEnv <- getEnvironment+runTest+ :: PD.PackageDescription+ -> LBI.LocalBuildInfo+ -> LBI.ComponentLocalBuildInfo+ -> HPCMarkupInfo+ -> TestFlags+ -> PD.TestSuite+ -> IO TestSuiteLog+runTest pkg_descr lbi clbi hpcMarkupInfo flags suite = do+ let isCoverageEnabled = LBI.testCoverage lbi+ way = guessWay lbi - let cmd = LBI.buildDir lbi </> stubName suite- </> stubName suite <.> exeExtension (LBI.hostPlatform lbi)- -- Check that the test executable exists.- exists <- doesFileExist cmd- unless exists $- die' verbosity $ "Error: Could not find test program \"" ++ cmd- ++ "\". Did you build the package first?"+ let mbWorkDir = LBI.mbWorkDirLBI lbi - -- Remove old .tix files if appropriate.- unless (fromFlag $ testKeepTix flags) $ do- let tDir = tixDir distPref way testName'- exists' <- doesDirectoryExist tDir- when exists' $ removeDirectoryRecursive tDir+ let cmd =+ interpretSymbolicPath mbWorkDir (LBI.buildDir lbi)+ </> stubName suite+ </> stubName suite <.> exeExtension (LBI.hostPlatform lbi)+ tDir = i $ tixDir distPref way+ -- Check that the test executable exists.+ exists <- doesFileExist cmd+ unless exists $+ dieWithException verbosity $+ Couldn'tFindTestProgLibV09 cmd - -- Create directory for HPC files.- createDirectoryIfMissing True $ tixDir distPref way testName'+ -- Remove old .tix files if appropriate.+ unless (fromFlag $ testKeepTix flags) $ do+ exists' <- doesDirectoryExist tDir+ when exists' $ removeDirectoryRecursive tDir - -- Write summary notices indicating start of test suite- notice verbosity $ summarizeSuiteStart testName'+ -- Create directory for HPC files.+ createDirectoryIfMissing True tDir - suiteLog <- CE.bracket openCabalTemp deleteIfExists $ \tempLog -> do+ -- Write summary notices indicating start of test suite+ notice verbosity $ summarizeSuiteStart testName' - -- TODO: this setup is broken,- -- if the test output is too big, we will deadlock.- (rOut, wOut) <- createPipe+ suiteLog <- CE.bracket openCabalTemp deleteIfExists $ \tempLog -> do+ -- Compute the appropriate environment for running the test suite+ let progDb = LBI.withPrograms lbi+ pathVar = progSearchPath progDb+ envOverrides = progOverrideEnv progDb+ newPath <- programSearchPathAsPATHVar pathVar - -- Run test executable- (Just wIn, _, _, process) <- do- let opts = map (testOption pkg_descr lbi suite) $ testOptions flags- dataDirPath = pwd </> PD.dataDir pkg_descr- tixFile = pwd </> tixFilePath distPref way testName'- pkgPathEnv = (pkgPathEnvVar pkg_descr "datadir", dataDirPath)- : existingEnv- shellEnv = [("HPCTIXFILE", tixFile) | isCoverageEnabled]- ++ pkgPathEnv- -- Add (DY)LD_LIBRARY_PATH if needed- shellEnv' <-- if LBI.withDynExe lbi- then do- let (Platform _ os) = LBI.hostPlatform lbi- paths <- LBI.depLibraryPaths True False lbi clbi- cpath <- canonicalizePath $ LBI.componentBuildDir lbi clbi- return (addLibraryPath os (cpath : paths) shellEnv)- else return shellEnv- case testWrapper flags of- Flag path -> createProcessWithEnv verbosity path (cmd:opts) Nothing (Just shellEnv')- -- these handles are closed automatically- CreatePipe (UseHandle wOut) (UseHandle wOut)+ -- Run test executable+ let opts = map (testOption pkg_descr lbi suite) $ testOptions flags+ tixFile = i $ tixFilePath distPref way testName' - NoFlag -> createProcessWithEnv verbosity cmd opts Nothing (Just shellEnv')- -- these handles are closed automatically- CreatePipe (UseHandle wOut) (UseHandle wOut)+ shellEnv <-+ getFullEnvironment+ ( [("PATH", Just newPath)]+ ++ [("HPCTIXFILE", Just tixFile) | isCoverageEnabled]+ ++ envOverrides+ )+ -- Add (DY)LD_LIBRARY_PATH if needed+ shellEnv' <-+ if LBI.withDynExe lbi+ then do+ let (Platform _ os) = LBI.hostPlatform lbi+ paths <- LBI.depLibraryPaths True False lbi clbi+ cpath <- canonicalizePath $ i $ LBI.componentBuildDir lbi clbi+ return (addLibraryPath os (cpath : paths) shellEnv)+ else return shellEnv+ let (cmd', opts') = case testWrapper flags of+ Flag path -> (path, cmd : opts)+ NoFlag -> (cmd, opts) + -- TODO: this setup is broken,+ -- if the test output is too big, we will deadlock.+ (rOut, wOut) <- Process.createPipe+ (exitcode, logText) <- rawSystemProcAction+ verbosity+ (proc cmd' opts')+ { Process.env = Just shellEnv'+ , Process.std_in = Process.CreatePipe+ , Process.std_out = Process.UseHandle wOut+ , Process.std_err = Process.UseHandle wOut+ }+ $ \mIn _ _ -> do+ let wIn = fromCreatePipe mIn hPutStr wIn $ show (tempLog, PD.testName suite) hClose wIn @@ -118,112 +143,132 @@ logText <- LBS.hGetContents rOut -- Force the IO manager to drain the test output pipe _ <- evaluate (force logText)-- exitcode <- waitForProcess process- unless (exitcode == ExitSuccess) $ do- debug verbosity $ cmd ++ " returned " ++ show exitcode+ return logText+ unless (exitcode == ExitSuccess) $+ debug verbosity $+ cmd ++ " returned " ++ show exitcode - -- Generate final log file name- let finalLogName l = testLogDir- </> testSuiteLogPath- (fromFlag $ testHumanLog flags) pkg_descr lbi- (unUnqualComponentName $ testSuiteName l) (testLogs l)- -- Generate TestSuiteLog from executable exit code and a machine-- -- readable test log- suiteLog <- fmap (\s -> (\l -> l { logFile = finalLogName l })- . fromMaybe (error $ "panic! read @TestSuiteLog " ++ show s) $ readMaybe s) -- TODO: eradicateNoParse- $ readFile tempLog+ -- Generate final log file name+ let finalLogName l =+ interpretSymbolicPath mbWorkDir testLogDir+ </> testSuiteLogPath+ (fromFlag $ testHumanLog flags)+ pkg_descr+ lbi+ (unUnqualComponentName $ testSuiteName l)+ (testLogs l)+ -- Generate TestSuiteLog from executable exit code and a machine-+ -- readable test log+ suiteLog <-+ fmap+ ( \s ->+ (\l -> l{logFile = finalLogName l})+ . fromMaybe (error $ "panic! read @TestSuiteLog " ++ show s)+ $ readMaybe s -- TODO: eradicateNoParse+ )+ $ readFile tempLog - -- Write summary notice to log file indicating start of test suite- appendFile (logFile suiteLog) $ summarizeSuiteStart testName'+ -- Write summary notice to log file indicating start of test suite+ appendFile (logFile suiteLog) $ summarizeSuiteStart testName' - LBS.appendFile (logFile suiteLog) logText+ LBS.appendFile (logFile suiteLog) logText - -- Write end-of-suite summary notice to log file- appendFile (logFile suiteLog) $ summarizeSuiteFinish suiteLog+ -- Write end-of-suite summary notice to log file+ appendFile (logFile suiteLog) $ summarizeSuiteFinish suiteLog - -- Show the contents of the human-readable log file on the terminal- -- if there is a failure and/or detailed output is requested- let details = fromFlag $ testShowDetails flags- whenPrinting = when $ (details > Never)- && (not (suitePassed $ testLogs suiteLog) || details == Always)- && verbosity >= normal- whenPrinting $ do- LBS.putStr logText- putChar '\n'+ -- Show the contents of the human-readable log file on the terminal+ -- if there is a failure and/or detailed output is requested+ let details = fromFlag $ testShowDetails flags+ whenPrinting =+ when $+ (details > Never)+ && (not (suitePassed $ testLogs suiteLog) || details == Always)+ && verbosity >= normal+ whenPrinting $ do+ LBS.putStr logText+ putChar '\n' - return suiteLog+ return suiteLog - -- Write summary notice to terminal indicating end of test suite- notice verbosity $ summarizeSuiteFinish suiteLog+ -- Write summary notice to terminal indicating end of test suite+ notice verbosity $ summarizeSuiteFinish suiteLog - when isCoverageEnabled $- case PD.library pkg_descr of- Nothing ->- die' verbosity $ "Error: test coverage is only supported for packages with a library component"+ when isCoverageEnabled $ do+ -- Until #9493 is fixed, we expect cabal-install to pass one dist dir per+ -- library and there being at least one library in the package with the+ -- testsuite. When it is fixed, we can remove this predicate and allow a+ -- testsuite without a library to cover libraries in other packages of the+ -- same project+ when (null $ PD.allLibraries pkg_descr) $+ dieWithException verbosity TestCoverageSupport - Just library ->- markupTest verbosity lbi distPref (prettyShow $ PD.package pkg_descr) suite library+ markupPackage verbosity hpcMarkupInfo lbi distPref pkg_descr [suite] - return suiteLog+ return suiteLog where+ i = LBI.interpretSymbolicPathLBI lbi+ common = testCommonFlags flags testName' = unUnqualComponentName $ PD.testName suite deleteIfExists file = do- exists <- doesFileExist file- when exists $ removeFile file+ exists <- doesFileExist file+ when exists $ removeFile file - testLogDir = distPref </> "test"+ testLogDir = distPref </> makeRelativePathEx "test" openCabalTemp = do- (f, h) <- openTempFile testLogDir $ "cabal-test-" <.> "log"- hClose h >> return f+ (f, h) <- openTempFile (i testLogDir) $ "cabal-test-" <.> "log"+ hClose h >> return f - distPref = fromFlag $ testDistPref flags- verbosity = fromFlag $ testVerbosity flags+ distPref = fromFlag $ setupDistPref common+ verbosity = fromFlag $ setupVerbosity common -- TODO: This is abusing the notion of a 'PathTemplate'. The result isn't -- necessarily a path.-testOption :: PD.PackageDescription- -> LBI.LocalBuildInfo- -> PD.TestSuite- -> PathTemplate- -> String+testOption+ :: PD.PackageDescription+ -> LBI.LocalBuildInfo+ -> PD.TestSuite+ -> PathTemplate+ -> String testOption pkg_descr lbi suite template =- fromPathTemplate $ substPathTemplate env template+ fromPathTemplate $ substPathTemplate env template where- env = initialPathTemplateEnv- (PD.package pkg_descr) (LBI.localUnitId lbi)- (compilerInfo $ LBI.compiler lbi) (LBI.hostPlatform lbi) ++- [(TestSuiteNameVar, toPathTemplate $ unUnqualComponentName $ PD.testName suite)]+ env =+ initialPathTemplateEnv+ (PD.package pkg_descr)+ (LBI.localUnitId lbi)+ (compilerInfo $ LBI.compiler lbi)+ (LBI.hostPlatform lbi)+ ++ [(TestSuiteNameVar, toPathTemplate $ unUnqualComponentName $ PD.testName suite)] -- Test stub ---------- --- | The name of the stub executable associated with a library 'TestSuite'.-stubName :: PD.TestSuite -> FilePath-stubName t = unUnqualComponentName (PD.testName t) ++ "Stub"- -- | The filename of the source file for the stub executable associated with a -- library 'TestSuite'. stubFilePath :: PD.TestSuite -> FilePath stubFilePath t = stubName t <.> "hs" -- | Write the source file for a library 'TestSuite' stub executable.-writeSimpleTestStub :: PD.TestSuite -- ^ library 'TestSuite' for which a stub- -- is being created- -> FilePath -- ^ path to directory where stub source- -- should be located- -> IO ()+writeSimpleTestStub+ :: PD.TestSuite+ -- ^ library 'TestSuite' for which a stub+ -- is being created+ -> FilePath+ -- ^ path to directory where stub source+ -- should be located+ -> IO () writeSimpleTestStub t dir = do- createDirectoryIfMissing True dir- let filename = dir </> stubFilePath t- m = case PD.testInterface t of- PD.TestSuiteLibV09 _ m' -> m'- _ -> error "writeSimpleTestStub: invalid TestSuite passed"- writeFile filename $ simpleTestStub m+ createDirectoryIfMissing True dir+ let filename = dir </> stubFilePath t+ m = case PD.testInterface t of+ PD.TestSuiteLibV09 _ m' -> m'+ _ -> error "writeSimpleTestStub: invalid TestSuite passed"+ writeFile filename $ simpleTestStub m -- | Source code for library test suite stub executable simpleTestStub :: ModuleName -> String-simpleTestStub m = unlines+simpleTestStub m =+ unlines [ "module Main ( main ) where" , "import Distribution.Simple.Test.LibV09 ( stubMain )" , "import " ++ show (pretty m) ++ " ( tests )"@@ -236,18 +281,22 @@ -- of detectable errors when Cabal is compiled. stubMain :: IO [Test] -> IO () stubMain tests = do- (f, n) <- fmap (\s -> fromMaybe (error $ "panic! read " ++ show s) $ readMaybe s) getContents -- TODO: eradicateNoParse- dir <- getCurrentDirectory- results <- (tests >>= stubRunTests) `CE.catch` errHandler- setCurrentDirectory dir- stubWriteLog f n results+ (f, n) <- fmap (\s -> fromMaybe (error $ "panic! read " ++ show s) $ readMaybe s) getContents -- TODO: eradicateNoParse+ dir <- getCurrentDirectory+ results <- (tests >>= stubRunTests) `CE.catch` errHandler+ setCurrentDirectory dir+ stubWriteLog f n results where errHandler :: CE.SomeException -> IO TestLogs errHandler e = case CE.fromException e of- Just CE.UserInterrupt -> CE.throwIO e- _ -> return $ TestLog { testName = "Cabal test suite exception",- testOptionsReturned = [],- testResult = Error $ show e }+ Just CE.UserInterrupt -> CE.throwIO e+ _ ->+ return $+ TestLog+ { testName = "Cabal test suite exception"+ , testOptionsReturned = []+ , testResult = Error $ show e+ } -- | The test runner used in library "TestSuite" stub executables. Runs a list -- of 'Test's. An executable calling this function is meant to be invoked as@@ -258,35 +307,36 @@ -- by the calling Cabal process. stubRunTests :: [Test] -> IO TestLogs stubRunTests tests = do- logs <- traverse stubRunTests' tests- return $ GroupLogs "Default" logs+ logs <- traverse stubRunTests' tests+ return $ GroupLogs "Default" logs where stubRunTests' (Test t) = do- l <- run t >>= finish- summarizeTest normal Always l- return l+ l <- run t >>= finish+ summarizeTest normal Always l+ return l where finish (Finished result) =- return TestLog- { testName = name t- , testOptionsReturned = defaultOptions t- , testResult = result- }+ return+ TestLog+ { testName = name t+ , testOptionsReturned = defaultOptions t+ , testResult = result+ } finish (Progress _ next) = next >>= finish- stubRunTests' g@(Group {}) = do- logs <- traverse stubRunTests' $ groupTests g- return $ GroupLogs (groupName g) logs+ stubRunTests' g@(Group{}) = do+ logs <- traverse stubRunTests' $ groupTests g+ return $ GroupLogs (groupName g) logs stubRunTests' (ExtraOptions _ t) = stubRunTests' t maybeDefaultOption opt =- maybe Nothing (\d -> Just (optionName opt, d)) $ optionDefault opt+ maybe Nothing (\d -> Just (optionName opt, d)) $ optionDefault opt defaultOptions testInst = mapMaybe maybeDefaultOption $ options testInst -- | From a test stub, write the 'TestSuiteLog' to temporary file for the calling -- Cabal process to read. stubWriteLog :: FilePath -> UnqualComponentName -> TestLogs -> IO () stubWriteLog f n logs = do- let testLog = TestSuiteLog { testSuiteName = n, testLogs = logs, logFile = f }- writeFile (logFile testLog) $ show testLog- when (suiteError logs) $ exitWith $ ExitFailure 2- when (suiteFailed logs) $ exitWith $ ExitFailure 1- exitSuccess+ let testLog = TestSuiteLog{testSuiteName = n, testLogs = logs, logFile = f}+ writeFile (logFile testLog) $ show testLog+ when (suiteError logs) $ exitWith $ ExitFailure 2+ when (suiteFailed logs) $ exitWith $ ExitFailure 1+ exitSuccess
src/Distribution/Simple/Test/Log.hs view
@@ -2,49 +2,53 @@ {-# LANGUAGE RankNTypes #-} module Distribution.Simple.Test.Log- ( PackageLog(..)- , TestLogs(..)- , TestSuiteLog(..)- , countTestResults- , localPackageLog- , summarizePackage- , summarizeSuiteFinish, summarizeSuiteStart- , summarizeTest- , suiteError, suiteFailed, suitePassed- , testSuiteLogPath- ) where+ ( PackageLog (..)+ , TestLogs (..)+ , TestSuiteLog (..)+ , countTestResults+ , localPackageLog+ , summarizePackage+ , summarizeSuiteFinish+ , summarizeSuiteStart+ , summarizeTest+ , suiteError+ , suiteFailed+ , suitePassed+ , testSuiteLogPath+ ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () import Distribution.Package-import Distribution.Types.UnqualComponentName import qualified Distribution.PackageDescription as PD+import Distribution.Pretty import Distribution.Simple.Compiler import Distribution.Simple.InstallDirs import qualified Distribution.Simple.LocalBuildInfo as LBI-import Distribution.Simple.Setup+import Distribution.Simple.Setup.Test (TestShowDetails (Always, Never)) import Distribution.Simple.Utils import Distribution.System import Distribution.TestSuite+import Distribution.Types.UnqualComponentName import Distribution.Verbosity-import Distribution.Pretty import qualified Prelude (foldl1) -- | Logs all test results for a package, broken down first by test suite and -- then by test case. data PackageLog = PackageLog- { package :: PackageId- , compiler :: CompilerId- , platform :: Platform- , testSuites :: [TestSuiteLog]- }- deriving (Read, Show, Eq)+ { package :: PackageId+ , compiler :: CompilerId+ , platform :: Platform+ , testSuites :: [TestSuiteLog]+ }+ deriving (Read, Show, Eq) -- | A 'PackageLog' with package and platform information specified. localPackageLog :: PD.PackageDescription -> LBI.LocalBuildInfo -> PackageLog-localPackageLog pkg_descr lbi = PackageLog+localPackageLog pkg_descr lbi =+ PackageLog { package = PD.package pkg_descr , compiler = compilerId $ LBI.compiler lbi , platform = LBI.hostPlatform lbi@@ -53,114 +57,137 @@ -- | Logs test suite results, itemized by test case. data TestSuiteLog = TestSuiteLog- { testSuiteName :: UnqualComponentName- , testLogs :: TestLogs- , logFile :: FilePath -- path to human-readable log file- }- deriving (Read, Show, Eq)+ { testSuiteName :: UnqualComponentName+ , testLogs :: TestLogs+ , logFile :: FilePath -- path to human-readable log file+ }+ deriving (Read, Show, Eq) data TestLogs- = TestLog- { testName :: String- , testOptionsReturned :: Options- , testResult :: Result- }- | GroupLogs String [TestLogs]- deriving (Read, Show, Eq)+ = TestLog+ { testName :: String+ , testOptionsReturned :: Options+ , testResult :: Result+ }+ | GroupLogs String [TestLogs]+ deriving (Read, Show, Eq) -- | Count the number of pass, fail, and error test results in a 'TestLogs' -- tree.-countTestResults :: TestLogs- -> (Int, Int, Int) -- ^ Passes, fails, and errors,- -- respectively.+countTestResults+ :: TestLogs+ -> (Int, Int, Int)+ -- ^ Passes, fails, and errors,+ -- respectively. countTestResults = go (0, 0, 0) where- go (p, f, e) (TestLog { testResult = r }) =- case r of- Pass -> (p + 1, f, e)- Fail _ -> (p, f + 1, e)- Error _ -> (p, f, e + 1)+ go (p, f, e) (TestLog{testResult = r}) =+ case r of+ Pass -> (p + 1, f, e)+ Fail _ -> (p, f + 1, e)+ Error _ -> (p, f, e + 1) go (p, f, e) (GroupLogs _ ts) = foldl go (p, f, e) ts -- | From a 'TestSuiteLog', determine if the test suite passed. suitePassed :: TestLogs -> Bool suitePassed l =- case countTestResults l of- (_, 0, 0) -> True- _ -> False+ case countTestResults l of+ (_, 0, 0) -> True+ _ -> False -- | From a 'TestSuiteLog', determine if the test suite failed. suiteFailed :: TestLogs -> Bool suiteFailed l =- case countTestResults l of- (_, 0, _) -> False- _ -> True+ case countTestResults l of+ (_, 0, _) -> False+ _ -> True -- | From a 'TestSuiteLog', determine if the test suite encountered errors. suiteError :: TestLogs -> Bool suiteError l =- case countTestResults l of- (_, _, 0) -> False- _ -> True+ case countTestResults l of+ (_, _, 0) -> False+ _ -> True resultString :: TestLogs -> String-resultString l | suiteError l = "error"- | suiteFailed l = "fail"- | otherwise = "pass"+resultString l+ | suiteError l = "error"+ | suiteFailed l = "fail"+ | otherwise = "pass" -testSuiteLogPath :: PathTemplate- -> PD.PackageDescription- -> LBI.LocalBuildInfo- -> String -- ^ test suite name- -> TestLogs -- ^ test suite results- -> FilePath+testSuiteLogPath+ :: PathTemplate+ -> PD.PackageDescription+ -> LBI.LocalBuildInfo+ -> String+ -- ^ test suite name+ -> TestLogs+ -- ^ test suite results+ -> FilePath testSuiteLogPath template pkg_descr lbi test_name result =- fromPathTemplate $ substPathTemplate env template- where- env = initialPathTemplateEnv- (PD.package pkg_descr) (LBI.localUnitId lbi)- (compilerInfo $ LBI.compiler lbi) (LBI.hostPlatform lbi)- ++ [ (TestSuiteNameVar, toPathTemplate test_name)- , (TestSuiteResultVar, toPathTemplate $ resultString result)- ]+ fromPathTemplate $ substPathTemplate env template+ where+ env =+ initialPathTemplateEnv+ (PD.package pkg_descr)+ (LBI.localUnitId lbi)+ (compilerInfo $ LBI.compiler lbi)+ (LBI.hostPlatform lbi)+ ++ [ (TestSuiteNameVar, toPathTemplate test_name)+ , (TestSuiteResultVar, toPathTemplate $ resultString result)+ ] -- | Print a summary to the console after all test suites have been run -- indicating the number of successful test suites and cases. Returns 'True' if -- all test suites passed and 'False' otherwise. summarizePackage :: Verbosity -> PackageLog -> IO Bool summarizePackage verbosity packageLog = do- let counts = map (countTestResults . testLogs) $ testSuites packageLog- (passed, failed, errors) = Prelude.foldl1 addTriple counts- totalCases = passed + failed + errors- passedSuites = length- $ filter (suitePassed . testLogs)- $ testSuites packageLog- totalSuites = length $ testSuites packageLog- notice verbosity $ show passedSuites ++ " of " ++ show totalSuites- ++ " test suites (" ++ show passed ++ " of "- ++ show totalCases ++ " test cases) passed."- return $! passedSuites == totalSuites+ let counts = map (countTestResults . testLogs) $ testSuites packageLog+ (passed, failed, errors) = Prelude.foldl1 addTriple counts+ totalCases = passed + failed + errors+ passedSuites =+ length $+ filter (suitePassed . testLogs) $+ testSuites packageLog+ totalSuites = length $ testSuites packageLog+ notice verbosity $+ show passedSuites+ ++ " of "+ ++ show totalSuites+ ++ " test suites ("+ ++ show passed+ ++ " of "+ ++ show totalCases+ ++ " test cases) passed."+ return $! passedSuites == totalSuites where addTriple (p1, f1, e1) (p2, f2, e2) = (p1 + p2, f1 + f2, e1 + e2) -- | Print a summary of a single test case's result to the console, suppressing -- output for certain verbosity or test filter levels. summarizeTest :: Verbosity -> TestShowDetails -> TestLogs -> IO ()-summarizeTest _ _ (GroupLogs {}) = return ()+summarizeTest _ _ (GroupLogs{}) = return () summarizeTest verbosity details t =- when shouldPrint $ notice verbosity $ "Test case " ++ testName t- ++ ": " ++ show (testResult t)- where shouldPrint = (details > Never) && (notPassed || details == Always)- notPassed = testResult t /= Pass+ when shouldPrint $+ notice verbosity $+ "Test case "+ ++ testName t+ ++ ": "+ ++ show (testResult t)+ where+ shouldPrint = (details > Never) && (notPassed || details == Always)+ notPassed = testResult t /= Pass -- | Print a summary of the test suite's results on the console, suppressing -- output for certain verbosity or test filter levels. summarizeSuiteFinish :: TestSuiteLog -> String-summarizeSuiteFinish testLog = unlines+summarizeSuiteFinish testLog =+ unlines [ "Test suite " ++ prettyShow (testSuiteName testLog) ++ ": " ++ resStr , "Test suite logged to: " ++ logFile testLog ]- where resStr = map toUpper (resultString $ testLogs testLog)+ where+ resStr = map toUpper (resultString $ testLogs testLog) summarizeSuiteStart :: String -> String summarizeSuiteStart n = "Test suite " ++ n ++ ": RUNNING...\n"
src/Distribution/Simple/UHC.hs view
@@ -1,7 +1,9 @@+{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.UHC -- Copyright : Andres Loeh 2009@@ -16,57 +18,66 @@ -- Thanks to the authors of the other implementation-specific files, in -- particular to Isaac Jones, Duncan Coutts and Henning Thielemann, for -- inspiration on how to design this module.--module Distribution.Simple.UHC (- configure, getInstalledPackages,- buildLib, buildExe, installLib, registerPackage, inplacePackageDbPath+module Distribution.Simple.UHC+ ( configure+ , getInstalledPackages+ , buildLib+ , buildExe+ , installLib+ , registerPackage+ , inplacePackageDbPath ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () import Distribution.InstalledPackageInfo import Distribution.Package hiding (installedUnitId) import Distribution.PackageDescription+import Distribution.Parsec+import Distribution.Pretty import Distribution.Simple.BuildPaths import Distribution.Simple.Compiler import Distribution.Simple.LocalBuildInfo import Distribution.Simple.PackageIndex import Distribution.Simple.Program import Distribution.Simple.Utils-import Distribution.Pretty-import Distribution.Parsec+import Distribution.System import Distribution.Types.MungedPackageId+import Distribution.Utils.Path import Distribution.Verbosity import Distribution.Version-import Distribution.System import Language.Haskell.Extension-import Distribution.Utils.Path -import qualified Data.Map as Map ( empty )+import qualified Data.Map as Map (empty) import System.Directory-import System.FilePath+import System.FilePath (pathSeparator) -- ----------------------------------------------------------------------------- -- Configuring -configure :: Verbosity -> Maybe FilePath -> Maybe FilePath- -> ProgramDb -> IO (Compiler, Maybe Platform, ProgramDb)-configure verbosity hcPath _hcPkgPath progdb = do-+configure+ :: Verbosity+ -> Maybe FilePath+ -> ProgramDb+ -> IO (Compiler, Maybe Platform, ProgramDb)+configure verbosity hcPath progdb = do (_uhcProg, uhcVersion, progdb') <-- requireProgramVersion verbosity uhcProgram- (orLaterVersion (mkVersion [1,0,2]))- (userMaybeSpecifyPath "uhc" hcPath progdb)+ requireProgramVersion+ verbosity+ uhcProgram+ (orLaterVersion (mkVersion [1, 0, 2]))+ (userMaybeSpecifyPath "uhc" hcPath progdb) - let comp = Compiler {- compilerId = CompilerId UHC uhcVersion,- compilerAbiTag = NoAbiTag,- compilerCompat = [],- compilerLanguages = uhcLanguages,- compilerExtensions = uhcLanguageExtensions,- compilerProperties = Map.empty- }+ let comp =+ Compiler+ { compilerId = CompilerId UHC uhcVersion+ , compilerAbiTag = NoAbiTag+ , compilerCompat = []+ , compilerLanguages = uhcLanguages+ , compilerExtensions = uhcLanguageExtensions+ , compilerProperties = Map.empty+ } compPlatform = Nothing return (comp, compPlatform, progdb') @@ -76,64 +87,84 @@ -- | The flags for the supported extensions. uhcLanguageExtensions :: [(Extension, Maybe CompilerFlag)] uhcLanguageExtensions =- let doFlag (f, (enable, disable)) = [(EnableExtension f, enable),- (DisableExtension f, disable)]- alwaysOn = (Nothing, Nothing{- wrong -})- in concatMap doFlag- [(CPP, (Just "--cpp", Nothing{- wrong -})),- (PolymorphicComponents, alwaysOn),- (ExistentialQuantification, alwaysOn),- (ForeignFunctionInterface, alwaysOn),- (UndecidableInstances, alwaysOn),- (MultiParamTypeClasses, alwaysOn),- (Rank2Types, alwaysOn),- (PatternSignatures, alwaysOn),- (EmptyDataDecls, alwaysOn),- (ImplicitPrelude, (Nothing, Just "--no-prelude"{- wrong -})),- (TypeOperators, alwaysOn),- (OverlappingInstances, alwaysOn),- (FlexibleInstances, alwaysOn)]+ let doFlag (f, (enable, disable)) =+ [ (EnableExtension f, enable)+ , (DisableExtension f, disable)+ ]+ alwaysOn = (Nothing, Nothing {- wrong -})+ in concatMap+ doFlag+ [ (CPP, (Just "--cpp", Nothing {- wrong -}))+ , (PolymorphicComponents, alwaysOn)+ , (ExistentialQuantification, alwaysOn)+ , (ForeignFunctionInterface, alwaysOn)+ , (UndecidableInstances, alwaysOn)+ , (MultiParamTypeClasses, alwaysOn)+ , (Rank2Types, alwaysOn)+ , (PatternSignatures, alwaysOn)+ , (EmptyDataDecls, alwaysOn)+ , (ImplicitPrelude, (Nothing, Just "--no-prelude" {- wrong -}))+ , (TypeOperators, alwaysOn)+ , (OverlappingInstances, alwaysOn)+ , (FlexibleInstances, alwaysOn)+ ] -getInstalledPackages :: Verbosity -> Compiler -> PackageDBStack -> ProgramDb- -> IO InstalledPackageIndex-getInstalledPackages verbosity comp packagedbs progdb = do+getInstalledPackages+ :: Verbosity+ -> Compiler+ -> Maybe (SymbolicPath CWD (Dir from))+ -> PackageDBStackX (SymbolicPath from (Dir PkgDB))+ -> ProgramDb+ -> IO InstalledPackageIndex+getInstalledPackages verbosity comp mbWorkDir packagedbs progdb = do let compilerid = compilerId comp systemPkgDir <- getGlobalPackageDir verbosity progdb- userPkgDir <- getUserPackageDir- let pkgDirs = nub (concatMap (packageDbPaths userPkgDir systemPkgDir) packagedbs)+ userPkgDir <- getUserPackageDir+ let pkgDirs = nub (concatMap (packageDbPaths userPkgDir systemPkgDir mbWorkDir) packagedbs) -- putStrLn $ "pkgdirs: " ++ show pkgDirs- pkgs <- liftM (map addBuiltinVersions . concat) $- traverse (\ d -> getDirectoryContents d >>= filterM (isPkgDir (prettyShow compilerid) d))- pkgDirs+ pkgs <-+ liftM (map addBuiltinVersions . concat) $+ traverse+ (\d -> getDirectoryContents d >>= filterM (isPkgDir (prettyShow compilerid) d))+ pkgDirs -- putStrLn $ "pkgs: " ++ show pkgs let iPkgs = map mkInstalledPackageInfo $- concatMap parsePackage $- pkgs+ concatMap parsePackage $+ pkgs -- putStrLn $ "installed pkgs: " ++ show iPkgs return (fromList iPkgs) getGlobalPackageDir :: Verbosity -> ProgramDb -> IO FilePath getGlobalPackageDir verbosity progdb = do- output <- getDbProgramOutput verbosity- uhcProgram progdb ["--meta-pkgdir-system"]- -- we need to trim because pkgdir contains an extra newline at the end- let pkgdir = trimEnd output- return pkgdir+ output <-+ getDbProgramOutput+ verbosity+ uhcProgram+ progdb+ ["--meta-pkgdir-system"]+ -- we need to trim because pkgdir contains an extra newline at the end+ let pkgdir = trimEnd output+ return pkgdir where- trimEnd = reverse . dropWhile isSpace . reverse+ trimEnd = dropWhileEnd isSpace getUserPackageDir :: IO FilePath getUserPackageDir = do- homeDir <- getHomeDirectory- return $ homeDir </> ".cabal" </> "lib" -- TODO: determine in some other way+ homeDir <- getHomeDirectory+ return $ homeDir </> ".cabal" </> "lib" -- TODO: determine in some other way -packageDbPaths :: FilePath -> FilePath -> PackageDB -> [FilePath]-packageDbPaths user system db =+packageDbPaths+ :: FilePath+ -> FilePath+ -> Maybe (SymbolicPath CWD (Dir from))+ -> PackageDBX (SymbolicPath from (Dir PkgDB))+ -> [FilePath]+packageDbPaths user system mbWorkDir db = case db of- GlobalPackageDB -> [ system ]- UserPackageDB -> [ user ]- SpecificPackageDB path -> [ path ]+ GlobalPackageDB -> [system]+ UserPackageDB -> [user]+ SpecificPackageDB path -> [interpretSymbolicPath mbWorkDir path] -- | Hack to add version numbers to UHC-built-in packages. This should sooner or -- later be fixed on the UHC side.@@ -143,7 +174,7 @@ addBuiltinVersions "base" = "base-3.0" addBuiltinVersions "array" = "array-0.2" -}-addBuiltinVersions xs = xs+addBuiltinVersions xs = xs -- | Name of the installed package config file. installedPkgConfig :: String@@ -153,144 +184,194 @@ -- looking only for the presence of an installed package configuration. -- TODO: Actually make use of the information provided in the file. isPkgDir :: String -> String -> String -> IO Bool-isPkgDir _ _ ('.' : _) = return False -- ignore files starting with a .-isPkgDir c dir xs = do- let candidate = dir </> uhcPackageDir xs c- -- putStrLn $ "trying: " ++ candidate- doesFileExist (candidate </> installedPkgConfig)+isPkgDir _ _ ('.' : _) = return False -- ignore files starting with a .+isPkgDir c dir xs = do+ let candidate = dir </> uhcPackageDir xs c+ -- putStrLn $ "trying: " ++ candidate+ doesFileExist (candidate </> installedPkgConfig) parsePackage :: String -> [PackageId]-parsePackage = toList . simpleParsec+parsePackage = toList . simpleParsec -- | Create a trivial package info from a directory name. mkInstalledPackageInfo :: PackageId -> InstalledPackageInfo-mkInstalledPackageInfo p = emptyInstalledPackageInfo- { installedUnitId = mkLegacyUnitId p,- sourcePackageId = p }-+mkInstalledPackageInfo p =+ emptyInstalledPackageInfo+ { installedUnitId = mkLegacyUnitId p+ , sourcePackageId = p+ } -- ----------------------------------------------------------------------------- -- Building -buildLib :: Verbosity -> PackageDescription -> LocalBuildInfo- -> Library -> ComponentLocalBuildInfo -> IO ()+buildLib+ :: Verbosity+ -> PackageDescription+ -> LocalBuildInfo+ -> Library+ -> ComponentLocalBuildInfo+ -> IO () buildLib verbosity pkg_descr lbi lib clbi = do- systemPkgDir <- getGlobalPackageDir verbosity (withPrograms lbi)- userPkgDir <- getUserPackageDir- let runUhcProg = runDbProgram verbosity uhcProgram (withPrograms lbi)- let uhcArgs = -- set package name- ["--pkg-build=" ++ prettyShow (packageId pkg_descr)]- -- common flags lib/exe- ++ constructUHCCmdLine userPkgDir systemPkgDir- lbi (libBuildInfo lib) clbi- (buildDir lbi) verbosity- -- source files- -- suboptimal: UHC does not understand module names, so- -- we replace periods by path separators- ++ map (map (\ c -> if c == '.' then pathSeparator else c))- (map prettyShow (allLibModules lib clbi))+ userPkgDir <- getUserPackageDir+ let runUhcProg = runDbProgramCwd verbosity (mbWorkDirLBI lbi) uhcProgram (withPrograms lbi)+ let uhcArgs =+ -- set package name+ ["--pkg-build=" ++ prettyShow (packageId pkg_descr)]+ -- common flags lib/exe+ ++ constructUHCCmdLine+ userPkgDir+ systemPkgDir+ lbi+ (libBuildInfo lib)+ clbi+ (buildDir lbi)+ verbosity+ -- source files+ -- suboptimal: UHC does not understand module names, so+ -- we replace periods by path separators+ ++ map+ (map (\c -> if c == '.' then pathSeparator else c))+ (map prettyShow (allLibModules lib clbi)) runUhcProg uhcArgs return () -buildExe :: Verbosity -> PackageDescription -> LocalBuildInfo- -> Executable -> ComponentLocalBuildInfo -> IO ()+buildExe+ :: Verbosity+ -> PackageDescription+ -> LocalBuildInfo+ -> Executable+ -> ComponentLocalBuildInfo+ -> IO () buildExe verbosity _pkg_descr lbi exe clbi = do systemPkgDir <- getGlobalPackageDir verbosity (withPrograms lbi)- userPkgDir <- getUserPackageDir- let runUhcProg = runDbProgram verbosity uhcProgram (withPrograms lbi)- let uhcArgs = -- common flags lib/exe- constructUHCCmdLine userPkgDir systemPkgDir- lbi (buildInfo exe) clbi- (buildDir lbi) verbosity- -- output file- ++ ["--output", buildDir lbi </> prettyShow (exeName exe)]- -- main source module- ++ [modulePath exe]+ userPkgDir <- getUserPackageDir+ let mbWorkDir = mbWorkDirLBI lbi+ srcMainPath <- findFileCwd verbosity mbWorkDir (hsSourceDirs $ buildInfo exe) (modulePath exe)+ let runUhcProg = runDbProgramCwd verbosity (mbWorkDirLBI lbi) uhcProgram (withPrograms lbi)+ u = interpretSymbolicPathCWD+ uhcArgs =+ -- common flags lib/exe+ constructUHCCmdLine+ userPkgDir+ systemPkgDir+ lbi+ (buildInfo exe)+ clbi+ (buildDir lbi)+ verbosity+ -- output file+ ++ ["--output", u $ buildDir lbi </> makeRelativePathEx (prettyShow (exeName exe))]+ -- main source module+ ++ [u $ srcMainPath] runUhcProg uhcArgs -constructUHCCmdLine :: FilePath -> FilePath- -> LocalBuildInfo -> BuildInfo -> ComponentLocalBuildInfo- -> FilePath -> Verbosity -> [String]+constructUHCCmdLine+ :: FilePath+ -> FilePath+ -> LocalBuildInfo+ -> BuildInfo+ -> ComponentLocalBuildInfo+ -> SymbolicPath Pkg (Dir Build)+ -> Verbosity+ -> [String] constructUHCCmdLine user system lbi bi clbi odir verbosity =- -- verbosity- (if verbosity >= deafening then ["-v4"]- else if verbosity >= normal then []- else ["-v0"])- ++ hcOptions UHC bi- -- flags for language extensions- ++ languageToFlags (compiler lbi) (defaultLanguage bi)- ++ extensionsToFlags (compiler lbi) (usedExtensions bi)- -- packages- ++ ["--hide-all-packages"]- ++ uhcPackageDbOptions user system (withPackageDB lbi)- ++ ["--package=uhcbase"]- ++ ["--package=" ++ prettyShow (mungedName pkgid) | (_, pkgid) <- componentPackageDeps clbi ]- -- search paths- ++ ["-i" ++ odir]- ++ ["-i" ++ getSymbolicPath l | l <- nub (hsSourceDirs bi)]- ++ ["-i" ++ autogenComponentModulesDir lbi clbi]- ++ ["-i" ++ autogenPackageModulesDir lbi]- -- cpp options- ++ ["--optP=" ++ opt | opt <- cppOptions bi]- -- output path- ++ ["--odir=" ++ odir]- -- optimization- ++ (case withOptimization lbi of- NoOptimisation -> ["-O0"]- NormalOptimisation -> ["-O1"]- MaximumOptimisation -> ["-O2"])+ -- verbosity+ ( if verbosity >= deafening+ then ["-v4"]+ else+ if verbosity >= normal+ then []+ else ["-v0"]+ )+ ++ hcOptions UHC bi+ -- flags for language extensions+ ++ languageToFlags (compiler lbi) (defaultLanguage bi)+ ++ extensionsToFlags (compiler lbi) (usedExtensions bi)+ -- packages+ ++ ["--hide-all-packages"]+ ++ uhcPackageDbOptions user system (withPackageDB lbi)+ ++ ["--package=uhcbase"]+ ++ ["--package=" ++ prettyShow (mungedName pkgid) | (_, pkgid) <- componentPackageDeps clbi]+ -- search paths+ ++ ["-i" ++ u odir]+ ++ ["-i" ++ u l | l <- nub (hsSourceDirs bi)]+ ++ ["-i" ++ u (autogenComponentModulesDir lbi clbi)]+ ++ ["-i" ++ u (autogenPackageModulesDir lbi)]+ -- cpp options+ ++ ["--optP=" ++ opt | opt <- cppOptions bi]+ -- output path+ ++ ["--odir=" ++ u odir]+ -- optimization+ ++ ( case withOptimization lbi of+ NoOptimisation -> ["-O0"]+ NormalOptimisation -> ["-O1"]+ MaximumOptimisation -> ["-O2"]+ )+ where+ u = interpretSymbolicPathCWD -- See Note [Symbolic paths] in Distribution.Utils.Path uhcPackageDbOptions :: FilePath -> FilePath -> PackageDBStack -> [String]-uhcPackageDbOptions user system db = map (\ x -> "--pkg-searchpath=" ++ x)- (concatMap (packageDbPaths user system) db)+uhcPackageDbOptions user system db =+ map+ (\x -> "--pkg-searchpath=" ++ x)+ (concatMap (packageDbPaths user system Nothing) db) -- ----------------------------------------------------------------------------- -- Installation -installLib :: Verbosity -> LocalBuildInfo- -> FilePath -> FilePath -> FilePath- -> PackageDescription -> Library -> ComponentLocalBuildInfo -> IO ()+installLib+ :: Verbosity+ -> LocalBuildInfo+ -> FilePath+ -> FilePath+ -> FilePath+ -> PackageDescription+ -> Library+ -> ComponentLocalBuildInfo+ -> IO () installLib verbosity _lbi targetDir _dynlibTargetDir builtDir pkg _library _clbi = do- -- putStrLn $ "dest: " ++ targetDir- -- putStrLn $ "built: " ++ builtDir- installDirectoryContents verbosity (builtDir </> prettyShow (packageId pkg)) targetDir+ -- putStrLn $ "dest: " ++ targetDir+ -- putStrLn $ "built: " ++ builtDir+ installDirectoryContents verbosity (builtDir </> prettyShow (packageId pkg)) targetDir -- currently hard-coded UHC code generator and variant to use uhcTarget, uhcTargetVariant :: String-uhcTarget = "bc"+uhcTarget = "bc" uhcTargetVariant = "plain" -- root directory for a package in UHC-uhcPackageDir :: String -> String -> FilePath-uhcPackageSubDir :: String -> FilePath-uhcPackageDir pkgid compilerid = pkgid </> uhcPackageSubDir compilerid-uhcPackageSubDir compilerid = compilerid </> uhcTarget </> uhcTargetVariant+uhcPackageDir :: String -> String -> FilePath+uhcPackageSubDir :: String -> FilePath+uhcPackageDir pkgid compilerid = pkgid </> uhcPackageSubDir compilerid+uhcPackageSubDir compilerid = compilerid </> uhcTarget </> uhcTargetVariant -- ----------------------------------------------------------------------------- -- Registering registerPackage :: Verbosity+ -> Maybe (SymbolicPath CWD (Dir from)) -> Compiler -> ProgramDb- -> PackageDBStack+ -> PackageDBStackS from -> InstalledPackageInfo -> IO ()-registerPackage verbosity comp progdb packageDbs installedPkgInfo = do- dbdir <- case registrationPackageDB packageDbs of- GlobalPackageDB -> getGlobalPackageDir verbosity progdb- UserPackageDB -> getUserPackageDir- SpecificPackageDB dir -> return dir- let pkgdir = dbdir </> uhcPackageDir (prettyShow pkgid) (prettyShow compilerid)- createDirectoryIfMissingVerbose verbosity True pkgdir- writeUTF8File (pkgdir </> installedPkgConfig)- (showInstalledPackageInfo installedPkgInfo)+registerPackage verbosity mbWorkDir comp progdb packageDbs installedPkgInfo = do+ dbdir <- case registrationPackageDB packageDbs of+ GlobalPackageDB -> getGlobalPackageDir verbosity progdb+ UserPackageDB -> getUserPackageDir+ SpecificPackageDB dir -> return (interpretSymbolicPath mbWorkDir dir)+ let pkgdir = dbdir </> uhcPackageDir (prettyShow pkgid) (prettyShow compilerid)+ createDirectoryIfMissingVerbose verbosity True pkgdir+ writeUTF8File+ (pkgdir </> installedPkgConfig)+ (showInstalledPackageInfo installedPkgInfo) where- pkgid = sourcePackageId installedPkgInfo+ pkgid = sourcePackageId installedPkgInfo compilerid = compilerId comp -inplacePackageDbPath :: LocalBuildInfo -> FilePath-inplacePackageDbPath lbi = buildDir lbi+inplacePackageDbPath :: LocalBuildInfo -> SymbolicPath Pkg (Dir PkgDB)+inplacePackageDbPath lbi = coerceSymbolicPath $ buildDir lbi
src/Distribution/Simple/UserHooks.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.UserHooks -- Copyright : Isaac Jones 2003-2005@@ -25,21 +26,21 @@ -- pass any extra parameters to most of the functions that implement the -- various phases because it would involve changing the types of the -- corresponding hook. At some point it will have to be replaced.--module Distribution.Simple.UserHooks (- UserHooks(..), Args,- emptyUserHooks,+module Distribution.Simple.UserHooks+ ( UserHooks (..)+ , Args+ , emptyUserHooks ) where +import Distribution.Compat.Prelude hiding (getContents, putStr) import Prelude ()-import Distribution.Compat.Prelude import Distribution.PackageDescription-import Distribution.Simple.Program import Distribution.Simple.Command+import Distribution.Simple.LocalBuildInfo import Distribution.Simple.PreProcess+import Distribution.Simple.Program import Distribution.Simple.Setup-import Distribution.Simple.LocalBuildInfo type Args = [String] @@ -49,149 +50,137 @@ -- * WARNING: The hooks interface is under rather constant flux as we try to -- understand users needs. Setup files that depend on this interface may -- break in future releases.-data UserHooks = UserHooks {-- -- | Read the description file- readDesc :: IO (Maybe GenericPackageDescription),- -- | Custom preprocessors in addition to and overriding 'knownSuffixHandlers'.- hookedPreProcessors :: [ PPSuffixHandler ],- -- | These programs are detected at configure time. Arguments for them are- -- added to the configure command.- hookedPrograms :: [Program],-- -- |Hook to run before configure command- preConf :: Args -> ConfigFlags -> IO HookedBuildInfo,- -- |Over-ride this hook to get different behavior during configure.- confHook :: (GenericPackageDescription, HookedBuildInfo)- -> ConfigFlags -> IO LocalBuildInfo,- -- |Hook to run after configure command- postConf :: Args -> ConfigFlags -> PackageDescription -> LocalBuildInfo -> IO (),-- -- |Hook to run before build command. Second arg indicates verbosity level.- preBuild :: Args -> BuildFlags -> IO HookedBuildInfo,-- -- |Over-ride this hook to get different behavior during build.- buildHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> BuildFlags -> IO (),- -- |Hook to run after build command. Second arg indicates verbosity level.- postBuild :: Args -> BuildFlags -> PackageDescription -> LocalBuildInfo -> IO (),-- -- |Hook to run before repl command. Second arg indicates verbosity level.- preRepl :: Args -> ReplFlags -> IO HookedBuildInfo,- -- |Over-ride this hook to get different behavior during interpretation.- replHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> ReplFlags -> [String] -> IO (),- -- |Hook to run after repl command. Second arg indicates verbosity level.- postRepl :: Args -> ReplFlags -> PackageDescription -> LocalBuildInfo -> IO (),-- -- |Hook to run before clean command. Second arg indicates verbosity level.- preClean :: Args -> CleanFlags -> IO HookedBuildInfo,- -- |Over-ride this hook to get different behavior during clean.- cleanHook :: PackageDescription -> () -> UserHooks -> CleanFlags -> IO (),- -- |Hook to run after clean command. Second arg indicates verbosity level.- postClean :: Args -> CleanFlags -> PackageDescription -> () -> IO (),-- -- |Hook to run before copy command- preCopy :: Args -> CopyFlags -> IO HookedBuildInfo,- -- |Over-ride this hook to get different behavior during copy.- copyHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> CopyFlags -> IO (),- -- |Hook to run after copy command- postCopy :: Args -> CopyFlags -> PackageDescription -> LocalBuildInfo -> IO (),-- -- |Hook to run before install command- preInst :: Args -> InstallFlags -> IO HookedBuildInfo,-- -- |Over-ride this hook to get different behavior during install.- instHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> InstallFlags -> IO (),- -- |Hook to run after install command. postInst should be run- -- on the target, not on the build machine.- postInst :: Args -> InstallFlags -> PackageDescription -> LocalBuildInfo -> IO (),-- -- |Hook to run before register command- preReg :: Args -> RegisterFlags -> IO HookedBuildInfo,- -- |Over-ride this hook to get different behavior during registration.- regHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> RegisterFlags -> IO (),- -- |Hook to run after register command- postReg :: Args -> RegisterFlags -> PackageDescription -> LocalBuildInfo -> IO (),-- -- |Hook to run before unregister command- preUnreg :: Args -> RegisterFlags -> IO HookedBuildInfo,- -- |Over-ride this hook to get different behavior during unregistration.- unregHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> RegisterFlags -> IO (),- -- |Hook to run after unregister command- postUnreg :: Args -> RegisterFlags -> PackageDescription -> LocalBuildInfo -> IO (),-- -- |Hook to run before hscolour command. Second arg indicates verbosity level.- preHscolour :: Args -> HscolourFlags -> IO HookedBuildInfo,- -- |Over-ride this hook to get different behavior during hscolour.- hscolourHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> HscolourFlags -> IO (),- -- |Hook to run after hscolour command. Second arg indicates verbosity level.- postHscolour :: Args -> HscolourFlags -> PackageDescription -> LocalBuildInfo -> IO (),-- -- |Hook to run before haddock command. Second arg indicates verbosity level.- preHaddock :: Args -> HaddockFlags -> IO HookedBuildInfo,- -- |Over-ride this hook to get different behavior during haddock.- haddockHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> HaddockFlags -> IO (),- -- |Hook to run after haddock command. Second arg indicates verbosity level.- postHaddock :: Args -> HaddockFlags -> PackageDescription -> LocalBuildInfo -> IO (),-- -- |Hook to run before test command.- preTest :: Args -> TestFlags -> IO HookedBuildInfo,- -- |Over-ride this hook to get different behavior during test.- testHook :: Args -> PackageDescription -> LocalBuildInfo -> UserHooks -> TestFlags -> IO (),- -- |Hook to run after test command.- postTest :: Args -> TestFlags -> PackageDescription -> LocalBuildInfo -> IO (),-- -- |Hook to run before bench command.- preBench :: Args -> BenchmarkFlags -> IO HookedBuildInfo,- -- |Over-ride this hook to get different behavior during bench.- benchHook :: Args -> PackageDescription -> LocalBuildInfo -> UserHooks -> BenchmarkFlags -> IO (),- -- |Hook to run after bench command.- postBench :: Args -> BenchmarkFlags -> PackageDescription -> LocalBuildInfo -> IO ()+data UserHooks = UserHooks+ { readDesc :: IO (Maybe GenericPackageDescription)+ -- ^ Read the description file+ , hookedPreProcessors :: [PPSuffixHandler]+ -- ^ Custom preprocessors in addition to and overriding 'knownSuffixHandlers'.+ , hookedPrograms :: [Program]+ -- ^ These programs are detected at configure time. Arguments for them are+ -- added to the configure command.+ , preConf :: Args -> ConfigFlags -> IO HookedBuildInfo+ -- ^ Hook to run before configure command+ , confHook+ :: (GenericPackageDescription, HookedBuildInfo)+ -> ConfigFlags+ -> IO LocalBuildInfo+ -- ^ Over-ride this hook to get different behavior during configure.+ , postConf :: Args -> ConfigFlags -> PackageDescription -> LocalBuildInfo -> IO ()+ -- ^ Hook to run after configure command+ , preBuild :: Args -> BuildFlags -> IO HookedBuildInfo+ -- ^ Hook to run before build command. Second arg indicates verbosity level.+ , buildHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> BuildFlags -> IO ()+ -- ^ Over-ride this hook to get different behavior during build.+ , postBuild :: Args -> BuildFlags -> PackageDescription -> LocalBuildInfo -> IO ()+ -- ^ Hook to run after build command. Second arg indicates verbosity level.+ , preRepl :: Args -> ReplFlags -> IO HookedBuildInfo+ -- ^ Hook to run before repl command. Second arg indicates verbosity level.+ , replHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> ReplFlags -> [String] -> IO ()+ -- ^ Over-ride this hook to get different behavior during interpretation.+ , postRepl :: Args -> ReplFlags -> PackageDescription -> LocalBuildInfo -> IO ()+ -- ^ Hook to run after repl command. Second arg indicates verbosity level.+ , preClean :: Args -> CleanFlags -> IO HookedBuildInfo+ -- ^ Hook to run before clean command. Second arg indicates verbosity level.+ , cleanHook :: PackageDescription -> () -> UserHooks -> CleanFlags -> IO ()+ -- ^ Over-ride this hook to get different behavior during clean.+ , postClean :: Args -> CleanFlags -> PackageDescription -> () -> IO ()+ -- ^ Hook to run after clean command. Second arg indicates verbosity level.+ , preCopy :: Args -> CopyFlags -> IO HookedBuildInfo+ -- ^ Hook to run before copy command+ , copyHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> CopyFlags -> IO ()+ -- ^ Over-ride this hook to get different behavior during copy.+ , postCopy :: Args -> CopyFlags -> PackageDescription -> LocalBuildInfo -> IO ()+ -- ^ Hook to run after copy command+ , preInst :: Args -> InstallFlags -> IO HookedBuildInfo+ -- ^ Hook to run before install command+ , instHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> InstallFlags -> IO ()+ -- ^ Over-ride this hook to get different behavior during install.+ , postInst :: Args -> InstallFlags -> PackageDescription -> LocalBuildInfo -> IO ()+ -- ^ Hook to run after install command. postInst should be run+ -- on the target, not on the build machine.+ , preReg :: Args -> RegisterFlags -> IO HookedBuildInfo+ -- ^ Hook to run before register command+ , regHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> RegisterFlags -> IO ()+ -- ^ Over-ride this hook to get different behavior during registration.+ , postReg :: Args -> RegisterFlags -> PackageDescription -> LocalBuildInfo -> IO ()+ -- ^ Hook to run after register command+ , preUnreg :: Args -> RegisterFlags -> IO HookedBuildInfo+ -- ^ Hook to run before unregister command+ , unregHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> RegisterFlags -> IO ()+ -- ^ Over-ride this hook to get different behavior during unregistration.+ , postUnreg :: Args -> RegisterFlags -> PackageDescription -> LocalBuildInfo -> IO ()+ -- ^ Hook to run after unregister command+ , preHscolour :: Args -> HscolourFlags -> IO HookedBuildInfo+ -- ^ Hook to run before hscolour command. Second arg indicates verbosity level.+ , hscolourHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> HscolourFlags -> IO ()+ -- ^ Over-ride this hook to get different behavior during hscolour.+ , postHscolour :: Args -> HscolourFlags -> PackageDescription -> LocalBuildInfo -> IO ()+ -- ^ Hook to run after hscolour command. Second arg indicates verbosity level.+ , preHaddock :: Args -> HaddockFlags -> IO HookedBuildInfo+ -- ^ Hook to run before haddock command. Second arg indicates verbosity level.+ , haddockHook :: PackageDescription -> LocalBuildInfo -> UserHooks -> HaddockFlags -> IO ()+ -- ^ Over-ride this hook to get different behavior during haddock.+ , postHaddock :: Args -> HaddockFlags -> PackageDescription -> LocalBuildInfo -> IO ()+ -- ^ Hook to run after haddock command. Second arg indicates verbosity level.+ , preTest :: Args -> TestFlags -> IO HookedBuildInfo+ -- ^ Hook to run before test command.+ , testHook :: Args -> PackageDescription -> LocalBuildInfo -> UserHooks -> TestFlags -> IO ()+ -- ^ Over-ride this hook to get different behavior during test.+ , postTest :: Args -> TestFlags -> PackageDescription -> LocalBuildInfo -> IO ()+ -- ^ Hook to run after test command.+ , preBench :: Args -> BenchmarkFlags -> IO HookedBuildInfo+ -- ^ Hook to run before bench command.+ , benchHook :: Args -> PackageDescription -> LocalBuildInfo -> UserHooks -> BenchmarkFlags -> IO ()+ -- ^ Over-ride this hook to get different behavior during bench.+ , postBench :: Args -> BenchmarkFlags -> PackageDescription -> LocalBuildInfo -> IO ()+ -- ^ Hook to run after bench command. } --- |Empty 'UserHooks' which do nothing.+-- | Empty 'UserHooks' which do nothing. emptyUserHooks :: UserHooks-emptyUserHooks- = UserHooks {- readDesc = return Nothing,- hookedPreProcessors = [],- hookedPrograms = [],- preConf = rn',- confHook = (\_ _ -> return (error "No local build info generated during configure. Over-ride empty configure hook.")),- postConf = ru,- preBuild = rn',- buildHook = ru,- postBuild = ru,- preRepl = \_ _ -> return emptyHookedBuildInfo,- replHook = \_ _ _ _ _ -> return (),- postRepl = ru,- preClean = rn,- cleanHook = ru,- postClean = ru,- preCopy = rn',- copyHook = ru,- postCopy = ru,- preInst = rn,- instHook = ru,- postInst = ru,- preReg = rn',- regHook = ru,- postReg = ru,- preUnreg = rn,- unregHook = ru,- postUnreg = ru,- preHscolour = rn,- hscolourHook = ru,- postHscolour = ru,- preHaddock = rn',- haddockHook = ru,- postHaddock = ru,- preTest = rn',- testHook = \_ -> ru,- postTest = ru,- preBench = rn',- benchHook = \_ -> ru,- postBench = ru+emptyUserHooks =+ UserHooks+ { readDesc = return Nothing+ , hookedPreProcessors = []+ , hookedPrograms = []+ , preConf = rn'+ , confHook = (\_ _ -> return (error "No local build info generated during configure. Over-ride empty configure hook."))+ , postConf = ru+ , preBuild = rn'+ , buildHook = ru+ , postBuild = ru+ , preRepl = \_ _ -> return emptyHookedBuildInfo+ , replHook = \_ _ _ _ _ -> return ()+ , postRepl = ru+ , preClean = rn+ , cleanHook = ru+ , postClean = ru+ , preCopy = rn'+ , copyHook = ru+ , postCopy = ru+ , preInst = rn+ , instHook = ru+ , postInst = ru+ , preReg = rn'+ , regHook = ru+ , postReg = ru+ , preUnreg = rn+ , unregHook = ru+ , postUnreg = ru+ , preHscolour = rn+ , hscolourHook = ru+ , postHscolour = ru+ , preHaddock = rn'+ , haddockHook = ru+ , postHaddock = ru+ , preTest = rn'+ , testHook = \_ -> ru+ , postTest = ru+ , preBench = rn'+ , benchHook = \_ -> ru+ , postBench = ru }- where rn args _ = noExtraFlags args >> return emptyHookedBuildInfo- rn' _ _ = return emptyHookedBuildInfo- ru _ _ _ _ = return ()+ where+ rn args _ = noExtraFlags args >> return emptyHookedBuildInfo+ rn' _ _ = return emptyHookedBuildInfo+ ru _ _ _ _ = return ()
src/Distribution/Simple/Utils.hs view
@@ -1,1609 +1,2072 @@ {-# LANGUAGE CPP #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE BangPatterns #-}---------------------------------------------------------------------------------- |--- Module : Distribution.Simple.Utils--- Copyright : Isaac Jones, Simon Marlow 2003-2004--- License : BSD3--- portions Copyright (c) 2007, Galois Inc.------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ A large and somewhat miscellaneous collection of utility functions used--- throughout the rest of the Cabal lib and in other tools that use the Cabal--- lib like @cabal-install@. It has a very simple set of logging actions. It--- has low level functions for running programs, a bunch of wrappers for--- various directory and file functions that do extra logging.--module Distribution.Simple.Utils (- cabalVersion,-- -- * logging and errors- dieNoVerbosity,- die', dieWithLocation',- dieNoWrap,- topHandler, topHandlerWith,- warn,- notice, noticeNoWrap, noticeDoc,- setupMessage,- info, infoNoWrap,- debug, debugNoWrap,- chattyTry,- annotateIO,- printRawCommandAndArgs, printRawCommandAndArgsAndEnv,- withOutputMarker,-- -- * exceptions- handleDoesNotExist,-- -- * running programs- rawSystemExit,- rawSystemExitCode,- rawSystemExitWithEnv,- rawSystemStdout,- rawSystemStdInOut,- rawSystemIOWithEnv,- rawSystemIOWithEnvAndAction,- createProcessWithEnv,- maybeExit,- xargs,- findProgramVersion,-- -- ** 'IOData' re-export- --- -- These types are re-exported from- -- "Distribution.Utils.IOData" for convience as they're- -- exposed in the API of 'rawSystemStdInOut'- IOData(..),- KnownIODataMode (..),- IODataMode (..),-- -- * copying files- createDirectoryIfMissingVerbose,- copyFileVerbose,- copyFiles,- copyFileTo,-- -- * installing files- installOrdinaryFile,- installExecutableFile,- installMaybeExecutableFile,- installOrdinaryFiles,- installExecutableFiles,- installMaybeExecutableFiles,- installDirectoryContents,- copyDirectoryRecursive,-- -- * File permissions- doesExecutableExist,- setFileOrdinary,- setFileExecutable,-- -- * file names- currentDir,- shortRelativePath,- dropExeExtension,- exeExtensions,-- -- * finding files- findFileEx,- findFileCwd,- findFirstFile,- findFileWithExtension,- findFileCwdWithExtension,- findFileWithExtension',- findAllFilesWithExtension,- findAllFilesCwdWithExtension,- findModuleFileEx,- findModuleFilesEx,- getDirectoryContentsRecursive,-- -- * environment variables- isInSearchPath,- addLibraryPath,-- -- * modification time- moreRecentFile,- existsAndIsMoreRecentThan,-- -- * temp files and dirs- TempFileOptions(..), defaultTempFileOptions,- withTempFile, withTempFileEx,- withTempDirectory, withTempDirectoryEx,- createTempDirectory,-- -- * .cabal and .buildinfo files- defaultPackageDesc,- findPackageDesc,- findPackageDescCwd,- tryFindPackageDesc,- tryFindPackageDescCwd,- findHookedPackageDesc,-- -- * reading and writing files safely- withFileContents,- writeFileAtomic,- rewriteFileEx,- rewriteFileLBS,-- -- * Unicode- fromUTF8BS,- fromUTF8LBS,- toUTF8BS,- toUTF8LBS,- readUTF8File,- withUTF8FileContents,- writeUTF8File,- normaliseLineEndings,-- -- * BOM- ignoreBOM,-- -- * generic utils- dropWhileEndLE,- takeWhileEndLE,- equating,- comparing,- isInfixOf,- intercalate,- lowercase,- listUnion,- listUnionRight,- ordNub,- ordNubBy,- ordNubRight,- safeHead,- safeTail,- safeLast,- safeInit,- unintersperse,- wrapText,- wrapLine,-- -- * FilePath stuff- isAbsoluteOnAnyPlatform,- isRelativeOnAnyPlatform,-- -- * Deprecated functions- findFile,- findModuleFile,- findModuleFiles,- ) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Utils.Generic-import Distribution.Utils.IOData (IOData(..), IODataMode (..), KnownIODataMode (..))-import qualified Distribution.Utils.IOData as IOData-import Distribution.ModuleName as ModuleName-import Distribution.System-import Distribution.Version-import Distribution.Compat.Async-import Distribution.Compat.CopyFile-import Distribution.Compat.Internal.TempFile-import Distribution.Compat.FilePath as FilePath-import Distribution.Compat.Stack-import Distribution.Verbosity-import Distribution.Types.PackageId--#if __GLASGOW_HASKELL__ < 711-#ifdef VERSION_base-#define BOOTSTRAPPED_CABAL 1-#endif-#else-#ifdef CURRENT_PACKAGE_KEY-#define BOOTSTRAPPED_CABAL 1-#endif-#endif--#ifdef BOOTSTRAPPED_CABAL-import qualified Paths_Cabal (version)-#endif--import Distribution.Pretty-import Distribution.Parsec--import Data.Typeable- ( cast )-import qualified Data.ByteString.Lazy as BS--import System.Directory- ( Permissions(executable), getDirectoryContents, getPermissions- , doesDirectoryExist, doesFileExist, removeFile- , getModificationTime, createDirectory, removeDirectoryRecursive )-import System.Environment- ( getProgName )-import System.FilePath as FilePath- ( normalise, (</>), (<.>)- , getSearchPath, joinPath, takeDirectory, splitExtension- , splitDirectories, searchPathSeparator )-import System.IO- ( Handle, hSetBinaryMode, hGetContents, stderr, stdout, hPutStr, hFlush- , hClose, hSetBuffering, BufferMode(..), hPutStrLn )-import System.IO.Error-import System.IO.Unsafe- ( unsafeInterleaveIO )-import qualified Control.Exception as Exception--import Foreign.C.Error (Errno (..), ePIPE)-import Data.Time.Clock.POSIX (getPOSIXTime, POSIXTime)-import Numeric (showFFloat)-import Distribution.Compat.Process (createProcess, rawSystem, runInteractiveProcess)-import System.Process- ( ProcessHandle- , showCommandForUser, waitForProcess)-import qualified System.Process as Process-import qualified GHC.IO.Exception as GHC--import qualified Text.PrettyPrint as Disp---- We only get our own version number when we're building with ourselves-cabalVersion :: Version-#if defined(BOOTSTRAPPED_CABAL)-cabalVersion = mkVersion' Paths_Cabal.version-#elif defined(CABAL_VERSION)-cabalVersion = mkVersion [CABAL_VERSION]-#else-cabalVersion = mkVersion [3,0] --used when bootstrapping-#endif---- ------------------------------------------------------------------------------- Exception and logging utils---- Cabal's logging infrastructure has a few constraints:------ * We must make all logging formatting and emissions decisions based--- on the 'Verbosity' parameter, which is the only parameter that is--- plumbed to enough call-sites to actually be used for this matter.--- (One of Cabal's "big mistakes" is to have never have defined a--- monad of its own.)------ * When we 'die', we must raise an IOError. This a backwards--- compatibility consideration, because that's what we've raised--- previously, and if we change to any other exception type,--- exception handlers which match on IOError will no longer work.--- One case where it is known we rely on IOError being catchable--- is 'readPkgConfigDb' in cabal-install; there may be other--- user code that also assumes this.------ * The 'topHandler' does not know what 'Verbosity' is, because--- it gets called before we've done command line parsing (where--- the 'Verbosity' parameter would come from).------ This leads to two big architectural choices:------ * Although naively we might imagine 'Verbosity' to be a simple--- enumeration type, actually it is a full-on abstract data type--- that may contain arbitrarily complex information. At the--- moment, it is fully representable as a string, but we might--- eventually also use verbosity to let users register their--- own logging handler.------ * When we call 'die', we perform all the formatting and addition--- of extra information we need, and then ship this in the IOError--- to the top-level handler. Here are alternate designs that--- don't work:------ a) Ship the unformatted info to the handler. This doesn't--- work because at the point the handler gets the message,--- we've lost call stacks, and even if we did, we don't have access--- to 'Verbosity' to decide whether or not to render it.------ b) Print the information at the 'die' site, then raise an--- error. This means that if the exception is subsequently--- caught by a handler, we will still have emitted the output,--- which is not the correct behavior.------ For the top-level handler to "know" that an error message--- contains one of these fully formatted packets, we set a sentinel--- in one of IOError's extra fields. This is handled by--- 'ioeSetVerbatim' and 'ioeGetVerbatim'.-----dieNoVerbosity :: String -> IO a-dieNoVerbosity msg- = ioError (userError msg)- where- _ = callStack -- TODO: Attach CallStack to exception---- | Tag an 'IOError' whose error string should be output to the screen--- verbatim.-ioeSetVerbatim :: IOError -> IOError-ioeSetVerbatim e = ioeSetLocation e "dieVerbatim"---- | Check if an 'IOError' should be output verbatim to screen.-ioeGetVerbatim :: IOError -> Bool-ioeGetVerbatim e = ioeGetLocation e == "dieVerbatim"---- | Create a 'userError' whose error text will be output verbatim-verbatimUserError :: String -> IOError-verbatimUserError = ioeSetVerbatim . userError--dieWithLocation' :: Verbosity -> FilePath -> Maybe Int -> String -> IO a-dieWithLocation' verbosity filename mb_lineno msg = withFrozenCallStack $ do- ts <- getPOSIXTime- pname <- getProgName- ioError . verbatimUserError- . withMetadata ts AlwaysMark VerboseTrace verbosity- . wrapTextVerbosity verbosity- $ pname ++ ": " ++- filename ++ (case mb_lineno of- Just lineno -> ":" ++ show lineno- Nothing -> "") ++- ": " ++ msg--die' :: Verbosity -> String -> IO a-die' verbosity msg = withFrozenCallStack $ do- ts <- getPOSIXTime- pname <- getProgName- ioError . verbatimUserError- . withMetadata ts AlwaysMark VerboseTrace verbosity- . wrapTextVerbosity verbosity- $ pname ++ ": " ++ msg--dieNoWrap :: Verbosity -> String -> IO a-dieNoWrap verbosity msg = withFrozenCallStack $ do- -- TODO: should this have program name or not?- ts <- getPOSIXTime- ioError . verbatimUserError- . withMetadata ts AlwaysMark VerboseTrace verbosity- $ msg---- | Given a block of IO code that may raise an exception, annotate--- it with the metadata from the current scope. Use this as close--- to external code that raises IO exceptions as possible, since--- this function unconditionally wraps the error message with a trace--- (so it is NOT idempotent.)-annotateIO :: Verbosity -> IO a -> IO a-annotateIO verbosity act = do- ts <- getPOSIXTime- modifyIOError (f ts) act- where- f ts ioe = ioeSetErrorString ioe- . withMetadata ts NeverMark VerboseTrace verbosity- $ ioeGetErrorString ioe---{-# NOINLINE topHandlerWith #-}-topHandlerWith :: forall a. (Exception.SomeException -> IO a) -> IO a -> IO a-topHandlerWith cont prog = do- -- By default, stderr to a terminal device is NoBuffering. But this- -- is *really slow*- hSetBuffering stderr LineBuffering- Exception.catches prog [- Exception.Handler rethrowAsyncExceptions- , Exception.Handler rethrowExitStatus- , Exception.Handler handle- ]- where- -- Let async exceptions rise to the top for the default top-handler- rethrowAsyncExceptions :: Exception.AsyncException -> IO a- rethrowAsyncExceptions a = throwIO a-- -- ExitCode gets thrown asynchronously too, and we don't want to print it- rethrowExitStatus :: ExitCode -> IO a- rethrowExitStatus = throwIO-- -- Print all other exceptions- handle :: Exception.SomeException -> IO a- handle se = do- hFlush stdout- pname <- getProgName- hPutStr stderr (message pname se)- cont se-- message :: String -> Exception.SomeException -> String- message pname (Exception.SomeException se) =- case cast se :: Maybe Exception.IOException of- Just ioe- | ioeGetVerbatim ioe ->- -- Use the message verbatim- ioeGetErrorString ioe ++ "\n"- | isUserError ioe ->- let file = case ioeGetFileName ioe of- Nothing -> ""- Just path -> path ++ location ++ ": "- location = case ioeGetLocation ioe of- l@(n:_) | isDigit n -> ':' : l- _ -> ""- detail = ioeGetErrorString ioe- in wrapText (pname ++ ": " ++ file ++ detail)- _ ->- displaySomeException se ++ "\n"---- | BC wrapper around 'Exception.displayException'.-displaySomeException :: Exception.Exception e => e -> String-displaySomeException se =-#if __GLASGOW_HASKELL__ < 710- show se-#else- Exception.displayException se-#endif--topHandler :: IO a -> IO a-topHandler prog = topHandlerWith (const $ exitWith (ExitFailure 1)) prog--verbosityHandle :: Verbosity -> Handle-verbosityHandle verbosity- | isVerboseStderr verbosity = stderr- | otherwise = stdout---- | Non fatal conditions that may be indicative of an error or problem.------ We display these at the 'normal' verbosity level.----warn :: Verbosity -> String -> IO ()-warn verbosity msg = withFrozenCallStack $ do- when (verbosity >= normal) $ do- ts <- getPOSIXTime- hFlush stdout- hPutStr stderr . withMetadata ts NormalMark FlagTrace verbosity- . wrapTextVerbosity verbosity- $ "Warning: " ++ msg---- | Useful status messages.------ We display these at the 'normal' verbosity level.------ This is for the ordinary helpful status messages that users see. Just--- enough information to know that things are working but not floods of detail.----notice :: Verbosity -> String -> IO ()-notice verbosity msg = withFrozenCallStack $ do- when (verbosity >= normal) $ do- let h = verbosityHandle verbosity- ts <- getPOSIXTime- hPutStr h- $ withMetadata ts NormalMark FlagTrace verbosity- $ wrapTextVerbosity verbosity- $ msg---- | Display a message at 'normal' verbosity level, but without--- wrapping.----noticeNoWrap :: Verbosity -> String -> IO ()-noticeNoWrap verbosity msg = withFrozenCallStack $ do- when (verbosity >= normal) $ do- let h = verbosityHandle verbosity- ts <- getPOSIXTime- hPutStr h . withMetadata ts NormalMark FlagTrace verbosity $ msg---- | Pretty-print a 'Disp.Doc' status message at 'normal' verbosity--- level. Use this if you need fancy formatting.----noticeDoc :: Verbosity -> Disp.Doc -> IO ()-noticeDoc verbosity msg = withFrozenCallStack $ do- when (verbosity >= normal) $ do- let h = verbosityHandle verbosity- ts <- getPOSIXTime- hPutStr h- $ withMetadata ts NormalMark FlagTrace verbosity- $ Disp.renderStyle defaultStyle- $ msg---- | Display a "setup status message". Prefer using setupMessage'--- if possible.----setupMessage :: Verbosity -> String -> PackageIdentifier -> IO ()-setupMessage verbosity msg pkgid = withFrozenCallStack $ do- noticeNoWrap verbosity (msg ++ ' ': prettyShow pkgid ++ "...")---- | More detail on the operation of some action.------ We display these messages when the verbosity level is 'verbose'----info :: Verbosity -> String -> IO ()-info verbosity msg = withFrozenCallStack $- when (verbosity >= verbose) $ do- let h = verbosityHandle verbosity- ts <- getPOSIXTime- hPutStr h- $ withMetadata ts NeverMark FlagTrace verbosity- $ wrapTextVerbosity verbosity- $ msg--infoNoWrap :: Verbosity -> String -> IO ()-infoNoWrap verbosity msg = withFrozenCallStack $- when (verbosity >= verbose) $ do- let h = verbosityHandle verbosity- ts <- getPOSIXTime- hPutStr h- $ withMetadata ts NeverMark FlagTrace verbosity- $ msg---- | Detailed internal debugging information------ We display these messages when the verbosity level is 'deafening'----debug :: Verbosity -> String -> IO ()-debug verbosity msg = withFrozenCallStack $- when (verbosity >= deafening) $ do- let h = verbosityHandle verbosity- ts <- getPOSIXTime- hPutStr h $ withMetadata ts NeverMark FlagTrace verbosity- $ wrapTextVerbosity verbosity- $ msg- -- ensure that we don't lose output if we segfault/infinite loop- hFlush stdout---- | A variant of 'debug' that doesn't perform the automatic line--- wrapping. Produces better output in some cases.-debugNoWrap :: Verbosity -> String -> IO ()-debugNoWrap verbosity msg = withFrozenCallStack $- when (verbosity >= deafening) $ do- let h = verbosityHandle verbosity- ts <- getPOSIXTime- hPutStr h- $ withMetadata ts NeverMark FlagTrace verbosity- $ msg- -- ensure that we don't lose output if we segfault/infinite loop- hFlush stdout---- | Perform an IO action, catching any IO exceptions and printing an error--- if one occurs.-chattyTry :: String -- ^ a description of the action we were attempting- -> IO () -- ^ the action itself- -> IO ()-chattyTry desc action =- catchIO action $ \exception ->- hPutStrLn stderr $ "Error while " ++ desc ++ ": " ++ show exception---- | Run an IO computation, returning @e@ if it raises a "file--- does not exist" error.-handleDoesNotExist :: a -> IO a -> IO a-handleDoesNotExist e =- Exception.handleJust- (\ioe -> if isDoesNotExistError ioe then Just ioe else Nothing)- (\_ -> return e)---- -------------------------------------------------------------------------------- Helper functions---- | Wraps text unless the @+nowrap@ verbosity flag is active-wrapTextVerbosity :: Verbosity -> String -> String-wrapTextVerbosity verb- | isVerboseNoWrap verb = withTrailingNewline- | otherwise = withTrailingNewline . wrapText----- | Prepends a timestamp if @+timestamp@ verbosity flag is set------ This is used by 'withMetadata'----withTimestamp :: Verbosity -> POSIXTime -> String -> String-withTimestamp v ts msg- | isVerboseTimestamp v = msg'- | otherwise = msg -- no-op- where- msg' = case lines msg of- [] -> tsstr "\n"- l1:rest -> unlines (tsstr (' ':l1) : map (contpfx++) rest)-- -- format timestamp to be prepended to first line with msec precision- tsstr = showFFloat (Just 3) (realToFrac ts :: Double)-- -- continuation prefix for subsequent lines of msg- contpfx = replicate (length (tsstr " ")) ' '---- | Wrap output with a marker if @+markoutput@ verbosity flag is set.------ NB: Why is markoutput done with start/end markers, and not prefixes?--- Markers are more convenient to add (if we want to add prefixes,--- we have to 'lines' and then 'map'; here's it's just some--- concatenates). Note that even in the prefix case, we can't--- guarantee that the markers are unambiguous, because some of--- Cabal's output comes straight from external programs, where--- we don't have the ability to interpose on the output.------ This is used by 'withMetadata'----withOutputMarker :: Verbosity -> String -> String-withOutputMarker v xs | not (isVerboseMarkOutput v) = xs-withOutputMarker _ "" = "" -- Minor optimization, don't mark uselessly-withOutputMarker _ xs =- "-----BEGIN CABAL OUTPUT-----\n" ++- withTrailingNewline xs ++- "-----END CABAL OUTPUT-----\n"---- | Append a trailing newline to a string if it does not--- already have a trailing newline.----withTrailingNewline :: String -> String-withTrailingNewline "" = ""-withTrailingNewline (x:xs) = x : go x xs- where- go _ (c:cs) = c : go c cs- go '\n' "" = ""- go _ "" = "\n"---- | Prepend a call-site and/or call-stack based on Verbosity----withCallStackPrefix :: WithCallStack (TraceWhen -> Verbosity -> String -> String)-withCallStackPrefix tracer verbosity s = withFrozenCallStack $- (if isVerboseCallSite verbosity- then parentSrcLocPrefix ++- -- Hack: need a newline before starting output marker :(- if isVerboseMarkOutput verbosity- then "\n"- else ""- else "") ++- (case traceWhen verbosity tracer of- Just pre -> pre ++ prettyCallStack callStack ++ "\n"- Nothing -> "") ++- s---- | When should we emit the call stack? We always emit--- for internal errors, emit the trace for errors when we--- are in verbose mode, and otherwise only emit it if--- explicitly asked for using the @+callstack@ verbosity--- flag. (At the moment, 'AlwaysTrace' is not used.----data TraceWhen- = AlwaysTrace- | VerboseTrace- | FlagTrace- deriving (Eq)---- | Determine if we should emit a call stack.--- If we trace, it also emits any prefix we should append.-traceWhen :: Verbosity -> TraceWhen -> Maybe String-traceWhen _ AlwaysTrace = Just ""-traceWhen v VerboseTrace | v >= verbose = Just ""-traceWhen v FlagTrace | isVerboseCallStack v = Just "----\n"-traceWhen _ _ = Nothing---- | When should we output the marker? Things like 'die'--- always get marked, but a 'NormalMark' will only be--- output if we're not a quiet verbosity.----data MarkWhen = AlwaysMark | NormalMark | NeverMark---- | Add all necessary metadata to a logging message----withMetadata :: WithCallStack (POSIXTime -> MarkWhen -> TraceWhen -> Verbosity -> String -> String)-withMetadata ts marker tracer verbosity x = withFrozenCallStack $- -- NB: order matters. Output marker first because we- -- don't want to capture call stacks.- withTrailingNewline- . withCallStackPrefix tracer verbosity- . (case marker of- AlwaysMark -> withOutputMarker verbosity- NormalMark | not (isVerboseQuiet verbosity)- -> withOutputMarker verbosity- | otherwise- -> id- NeverMark -> id)- -- Clear out any existing markers- . clearMarkers- . withTimestamp verbosity ts- $ x--clearMarkers :: String -> String-clearMarkers s = unlines . filter isMarker $ lines s- where- isMarker "-----BEGIN CABAL OUTPUT-----" = False- isMarker "-----END CABAL OUTPUT-----" = False- isMarker _ = True---- -------------------------------------------------------------------------------- rawSystem variants-maybeExit :: IO ExitCode -> IO ()-maybeExit cmd = do- res <- cmd- unless (res == ExitSuccess) $ exitWith res----printRawCommandAndArgs :: Verbosity -> FilePath -> [String] -> IO ()-printRawCommandAndArgs verbosity path args = withFrozenCallStack $- printRawCommandAndArgsAndEnv verbosity path args Nothing Nothing--printRawCommandAndArgsAndEnv :: Verbosity- -> FilePath- -> [String]- -> Maybe FilePath- -> Maybe [(String, String)]- -> IO ()-printRawCommandAndArgsAndEnv verbosity path args mcwd menv = do- case menv of- Just env -> debugNoWrap verbosity ("Environment: " ++ show env)- Nothing -> return ()- case mcwd of- Just cwd -> debugNoWrap verbosity ("Working directory: " ++ show cwd)- Nothing -> return ()- infoNoWrap verbosity (showCommandForUser path args)---- Exit with the same exit code if the subcommand fails-rawSystemExit :: Verbosity -> FilePath -> [String] -> IO ()-rawSystemExit verbosity path args = withFrozenCallStack $ do- printRawCommandAndArgs verbosity path args- hFlush stdout- exitcode <- rawSystem path args- unless (exitcode == ExitSuccess) $ do- debug verbosity $ path ++ " returned " ++ show exitcode- exitWith exitcode--rawSystemExitCode :: Verbosity -> FilePath -> [String] -> IO ExitCode-rawSystemExitCode verbosity path args = withFrozenCallStack $ do- printRawCommandAndArgs verbosity path args- hFlush stdout- exitcode <- rawSystem path args- unless (exitcode == ExitSuccess) $ do- debug verbosity $ path ++ " returned " ++ show exitcode- return exitcode--rawSystemExitWithEnv :: Verbosity- -> FilePath- -> [String]- -> [(String, String)]- -> IO ()-rawSystemExitWithEnv verbosity path args env = withFrozenCallStack $ do- printRawCommandAndArgsAndEnv verbosity path args Nothing (Just env)- hFlush stdout- (_,_,_,ph) <- createProcess $- (Process.proc path args) { Process.env = (Just env)-#ifdef MIN_VERSION_process-#if MIN_VERSION_process(1,2,0)--- delegate_ctlc has been added in process 1.2, and we still want to be able to--- bootstrap GHC on systems not having that version- , Process.delegate_ctlc = True-#endif-#endif- }- exitcode <- waitForProcess ph- unless (exitcode == ExitSuccess) $ do- debug verbosity $ path ++ " returned " ++ show exitcode- exitWith exitcode---- Closes the passed in handles before returning.-rawSystemIOWithEnv :: Verbosity- -> FilePath- -> [String]- -> Maybe FilePath -- ^ New working dir or inherit- -> Maybe [(String, String)] -- ^ New environment or inherit- -> Maybe Handle -- ^ stdin- -> Maybe Handle -- ^ stdout- -> Maybe Handle -- ^ stderr- -> IO ExitCode-rawSystemIOWithEnv verbosity path args mcwd menv inp out err = withFrozenCallStack $ do- (_,_,_,ph) <- createProcessWithEnv verbosity path args mcwd menv- (mbToStd inp) (mbToStd out) (mbToStd err)- exitcode <- waitForProcess ph- unless (exitcode == ExitSuccess) $ do- debug verbosity $ path ++ " returned " ++ show exitcode- return exitcode- where- mbToStd :: Maybe Handle -> Process.StdStream- mbToStd = maybe Process.Inherit Process.UseHandle--rawSystemIOWithEnvAndAction- :: Verbosity- -> FilePath- -> [String]- -> Maybe FilePath -- ^ New working dir or inherit- -> Maybe [(String, String)] -- ^ New environment or inherit- -> IO a -- ^ action to perform after process is created, but before 'waitForProcess'.- -> Maybe Handle -- ^ stdin- -> Maybe Handle -- ^ stdout- -> Maybe Handle -- ^ stderr- -> IO (ExitCode, a)-rawSystemIOWithEnvAndAction verbosity path args mcwd menv action inp out err = withFrozenCallStack $ do- (_,_,_,ph) <- createProcessWithEnv verbosity path args mcwd menv- (mbToStd inp) (mbToStd out) (mbToStd err)- a <- action- exitcode <- waitForProcess ph- unless (exitcode == ExitSuccess) $ do- debug verbosity $ path ++ " returned " ++ show exitcode- return (exitcode, a)- where- mbToStd :: Maybe Handle -> Process.StdStream- mbToStd = maybe Process.Inherit Process.UseHandle--createProcessWithEnv ::- Verbosity- -> FilePath- -> [String]- -> Maybe FilePath -- ^ New working dir or inherit- -> Maybe [(String, String)] -- ^ New environment or inherit- -> Process.StdStream -- ^ stdin- -> Process.StdStream -- ^ stdout- -> Process.StdStream -- ^ stderr- -> IO (Maybe Handle, Maybe Handle, Maybe Handle,ProcessHandle)- -- ^ Any handles created for stdin, stdout, or stderr- -- with 'CreateProcess', and a handle to the process.-createProcessWithEnv verbosity path args mcwd menv inp out err = withFrozenCallStack $ do- printRawCommandAndArgsAndEnv verbosity path args mcwd menv- hFlush stdout- (inp', out', err', ph) <- createProcess $- (Process.proc path args) {- Process.cwd = mcwd- , Process.env = menv- , Process.std_in = inp- , Process.std_out = out- , Process.std_err = err-#ifdef MIN_VERSION_process-#if MIN_VERSION_process(1,2,0)--- delegate_ctlc has been added in process 1.2, and we still want to be able to--- bootstrap GHC on systems not having that version- , Process.delegate_ctlc = True-#endif-#endif- }- return (inp', out', err', ph)---- | Run a command and return its output.------ The output is assumed to be text in the locale encoding.----rawSystemStdout :: forall mode. KnownIODataMode mode => Verbosity -> FilePath -> [String] -> IO mode-rawSystemStdout verbosity path args = withFrozenCallStack $ do- (output, errors, exitCode) <- rawSystemStdInOut verbosity path args- Nothing Nothing Nothing (IOData.iodataMode :: IODataMode mode)- when (exitCode /= ExitSuccess) $- die' verbosity errors- return output---- | Run a command and return its output, errors and exit status. Optionally--- also supply some input. Also provides control over whether the binary/text--- mode of the input and output.----rawSystemStdInOut :: KnownIODataMode mode- => Verbosity- -> FilePath -- ^ Program location- -> [String] -- ^ Arguments- -> Maybe FilePath -- ^ New working dir or inherit- -> Maybe [(String, String)] -- ^ New environment or inherit- -> Maybe IOData -- ^ input text and binary mode- -> IODataMode mode -- ^ iodata mode, acts as proxy- -> IO (mode, String, ExitCode) -- ^ output, errors, exit-rawSystemStdInOut verbosity path args mcwd menv input _ = withFrozenCallStack $ do- printRawCommandAndArgs verbosity path args-- Exception.bracket- (runInteractiveProcess path args mcwd menv)- (\(inh,outh,errh,_) -> hClose inh >> hClose outh >> hClose errh)- $ \(inh,outh,errh,pid) -> do-- -- output mode depends on what the caller wants- -- but the errors are always assumed to be text (in the current locale)- hSetBinaryMode errh False-- -- fork off a couple threads to pull on the stderr and stdout- -- so if the process writes to stderr we do not block.-- withAsyncNF (hGetContents errh) $ \errA -> withAsyncNF (IOData.hGetIODataContents outh) $ \outA -> do- -- push all the input, if any- ignoreSigPipe $ case input of- Nothing -> hClose inh- Just inputData -> IOData.hPutContents inh inputData-- -- wait for both to finish- mberr1 <- waitCatch outA- mberr2 <- waitCatch errA-- -- wait for the program to terminate- exitcode <- waitForProcess pid-- -- get the stderr, so it can be added to error message- err <- reportOutputIOError mberr2-- unless (exitcode == ExitSuccess) $- debug verbosity $ path ++ " returned " ++ show exitcode- ++ if null err then "" else- " with error message:\n" ++ err- ++ case input of- Nothing -> ""- Just d | IOData.null d -> ""- Just (IODataText inp) -> "\nstdin input:\n" ++ inp- Just (IODataBinary inp) -> "\nstdin input (binary):\n" ++ show inp-- -- Check if we hit an exception while consuming the output- -- (e.g. a text decoding error)- out <- reportOutputIOError mberr1-- return (out, err, exitcode)- where- reportOutputIOError :: Either Exception.SomeException a -> IO a- reportOutputIOError (Right x) = return x- reportOutputIOError (Left exc) = case fromException exc of- Just ioe -> throwIO (ioeSetFileName ioe ("output of " ++ path))- Nothing -> throwIO exc-- ignoreSigPipe :: IO () -> IO ()- ignoreSigPipe = Exception.handle $ \e -> case e of- GHC.IOError { GHC.ioe_type = GHC.ResourceVanished, GHC.ioe_errno = Just ioe }- | Errno ioe == ePIPE -> return ()- _ -> throwIO e---- | Look for a program and try to find it's version number. It can accept--- either an absolute path or the name of a program binary, in which case we--- will look for the program on the path.----findProgramVersion :: String -- ^ version args- -> (String -> String) -- ^ function to select version- -- number from program output- -> Verbosity- -> FilePath -- ^ location- -> IO (Maybe Version)-findProgramVersion versionArg selectVersion verbosity path = withFrozenCallStack $ do- str <- rawSystemStdout verbosity path [versionArg]- `catchIO` (\_ -> return "")- `catchExit` (\_ -> return "")- let version :: Maybe Version- version = simpleParsec (selectVersion str)- case version of- Nothing -> warn verbosity $ "cannot determine version of " ++ path- ++ " :\n" ++ show str- Just v -> debug verbosity $ path ++ " is version " ++ prettyShow v- return version----- | Like the Unix xargs program. Useful for when we've got very long command--- lines that might overflow an OS limit on command line length and so you--- need to invoke a command multiple times to get all the args in.------ Use it with either of the rawSystem variants above. For example:------ > xargs (32*1024) (rawSystemExit verbosity) prog fixedArgs bigArgs----xargs :: Int -> ([String] -> IO ())- -> [String] -> [String] -> IO ()-xargs maxSize rawSystemFun fixedArgs bigArgs =- let fixedArgSize = sum (map length fixedArgs) + length fixedArgs- chunkSize = maxSize - fixedArgSize- in traverse_ (rawSystemFun . (fixedArgs ++)) (chunks chunkSize bigArgs)-- where chunks len = unfoldr $ \s ->- if null s then Nothing- else Just (chunk [] len s)-- chunk acc _ [] = (reverse acc,[])- chunk acc len (s:ss)- | len' < len = chunk (s:acc) (len-len'-1) ss- | otherwise = (reverse acc, s:ss)- where len' = length s---- --------------------------------------------------------------- * File Utilities--- --------------------------------------------------------------------------------- Finding files---{-# DEPRECATED findFile "Use findFileEx instead. This symbol will be removed in Cabal 3.2 (est. December 2019)" #-}-findFile :: [FilePath] -- ^search locations- -> FilePath -- ^File Name- -> IO FilePath-findFile = findFileEx normal---- | Find a file by looking in a search path. The file path must match exactly.------ @since 3.4.0.0-findFileCwd- :: Verbosity- -> FilePath -- ^ cwd- -> [FilePath] -- ^ relative search location- -> FilePath -- ^ File Name- -> IO FilePath-findFileCwd verbosity cwd searchPath fileName =- findFirstFile (cwd </>)- [ path </> fileName- | path <- nub searchPath]- >>= maybe (die' verbosity $ fileName ++ " doesn't exist") return---- | Find a file by looking in a search path. The file path must match exactly.----findFileEx :: Verbosity- -> [FilePath] -- ^search locations- -> FilePath -- ^File Name- -> IO FilePath-findFileEx verbosity searchPath fileName =- findFirstFile id- [ path </> fileName- | path <- nub searchPath]- >>= maybe (die' verbosity $ fileName ++ " doesn't exist") return---- | Find a file by looking in a search path with one of a list of possible--- file extensions. The file base name should be given and it will be tried--- with each of the extensions in each element of the search path.----findFileWithExtension :: [String]- -> [FilePath]- -> FilePath- -> IO (Maybe FilePath)-findFileWithExtension extensions searchPath baseName =- findFirstFile id- [ path </> baseName <.> ext- | path <- nub searchPath- , ext <- nub extensions ]---- | @since 3.4.0.0-findFileCwdWithExtension- :: FilePath- -> [String]- -> [FilePath]- -> FilePath- -> IO (Maybe FilePath)-findFileCwdWithExtension cwd extensions searchPath baseName =- findFirstFile (cwd </>)- [ path </> baseName <.> ext- | path <- nub searchPath- , ext <- nub extensions ]---- | @since 3.4.0.0-findAllFilesCwdWithExtension- :: FilePath -- ^ cwd- -> [String] -- ^ extensions- -> [FilePath] -- ^ relative search locations- -> FilePath -- ^ basename- -> IO [FilePath]-findAllFilesCwdWithExtension cwd extensions searchPath basename =- findAllFiles (cwd </>)- [ path </> basename <.> ext- | path <- nub searchPath- , ext <- nub extensions ]--findAllFilesWithExtension :: [String]- -> [FilePath]- -> FilePath- -> IO [FilePath]-findAllFilesWithExtension extensions searchPath basename =- findAllFiles id- [ path </> basename <.> ext- | path <- nub searchPath- , ext <- nub extensions ]---- | Like 'findFileWithExtension' but returns which element of the search path--- the file was found in, and the file path relative to that base directory.----findFileWithExtension' :: [String]- -> [FilePath]- -> FilePath- -> IO (Maybe (FilePath, FilePath))-findFileWithExtension' extensions searchPath baseName =- findFirstFile (uncurry (</>))- [ (path, baseName <.> ext)- | path <- nub searchPath- , ext <- nub extensions ]--findFirstFile :: (a -> FilePath) -> [a] -> IO (Maybe a)-findFirstFile file = findFirst- where findFirst [] = return Nothing- findFirst (x:xs) = do exists <- doesFileExist (file x)- if exists- then return (Just x)- else findFirst xs--findAllFiles :: (a -> FilePath) -> [a] -> IO [a]-findAllFiles file = filterM (doesFileExist . file)---{-# DEPRECATED findModuleFiles "Use findModuleFilesEx instead. This symbol will be removed in Cabal 3.2 (est. December 2019)" #-}-findModuleFiles :: [FilePath] -- ^ build prefix (location of objects)- -> [String] -- ^ search suffixes- -> [ModuleName] -- ^ modules- -> IO [(FilePath, FilePath)]-findModuleFiles = findModuleFilesEx normal---- | Finds the files corresponding to a list of Haskell module names.------ As 'findModuleFile' but for a list of module names.----findModuleFilesEx :: Verbosity- -> [FilePath] -- ^ build prefix (location of objects)- -> [String] -- ^ search suffixes- -> [ModuleName] -- ^ modules- -> IO [(FilePath, FilePath)]-findModuleFilesEx verbosity searchPath extensions moduleNames =- traverse (findModuleFileEx verbosity searchPath extensions) moduleNames--{-# DEPRECATED findModuleFile "Use findModuleFileEx instead. This symbol will be removed in Cabal 3.2 (est. December 2019)" #-}-findModuleFile :: [FilePath] -- ^ build prefix (location of objects)- -> [String] -- ^ search suffixes- -> ModuleName -- ^ module- -> IO (FilePath, FilePath)-findModuleFile = findModuleFileEx normal---- | Find the file corresponding to a Haskell module name.------ This is similar to 'findFileWithExtension'' but specialised to a module--- name. The function fails if the file corresponding to the module is missing.----findModuleFileEx :: Verbosity- -> [FilePath] -- ^ build prefix (location of objects)- -> [String] -- ^ search suffixes- -> ModuleName -- ^ module- -> IO (FilePath, FilePath)-findModuleFileEx verbosity searchPath extensions mod_name =- maybe notFound return- =<< findFileWithExtension' extensions searchPath- (ModuleName.toFilePath mod_name)- where- notFound = die' verbosity $- "Error: Could not find module: " ++ prettyShow mod_name- ++ " with any suffix: " ++ show extensions- ++ " in the search path: " ++ show searchPath---- | List all the files in a directory and all subdirectories.------ The order places files in sub-directories after all the files in their--- parent directories. The list is generated lazily so is not well defined if--- the source directory structure changes before the list is used.----getDirectoryContentsRecursive :: FilePath -> IO [FilePath]-getDirectoryContentsRecursive topdir = recurseDirectories [""]- where- recurseDirectories :: [FilePath] -> IO [FilePath]- recurseDirectories [] = return []- recurseDirectories (dir:dirs) = unsafeInterleaveIO $ do- (files, dirs') <- collect [] [] =<< getDirectoryContents (topdir </> dir)- files' <- recurseDirectories (dirs' ++ dirs)- return (files ++ files')-- where- collect files dirs' [] = return (reverse files- ,reverse dirs')- collect files dirs' (entry:entries) | ignore entry- = collect files dirs' entries- collect files dirs' (entry:entries) = do- let dirEntry = dir </> entry- isDirectory <- doesDirectoryExist (topdir </> dirEntry)- if isDirectory- then collect files (dirEntry:dirs') entries- else collect (dirEntry:files) dirs' entries-- ignore ['.'] = True- ignore ['.', '.'] = True- ignore _ = False----------------------------- Environment variables---- | Is this directory in the system search path?-isInSearchPath :: FilePath -> IO Bool-isInSearchPath path = fmap (elem path) getSearchPath--addLibraryPath :: OS- -> [FilePath]- -> [(String,String)]- -> [(String,String)]-addLibraryPath os paths = addEnv- where- pathsString = intercalate [searchPathSeparator] paths- ldPath = case os of- OSX -> "DYLD_LIBRARY_PATH"- _ -> "LD_LIBRARY_PATH"-- addEnv [] = [(ldPath,pathsString)]- addEnv ((key,value):xs)- | key == ldPath =- if null value- then (key,pathsString):xs- else (key,value ++ (searchPathSeparator:pathsString)):xs- | otherwise = (key,value):addEnv xs------------------------- Modification time---- | Compare the modification times of two files to see if the first is newer--- than the second. The first file must exist but the second need not.--- The expected use case is when the second file is generated using the first.--- In this use case, if the result is True then the second file is out of date.----moreRecentFile :: FilePath -> FilePath -> IO Bool-moreRecentFile a b = do- exists <- doesFileExist b- if not exists- then return True- else do tb <- getModificationTime b- ta <- getModificationTime a- return (ta > tb)---- | Like 'moreRecentFile', but also checks that the first file exists.-existsAndIsMoreRecentThan :: FilePath -> FilePath -> IO Bool-existsAndIsMoreRecentThan a b = do- exists <- doesFileExist a- if not exists- then return False- else a `moreRecentFile` b--------------------------------------------- Copying and installing files and dirs---- | Same as 'createDirectoryIfMissing' but logs at higher verbosity levels.----createDirectoryIfMissingVerbose :: Verbosity- -> Bool -- ^ Create its parents too?- -> FilePath- -> IO ()-createDirectoryIfMissingVerbose verbosity create_parents path0- | create_parents = withFrozenCallStack $ createDirs (parents path0)- | otherwise = withFrozenCallStack $ createDirs (take 1 (parents path0))- where- parents = reverse . scanl1 (</>) . splitDirectories . normalise-- createDirs [] = return ()- createDirs (dir:[]) = createDir dir throwIO- createDirs (dir:dirs) =- createDir dir $ \_ -> do- createDirs dirs- createDir dir throwIO-- createDir :: FilePath -> (IOException -> IO ()) -> IO ()- createDir dir notExistHandler = do- r <- tryIO $ createDirectoryVerbose verbosity dir- case (r :: Either IOException ()) of- Right () -> return ()- Left e- | isDoesNotExistError e -> notExistHandler e- -- createDirectory (and indeed POSIX mkdir) does not distinguish- -- between a dir already existing and a file already existing. So we- -- check for it here. Unfortunately there is a slight race condition- -- here, but we think it is benign. It could report an exception in- -- the case that the dir did exist but another process deletes the- -- directory and creates a file in its place before we can check- -- that the directory did indeed exist.- | isAlreadyExistsError e -> (do- isDir <- doesDirectoryExist dir- unless isDir $ throwIO e- ) `catchIO` ((\_ -> return ()) :: IOException -> IO ())- | otherwise -> throwIO e--createDirectoryVerbose :: Verbosity -> FilePath -> IO ()-createDirectoryVerbose verbosity dir = withFrozenCallStack $ do- info verbosity $ "creating " ++ dir- createDirectory dir- setDirOrdinary dir---- | Copies a file without copying file permissions. The target file is created--- with default permissions. Any existing target file is replaced.------ At higher verbosity levels it logs an info message.----copyFileVerbose :: Verbosity -> FilePath -> FilePath -> IO ()-copyFileVerbose verbosity src dest = withFrozenCallStack $ do- info verbosity ("copy " ++ src ++ " to " ++ dest)- copyFile src dest---- | Install an ordinary file. This is like a file copy but the permissions--- are set appropriately for an installed file. On Unix it is \"-rw-r--r--\"--- while on Windows it uses the default permissions for the target directory.----installOrdinaryFile :: Verbosity -> FilePath -> FilePath -> IO ()-installOrdinaryFile verbosity src dest = withFrozenCallStack $ do- info verbosity ("Installing " ++ src ++ " to " ++ dest)- copyOrdinaryFile src dest---- | Install an executable file. This is like a file copy but the permissions--- are set appropriately for an installed file. On Unix it is \"-rwxr-xr-x\"--- while on Windows it uses the default permissions for the target directory.----installExecutableFile :: Verbosity -> FilePath -> FilePath -> IO ()-installExecutableFile verbosity src dest = withFrozenCallStack $ do- info verbosity ("Installing executable " ++ src ++ " to " ++ dest)- copyExecutableFile src dest---- | Install a file that may or not be executable, preserving permissions.-installMaybeExecutableFile :: Verbosity -> FilePath -> FilePath -> IO ()-installMaybeExecutableFile verbosity src dest = withFrozenCallStack $ do- perms <- getPermissions src- if (executable perms) --only checks user x bit- then installExecutableFile verbosity src dest- else installOrdinaryFile verbosity src dest---- | Given a relative path to a file, copy it to the given directory, preserving--- the relative path and creating the parent directories if needed.-copyFileTo :: Verbosity -> FilePath -> FilePath -> IO ()-copyFileTo verbosity dir file = withFrozenCallStack $ do- let targetFile = dir </> file- createDirectoryIfMissingVerbose verbosity True (takeDirectory targetFile)- installOrdinaryFile verbosity file targetFile---- | Common implementation of 'copyFiles', 'installOrdinaryFiles',--- 'installExecutableFiles' and 'installMaybeExecutableFiles'.-copyFilesWith :: (Verbosity -> FilePath -> FilePath -> IO ())- -> Verbosity -> FilePath -> [(FilePath, FilePath)] -> IO ()-copyFilesWith doCopy verbosity targetDir srcFiles = withFrozenCallStack $ do-- -- Create parent directories for everything- let dirs = map (targetDir </>) . nub . map (takeDirectory . snd) $ srcFiles- traverse_ (createDirectoryIfMissingVerbose verbosity True) dirs-- -- Copy all the files- sequence_ [ let src = srcBase </> srcFile- dest = targetDir </> srcFile- in doCopy verbosity src dest- | (srcBase, srcFile) <- srcFiles ]---- | Copies a bunch of files to a target directory, preserving the directory--- structure in the target location. The target directories are created if they--- do not exist.------ The files are identified by a pair of base directory and a path relative to--- that base. It is only the relative part that is preserved in the--- destination.------ For example:------ > copyFiles normal "dist/src"--- > [("", "src/Foo.hs"), ("dist/build/", "src/Bar.hs")]------ This would copy \"src\/Foo.hs\" to \"dist\/src\/src\/Foo.hs\" and--- copy \"dist\/build\/src\/Bar.hs\" to \"dist\/src\/src\/Bar.hs\".------ This operation is not atomic. Any IO failure during the copy (including any--- missing source files) leaves the target in an unknown state so it is best to--- use it with a freshly created directory so that it can be simply deleted if--- anything goes wrong.----copyFiles :: Verbosity -> FilePath -> [(FilePath, FilePath)] -> IO ()-copyFiles v fp fs = withFrozenCallStack (copyFilesWith copyFileVerbose v fp fs)---- | This is like 'copyFiles' but uses 'installOrdinaryFile'.----installOrdinaryFiles :: Verbosity -> FilePath -> [(FilePath, FilePath)] -> IO ()-installOrdinaryFiles v fp fs = withFrozenCallStack (copyFilesWith installOrdinaryFile v fp fs)---- | This is like 'copyFiles' but uses 'installExecutableFile'.----installExecutableFiles :: Verbosity -> FilePath -> [(FilePath, FilePath)]- -> IO ()-installExecutableFiles v fp fs = withFrozenCallStack (copyFilesWith installExecutableFile v fp fs)---- | This is like 'copyFiles' but uses 'installMaybeExecutableFile'.----installMaybeExecutableFiles :: Verbosity -> FilePath -> [(FilePath, FilePath)]- -> IO ()-installMaybeExecutableFiles v fp fs = withFrozenCallStack (copyFilesWith installMaybeExecutableFile v fp fs)---- | This installs all the files in a directory to a target location,--- preserving the directory layout. All the files are assumed to be ordinary--- rather than executable files.----installDirectoryContents :: Verbosity -> FilePath -> FilePath -> IO ()-installDirectoryContents verbosity srcDir destDir = withFrozenCallStack $ do- info verbosity ("copy directory '" ++ srcDir ++ "' to '" ++ destDir ++ "'.")- srcFiles <- getDirectoryContentsRecursive srcDir- installOrdinaryFiles verbosity destDir [ (srcDir, f) | f <- srcFiles ]---- | Recursively copy the contents of one directory to another path.-copyDirectoryRecursive :: Verbosity -> FilePath -> FilePath -> IO ()-copyDirectoryRecursive verbosity srcDir destDir = withFrozenCallStack $ do- info verbosity ("copy directory '" ++ srcDir ++ "' to '" ++ destDir ++ "'.")- srcFiles <- getDirectoryContentsRecursive srcDir- copyFilesWith (const copyFile) verbosity destDir [ (srcDir, f)- | f <- srcFiles ]------------------------ File permissions---- | Like 'doesFileExist', but also checks that the file is executable.-doesExecutableExist :: FilePath -> IO Bool-doesExecutableExist f = do- exists <- doesFileExist f- if exists- then do perms <- getPermissions f- return (executable perms)- else return False-------------------------------- Temporary files and dirs---- | Advanced options for 'withTempFile' and 'withTempDirectory'.-data TempFileOptions = TempFileOptions {- optKeepTempFiles :: Bool -- ^ Keep temporary files?- }--defaultTempFileOptions :: TempFileOptions-defaultTempFileOptions = TempFileOptions { optKeepTempFiles = False }---- | Use a temporary filename that doesn't already exist.----withTempFile :: FilePath -- ^ Temp dir to create the file in- -> String -- ^ File name template. See 'openTempFile'.- -> (FilePath -> Handle -> IO a) -> IO a-withTempFile tmpDir template action =- withTempFileEx defaultTempFileOptions tmpDir template action---- | A version of 'withTempFile' that additionally takes a 'TempFileOptions'--- argument.-withTempFileEx :: TempFileOptions- -> FilePath -- ^ Temp dir to create the file in- -> String -- ^ File name template. See 'openTempFile'.- -> (FilePath -> Handle -> IO a) -> IO a-withTempFileEx opts tmpDir template action =- Exception.bracket- (openTempFile tmpDir template)- (\(name, handle) -> do hClose handle- unless (optKeepTempFiles opts) $- handleDoesNotExist () . removeFile $ name)- (withLexicalCallStack (\x -> uncurry action x))---- | Create and use a temporary directory.------ Creates a new temporary directory inside the given directory, making use--- of the template. The temp directory is deleted after use. For example:------ > withTempDirectory verbosity "src" "sdist." $ \tmpDir -> do ...------ The @tmpDir@ will be a new subdirectory of the given directory, e.g.--- @src/sdist.342@.----withTempDirectory :: Verbosity -> FilePath -> String -> (FilePath -> IO a) -> IO a-withTempDirectory verbosity targetDir template f = withFrozenCallStack $- withTempDirectoryEx verbosity defaultTempFileOptions targetDir template- (withLexicalCallStack (\x -> f x))---- | A version of 'withTempDirectory' that additionally takes a--- 'TempFileOptions' argument.-withTempDirectoryEx :: Verbosity -> TempFileOptions- -> FilePath -> String -> (FilePath -> IO a) -> IO a-withTempDirectoryEx _verbosity opts targetDir template f = withFrozenCallStack $- Exception.bracket- (createTempDirectory targetDir template)- (unless (optKeepTempFiles opts)- . handleDoesNotExist () . removeDirectoryRecursive)- (withLexicalCallStack (\x -> f x))---------------------------------------- Safely reading and writing files---- | Write a file but only if it would have new content. If we would be writing--- the same as the existing content then leave the file as is so that we do not--- update the file's modification time.------ NB: Before Cabal-3.0 the file content was assumed to be--- ASCII-representable. Since Cabal-3.0 the file is assumed to be--- UTF-8 encoded.-rewriteFileEx :: Verbosity -> FilePath -> String -> IO ()-rewriteFileEx verbosity path =- rewriteFileLBS verbosity path . toUTF8LBS---- | Same as `rewriteFileEx` but for 'ByteString's.-rewriteFileLBS :: Verbosity -> FilePath -> BS.ByteString -> IO ()-rewriteFileLBS verbosity path newContent =- flip catchIO mightNotExist $ do- existingContent <- annotateIO verbosity $ BS.readFile path- _ <- evaluate (BS.length existingContent)- unless (existingContent == newContent) $- annotateIO verbosity $- writeFileAtomic path newContent- where- mightNotExist e | isDoesNotExistError e- = annotateIO verbosity $ writeFileAtomic path newContent- | otherwise- = ioError e----- | The path name that represents the current directory.--- In Unix, it's @\".\"@, but this is system-specific.--- (E.g. AmigaOS uses the empty string @\"\"@ for the current directory.)-currentDir :: FilePath-currentDir = "."--shortRelativePath :: FilePath -> FilePath -> FilePath-shortRelativePath from to =- case dropCommonPrefix (splitDirectories from) (splitDirectories to) of- (stuff, path) -> joinPath (map (const "..") stuff ++ path)- where- dropCommonPrefix :: Eq a => [a] -> [a] -> ([a],[a])- dropCommonPrefix (x:xs) (y:ys)- | x == y = dropCommonPrefix xs ys- dropCommonPrefix xs ys = (xs,ys)---- | Drop the extension if it's one of 'exeExtensions', or return the path--- unchanged.-dropExeExtension :: FilePath -> FilePath-dropExeExtension filepath =- -- System.FilePath's extension handling functions are horribly- -- inconsistent, consider:- --- -- isExtensionOf "" "foo" == False but- -- isExtensionOf "" "foo." == True.- --- -- On the other hand stripExtension doesn't remove the empty extension:- --- -- stripExtension "" "foo." == Just "foo."- --- -- Since by "" in exeExtensions we mean 'no extension' anyways we can- -- just always ignore it here.- let exts = [ ext | ext <- exeExtensions, ext /= "" ] in- fromMaybe filepath $ do- ext <- find (`FilePath.isExtensionOf` filepath) exts- ext `FilePath.stripExtension` filepath---- | List of possible executable file extensions on the current build--- platform.-exeExtensions :: [String]-exeExtensions = case buildOS of- -- Possible improvement: on Windows, read the list of extensions from the- -- PATHEXT environment variable. By default PATHEXT is ".com; .exe; .bat;- -- .cmd".- Windows -> ["", "exe"]- Ghcjs -> ["", "exe"]- _ -> [""]---- --------------------------------------------------------------- * Finding the description file--- ---------------------------------------------------------------- | Package description file (/pkgname/@.cabal@)-defaultPackageDesc :: Verbosity -> IO FilePath-defaultPackageDesc verbosity = tryFindPackageDesc verbosity currentDir---- |Find a package description file in the given directory. Looks for--- @.cabal@ files.-findPackageDesc :: FilePath -- ^Where to look- -> IO (Either String FilePath) -- ^<pkgname>.cabal-findPackageDesc = findPackageDescCwd "."---- | @since 3.4.0.0-findPackageDescCwd- :: FilePath -- ^ project root- -> FilePath -- ^ relative directory- -> IO (Either String FilePath) -- ^ <pkgname>.cabal relative to the project root-findPackageDescCwd cwd dir- = do files <- getDirectoryContents (cwd </> dir)- -- to make sure we do not mistake a ~/.cabal/ dir for a <pkgname>.cabal- -- file we filter to exclude dirs and null base file names:- cabalFiles <- filterM (doesFileExist . snd)- [ (dir </> file, cwd </> dir </> file)- | file <- files- , let (name, ext) = splitExtension file- , not (null name) && ext == ".cabal" ]- case map fst cabalFiles of- [] -> return (Left noDesc)- [cabalFile] -> return (Right cabalFile)- multiple -> return (Left $ multiDesc multiple)-- where- noDesc :: String- noDesc = "No cabal file found.\n"- ++ "Please create a package description file <pkgname>.cabal"-- multiDesc :: [String] -> String- multiDesc l = "Multiple cabal files found.\n"- ++ "Please use only one of: "- ++ intercalate ", " l---- |Like 'findPackageDesc', but calls 'die' in case of error.-tryFindPackageDesc :: Verbosity -> FilePath -> IO FilePath-tryFindPackageDesc verbosity dir =- either (die' verbosity) return =<< findPackageDesc dir---- | Like 'findPackageDescCwd', but calls 'die' in case of error.------ @since 3.4.0.0-tryFindPackageDescCwd :: Verbosity -> FilePath -> FilePath -> IO FilePath-tryFindPackageDescCwd verbosity cwd dir =- either (die' verbosity) return =<< findPackageDescCwd cwd dir---- |Find auxiliary package information in the given directory.--- Looks for @.buildinfo@ files.-findHookedPackageDesc- :: Verbosity- -> FilePath -- ^Directory to search- -> IO (Maybe FilePath) -- ^/dir/@\/@/pkgname/@.buildinfo@, if present-findHookedPackageDesc verbosity dir = do- files <- getDirectoryContents dir- buildInfoFiles <- filterM doesFileExist- [ dir </> file- | file <- files- , let (name, ext) = splitExtension file- , not (null name) && ext == buildInfoExt ]- case buildInfoFiles of- [] -> return Nothing- [f] -> return (Just f)- _ -> die' verbosity ("Multiple files with extension " ++ buildInfoExt)--buildInfoExt :: String-buildInfoExt = ".buildinfo"+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}+#ifdef GIT_REV+{-# LANGUAGE TemplateHaskell #-}+#endif++-----------------------------------------------------------------------------++-- |+-- Module : Distribution.Simple.Utils+-- Copyright : Isaac Jones, Simon Marlow 2003-2004+-- License : BSD3+-- portions Copyright (c) 2007, Galois Inc.+--+-- Maintainer : cabal-devel@haskell.org+-- Portability : portable+--+-- A large and somewhat miscellaneous collection of utility functions used+-- throughout the rest of the Cabal lib and in other tools that use the Cabal+-- lib like @cabal-install@. It has a very simple set of logging actions. It+-- has low level functions for running programs, a bunch of wrappers for+-- various directory and file functions that do extra logging.+module Distribution.Simple.Utils+ ( cabalVersion+ , cabalGitInfo++ -- * logging and errors+ , dieNoVerbosity+ , die'+ , dieWithException+ , dieWithLocation'+ , dieNoWrap+ , topHandler+ , topHandlerWith+ , warn+ , warnError+ , notice+ , noticeNoWrap+ , noticeDoc+ , setupMessage+ , info+ , infoNoWrap+ , debug+ , debugNoWrap+ , chattyTry+ , annotateIO+ , exceptionWithMetadata+ , withOutputMarker++ -- * exceptions+ , handleDoesNotExist+ , ignoreSigPipe++ -- * running programs+ , rawSystemExit+ , rawSystemExitCode+ , rawSystemProc+ , rawSystemProcAction+ , rawSystemExitWithEnv+ , rawSystemExitWithEnvCwd+ , rawSystemStdout+ , rawSystemStdInOut+ , rawSystemIOWithEnv+ , rawSystemIOWithEnvAndAction+ , fromCreatePipe+ , maybeExit+ , xargs+ , findProgramVersion++ -- ** 'IOData' re-export++ --+ -- These types are re-exported from+ -- "Distribution.Utils.IOData" for convenience as they're+ -- exposed in the API of 'rawSystemStdInOut'+ , IOData (..)+ , KnownIODataMode (..)+ , IODataMode (..)+ , VerboseException (..)++ -- * copying files+ , createDirectoryIfMissingVerbose+ , copyFileVerbose+ , copyFiles+ , copyFileTo+ , copyFileToCwd++ -- * installing files+ , installOrdinaryFile+ , installExecutableFile+ , installMaybeExecutableFile+ , installOrdinaryFiles+ , installExecutableFiles+ , installMaybeExecutableFiles+ , installDirectoryContents+ , copyDirectoryRecursive++ -- * File permissions+ , doesExecutableExist+ , setFileOrdinary+ , setFileExecutable++ -- * file names+ , shortRelativePath+ , dropExeExtension+ , exeExtensions++ -- * finding files+ , findFileEx+ , findFileCwd+ , findFirstFile+ , Suffix (..)+ , findFileWithExtension+ , findFileCwdWithExtension+ , findFileWithExtension'+ , findFileCwdWithExtension'+ , findAllFilesWithExtension+ , findAllFilesCwdWithExtension+ , findModuleFileEx+ , findModuleFileCwd+ , findModuleFilesEx+ , findModuleFilesCwd+ , getDirectoryContentsRecursive++ -- * environment variables+ , isInSearchPath+ , addLibraryPath++ -- * modification time+ , moreRecentFile+ , existsAndIsMoreRecentThan++ -- * temp files and dirs+ , TempFileOptions (..)+ , defaultTempFileOptions+ , withTempFile+ , withTempFileCwd+ , withTempFileEx+ , withTempDirectory+ , withTempDirectoryCwd+ , withTempDirectoryEx+ , withTempDirectoryCwdEx+ , createTempDirectory++ -- * .cabal and .buildinfo files+ , defaultPackageDescCwd+ , findPackageDesc+ , tryFindPackageDesc+ , findHookedPackageDesc++ -- * reading and writing files safely+ , withFileContents+ , writeFileAtomic+ , rewriteFileEx+ , rewriteFileLBS++ -- * Unicode+ , fromUTF8BS+ , fromUTF8LBS+ , toUTF8BS+ , toUTF8LBS+ , readUTF8File+ , withUTF8FileContents+ , writeUTF8File+ , normaliseLineEndings++ -- * BOM+ , ignoreBOM++ -- * generic utils+ , dropWhileEndLE+ , takeWhileEndLE+ , equating+ , comparing+ , isInfixOf+ , intercalate+ , lowercase+ , listUnion+ , listUnionRight+ , ordNub+ , sortNub+ , ordNubBy+ , ordNubRight+ , safeHead+ , safeTail+ , safeLast+ , safeInit+ , unintersperse+ , wrapText+ , wrapLine+ , stripCommonPrefix++ -- * FilePath stuff+ , isAbsoluteOnAnyPlatform+ , isRelativeOnAnyPlatform+ , exceptionWithCallStackPrefix+ ) where++import Distribution.Compat.Async (waitCatch, withAsyncNF)+import Distribution.Compat.CopyFile+import Distribution.Compat.FilePath as FilePath+import Distribution.Compat.Internal.TempFile+import Distribution.Compat.Lens (Lens', over)+import Distribution.Compat.Prelude+import Distribution.Compat.Stack+import Distribution.ModuleName as ModuleName+import Distribution.Simple.Errors+import Distribution.Simple.PreProcess.Types+import Distribution.System+import Distribution.Types.PackageId+import Distribution.Utils.Generic+import Distribution.Utils.IOData (IOData (..), IODataMode (..), KnownIODataMode (..))+import qualified Distribution.Utils.IOData as IOData+import Distribution.Utils.Path+import Distribution.Verbosity+import Distribution.Version+import Prelude ()++#ifdef CURRENT_PACKAGE_KEY+#define BOOTSTRAPPED_CABAL 1+#endif++#ifdef BOOTSTRAPPED_CABAL+import qualified Paths_Cabal (version)+#endif++import Distribution.Parsec+import Distribution.Pretty++import qualified Data.ByteString.Lazy as BS+import Data.Typeable+ ( cast+ )++import qualified Control.Exception as Exception+import Data.Time.Clock.POSIX (POSIXTime, getPOSIXTime)+import Distribution.Compat.Process (proc)+import Foreign.C.Error (Errno (..), ePIPE)+import qualified GHC.IO.Exception as GHC+import GHC.Stack (HasCallStack)+import Numeric (showFFloat)+import System.Directory+ ( Permissions (executable)+ , createDirectory+ , doesDirectoryExist+ , doesFileExist+ , getDirectoryContents+ , getModificationTime+ , getPermissions+ , getTemporaryDirectory+ , removeDirectoryRecursive+ , removeFile+ )+import System.Environment+ ( getProgName+ )+import System.FilePath (takeFileName)+import System.FilePath as FilePath+ ( getSearchPath+ , joinPath+ , normalise+ , searchPathSeparator+ , splitDirectories+ , splitExtension+ , takeDirectory+ )+import System.IO+ ( BufferMode (..)+ , Handle+ , hClose+ , hFlush+ , hGetContents+ , hPutStr+ , hPutStrLn+ , hSetBinaryMode+ , hSetBuffering+ , stderr+ , stdout+ )+import System.IO.Error+import System.IO.Unsafe+ ( unsafeInterleaveIO+ )+import qualified System.Process as Process+import qualified Text.PrettyPrint as Disp++#ifdef GIT_REV+import Data.Either (isLeft)+import GitHash+ ( giHash+ , giBranch+ , giCommitDate+ , tGitInfoCwdTry+ )+#endif++-- We only get our own version number when we're building with ourselves+cabalVersion :: Version+#if defined(BOOTSTRAPPED_CABAL)+cabalVersion = mkVersion' Paths_Cabal.version+#elif defined(CABAL_VERSION)+cabalVersion = mkVersion [CABAL_VERSION]+#else+cabalVersion = mkVersion [3,0] --used when bootstrapping+#endif++-- |+-- `Cabal` Git information. Only filled in if built in a Git tree in+-- development mode and Template Haskell is available.+cabalGitInfo :: String+#ifdef GIT_REV+cabalGitInfo = if giHash' == ""+ then ""+ else concat [ "(commit "+ , giHash'+ , branchInfo+ , ", "+ , either (const "") giCommitDate gi'+ , ")"+ ]+ where+ gi' = $$tGitInfoCwdTry+ giHash' = take 7 . either (const "") giHash $ gi'+ branchInfo | isLeft gi' = ""+ | either id giBranch gi' == "master" = ""+ | otherwise = " on " <> either id giBranch gi'+#else+cabalGitInfo = ""+#endif++-- ----------------------------------------------------------------------------+-- Exception and logging utils++-- Cabal's logging infrastructure has a few constraints:+--+-- * We must make all logging formatting and emissions decisions based+-- on the 'Verbosity' parameter, which is the only parameter that is+-- plumbed to enough call-sites to actually be used for this matter.+-- (One of Cabal's "big mistakes" is to have never have defined a+-- monad of its own.)+--+-- * When we 'die', we must raise an IOError. This a backwards+-- compatibility consideration, because that's what we've raised+-- previously, and if we change to any other exception type,+-- exception handlers which match on IOError will no longer work.+-- One case where it is known we rely on IOError being catchable+-- is 'readPkgConfigDb' in cabal-install; there may be other+-- user code that also assumes this.+--+-- * The 'topHandler' does not know what 'Verbosity' is, because+-- it gets called before we've done command line parsing (where+-- the 'Verbosity' parameter would come from).+--+-- This leads to two big architectural choices:+--+-- * Although naively we might imagine 'Verbosity' to be a simple+-- enumeration type, actually it is a full-on abstract data type+-- that may contain arbitrarily complex information. At the+-- moment, it is fully representable as a string, but we might+-- eventually also use verbosity to let users register their+-- own logging handler.+--+-- * When we call 'die', we perform all the formatting and addition+-- of extra information we need, and then ship this in the IOError+-- to the top-level handler. Here are alternate designs that+-- don't work:+--+-- a) Ship the unformatted info to the handler. This doesn't+-- work because at the point the handler gets the message,+-- we've lost call stacks, and even if we did, we don't have access+-- to 'Verbosity' to decide whether or not to render it.+--+-- b) Print the information at the 'die' site, then raise an+-- error. This means that if the exception is subsequently+-- caught by a handler, we will still have emitted the output,+-- which is not the correct behavior.+--+-- For the top-level handler to "know" that an error message+-- contains one of these fully formatted packets, we set a sentinel+-- in one of IOError's extra fields. This is handled by+-- 'ioeSetVerbatim' and 'ioeGetVerbatim'.+--++dieNoVerbosity :: String -> IO a+dieNoVerbosity msg =+ ioError (userError msg)+ where+ _ = callStack -- TODO: Attach CallStack to exception++-- | Tag an 'IOError' whose error string should be output to the screen+-- verbatim.+ioeSetVerbatim :: IOError -> IOError+ioeSetVerbatim e = ioeSetLocation e "dieVerbatim"++-- | Check if an 'IOError' should be output verbatim to screen.+ioeGetVerbatim :: IOError -> Bool+ioeGetVerbatim e = ioeGetLocation e == "dieVerbatim"++-- | Create a 'userError' whose error text will be output verbatim+verbatimUserError :: String -> IOError+verbatimUserError = ioeSetVerbatim . userError++dieWithLocation' :: Verbosity -> FilePath -> Maybe Int -> String -> IO a+dieWithLocation' verbosity filename mb_lineno msg =+ die' verbosity $+ filename+ ++ ( case mb_lineno of+ Just lineno -> ":" ++ show lineno+ Nothing -> ""+ )+ ++ ": "+ ++ msg++die' :: Verbosity -> String -> IO a+die' verbosity msg = withFrozenCallStack $ do+ ioError . verbatimUserError+ =<< annotateErrorString verbosity+ =<< pure . wrapTextVerbosity verbosity+ =<< pure . addErrorPrefix+ =<< prefixWithProgName msg++-- Type which will be a wrapper for cabal -exceptions and cabal-install exceptions+data VerboseException a = VerboseException CallStack POSIXTime Verbosity a+ deriving (Show)++-- Function which will replace the existing die' call sites+dieWithException :: (HasCallStack, Show a1, Typeable a1, Exception (VerboseException a1)) => Verbosity -> a1 -> IO a+dieWithException verbosity exception = do+ ts <- getPOSIXTime+ throwIO $ VerboseException callStack ts verbosity exception++-- Instance for Cabal Exception which will display error code and error message with callStack info+instance Exception (VerboseException CabalException) where+ displayException :: VerboseException CabalException -> [Char]+ displayException (VerboseException stack timestamp verb cabalexception) =+ withOutputMarker+ verb+ ( concat+ [ "Error: [Cabal-"+ , show (exceptionCode cabalexception)+ , "]\n"+ ]+ )+ ++ exceptionWithMetadata stack timestamp verb (exceptionMessage cabalexception)++dieNoWrap :: Verbosity -> String -> IO a+dieNoWrap verbosity msg = withFrozenCallStack $ do+ -- TODO: should this have program name or not?+ ioError . verbatimUserError+ =<< annotateErrorString+ verbosity+ (addErrorPrefix msg)++-- | Prefixing a message to indicate that it is a fatal error,+-- if the 'errorPrefix' is not already present.+addErrorPrefix :: String -> String+addErrorPrefix msg+ | errorPrefix `isPrefixOf` msg = msg+ -- Backpack prefixes its errors already with "Error:", see+ -- 'Distribution.Utils.LogProgress.dieProgress'.+ -- Taking it away there destroys the layout, so we rather+ -- check here whether the prefix is already present.+ | otherwise = unwords [errorPrefix, msg]++-- | A prefix indicating that a message is a fatal error.+errorPrefix :: String+errorPrefix = "Error:"++-- | Prefix an error string with program name from 'getProgName'+prefixWithProgName :: String -> IO String+prefixWithProgName msg = do+ pname <- getProgName+ return $ pname ++ ": " ++ msg++-- | Annotate an error string with timestamp and 'withMetadata'.+annotateErrorString :: Verbosity -> String -> IO String+annotateErrorString verbosity msg = do+ ts <- getPOSIXTime+ return $ withMetadata ts AlwaysMark VerboseTrace verbosity msg++-- | Given a block of IO code that may raise an exception, annotate+-- it with the metadata from the current scope. Use this as close+-- to external code that raises IO exceptions as possible, since+-- this function unconditionally wraps the error message with a trace+-- (so it is NOT idempotent.)+annotateIO :: Verbosity -> IO a -> IO a+annotateIO verbosity act = do+ ts <- getPOSIXTime+ flip modifyIOError act $+ ioeModifyErrorString $+ withMetadata ts NeverMark VerboseTrace verbosity++-- | A semantic editor for the error message inside an 'IOError'.+ioeModifyErrorString :: (String -> String) -> IOError -> IOError+ioeModifyErrorString = over ioeErrorString++-- | A lens for the error message inside an 'IOError'.+ioeErrorString :: Lens' IOError String+ioeErrorString f ioe = ioeSetErrorString ioe <$> f (ioeGetErrorString ioe)++{-# NOINLINE topHandlerWith #-}+topHandlerWith :: forall a. (Exception.SomeException -> IO a) -> IO a -> IO a+topHandlerWith cont prog = do+ -- By default, stderr to a terminal device is NoBuffering. But this+ -- is *really slow*+ hSetBuffering stderr LineBuffering+ Exception.catches+ prog+ [ Exception.Handler rethrowAsyncExceptions+ , Exception.Handler rethrowExitStatus+ , Exception.Handler handle+ ]+ where+ -- Let async exceptions rise to the top for the default top-handler+ rethrowAsyncExceptions :: Exception.AsyncException -> IO a+ rethrowAsyncExceptions a = throwIO a++ -- ExitCode gets thrown asynchronously too, and we don't want to print it+ rethrowExitStatus :: ExitCode -> IO a+ rethrowExitStatus = throwIO++ -- Print all other exceptions+ handle :: Exception.SomeException -> IO a+ handle se = do+ hFlush stdout+ pname <- getProgName+ hPutStr stderr (message pname se)+ cont se++ message :: String -> Exception.SomeException -> String+ message pname (Exception.SomeException se) =+ case cast se :: Maybe Exception.IOException of+ Just ioe+ | ioeGetVerbatim ioe ->+ -- Use the message verbatim+ ioeGetErrorString ioe ++ "\n"+ | isUserError ioe ->+ let file = case ioeGetFileName ioe of+ Nothing -> ""+ Just path -> path ++ location ++ ": "+ location = case ioeGetLocation ioe of+ l@(n : _) | isDigit n -> ':' : l+ _ -> ""+ detail = ioeGetErrorString ioe+ in wrapText $ addErrorPrefix $ pname ++ ": " ++ file ++ detail+ _ ->+ displaySomeException se ++ "\n"++-- | BC wrapper around 'Exception.displayException'.+displaySomeException :: Exception.Exception e => e -> String+displaySomeException se = Exception.displayException se++topHandler :: IO a -> IO a+topHandler prog = topHandlerWith (const $ exitWith (ExitFailure 1)) prog++-- | Depending on 'isVerboseStderr', set the output handle to 'stderr' or 'stdout'.+verbosityHandle :: Verbosity -> Handle+verbosityHandle verbosity+ | isVerboseStderr verbosity = stderr+ | otherwise = stdout++-- | Non fatal conditions that may be indicative of an error or problem.+--+-- We display these at the 'normal' verbosity level.+warn :: Verbosity -> String -> IO ()+warn verbosity msg = warnMessage "Warning" verbosity msg++-- | Like 'warn', but prepend @Error: …@ instead of @Warning: …@ before the+-- the message. Useful when you want to highlight the condition is an error+-- but do not want to quit the program yet.+warnError :: Verbosity -> String -> IO ()+warnError verbosity message = warnMessage "Error" verbosity message++-- | Warning message, with a custom label.+warnMessage :: String -> Verbosity -> String -> IO ()+warnMessage l verbosity msg = withFrozenCallStack $ do+ when ((verbosity >= normal) && not (isVerboseNoWarn verbosity)) $ do+ ts <- getPOSIXTime+ hFlush stdout+ hPutStr stderr+ . withMetadata ts NormalMark FlagTrace verbosity+ . wrapTextVerbosity verbosity+ $ l ++ ": " ++ msg++-- | Useful status messages.+--+-- We display these at the 'normal' verbosity level.+--+-- This is for the ordinary helpful status messages that users see. Just+-- enough information to know that things are working but not floods of detail.+notice :: Verbosity -> String -> IO ()+notice verbosity msg = withFrozenCallStack $ do+ when (verbosity >= normal) $ do+ let h = verbosityHandle verbosity+ ts <- getPOSIXTime+ hPutStr h $+ withMetadata ts NormalMark FlagTrace verbosity $+ wrapTextVerbosity verbosity $+ msg++-- | Display a message at 'normal' verbosity level, but without+-- wrapping.+noticeNoWrap :: Verbosity -> String -> IO ()+noticeNoWrap verbosity msg = withFrozenCallStack $ do+ when (verbosity >= normal) $ do+ let h = verbosityHandle verbosity+ ts <- getPOSIXTime+ hPutStr h . withMetadata ts NormalMark FlagTrace verbosity $ msg++-- | Pretty-print a 'Disp.Doc' status message at 'normal' verbosity+-- level. Use this if you need fancy formatting.+noticeDoc :: Verbosity -> Disp.Doc -> IO ()+noticeDoc verbosity msg = withFrozenCallStack $ do+ when (verbosity >= normal) $ do+ let h = verbosityHandle verbosity+ ts <- getPOSIXTime+ hPutStr h $+ withMetadata ts NormalMark FlagTrace verbosity $+ Disp.renderStyle defaultStyle $+ msg++-- | Display a "setup status message". Prefer using setupMessage'+-- if possible.+setupMessage :: Verbosity -> String -> PackageIdentifier -> IO ()+setupMessage verbosity msg pkgid = withFrozenCallStack $ do+ noticeNoWrap verbosity (msg ++ ' ' : prettyShow pkgid ++ "...")++-- | More detail on the operation of some action.+--+-- We display these messages when the verbosity level is 'verbose'+info :: Verbosity -> String -> IO ()+info verbosity msg = withFrozenCallStack $+ when (verbosity >= verbose) $ do+ let h = verbosityHandle verbosity+ ts <- getPOSIXTime+ hPutStr h $+ withMetadata ts NeverMark FlagTrace verbosity $+ wrapTextVerbosity verbosity $+ msg++infoNoWrap :: Verbosity -> String -> IO ()+infoNoWrap verbosity msg = withFrozenCallStack $+ when (verbosity >= verbose) $ do+ let h = verbosityHandle verbosity+ ts <- getPOSIXTime+ hPutStr h $+ withMetadata ts NeverMark FlagTrace verbosity $+ msg++-- | Detailed internal debugging information+--+-- We display these messages when the verbosity level is 'deafening'+debug :: Verbosity -> String -> IO ()+debug verbosity msg = withFrozenCallStack $+ when (verbosity >= deafening) $ do+ let h = verbosityHandle verbosity+ ts <- getPOSIXTime+ hPutStr h $+ withMetadata ts NeverMark FlagTrace verbosity $+ wrapTextVerbosity verbosity $+ msg+ -- ensure that we don't lose output if we segfault/infinite loop+ hFlush stdout++-- | A variant of 'debug' that doesn't perform the automatic line+-- wrapping. Produces better output in some cases.+debugNoWrap :: Verbosity -> String -> IO ()+debugNoWrap verbosity msg = withFrozenCallStack $+ when (verbosity >= deafening) $ do+ let h = verbosityHandle verbosity+ ts <- getPOSIXTime+ hPutStr h $+ withMetadata ts NeverMark FlagTrace verbosity $+ msg+ -- ensure that we don't lose output if we segfault/infinite loop+ hFlush stdout++-- | Perform an IO action, catching any IO exceptions and printing an error+-- if one occurs.+chattyTry+ :: String+ -- ^ a description of the action we were attempting+ -> IO ()+ -- ^ the action itself+ -> IO ()+chattyTry desc action =+ catchIO action $ \exception ->+ hPutStrLn stderr $ "Error while " ++ desc ++ ": " ++ show exception++-- | Run an IO computation, returning @e@ if it raises a "file+-- does not exist" error.+handleDoesNotExist :: a -> IO a -> IO a+handleDoesNotExist e =+ Exception.handleJust+ (\ioe -> if isDoesNotExistError ioe then Just ioe else Nothing)+ (\_ -> return e)++-- -----------------------------------------------------------------------------+-- Helper functions++-- | Wraps text unless the @+nowrap@ verbosity flag is active+wrapTextVerbosity :: Verbosity -> String -> String+wrapTextVerbosity verb+ | isVerboseNoWrap verb = withTrailingNewline+ | otherwise = withTrailingNewline . wrapText++-- | Prepends a timestamp if @+timestamp@ verbosity flag is set+--+-- This is used by 'withMetadata'+withTimestamp :: Verbosity -> POSIXTime -> String -> String+withTimestamp v ts msg+ | isVerboseTimestamp v = msg'+ | otherwise = msg -- no-op+ where+ msg' = case lines msg of+ [] -> tsstr "\n"+ l1 : rest -> unlines (tsstr (' ' : l1) : map (contpfx ++) rest)++ -- format timestamp to be prepended to first line with msec precision+ tsstr = showFFloat (Just 3) (realToFrac ts :: Double)++ -- continuation prefix for subsequent lines of msg+ contpfx = replicate (length (tsstr " ")) ' '++-- | Wrap output with a marker if @+markoutput@ verbosity flag is set.+--+-- NB: Why is markoutput done with start/end markers, and not prefixes?+-- Markers are more convenient to add (if we want to add prefixes,+-- we have to 'lines' and then 'map'; here's it's just some+-- concatenates). Note that even in the prefix case, we can't+-- guarantee that the markers are unambiguous, because some of+-- Cabal's output comes straight from external programs, where+-- we don't have the ability to interpose on the output.+--+-- This is used by 'withMetadata'+withOutputMarker :: Verbosity -> String -> String+withOutputMarker v xs | not (isVerboseMarkOutput v) = xs+withOutputMarker _ "" = "" -- Minor optimization, don't mark uselessly+withOutputMarker _ xs =+ "-----BEGIN CABAL OUTPUT-----\n"+ ++ withTrailingNewline xs+ ++ "-----END CABAL OUTPUT-----\n"++-- | Append a trailing newline to a string if it does not+-- already have a trailing newline.+withTrailingNewline :: String -> String+withTrailingNewline "" = ""+withTrailingNewline (x : xs) = x : go x xs+ where+ go _ (c : cs) = c : go c cs+ go '\n' "" = ""+ go _ "" = "\n"++-- | Prepend a call-site and/or call-stack based on Verbosity+withCallStackPrefix :: WithCallStack (TraceWhen -> Verbosity -> String -> String)+withCallStackPrefix tracer verbosity s =+ withFrozenCallStack $+ ( if isVerboseCallSite verbosity+ then+ parentSrcLocPrefix+ +++ -- Hack: need a newline before starting output marker :(+ if isVerboseMarkOutput verbosity+ then "\n"+ else ""+ else ""+ )+ ++ ( case traceWhen verbosity tracer of+ Just pre -> pre ++ prettyCallStack callStack ++ "\n"+ Nothing -> ""+ )+ ++ s++-- | When should we emit the call stack? We always emit+-- for internal errors, emit the trace for errors when we+-- are in verbose mode, and otherwise only emit it if+-- explicitly asked for using the @+callstack@ verbosity+-- flag. (At the moment, 'AlwaysTrace' is not used.+data TraceWhen+ = AlwaysTrace+ | VerboseTrace+ | FlagTrace+ deriving (Eq)++-- | Determine if we should emit a call stack.+-- If we trace, it also emits any prefix we should append.+traceWhen :: Verbosity -> TraceWhen -> Maybe String+traceWhen _ AlwaysTrace = Just ""+traceWhen v VerboseTrace | v >= verbose = Just ""+traceWhen v FlagTrace | isVerboseCallStack v = Just "----\n"+traceWhen _ _ = Nothing++-- | When should we output the marker? Things like 'die'+-- always get marked, but a 'NormalMark' will only be+-- output if we're not a quiet verbosity.+data MarkWhen = AlwaysMark | NormalMark | NeverMark++-- | Add all necessary metadata to a logging message+withMetadata :: WithCallStack (POSIXTime -> MarkWhen -> TraceWhen -> Verbosity -> String -> String)+withMetadata ts marker tracer verbosity x =+ withFrozenCallStack+ $+ -- NB: order matters. Output marker first because we+ -- don't want to capture call stacks.+ withTrailingNewline+ . withCallStackPrefix tracer verbosity+ . ( case marker of+ AlwaysMark -> withOutputMarker verbosity+ NormalMark+ | not (isVerboseQuiet verbosity) ->+ withOutputMarker verbosity+ | otherwise ->+ id+ NeverMark -> id+ )+ -- Clear out any existing markers+ . clearMarkers+ . withTimestamp verbosity ts+ $ x++-- | Add all necessary metadata to a logging message+exceptionWithMetadata :: CallStack -> POSIXTime -> Verbosity -> String -> String+exceptionWithMetadata stack ts verbosity x =+ withTrailingNewline+ . exceptionWithCallStackPrefix stack verbosity+ . withOutputMarker verbosity+ . clearMarkers+ . withTimestamp verbosity ts+ $ x++clearMarkers :: String -> String+clearMarkers s = unlines . filter isMarker $ lines s+ where+ isMarker "-----BEGIN CABAL OUTPUT-----" = False+ isMarker "-----END CABAL OUTPUT-----" = False+ isMarker _ = True++-- | Append a call-site and/or call-stack based on Verbosity+exceptionWithCallStackPrefix :: CallStack -> Verbosity -> String -> String+exceptionWithCallStackPrefix stack verbosity s =+ s+ ++ withFrozenCallStack+ ( ( if isVerboseCallSite verbosity+ then+ parentSrcLocPrefix+ +++ -- Hack: need a newline before starting output marker :(+ if isVerboseMarkOutput verbosity+ then "\n"+ else ""+ else ""+ )+ ++ ( if verbosity >= verbose+ then prettyCallStack stack ++ "\n"+ else ""+ )+ )++-- -----------------------------------------------------------------------------+-- rawSystem variants+--+-- These all use 'Distribution.Compat.Process.proc' to ensure we+-- consistently use process jobs on Windows and Ctrl-C delegation+-- on Unix.+--+-- Additionally, they take care of logging command execution.+--++-- | Helper to use with one of the 'rawSystem' variants, and exit+-- unless the command completes successfully.+maybeExit :: IO ExitCode -> IO ()+maybeExit cmd = do+ exitcode <- cmd+ unless (exitcode == ExitSuccess) $ exitWith exitcode++-- | Log a command execution (that's typically about to happen)+-- at info level, and log working directory and environment overrides+-- at debug level if specified.+logCommand :: Verbosity -> Process.CreateProcess -> IO ()+logCommand verbosity cp = do+ infoNoWrap verbosity $+ "Running: " <> case Process.cmdspec cp of+ Process.ShellCommand sh -> sh+ Process.RawCommand path args -> Process.showCommandForUser path args+ case Process.env cp of+ Just env -> debugNoWrap verbosity $ "with environment: " ++ show env+ Nothing -> return ()+ case Process.cwd cp of+ Just cwd -> debugNoWrap verbosity $ "with working directory: " ++ show cwd+ Nothing -> return ()+ hFlush stdout++-- | Execute the given command with the given arguments, exiting+-- with the same exit code if the command fails.+rawSystemExit :: Verbosity -> Maybe (SymbolicPath CWD (Dir Pkg)) -> FilePath -> [String] -> IO ()+rawSystemExit verbosity mbWorkDir path args =+ withFrozenCallStack $+ maybeExit $+ rawSystemExitCode verbosity mbWorkDir path args Nothing++-- | Execute the given command with the given arguments, returning+-- the command's exit code.+rawSystemExitCode+ :: Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> FilePath+ -> [String]+ -> Maybe [(String, String)]+ -> IO ExitCode+rawSystemExitCode verbosity mbWorkDir path args menv =+ withFrozenCallStack $+ rawSystemProc verbosity $+ (proc path args)+ { Process.cwd = fmap getSymbolicPath mbWorkDir+ , Process.env = menv+ }++-- | Execute the given command with the given arguments, returning+-- the command's exit code.+--+-- Create the process argument with 'Distribution.Compat.Process.proc'+-- to ensure consistent options with other 'rawSystem' functions in this+-- module.+rawSystemProc :: Verbosity -> Process.CreateProcess -> IO ExitCode+rawSystemProc verbosity cp = withFrozenCallStack $ do+ (exitcode, _) <- rawSystemProcAction verbosity cp $ \_ _ _ -> return ()+ return exitcode++-- | Execute the given command with the given arguments, returning+-- the command's exit code. 'action' is executed while the command+-- is running, and would typically be used to communicate with the+-- process through pipes.+--+-- Create the process argument with 'Distribution.Compat.Process.proc'+-- to ensure consistent options with other 'rawSystem' functions in this+-- module.+rawSystemProcAction+ :: Verbosity+ -> Process.CreateProcess+ -> (Maybe Handle -> Maybe Handle -> Maybe Handle -> IO a)+ -> IO (ExitCode, a)+rawSystemProcAction verbosity cp action = withFrozenCallStack $ do+ logCommand verbosity cp+ (exitcode, a) <- Process.withCreateProcess cp $ \mStdin mStdout mStderr p -> do+ a <- action mStdin mStdout mStderr+ exitcode <- Process.waitForProcess p+ return (exitcode, a)+ unless (exitcode == ExitSuccess) $ do+ let cmd = case Process.cmdspec cp of+ Process.ShellCommand sh -> sh+ Process.RawCommand path _args -> path+ debug verbosity $ cmd ++ " returned " ++ show exitcode+ return (exitcode, a)++-- | fromJust for dealing with 'Maybe Handle' values as obtained via+-- 'System.Process.CreatePipe'. Creating a pipe using 'CreatePipe' guarantees+-- a 'Just' value for the corresponding handle.+fromCreatePipe :: Maybe Handle -> Handle+fromCreatePipe = maybe (error "fromCreatePipe: Nothing") id++-- | Execute the given command with the given arguments and+-- environment, exiting with the same exit code if the command fails.+rawSystemExitWithEnv+ :: Verbosity+ -> FilePath+ -> [String]+ -> [(String, String)]+ -> IO ()+rawSystemExitWithEnv verbosity =+ rawSystemExitWithEnvCwd verbosity Nothing++-- | Like 'rawSystemExitWithEnv', but setting a working directory.+rawSystemExitWithEnvCwd+ :: Verbosity+ -> Maybe (SymbolicPath CWD to)+ -> FilePath+ -> [String]+ -> [(String, String)]+ -> IO ()+rawSystemExitWithEnvCwd verbosity mbWorkDir path args env =+ withFrozenCallStack $+ maybeExit $+ rawSystemProc verbosity $+ (proc path args)+ { Process.env = Just env+ , Process.cwd = getSymbolicPath <$> mbWorkDir+ }++-- | Execute the given command with the given arguments, returning+-- the command's exit code.+--+-- Optional arguments allow setting working directory, environment+-- and input and output handles.+rawSystemIOWithEnv+ :: Verbosity+ -> FilePath+ -> [String]+ -> Maybe FilePath+ -- ^ New working dir or inherit+ -> Maybe [(String, String)]+ -- ^ New environment or inherit+ -> Maybe Handle+ -- ^ stdin+ -> Maybe Handle+ -- ^ stdout+ -> Maybe Handle+ -- ^ stderr+ -> IO ExitCode+rawSystemIOWithEnv verbosity path args mcwd menv inp out err = withFrozenCallStack $ do+ (exitcode, _) <-+ rawSystemIOWithEnvAndAction+ verbosity+ path+ args+ mcwd+ menv+ action+ inp+ out+ err+ return exitcode+ where+ action = return ()++-- | Execute the given command with the given arguments, returning+-- the command's exit code. 'action' is executed while the command+-- is running, and would typically be used to communicate with the+-- process through pipes.+--+-- Optional arguments allow setting working directory, environment+-- and input and output handles.+rawSystemIOWithEnvAndAction+ :: Verbosity+ -> FilePath+ -> [String]+ -> Maybe FilePath+ -- ^ New working dir or inherit+ -> Maybe [(String, String)]+ -- ^ New environment or inherit+ -> IO a+ -- ^ action to perform after process is created, but before 'waitForProcess'.+ -> Maybe Handle+ -- ^ stdin+ -> Maybe Handle+ -- ^ stdout+ -> Maybe Handle+ -- ^ stderr+ -> IO (ExitCode, a)+rawSystemIOWithEnvAndAction verbosity path args mcwd menv action inp out err = withFrozenCallStack $ do+ let cp =+ (proc path args)+ { Process.cwd = mcwd+ , Process.env = menv+ , Process.std_in = mbToStd inp+ , Process.std_out = mbToStd out+ , Process.std_err = mbToStd err+ }+ rawSystemProcAction verbosity cp (\_ _ _ -> action)+ where+ mbToStd :: Maybe Handle -> Process.StdStream+ mbToStd = maybe Process.Inherit Process.UseHandle++-- | Execute the given command with the given arguments, returning+-- the command's output. Exits if the command exits with error.+--+-- Provides control over the binary/text mode of the output.+rawSystemStdout :: forall mode. KnownIODataMode mode => Verbosity -> FilePath -> [String] -> IO mode+rawSystemStdout verbosity path args = withFrozenCallStack $ do+ (output, errors, exitCode) <-+ rawSystemStdInOut+ verbosity+ path+ args+ Nothing+ Nothing+ Nothing+ (IOData.iodataMode :: IODataMode mode)+ when (exitCode /= ExitSuccess) $+ dieWithException verbosity $+ RawSystemStdout errors+ return output++-- | Execute the given command with the given arguments, returning+-- the command's output, errors and exit code.+--+-- Optional arguments allow setting working directory, environment+-- and command input.+--+-- Provides control over the binary/text mode of the input and output.+rawSystemStdInOut+ :: KnownIODataMode mode+ => Verbosity+ -> FilePath+ -- ^ Program location+ -> [String]+ -- ^ Arguments+ -> Maybe FilePath+ -- ^ New working dir or inherit+ -> Maybe [(String, String)]+ -- ^ New environment or inherit+ -> Maybe IOData+ -- ^ input text and binary mode+ -> IODataMode mode+ -- ^ iodata mode, acts as proxy+ -> IO (mode, String, ExitCode)+ -- ^ output, errors, exit+rawSystemStdInOut verbosity path args mcwd menv input _ = withFrozenCallStack $ do+ let cp =+ (proc path args)+ { Process.cwd = mcwd+ , Process.env = menv+ , Process.std_in = Process.CreatePipe+ , Process.std_out = Process.CreatePipe+ , Process.std_err = Process.CreatePipe+ }++ (exitcode, (mberr1, mberr2)) <- rawSystemProcAction verbosity cp $ \mb_in mb_out mb_err -> do+ let (inh, outh, errh) = (fromCreatePipe mb_in, fromCreatePipe mb_out, fromCreatePipe mb_err)+ flip Exception.finally (hClose inh >> hClose outh >> hClose errh) $ do+ -- output mode depends on what the caller wants+ -- but the errors are always assumed to be text (in the current locale)+ hSetBinaryMode errh False++ -- fork off a couple threads to pull on the stderr and stdout+ -- so if the process writes to stderr we do not block.++ withAsyncNF (hGetContents errh) $ \errA -> withAsyncNF (IOData.hGetIODataContents outh) $ \outA -> do+ -- push all the input, if any+ ignoreSigPipe $ case input of+ Nothing -> hClose inh+ Just inputData -> IOData.hPutContents inh inputData++ -- wait for both to finish+ mberr1 <- waitCatch outA+ mberr2 <- waitCatch errA+ return (mberr1, mberr2)++ -- get the stderr, so it can be added to error message+ err <- reportOutputIOError mberr2++ unless (exitcode == ExitSuccess) $+ debug verbosity $+ path+ ++ " returned "+ ++ show exitcode+ ++ if null err+ then ""+ else+ " with error message:\n"+ ++ err+ ++ case input of+ Nothing -> ""+ Just d | IOData.null d -> ""+ Just (IODataText inp) -> "\nstdin input:\n" ++ inp+ Just (IODataBinary inp) -> "\nstdin input (binary):\n" ++ show inp++ -- Check if we hit an exception while consuming the output+ -- (e.g. a text decoding error)+ out <- reportOutputIOError mberr1++ return (out, err, exitcode)+ where+ reportOutputIOError :: Either Exception.SomeException a -> IO a+ reportOutputIOError (Right x) = return x+ reportOutputIOError (Left exc) = case fromException exc of+ Just ioe -> throwIO (ioeSetFileName ioe ("output of " ++ path))+ Nothing -> throwIO exc++-- | Ignore SIGPIPE in a subcomputation.+ignoreSigPipe :: IO () -> IO ()+ignoreSigPipe = Exception.handle $ \case+ GHC.IOError{GHC.ioe_type = GHC.ResourceVanished, GHC.ioe_errno = Just ioe}+ | Errno ioe == ePIPE -> return ()+ e -> throwIO e++-- | Look for a program and try to find it's version number. It can accept+-- either an absolute path or the name of a program binary, in which case we+-- will look for the program on the path.+findProgramVersion+ :: String+ -- ^ version args+ -> (String -> String)+ -- ^ function to select version+ -- number from program output+ -> Verbosity+ -> FilePath+ -- ^ location+ -> IO (Maybe Version)+findProgramVersion versionArg selectVersion verbosity path = withFrozenCallStack $ do+ str <-+ rawSystemStdout verbosity path [versionArg]+ `catchIO` (\_ -> return "")+ `catch` (\(_ :: VerboseException CabalException) -> return "")+ `catchExit` (\_ -> return "")+ let version :: Maybe Version+ version = simpleParsec (selectVersion str)+ case version of+ Nothing ->+ warn verbosity $+ "cannot determine version of "+ ++ path+ ++ " :\n"+ ++ show str+ Just v -> debug verbosity $ path ++ " is version " ++ prettyShow v+ return version++-- | Like the Unix xargs program. Useful for when we've got very long command+-- lines that might overflow an OS limit on command line length and so you+-- need to invoke a command multiple times to get all the args in.+--+-- Use it with either of the rawSystem variants above. For example:+--+-- > xargs (32*1024) (rawSystemExit verbosity) prog fixedArgs bigArgs+xargs+ :: Int+ -> ([String] -> IO ())+ -> [String]+ -> [String]+ -> IO ()+xargs maxSize rawSystemFun fixedArgs bigArgs =+ let fixedArgSize = sum (map length fixedArgs) + length fixedArgs+ chunkSize = maxSize - fixedArgSize+ in traverse_ (rawSystemFun . (fixedArgs ++)) (chunks chunkSize bigArgs)+ where+ chunks len = unfoldr $ \s ->+ if null s+ then Nothing+ else Just (chunk [] len s)++ chunk acc _ [] = (reverse acc, [])+ chunk acc len (s : ss)+ | len' < len = chunk (s : acc) (len - len' - 1) ss+ | otherwise = (reverse acc, s : ss)+ where+ len' = length s++-- ------------------------------------------------------------++-- * File Utilities++-- ------------------------------------------------------------++----------------+-- Finding files++-- | Find a file by looking in a search path. The file path must match exactly.+--+-- @since 3.4.0.0+findFileCwd+ :: forall searchDir allowAbsolute+ . Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ working directory+ -> [SymbolicPathX allowAbsolute Pkg (Dir searchDir)]+ -- ^ search directories+ -> RelativePath searchDir File+ -- ^ File Name+ -> IO (SymbolicPathX allowAbsolute Pkg File)+findFileCwd verbosity mbWorkDir searchPath fileName =+ findFirstFile+ (interpretSymbolicPath mbWorkDir)+ [ path </> fileName+ | path <- ordNub searchPath+ ]+ >>= maybe (dieWithException verbosity $ FindFile $ getSymbolicPath fileName) return++-- | Find a file by looking in a search path. The file path must match exactly.+findFileEx+ :: forall searchDir allowAbsolute+ . Verbosity+ -> [SymbolicPathX allowAbsolute Pkg (Dir searchDir)]+ -- ^ search directories+ -> RelativePath searchDir File+ -- ^ File Name+ -> IO (SymbolicPathX allowAbsolute Pkg File)+findFileEx v = findFileCwd v Nothing++-- | Find a file by looking in a search path with one of a list of possible+-- file extensions. The file base name should be given and it will be tried+-- with each of the extensions in each element of the search path.+findFileWithExtension+ :: [Suffix]+ -> [SymbolicPathX allowAbsolute Pkg (Dir searchDir)]+ -> RelativePath searchDir File+ -> IO (Maybe (SymbolicPathX allowAbsolute Pkg File))+findFileWithExtension =+ findFileCwdWithExtension Nothing++-- | Find a file by looking in a search path with one of a list of possible+-- file extensions.+--+-- @since 3.4.0.0+findFileCwdWithExtension+ :: forall searchDir allowAbsolute+ . Maybe (SymbolicPath CWD (Dir Pkg))+ -> [Suffix]+ -> [SymbolicPathX allowAbsolute Pkg (Dir searchDir)]+ -> RelativePath searchDir File+ -> IO (Maybe (SymbolicPathX allowAbsolute Pkg File))+findFileCwdWithExtension cwd extensions searchPath baseName =+ fmap (uncurry (</>))+ <$> findFileCwdWithExtension' cwd extensions searchPath baseName++-- | @since 3.4.0.0+findAllFilesCwdWithExtension+ :: forall searchDir allowAbsolute+ . Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ working directory+ -> [Suffix]+ -- ^ extensions+ -> [SymbolicPathX allowAbsolute Pkg (Dir searchDir)]+ -- ^ relative search locations+ -> RelativePath searchDir File+ -- ^ basename+ -> IO [SymbolicPathX allowAbsolute Pkg File]+findAllFilesCwdWithExtension mbWorkDir extensions searchPath basename =+ findAllFiles+ (interpretSymbolicPath mbWorkDir)+ [ path </> basename <.> ext+ | path <- ordNub searchPath+ , Suffix ext <- ordNub extensions+ ]++findAllFilesWithExtension+ :: [Suffix]+ -> [SymbolicPathX allowAbsolute Pkg (Dir searchDir)]+ -> RelativePath searchDir File+ -> IO [SymbolicPathX allowAbsolute Pkg File]+findAllFilesWithExtension =+ findAllFilesCwdWithExtension Nothing++-- | Like 'findFileWithExtension' but returns which element of the search path+-- the file was found in, and the file path relative to that base directory.+findFileWithExtension'+ :: [Suffix]+ -> [SymbolicPathX allowAbsolute Pkg (Dir searchDir)]+ -> RelativePath searchDir File+ -> IO (Maybe (SymbolicPathX allowAbsolute Pkg (Dir searchDir), RelativePath searchDir File))+findFileWithExtension' =+ findFileCwdWithExtension' Nothing++-- | Like 'findFileCwdWithExtension' but returns which element of the search path+-- the file was found in, and the file path relative to that base directory.+findFileCwdWithExtension'+ :: forall searchDir allowAbsolute+ . Maybe (SymbolicPath CWD (Dir Pkg))+ -> [Suffix]+ -> [SymbolicPathX allowAbsolute Pkg (Dir searchDir)]+ -> RelativePath searchDir File+ -> IO (Maybe (SymbolicPathX allowAbsolute Pkg (Dir searchDir), RelativePath searchDir File))+findFileCwdWithExtension' cwd extensions searchPath baseName =+ findFirstFile+ (uncurry mkPath)+ [ (path, baseName <.> ext)+ | path <- ordNub searchPath+ , Suffix ext <- ordNub extensions+ ]+ where+ mkPath :: SymbolicPathX allowAbsolute Pkg (Dir searchDir) -> RelativePath searchDir File -> FilePath+ mkPath base file =+ interpretSymbolicPath cwd (base </> file)++findFirstFile :: (a -> FilePath) -> [a] -> IO (Maybe a)+findFirstFile file = findFirst+ where+ findFirst [] = return Nothing+ findFirst (x : xs) = do+ exists <- doesFileExist (file x)+ if exists+ then return (Just x)+ else findFirst xs++findAllFiles :: (a -> FilePath) -> [a] -> IO [a]+findAllFiles file = filterM (doesFileExist . file)++-- | Finds the files corresponding to a list of Haskell module names.+--+-- As 'findModuleFile' but for a list of module names.+findModuleFilesEx+ :: forall searchDir allowAbsolute+ . Verbosity+ -> [SymbolicPathX allowAbsolute Pkg (Dir searchDir)]+ -- ^ build prefix (location of objects)+ -> [Suffix]+ -- ^ search suffixes+ -> [ModuleName]+ -- ^ modules+ -> IO [(SymbolicPathX allowAbsolute Pkg (Dir searchDir), RelativePath searchDir File)]+findModuleFilesEx verbosity searchPath extensions moduleNames =+ traverse (findModuleFileEx verbosity searchPath extensions) moduleNames++-- | Finds the files corresponding to a list of Haskell module names.+--+-- As 'findModuleFileCwd' but for a list of module names.+findModuleFilesCwd+ :: forall searchDir allowAbsolute+ . Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> [SymbolicPathX allowAbsolute Pkg (Dir searchDir)]+ -- ^ build prefix (location of objects)+ -> [Suffix]+ -- ^ search suffixes+ -> [ModuleName]+ -- ^ modules+ -> IO [(SymbolicPathX allowAbsolute Pkg (Dir searchDir), RelativePath searchDir File)]+findModuleFilesCwd verbosity cwd searchPath extensions moduleNames =+ traverse (findModuleFileCwd verbosity cwd searchPath extensions) moduleNames++-- | Find the file corresponding to a Haskell module name.+--+-- This is similar to 'findFileWithExtension'' but specialised to a module+-- name. The function fails if the file corresponding to the module is missing.+findModuleFileEx+ :: forall searchDir allowAbsolute+ . Verbosity+ -> [SymbolicPathX allowAbsolute Pkg (Dir searchDir)]+ -- ^ build prefix (location of objects)+ -> [Suffix]+ -- ^ search suffixes+ -> ModuleName+ -- ^ module+ -> IO (SymbolicPathX allowAbsolute Pkg (Dir searchDir), RelativePath searchDir File)+findModuleFileEx verbosity =+ findModuleFileCwd verbosity Nothing++-- | Find the file corresponding to a Haskell module name.+--+-- This is similar to 'findFileCwdWithExtension'' but specialised to a module+-- name. The function fails if the file corresponding to the module is missing.+findModuleFileCwd+ :: forall searchDir allowAbsolute+ . Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> [SymbolicPathX allowAbsolute Pkg (Dir searchDir)]+ -- ^ build prefix (location of objects)+ -> [Suffix]+ -- ^ search suffixes+ -> ModuleName+ -- ^ module+ -> IO (SymbolicPathX allowAbsolute Pkg (Dir searchDir), RelativePath searchDir File)+findModuleFileCwd verbosity cwd searchPath extensions mod_name = do+ mbRes <-+ findFileCwdWithExtension'+ cwd+ extensions+ searchPath+ (makeRelativePathEx $ ModuleName.toFilePath mod_name)+ case mbRes of+ Nothing ->+ dieWithException verbosity $+ FindModuleFileEx mod_name extensions (map getSymbolicPath searchPath)+ Just res -> return res++-- | List all the files in a directory and all subdirectories.+--+-- The order places files in sub-directories after all the files in their+-- parent directories. The list is generated lazily so is not well defined if+-- the source directory structure changes before the list is used.+getDirectoryContentsRecursive :: FilePath -> IO [FilePath]+getDirectoryContentsRecursive topdir = recurseDirectories [""]+ where+ recurseDirectories :: [FilePath] -> IO [FilePath]+ recurseDirectories [] = return []+ recurseDirectories (dir : dirs) = unsafeInterleaveIO $ do+ (files, dirs') <- collect [] [] =<< getDirectoryContents (topdir </> dir)+ files' <- recurseDirectories (dirs' ++ dirs)+ return (files ++ files')+ where+ collect files dirs' [] =+ return+ ( reverse files+ , reverse dirs'+ )+ collect files dirs' (entry : entries)+ | ignore entry =+ collect files dirs' entries+ collect files dirs' (entry : entries) = do+ let dirEntry = dir </> entry+ isDirectory <- doesDirectoryExist (topdir </> dirEntry)+ if isDirectory+ then collect files (dirEntry : dirs') entries+ else collect (dirEntry : files) dirs' entries++ ignore ['.'] = True+ ignore ['.', '.'] = True+ ignore _ = False++------------------------+-- Environment variables++-- | Is this directory in the system search path?+isInSearchPath :: FilePath -> IO Bool+isInSearchPath path = fmap (elem path) getSearchPath++addLibraryPath+ :: OS+ -> [FilePath]+ -> [(String, String)]+ -> [(String, String)]+addLibraryPath os paths = addEnv+ where+ pathsString = intercalate [searchPathSeparator] paths+ ldPath = case os of+ OSX -> "DYLD_LIBRARY_PATH"+ _ -> "LD_LIBRARY_PATH"++ addEnv [] = [(ldPath, pathsString)]+ addEnv ((key, value) : xs)+ | key == ldPath =+ if null value+ then (key, pathsString) : xs+ else (key, value ++ (searchPathSeparator : pathsString)) : xs+ | otherwise = (key, value) : addEnv xs++--------------------+-- Modification time++-- | Compare the modification times of two files to see if the first is newer+-- than the second. The first file must exist but the second need not.+-- The expected use case is when the second file is generated using the first.+-- In this use case, if the result is True then the second file is out of date.+moreRecentFile :: FilePath -> FilePath -> IO Bool+moreRecentFile a b = do+ exists <- doesFileExist b+ if not exists+ then return True+ else do+ tb <- getModificationTime b+ ta <- getModificationTime a+ return (ta > tb)++-- | Like 'moreRecentFile', but also checks that the first file exists.+existsAndIsMoreRecentThan :: FilePath -> FilePath -> IO Bool+existsAndIsMoreRecentThan a b = do+ exists <- doesFileExist a+ if not exists+ then return False+ else a `moreRecentFile` b++----------------------------------------+-- Copying and installing files and dirs++-- | Same as 'createDirectoryIfMissing' but logs at higher verbosity levels.+createDirectoryIfMissingVerbose+ :: Verbosity+ -> Bool+ -- ^ Create its parents too?+ -> FilePath+ -> IO ()+createDirectoryIfMissingVerbose verbosity create_parents path0+ | create_parents = withFrozenCallStack $ createDirs (parents path0)+ | otherwise = withFrozenCallStack $ createDirs (take 1 (parents path0))+ where+ parents = reverse . scanl1 (</>) . splitDirectories . normalise++ createDirs [] = return ()+ createDirs (dir : []) = createDir dir throwIO+ createDirs (dir : dirs) =+ createDir dir $ \_ -> do+ createDirs dirs+ createDir dir throwIO++ createDir :: FilePath -> (IOException -> IO ()) -> IO ()+ createDir dir notExistHandler = do+ r <- tryIO $ createDirectoryVerbose verbosity dir+ case (r :: Either IOException ()) of+ Right () -> return ()+ Left e+ | isDoesNotExistError e -> notExistHandler e+ -- createDirectory (and indeed POSIX mkdir) does not distinguish+ -- between a dir already existing and a file already existing. So we+ -- check for it here. Unfortunately there is a slight race condition+ -- here, but we think it is benign. It could report an exception in+ -- the case that the dir did exist but another process deletes the+ -- directory and creates a file in its place before we can check+ -- that the directory did indeed exist.+ | isAlreadyExistsError e ->+ ( do+ isDir <- doesDirectoryExist dir+ unless isDir $ throwIO e+ )+ `catchIO` ((\_ -> return ()) :: IOException -> IO ())+ | otherwise -> throwIO e++createDirectoryVerbose :: Verbosity -> FilePath -> IO ()+createDirectoryVerbose verbosity dir = withFrozenCallStack $ do+ info verbosity $ "creating " ++ dir+ createDirectory dir+ setDirOrdinary dir++-- | Copies a file without copying file permissions. The target file is created+-- with default permissions. Any existing target file is replaced.+--+-- At higher verbosity levels it logs an info message.+copyFileVerbose :: Verbosity -> FilePath -> FilePath -> IO ()+copyFileVerbose verbosity src dest = withFrozenCallStack $ do+ info verbosity ("copy " ++ src ++ " to " ++ dest)+ copyFile src dest++-- | Install an ordinary file. This is like a file copy but the permissions+-- are set appropriately for an installed file. On Unix it is \"-rw-r--r--\"+-- while on Windows it uses the default permissions for the target directory.+installOrdinaryFile :: Verbosity -> FilePath -> FilePath -> IO ()+installOrdinaryFile verbosity src dest = withFrozenCallStack $ do+ info verbosity ("Installing " ++ src ++ " to " ++ dest)+ copyOrdinaryFile src dest++-- | Install an executable file. This is like a file copy but the permissions+-- are set appropriately for an installed file. On Unix it is \"-rwxr-xr-x\"+-- while on Windows it uses the default permissions for the target directory.+installExecutableFile :: Verbosity -> FilePath -> FilePath -> IO ()+installExecutableFile verbosity src dest = withFrozenCallStack $ do+ info verbosity ("Installing executable " ++ src ++ " to " ++ dest)+ copyExecutableFile src dest++-- | Install a file that may or not be executable, preserving permissions.+installMaybeExecutableFile :: Verbosity -> FilePath -> FilePath -> IO ()+installMaybeExecutableFile verbosity src dest = withFrozenCallStack $ do+ perms <- getPermissions src+ if (executable perms) -- only checks user x bit+ then installExecutableFile verbosity src dest+ else installOrdinaryFile verbosity src dest++-- | Given a relative path to a file, copy it to the given directory, preserving+-- the relative path and creating the parent directories if needed.+copyFileTo+ :: Verbosity+ -> FilePath+ -> FilePath+ -> IO ()+copyFileTo verbosity dir file =+ withFrozenCallStack $+ copyFileToCwd+ verbosity+ Nothing+ (makeSymbolicPath dir)+ (makeRelativePathEx file)++-- | Given a relative path to a file, copy it to the given directory, preserving+-- the relative path and creating the parent directories if needed.+copyFileToCwd+ :: Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -> SymbolicPath Pkg (Dir target)+ -> RelativePath Pkg File+ -> IO ()+copyFileToCwd verbosity mbWorkDir dir file = withFrozenCallStack $ do+ let targetFile = i $ dir </> unsafeCoerceSymbolicPath file+ createDirectoryIfMissingVerbose verbosity True (takeDirectory targetFile)+ installOrdinaryFile verbosity (i file) targetFile+ where+ i :: SymbolicPathX allowAbs Pkg to -> FilePath+ i = interpretSymbolicPath mbWorkDir++-- | Common implementation of 'copyFiles', 'installOrdinaryFiles',+-- 'installExecutableFiles' and 'installMaybeExecutableFiles'.+copyFilesWith+ :: (Verbosity -> FilePath -> FilePath -> IO ())+ -> Verbosity+ -> FilePath+ -> [(FilePath, FilePath)]+ -> IO ()+copyFilesWith doCopy verbosity targetDir srcFiles = withFrozenCallStack $ do+ -- Create parent directories for everything+ let dirs = map (targetDir </>) . ordNub . map (takeDirectory . snd) $ srcFiles+ traverse_ (createDirectoryIfMissingVerbose verbosity True) dirs++ -- Copy all the files+ sequence_+ [ let src = srcBase </> srcFile+ dest = targetDir </> srcFile+ in doCopy verbosity src dest+ | (srcBase, srcFile) <- srcFiles+ ]++-- | Copies a bunch of files to a target directory, preserving the directory+-- structure in the target location. The target directories are created if they+-- do not exist.+--+-- The files are identified by a pair of base directory and a path relative to+-- that base. It is only the relative part that is preserved in the+-- destination.+--+-- For example:+--+-- > copyFiles normal "dist/src"+-- > [("", "src/Foo.hs"), ("dist/build/", "src/Bar.hs")]+--+-- This would copy \"src\/Foo.hs\" to \"dist\/src\/src\/Foo.hs\" and+-- copy \"dist\/build\/src\/Bar.hs\" to \"dist\/src\/src\/Bar.hs\".+--+-- This operation is not atomic. Any IO failure during the copy (including any+-- missing source files) leaves the target in an unknown state so it is best to+-- use it with a freshly created directory so that it can be simply deleted if+-- anything goes wrong.+copyFiles :: Verbosity -> FilePath -> [(FilePath, FilePath)] -> IO ()+copyFiles v fp fs = withFrozenCallStack (copyFilesWith copyFileVerbose v fp fs)++-- | This is like 'copyFiles' but uses 'installOrdinaryFile'.+installOrdinaryFiles :: Verbosity -> FilePath -> [(FilePath, FilePath)] -> IO ()+installOrdinaryFiles v fp fs = withFrozenCallStack (copyFilesWith installOrdinaryFile v fp fs)++-- | This is like 'copyFiles' but uses 'installExecutableFile'.+installExecutableFiles+ :: Verbosity+ -> FilePath+ -> [(FilePath, FilePath)]+ -> IO ()+installExecutableFiles v fp fs = withFrozenCallStack (copyFilesWith installExecutableFile v fp fs)++-- | This is like 'copyFiles' but uses 'installMaybeExecutableFile'.+installMaybeExecutableFiles+ :: Verbosity+ -> FilePath+ -> [(FilePath, FilePath)]+ -> IO ()+installMaybeExecutableFiles v fp fs = withFrozenCallStack (copyFilesWith installMaybeExecutableFile v fp fs)++-- | This installs all the files in a directory to a target location,+-- preserving the directory layout. All the files are assumed to be ordinary+-- rather than executable files.+installDirectoryContents :: Verbosity -> FilePath -> FilePath -> IO ()+installDirectoryContents verbosity srcDir destDir = withFrozenCallStack $ do+ info verbosity ("copy directory '" ++ srcDir ++ "' to '" ++ destDir ++ "'.")+ srcFiles <- getDirectoryContentsRecursive srcDir+ installOrdinaryFiles verbosity destDir [(srcDir, f) | f <- srcFiles]++-- | Recursively copy the contents of one directory to another path.+copyDirectoryRecursive :: Verbosity -> FilePath -> FilePath -> IO ()+copyDirectoryRecursive verbosity srcDir destDir = withFrozenCallStack $ do+ info verbosity ("copy directory '" ++ srcDir ++ "' to '" ++ destDir ++ "'.")+ srcFiles <- getDirectoryContentsRecursive srcDir+ copyFilesWith+ (const copyFile)+ verbosity+ destDir+ [ (srcDir, f)+ | f <- srcFiles+ ]++-------------------+-- File permissions++-- | Like 'doesFileExist', but also checks that the file is executable.+doesExecutableExist :: FilePath -> IO Bool+doesExecutableExist f = do+ exists <- doesFileExist f+ if exists+ then do+ perms <- getPermissions f+ return (executable perms)+ else return False++---------------------------+-- Temporary files and dirs++-- | Advanced options for 'withTempFile' and 'withTempDirectory'.+data TempFileOptions = TempFileOptions+ { optKeepTempFiles :: Bool+ -- ^ Keep temporary files?+ }++defaultTempFileOptions :: TempFileOptions+defaultTempFileOptions = TempFileOptions{optKeepTempFiles = False}++-- | Use a temporary filename that doesn't already exist+withTempFile+ :: String+ -- ^ File name template. See 'openTempFile'.+ -> (FilePath -> Handle -> IO a)+ -> IO a+withTempFile template f = withFrozenCallStack $+ withTempFileCwd template $+ \fp h -> f (getSymbolicPath fp) h++-- | Use a temporary filename that doesn't already exist.+withTempFileCwd+ :: String+ -- ^ File name template. See 'openTempFile'.+ -> (SymbolicPath Pkg File -> Handle -> IO a)+ -> IO a+withTempFileCwd = withFrozenCallStack $ withTempFileEx defaultTempFileOptions++-- | A version of 'withTempFile' that additionally takes a 'TempFileOptions'+-- argument.+withTempFileEx+ :: forall a+ . TempFileOptions+ -> String+ -- ^ File name template. See 'openTempFile'.+ -> (SymbolicPath Pkg File -> Handle -> IO a)+ -> IO a+withTempFileEx opts template action = do+ tmp <- getTemporaryDirectory+ withFrozenCallStack $+ Exception.bracket+ (openTempFile tmp template)+ ( \(name, handle) -> do+ hClose handle+ unless (optKeepTempFiles opts) $+ handleDoesNotExist () $+ removeFile $+ name+ )+ (withLexicalCallStack (\(fn, h) -> action (mkRelToPkg tmp fn) h))+ where+ mkRelToPkg :: FilePath -> FilePath -> SymbolicPath Pkg File+ mkRelToPkg tmp fp =+ makeSymbolicPath tmp </> makeRelativePathEx (takeFileName fp)++-- 'openTempFile' returns a path of the form @i tmpDir </> fn@, but we+-- want 'withTempFileEx' to return @tmpDir </> fn@. So we split off+-- the filename and add back the (un-interpreted) directory.+-- This assumes 'openTempFile' returns a filepath of the form+-- @inputDir </> fn@, where @fn@ does not contain any path separators.++-- | Create and use a temporary directory.+--+-- Creates a new temporary directory inside the given directory, making use+-- of the template. The temp directory is deleted after use. For example:+--+-- > withTempDirectory verbosity "src" "sdist." $ \tmpDir -> do ...+--+-- The @tmpDir@ will be a new subdirectory of the given directory, e.g.+-- @src/sdist.342@.+withTempDirectory+ :: Verbosity+ -> FilePath+ -> String+ -> (FilePath -> IO a)+ -> IO a+withTempDirectory verb targetDir template f =+ withFrozenCallStack $+ withTempDirectoryCwd+ verb+ Nothing+ (makeSymbolicPath targetDir)+ template+ (f . getSymbolicPath)++-- | Create and use a temporary directory.+--+-- Creates a new temporary directory inside the given directory, making use+-- of the template. The temp directory is deleted after use. For example:+--+-- > withTempDirectory verbosity "src" "sdist." $ \tmpDir -> do ...+--+-- The @tmpDir@ will be a new subdirectory of the given directory, e.g.+-- @src/sdist.342@.+withTempDirectoryCwd+ :: Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ Working directory+ -> SymbolicPath Pkg (Dir tmpDir1)+ -> String+ -> (SymbolicPath Pkg (Dir tmpDir2) -> IO a)+ -> IO a+withTempDirectoryCwd verbosity mbWorkDir targetDir template f =+ withFrozenCallStack $+ withTempDirectoryCwdEx+ verbosity+ defaultTempFileOptions+ mbWorkDir+ targetDir+ template+ (withLexicalCallStack (\x -> f x))++-- | A version of 'withTempDirectory' that additionally takes a+-- 'TempFileOptions' argument.+withTempDirectoryEx+ :: Verbosity+ -> TempFileOptions+ -> FilePath+ -> String+ -> (FilePath -> IO a)+ -> IO a+withTempDirectoryEx verbosity opts targetDir template f =+ withFrozenCallStack $+ withTempDirectoryCwdEx verbosity opts Nothing (makeSymbolicPath targetDir) template $+ \fp -> f (getSymbolicPath fp)++-- | A version of 'withTempDirectoryCwd' that additionally takes a+-- 'TempFileOptions' argument.+withTempDirectoryCwdEx+ :: forall a tmpDir1 tmpDir2+ . Verbosity+ -> TempFileOptions+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ Working directory+ -> SymbolicPath Pkg (Dir tmpDir1)+ -> String+ -> (SymbolicPath Pkg (Dir tmpDir2) -> IO a)+ -> IO a+withTempDirectoryCwdEx _verbosity opts mbWorkDir targetDir template f =+ withFrozenCallStack $+ Exception.bracket+ (createTempDirectory (i targetDir) template)+ ( \tmpDirRelPath ->+ unless (optKeepTempFiles opts) $+ handleDoesNotExist () $+ removeDirectoryRecursive (i targetDir </> tmpDirRelPath)+ )+ (withLexicalCallStack (\tmpDirRelPath -> f $ targetDir </> makeRelativePathEx tmpDirRelPath))+ where+ i = interpretSymbolicPath mbWorkDir -- See Note [Symbolic paths] in Distribution.Utils.Path++-----------------------------------+-- Safely reading and writing files++-- | Write a file but only if it would have new content. If we would be writing+-- the same as the existing content then leave the file as is so that we do not+-- update the file's modification time.+--+-- NB: Before Cabal-3.0 the file content was assumed to be+-- ASCII-representable. Since Cabal-3.0 the file is assumed to be+-- UTF-8 encoded.+rewriteFileEx :: Verbosity -> FilePath -> String -> IO ()+rewriteFileEx verbosity path =+ rewriteFileLBS verbosity path . toUTF8LBS++-- | Same as `rewriteFileEx` but for 'ByteString's.+rewriteFileLBS :: Verbosity -> FilePath -> BS.ByteString -> IO ()+rewriteFileLBS verbosity path newContent =+ flip catchIO mightNotExist $ do+ existingContent <- annotateIO verbosity $ BS.readFile path+ _ <- evaluate (BS.length existingContent)+ unless (existingContent == newContent) $+ annotateIO verbosity $+ writeFileAtomic path newContent+ where+ mightNotExist e+ | isDoesNotExistError e =+ annotateIO verbosity $ writeFileAtomic path newContent+ | otherwise =+ ioError e++shortRelativePath :: FilePath -> FilePath -> FilePath+shortRelativePath from to =+ case dropCommonPrefix (splitDirectories from) (splitDirectories to) of+ (stuff, path) -> joinPath (map (const "..") stuff ++ path)+ where+ dropCommonPrefix :: Eq a => [a] -> [a] -> ([a], [a])+ dropCommonPrefix (x : xs) (y : ys)+ | x == y = dropCommonPrefix xs ys+ dropCommonPrefix xs ys = (xs, ys)++-- | Drop the extension if it's one of 'exeExtensions', or return the path+-- unchanged.+dropExeExtension :: FilePath -> FilePath+dropExeExtension filepath =+ -- System.FilePath's extension handling functions are horribly+ -- inconsistent, consider:+ --+ -- isExtensionOf "" "foo" == False but+ -- isExtensionOf "" "foo." == True.+ --+ -- On the other hand stripExtension doesn't remove the empty extension:+ --+ -- stripExtension "" "foo." == Just "foo."+ --+ -- Since by "" in exeExtensions we mean 'no extension' anyways we can+ -- just always ignore it here.+ let exts = [ext | ext <- exeExtensions, ext /= ""]+ in fromMaybe filepath $ do+ ext <- find (`FilePath.isExtensionOf` filepath) exts+ ext `FilePath.stripExtension` filepath++-- | List of possible executable file extensions on the current build+-- platform.+exeExtensions :: [String]+exeExtensions = case (buildArch, buildOS) of+ -- Possible improvement: on Windows, read the list of extensions from the+ -- PATHEXT environment variable. By default PATHEXT is ".com; .exe; .bat;+ -- .cmd".+ --+ -- See also #10179.+ --+ -- Also we cannot actually run @.bat@ files as we do now, because of+ -- https://github.com/haskell/process/issues/140. If we detect one of those,+ -- we should record that the program is a script and run a @Process.shell@ instead+ -- of a @Process.proc@.+ (_, Windows) -> ["", "exe"]+ (_, Ghcjs) -> ["", "exe"]+ (Wasm32, _) -> ["", "wasm"]+ _ -> [""]++-- ------------------------------------------------------------++-- * Finding the description file++-- ------------------------------------------------------------++-- | Package description file (/pkgname/@.cabal@) in the current+-- working directory.+defaultPackageDescCwd :: Verbosity -> IO (RelativePath Pkg File)+defaultPackageDescCwd verbosity = tryFindPackageDesc verbosity Nothing++-- | Find a package description file in the given directory. Looks for+-- @.cabal@ files.+findPackageDesc+ :: Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ package directory+ -> IO (Either CabalException (RelativePath Pkg File))+findPackageDesc mbPkgDir =+ do+ let pkgDir = maybe "." getSymbolicPath mbPkgDir+ files <- getDirectoryContents pkgDir+ -- to make sure we do not mistake a ~/.cabal/ dir for a <pkgname>.cabal+ -- file we filter to exclude dirs and null base file names:+ cabalFiles <-+ filterM+ (doesFileExist . uncurry (</>))+ [ (pkgDir, file)+ | file <- files+ , let (name, ext) = splitExtension file+ , not (null name) && ext == ".cabal"+ ]+ case map snd cabalFiles of+ [] -> return (Left NoDesc)+ [cabalFile] -> return (Right $ makeRelativePathEx cabalFile)+ multiple -> return (Left $ MultiDesc multiple)++-- | Like 'findPackageDesc', but calls 'die' in case of error.+tryFindPackageDesc+ :: Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ directory in which to look+ -> IO (RelativePath Pkg File)+tryFindPackageDesc verbosity dir =+ either (dieWithException verbosity) return =<< findPackageDesc dir++-- | Find auxiliary package information in the given directory.+-- Looks for @.buildinfo@ files.+findHookedPackageDesc+ :: Verbosity+ -> Maybe (SymbolicPath CWD (Dir Pkg))+ -- ^ Working directory+ -> SymbolicPath Pkg (Dir Build)+ -- ^ Directory to search+ -> IO (Maybe (SymbolicPath Pkg File))+ -- ^ /dir/@\/@/pkgname/@.buildinfo@, if present+findHookedPackageDesc verbosity mbWorkDir dir = do+ files <- getDirectoryContents $ interpretSymbolicPath mbWorkDir dir+ buildInfoFiles <-+ filterM+ (doesFileExist . interpretSymbolicPath mbWorkDir)+ [ dir </> makeRelativePathEx file+ | file <- files+ , let (name, ext) = splitExtension file+ , not (null name) && ext == buildInfoExt+ ]+ case buildInfoFiles of+ [] -> return Nothing+ [f] -> return (Just f)+ _ -> dieWithException verbosity $ MultipleFilesWithExtension buildInfoExt++buildInfoExt :: String+buildInfoExt = ".buildinfo"++-- | @stripCommonPrefix xs ys@ gives you @ys@ without the common prefix with @xs@.+stripCommonPrefix :: String -> String -> String+stripCommonPrefix (x : xs) (y : ys)+ | x == y = stripCommonPrefix xs ys+ | otherwise = y : ys+stripCommonPrefix _ ys = ys
− src/Distribution/Simple/Utils/Json.hs
@@ -1,46 +0,0 @@--- | Utility json lib for Cabal--- TODO: Remove it again.-module Distribution.Simple.Utils.Json- ( Json(..)- , renderJson- ) where--data Json = JsonArray [Json]- | JsonBool !Bool- | JsonNull- | JsonNumber !Int- | JsonObject [(String, Json)]- | JsonString !String--renderJson :: Json -> ShowS-renderJson (JsonArray objs) =- surround "[" "]" $ intercalate "," $ map renderJson objs-renderJson (JsonBool True) = showString "true"-renderJson (JsonBool False) = showString "false"-renderJson JsonNull = showString "null"-renderJson (JsonNumber n) = shows n-renderJson (JsonObject attrs) =- surround "{" "}" $ intercalate "," $ map render attrs- where- render (k,v) = (surround "\"" "\"" $ showString' k) . showString ":" . renderJson v-renderJson (JsonString s) = surround "\"" "\"" $ showString' s--surround :: String -> String -> ShowS -> ShowS-surround begin end middle = showString begin . middle . showString end--showString' :: String -> ShowS-showString' xs = showStringWorker xs- where- showStringWorker :: String -> ShowS- showStringWorker ('\"':as) = showString "\\\"" . showStringWorker as- showStringWorker ('\\':as) = showString "\\\\" . showStringWorker as- showStringWorker ('\'':as) = showString "\\\'" . showStringWorker as- showStringWorker (x:as) = showString [x] . showStringWorker as- showStringWorker [] = showString ""--intercalate :: String -> [ShowS] -> ShowS-intercalate sep = go- where- go [] = id- go [x] = x- go (x:xs) = x . showString' sep . go xs
− src/Distribution/System.hs
@@ -1,275 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleContexts #-}---------------------------------------------------------------------------------- |--- Module : Distribution.System--- Copyright : Duncan Coutts 2007-2008------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ Cabal often needs to do slightly different things on specific platforms. You--- probably know about the 'System.Info.os' however using that is very--- inconvenient because it is a string and different Haskell implementations--- do not agree on using the same strings for the same platforms! (In--- particular see the controversy over \"windows\" vs \"mingw32\"). So to make it--- more consistent and easy to use we have an 'OS' enumeration.----module Distribution.System (- -- * Operating System- OS(..),- buildOS,-- -- * Machine Architecture- Arch(..),- buildArch,-- -- * Platform is a pair of arch and OS- Platform(..),- buildPlatform,- platformFromTriple,-- -- * Internal- knownOSs,- knownArches,-- -- * Classification- ClassificationStrictness (..),- classifyOS,- classifyArch,- ) where--import Prelude ()-import Distribution.Compat.Prelude-import Control.Applicative (liftA2)--import qualified System.Info (os, arch)-import Distribution.Utils.Generic (lowercase)--import Distribution.Parsec-import Distribution.Pretty--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp---- | How strict to be when classifying strings into the 'OS' and 'Arch' enums.------ The reason we have multiple ways to do the classification is because there--- are two situations where we need to do it.------ For parsing OS and arch names in .cabal files we really want everyone to be--- referring to the same or arch by the same name. Variety is not a virtue--- in this case. We don't mind about case though.------ For the System.Info.os\/arch different Haskell implementations use different--- names for the same or\/arch. Also they tend to distinguish versions of an--- OS\/arch which we just don't care about.------ The 'Compat' classification allows us to recognise aliases that are already--- in common use but it allows us to distinguish them from the canonical name--- which enables us to warn about such deprecated aliases.----data ClassificationStrictness = Permissive | Compat | Strict---- --------------------------------------------------------------- * Operating System--- ---------------------------------------------------------------- | These are the known OS names: Linux, Windows, OSX--- ,FreeBSD, OpenBSD, NetBSD, DragonFly--- ,Solaris, AIX, HPUX, IRIX--- ,HaLVM ,Hurd ,IOS, Android,Ghcjs------ The following aliases can also be used:,--- * Windows aliases: mingw32, win32, cygwin32--- * OSX alias: darwin--- * Hurd alias: gnu--- * FreeBSD alias: kfreebsdgnu--- * Solaris alias: solaris2----data OS = Linux | Windows | OSX -- tier 1 desktop OSs- | FreeBSD | OpenBSD | NetBSD -- other free Unix OSs- | DragonFly- | Solaris | AIX | HPUX | IRIX -- ageing Unix OSs- | HaLVM -- bare metal / VMs / hypervisors- | Hurd -- GNU's microkernel- | IOS | Android -- mobile OSs- | Ghcjs- | OtherOS String- deriving (Eq, Generic, Ord, Show, Read, Typeable, Data)--instance Binary OS-instance Structured OS-instance NFData OS where rnf = genericRnf--knownOSs :: [OS]-knownOSs = [Linux, Windows, OSX- ,FreeBSD, OpenBSD, NetBSD, DragonFly- ,Solaris, AIX, HPUX, IRIX- ,HaLVM- ,Hurd- ,IOS, Android- ,Ghcjs]--osAliases :: ClassificationStrictness -> OS -> [String]-osAliases Permissive Windows = ["mingw32", "win32", "cygwin32"]-osAliases Compat Windows = ["mingw32", "win32"]-osAliases _ OSX = ["darwin"]-osAliases _ Hurd = ["gnu"]-osAliases Permissive FreeBSD = ["kfreebsdgnu"]-osAliases Compat FreeBSD = ["kfreebsdgnu"]-osAliases Permissive Solaris = ["solaris2"]-osAliases Compat Solaris = ["solaris2"]-osAliases Permissive Android = ["linux-android", "linux-androideabi", "linux-androideabihf"]-osAliases Compat Android = ["linux-android"]-osAliases _ _ = []--instance Pretty OS where- pretty (OtherOS name) = Disp.text name- pretty other = Disp.text (lowercase (show other))--instance Parsec OS where- parsec = classifyOS Compat <$> parsecIdent----classifyOS :: ClassificationStrictness -> String -> OS-classifyOS strictness s =- fromMaybe (OtherOS s) $ lookup (lowercase s) osMap- where- osMap = [ (name, os)- | os <- knownOSs- , name <- prettyShow os : osAliases strictness os ]--buildOS :: OS-buildOS = classifyOS Permissive System.Info.os---- --------------------------------------------------------------- * Machine Architecture--- ---------------------------------------------------------------- | These are the known Arches: I386, X86_64, PPC, PPC64, Sparc,--- Arm, AArch64, Mips, SH, IA64, S39, Alpha, Hppa, Rs6000, M68k,--- Vax, and JavaScript.------ The following aliases can also be used:--- * PPC alias: powerpc--- * PPC64 alias : powerpc64, powerpc64le--- * Sparc aliases: sparc64, sun4--- * Mips aliases: mipsel, mipseb--- * Arm aliases: armeb, armel--- * AArch64 aliases: arm64----data Arch = I386 | X86_64 | PPC | PPC64 | Sparc- | Arm | AArch64 | Mips | SH- | IA64 | S390- | Alpha | Hppa | Rs6000- | M68k | Vax- | JavaScript- | OtherArch String- deriving (Eq, Generic, Ord, Show, Read, Typeable, Data)--instance Binary Arch-instance Structured Arch-instance NFData Arch where rnf = genericRnf--knownArches :: [Arch]-knownArches = [I386, X86_64, PPC, PPC64, Sparc- ,Arm, AArch64, Mips, SH- ,IA64, S390- ,Alpha, Hppa, Rs6000- ,M68k, Vax- ,JavaScript]--archAliases :: ClassificationStrictness -> Arch -> [String]-archAliases Strict _ = []-archAliases Compat _ = []-archAliases _ PPC = ["powerpc"]-archAliases _ PPC64 = ["powerpc64", "powerpc64le"]-archAliases _ Sparc = ["sparc64", "sun4"]-archAliases _ Mips = ["mipsel", "mipseb"]-archAliases _ Arm = ["armeb", "armel"]-archAliases _ AArch64 = ["arm64"]-archAliases _ _ = []--instance Pretty Arch where- pretty (OtherArch name) = Disp.text name- pretty other = Disp.text (lowercase (show other))--instance Parsec Arch where- parsec = classifyArch Strict <$> parsecIdent--classifyArch :: ClassificationStrictness -> String -> Arch-classifyArch strictness s =- fromMaybe (OtherArch s) $ lookup (lowercase s) archMap- where- archMap = [ (name, arch)- | arch <- knownArches- , name <- prettyShow arch : archAliases strictness arch ]--buildArch :: Arch-buildArch = classifyArch Permissive System.Info.arch---- --------------------------------------------------------------- * Platform--- --------------------------------------------------------------data Platform = Platform Arch OS- deriving (Eq, Generic, Ord, Show, Read, Typeable, Data)--instance Binary Platform-instance Structured Platform-instance NFData Platform where rnf = genericRnf--instance Pretty Platform where- pretty (Platform arch os) = pretty arch <<>> Disp.char '-' <<>> pretty os--instance Parsec Platform where- -- TODO: there are ambigious platforms like: `arch-word-os`- -- which could be parsed as- -- * Platform "arch-word" "os"- -- * Platform "arch" "word-os"- -- We could support that preferring variants 'OtherOS' or 'OtherArch'- --- -- For now we split into arch and os parts on the first dash.- parsec = do- arch <- parsecDashlessArch- _ <- P.char '-'- os <- parsec- return (Platform arch os)- where- parsecDashlessArch = classifyArch Strict <$> dashlessIdent-- dashlessIdent = liftA2 (:) firstChar rest- where- firstChar = P.satisfy isAlpha- rest = P.munch (\c -> isAlphaNum c || c == '_')---- | The platform Cabal was compiled on. In most cases,--- @LocalBuildInfo.hostPlatform@ should be used instead (the platform we're--- targeting).-buildPlatform :: Platform-buildPlatform = Platform buildArch buildOS---- Utils:--parsecIdent :: CabalParsing m => m String-parsecIdent = (:) <$> firstChar <*> rest- where- firstChar = P.satisfy isAlpha- rest = P.munch (\c -> isAlphaNum c || c == '_' || c == '-')--platformFromTriple :: String -> Maybe Platform-platformFromTriple triple =- either (const Nothing) Just $ explicitEitherParsec parseTriple triple- where parseWord = P.munch1 (\c -> isAlphaNum c || c == '_')- parseTriple = do- arch <- fmap (classifyArch Permissive) parseWord- _ <- P.char '-'- _ <- parseWord -- Skip vendor- _ <- P.char '-'- os <- fmap (classifyOS Permissive) parsecIdent -- OS may have hyphens, like- -- 'nto-qnx'- return $ Platform arch os
src/Distribution/TestSuite.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.TestSuite -- Copyright : Thomas Tuegel 2010@@ -12,91 +13,97 @@ -- -- This module defines the detailed test suite interface which makes it -- possible to expose individual tests to Cabal or other test agents.- module Distribution.TestSuite- ( TestInstance(..)- , OptionDescr(..)- , OptionType(..)- , Test(..)- , Options- , Progress(..)- , Result(..)- , testGroup- ) where+ ( TestInstance (..)+ , OptionDescr (..)+ , OptionType (..)+ , Test (..)+ , Options+ , Progress (..)+ , Result (..)+ , testGroup+ ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () data TestInstance = TestInstance- { run :: IO Progress -- ^ Perform the test.- , name :: String -- ^ A name for the test, unique within a- -- test suite.- , tags :: [String] -- ^ Users can select groups of tests by- -- their tags.- , options :: [OptionDescr] -- ^ Descriptions of the options recognized- -- by this test.- , setOption :: String -> String -> Either String TestInstance- -- ^ Try to set the named option to the given value. Returns an error- -- message if the option is not supported or the value could not be- -- correctly parsed; otherwise, a 'TestInstance' with the option set to- -- the given value is returned.- }+ { run :: IO Progress+ -- ^ Perform the test.+ , name :: String+ -- ^ A name for the test, unique within a+ -- test suite.+ , tags :: [String]+ -- ^ Users can select groups of tests by+ -- their tags.+ , options :: [OptionDescr]+ -- ^ Descriptions of the options recognized+ -- by this test.+ , setOption :: String -> String -> Either String TestInstance+ -- ^ Try to set the named option to the given value. Returns an error+ -- message if the option is not supported or the value could not be+ -- correctly parsed; otherwise, a 'TestInstance' with the option set to+ -- the given value is returned.+ } data OptionDescr = OptionDescr- { optionName :: String- , optionDescription :: String -- ^ A human-readable description of the- -- option to guide the user setting it.- , optionType :: OptionType- , optionDefault :: Maybe String- }+ { optionName :: String+ , optionDescription :: String+ -- ^ A human-readable description of the+ -- option to guide the user setting it.+ , optionType :: OptionType+ , optionDefault :: Maybe String+ } deriving (Eq, Read, Show) data OptionType- = OptionFile- { optionFileMustExist :: Bool- , optionFileIsDir :: Bool- , optionFileExtensions :: [String]- }- | OptionString- { optionStringMultiline :: Bool- }- | OptionNumber- { optionNumberIsInt :: Bool- , optionNumberBounds :: (Maybe String, Maybe String)- }- | OptionBool- | OptionEnum [String]- | OptionSet [String]- | OptionRngSeed+ = OptionFile+ { optionFileMustExist :: Bool+ , optionFileIsDir :: Bool+ , optionFileExtensions :: [String]+ }+ | OptionString+ { optionStringMultiline :: Bool+ }+ | OptionNumber+ { optionNumberIsInt :: Bool+ , optionNumberBounds :: (Maybe String, Maybe String)+ }+ | OptionBool+ | OptionEnum [String]+ | OptionSet [String]+ | OptionRngSeed deriving (Eq, Read, Show) data Test- = Test TestInstance- | Group- { groupName :: String- , concurrently :: Bool- -- ^ If true, then children of this group may be run in parallel.- -- Note that this setting is not inherited by children. In- -- particular, consider a group F with "concurrently = False" that- -- has some children, including a group T with "concurrently =- -- True". The children of group T may be run concurrently with each- -- other, as long as none are run at the same time as any of the- -- direct children of group F.- , groupTests :: [Test]- }- | ExtraOptions [OptionDescr] Test+ = Test TestInstance+ | Group+ { groupName :: String+ , concurrently :: Bool+ -- ^ If true, then children of this group may be run in parallel.+ -- Note that this setting is not inherited by children. In+ -- particular, consider a group F with "concurrently = False" that+ -- has some children, including a group T with "concurrently =+ -- True". The children of group T may be run concurrently with each+ -- other, as long as none are run at the same time as any of the+ -- direct children of group F.+ , groupTests :: [Test]+ }+ | ExtraOptions [OptionDescr] Test type Options = [(String, String)] -data Progress = Finished Result- | Progress String (IO Progress)+data Progress+ = Finished Result+ | Progress String (IO Progress) -data Result = Pass- | Fail String- | Error String+data Result+ = Pass+ | Fail String+ | Error String deriving (Eq, Read, Show) -- | Create a named group of tests, which are assumed to be safe to run in -- parallel. testGroup :: String -> [Test] -> Test-testGroup n ts = Group { groupName = n, concurrently = True, groupTests = ts }+testGroup n ts = Group{groupName = n, concurrently = True, groupTests = ts}
− src/Distribution/Text.hs
@@ -1,12 +0,0 @@--- Since @3.0@ this is a compat module.-module Distribution.Text (display, simpleParse) where-{- {-# DEPRECATED "Use Distribution.Parsec or Distribution.Pretty" #-} -}--import Distribution.Pretty-import Distribution.Parsec--display :: Pretty a => a -> String-display = prettyShow--simpleParse :: Parsec a => String -> Maybe a-simpleParse = simpleParsec
− src/Distribution/Types/AbiDependency.hs
@@ -1,44 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Types.AbiDependency where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Parsec-import Distribution.Pretty--import qualified Distribution.Compat.CharParsing as P-import qualified Distribution.Package as Package-import qualified Text.PrettyPrint as Disp---- | An ABI dependency is a dependency on a library which also--- records the ABI hash ('abiHash') of the library it depends--- on.------ The primary utility of this is to enable an extra sanity when--- GHC loads libraries: it can check if the dependency has a matching--- ABI and if not, refuse to load this library. This information--- is critical if we are shadowing libraries; differences in the--- ABI hash let us know what packages get shadowed by the new version--- of a package.-data AbiDependency = AbiDependency {- depUnitId :: Package.UnitId,- depAbiHash :: Package.AbiHash- }- deriving (Eq, Generic, Read, Show, Typeable)--instance Pretty AbiDependency where- pretty (AbiDependency uid abi) =- pretty uid <<>> Disp.char '=' <<>> pretty abi--instance Parsec AbiDependency where- parsec = do- uid <- parsec- _ <- P.char '='- abi <- parsec- return (AbiDependency uid abi)--instance Binary AbiDependency-instance Structured AbiDependency-instance NFData AbiDependency where rnf = genericRnf
− src/Distribution/Types/AbiHash.hs
@@ -1,61 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}--module Distribution.Types.AbiHash- ( AbiHash, unAbiHash, mkAbiHash- ) where--import Prelude ()-import Distribution.Compat.Prelude-import Distribution.Utils.ShortText--import qualified Distribution.Compat.CharParsing as P-import Distribution.Pretty-import Distribution.Parsec--import Text.PrettyPrint (text)---- | ABI Hashes------ Use 'mkAbiHash' and 'unAbiHash' to convert from/to a--- 'String'.------ This type is opaque since @Cabal-2.0@------ @since 2.0.0.2-newtype AbiHash = AbiHash ShortText- deriving (Eq, Show, Read, Generic, Typeable)---- | Construct a 'AbiHash' from a 'String'------ 'mkAbiHash' is the inverse to 'unAbiHash'------ Note: No validations are performed to ensure that the resulting--- 'AbiHash' is valid------ @since 2.0.0.2-unAbiHash :: AbiHash -> String-unAbiHash (AbiHash h) = fromShortText h---- | Convert 'AbiHash' to 'String'------ @since 2.0.0.2-mkAbiHash :: String -> AbiHash-mkAbiHash = AbiHash . toShortText---- | 'mkAbiHash'------ @since 2.0.0.2-instance IsString AbiHash where- fromString = mkAbiHash--instance Binary AbiHash-instance Structured AbiHash-instance NFData AbiHash where rnf = genericRnf--instance Pretty AbiHash where- pretty = text . unAbiHash--instance Parsec AbiHash where- parsec = fmap mkAbiHash (P.munch isAlphaNum)
src/Distribution/Types/AnnotatedId.hs view
@@ -1,9 +1,11 @@-module Distribution.Types.AnnotatedId (- AnnotatedId(..)-) where+{-# LANGUAGE DeriveFunctor #-} -import Prelude ()+module Distribution.Types.AnnotatedId+ ( AnnotatedId (..)+ ) where+ import Distribution.Compat.Prelude+import Prelude () import Distribution.Package import Distribution.Types.ComponentName@@ -14,21 +16,18 @@ -- -- Invariant: if ann_id x == ann_id y, then ann_pid x == ann_pid y -- and ann_cname x == ann_cname y-data AnnotatedId id = AnnotatedId {- ann_pid :: PackageId,- ann_cname :: ComponentName,- ann_id :: id- }- deriving (Show)+data AnnotatedId id = AnnotatedId+ { ann_pid :: PackageId+ , ann_cname :: ComponentName+ , ann_id :: id+ }+ deriving (Show, Functor) instance Eq id => Eq (AnnotatedId id) where- x == y = ann_id x == ann_id y+ x == y = ann_id x == ann_id y instance Ord id => Ord (AnnotatedId id) where- compare x y = compare (ann_id x) (ann_id y)+ compare x y = compare (ann_id x) (ann_id y) instance Package (AnnotatedId id) where- packageId = ann_pid--instance Functor AnnotatedId where- fmap f (AnnotatedId pid cn x) = AnnotatedId pid cn (f x)+ packageId = ann_pid
− src/Distribution/Types/Benchmark.hs
@@ -1,77 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Distribution.Types.Benchmark (- Benchmark(..),- emptyBenchmark,- benchmarkType,- benchmarkModules,- benchmarkModulesAutogen-) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Types.BuildInfo-import Distribution.Types.BenchmarkType-import Distribution.Types.BenchmarkInterface-import Distribution.Types.UnqualComponentName--import Distribution.ModuleName--import qualified Distribution.Types.BuildInfo.Lens as L---- | A \"benchmark\" stanza in a cabal file.----data Benchmark = Benchmark {- benchmarkName :: UnqualComponentName,- benchmarkInterface :: BenchmarkInterface,- benchmarkBuildInfo :: BuildInfo- }- deriving (Generic, Show, Read, Eq, Typeable, Data)--instance Binary Benchmark-instance Structured Benchmark-instance NFData Benchmark where rnf = genericRnf--instance L.HasBuildInfo Benchmark where- buildInfo f (Benchmark x1 x2 x3) = fmap (\y1 -> Benchmark x1 x2 y1) (f x3)--instance Monoid Benchmark where- mempty = Benchmark {- benchmarkName = mempty,- benchmarkInterface = mempty,- benchmarkBuildInfo = mempty- }- mappend = (<>)--instance Semigroup Benchmark where- a <> b = Benchmark {- benchmarkName = combine' benchmarkName,- benchmarkInterface = combine benchmarkInterface,- benchmarkBuildInfo = combine benchmarkBuildInfo- }- where combine field = field a `mappend` field b- combine' field = case ( unUnqualComponentName $ field a- , unUnqualComponentName $ field b) of- ("", _) -> field b- (_, "") -> field a- (x, y) -> error $ "Ambiguous values for test field: '"- ++ x ++ "' and '" ++ y ++ "'"--emptyBenchmark :: Benchmark-emptyBenchmark = mempty--benchmarkType :: Benchmark -> BenchmarkType-benchmarkType benchmark = case benchmarkInterface benchmark of- BenchmarkExeV10 ver _ -> BenchmarkTypeExe ver- BenchmarkUnsupported benchmarktype -> benchmarktype---- | Get all the module names from a benchmark.-benchmarkModules :: Benchmark -> [ModuleName]-benchmarkModules benchmark = otherModules (benchmarkBuildInfo benchmark)---- | Get all the auto generated module names from a benchmark.--- This are a subset of 'benchmarkModules'.-benchmarkModulesAutogen :: Benchmark -> [ModuleName]-benchmarkModulesAutogen benchmark = autogenModules (benchmarkBuildInfo benchmark)
− src/Distribution/Types/Benchmark/Lens.hs
@@ -1,27 +0,0 @@-module Distribution.Types.Benchmark.Lens (- Benchmark,- module Distribution.Types.Benchmark.Lens,- ) where--import Distribution.Compat.Lens-import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Types.Benchmark (Benchmark)-import Distribution.Types.BenchmarkInterface (BenchmarkInterface)-import Distribution.Types.BuildInfo (BuildInfo)-import Distribution.Types.UnqualComponentName (UnqualComponentName)--import qualified Distribution.Types.Benchmark as T--benchmarkName :: Lens' Benchmark UnqualComponentName-benchmarkName f s = fmap (\x -> s { T.benchmarkName = x }) (f (T.benchmarkName s))-{-# INLINE benchmarkName #-}--benchmarkInterface :: Lens' Benchmark BenchmarkInterface-benchmarkInterface f s = fmap (\x -> s { T.benchmarkInterface = x }) (f (T.benchmarkInterface s))-{-# INLINE benchmarkInterface #-}--benchmarkBuildInfo :: Lens' Benchmark BuildInfo-benchmarkBuildInfo f s = fmap (\x -> s { T.benchmarkBuildInfo = x }) (f (T.benchmarkBuildInfo s))-{-# INLINE benchmarkBuildInfo #-}
− src/Distribution/Types/BenchmarkInterface.hs
@@ -1,46 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Distribution.Types.BenchmarkInterface (- BenchmarkInterface(..),-) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Types.BenchmarkType-import Distribution.Version---- | The benchmark interfaces that are currently defined. Each--- benchmark must specify which interface it supports.------ More interfaces may be defined in future, either new revisions or--- totally new interfaces.----data BenchmarkInterface =-- -- | Benchmark interface \"exitcode-stdio-1.0\". The benchmark- -- takes the form of an executable. It returns a zero exit code- -- for success, non-zero for failure. The stdout and stderr- -- channels may be logged. It takes no command line parameters- -- and nothing on stdin.- --- BenchmarkExeV10 Version FilePath-- -- | A benchmark that does not conform to one of the above- -- interfaces for the given reason (e.g. unknown benchmark type).- --- | BenchmarkUnsupported BenchmarkType- deriving (Eq, Generic, Read, Show, Typeable, Data)--instance Binary BenchmarkInterface-instance Structured BenchmarkInterface-instance NFData BenchmarkInterface where rnf = genericRnf--instance Monoid BenchmarkInterface where- mempty = BenchmarkUnsupported (BenchmarkTypeUnknown mempty nullVersion)- mappend = (<>)--instance Semigroup BenchmarkInterface where- a <> (BenchmarkUnsupported _) = a- _ <> b = b
− src/Distribution/Types/BenchmarkType.hs
@@ -1,40 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}--module Distribution.Types.BenchmarkType (- BenchmarkType(..),- knownBenchmarkTypes,-) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Parsec-import Distribution.Pretty-import Distribution.Version-import Text.PrettyPrint (char, text)---- | The \"benchmark-type\" field in the benchmark stanza.----data BenchmarkType = BenchmarkTypeExe Version- -- ^ \"type: exitcode-stdio-x.y\"- | BenchmarkTypeUnknown String Version- -- ^ Some unknown benchmark type e.g. \"type: foo\"- deriving (Generic, Show, Read, Eq, Typeable, Data)--instance Binary BenchmarkType-instance Structured BenchmarkType-instance NFData BenchmarkType where rnf = genericRnf--knownBenchmarkTypes :: [BenchmarkType]-knownBenchmarkTypes = [ BenchmarkTypeExe (mkVersion [1,0]) ]--instance Pretty BenchmarkType where- pretty (BenchmarkTypeExe ver) = text "exitcode-stdio-" <<>> pretty ver- pretty (BenchmarkTypeUnknown name ver) = text name <<>> char '-' <<>> pretty ver--instance Parsec BenchmarkType where- parsec = parsecStandard $ \ver name -> case name of- "exitcode-stdio" -> BenchmarkTypeExe ver- _ -> BenchmarkTypeUnknown name ver
− src/Distribution/Types/BuildInfo.hs
@@ -1,266 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Distribution.Types.BuildInfo (- BuildInfo(..),-- emptyBuildInfo,- allLanguages,- allExtensions,- usedExtensions,- usesTemplateHaskellOrQQ,-- hcOptions,- hcProfOptions,- hcSharedOptions,- hcStaticOptions,-) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Types.Mixin-import Distribution.Types.Dependency-import Distribution.Types.ExeDependency-import Distribution.Types.LegacyExeDependency-import Distribution.Types.PkgconfigDependency-import Distribution.Utils.Path--import Distribution.ModuleName-import Distribution.Compiler-import Language.Haskell.Extension---- Consider refactoring into executable and library versions.-data BuildInfo = BuildInfo {- -- | component is buildable here- buildable :: Bool,- -- | Tools needed to build this bit.- --- -- This is a legacy field that 'buildToolDepends' largely supersedes.- --- -- Unless use are very sure what you are doing, use the functions in- -- "Distribution.Simple.BuildToolDepends" rather than accessing this- -- field directly.- buildTools :: [LegacyExeDependency],- -- | Haskell tools needed to build this bit- --- -- This field is better than 'buildTools' because it allows one to- -- precisely specify an executable in a package.- --- -- Unless use are very sure what you are doing, use the functions in- -- "Distribution.Simple.BuildToolDepends" rather than accessing this- -- field directly.- buildToolDepends :: [ExeDependency],- cppOptions :: [String], -- ^ options for pre-processing Haskell code- asmOptions :: [String], -- ^ options for assmebler- cmmOptions :: [String], -- ^ options for C-- compiler- ccOptions :: [String], -- ^ options for C compiler- cxxOptions :: [String], -- ^ options for C++ compiler- ldOptions :: [String], -- ^ options for linker- hsc2hsOptions :: [String], -- ^ options for hsc2hs- pkgconfigDepends :: [PkgconfigDependency], -- ^ pkg-config packages that are used- frameworks :: [String], -- ^support frameworks for Mac OS X- extraFrameworkDirs:: [String], -- ^ extra locations to find frameworks.- asmSources :: [FilePath], -- ^ Assembly files.- cmmSources :: [FilePath], -- ^ C-- files.- cSources :: [FilePath],- cxxSources :: [FilePath],- jsSources :: [FilePath],- hsSourceDirs :: [SymbolicPath PackageDir SourceDir], -- ^ where to look for the Haskell module hierarchy- otherModules :: [ModuleName], -- ^ non-exposed or non-main modules- virtualModules :: [ModuleName], -- ^ exposed modules that do not have a source file (e.g. @GHC.Prim@ from @ghc-prim@ package)- autogenModules :: [ModuleName], -- ^ not present on sdist, Paths_* or user-generated with a custom Setup.hs-- defaultLanguage :: Maybe Language,-- ^ language used when not explicitly specified- otherLanguages :: [Language], -- ^ other languages used within the package- defaultExtensions :: [Extension], -- ^ language extensions used by all modules- otherExtensions :: [Extension], -- ^ other language extensions used within the package- oldExtensions :: [Extension], -- ^ the old extensions field, treated same as 'defaultExtensions'-- extraLibs :: [String], -- ^ what libraries to link with when compiling a program that uses your package- extraGHCiLibs :: [String], -- ^ if present, overrides extraLibs when package is loaded with GHCi.- extraBundledLibs :: [String], -- ^ if present, adds libs to hs-libraries, which become part of the package.- -- Example: the Cffi library shipping with the rts, alognside the HSrts-1.0.a,.o,...- -- Example 2: a library that is being built by a foreing tool (e.g. rust)- -- and copied and registered together with this library. The- -- logic on how this library is built will have to be encoded in a- -- custom Setup for now. Otherwise cabal would need to lear how to- -- call arbitrary library builders.- extraLibFlavours :: [String], -- ^ Hidden Flag. This set of strings, will be appended to all libraries when- -- copying. E.g. [libHS<name>_<flavour> | flavour <- extraLibFlavours]. This- -- should only be needed in very specific cases, e.g. the `rts` package, where- -- there are multiple copies of slightly differently built libs.- extraDynLibFlavours :: [String], -- ^ Hidden Flag. This set of strings will be appended to all /dynamic/- -- libraries when copying. This is particularly useful with the `rts` package,- -- where we want different dynamic flavours of the RTS library to be installed.- extraLibDirs :: [String],- includeDirs :: [FilePath], -- ^directories to find .h files- includes :: [FilePath], -- ^ The .h files to be found in includeDirs- autogenIncludes :: [FilePath], -- ^ The .h files to be generated (e.g. by @autoconf@)- installIncludes :: [FilePath], -- ^ .h files to install with the package- options :: PerCompilerFlavor [String],- profOptions :: PerCompilerFlavor [String],- sharedOptions :: PerCompilerFlavor [String],- staticOptions :: PerCompilerFlavor [String],- customFieldsBI :: [(String,String)], -- ^Custom fields starting- -- with x-, stored in a- -- simple assoc-list.- targetBuildDepends :: [Dependency], -- ^ Dependencies specific to a library or executable target- mixins :: [Mixin]- }- deriving (Generic, Show, Read, Eq, Typeable, Data)--instance Binary BuildInfo-instance Structured BuildInfo-instance NFData BuildInfo where rnf = genericRnf--instance Monoid BuildInfo where- mempty = BuildInfo {- buildable = True,- buildTools = [],- buildToolDepends = [],- cppOptions = [],- asmOptions = [],- cmmOptions = [],- ccOptions = [],- cxxOptions = [],- ldOptions = [],- hsc2hsOptions = [],- pkgconfigDepends = [],- frameworks = [],- extraFrameworkDirs = [],- asmSources = [],- cmmSources = [],- cSources = [],- cxxSources = [],- jsSources = [],- hsSourceDirs = [],- otherModules = [],- virtualModules = [],- autogenModules = [],- defaultLanguage = Nothing,- otherLanguages = [],- defaultExtensions = [],- otherExtensions = [],- oldExtensions = [],- extraLibs = [],- extraGHCiLibs = [],- extraBundledLibs = [],- extraLibFlavours = [],- extraDynLibFlavours = [],- extraLibDirs = [],- includeDirs = [],- includes = [],- autogenIncludes = [],- installIncludes = [],- options = mempty,- profOptions = mempty,- sharedOptions = mempty,- staticOptions = mempty,- customFieldsBI = [],- targetBuildDepends = [],- mixins = []- }- mappend = (<>)--instance Semigroup BuildInfo where- a <> b = BuildInfo {- buildable = buildable a && buildable b,- buildTools = combine buildTools,- buildToolDepends = combine buildToolDepends,- cppOptions = combine cppOptions,- asmOptions = combine asmOptions,- cmmOptions = combine cmmOptions,- ccOptions = combine ccOptions,- cxxOptions = combine cxxOptions,- ldOptions = combine ldOptions,- hsc2hsOptions = combine hsc2hsOptions,- pkgconfigDepends = combine pkgconfigDepends,- frameworks = combineNub frameworks,- extraFrameworkDirs = combineNub extraFrameworkDirs,- asmSources = combineNub asmSources,- cmmSources = combineNub cmmSources,- cSources = combineNub cSources,- cxxSources = combineNub cxxSources,- jsSources = combineNub jsSources,- hsSourceDirs = combineNub hsSourceDirs,- otherModules = combineNub otherModules,- virtualModules = combineNub virtualModules,- autogenModules = combineNub autogenModules,- defaultLanguage = combineMby defaultLanguage,- otherLanguages = combineNub otherLanguages,- defaultExtensions = combineNub defaultExtensions,- otherExtensions = combineNub otherExtensions,- oldExtensions = combineNub oldExtensions,- extraLibs = combine extraLibs,- extraGHCiLibs = combine extraGHCiLibs,- extraBundledLibs = combine extraBundledLibs,- extraLibFlavours = combine extraLibFlavours,- extraDynLibFlavours = combine extraDynLibFlavours,- extraLibDirs = combineNub extraLibDirs,- includeDirs = combineNub includeDirs,- includes = combineNub includes,- autogenIncludes = combineNub autogenIncludes,- installIncludes = combineNub installIncludes,- options = combine options,- profOptions = combine profOptions,- sharedOptions = combine sharedOptions,- staticOptions = combine staticOptions,- customFieldsBI = combine customFieldsBI,- targetBuildDepends = combineNub targetBuildDepends,- mixins = combine mixins- }- where- combine field = field a `mappend` field b- combineNub field = nub (combine field)- combineMby field = field b `mplus` field a--emptyBuildInfo :: BuildInfo-emptyBuildInfo = mempty---- | The 'Language's used by this component----allLanguages :: BuildInfo -> [Language]-allLanguages bi = maybeToList (defaultLanguage bi)- ++ otherLanguages bi---- | The 'Extension's that are used somewhere by this component----allExtensions :: BuildInfo -> [Extension]-allExtensions bi = usedExtensions bi- ++ otherExtensions bi---- | The 'Extensions' that are used by all modules in this component----usedExtensions :: BuildInfo -> [Extension]-usedExtensions bi = oldExtensions bi- ++ defaultExtensions bi---- | Whether any modules in this component use Template Haskell or--- Quasi Quotes-usesTemplateHaskellOrQQ :: BuildInfo -> Bool-usesTemplateHaskellOrQQ bi = any p (allExtensions bi)- where- p ex = ex `elem`- [EnableExtension TemplateHaskell, EnableExtension QuasiQuotes]---- |Select options for a particular Haskell compiler.-hcOptions :: CompilerFlavor -> BuildInfo -> [String]-hcOptions = lookupHcOptions options--hcProfOptions :: CompilerFlavor -> BuildInfo -> [String]-hcProfOptions = lookupHcOptions profOptions--hcSharedOptions :: CompilerFlavor -> BuildInfo -> [String]-hcSharedOptions = lookupHcOptions sharedOptions--hcStaticOptions :: CompilerFlavor -> BuildInfo -> [String]-hcStaticOptions = lookupHcOptions staticOptions--lookupHcOptions :: (BuildInfo -> PerCompilerFlavor [String])- -> CompilerFlavor -> BuildInfo -> [String]-lookupHcOptions f hc bi = case f bi of- PerCompilerFlavor ghc ghcjs- | hc == GHC -> ghc- | hc == GHCJS -> ghcjs- | otherwise -> mempty
− src/Distribution/Types/BuildInfo/Lens.hs
@@ -1,342 +0,0 @@-module Distribution.Types.BuildInfo.Lens (- BuildInfo,- HasBuildInfo (..),- HasBuildInfos (..),- ) where--import Distribution.Compat.Lens-import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Compiler (PerCompilerFlavor)-import Distribution.ModuleName (ModuleName)-import Distribution.Types.BuildInfo (BuildInfo)-import Distribution.Types.Dependency (Dependency)-import Distribution.Types.ExeDependency (ExeDependency)-import Distribution.Types.LegacyExeDependency (LegacyExeDependency)-import Distribution.Types.Mixin (Mixin)-import Distribution.Types.PkgconfigDependency (PkgconfigDependency)-import Distribution.Utils.Path-import Language.Haskell.Extension (Extension, Language)--import qualified Distribution.Types.BuildInfo as T---- | Classy lenses for 'BuildInfo'.-class HasBuildInfo a where- buildInfo :: Lens' a BuildInfo-- buildable :: Lens' a Bool- buildable = buildInfo . buildable- {-# INLINE buildable #-}-- buildTools :: Lens' a [LegacyExeDependency]- buildTools = buildInfo . buildTools- {-# INLINE buildTools #-}-- buildToolDepends :: Lens' a [ExeDependency]- buildToolDepends = buildInfo . buildToolDepends- {-# INLINE buildToolDepends #-}-- cppOptions :: Lens' a [String]- cppOptions = buildInfo . cppOptions- {-# INLINE cppOptions #-}-- asmOptions :: Lens' a [String]- asmOptions = buildInfo . asmOptions- {-# INLINE asmOptions #-}-- cmmOptions :: Lens' a [String]- cmmOptions = buildInfo . cmmOptions- {-# INLINE cmmOptions #-}-- ccOptions :: Lens' a [String]- ccOptions = buildInfo . ccOptions- {-# INLINE ccOptions #-}-- cxxOptions :: Lens' a [String]- cxxOptions = buildInfo . cxxOptions- {-# INLINE cxxOptions #-}-- ldOptions :: Lens' a [String]- ldOptions = buildInfo . ldOptions- {-# INLINE ldOptions #-}-- hsc2hsOptions :: Lens' a [String]- hsc2hsOptions = buildInfo . hsc2hsOptions- {-# INLINE hsc2hsOptions #-}-- pkgconfigDepends :: Lens' a [PkgconfigDependency]- pkgconfigDepends = buildInfo . pkgconfigDepends- {-# INLINE pkgconfigDepends #-}-- frameworks :: Lens' a [String]- frameworks = buildInfo . frameworks- {-# INLINE frameworks #-}-- extraFrameworkDirs :: Lens' a [String]- extraFrameworkDirs = buildInfo . extraFrameworkDirs- {-# INLINE extraFrameworkDirs #-}-- asmSources :: Lens' a [FilePath]- asmSources = buildInfo . asmSources- {-# INLINE asmSources #-}-- cmmSources :: Lens' a [FilePath]- cmmSources = buildInfo . cmmSources- {-# INLINE cmmSources #-}-- cSources :: Lens' a [FilePath]- cSources = buildInfo . cSources- {-# INLINE cSources #-}-- cxxSources :: Lens' a [FilePath]- cxxSources = buildInfo . cxxSources- {-# INLINE cxxSources #-}-- jsSources :: Lens' a [FilePath]- jsSources = buildInfo . jsSources- {-# INLINE jsSources #-}-- hsSourceDirs :: Lens' a [SymbolicPath PackageDir SourceDir]- hsSourceDirs = buildInfo . hsSourceDirs- {-# INLINE hsSourceDirs #-}-- otherModules :: Lens' a [ModuleName]- otherModules = buildInfo . otherModules- {-# INLINE otherModules #-}-- virtualModules :: Lens' a [ModuleName]- virtualModules = buildInfo . virtualModules- {-# INLINE virtualModules #-}-- autogenModules :: Lens' a [ModuleName]- autogenModules = buildInfo . autogenModules- {-# INLINE autogenModules #-}-- defaultLanguage :: Lens' a (Maybe Language)- defaultLanguage = buildInfo . defaultLanguage- {-# INLINE defaultLanguage #-}-- otherLanguages :: Lens' a [Language]- otherLanguages = buildInfo . otherLanguages- {-# INLINE otherLanguages #-}-- defaultExtensions :: Lens' a [Extension]- defaultExtensions = buildInfo . defaultExtensions- {-# INLINE defaultExtensions #-}-- otherExtensions :: Lens' a [Extension]- otherExtensions = buildInfo . otherExtensions- {-# INLINE otherExtensions #-}-- oldExtensions :: Lens' a [Extension]- oldExtensions = buildInfo . oldExtensions- {-# INLINE oldExtensions #-}-- extraLibs :: Lens' a [String]- extraLibs = buildInfo . extraLibs- {-# INLINE extraLibs #-}-- extraGHCiLibs :: Lens' a [String]- extraGHCiLibs = buildInfo . extraGHCiLibs- {-# INLINE extraGHCiLibs #-}-- extraBundledLibs :: Lens' a [String]- extraBundledLibs = buildInfo . extraBundledLibs- {-# INLINE extraBundledLibs #-}-- extraLibFlavours :: Lens' a [String]- extraLibFlavours = buildInfo . extraLibFlavours- {-# INLINE extraLibFlavours #-}-- extraDynLibFlavours :: Lens' a [String]- extraDynLibFlavours = buildInfo . extraDynLibFlavours- {-# INLINE extraDynLibFlavours #-}-- extraLibDirs :: Lens' a [String]- extraLibDirs = buildInfo . extraLibDirs- {-# INLINE extraLibDirs #-}-- includeDirs :: Lens' a [FilePath]- includeDirs = buildInfo . includeDirs- {-# INLINE includeDirs #-}-- includes :: Lens' a [FilePath]- includes = buildInfo . includes- {-# INLINE includes #-}-- autogenIncludes :: Lens' a [FilePath]- autogenIncludes = buildInfo . autogenIncludes- {-# INLINE autogenIncludes #-}-- installIncludes :: Lens' a [FilePath]- installIncludes = buildInfo . installIncludes- {-# INLINE installIncludes #-}-- options :: Lens' a (PerCompilerFlavor [String])- options = buildInfo . options- {-# INLINE options #-}-- profOptions :: Lens' a (PerCompilerFlavor [String])- profOptions = buildInfo . profOptions- {-# INLINE profOptions #-}-- sharedOptions :: Lens' a (PerCompilerFlavor [String])- sharedOptions = buildInfo . sharedOptions- {-# INLINE sharedOptions #-}-- staticOptions :: Lens' a (PerCompilerFlavor [String])- staticOptions = buildInfo . staticOptions- {-# INLINE staticOptions #-}-- customFieldsBI :: Lens' a [(String,String)]- customFieldsBI = buildInfo . customFieldsBI- {-# INLINE customFieldsBI #-}-- targetBuildDepends :: Lens' a [Dependency]- targetBuildDepends = buildInfo . targetBuildDepends- {-# INLINE targetBuildDepends #-}-- mixins :: Lens' a [Mixin]- mixins = buildInfo . mixins- {-# INLINE mixins #-}---instance HasBuildInfo BuildInfo where- buildInfo = id- {-# INLINE buildInfo #-}-- buildable f s = fmap (\x -> s { T.buildable = x }) (f (T.buildable s))- {-# INLINE buildable #-}-- buildTools f s = fmap (\x -> s { T.buildTools = x }) (f (T.buildTools s))- {-# INLINE buildTools #-}-- buildToolDepends f s = fmap (\x -> s { T.buildToolDepends = x }) (f (T.buildToolDepends s))- {-# INLINE buildToolDepends #-}-- cppOptions f s = fmap (\x -> s { T.cppOptions = x }) (f (T.cppOptions s))- {-# INLINE cppOptions #-}-- asmOptions f s = fmap (\x -> s { T.asmOptions = x }) (f (T.asmOptions s))- {-# INLINE asmOptions #-}-- cmmOptions f s = fmap (\x -> s { T.cmmOptions = x }) (f (T.cmmOptions s))- {-# INLINE cmmOptions #-}-- ccOptions f s = fmap (\x -> s { T.ccOptions = x }) (f (T.ccOptions s))- {-# INLINE ccOptions #-}-- cxxOptions f s = fmap (\x -> s { T.cxxOptions = x }) (f (T.cxxOptions s))- {-# INLINE cxxOptions #-}-- ldOptions f s = fmap (\x -> s { T.ldOptions = x }) (f (T.ldOptions s))- {-# INLINE ldOptions #-}-- hsc2hsOptions f s = fmap (\x -> s { T.hsc2hsOptions = x }) (f (T.hsc2hsOptions s))- {-# INLINE hsc2hsOptions #-}-- pkgconfigDepends f s = fmap (\x -> s { T.pkgconfigDepends = x }) (f (T.pkgconfigDepends s))- {-# INLINE pkgconfigDepends #-}-- frameworks f s = fmap (\x -> s { T.frameworks = x }) (f (T.frameworks s))- {-# INLINE frameworks #-}-- extraFrameworkDirs f s = fmap (\x -> s { T.extraFrameworkDirs = x }) (f (T.extraFrameworkDirs s))- {-# INLINE extraFrameworkDirs #-}-- asmSources f s = fmap (\x -> s { T.asmSources = x }) (f (T.asmSources s))- {-# INLINE asmSources #-}-- cmmSources f s = fmap (\x -> s { T.cmmSources = x }) (f (T.cmmSources s))- {-# INLINE cmmSources #-}-- cSources f s = fmap (\x -> s { T.cSources = x }) (f (T.cSources s))- {-# INLINE cSources #-}-- cxxSources f s = fmap (\x -> s { T.cSources = x }) (f (T.cxxSources s))- {-# INLINE cxxSources #-}-- jsSources f s = fmap (\x -> s { T.jsSources = x }) (f (T.jsSources s))- {-# INLINE jsSources #-}-- hsSourceDirs f s = fmap (\x -> s { T.hsSourceDirs = x }) (f (T.hsSourceDirs s))- {-# INLINE hsSourceDirs #-}-- otherModules f s = fmap (\x -> s { T.otherModules = x }) (f (T.otherModules s))- {-# INLINE otherModules #-}-- virtualModules f s = fmap (\x -> s { T.virtualModules = x }) (f (T.virtualModules s))- {-# INLINE virtualModules #-}-- autogenModules f s = fmap (\x -> s { T.autogenModules = x }) (f (T.autogenModules s))- {-# INLINE autogenModules #-}-- defaultLanguage f s = fmap (\x -> s { T.defaultLanguage = x }) (f (T.defaultLanguage s))- {-# INLINE defaultLanguage #-}-- otherLanguages f s = fmap (\x -> s { T.otherLanguages = x }) (f (T.otherLanguages s))- {-# INLINE otherLanguages #-}-- defaultExtensions f s = fmap (\x -> s { T.defaultExtensions = x }) (f (T.defaultExtensions s))- {-# INLINE defaultExtensions #-}-- otherExtensions f s = fmap (\x -> s { T.otherExtensions = x }) (f (T.otherExtensions s))- {-# INLINE otherExtensions #-}-- oldExtensions f s = fmap (\x -> s { T.oldExtensions = x }) (f (T.oldExtensions s))- {-# INLINE oldExtensions #-}-- extraLibs f s = fmap (\x -> s { T.extraLibs = x }) (f (T.extraLibs s))- {-# INLINE extraLibs #-}-- extraGHCiLibs f s = fmap (\x -> s { T.extraGHCiLibs = x }) (f (T.extraGHCiLibs s))- {-# INLINE extraGHCiLibs #-}-- extraBundledLibs f s = fmap (\x -> s { T.extraBundledLibs = x }) (f (T.extraBundledLibs s))- {-# INLINE extraBundledLibs #-}-- extraLibFlavours f s = fmap (\x -> s { T.extraLibFlavours = x }) (f (T.extraLibFlavours s))- {-# INLINE extraLibFlavours #-}-- extraDynLibFlavours f s = fmap (\x -> s { T.extraDynLibFlavours = x}) (f (T.extraDynLibFlavours s))- {-# INLINE extraDynLibFlavours #-}-- extraLibDirs f s = fmap (\x -> s { T.extraLibDirs = x }) (f (T.extraLibDirs s))- {-# INLINE extraLibDirs #-}-- includeDirs f s = fmap (\x -> s { T.includeDirs = x }) (f (T.includeDirs s))- {-# INLINE includeDirs #-}-- includes f s = fmap (\x -> s { T.includes = x }) (f (T.includes s))- {-# INLINE includes #-}-- autogenIncludes f s = fmap (\x -> s { T.autogenIncludes = x }) (f (T.autogenIncludes s))- {-# INLINE autogenIncludes #-}-- installIncludes f s = fmap (\x -> s { T.installIncludes = x }) (f (T.installIncludes s))- {-# INLINE installIncludes #-}-- options f s = fmap (\x -> s { T.options = x }) (f (T.options s))- {-# INLINE options #-}-- profOptions f s = fmap (\x -> s { T.profOptions = x }) (f (T.profOptions s))- {-# INLINE profOptions #-}-- sharedOptions f s = fmap (\x -> s { T.sharedOptions = x }) (f (T.sharedOptions s))- {-# INLINE sharedOptions #-}-- staticOptions f s = fmap (\x -> s { T.staticOptions = x }) (f (T.staticOptions s))- {-# INLINE staticOptions #-}-- customFieldsBI f s = fmap (\x -> s { T.customFieldsBI = x }) (f (T.customFieldsBI s))- {-# INLINE customFieldsBI #-}-- targetBuildDepends f s = fmap (\x -> s { T.targetBuildDepends = x }) (f (T.targetBuildDepends s))- {-# INLINE targetBuildDepends #-}-- mixins f s = fmap (\x -> s { T.mixins = x }) (f (T.mixins s))- {-# INLINE mixins #-}--class HasBuildInfos a where- traverseBuildInfos :: Traversal' a BuildInfo
− src/Distribution/Types/BuildType.hs
@@ -1,55 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}--module Distribution.Types.BuildType (- BuildType(..),- knownBuildTypes,-) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.CabalSpecVersion (CabalSpecVersion (..))-import Distribution.Pretty-import Distribution.Parsec--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp---- | The type of build system used by this package.-data BuildType- = Simple -- ^ calls @Distribution.Simple.defaultMain@- | Configure -- ^ calls @Distribution.Simple.defaultMainWithHooks defaultUserHooks@,- -- which invokes @configure@ to generate additional build- -- information used by later phases.- | Make -- ^ calls @Distribution.Make.defaultMain@- | Custom -- ^ uses user-supplied @Setup.hs@ or @Setup.lhs@ (default)- deriving (Generic, Show, Read, Eq, Typeable, Data)--instance Binary BuildType-instance Structured BuildType-instance NFData BuildType where rnf = genericRnf--knownBuildTypes :: [BuildType]-knownBuildTypes = [Simple, Configure, Make, Custom]--instance Pretty BuildType where- pretty = Disp.text . show--instance Parsec BuildType where- parsec = do- name <- P.munch1 isAlphaNum- case name of- "Simple" -> return Simple- "Configure" -> return Configure- "Custom" -> return Custom- "Make" -> return Make- "Default" -> do- v <- askCabalSpecVersion- if v <= CabalSpecV1_18 -- oldest version needing this, based on hackage-tests- then do- parsecWarning PWTBuildTypeDefault "build-type: Default is parsed as Custom for legacy reasons. See https://github.com/haskell/cabal/issues/5020"- return Custom- else fail ("unknown build-type: '" ++ name ++ "'")- _ -> fail ("unknown build-type: '" ++ name ++ "'")
− src/Distribution/Types/Component.hs
@@ -1,91 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}--module Distribution.Types.Component (- Component(..),- foldComponent,- componentBuildInfo,- componentBuildable,- componentName,- partitionComponents,-) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Types.Library-import Distribution.Types.ForeignLib-import Distribution.Types.Executable-import Distribution.Types.TestSuite-import Distribution.Types.Benchmark--import Distribution.Types.ComponentName-import Distribution.Types.BuildInfo--import qualified Distribution.Types.BuildInfo.Lens as L--data Component = CLib Library- | CFLib ForeignLib- | CExe Executable- | CTest TestSuite- | CBench Benchmark- deriving (Show, Eq, Read)--instance Semigroup Component where- CLib l <> CLib l' = CLib (l <> l')- CFLib l <> CFLib l' = CFLib (l <> l')- CExe e <> CExe e' = CExe (e <> e')- CTest t <> CTest t' = CTest (t <> t')- CBench b <> CBench b' = CBench (b <> b')- _ <> _ = error "Cannot merge Component"--instance L.HasBuildInfo Component where- buildInfo f (CLib l) = CLib <$> L.buildInfo f l- buildInfo f (CFLib l) = CFLib <$> L.buildInfo f l- buildInfo f (CExe e) = CExe <$> L.buildInfo f e- buildInfo f (CTest t) = CTest <$> L.buildInfo f t- buildInfo f (CBench b) = CBench <$> L.buildInfo f b--foldComponent :: (Library -> a)- -> (ForeignLib -> a)- -> (Executable -> a)- -> (TestSuite -> a)- -> (Benchmark -> a)- -> Component- -> a-foldComponent f _ _ _ _ (CLib lib) = f lib-foldComponent _ f _ _ _ (CFLib flib)= f flib-foldComponent _ _ f _ _ (CExe exe) = f exe-foldComponent _ _ _ f _ (CTest tst) = f tst-foldComponent _ _ _ _ f (CBench bch) = f bch--componentBuildInfo :: Component -> BuildInfo-componentBuildInfo =- foldComponent libBuildInfo foreignLibBuildInfo buildInfo testBuildInfo benchmarkBuildInfo---- | Is a component buildable (i.e., not marked with @buildable: False@)?--- See also this note in--- "Distribution.Types.ComponentRequestedSpec#buildable_vs_enabled_components".------ @since 2.0.0.2----componentBuildable :: Component -> Bool-componentBuildable = buildable . componentBuildInfo--componentName :: Component -> ComponentName-componentName =- foldComponent (CLibName . libName)- (CFLibName . foreignLibName)- (CExeName . exeName)- (CTestName . testName)- (CBenchName . benchmarkName)--partitionComponents- :: [Component]- -> ([Library], [ForeignLib], [Executable], [TestSuite], [Benchmark])-partitionComponents = foldr (foldComponent fa fb fc fd fe) ([],[],[],[],[])- where- fa x ~(a,b,c,d,e) = (x:a,b,c,d,e)- fb x ~(a,b,c,d,e) = (a,x:b,c,d,e)- fc x ~(a,b,c,d,e) = (a,b,x:c,d,e)- fd x ~(a,b,c,d,e) = (a,b,c,x:d,e)- fe x ~(a,b,c,d,e) = (a,b,c,d,x:e)
− src/Distribution/Types/ComponentId.hs
@@ -1,69 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}--module Distribution.Types.ComponentId- ( ComponentId, unComponentId, mkComponentId- ) where--import Prelude ()-import Distribution.Compat.Prelude-import Distribution.Utils.ShortText--import Distribution.Pretty-import Distribution.Parsec--import qualified Distribution.Compat.CharParsing as P-import Text.PrettyPrint (text)---- | A 'ComponentId' uniquely identifies the transitive source--- code closure of a component (i.e. libraries, executables).------ For non-Backpack components, this corresponds one to one with--- the 'UnitId', which serves as the basis for install paths,--- linker symbols, etc.------ Use 'mkComponentId' and 'unComponentId' to convert from/to a--- 'String'.------ This type is opaque since @Cabal-2.0@------ @since 2.0.0.2-newtype ComponentId = ComponentId ShortText- deriving (Generic, Read, Show, Eq, Ord, Typeable, Data)---- | Construct a 'ComponentId' from a 'String'------ 'mkComponentId' is the inverse to 'unComponentId'------ Note: No validations are performed to ensure that the resulting--- 'ComponentId' is valid------ @since 2.0.0.2-mkComponentId :: String -> ComponentId-mkComponentId = ComponentId . toShortText---- | Convert 'ComponentId' to 'String'------ @since 2.0.0.2-unComponentId :: ComponentId -> String-unComponentId (ComponentId s) = fromShortText s---- | 'mkComponentId'------ @since 2.0.0.2-instance IsString ComponentId where- fromString = mkComponentId--instance Binary ComponentId-instance Structured ComponentId--instance Pretty ComponentId where- pretty = text . unComponentId--instance Parsec ComponentId where- parsec = mkComponentId `fmap` P.munch1 abi_char- where abi_char c = isAlphaNum c || c `elem` "-_."--instance NFData ComponentId where- rnf = rnf . unComponentId
src/Distribution/Types/ComponentInclude.hs view
@@ -1,25 +1,25 @@-module Distribution.Types.ComponentInclude (- ComponentInclude(..),- ci_id,- ci_pkgid,- ci_cname-) where+module Distribution.Types.ComponentInclude+ ( ComponentInclude (..)+ , ci_id+ , ci_pkgid+ , ci_cname+ ) where -import Distribution.Types.PackageId-import Distribution.Types.ComponentName import Distribution.Types.AnnotatedId+import Distribution.Types.ComponentName+import Distribution.Types.PackageId -- Once ci_id is refined to an 'OpenUnitId' or 'DefUnitId', -- the 'includeRequiresRn' is not so useful (because it -- includes the requirements renaming that is no longer -- needed); use 'ci_prov_renaming' instead.-data ComponentInclude id rn = ComponentInclude {- ci_ann_id :: AnnotatedId id,- ci_renaming :: rn,- -- | Did this come from an entry in @mixins@, or- -- was implicitly generated by @build-depends@?- ci_implicit :: Bool- }+data ComponentInclude id rn = ComponentInclude+ { ci_ann_id :: AnnotatedId id+ , ci_renaming :: rn+ , ci_implicit :: Bool+ -- ^ Did this come from an entry in @mixins@, or+ -- was implicitly generated by @build-depends@?+ } ci_id :: ComponentInclude id rn -> id ci_id = ann_id . ci_ann_id
src/Distribution/Types/ComponentLocalBuildInfo.hs view
@@ -1,129 +1,139 @@-{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TypeFamilies #-} -module Distribution.Types.ComponentLocalBuildInfo (- ComponentLocalBuildInfo(..),- componentIsIndefinite,- maybeComponentInstantiatedWith,+module Distribution.Types.ComponentLocalBuildInfo+ ( ComponentLocalBuildInfo (..)+ , componentIsIndefinite+ , maybeComponentInstantiatedWith+ , maybeComponentCompatPackageKey+ , maybeComponentExposedModules ) where -import Prelude () import Distribution.Compat.Prelude import Distribution.ModuleName+import Prelude () import Distribution.Backpack import Distribution.Compat.Graph import Distribution.Types.ComponentId-import Distribution.Types.MungedPackageId-import Distribution.Types.UnitId import Distribution.Types.ComponentName-import Distribution.Types.MungedPackageName import Distribution.Types.ModuleRenaming+import Distribution.Types.MungedPackageId+import Distribution.Types.MungedPackageName+import Distribution.Types.UnitId import qualified Distribution.InstalledPackageInfo as Installed -- | The first five fields are common across all algebraic variants. data ComponentLocalBuildInfo- = LibComponentLocalBuildInfo {- -- | It would be very convenient to store the literal Library here,- -- but if we do that, it will get serialized (via the Binary)- -- instance twice. So instead we just provide the ComponentName,- -- which can be used to find the Component in the- -- PackageDescription. NB: eventually, this will NOT uniquely- -- identify the ComponentLocalBuildInfo.- componentLocalName :: ComponentName,- -- | The computed 'ComponentId' of this component.- componentComponentId :: ComponentId,- -- | The computed 'UnitId' which uniquely identifies this- -- component. Might be hashed.- componentUnitId :: UnitId,- -- | Is this an indefinite component (i.e. has unfilled holes)?- componentIsIndefinite_ :: Bool,- -- | How the component was instantiated- componentInstantiatedWith :: [(ModuleName, OpenModule)],- -- | Resolved internal and external package dependencies for this component.- -- The 'BuildInfo' specifies a set of build dependencies that must be- -- satisfied in terms of version ranges. This field fixes those dependencies- -- to the specific versions available on this machine for this compiler.- componentPackageDeps :: [(UnitId, MungedPackageId)],- -- | The set of packages that are brought into scope during- -- compilation, including a 'ModuleRenaming' which may used- -- to hide or rename modules. This is what gets translated into- -- @-package-id@ arguments. This is a modernized version of- -- 'componentPackageDeps', which is kept around for BC purposes.- componentIncludes :: [(OpenUnitId, ModuleRenaming)],- componentExeDeps :: [UnitId],- -- | The internal dependencies which induce a graph on the- -- 'ComponentLocalBuildInfo' of this package. This does NOT- -- coincide with 'componentPackageDeps' because it ALSO records- -- 'build-tool' dependencies on executables. Maybe one day- -- @cabal-install@ will also handle these correctly too!- componentInternalDeps :: [UnitId],- -- | Compatibility "package key" that we pass to older versions of GHC.- componentCompatPackageKey :: String,- -- | Compatibility "package name" that we register this component as.- componentCompatPackageName :: MungedPackageName,- -- | A list of exposed modules (either defined in this component,- -- or reexported from another component.)- componentExposedModules :: [Installed.ExposedModule],- -- | Convenience field, specifying whether or not this is the- -- "public library" that has the same name as the package.- componentIsPublic :: Bool- }- -- TODO: refactor all these duplicates- | FLibComponentLocalBuildInfo {- componentLocalName :: ComponentName,- componentComponentId :: ComponentId,- componentUnitId :: UnitId,- componentPackageDeps :: [(UnitId, MungedPackageId)],- componentIncludes :: [(OpenUnitId, ModuleRenaming)],- componentExeDeps :: [UnitId],- componentInternalDeps :: [UnitId]- }- | ExeComponentLocalBuildInfo {- componentLocalName :: ComponentName,- componentComponentId :: ComponentId,- componentUnitId :: UnitId,- componentPackageDeps :: [(UnitId, MungedPackageId)],- componentIncludes :: [(OpenUnitId, ModuleRenaming)],- componentExeDeps :: [UnitId],- componentInternalDeps :: [UnitId]- }- | TestComponentLocalBuildInfo {- componentLocalName :: ComponentName,- componentComponentId :: ComponentId,- componentUnitId :: UnitId,- componentPackageDeps :: [(UnitId, MungedPackageId)],- componentIncludes :: [(OpenUnitId, ModuleRenaming)],- componentExeDeps :: [UnitId],- componentInternalDeps :: [UnitId]-- }- | BenchComponentLocalBuildInfo {- componentLocalName :: ComponentName,- componentComponentId :: ComponentId,- componentUnitId :: UnitId,- componentPackageDeps :: [(UnitId, MungedPackageId)],- componentIncludes :: [(OpenUnitId, ModuleRenaming)],- componentExeDeps :: [UnitId],- componentInternalDeps :: [UnitId]- }- deriving (Generic, Read, Show, Typeable)+ = LibComponentLocalBuildInfo+ { componentLocalName :: ComponentName+ -- ^ It would be very convenient to store the literal Library here,+ -- but if we do that, it will get serialized (via the Binary)+ -- instance twice. So instead we just provide the ComponentName,+ -- which can be used to find the Component in the+ -- PackageDescription. NB: eventually, this will NOT uniquely+ -- identify the ComponentLocalBuildInfo.+ , componentComponentId :: ComponentId+ -- ^ The computed 'ComponentId' of this component.+ , componentUnitId :: UnitId+ -- ^ The computed 'UnitId' which uniquely identifies this+ -- component. Might be hashed.+ , componentIsIndefinite_ :: Bool+ -- ^ Is this an indefinite component (i.e. has unfilled holes)?+ , componentInstantiatedWith :: [(ModuleName, OpenModule)]+ -- ^ How the component was instantiated+ , componentPackageDeps :: [(UnitId, MungedPackageId)]+ -- ^ Resolved internal and external package dependencies for this component.+ -- The 'BuildInfo' specifies a set of build dependencies that must be+ -- satisfied in terms of version ranges. This field fixes those dependencies+ -- to the specific versions available on this machine for this compiler.+ , componentIncludes :: [(OpenUnitId, ModuleRenaming)]+ -- ^ The set of packages that are brought into scope during+ -- compilation, including a 'ModuleRenaming' which may used+ -- to hide or rename modules. This is what gets translated into+ -- @-package-id@ arguments. This is a modernized version of+ -- 'componentPackageDeps', which is kept around for BC purposes.+ , componentExeDeps :: [UnitId]+ , componentInternalDeps :: [UnitId]+ -- ^ The internal dependencies which induce a graph on the+ -- 'ComponentLocalBuildInfo' of this package. This does NOT+ -- coincide with 'componentPackageDeps' because it ALSO records+ -- 'build-tool' dependencies on executables. Maybe one day+ -- @cabal-install@ will also handle these correctly too!+ , componentCompatPackageKey :: String+ -- ^ Compatibility "package key" that we pass to older versions of GHC.+ , componentCompatPackageName :: MungedPackageName+ -- ^ Compatibility "package name" that we register this component as.+ , componentExposedModules :: [Installed.ExposedModule]+ -- ^ A list of exposed modules (either defined in this component,+ -- or reexported from another component.)+ , componentIsPublic :: Bool+ -- ^ Convenience field, specifying whether or not this is the+ -- "public library" that has the same name as the package.+ }+ | -- TODO: refactor all these duplicates+ FLibComponentLocalBuildInfo+ { componentLocalName :: ComponentName+ , componentComponentId :: ComponentId+ , componentUnitId :: UnitId+ , componentPackageDeps :: [(UnitId, MungedPackageId)]+ , componentIncludes :: [(OpenUnitId, ModuleRenaming)]+ , componentExeDeps :: [UnitId]+ , componentInternalDeps :: [UnitId]+ }+ | ExeComponentLocalBuildInfo+ { componentLocalName :: ComponentName+ , componentComponentId :: ComponentId+ , componentUnitId :: UnitId+ , componentPackageDeps :: [(UnitId, MungedPackageId)]+ , componentIncludes :: [(OpenUnitId, ModuleRenaming)]+ , componentExeDeps :: [UnitId]+ , componentInternalDeps :: [UnitId]+ }+ | TestComponentLocalBuildInfo+ { componentLocalName :: ComponentName+ , componentComponentId :: ComponentId+ , componentUnitId :: UnitId+ , componentPackageDeps :: [(UnitId, MungedPackageId)]+ , componentIncludes :: [(OpenUnitId, ModuleRenaming)]+ , componentExeDeps :: [UnitId]+ , componentInternalDeps :: [UnitId]+ }+ | BenchComponentLocalBuildInfo+ { componentLocalName :: ComponentName+ , componentComponentId :: ComponentId+ , componentUnitId :: UnitId+ , componentPackageDeps :: [(UnitId, MungedPackageId)]+ , componentIncludes :: [(OpenUnitId, ModuleRenaming)]+ , componentExeDeps :: [UnitId]+ , componentInternalDeps :: [UnitId]+ }+ deriving (Generic, Read, Show) instance Binary ComponentLocalBuildInfo instance Structured ComponentLocalBuildInfo instance IsNode ComponentLocalBuildInfo where- type Key ComponentLocalBuildInfo = UnitId- nodeKey = componentUnitId- nodeNeighbors = componentInternalDeps+ type Key ComponentLocalBuildInfo = UnitId+ nodeKey = componentUnitId+ nodeNeighbors = componentInternalDeps componentIsIndefinite :: ComponentLocalBuildInfo -> Bool-componentIsIndefinite LibComponentLocalBuildInfo{ componentIsIndefinite_ = b } = b+componentIsIndefinite LibComponentLocalBuildInfo{componentIsIndefinite_ = b} = b componentIsIndefinite _ = False maybeComponentInstantiatedWith :: ComponentLocalBuildInfo -> Maybe [(ModuleName, OpenModule)] maybeComponentInstantiatedWith- LibComponentLocalBuildInfo { componentInstantiatedWith = insts } = Just insts+ LibComponentLocalBuildInfo{componentInstantiatedWith = insts} = Just insts maybeComponentInstantiatedWith _ = Nothing++maybeComponentCompatPackageKey :: ComponentLocalBuildInfo -> Maybe String+maybeComponentCompatPackageKey+ LibComponentLocalBuildInfo{componentCompatPackageKey = key} = Just key+maybeComponentCompatPackageKey _ = Nothing++maybeComponentExposedModules :: ComponentLocalBuildInfo -> Maybe [Installed.ExposedModule]+maybeComponentExposedModules+ LibComponentLocalBuildInfo{componentExposedModules = exposed} = Just exposed+maybeComponentExposedModules _ = Nothing
− src/Distribution/Types/ComponentName.hs
@@ -1,78 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Distribution.Types.ComponentName (- ComponentName(..),- showComponentName,- componentNameStanza,- componentNameString,- ) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Types.UnqualComponentName-import Distribution.Types.LibraryName-import Distribution.Pretty-import Distribution.Parsec--import qualified Text.PrettyPrint as Disp-import qualified Distribution.Compat.CharParsing as P---- Libraries live in a separate namespace, so must distinguish-data ComponentName = CLibName LibraryName- | CFLibName UnqualComponentName- | CExeName UnqualComponentName- | CTestName UnqualComponentName- | CBenchName UnqualComponentName- deriving (Eq, Generic, Ord, Read, Show, Typeable)--instance Binary ComponentName-instance Structured ComponentName---- Build-target-ish syntax-instance Pretty ComponentName where- pretty (CLibName lib) = prettyLibraryNameComponent lib- pretty (CFLibName str) = Disp.text "flib:" <<>> pretty str- pretty (CExeName str) = Disp.text "exe:" <<>> pretty str- pretty (CTestName str) = Disp.text "test:" <<>> pretty str- pretty (CBenchName str) = Disp.text "bench:" <<>> pretty str--instance Parsec ComponentName where- -- note: this works as lib/flib/... all start with different character!- parsec = parseComposite <|> parseLib- where- parseLib = CLibName <$> parsecLibraryNameComponent- parseComposite = do- ctor <- P.choice- [ P.string "flib:" >> return CFLibName- , P.string "exe:" >> return CExeName- , P.string "bench:" >> return CBenchName- , P.string "test:" >> return CTestName- ]- ctor <$> parsec--showComponentName :: ComponentName -> String-showComponentName (CLibName lib) = showLibraryName lib-showComponentName (CFLibName name) = "foreign library '" ++ prettyShow name ++ "'"-showComponentName (CExeName name) = "executable '" ++ prettyShow name ++ "'"-showComponentName (CTestName name) = "test suite '" ++ prettyShow name ++ "'"-showComponentName (CBenchName name) = "benchmark '" ++ prettyShow name ++ "'"--componentNameStanza :: ComponentName -> String-componentNameStanza (CLibName lib) = libraryNameStanza lib-componentNameStanza (CFLibName name) = "foreign-library " ++ prettyShow name-componentNameStanza (CExeName name) = "executable " ++ prettyShow name-componentNameStanza (CTestName name) = "test-suite " ++ prettyShow name-componentNameStanza (CBenchName name) = "benchmark " ++ prettyShow name---- | This gets the underlying unqualified component name. In fact, it is--- guaranteed to uniquely identify a component, returning--- @Nothing@ if the 'ComponentName' was for the public--- library.-componentNameString :: ComponentName -> Maybe UnqualComponentName-componentNameString (CLibName lib) = libraryNameString lib-componentNameString (CFLibName n) = Just n-componentNameString (CExeName n) = Just n-componentNameString (CTestName n) = Just n-componentNameString (CBenchName n) = Just n
− src/Distribution/Types/ComponentRequestedSpec.hs
@@ -1,127 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Types.ComponentRequestedSpec (- -- $buildable_vs_enabled_components-- ComponentRequestedSpec(..),- ComponentDisabledReason(..),-- defaultComponentRequestedSpec,- componentNameRequested,-- componentEnabled,- componentDisabledReason,-) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Types.Component -- TODO: maybe remove me?-import Distribution.Types.ComponentName--import Distribution.Pretty (prettyShow)---- $buildable_vs_enabled_components--- #buildable_vs_enabled_components#------ = Note: Buildable versus requested versus enabled components--- What's the difference between a buildable component (ala--- 'componentBuildable'), a requested component--- (ala 'componentNameRequested'), and an enabled component (ala--- 'componentEnabled')?------ A component is __buildable__ if, after resolving flags and--- conditionals, there is no @buildable: False@ property in it.--- This is a /static/ property that arises from the--- Cabal file and the package description flattening; once we have--- a 'PackageDescription' buildability is known.------ A component is __requested__ if a user specified, via a--- the flags and arguments passed to configure, that it should be--- built. E.g., @--enable-tests@ or @--enable-benchmarks@ request--- all tests and benchmarks, if they are provided. What is requested--- can be read off directly from 'ComponentRequestedSpec'. A requested--- component is not always buildable; e.g., a user may @--enable-tests@--- but one of the test suites may have @buildable: False@.------ A component is __enabled__ if it is BOTH buildable--- and requested. Once we have a 'LocalBuildInfo', whether or not a--- component is enabled is known.------ Generally speaking, most Cabal API code cares if a component--- is enabled. (For example, if you want to run a preprocessor on each--- component prior to building them, you want to run this on each--- /enabled/ component.)------ Note that post-configuration, you will generally not see a--- non-buildable 'Component'. This is because 'flattenPD' will drop--- any such components from 'PackageDescription'. See #3858 for--- an example where this causes problems.---- | Describes what components are enabled by user-interaction.--- See also this note in--- "Distribution.Types.ComponentRequestedSpec#buildable_vs_enabled_components".------ @since 2.0.0.2-data ComponentRequestedSpec- = ComponentRequestedSpec { testsRequested :: Bool- , benchmarksRequested :: Bool }- | OneComponentRequestedSpec ComponentName- deriving (Generic, Read, Show, Eq, Typeable)--instance Binary ComponentRequestedSpec-instance Structured ComponentRequestedSpec---- | The default set of enabled components. Historically tests and--- benchmarks are NOT enabled by default.------ @since 2.0.0.2-defaultComponentRequestedSpec :: ComponentRequestedSpec-defaultComponentRequestedSpec = ComponentRequestedSpec False False---- | Is this component enabled? See also this note in--- "Distribution.Types.ComponentRequestedSpec#buildable_vs_enabled_components".------ @since 2.0.0.2-componentEnabled :: ComponentRequestedSpec -> Component -> Bool-componentEnabled enabled = isNothing . componentDisabledReason enabled---- | Is this component name enabled? See also this note in--- "Distribution.Types.ComponentRequestedSpec#buildable_vs_enabled_components".------ @since 2.0.0.2-componentNameRequested :: ComponentRequestedSpec -> ComponentName -> Bool-componentNameRequested enabled = isNothing . componentNameNotRequestedReason enabled---- | Is this component disabled, and if so, why?------ @since 2.0.0.2-componentDisabledReason :: ComponentRequestedSpec -> Component- -> Maybe ComponentDisabledReason-componentDisabledReason enabled comp- | not (componentBuildable comp) = Just DisabledComponent- | otherwise = componentNameNotRequestedReason enabled (componentName comp)---- | Is this component name disabled, and if so, why?------ @since 2.0.0.2-componentNameNotRequestedReason :: ComponentRequestedSpec -> ComponentName- -> Maybe ComponentDisabledReason-componentNameNotRequestedReason- ComponentRequestedSpec{ testsRequested = False } (CTestName _)- = Just DisabledAllTests-componentNameNotRequestedReason- ComponentRequestedSpec{ benchmarksRequested = False } (CBenchName _)- = Just DisabledAllBenchmarks-componentNameNotRequestedReason ComponentRequestedSpec{} _ = Nothing-componentNameNotRequestedReason (OneComponentRequestedSpec cname) c- | c == cname = Nothing- | otherwise = Just (DisabledAllButOne (prettyShow cname))---- | A reason explaining why a component is disabled.------ @since 2.0.0.2-data ComponentDisabledReason = DisabledComponent- | DisabledAllTests- | DisabledAllBenchmarks- | DisabledAllButOne String
− src/Distribution/Types/CondTree.hs
@@ -1,181 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE DeriveFoldable #-}-{-# LANGUAGE DeriveTraversable #-}-{-# LANGUAGE ScopedTypeVariables #-}--module Distribution.Types.CondTree (- CondTree(..),- CondBranch(..),- condIfThen,- condIfThenElse,- mapCondTree,- mapTreeConstrs,- mapTreeConds,- mapTreeData,- traverseCondTreeV,- traverseCondBranchV,- traverseCondTreeC,- traverseCondBranchC,- extractCondition,- simplifyCondTree,- ignoreConditions,-) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Types.Condition--import qualified Distribution.Compat.Lens as L----- | A 'CondTree' is used to represent the conditional structure of--- a Cabal file, reflecting a syntax element subject to constraints,--- and then any number of sub-elements which may be enabled subject--- to some condition. Both @a@ and @c@ are usually 'Monoid's.------ To be more concrete, consider the following fragment of a @Cabal@--- file:------ @--- build-depends: base >= 4.0--- if flag(extra)--- build-depends: base >= 4.2--- @------ One way to represent this is to have @'CondTree' 'ConfVar'--- ['Dependency'] 'BuildInfo'@. Here, 'condTreeData' represents--- the actual fields which are not behind any conditional, while--- 'condTreeComponents' recursively records any further fields--- which are behind a conditional. 'condTreeConstraints' records--- the constraints (in this case, @base >= 4.0@) which would--- be applied if you use this syntax; in general, this is--- derived off of 'targetBuildInfo' (perhaps a good refactoring--- would be to convert this into an opaque type, with a smart--- constructor that pre-computes the dependencies.)----data CondTree v c a = CondNode- { condTreeData :: a- , condTreeConstraints :: c- , condTreeComponents :: [CondBranch v c a]- }- deriving (Show, Eq, Typeable, Data, Generic, Functor, Foldable, Traversable)--instance (Binary v, Binary c, Binary a) => Binary (CondTree v c a)-instance (Structured v, Structured c, Structured a) => Structured (CondTree v c a)-instance (NFData v, NFData c, NFData a) => NFData (CondTree v c a) where rnf = genericRnf---- | A 'CondBranch' represents a conditional branch, e.g., @if--- flag(foo)@ on some syntax @a@. It also has an optional false--- branch.----data CondBranch v c a = CondBranch- { condBranchCondition :: Condition v- , condBranchIfTrue :: CondTree v c a- , condBranchIfFalse :: Maybe (CondTree v c a)- }- deriving (Show, Eq, Typeable, Data, Generic, Functor, Traversable)---- This instance is written by hand because GHC 8.0.1/8.0.2 infinite--- loops when trying to derive it with optimizations. See--- https://gitlab.haskell.org/ghc/ghc/-/issues/13056-instance Foldable (CondBranch v c) where- foldMap f (CondBranch _ c Nothing) = foldMap f c- foldMap f (CondBranch _ c (Just a)) = foldMap f c `mappend` foldMap f a--instance (Binary v, Binary c, Binary a) => Binary (CondBranch v c a)-instance (Structured v, Structured c, Structured a) => Structured (CondBranch v c a)-instance (NFData v, NFData c, NFData a) => NFData (CondBranch v c a) where rnf = genericRnf--condIfThen :: Condition v -> CondTree v c a -> CondBranch v c a-condIfThen c t = CondBranch c t Nothing--condIfThenElse :: Condition v -> CondTree v c a -> CondTree v c a -> CondBranch v c a-condIfThenElse c t e = CondBranch c t (Just e)--mapCondTree :: (a -> b) -> (c -> d) -> (Condition v -> Condition w)- -> CondTree v c a -> CondTree w d b-mapCondTree fa fc fcnd (CondNode a c ifs) =- CondNode (fa a) (fc c) (map g ifs)- where- g (CondBranch cnd t me)- = CondBranch (fcnd cnd)- (mapCondTree fa fc fcnd t)- (fmap (mapCondTree fa fc fcnd) me)--mapTreeConstrs :: (c -> d) -> CondTree v c a -> CondTree v d a-mapTreeConstrs f = mapCondTree id f id--mapTreeConds :: (Condition v -> Condition w) -> CondTree v c a -> CondTree w c a-mapTreeConds f = mapCondTree id id f--mapTreeData :: (a -> b) -> CondTree v c a -> CondTree v c b-mapTreeData f = mapCondTree f id id---- | @@Traversal@@ for the variables-traverseCondTreeV :: L.Traversal (CondTree v c a) (CondTree w c a) v w-traverseCondTreeV f (CondNode a c ifs) =- CondNode a c <$> traverse (traverseCondBranchV f) ifs---- | @@Traversal@@ for the variables-traverseCondBranchV :: L.Traversal (CondBranch v c a) (CondBranch w c a) v w-traverseCondBranchV f (CondBranch cnd t me) = CondBranch- <$> traverse f cnd- <*> traverseCondTreeV f t- <*> traverse (traverseCondTreeV f) me---- | @@Traversal@@ for the aggregated constraints-traverseCondTreeC :: L.Traversal (CondTree v c a) (CondTree v d a) c d-traverseCondTreeC f (CondNode a c ifs) =- CondNode a <$> f c <*> traverse (traverseCondBranchC f) ifs---- | @@Traversal@@ for the aggregated constraints-traverseCondBranchC :: L.Traversal (CondBranch v c a) (CondBranch v d a) c d-traverseCondBranchC f (CondBranch cnd t me) = CondBranch cnd- <$> traverseCondTreeC f t- <*> traverse (traverseCondTreeC f) me----- | Extract the condition matched by the given predicate from a cond tree.------ We use this mainly for extracting buildable conditions (see the Note in--- Distribution.PackageDescription.Configuration), but the function is in fact--- more general.-extractCondition :: Eq v => (a -> Bool) -> CondTree v c a -> Condition v-extractCondition p = go- where- go (CondNode x _ cs) | not (p x) = Lit False- | otherwise = goList cs-- goList [] = Lit True- goList (CondBranch c t e : cs) =- let- ct = go t- ce = maybe (Lit True) go e- in- ((c `cAnd` ct) `cOr` (CNot c `cAnd` ce)) `cAnd` goList cs---- | Flattens a CondTree using a partial flag assignment. When a condition--- cannot be evaluated, both branches are ignored.-simplifyCondTree :: (Semigroup a, Semigroup d) =>- (v -> Either v Bool)- -> CondTree v d a- -> (d, a)-simplifyCondTree env (CondNode a d ifs) =- foldl (<>) (d, a) $ mapMaybe simplifyIf ifs- where- simplifyIf (CondBranch cnd t me) =- case simplifyCondition cnd env of- (Lit True, _) -> Just $ simplifyCondTree env t- (Lit False, _) -> fmap (simplifyCondTree env) me- _ -> Nothing---- | Flatten a CondTree. This will resolve the CondTree by taking all--- possible paths into account. Note that since branches represent exclusive--- choices this may not result in a \"sane\" result.-ignoreConditions :: (Semigroup a, Semigroup c) => CondTree v c a -> (a, c)-ignoreConditions (CondNode a c ifs) = foldl (<>) (a, c) $ concatMap f ifs- where f (CondBranch _ t me) = ignoreConditions t- : maybeToList (fmap ignoreConditions me)
− src/Distribution/Types/Condition.hs
@@ -1,135 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Distribution.Types.Condition (- Condition(..),- cNot,- cAnd,- cOr,- simplifyCondition,-) where--import Prelude ()-import Distribution.Compat.Prelude---- | A boolean expression parameterized over the variable type used.-data Condition c = Var c- | Lit Bool- | CNot (Condition c)- | COr (Condition c) (Condition c)- | CAnd (Condition c) (Condition c)- deriving (Show, Eq, Typeable, Data, Generic)---- | Boolean negation of a 'Condition' value.-cNot :: Condition a -> Condition a-cNot (Lit b) = Lit (not b)-cNot (CNot c) = c-cNot c = CNot c---- | Boolean AND of two 'Condtion' values.-cAnd :: Condition a -> Condition a -> Condition a-cAnd (Lit False) _ = Lit False-cAnd _ (Lit False) = Lit False-cAnd (Lit True) x = x-cAnd x (Lit True) = x-cAnd x y = CAnd x y---- | Boolean OR of two 'Condition' values.-cOr :: Eq v => Condition v -> Condition v -> Condition v-cOr (Lit True) _ = Lit True-cOr _ (Lit True) = Lit True-cOr (Lit False) x = x-cOr x (Lit False) = x-cOr c (CNot d)- | c == d = Lit True-cOr (CNot c) d- | c == d = Lit True-cOr x y = COr x y--instance Functor Condition where- f `fmap` Var c = Var (f c)- _ `fmap` Lit c = Lit c- f `fmap` CNot c = CNot (fmap f c)- f `fmap` COr c d = COr (fmap f c) (fmap f d)- f `fmap` CAnd c d = CAnd (fmap f c) (fmap f d)--instance Foldable Condition where- f `foldMap` Var c = f c- _ `foldMap` Lit _ = mempty- f `foldMap` CNot c = foldMap f c- f `foldMap` COr c d = foldMap f c `mappend` foldMap f d- f `foldMap` CAnd c d = foldMap f c `mappend` foldMap f d--instance Traversable Condition where- f `traverse` Var c = Var `fmap` f c- _ `traverse` Lit c = pure $ Lit c- f `traverse` CNot c = CNot `fmap` traverse f c- f `traverse` COr c d = COr `fmap` traverse f c <*> traverse f d- f `traverse` CAnd c d = CAnd `fmap` traverse f c <*> traverse f d--instance Applicative Condition where- pure = Var- (<*>) = ap--instance Monad Condition where- return = pure- -- Terminating cases- (>>=) (Lit x) _ = Lit x- (>>=) (Var x) f = f x- -- Recursing cases- (>>=) (CNot x ) f = CNot (x >>= f)- (>>=) (COr x y) f = COr (x >>= f) (y >>= f)- (>>=) (CAnd x y) f = CAnd (x >>= f) (y >>= f)--instance Monoid (Condition a) where- mempty = Lit False- mappend = (<>)--instance Semigroup (Condition a) where- (<>) = COr--instance Alternative Condition where- empty = mempty- (<|>) = mappend--instance MonadPlus Condition where- mzero = mempty- mplus = mappend--instance Binary c => Binary (Condition c)-instance Structured c => Structured (Condition c)-instance NFData c => NFData (Condition c) where rnf = genericRnf---- | Simplify the condition and return its free variables.-simplifyCondition :: Condition c- -> (c -> Either d Bool) -- ^ (partial) variable assignment- -> (Condition d, [d])-simplifyCondition cond i = fv . walk $ cond- where- walk cnd = case cnd of- Var v -> either Var Lit (i v)- Lit b -> Lit b- CNot c -> case walk c of- Lit True -> Lit False- Lit False -> Lit True- c' -> CNot c'- COr c d -> case (walk c, walk d) of- (Lit False, d') -> d'- (Lit True, _) -> Lit True- (c', Lit False) -> c'- (_, Lit True) -> Lit True- (c',d') -> COr c' d'- CAnd c d -> case (walk c, walk d) of- (Lit False, _) -> Lit False- (Lit True, d') -> d'- (_, Lit False) -> Lit False- (c', Lit True) -> c'- (c',d') -> CAnd c' d'- -- gather free vars- fv c = (c, fv' c)- fv' c = case c of- Var v -> [v]- Lit _ -> []- CNot c' -> fv' c'- COr c1 c2 -> fv' c1 ++ fv' c2- CAnd c1 c2 -> fv' c1 ++ fv' c2
− src/Distribution/Types/ConfVar.hs
@@ -1,25 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Types.ConfVar (- ConfVar(..),- ) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Types.Flag-import Distribution.Types.VersionRange-import Distribution.Compiler-import Distribution.System---- | A @ConfVar@ represents the variable type used.-data ConfVar = OS OS- | Arch Arch- | PackageFlag FlagName- | Impl CompilerFlavor VersionRange- deriving (Eq, Show, Typeable, Data, Generic)--instance Binary ConfVar-instance Structured ConfVar--instance NFData ConfVar where rnf = genericRnf
− src/Distribution/Types/Dependency.hs
@@ -1,179 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Types.Dependency- ( Dependency(..)- , mkDependency- , depPkgName- , depVerRange- , depLibraries- , simplifyDependency- , mainLibSet- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Types.VersionRange (isAnyVersionLight)-import Distribution.Version (VersionRange, anyVersion, simplifyVersionRange)--import Distribution.CabalSpecVersion-import Distribution.Compat.CharParsing (char, spaces)-import Distribution.Compat.Parsing (between, option)-import Distribution.Parsec-import Distribution.Pretty-import Distribution.Types.LibraryName-import Distribution.Types.PackageName-import Distribution.Types.UnqualComponentName--import qualified Distribution.Compat.NonEmptySet as NES-import qualified Text.PrettyPrint as PP---- | Describes a dependency on a source package (API)------ /Invariant:/ package name does not appear as 'LSubLibName' in--- set of library names.----data Dependency = Dependency- PackageName- VersionRange- (NonEmptySet LibraryName)- -- ^ The set of libraries required from the package.- -- Only the selected libraries will be built.- -- It does not affect the cabal-install solver yet.- deriving (Generic, Read, Show, Eq, Typeable, Data)--depPkgName :: Dependency -> PackageName-depPkgName (Dependency pn _ _) = pn--depVerRange :: Dependency -> VersionRange-depVerRange (Dependency _ vr _) = vr--depLibraries :: Dependency -> NonEmptySet LibraryName-depLibraries (Dependency _ _ cs) = cs---- | Smart constructor of 'Dependency'.------ If 'PackageName' is appears as 'LSubLibName' in a set of sublibraries,--- it is automatically converted to 'LMainLibName'.------ @since 3.4.0.0----mkDependency :: PackageName -> VersionRange -> NonEmptySet LibraryName -> Dependency-mkDependency pn vr lb = Dependency pn vr (NES.map conv lb)- where- pn' = packageNameToUnqualComponentName pn-- conv l@LMainLibName = l- conv l@(LSubLibName ln) | ln == pn' = LMainLibName- | otherwise = l--instance Binary Dependency-instance Structured Dependency-instance NFData Dependency where rnf = genericRnf---- |------ >>> prettyShow $ Dependency "pkg" anyVersion mainLibSet--- "pkg"------ >>> prettyShow $ Dependency "pkg" anyVersion $ NES.insert (LSubLibName "sublib") mainLibSet--- "pkg:{pkg, sublib}"------ >>> prettyShow $ Dependency "pkg" anyVersion $ NES.singleton (LSubLibName "sublib")--- "pkg:sublib"------ >>> prettyShow $ Dependency "pkg" anyVersion $ NES.insert (LSubLibName "sublib-b") $ NES.singleton (LSubLibName "sublib-a")--- "pkg:{sublib-a, sublib-b}"----instance Pretty Dependency where- pretty (Dependency name ver sublibs) = withSubLibs (pretty name) <+> pver- where- -- TODO: change to isAnyVersion after #6736- pver | isAnyVersionLight ver = PP.empty- | otherwise = pretty ver-- withSubLibs doc = case NES.toList sublibs of- [LMainLibName] -> doc- [LSubLibName uq] -> doc <<>> PP.colon <<>> pretty uq- _ -> doc <<>> PP.colon <<>> PP.braces prettySublibs-- prettySublibs = PP.hsep $ PP.punctuate PP.comma $ prettySublib <$> NES.toList sublibs-- prettySublib LMainLibName = PP.text $ unPackageName name- prettySublib (LSubLibName un) = PP.text $ unUnqualComponentName un---- |------ >>> simpleParsec "mylib:sub" :: Maybe Dependency--- Just (Dependency (PackageName "mylib") (OrLaterVersion (mkVersion [0])) (fromNonEmpty (LSubLibName (UnqualComponentName "sub") :| [])))------ >>> simpleParsec "mylib:{sub1,sub2}" :: Maybe Dependency--- Just (Dependency (PackageName "mylib") (OrLaterVersion (mkVersion [0])) (fromNonEmpty (LSubLibName (UnqualComponentName "sub1") :| [LSubLibName (UnqualComponentName "sub2")])))------ >>> simpleParsec "mylib:{ sub1 , sub2 }" :: Maybe Dependency--- Just (Dependency (PackageName "mylib") (OrLaterVersion (mkVersion [0])) (fromNonEmpty (LSubLibName (UnqualComponentName "sub1") :| [LSubLibName (UnqualComponentName "sub2")])))------ >>> simpleParsec "mylib:{ sub1 , sub2 } ^>= 42" :: Maybe Dependency--- Just (Dependency (PackageName "mylib") (MajorBoundVersion (mkVersion [42])) (fromNonEmpty (LSubLibName (UnqualComponentName "sub1") :| [LSubLibName (UnqualComponentName "sub2")])))------ >>> simpleParsec "mylib:{ } ^>= 42" :: Maybe Dependency--- Nothing------ >>> traverse_ print (map simpleParsec ["mylib:mylib", "mylib:{mylib}", "mylib:{mylib,sublib}" ] :: [Maybe Dependency])--- Just (Dependency (PackageName "mylib") (OrLaterVersion (mkVersion [0])) (fromNonEmpty (LMainLibName :| [])))--- Just (Dependency (PackageName "mylib") (OrLaterVersion (mkVersion [0])) (fromNonEmpty (LMainLibName :| [])))--- Just (Dependency (PackageName "mylib") (OrLaterVersion (mkVersion [0])) (fromNonEmpty (LMainLibName :| [LSubLibName (UnqualComponentName "sublib")])))------ Spaces around colon are not allowed:------ >>> map simpleParsec ["mylib: sub", "mylib :sub", "mylib: {sub1,sub2}", "mylib :{sub1,sub2}"] :: [Maybe Dependency]--- [Nothing,Nothing,Nothing,Nothing]------ Sublibrary syntax is accepted since @cabal-version: 3.0@------ >>> map (`simpleParsec'` "mylib:sub") [CabalSpecV2_4, CabalSpecV3_0] :: [Maybe Dependency]--- [Nothing,Just (Dependency (PackageName "mylib") (OrLaterVersion (mkVersion [0])) (fromNonEmpty (LSubLibName (UnqualComponentName "sub") :| [])))]----instance Parsec Dependency where- parsec = do- name <- parsec-- libs <- option mainLibSet $ do- _ <- char ':'- versionGuardMultilibs- parsecWarning PWTExperimental "colon specifier is experimental feature (issue #5660)"- NES.singleton <$> parseLib <|> parseMultipleLibs-- spaces -- https://github.com/haskell/cabal/issues/5846-- ver <- parsec <|> pure anyVersion- return $ mkDependency name ver libs- where- parseLib = LSubLibName <$> parsec- parseMultipleLibs = between- (char '{' *> spaces)- (spaces *> char '}')- (NES.fromNonEmpty <$> parsecCommaNonEmpty parseLib)--versionGuardMultilibs :: CabalParsing m => m ()-versionGuardMultilibs = do- csv <- askCabalSpecVersion- when (csv < CabalSpecV3_0) $ fail $ unwords- [ "Sublibrary dependency syntax used."- , "To use this syntax the package needs to specify at least 'cabal-version: 3.0'."- , "Alternatively, if you are depending on an internal library, you can write"- , "directly the library name as it were a package."- ]---- | Library set with main library.------ @since 3.4.0.0-mainLibSet :: NonEmptySet LibraryName-mainLibSet = NES.singleton LMainLibName---- | Simplify the 'VersionRange' expression in a 'Dependency'.--- See 'simplifyVersionRange'.----simplifyDependency :: Dependency -> Dependency-simplifyDependency (Dependency name range comps) =- Dependency name (simplifyVersionRange range) comps
− src/Distribution/Types/DependencyMap.hs
@@ -1,57 +0,0 @@-module Distribution.Types.DependencyMap (- DependencyMap,- toDepMap,- fromDepMap,- constrainBy,-) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Types.Dependency-import Distribution.Types.LibraryName-import Distribution.Types.PackageName-import Distribution.Types.PackageVersionConstraint-import Distribution.Version--import qualified Data.Map.Lazy as Map---- | A map of dependencies. Newtyped since the default monoid instance is not--- appropriate. The monoid instance uses 'intersectVersionRanges'.-newtype DependencyMap = DependencyMap { unDependencyMap :: Map PackageName (VersionRange, NonEmptySet LibraryName) }- deriving (Show, Read)--instance Monoid DependencyMap where- mempty = DependencyMap Map.empty- mappend = (<>)--instance Semigroup DependencyMap where- (DependencyMap a) <> (DependencyMap b) =- DependencyMap (Map.unionWith intersectVersionRangesAndJoinComponents a b)--intersectVersionRangesAndJoinComponents :: (VersionRange, NonEmptySet LibraryName)- -> (VersionRange, NonEmptySet LibraryName)- -> (VersionRange, NonEmptySet LibraryName)-intersectVersionRangesAndJoinComponents (va, ca) (vb, cb) =- (intersectVersionRanges va vb, ca <> cb)--toDepMap :: [Dependency] -> DependencyMap-toDepMap ds =- DependencyMap $ Map.fromListWith intersectVersionRangesAndJoinComponents [ (p,(vr,cs)) | Dependency p vr cs <- ds ]--fromDepMap :: DependencyMap -> [Dependency]-fromDepMap m = [ Dependency p vr cs | (p,(vr,cs)) <- Map.toList (unDependencyMap m) ]---- Apply extra constraints to a dependency map.--- Combines dependencies where the result will only contain keys from the left--- (first) map. If a key also exists in the right map, both constraints will--- be intersected.-constrainBy- :: DependencyMap- -> [PackageVersionConstraint]- -> DependencyMap-constrainBy = foldl' tightenConstraint where- tightenConstraint (DependencyMap l) (PackageVersionConstraint pn vr) = DependencyMap $- case Map.lookup pn l of- Nothing -> l- Just (vr', cs) -> Map.insert pn (intersectVersionRanges vr' vr, cs) l
+ src/Distribution/Types/DumpBuildInfo.hs view
@@ -0,0 +1,15 @@+{-# LANGUAGE DeriveGeneric #-}++module Distribution.Types.DumpBuildInfo+ ( DumpBuildInfo (..)+ ) where++import Distribution.Compat.Prelude++data DumpBuildInfo+ = NoDumpBuildInfo+ | DumpBuildInfo+ deriving (Read, Show, Eq, Ord, Enum, Bounded, Generic)++instance Binary DumpBuildInfo+instance Structured DumpBuildInfo
− src/Distribution/Types/ExeDependency.hs
@@ -1,71 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Types.ExeDependency- ( ExeDependency(..)- , qualifiedExeName- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Parsec-import Distribution.Pretty-import Distribution.Types.ComponentName-import Distribution.Types.PackageName-import Distribution.Types.UnqualComponentName-import Distribution.Version (VersionRange, anyVersion, isAnyVersion)--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as PP---- | Describes a dependency on an executable from a package----data ExeDependency = ExeDependency- PackageName- UnqualComponentName -- name of executable component of package- VersionRange- deriving (Generic, Read, Show, Eq, Typeable, Data)--instance Binary ExeDependency-instance Structured ExeDependency-instance NFData ExeDependency where rnf = genericRnf--instance Pretty ExeDependency where- pretty (ExeDependency name exe ver) =- pretty name <<>> PP.colon <<>> pretty exe PP.<+> pver- where- pver | isAnyVersion ver = PP.empty- | otherwise = pretty ver---- | ------ Examples------ >>> simpleParsec "happy:happy" :: Maybe ExeDependency--- Just (ExeDependency (PackageName "happy") (UnqualComponentName "happy") (OrLaterVersion (mkVersion [0])))------ >>> simpleParsec "happy:happy >= 1.19.12" :: Maybe ExeDependency--- Just (ExeDependency (PackageName "happy") (UnqualComponentName "happy") (OrLaterVersion (mkVersion [1,19,12])))------ >>> simpleParsec "happy:happy>=1.19.12" :: Maybe ExeDependency--- Just (ExeDependency (PackageName "happy") (UnqualComponentName "happy") (OrLaterVersion (mkVersion [1,19,12])))------ >>> simpleParsec "happy : happy >= 1.19.12" :: Maybe ExeDependency--- Nothing------ >>> simpleParsec "happy: happy >= 1.19.12" :: Maybe ExeDependency--- Nothing------ >>> simpleParsec "happy :happy >= 1.19.12" :: Maybe ExeDependency--- Nothing----instance Parsec ExeDependency where- parsec = do- name <- parsec- _ <- P.char ':'- exe <- lexemeParsec- ver <- parsec <|> pure anyVersion- return (ExeDependency name exe ver)--qualifiedExeName :: ExeDependency -> ComponentName-qualifiedExeName (ExeDependency _ ucn _) = CExeName ucn
− src/Distribution/Types/Executable.hs
@@ -1,65 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Distribution.Types.Executable (- Executable(..),- emptyExecutable,- exeModules,- exeModulesAutogen-) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Types.BuildInfo-import Distribution.Types.UnqualComponentName-import Distribution.Types.ExecutableScope-import Distribution.ModuleName--import qualified Distribution.Types.BuildInfo.Lens as L--data Executable = Executable {- exeName :: UnqualComponentName,- modulePath :: FilePath,- exeScope :: ExecutableScope,- buildInfo :: BuildInfo- }- deriving (Generic, Show, Read, Eq, Typeable, Data)--instance L.HasBuildInfo Executable where- buildInfo f l = (\x -> l { buildInfo = x }) <$> f (buildInfo l)--instance Binary Executable-instance Structured Executable-instance NFData Executable where rnf = genericRnf--instance Monoid Executable where- mempty = gmempty- mappend = (<>)--instance Semigroup Executable where- a <> b = Executable{- exeName = combine' exeName,- modulePath = combine modulePath,- exeScope = combine exeScope,- buildInfo = combine buildInfo- }- where combine field = field a `mappend` field b- combine' field = case ( unUnqualComponentName $ field a- , unUnqualComponentName $ field b) of- ("", _) -> field b- (_, "") -> field a- (x, y) -> error $ "Ambiguous values for executable field: '"- ++ x ++ "' and '" ++ y ++ "'"--emptyExecutable :: Executable-emptyExecutable = mempty---- | Get all the module names from an exe-exeModules :: Executable -> [ModuleName]-exeModules exe = otherModules (buildInfo exe)---- | Get all the auto generated module names from an exe--- This are a subset of 'exeModules'.-exeModulesAutogen :: Executable -> [ModuleName]-exeModulesAutogen exe = autogenModules (buildInfo exe)
− src/Distribution/Types/Executable/Lens.hs
@@ -1,31 +0,0 @@-module Distribution.Types.Executable.Lens (- Executable,- module Distribution.Types.Executable.Lens,- ) where--import Distribution.Compat.Lens-import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Types.BuildInfo (BuildInfo)-import Distribution.Types.Executable (Executable)-import Distribution.Types.ExecutableScope (ExecutableScope)-import Distribution.Types.UnqualComponentName (UnqualComponentName)--import qualified Distribution.Types.Executable as T--exeName :: Lens' Executable UnqualComponentName-exeName f s = fmap (\x -> s { T.exeName = x }) (f (T.exeName s))-{-# INLINE exeName #-}--modulePath :: Lens' Executable String-modulePath f s = fmap (\x -> s { T.modulePath = x }) (f (T.modulePath s))-{-# INLINE modulePath #-}--exeScope :: Lens' Executable ExecutableScope-exeScope f s = fmap (\x -> s { T.exeScope = x }) (f (T.exeScope s))-{-# INLINE exeScope #-}--exeBuildInfo :: Lens' Executable BuildInfo-exeBuildInfo f s = fmap (\x -> s { T.buildInfo = x }) (f (T.buildInfo s))-{-# INLINE exeBuildInfo #-}
− src/Distribution/Types/ExecutableScope.hs
@@ -1,43 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}--module Distribution.Types.ExecutableScope (- ExecutableScope(..),-) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Pretty-import Distribution.Parsec--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp--data ExecutableScope = ExecutablePublic- | ExecutablePrivate- deriving (Generic, Show, Read, Eq, Typeable, Data)--instance Pretty ExecutableScope where- pretty ExecutablePublic = Disp.text "public"- pretty ExecutablePrivate = Disp.text "private"--instance Parsec ExecutableScope where- parsec = P.try pub <|> pri where- pub = ExecutablePublic <$ P.string "public"- pri = ExecutablePrivate <$ P.string "private"--instance Binary ExecutableScope-instance Structured ExecutableScope-instance NFData ExecutableScope where rnf = genericRnf---- | 'Any' like semigroup, where 'ExecutablePrivate' is 'Any True'-instance Semigroup ExecutableScope where- ExecutablePublic <> x = x- x@ExecutablePrivate <> _ = x---- | 'mempty' = 'ExecutablePublic'-instance Monoid ExecutableScope where- mempty = ExecutablePublic- mappend = (<>)
− src/Distribution/Types/ExposedModule.hs
@@ -1,45 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Types.ExposedModule where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Backpack-import Distribution.ModuleName-import Distribution.Parsec-import Distribution.Pretty--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp--data ExposedModule- = ExposedModule {- exposedName :: ModuleName,- exposedReexport :: Maybe OpenModule- }- deriving (Eq, Generic, Read, Show, Typeable)--instance Pretty ExposedModule where- pretty (ExposedModule m reexport) =- Disp.hsep [ pretty m- , case reexport of- Just m' -> Disp.hsep [Disp.text "from", pretty m']- Nothing -> Disp.empty- ]--instance Parsec ExposedModule where- parsec = do- m <- parsecMaybeQuoted parsec- P.spaces-- reexport <- P.optional $ do- _ <- P.string "from"- P.skipSpaces1- parsec-- return (ExposedModule m reexport)--instance Binary ExposedModule-instance Structured ExposedModule-instance NFData ExposedModule where rnf = genericRnf
− src/Distribution/Types/Flag.hs
@@ -1,346 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-module Distribution.Types.Flag (- -- * Package flag- PackageFlag(..),- emptyFlag,- -- * Flag name- FlagName,- mkFlagName,- unFlagName,- -- * Flag assignment- FlagAssignment,- mkFlagAssignment,- unFlagAssignment,- lookupFlagAssignment,- insertFlagAssignment,- diffFlagAssignment,- findDuplicateFlagAssignments,- nullFlagAssignment,- showFlagValue,- dispFlagAssignment,- showFlagAssignment,- parsecFlagAssignment,- parsecFlagAssignmentNonEmpty,- -- ** Legacy formats- legacyShowFlagAssignment,- legacyShowFlagAssignment',- legacyParsecFlagAssignment,- ) where--import Prelude ()-import Distribution.Compat.Prelude-import Distribution.Utils.ShortText-import Distribution.Utils.Generic (lowercase)--import Distribution.Parsec-import Distribution.Pretty--import qualified Data.Map as Map-import qualified Text.PrettyPrint as Disp-import qualified Distribution.Compat.CharParsing as P---- -------------------------------------------------------------------------------- The Flag' type---- | A flag can represent a feature to be included, or a way of linking--- a target against its dependencies, or in fact whatever you can think of.-data PackageFlag = MkPackageFlag- { flagName :: FlagName- , flagDescription :: String- , flagDefault :: Bool- , flagManual :: Bool- }- deriving (Show, Eq, Typeable, Data, Generic)--instance Binary PackageFlag-instance Structured PackageFlag-instance NFData PackageFlag where rnf = genericRnf---- | A 'PackageFlag' initialized with default parameters.-emptyFlag :: FlagName -> PackageFlag-emptyFlag name = MkPackageFlag- { flagName = name- , flagDescription = ""- , flagDefault = True- , flagManual = False- }---- | A 'FlagName' is the name of a user-defined configuration flag------ Use 'mkFlagName' and 'unFlagName' to convert from/to a 'String'.------ This type is opaque since @Cabal-2.0@------ @since 2.0.0.2-newtype FlagName = FlagName ShortText- deriving (Eq, Generic, Ord, Show, Read, Typeable, Data, NFData)---- | Construct a 'FlagName' from a 'String'------ 'mkFlagName' is the inverse to 'unFlagName'------ Note: No validations are performed to ensure that the resulting--- 'FlagName' is valid------ @since 2.0.0.2-mkFlagName :: String -> FlagName-mkFlagName = FlagName . toShortText---- | 'mkFlagName'------ @since 2.0.0.2-instance IsString FlagName where- fromString = mkFlagName---- | Convert 'FlagName' to 'String'------ @since 2.0.0.2-unFlagName :: FlagName -> String-unFlagName (FlagName s) = fromShortText s--instance Binary FlagName-instance Structured FlagName--instance Pretty FlagName where- pretty = Disp.text . unFlagName--instance Parsec FlagName where- -- Note: we don't check that FlagName doesn't have leading dash,- -- cabal check will do that.- parsec = mkFlagName . lowercase <$> parsec'- where- parsec' = (:) <$> lead <*> rest- lead = P.satisfy (\c -> isAlphaNum c || c == '_')- rest = P.munch (\c -> isAlphaNum c || c == '_' || c == '-')---- | A 'FlagAssignment' is a total or partial mapping of 'FlagName's to--- 'Bool' flag values. It represents the flags chosen by the user or--- discovered during configuration. For example @--flags=foo --flags=-bar@--- becomes @[("foo", True), ("bar", False)]@------ TODO: Why we record the multiplicity of the flag?----newtype FlagAssignment- = FlagAssignment { getFlagAssignment :: Map.Map FlagName (Int, Bool) }- deriving (Binary, Generic, NFData, Typeable)--instance Structured FlagAssignment--instance Eq FlagAssignment where- (==) (FlagAssignment m1) (FlagAssignment m2)- = fmap snd m1 == fmap snd m2--instance Ord FlagAssignment where- compare (FlagAssignment m1) (FlagAssignment m2)- = fmap snd m1 `compare` fmap snd m2---- | Combines pairs of values contained in the 'FlagAssignment' Map.------ The last flag specified takes precedence, and we record the number--- of times we have seen the flag.----combineFlagValues :: (Int, Bool) -> (Int, Bool) -> (Int, Bool)-combineFlagValues (c1, _) (c2, b2) = (c1 + c2, b2)---- The 'Semigroup' instance currently is right-biased.------ If duplicate flags are specified, we want the last flag specified to--- take precedence and we want to know how many times the flag has been--- specified so that we have the option of warning the user about--- supplying duplicate flags.-instance Semigroup FlagAssignment where- (<>) (FlagAssignment m1) (FlagAssignment m2)- = FlagAssignment (Map.unionWith combineFlagValues m1 m2)--instance Monoid FlagAssignment where- mempty = FlagAssignment Map.empty- mappend = (<>)---- | Construct a 'FlagAssignment' from a list of flag/value pairs.------ If duplicate flags occur in the input list, the later entries--- in the list will take precedence.------ @since 2.2.0-mkFlagAssignment :: [(FlagName, Bool)] -> FlagAssignment-mkFlagAssignment =- FlagAssignment .- Map.fromListWith (flip combineFlagValues) . fmap (fmap (\b -> (1, b)))---- | Deconstruct a 'FlagAssignment' into a list of flag/value pairs.------ @ 'null' ('findDuplicateFlagAssignments' fa) ==> ('mkFlagAssignment' . 'unFlagAssignment') fa == fa @------ @since 2.2.0-unFlagAssignment :: FlagAssignment -> [(FlagName, Bool)]-unFlagAssignment = fmap (fmap snd) . Map.toList . getFlagAssignment---- | Test whether 'FlagAssignment' is empty.------ @since 2.2.0-nullFlagAssignment :: FlagAssignment -> Bool-nullFlagAssignment = Map.null . getFlagAssignment---- | Lookup the value for a flag------ Returns 'Nothing' if the flag isn't contained in the 'FlagAssignment'.------ @since 2.2.0-lookupFlagAssignment :: FlagName -> FlagAssignment -> Maybe Bool-lookupFlagAssignment fn = fmap snd . Map.lookup fn . getFlagAssignment---- | Insert or update the boolean value of a flag.------ If the flag is already present in the 'FlagAssigment', the--- value will be updated and the fact that multiple values have--- been provided for that flag will be recorded so that a--- warning can be generated later on.------ @since 2.2.0-insertFlagAssignment :: FlagName -> Bool -> FlagAssignment -> FlagAssignment--- TODO: this currently just shadows prior values for an existing--- flag; rather than enforcing uniqueness at construction, it's--- verified later on via `D.C.Dependency.configuredPackageProblems`-insertFlagAssignment flag val =- FlagAssignment .- Map.insertWith (flip combineFlagValues) flag (1, val) . getFlagAssignment---- | Remove all flag-assignments from the first 'FlagAssignment' that--- are contained in the second 'FlagAssignment'------ NB/TODO: This currently only removes flag assignments which also--- match the value assignment! We should review the code which uses--- this operation to figure out if this it's not enough to only--- compare the flagnames without the values.------ @since 2.2.0-diffFlagAssignment :: FlagAssignment -> FlagAssignment -> FlagAssignment-diffFlagAssignment fa1 fa2 = FlagAssignment- (Map.difference (getFlagAssignment fa1) (getFlagAssignment fa2))---- | Find the 'FlagName's that have been listed more than once.------ @since 2.2.0-findDuplicateFlagAssignments :: FlagAssignment -> [FlagName]-findDuplicateFlagAssignments =- Map.keys . Map.filter ((> 1) . fst) . getFlagAssignment---- | @since 2.2.0-instance Read FlagAssignment where- readsPrec p s = [ (FlagAssignment x, rest) | (x,rest) <- readsPrec p s ]---- | @since 2.2.0-instance Show FlagAssignment where- showsPrec p (FlagAssignment xs) = showsPrec p xs---- | String representation of a flag-value pair.-showFlagValue :: (FlagName, Bool) -> String-showFlagValue (f, True) = '+' : unFlagName f-showFlagValue (f, False) = '-' : unFlagName f---- | @since 3.4.0.0-instance Pretty FlagAssignment where- pretty = dispFlagAssignment---- |------ >>> simpleParsec "" :: Maybe FlagAssignment--- Just (fromList [])------ >>> simpleParsec "+foo -bar" :: Maybe FlagAssignment--- Just (fromList [(FlagName "bar",(1,False)),(FlagName "foo",(1,True))])------ >>> simpleParsec "-none -any" :: Maybe FlagAssignment--- Just (fromList [(FlagName "any",(1,False)),(FlagName "none",(1,False))])------ >>> simpleParsec "+foo -foo +foo +foo" :: Maybe FlagAssignment--- Just (fromList [(FlagName "foo",(4,True))])------ >>> simpleParsec "+foo -bar baz" :: Maybe FlagAssignment--- Nothing------ @since 3.4.0.0----instance Parsec FlagAssignment where- parsec = parsecFlagAssignment---- | Pretty-prints a flag assignment.-dispFlagAssignment :: FlagAssignment -> Disp.Doc-dispFlagAssignment = Disp.hsep . map (Disp.text . showFlagValue) . unFlagAssignment---- | Parses a flag assignment.-parsecFlagAssignment :: CabalParsing m => m FlagAssignment-parsecFlagAssignment = mkFlagAssignment <$>- P.sepBy (onFlag <|> offFlag) P.skipSpaces1- where- onFlag = do- _ <- P.char '+'- f <- parsec- return (f, True)- offFlag = do- _ <- P.char '-'- f <- parsec- return (f, False)---- | Parse a non-empty flag assignment------ The flags have to explicitly start with minus or plus.------ @since 3.4.0.0-parsecFlagAssignmentNonEmpty :: CabalParsing m => m FlagAssignment-parsecFlagAssignmentNonEmpty = mkFlagAssignment . toList <$>- P.sepByNonEmpty (onFlag <|> offFlag) P.skipSpaces1- where- onFlag = do- _ <- P.char '+'- f <- parsec- return (f, True)- offFlag = do- _ <- P.char '-'- f <- parsec- return (f, False)---- | Show flag assignment.------ @since 3.4.0.0-showFlagAssignment :: FlagAssignment -> String-showFlagAssignment = prettyShow . dispFlagAssignment------------------------------------------------------------------------------------ Legacy: without requiring +------------------------------------------------------------------------------------ | We need this as far as we support custom setups older than 2.2.0.0------ @since 3.4.0.0-legacyShowFlagAssignment :: FlagAssignment -> String-legacyShowFlagAssignment =- prettyShow . Disp.hsep . map Disp.text . legacyShowFlagAssignment'---- | @since 3.4.0.0-legacyShowFlagAssignment' :: FlagAssignment -> [String]-legacyShowFlagAssignment' = map legacyShowFlagValue . unFlagAssignment---- | @since 3.4.0.0-legacyShowFlagValue :: (FlagName, Bool) -> String-legacyShowFlagValue (f, True) = unFlagName f-legacyShowFlagValue (f, False) = '-' : unFlagName f---- |--- We need this as far as we support custom setups older than 2.2.0.0------ @since 3.4.0.0-legacyParsecFlagAssignment :: CabalParsing m => m FlagAssignment-legacyParsecFlagAssignment = mkFlagAssignment <$>- P.sepBy (onFlag <|> offFlag) P.skipSpaces1- where- onFlag = do- _ <- P.optional (P.char '+')- f <- parsec- return (f, True)- offFlag = do- _ <- P.char '-'- f <- parsec- return (f, False)
− src/Distribution/Types/ForeignLib.hs
@@ -1,199 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Distribution.Types.ForeignLib(- ForeignLib(..),- emptyForeignLib,- foreignLibModules,- foreignLibIsShared,- foreignLibVersion,-- LibVersionInfo,- mkLibVersionInfo,- libVersionInfoCRA,- libVersionNumber,- libVersionNumberShow,- libVersionMajor-) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.ModuleName-import Distribution.Parsec-import Distribution.Pretty-import Distribution.System-import Distribution.Types.BuildInfo-import Distribution.Types.ForeignLibOption-import Distribution.Types.ForeignLibType-import Distribution.Types.UnqualComponentName-import Distribution.Version--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp-import qualified Text.Read as Read--import qualified Distribution.Types.BuildInfo.Lens as L---- | A foreign library stanza is like a library stanza, except that--- the built code is intended for consumption by a non-Haskell client.-data ForeignLib = ForeignLib {- -- | Name of the foreign library- foreignLibName :: UnqualComponentName- -- | What kind of foreign library is this (static or dynamic).- , foreignLibType :: ForeignLibType- -- | What options apply to this foreign library (e.g., are we- -- merging in all foreign dependencies.)- , foreignLibOptions :: [ForeignLibOption]- -- | Build information for this foreign library.- , foreignLibBuildInfo :: BuildInfo- -- | Libtool-style version-info data to compute library version.- -- Refer to the libtool documentation on the- -- current:revision:age versioning scheme.- , foreignLibVersionInfo :: Maybe LibVersionInfo- -- | Linux library version- , foreignLibVersionLinux :: Maybe Version-- -- | (Windows-specific) module definition files- --- -- This is a list rather than a maybe field so that we can flatten- -- the condition trees (for instance, when creating an sdist)- , foreignLibModDefFile :: [FilePath]- }- deriving (Generic, Show, Read, Eq, Typeable, Data)--data LibVersionInfo = LibVersionInfo Int Int Int deriving (Data, Eq, Generic, Typeable)--instance Ord LibVersionInfo where- LibVersionInfo c r _ `compare` LibVersionInfo c' r' _ =- case c `compare` c' of- EQ -> r `compare` r'- e -> e--instance Show LibVersionInfo where- showsPrec d (LibVersionInfo c r a) = showParen (d > 10)- $ showString "mkLibVersionInfo "- . showsPrec 11 (c,r,a)--instance Read LibVersionInfo where- readPrec = Read.parens $ do- Read.Ident "mkLibVersionInfo" <- Read.lexP- t <- Read.step Read.readPrec- return (mkLibVersionInfo t)--instance Binary LibVersionInfo-instance Structured LibVersionInfo-instance NFData LibVersionInfo where rnf = genericRnf--instance Pretty LibVersionInfo where- pretty (LibVersionInfo c r a)- = Disp.hcat $ Disp.punctuate (Disp.char ':') $ map Disp.int [c,r,a]--instance Parsec LibVersionInfo where- parsec = do- c <- P.integral- (r, a) <- P.option (0,0) $ do- _ <- P.char ':'- r <- P.integral- a <- P.option 0 $ do- _ <- P.char ':'- P.integral- return (r,a)- return $ mkLibVersionInfo (c,r,a)---- | Construct 'LibVersionInfo' from @(current, revision, age)@--- numbers.------ For instance, @mkLibVersionInfo (3,0,0)@ constructs a--- 'LibVersionInfo' representing the version-info @3:0:0@.------ All version components must be non-negative.-mkLibVersionInfo :: (Int, Int, Int) -> LibVersionInfo-mkLibVersionInfo (c,r,a) = LibVersionInfo c r a---- | From a given 'LibVersionInfo', extract the @(current, revision,--- age)@ numbers.-libVersionInfoCRA :: LibVersionInfo -> (Int, Int, Int)-libVersionInfoCRA (LibVersionInfo c r a) = (c,r,a)---- | Given a version-info field, produce a @major.minor.build@ version-libVersionNumber :: LibVersionInfo -> (Int, Int, Int)-libVersionNumber (LibVersionInfo c r a) = (c-a , a , r)---- | Given a version-info field, return @"major.minor.build"@ as a--- 'String'-libVersionNumberShow :: LibVersionInfo -> String-libVersionNumberShow v =- let (major, minor, build) = libVersionNumber v- in show major ++ "." ++ show minor ++ "." ++ show build---- | Return the @major@ version of a version-info field.-libVersionMajor :: LibVersionInfo -> Int-libVersionMajor (LibVersionInfo c _ a) = c-a--instance L.HasBuildInfo ForeignLib where- buildInfo f l = (\x -> l { foreignLibBuildInfo = x }) <$> f (foreignLibBuildInfo l)--instance Binary ForeignLib-instance Structured ForeignLib-instance NFData ForeignLib where rnf = genericRnf--instance Semigroup ForeignLib where- a <> b = ForeignLib {- foreignLibName = combine' foreignLibName- , foreignLibType = combine foreignLibType- , foreignLibOptions = combine foreignLibOptions- , foreignLibBuildInfo = combine foreignLibBuildInfo- , foreignLibVersionInfo = combine'' foreignLibVersionInfo- , foreignLibVersionLinux = combine'' foreignLibVersionLinux- , foreignLibModDefFile = combine foreignLibModDefFile- }- where combine field = field a `mappend` field b- combine' field = case ( unUnqualComponentName $ field a- , unUnqualComponentName $ field b) of- ("", _) -> field b- (_, "") -> field a- (x, y) -> error $ "Ambiguous values for executable field: '"- ++ x ++ "' and '" ++ y ++ "'"- combine'' field = field b--instance Monoid ForeignLib where- mempty = ForeignLib {- foreignLibName = mempty- , foreignLibType = ForeignLibTypeUnknown- , foreignLibOptions = []- , foreignLibBuildInfo = mempty- , foreignLibVersionInfo = Nothing- , foreignLibVersionLinux = Nothing- , foreignLibModDefFile = []- }- mappend = (<>)---- | An empty foreign library.-emptyForeignLib :: ForeignLib-emptyForeignLib = mempty---- | Modules defined by a foreign library.-foreignLibModules :: ForeignLib -> [ModuleName]-foreignLibModules = otherModules . foreignLibBuildInfo---- | Is the foreign library shared?-foreignLibIsShared :: ForeignLib -> Bool-foreignLibIsShared = foreignLibTypeIsShared . foreignLibType---- | Get a version number for a foreign library.--- If we're on Linux, and a Linux version is specified, use that.--- If we're on Linux, and libtool-style version-info is specified, translate--- that field into appropriate version numbers.--- Otherwise, this feature is unsupported so we don't return any version data.-foreignLibVersion :: ForeignLib -> OS -> [Int]-foreignLibVersion flib Linux =- case foreignLibVersionLinux flib of- Just v -> versionNumbers v- Nothing ->- case foreignLibVersionInfo flib of- Just v' ->- let (major, minor, build) = libVersionNumber v'- in [major, minor, build]- Nothing -> []-foreignLibVersion _ _ = []
− src/Distribution/Types/ForeignLib/Lens.hs
@@ -1,45 +0,0 @@-module Distribution.Types.ForeignLib.Lens (- ForeignLib,- module Distribution.Types.ForeignLib.Lens,- ) where--import Distribution.Compat.Lens-import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Types.BuildInfo (BuildInfo)-import Distribution.Types.ForeignLib (ForeignLib, LibVersionInfo)-import Distribution.Types.ForeignLibOption (ForeignLibOption)-import Distribution.Types.ForeignLibType (ForeignLibType)-import Distribution.Types.UnqualComponentName (UnqualComponentName)-import Distribution.Version (Version)--import qualified Distribution.Types.ForeignLib as T--foreignLibName :: Lens' ForeignLib UnqualComponentName-foreignLibName f s = fmap (\x -> s { T.foreignLibName = x }) (f (T.foreignLibName s))-{-# INLINE foreignLibName #-}--foreignLibType :: Lens' ForeignLib ForeignLibType-foreignLibType f s = fmap (\x -> s { T.foreignLibType = x }) (f (T.foreignLibType s))-{-# INLINE foreignLibType #-}--foreignLibOptions :: Lens' ForeignLib [ForeignLibOption]-foreignLibOptions f s = fmap (\x -> s { T.foreignLibOptions = x }) (f (T.foreignLibOptions s))-{-# INLINE foreignLibOptions #-}--foreignLibBuildInfo :: Lens' ForeignLib BuildInfo-foreignLibBuildInfo f s = fmap (\x -> s { T.foreignLibBuildInfo = x }) (f (T.foreignLibBuildInfo s))-{-# INLINE foreignLibBuildInfo #-}--foreignLibVersionInfo :: Lens' ForeignLib (Maybe LibVersionInfo)-foreignLibVersionInfo f s = fmap (\x -> s { T.foreignLibVersionInfo = x }) (f (T.foreignLibVersionInfo s))-{-# INLINE foreignLibVersionInfo #-}--foreignLibVersionLinux :: Lens' ForeignLib (Maybe Version)-foreignLibVersionLinux f s = fmap (\x -> s { T.foreignLibVersionLinux = x }) (f (T.foreignLibVersionLinux s))-{-# INLINE foreignLibVersionLinux #-}--foreignLibModDefFile :: Lens' ForeignLib [FilePath]-foreignLibModDefFile f s = fmap (\x -> s { T.foreignLibModDefFile = x }) (f (T.foreignLibModDefFile s))-{-# INLINE foreignLibModDefFile #-}
− src/Distribution/Types/ForeignLibOption.hs
@@ -1,39 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}--module Distribution.Types.ForeignLibOption(- ForeignLibOption(..)-) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Pretty-import Distribution.Parsec--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp--data ForeignLibOption =- -- | Merge in all dependent libraries (i.e., use- -- @ghc -shared -static@ rather than just record- -- the dependencies, ala @ghc -shared -dynamic@).- -- This option is compulsory on Windows and unsupported- -- on other platforms.- ForeignLibStandalone- deriving (Generic, Show, Read, Eq, Typeable, Data)--instance Pretty ForeignLibOption where- pretty ForeignLibStandalone = Disp.text "standalone"--instance Parsec ForeignLibOption where- parsec = do- name <- P.munch1 (\c -> isAlphaNum c || c == '-')- case name of- "standalone" -> return ForeignLibStandalone- _ -> fail "unrecognized foreign-library option"--instance Binary ForeignLibOption-instance Structured ForeignLibOption-instance NFData ForeignLibOption where rnf = genericRnf
− src/Distribution/Types/ForeignLibType.hs
@@ -1,69 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}--module Distribution.Types.ForeignLibType(- ForeignLibType(..),- knownForeignLibTypes,- foreignLibTypeIsShared,-) where--import Prelude ()-import Distribution.Compat.Prelude-import Distribution.PackageDescription.Utils--import Distribution.Pretty-import Distribution.Parsec--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp---- | What kind of foreign library is to be built?-data ForeignLibType =- -- | A native shared library (@.so@ on Linux, @.dylib@ on OSX, or- -- @.dll@ on Windows).- ForeignLibNativeShared- -- | A native static library (not currently supported.)- | ForeignLibNativeStatic- -- TODO: Maybe this should record a string?- | ForeignLibTypeUnknown- deriving (Generic, Show, Read, Eq, Typeable, Data)--instance Pretty ForeignLibType where- pretty ForeignLibNativeShared = Disp.text "native-shared"- pretty ForeignLibNativeStatic = Disp.text "native-static"- pretty ForeignLibTypeUnknown = Disp.text "unknown"--instance Parsec ForeignLibType where- parsec = do- name <- P.munch1 (\c -> isAlphaNum c || c == '-')- return $ case name of- "native-shared" -> ForeignLibNativeShared- "native-static" -> ForeignLibNativeStatic- _ -> ForeignLibTypeUnknown--instance Binary ForeignLibType-instance Structured ForeignLibType-instance NFData ForeignLibType where rnf = genericRnf--instance Semigroup ForeignLibType where- ForeignLibTypeUnknown <> b = b- a <> ForeignLibTypeUnknown = a- _ <> _ = error "Ambiguous foreign library type"--instance Monoid ForeignLibType where- mempty = ForeignLibTypeUnknown- mappend = (<>)--knownForeignLibTypes :: [ForeignLibType]-knownForeignLibTypes = [- ForeignLibNativeShared- , ForeignLibNativeStatic- ]--foreignLibTypeIsShared :: ForeignLibType -> Bool-foreignLibTypeIsShared t =- case t of- ForeignLibNativeShared -> True- ForeignLibNativeStatic -> False- ForeignLibTypeUnknown -> cabalBug "Unknown foreign library type"
− src/Distribution/Types/GenericPackageDescription.hs
@@ -1,105 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE ScopedTypeVariables #-}--module Distribution.Types.GenericPackageDescription (- GenericPackageDescription(..),- emptyGenericPackageDescription,-) where--import Prelude ()-import Distribution.Compat.Prelude---- lens-import Distribution.Compat.Lens as L-import qualified Distribution.Types.BuildInfo.Lens as L--import Distribution.Types.PackageDescription--import Distribution.Types.Benchmark-import Distribution.Types.CondTree-import Distribution.Types.ConfVar-import Distribution.Types.Dependency-import Distribution.Types.Executable-import Distribution.Types.Flag-import Distribution.Types.ForeignLib-import Distribution.Types.Library-import Distribution.Types.TestSuite-import Distribution.Types.UnqualComponentName-import Distribution.Package-import Distribution.Version---- ------------------------------------------------------------------------------ The 'GenericPackageDescription' type--data GenericPackageDescription =- GenericPackageDescription- { packageDescription :: PackageDescription- , gpdScannedVersion :: Maybe Version- -- ^ This is a version as specified in source.- -- We populate this field in index reading for dummy GPDs,- -- only when GPD reading failed, but scanning haven't.- --- -- Cabal-the-library never produces GPDs with Just as gpdScannedVersion.- --- -- Perfectly, PackageIndex should have sum type, so we don't need to- -- have dummy GPDs.- , genPackageFlags :: [PackageFlag]- , condLibrary :: Maybe (CondTree ConfVar [Dependency] Library)- , condSubLibraries :: [( UnqualComponentName- , CondTree ConfVar [Dependency] Library )]- , condForeignLibs :: [( UnqualComponentName- , CondTree ConfVar [Dependency] ForeignLib )]- , condExecutables :: [( UnqualComponentName- , CondTree ConfVar [Dependency] Executable )]- , condTestSuites :: [( UnqualComponentName- , CondTree ConfVar [Dependency] TestSuite )]- , condBenchmarks :: [( UnqualComponentName- , CondTree ConfVar [Dependency] Benchmark )]- }- deriving (Show, Eq, Typeable, Data, Generic)--instance Package GenericPackageDescription where- packageId = packageId . packageDescription--instance Binary GenericPackageDescription-instance Structured GenericPackageDescription-instance NFData GenericPackageDescription where rnf = genericRnf--emptyGenericPackageDescription :: GenericPackageDescription-emptyGenericPackageDescription = GenericPackageDescription emptyPackageDescription Nothing [] Nothing [] [] [] [] []---- -------------------------------------------------------------------------------- Traversal Instances--instance L.HasBuildInfos GenericPackageDescription where- traverseBuildInfos f (GenericPackageDescription p v a1 x1 x2 x3 x4 x5 x6) =- GenericPackageDescription- <$> L.traverseBuildInfos f p- <*> pure v- <*> pure a1- <*> (traverse . traverseCondTreeBuildInfo) f x1- <*> (traverse . L._2 . traverseCondTreeBuildInfo) f x2- <*> (traverse . L._2 . traverseCondTreeBuildInfo) f x3- <*> (traverse . L._2 . traverseCondTreeBuildInfo) f x4- <*> (traverse . L._2 . traverseCondTreeBuildInfo) f x5- <*> (traverse . L._2 . traverseCondTreeBuildInfo) f x6- where---- We use this traversal to keep [Dependency] field in CondTree up to date.-traverseCondTreeBuildInfo- :: forall f comp v. (Applicative f, L.HasBuildInfo comp)- => LensLike' f (CondTree v [Dependency] comp) L.BuildInfo-traverseCondTreeBuildInfo g = node where- mkCondNode :: comp -> [CondBranch v [Dependency] comp] -> CondTree v [Dependency] comp- mkCondNode comp branches = CondNode comp (view L.targetBuildDepends comp) branches-- node (CondNode comp _ branches) = mkCondNode- <$> L.buildInfo g comp- <*> traverse branch branches-- branch (CondBranch v x y) = CondBranch v- <$> node x- <*> traverse node y
− src/Distribution/Types/GenericPackageDescription/Lens.hs
@@ -1,131 +0,0 @@-{-# LANGUAGE Rank2Types #-}-module Distribution.Types.GenericPackageDescription.Lens (- GenericPackageDescription,- PackageFlag,- FlagName,- ConfVar (..),- module Distribution.Types.GenericPackageDescription.Lens,- ) where--import Prelude()-import Distribution.Compat.Prelude-import Distribution.Compat.Lens--import qualified Distribution.Types.GenericPackageDescription as T---- We import types from their packages, so we can remove unused imports--- and have wider inter-module dependency graph-import Distribution.Types.CondTree (CondTree)-import Distribution.Types.Dependency (Dependency)-import Distribution.Types.Executable (Executable)-import Distribution.Types.PackageDescription (PackageDescription)-import Distribution.Types.Benchmark (Benchmark)-import Distribution.Types.ForeignLib (ForeignLib)-import Distribution.Types.GenericPackageDescription (GenericPackageDescription(GenericPackageDescription) )-import Distribution.Types.Flag (PackageFlag(MkPackageFlag), FlagName)-import Distribution.Types.ConfVar (ConfVar (..))-import Distribution.Types.Library (Library)-import Distribution.Types.TestSuite (TestSuite)-import Distribution.Types.UnqualComponentName (UnqualComponentName)-import Distribution.System (Arch, OS)-import Distribution.Compiler (CompilerFlavor)-import Distribution.Version (Version, VersionRange)------------------------------------------------------------------------------------ GenericPackageDescription----------------------------------------------------------------------------------packageDescription :: Lens' GenericPackageDescription PackageDescription-packageDescription f s = fmap (\x -> s { T.packageDescription = x }) (f (T.packageDescription s))-{-# INLINE packageDescription #-}--gpdScannedVersion :: Lens' GenericPackageDescription (Maybe Version)-gpdScannedVersion f s = fmap (\x -> s { T.gpdScannedVersion = x }) (f (T.gpdScannedVersion s))-{-# INLINE gpdScannedVersion #-}--genPackageFlags :: Lens' GenericPackageDescription [PackageFlag]-genPackageFlags f s = fmap (\x -> s { T.genPackageFlags = x }) (f (T.genPackageFlags s))-{-# INLINE genPackageFlags #-}--condLibrary :: Lens' GenericPackageDescription (Maybe (CondTree ConfVar [Dependency] Library))-condLibrary f s = fmap (\x -> s { T.condLibrary = x }) (f (T.condLibrary s))-{-# INLINE condLibrary #-}--condSubLibraries :: Lens' GenericPackageDescription [(UnqualComponentName,(CondTree ConfVar [Dependency] Library))]-condSubLibraries f s = fmap (\x -> s { T.condSubLibraries = x }) (f (T.condSubLibraries s))-{-# INLINE condSubLibraries #-}--condForeignLibs :: Lens' GenericPackageDescription [(UnqualComponentName,(CondTree ConfVar [Dependency] ForeignLib))]-condForeignLibs f s = fmap (\x -> s { T.condForeignLibs = x }) (f (T.condForeignLibs s))-{-# INLINE condForeignLibs #-}--condExecutables :: Lens' GenericPackageDescription [(UnqualComponentName,(CondTree ConfVar [Dependency] Executable))]-condExecutables f s = fmap (\x -> s { T.condExecutables = x }) (f (T.condExecutables s))-{-# INLINE condExecutables #-}--condTestSuites :: Lens' GenericPackageDescription [(UnqualComponentName,(CondTree ConfVar [Dependency] TestSuite))]-condTestSuites f s = fmap (\x -> s { T.condTestSuites = x }) (f (T.condTestSuites s))-{-# INLINE condTestSuites #-}--condBenchmarks :: Lens' GenericPackageDescription [(UnqualComponentName,(CondTree ConfVar [Dependency] Benchmark))]-condBenchmarks f s = fmap (\x -> s { T.condBenchmarks = x }) (f (T.condBenchmarks s))-{-# INLINE condBenchmarks #-}--allCondTrees- :: Applicative f- => (forall a. CondTree ConfVar [Dependency] a- -> f (CondTree ConfVar [Dependency] a))- -> GenericPackageDescription- -> f GenericPackageDescription-allCondTrees f (GenericPackageDescription p v a1 x1 x2 x3 x4 x5 x6) =- GenericPackageDescription- <$> pure p- <*> pure v- <*> pure a1- <*> traverse f x1- <*> (traverse . _2) f x2- <*> (traverse . _2) f x3- <*> (traverse . _2) f x4- <*> (traverse . _2) f x5- <*> (traverse . _2) f x6------------------------------------------------------------------------------------- Flag----------------------------------------------------------------------------------flagName :: Lens' PackageFlag FlagName-flagName f (MkPackageFlag x1 x2 x3 x4) = fmap (\y1 -> MkPackageFlag y1 x2 x3 x4) (f x1)-{-# INLINE flagName #-}--flagDescription :: Lens' PackageFlag String-flagDescription f (MkPackageFlag x1 x2 x3 x4) = fmap (\y1 -> MkPackageFlag x1 y1 x3 x4) (f x2)-{-# INLINE flagDescription #-}--flagDefault :: Lens' PackageFlag Bool-flagDefault f (MkPackageFlag x1 x2 x3 x4) = fmap (\y1 -> MkPackageFlag x1 x2 y1 x4) (f x3)-{-# INLINE flagDefault #-}--flagManual :: Lens' PackageFlag Bool-flagManual f (MkPackageFlag x1 x2 x3 x4) = fmap (\y1 -> MkPackageFlag x1 x2 x3 y1) (f x4)-{-# INLINE flagManual #-}------------------------------------------------------------------------------------ ConfVar----------------------------------------------------------------------------------_OS :: Traversal' ConfVar OS-_OS f (OS os) = OS <$> f os-_OS _ x = pure x--_Arch :: Traversal' ConfVar Arch-_Arch f (Arch arch) = Arch <$> f arch-_Arch _ x = pure x--_PackageFlag :: Traversal' ConfVar FlagName-_PackageFlag f (PackageFlag flag) = PackageFlag <$> f flag-_PackageFlag _ x = pure x--_Impl :: Traversal' ConfVar (CompilerFlavor, VersionRange)-_Impl f (Impl cf vr) = uncurry Impl <$> f (cf, vr)-_Impl _ x = pure x
src/Distribution/Types/GivenComponent.hs view
@@ -1,13 +1,15 @@-{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-}-module Distribution.Types.GivenComponent (- GivenComponent(..)-) where +module Distribution.Types.GivenComponent+ ( GivenComponent (..)+ , PromisedComponent (..)+ ) where+ import Distribution.Compat.Prelude import Distribution.Types.ComponentId import Distribution.Types.LibraryName+import Distribution.Types.PackageId import Distribution.Types.PackageName -- | A 'GivenComponent' represents a library depended on and explicitly@@ -16,13 +18,30 @@ -- It enables Cabal to know which 'ComponentId' to associate with a library -- -- @since 2.3.0.0-data GivenComponent =- GivenComponent- { givenComponentPackage :: PackageName- , givenComponentName :: LibraryName -- --dependency is for libraries- -- only, not for any component- , givenComponentId :: ComponentId }- deriving (Generic, Read, Show, Eq, Typeable)+data GivenComponent = GivenComponent+ { givenComponentPackage :: PackageName+ , givenComponentName :: LibraryName -- --dependency is for libraries+ -- only, not for any component+ , givenComponentId :: ComponentId+ }+ deriving (Generic, Read, Show, Eq) instance Binary GivenComponent instance Structured GivenComponent++-- | A 'PromisedComponent' represents a promised library depended on and explicitly+-- specified by the user/client with @--promised-dependency@+--+-- It enables Cabal to know which 'ComponentId' to associate with a library+--+-- @since 3.14.0.0+data PromisedComponent = PromisedComponent+ { promisedComponentPackage :: PackageId+ , promisedComponentName :: LibraryName -- --dependency is for libraries+ -- only, not for any component+ , promisedComponentId :: ComponentId+ }+ deriving (Generic, Read, Show, Eq)++instance Binary PromisedComponent+instance Structured PromisedComponent
− src/Distribution/Types/HookedBuildInfo.hs
@@ -1,66 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Distribution.Types.HookedBuildInfo (- HookedBuildInfo,- emptyHookedBuildInfo,- ) where---- import Distribution.Compat.Prelude-import Distribution.Types.BuildInfo-import Distribution.Types.UnqualComponentName---- | 'HookedBuildInfo' is mechanism that hooks can use to--- override the 'BuildInfo's inside packages. One example--- use-case (which is used in core libraries today) is as--- a way of passing flags which are computed by a configure--- script into Cabal. In this case, the autoconf build type adds--- hooks to read in a textual 'HookedBuildInfo' format prior--- to doing any operations.------ Quite honestly, this mechanism is a massive hack since we shouldn't--- be editing the 'PackageDescription' data structure (it's easy--- to assume that this data structure shouldn't change and--- run into bugs, see for example 1c20a6328579af9e37677d507e2e9836ef70ab9d).--- But it's a bit convenient, because there isn't another data--- structure that allows adding extra 'BuildInfo' style things.------ In any case, a lot of care has to be taken to make sure the--- 'HookedBuildInfo' is applied to the 'PackageDescription'. In--- general this process occurs in "Distribution.Simple", which is--- responsible for orchestrating the hooks mechanism. The--- general strategy:------ 1. We run the pre-hook, which produces a 'HookedBuildInfo'--- (e.g., in the Autoconf case, it reads it out from a file).--- 2. We sanity-check the hooked build info with--- 'sanityCheckHookedBuildInfo'.--- 3. We update our 'PackageDescription' (either freshly read--- or cached from 'LocalBuildInfo') with 'updatePackageDescription'.------ In principle, we are also supposed to update the copy of--- the 'PackageDescription' stored in 'LocalBuildInfo'--- at 'localPkgDescr'. Unfortunately, in practice, there--- are lots of Custom setup scripts which fail to update--- 'localPkgDescr' so you really shouldn't rely on it.--- It's not DEPRECATED because there are legitimate uses--- for it, but... yeah. Sharp knife. See--- <https://github.com/haskell/cabal/issues/3606>--- for more information on the issue.------ It is not well-specified whether or not a 'HookedBuildInfo' applied--- at configure time is persistent to the 'LocalBuildInfo'. The--- fact that 'HookedBuildInfo' is passed to 'confHook' MIGHT SUGGEST--- that the 'HookedBuildInfo' is applied at this time, but actually--- since 9317b67e6122ab14e53f81b573bd0ecb388eca5a it has been ONLY used--- to create a modified package description that we check for problems:--- it is never actually saved to the LBI. Since 'HookedBuildInfo' is--- applied monoidally to the existing build infos (and it is not an--- idempotent monoid), it could break things to save it, since we--- are obligated to apply any new 'HookedBuildInfo' and then we'd--- get the effect twice. But this does mean we have to re-apply--- it every time. Hey, it's more flexibility.-type HookedBuildInfo = (Maybe BuildInfo, [(UnqualComponentName, BuildInfo)])--emptyHookedBuildInfo :: HookedBuildInfo-emptyHookedBuildInfo = (Nothing, [])
− src/Distribution/Types/IncludeRenaming.hs
@@ -1,68 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Distribution.Types.IncludeRenaming (- IncludeRenaming(..),- defaultIncludeRenaming,- isDefaultIncludeRenaming,-) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Types.ModuleRenaming--import qualified Distribution.Compat.CharParsing as P-import Distribution.Parsec-import Distribution.Pretty-import Text.PrettyPrint (text)-import qualified Text.PrettyPrint as Disp---- ------------------------------------------------------------------------------ Module renaming---- | A renaming on an include: (provides renaming, requires renaming)-data IncludeRenaming- = IncludeRenaming {- includeProvidesRn :: ModuleRenaming,- includeRequiresRn :: ModuleRenaming- }- deriving (Show, Read, Eq, Ord, Typeable, Data, Generic)--instance Binary IncludeRenaming-instance Structured IncludeRenaming--instance NFData IncludeRenaming where rnf = genericRnf---- | The 'defaultIncludeRenaming' applied when you only @build-depends@--- on a package.-defaultIncludeRenaming :: IncludeRenaming-defaultIncludeRenaming = IncludeRenaming defaultRenaming defaultRenaming---- | Is an 'IncludeRenaming' the default one?-isDefaultIncludeRenaming :: IncludeRenaming -> Bool-isDefaultIncludeRenaming (IncludeRenaming p r) = isDefaultRenaming p && isDefaultRenaming r--instance Pretty IncludeRenaming where- pretty (IncludeRenaming prov_rn req_rn) =- pretty prov_rn- <+> (if isDefaultRenaming req_rn- then Disp.empty- else text "requires" <+> pretty req_rn)--instance Parsec IncludeRenaming where- parsec = do- prov_rn <- parsec- req_rn <- P.option defaultRenaming $ P.try $ do- P.spaces -- no need to be space- _ <- P.string "requires"- P.spaces- parsec- -- Requirements don't really care if they're mentioned- -- or not (since you can't thin a requirement.) But- -- we have a little hack in Configure to combine- -- the provisions and requirements together before passing- -- them to GHC, and so the most neutral choice for a requirement- -- is for the "with" field to be False, so we correctly- -- thin provisions.- return (IncludeRenaming prov_rn req_rn)
− src/Distribution/Types/InstalledPackageInfo.hs
@@ -1,173 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE OverloadedStrings #-}-module Distribution.Types.InstalledPackageInfo (- InstalledPackageInfo (..),- emptyInstalledPackageInfo,- mungedPackageId,- mungedPackageName,- AbiDependency (..),- ExposedModule (..),- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Backpack-import Distribution.Compat.Graph (IsNode (..))-import Distribution.License-import Distribution.ModuleName-import Distribution.Package hiding (installedUnitId)-import Distribution.Types.AbiDependency-import Distribution.Types.ExposedModule-import Distribution.Types.LibraryName-import Distribution.Types.LibraryVisibility-import Distribution.Types.MungedPackageId-import Distribution.Types.MungedPackageName-import Distribution.Version (nullVersion)-import Distribution.Utils.ShortText (ShortText)--import qualified Distribution.Package as Package-import qualified Distribution.SPDX as SPDX---- -------------------------------------------------------------------------------- The InstalledPackageInfo type---- For BC reasons, we continue to name this record an InstalledPackageInfo;--- but it would more accurately be called an InstalledUnitInfo with Backpack-data InstalledPackageInfo- = InstalledPackageInfo {- -- these parts (sourcePackageId, installedUnitId) are- -- exactly the same as PackageDescription- sourcePackageId :: PackageId,- sourceLibName :: LibraryName,- installedComponentId_ :: ComponentId,- libVisibility :: LibraryVisibility,- installedUnitId :: UnitId,- -- INVARIANT: if this package is definite, OpenModule's- -- OpenUnitId directly records UnitId. If it is- -- indefinite, OpenModule is always an OpenModuleVar- -- with the same ModuleName as the key.- instantiatedWith :: [(ModuleName, OpenModule)],- compatPackageKey :: String,- license :: Either SPDX.License License,- copyright :: !ShortText,- maintainer :: !ShortText,- author :: !ShortText,- stability :: !ShortText,- homepage :: !ShortText,- pkgUrl :: !ShortText,- synopsis :: !ShortText,- description :: !ShortText,- category :: !ShortText,- -- these parts are required by an installed package only:- abiHash :: AbiHash,- indefinite :: Bool,- exposed :: Bool,- -- INVARIANT: if the package is definite, OpenModule's- -- OpenUnitId directly records UnitId.- exposedModules :: [ExposedModule],- hiddenModules :: [ModuleName],- trusted :: Bool,- importDirs :: [FilePath],- libraryDirs :: [FilePath],- libraryDynDirs :: [FilePath], -- ^ overrides 'libraryDirs'- dataDir :: FilePath,- hsLibraries :: [String],- extraLibraries :: [String],- extraGHCiLibraries:: [String], -- overrides extraLibraries for GHCi- includeDirs :: [FilePath],- includes :: [String],- -- INVARIANT: if the package is definite, UnitId is NOT- -- a ComponentId of an indefinite package- depends :: [UnitId],- abiDepends :: [AbiDependency],- ccOptions :: [String],- cxxOptions :: [String],- ldOptions :: [String],- frameworkDirs :: [FilePath],- frameworks :: [String],- haddockInterfaces :: [FilePath],- haddockHTMLs :: [FilePath],- pkgRoot :: Maybe FilePath- }- deriving (Eq, Generic, Typeable, Read, Show)--instance Binary InstalledPackageInfo-instance Structured InstalledPackageInfo--instance NFData InstalledPackageInfo where rnf = genericRnf--instance Package.HasMungedPackageId InstalledPackageInfo where- mungedId = mungedPackageId--instance Package.Package InstalledPackageInfo where- packageId = sourcePackageId--instance Package.HasUnitId InstalledPackageInfo where- installedUnitId = installedUnitId--instance Package.PackageInstalled InstalledPackageInfo where- installedDepends = depends--instance IsNode InstalledPackageInfo where- type Key InstalledPackageInfo = UnitId- nodeKey = installedUnitId- nodeNeighbors = depends--mungedPackageId :: InstalledPackageInfo -> MungedPackageId-mungedPackageId ipi =- MungedPackageId (mungedPackageName ipi) (packageVersion ipi)---- | Returns the munged package name, which we write into @name@ for--- compatibility with old versions of GHC.-mungedPackageName :: InstalledPackageInfo -> MungedPackageName-mungedPackageName ipi = MungedPackageName (packageName ipi) (sourceLibName ipi)--emptyInstalledPackageInfo :: InstalledPackageInfo-emptyInstalledPackageInfo- = InstalledPackageInfo {- sourcePackageId = PackageIdentifier (mkPackageName "") nullVersion,- sourceLibName = LMainLibName,- installedComponentId_ = mkComponentId "",- installedUnitId = mkUnitId "",- instantiatedWith = [],- compatPackageKey = "",- license = Left SPDX.NONE,- copyright = "",- maintainer = "",- author = "",- stability = "",- homepage = "",- pkgUrl = "",- synopsis = "",- description = "",- category = "",- abiHash = mkAbiHash "",- indefinite = False,- exposed = False,- exposedModules = [],- hiddenModules = [],- trusted = False,- importDirs = [],- libraryDirs = [],- libraryDynDirs = [],- dataDir = "",- hsLibraries = [],- extraLibraries = [],- extraGHCiLibraries= [],- includeDirs = [],- includes = [],- depends = [],- abiDepends = [],- ccOptions = [],- cxxOptions = [],- ldOptions = [],- frameworkDirs = [],- frameworks = [],- haddockInterfaces = [],- haddockHTMLs = [],- pkgRoot = Nothing,- libVisibility = LibraryVisibilityPrivate- }
− src/Distribution/Types/InstalledPackageInfo/FieldGrammar.hs
@@ -1,317 +0,0 @@-{-# LANGUAGE ConstraintKinds #-}-{-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards #-}-module Distribution.Types.InstalledPackageInfo.FieldGrammar (- ipiFieldGrammar,- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Backpack-import Distribution.CabalSpecVersion-import Distribution.Compat.Lens (Lens', (&), (.~))-import Distribution.Compat.Newtype-import Distribution.FieldGrammar-import Distribution.FieldGrammar.FieldDescrs-import Distribution.License-import Distribution.ModuleName-import Distribution.Package-import Distribution.Parsec-import Distribution.Pretty-import Distribution.Types.LibraryName-import Distribution.Types.LibraryVisibility-import Distribution.Types.MungedPackageName-import Distribution.Types.UnqualComponentName-import Distribution.Version--import qualified Data.Char as Char-import qualified Data.Map as Map-import qualified Distribution.Compat.CharParsing as P-import qualified Distribution.SPDX as SPDX-import qualified Text.PrettyPrint as Disp--import Distribution.Types.InstalledPackageInfo--import qualified Distribution.Types.InstalledPackageInfo.Lens as L-import qualified Distribution.Types.PackageId.Lens as L---- Note: GHC goes nuts and inlines everything,--- One can see e.g. in -ddump-simpl-stats:------ 34886 KnownBranch--- 8197 wild1_ixF0------ https://gitlab.haskell.org/ghc/ghc/-/issues/13253 might be the cause.------ The workaround is to prevent GHC optimising the code:-infixl 4 <@>-(<@>) :: Applicative f => f (a -> b) -> f a -> f b-f <@> x = f <*> x-{-# NOINLINE (<@>) #-}--ipiFieldGrammar- :: ( FieldGrammar c g, Applicative (g InstalledPackageInfo), Applicative (g Basic)- , c (Identity AbiHash)- , c (Identity LibraryVisibility)- , c (Identity PackageName)- , c (Identity UnitId)- , c (Identity UnqualComponentName)- , c (List FSep (Identity AbiDependency) AbiDependency)- , c (List FSep (Identity UnitId) UnitId)- , c (List FSep (MQuoted ModuleName) ModuleName)- , c (List FSep FilePathNT String)- , c (List FSep Token String)- , c (MQuoted MungedPackageName)- , c (MQuoted Version)- , c CompatPackageKey- , c ExposedModules- , c InstWith- , c SpecLicenseLenient- )- => g InstalledPackageInfo InstalledPackageInfo-ipiFieldGrammar = mkInstalledPackageInfo- -- Deprecated fields- <$> monoidalFieldAla "hugs-options" (alaList' FSep Token) unitedList- --- https://github.com/haskell/cabal/commit/40f3601e17024f07e0da8e64d3dd390177ce908b- ^^^ deprecatedSince CabalSpecV1_22 "hugs isn't supported anymore"- -- Very basic fields: name, version, package-name, lib-name and visibility- <@> blurFieldGrammar basic basicFieldGrammar- -- Basic fields- <@> optionalFieldDef "id" L.installedUnitId (mkUnitId "")- <@> optionalFieldDefAla "instantiated-with" InstWith L.instantiatedWith []- <@> optionalFieldDefAla "key" CompatPackageKey L.compatPackageKey ""- <@> optionalFieldDefAla "license" SpecLicenseLenient L.license (Left SPDX.NONE)- <@> freeTextFieldDefST "copyright" L.copyright- <@> freeTextFieldDefST "maintainer" L.maintainer- <@> freeTextFieldDefST "author" L.author- <@> freeTextFieldDefST "stability" L.stability- <@> freeTextFieldDefST "homepage" L.homepage- <@> freeTextFieldDefST "package-url" L.pkgUrl- <@> freeTextFieldDefST "synopsis" L.synopsis- <@> freeTextFieldDefST "description" L.description- <@> freeTextFieldDefST "category" L.category- -- Installed fields- <@> optionalFieldDef "abi" L.abiHash (mkAbiHash "")- <@> booleanFieldDef "indefinite" L.indefinite False- <@> booleanFieldDef "exposed" L.exposed False- <@> monoidalFieldAla "exposed-modules" ExposedModules L.exposedModules- <@> monoidalFieldAla "hidden-modules" (alaList' FSep MQuoted) L.hiddenModules- <@> booleanFieldDef "trusted" L.trusted False- <@> monoidalFieldAla "import-dirs" (alaList' FSep FilePathNT) L.importDirs- <@> monoidalFieldAla "library-dirs" (alaList' FSep FilePathNT) L.libraryDirs- <@> monoidalFieldAla "dynamic-library-dirs" (alaList' FSep FilePathNT) L.libraryDynDirs- <@> optionalFieldDefAla "data-dir" FilePathNT L.dataDir ""- <@> monoidalFieldAla "hs-libraries" (alaList' FSep Token) L.hsLibraries- <@> monoidalFieldAla "extra-libraries" (alaList' FSep Token) L.extraLibraries- <@> monoidalFieldAla "extra-ghci-libraries" (alaList' FSep Token) L.extraGHCiLibraries- <@> monoidalFieldAla "include-dirs" (alaList' FSep FilePathNT) L.includeDirs- <@> monoidalFieldAla "includes" (alaList' FSep FilePathNT) L.includes- <@> monoidalFieldAla "depends" (alaList FSep) L.depends- <@> monoidalFieldAla "abi-depends" (alaList FSep) L.abiDepends- <@> monoidalFieldAla "cc-options" (alaList' FSep Token) L.ccOptions- <@> monoidalFieldAla "cxx-options" (alaList' FSep Token) L.cxxOptions- <@> monoidalFieldAla "ld-options" (alaList' FSep Token) L.ldOptions- <@> monoidalFieldAla "framework-dirs" (alaList' FSep FilePathNT) L.frameworkDirs- <@> monoidalFieldAla "frameworks" (alaList' FSep Token) L.frameworks- <@> monoidalFieldAla "haddock-interfaces" (alaList' FSep FilePathNT) L.haddockInterfaces- <@> monoidalFieldAla "haddock-html" (alaList' FSep FilePathNT) L.haddockHTMLs- <@> optionalFieldAla "pkgroot" FilePathNT L.pkgRoot- where- mkInstalledPackageInfo _ Basic {..} = InstalledPackageInfo- -- _basicPkgName is not used- -- setMaybePackageId says it can be no-op.- (PackageIdentifier pn _basicVersion)- (combineLibraryName ln _basicLibName)- (mkComponentId "") -- installedComponentId_, not in use- _basicLibVisibility- where- MungedPackageName pn ln = _basicName-{-# SPECIALIZE ipiFieldGrammar :: FieldDescrs InstalledPackageInfo InstalledPackageInfo #-}-{-# SPECIALIZE ipiFieldGrammar :: ParsecFieldGrammar InstalledPackageInfo InstalledPackageInfo #-}-{-# SPECIALIZE ipiFieldGrammar :: PrettyFieldGrammar InstalledPackageInfo InstalledPackageInfo #-}---- (forall b. [b]) ~ ()-unitedList :: Lens' a [b]-unitedList f s = s <$ f []------------------------------------------------------------------------------------ Helper functions------------------------------------------------------------------------------------ | Combine 'LibraryName'. in parsing we prefer value coming--- from munged @name@ field over the @lib-name@.------ /Should/ be irrelevant.-combineLibraryName :: LibraryName -> LibraryName -> LibraryName-combineLibraryName l@(LSubLibName _) _ = l-combineLibraryName _ l = l---- To maintain backwards-compatibility, we accept both comma/non-comma--- separated variants of this field. You SHOULD use the comma syntax if you--- use any new functions, although actually it's unambiguous due to a quirk--- of the fact that modules must start with capital letters.--showExposedModules :: [ExposedModule] -> Disp.Doc-showExposedModules xs- | all isExposedModule xs = Disp.fsep (map pretty xs)- | otherwise = Disp.fsep (Disp.punctuate Disp.comma (map pretty xs))- where isExposedModule (ExposedModule _ Nothing) = True- isExposedModule _ = False---- | Setter for the @package-name@ field. It should be acceptable for this--- to be a no-op.-setMaybePackageName :: Maybe PackageName -> InstalledPackageInfo -> InstalledPackageInfo-setMaybePackageName Nothing ipi = ipi-setMaybePackageName (Just pn) ipi = ipi- { sourcePackageId = (sourcePackageId ipi) {pkgName=pn}- }--setMungedPackageName :: MungedPackageName -> InstalledPackageInfo -> InstalledPackageInfo-setMungedPackageName (MungedPackageName pn ln) ipi = ipi- { sourcePackageId = (sourcePackageId ipi) {pkgName=pn}- , sourceLibName = ln- }----- | Returns @Just@ if the @name@ field of the IPI record would not contain---- the package name verbatim. This helps us avoid writing @package-name@---- when it's redundant.-maybePackageName :: InstalledPackageInfo -> Maybe PackageName-maybePackageName ipi = case sourceLibName ipi of- LMainLibName -> Nothing- LSubLibName _ -> Just (packageName ipi)------------------------------------------------------------------------------------ Auxiliary types----------------------------------------------------------------------------------newtype ExposedModules = ExposedModules { getExposedModules :: [ExposedModule] }--instance Newtype [ExposedModule] ExposedModules--instance Parsec ExposedModules where- parsec = ExposedModules <$> parsecOptCommaList parsec--instance Pretty ExposedModules where- pretty = showExposedModules . getExposedModules--newtype CompatPackageKey = CompatPackageKey { getCompatPackageKey :: String }--instance Newtype String CompatPackageKey--instance Pretty CompatPackageKey where- pretty = Disp.text . getCompatPackageKey--instance Parsec CompatPackageKey where- parsec = CompatPackageKey <$> P.munch1 uid_char where- uid_char c = Char.isAlphaNum c || c `elem` ("-_.=[],:<>+" :: String)--newtype InstWith = InstWith { getInstWith :: [(ModuleName,OpenModule)] }--instance Newtype [(ModuleName, OpenModule)] InstWith--instance Pretty InstWith where- pretty = dispOpenModuleSubst . Map.fromList . getInstWith--instance Parsec InstWith where- parsec = InstWith . Map.toList <$> parsecOpenModuleSubst---- | SPDX License expression or legacy license. Lenient parser, accepts either.-newtype SpecLicenseLenient = SpecLicenseLenient { getSpecLicenseLenient :: Either SPDX.License License }--instance Newtype (Either SPDX.License License) SpecLicenseLenient--instance Parsec SpecLicenseLenient where- parsec = fmap SpecLicenseLenient $ Left <$> P.try parsec <|> Right <$> parsec--instance Pretty SpecLicenseLenient where- pretty = either pretty pretty . getSpecLicenseLenient------------------------------------------------------------------------------------ Basic fields------------------------------------------------------------------------------------ | This type is used to mangle fields as--- in serialised textual representation--- to the actual 'InstalledPackageInfo' fields.-data Basic = Basic- { _basicName :: MungedPackageName- , _basicVersion :: Version- , _basicPkgName :: Maybe PackageName- , _basicLibName :: LibraryName- , _basicLibVisibility :: LibraryVisibility- }--basic :: Lens' InstalledPackageInfo Basic-basic f ipi = g <$> f b- where- b = Basic- (mungedPackageName ipi)- (packageVersion ipi)- (maybePackageName ipi)- (sourceLibName ipi)- (libVisibility ipi)-- g (Basic n v pn ln lv) = ipi- & setMungedPackageName n- & L.sourcePackageId . L.pkgVersion .~ v- & setMaybePackageName pn- & L.sourceLibName .~ ln- & L.libVisibility .~ lv--basicName :: Lens' Basic MungedPackageName-basicName f b = (\x -> b { _basicName = x }) <$> f (_basicName b)-{-# INLINE basicName #-}--basicVersion :: Lens' Basic Version-basicVersion f b = (\x -> b { _basicVersion = x }) <$> f (_basicVersion b)-{-# INLINE basicVersion #-}--basicPkgName :: Lens' Basic (Maybe PackageName)-basicPkgName f b = (\x -> b { _basicPkgName = x }) <$> f (_basicPkgName b)-{-# INLINE basicPkgName #-}--basicLibName :: Lens' Basic (Maybe UnqualComponentName)-basicLibName f b = (\x -> b { _basicLibName = maybeToLibraryName x }) <$>- f (libraryNameString (_basicLibName b))-{-# INLINE basicLibName #-}--basicLibVisibility :: Lens' Basic LibraryVisibility-basicLibVisibility f b = (\x -> b { _basicLibVisibility = x }) <$>- f (_basicLibVisibility b)-{-# INLINE basicLibVisibility #-}--basicFieldGrammar- :: ( FieldGrammar c g, Applicative (g Basic)- , c (Identity LibraryVisibility)- , c (Identity PackageName)- , c (Identity UnqualComponentName)- , c (MQuoted MungedPackageName)- , c (MQuoted Version)- )- => g Basic Basic-basicFieldGrammar = mkBasic- <$> optionalFieldDefAla "name" MQuoted basicName (mungedPackageName emptyInstalledPackageInfo)- <*> optionalFieldDefAla "version" MQuoted basicVersion nullVersion- <*> optionalField "package-name" basicPkgName- <*> optionalField "lib-name" basicLibName- <*> optionalFieldDef "visibility" basicLibVisibility LibraryVisibilityPrivate- where- mkBasic n v pn ln lv = Basic n v pn ln' lv'- where- ln' = maybe LMainLibName LSubLibName ln- -- Older GHCs (<8.8) always report installed libraries as private- -- because their ghc-pkg builds with an older Cabal.- -- So we always set LibraryVisibilityPublic for main (unnamed) libs.- -- This can be removed once we stop supporting GHC<8.8, at the- -- condition that we keep marking main libraries as public when- -- registering them.- lv' = if- let MungedPackageName _ mln = n in- -- We need to check both because on ghc<8.2 ln' will always- -- be LMainLibName- ln' == LMainLibName && mln == LMainLibName- then LibraryVisibilityPublic- else lv
− src/Distribution/Types/InstalledPackageInfo/Lens.hs
@@ -1,190 +0,0 @@-module Distribution.Types.InstalledPackageInfo.Lens (- InstalledPackageInfo,- module Distribution.Types.InstalledPackageInfo.Lens- ) where--import Distribution.Compat.Lens-import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Backpack (OpenModule)-import Distribution.License (License)-import Distribution.ModuleName (ModuleName)-import Distribution.Package (AbiHash, ComponentId, PackageIdentifier, UnitId)-import Distribution.Types.InstalledPackageInfo (AbiDependency, ExposedModule, InstalledPackageInfo)-import Distribution.Types.LibraryName (LibraryName)-import Distribution.Types.LibraryVisibility (LibraryVisibility)-import Distribution.Utils.ShortText (ShortText)---import qualified Distribution.SPDX as SPDX-import qualified Distribution.Types.InstalledPackageInfo as T--sourcePackageId :: Lens' InstalledPackageInfo PackageIdentifier-sourcePackageId f s = fmap (\x -> s { T.sourcePackageId = x }) (f (T.sourcePackageId s))-{-# INLINE sourcePackageId #-}--installedUnitId :: Lens' InstalledPackageInfo UnitId-installedUnitId f s = fmap (\x -> s { T.installedUnitId = x }) (f (T.installedUnitId s))-{-# INLINE installedUnitId #-}--installedComponentId_ :: Lens' InstalledPackageInfo ComponentId-installedComponentId_ f s = fmap (\x -> s { T.installedComponentId_ = x }) (f (T.installedComponentId_ s))-{-# INLINE installedComponentId_ #-}--instantiatedWith :: Lens' InstalledPackageInfo [(ModuleName,OpenModule)]-instantiatedWith f s = fmap (\x -> s { T.instantiatedWith = x }) (f (T.instantiatedWith s))-{-# INLINE instantiatedWith #-}--sourceLibName :: Lens' InstalledPackageInfo LibraryName-sourceLibName f s = fmap (\x -> s { T.sourceLibName = x }) (f (T.sourceLibName s))-{-# INLINE sourceLibName #-}--compatPackageKey :: Lens' InstalledPackageInfo String-compatPackageKey f s = fmap (\x -> s { T.compatPackageKey = x }) (f (T.compatPackageKey s))-{-# INLINE compatPackageKey #-}--license :: Lens' InstalledPackageInfo (Either SPDX.License License)-license f s = fmap (\x -> s { T.license = x }) (f (T.license s))-{-# INLINE license #-}--copyright :: Lens' InstalledPackageInfo ShortText-copyright f s = fmap (\x -> s { T.copyright = x }) (f (T.copyright s))-{-# INLINE copyright #-}--maintainer :: Lens' InstalledPackageInfo ShortText-maintainer f s = fmap (\x -> s { T.maintainer = x }) (f (T.maintainer s))-{-# INLINE maintainer #-}--author :: Lens' InstalledPackageInfo ShortText-author f s = fmap (\x -> s { T.author = x }) (f (T.author s))-{-# INLINE author #-}--stability :: Lens' InstalledPackageInfo ShortText-stability f s = fmap (\x -> s { T.stability = x }) (f (T.stability s))-{-# INLINE stability #-}--homepage :: Lens' InstalledPackageInfo ShortText-homepage f s = fmap (\x -> s { T.homepage = x }) (f (T.homepage s))-{-# INLINE homepage #-}--pkgUrl :: Lens' InstalledPackageInfo ShortText-pkgUrl f s = fmap (\x -> s { T.pkgUrl = x }) (f (T.pkgUrl s))-{-# INLINE pkgUrl #-}--synopsis :: Lens' InstalledPackageInfo ShortText-synopsis f s = fmap (\x -> s { T.synopsis = x }) (f (T.synopsis s))-{-# INLINE synopsis #-}--description :: Lens' InstalledPackageInfo ShortText-description f s = fmap (\x -> s { T.description = x }) (f (T.description s))-{-# INLINE description #-}--category :: Lens' InstalledPackageInfo ShortText-category f s = fmap (\x -> s { T.category = x }) (f (T.category s))-{-# INLINE category #-}--abiHash :: Lens' InstalledPackageInfo AbiHash-abiHash f s = fmap (\x -> s { T.abiHash = x }) (f (T.abiHash s))-{-# INLINE abiHash #-}--indefinite :: Lens' InstalledPackageInfo Bool-indefinite f s = fmap (\x -> s { T.indefinite = x }) (f (T.indefinite s))-{-# INLINE indefinite #-}--exposed :: Lens' InstalledPackageInfo Bool-exposed f s = fmap (\x -> s { T.exposed = x }) (f (T.exposed s))-{-# INLINE exposed #-}--exposedModules :: Lens' InstalledPackageInfo [ExposedModule]-exposedModules f s = fmap (\x -> s { T.exposedModules = x }) (f (T.exposedModules s))-{-# INLINE exposedModules #-}--hiddenModules :: Lens' InstalledPackageInfo [ModuleName]-hiddenModules f s = fmap (\x -> s { T.hiddenModules = x }) (f (T.hiddenModules s))-{-# INLINE hiddenModules #-}--trusted :: Lens' InstalledPackageInfo Bool-trusted f s = fmap (\x -> s { T.trusted = x }) (f (T.trusted s))-{-# INLINE trusted #-}--importDirs :: Lens' InstalledPackageInfo [FilePath]-importDirs f s = fmap (\x -> s { T.importDirs = x }) (f (T.importDirs s))-{-# INLINE importDirs #-}--libraryDirs :: Lens' InstalledPackageInfo [FilePath]-libraryDirs f s = fmap (\x -> s { T.libraryDirs = x }) (f (T.libraryDirs s))-{-# INLINE libraryDirs #-}--libraryDynDirs :: Lens' InstalledPackageInfo [FilePath]-libraryDynDirs f s = fmap (\x -> s { T.libraryDynDirs = x }) (f (T.libraryDynDirs s))-{-# INLINE libraryDynDirs #-}--dataDir :: Lens' InstalledPackageInfo FilePath-dataDir f s = fmap (\x -> s { T.dataDir = x }) (f (T.dataDir s))-{-# INLINE dataDir #-}--hsLibraries :: Lens' InstalledPackageInfo [String]-hsLibraries f s = fmap (\x -> s { T.hsLibraries = x }) (f (T.hsLibraries s))-{-# INLINE hsLibraries #-}--extraLibraries :: Lens' InstalledPackageInfo [String]-extraLibraries f s = fmap (\x -> s { T.extraLibraries = x }) (f (T.extraLibraries s))-{-# INLINE extraLibraries #-}--extraGHCiLibraries :: Lens' InstalledPackageInfo [String]-extraGHCiLibraries f s = fmap (\x -> s { T.extraGHCiLibraries = x }) (f (T.extraGHCiLibraries s))-{-# INLINE extraGHCiLibraries #-}--includeDirs :: Lens' InstalledPackageInfo [FilePath]-includeDirs f s = fmap (\x -> s { T.includeDirs = x }) (f (T.includeDirs s))-{-# INLINE includeDirs #-}--includes :: Lens' InstalledPackageInfo [String]-includes f s = fmap (\x -> s { T.includes = x }) (f (T.includes s))-{-# INLINE includes #-}--depends :: Lens' InstalledPackageInfo [UnitId]-depends f s = fmap (\x -> s { T.depends = x }) (f (T.depends s))-{-# INLINE depends #-}--abiDepends :: Lens' InstalledPackageInfo [AbiDependency]-abiDepends f s = fmap (\x -> s { T.abiDepends = x }) (f (T.abiDepends s))-{-# INLINE abiDepends #-}--ccOptions :: Lens' InstalledPackageInfo [String]-ccOptions f s = fmap (\x -> s { T.ccOptions = x }) (f (T.ccOptions s))-{-# INLINE ccOptions #-}--cxxOptions :: Lens' InstalledPackageInfo [String]-cxxOptions f s = fmap (\x -> s { T.cxxOptions = x }) (f (T.cxxOptions s))-{-# INLINE cxxOptions #-}--ldOptions :: Lens' InstalledPackageInfo [String]-ldOptions f s = fmap (\x -> s { T.ldOptions = x }) (f (T.ldOptions s))-{-# INLINE ldOptions #-}--frameworkDirs :: Lens' InstalledPackageInfo [FilePath]-frameworkDirs f s = fmap (\x -> s { T.frameworkDirs = x }) (f (T.frameworkDirs s))-{-# INLINE frameworkDirs #-}--frameworks :: Lens' InstalledPackageInfo [String]-frameworks f s = fmap (\x -> s { T.frameworks = x }) (f (T.frameworks s))-{-# INLINE frameworks #-}--haddockInterfaces :: Lens' InstalledPackageInfo [FilePath]-haddockInterfaces f s = fmap (\x -> s { T.haddockInterfaces = x }) (f (T.haddockInterfaces s))-{-# INLINE haddockInterfaces #-}--haddockHTMLs :: Lens' InstalledPackageInfo [FilePath]-haddockHTMLs f s = fmap (\x -> s { T.haddockHTMLs = x }) (f (T.haddockHTMLs s))-{-# INLINE haddockHTMLs #-}--pkgRoot :: Lens' InstalledPackageInfo (Maybe FilePath)-pkgRoot f s = fmap (\x -> s { T.pkgRoot = x }) (f (T.pkgRoot s))-{-# INLINE pkgRoot #-}--libVisibility :: Lens' InstalledPackageInfo LibraryVisibility-libVisibility f s = fmap (\x -> s { T.libVisibility = x }) (f (T.libVisibility s))-{-# INLINE libVisibility #-}-
− src/Distribution/Types/LegacyExeDependency.hs
@@ -1,47 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Types.LegacyExeDependency- ( LegacyExeDependency(..)- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Parsec-import Distribution.Pretty-import Distribution.Version (VersionRange, anyVersion)--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp---- | Describes a legacy `build-tools`-style dependency on an executable------ It is "legacy" because we do not know what the build-tool referred to. It--- could refer to a pkg-config executable (PkgconfigName), or an internal--- executable (UnqualComponentName). Thus the name is stringly typed.------ @since 2.0.0.2-data LegacyExeDependency = LegacyExeDependency- String- VersionRange- deriving (Generic, Read, Show, Eq, Typeable, Data)--instance Binary LegacyExeDependency-instance Structured LegacyExeDependency-instance NFData LegacyExeDependency where rnf = genericRnf--instance Pretty LegacyExeDependency where- pretty (LegacyExeDependency name ver) =- Disp.text name <+> pretty ver--instance Parsec LegacyExeDependency where- parsec = do- name <- parsecMaybeQuoted nameP- P.spaces- verRange <- parsecMaybeQuoted parsec <|> pure anyVersion- pure $ LegacyExeDependency name verRange- where- nameP = intercalate "-" <$> toList <$> P.sepByNonEmpty component (P.char '-')- component = do- cs <- P.munch1 (\c -> isAlphaNum c || c == '+' || c == '_')- if all isDigit cs then fail "invalid component" else return cs
− src/Distribution/Types/Lens.hs
@@ -1,25 +0,0 @@-module Distribution.Types.Lens (- module Distribution.Types.Benchmark.Lens,- module Distribution.Types.BuildInfo.Lens,- module Distribution.Types.Executable.Lens,- module Distribution.Types.ForeignLib.Lens,- module Distribution.Types.GenericPackageDescription.Lens,- module Distribution.Types.Library.Lens,- module Distribution.Types.PackageDescription.Lens,- module Distribution.Types.PackageId.Lens,- module Distribution.Types.SetupBuildInfo.Lens,- module Distribution.Types.SourceRepo.Lens,- module Distribution.Types.TestSuite.Lens,- ) where--import Distribution.Types.Benchmark.Lens-import Distribution.Types.BuildInfo.Lens-import Distribution.Types.Executable.Lens-import Distribution.Types.ForeignLib.Lens-import Distribution.Types.GenericPackageDescription.Lens-import Distribution.Types.Library.Lens-import Distribution.Types.PackageDescription.Lens-import Distribution.Types.PackageId.Lens-import Distribution.Types.SetupBuildInfo.Lens-import Distribution.Types.SourceRepo.Lens-import Distribution.Types.TestSuite.Lens
− src/Distribution/Types/Library.hs
@@ -1,96 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Distribution.Types.Library (- Library(..),- emptyLibrary,- explicitLibModules,- libModulesAutogen,-) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.ModuleName-import Distribution.Types.BuildInfo-import Distribution.Types.LibraryVisibility-import Distribution.Types.ModuleReexport-import Distribution.Types.LibraryName--import qualified Distribution.Types.BuildInfo.Lens as L--data Library = Library- { libName :: LibraryName- , exposedModules :: [ModuleName]- , reexportedModules :: [ModuleReexport]- , signatures :: [ModuleName] -- ^ What sigs need implementations?- , libExposed :: Bool -- ^ Is the lib to be exposed by default? (i.e. whether its modules available in GHCi for example)- , libVisibility :: LibraryVisibility -- ^ Whether this multilib can be dependent from outside.- , libBuildInfo :: BuildInfo- }- deriving (Generic, Show, Eq, Read, Typeable, Data)--instance L.HasBuildInfo Library where- buildInfo f l = (\x -> l { libBuildInfo = x }) <$> f (libBuildInfo l)--instance Binary Library-instance Structured Library-instance NFData Library where rnf = genericRnf--emptyLibrary :: Library-emptyLibrary = Library- { libName = LMainLibName- , exposedModules = mempty- , reexportedModules = mempty- , signatures = mempty- , libExposed = True- , libVisibility = mempty- , libBuildInfo = mempty- }---- | This instance is not good.------ We need it for 'PackageDescription.Configuration.addBuildableCondition'.--- More correct method would be some kind of "create empty clone".------ More concretely, 'addBuildableCondition' will make `libVisibility = False`--- libraries when `buildable: false`. This may cause problems.----instance Monoid Library where- mempty = emptyLibrary- mappend = (<>)--instance Semigroup Library where- a <> b = Library- { libName = combineLibraryName (libName a) (libName b)- , exposedModules = combine exposedModules- , reexportedModules = combine reexportedModules- , signatures = combine signatures- , libExposed = libExposed a && libExposed b -- so False propagates- , libVisibility = combine libVisibility- , libBuildInfo = combine libBuildInfo- }- where combine field = field a `mappend` field b---- | Get all the module names from the library (exposed and internal modules)--- which are explicitly listed in the package description which would--- need to be compiled. (This does not include reexports, which--- do not need to be compiled.) This may not include all modules for which--- GHC generated interface files (i.e., implicit modules.)-explicitLibModules :: Library -> [ModuleName]-explicitLibModules lib = exposedModules lib- ++ otherModules (libBuildInfo lib)- ++ signatures lib---- | Get all the auto generated module names from the library, exposed or not.--- This are a subset of 'libModules'.-libModulesAutogen :: Library -> [ModuleName]-libModulesAutogen lib = autogenModules (libBuildInfo lib)---- | Combine 'LibraryName'. in parsing we prefer value coming--- from munged @name@ field over the @lib-name@.------ /Should/ be irrelevant.-combineLibraryName :: LibraryName -> LibraryName -> LibraryName-combineLibraryName l@(LSubLibName _) _ = l-combineLibraryName _ l = l
− src/Distribution/Types/Library/Lens.hs
@@ -1,45 +0,0 @@-module Distribution.Types.Library.Lens (- Library,- module Distribution.Types.Library.Lens,- ) where--import Distribution.Compat.Lens-import Distribution.Compat.Prelude-import Prelude ()--import Distribution.ModuleName (ModuleName)-import Distribution.Types.BuildInfo (BuildInfo)-import Distribution.Types.Library (Library)-import Distribution.Types.LibraryName (LibraryName)-import Distribution.Types.LibraryVisibility (LibraryVisibility)-import Distribution.Types.ModuleReexport (ModuleReexport)--import qualified Distribution.Types.Library as T--libName :: Lens' Library LibraryName-libName f s = fmap (\x -> s { T.libName = x }) (f (T.libName s))-{-# INLINE libName #-}--exposedModules :: Lens' Library [ModuleName]-exposedModules f s = fmap (\x -> s { T.exposedModules = x }) (f (T.exposedModules s))-{-# INLINE exposedModules #-}--reexportedModules :: Lens' Library [ModuleReexport]-reexportedModules f s = fmap (\x -> s { T.reexportedModules = x }) (f (T.reexportedModules s))-{-# INLINE reexportedModules #-}--signatures :: Lens' Library [ModuleName]-signatures f s = fmap (\x -> s { T.signatures = x }) (f (T.signatures s))-{-# INLINE signatures #-}--libExposed :: Lens' Library Bool-libExposed f s = fmap (\x -> s { T.libExposed = x }) (f (T.libExposed s))-{-# INLINE libExposed #-}--libVisibility :: Lens' Library LibraryVisibility-libVisibility f s = fmap (\x -> s { T.libVisibility = x }) (f (T.libVisibility s))-{-# INLINE libVisibility #-}--libBuildInfo :: Lens' Library BuildInfo-libBuildInfo f s = fmap (\x -> s { T.libBuildInfo = x }) (f (T.libBuildInfo s))-{-# INLINE libBuildInfo #-}
− src/Distribution/Types/LibraryName.hs
@@ -1,73 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Distribution.Types.LibraryName (- LibraryName(..),- defaultLibName,- maybeToLibraryName,- showLibraryName,- libraryNameStanza,- libraryNameString,- -- * Pretty & Parse- prettyLibraryNameComponent,- parsecLibraryNameComponent,- ) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Types.UnqualComponentName-import Distribution.Pretty-import Distribution.Parsec--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp--data LibraryName = LMainLibName- | LSubLibName UnqualComponentName- deriving (Eq, Generic, Ord, Read, Show, Typeable, Data)--instance Binary LibraryName-instance Structured LibraryName-instance NFData LibraryName where rnf = genericRnf---- | Pretty print 'LibraryName' in build-target-ish syntax.------ /Note:/ there are no 'Pretty' or 'Parsec' instances,--- as there's other way to represent 'LibraryName', namely as bare--- 'UnqualComponentName'. -prettyLibraryNameComponent :: LibraryName -> Disp.Doc-prettyLibraryNameComponent LMainLibName = Disp.text "lib"-prettyLibraryNameComponent (LSubLibName str) = Disp.text "lib:" <<>> pretty str--parsecLibraryNameComponent :: CabalParsing m => m LibraryName-parsecLibraryNameComponent = do- _ <- P.string "lib"- parseComposite <|> parseSingle- where- parseSingle = return LMainLibName- parseComposite = do- _ <- P.char ':'- LSubLibName <$> parsec--defaultLibName :: LibraryName-defaultLibName = LMainLibName--showLibraryName :: LibraryName -> String-showLibraryName LMainLibName = "library"-showLibraryName (LSubLibName name) = "library '" ++ prettyShow name ++ "'"--libraryNameStanza :: LibraryName -> String-libraryNameStanza LMainLibName = "library"-libraryNameStanza (LSubLibName name) = "library " ++ prettyShow name--libraryNameString :: LibraryName -> Maybe UnqualComponentName-libraryNameString LMainLibName = Nothing-libraryNameString (LSubLibName n) = Just n---- | Convert the 'UnqualComponentName' of a library into a--- 'LibraryName'.-maybeToLibraryName :: Maybe UnqualComponentName -> LibraryName-maybeToLibraryName Nothing = LMainLibName-maybeToLibraryName (Just n) = LSubLibName n-
− src/Distribution/Types/LibraryVisibility.hs
@@ -1,52 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}--module Distribution.Types.LibraryVisibility(- LibraryVisibility(..),-) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Parsec-import Distribution.Pretty--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp---- | Multi-lib visibility------ @since 3.0.0.0----data LibraryVisibility- -- | Can be depenendent from other packages- = LibraryVisibilityPublic- -- | Internal library, default- | LibraryVisibilityPrivate- deriving (Generic, Show, Read, Eq, Typeable, Data)--instance Pretty LibraryVisibility where- pretty LibraryVisibilityPublic = Disp.text "public"- pretty LibraryVisibilityPrivate = Disp.text "private"--instance Parsec LibraryVisibility where- parsec = do- name <- P.munch1 isAlpha- parsecWarning PWTExperimental "visibility is experimental feature (issue #5660)"- case name of- "public" -> return LibraryVisibilityPublic- "private" -> return LibraryVisibilityPrivate- _ -> fail $ "Unknown visibility: " ++ name--instance Binary LibraryVisibility-instance Structured LibraryVisibility-instance NFData LibraryVisibility where rnf = genericRnf--instance Semigroup LibraryVisibility where- LibraryVisibilityPrivate <> LibraryVisibilityPrivate = LibraryVisibilityPrivate- _ <> _ = LibraryVisibilityPublic--instance Monoid LibraryVisibility where- mempty = LibraryVisibilityPrivate- mappend = (<>)
+ src/Distribution/Types/LocalBuildConfig.hs view
@@ -0,0 +1,229 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}++module Distribution.Types.LocalBuildConfig+ ( -- * The types+ PackageBuildDescr (..)+ , ComponentBuildDescr (..)+ , LocalBuildDescr (..)+ , LocalBuildConfig (..)+ , BuildOptions (..)++ -- * Conversion functions+ , buildOptionsConfigFlags+ ) where++import Distribution.Compat.Prelude+import Prelude ()++import Distribution.Types.ComponentLocalBuildInfo+import Distribution.Types.ComponentRequestedSpec+import Distribution.Types.GivenComponent+import Distribution.Types.PackageDescription+import Distribution.Types.UnitId++import Distribution.PackageDescription+import Distribution.Simple.Compiler+import Distribution.Simple.Flag+import Distribution.Simple.InstallDirs hiding+ ( absoluteInstallDirs+ , prefixRelativeInstallDirs+ , substPathTemplate+ )+import Distribution.Simple.PackageIndex+import Distribution.Simple.Program.Db (ProgramDb)+import Distribution.Simple.Setup.Config+import Distribution.System+import Distribution.Utils.Path++import Distribution.Compat.Graph (Graph)++-- | 'PackageBuildDescr' contains the information Cabal determines after+-- performing package-wide configuration of a package, before doing any+-- per-component configuration.+data PackageBuildDescr = PackageBuildDescr+ { configFlags :: ConfigFlags+ -- ^ Options passed to the configuration step.+ -- Needed to re-run configuration when .cabal is out of date+ , flagAssignment :: FlagAssignment+ -- ^ The final set of flags which were picked for this package+ , componentEnabledSpec :: ComponentRequestedSpec+ -- ^ What components were enabled during configuration, and why.+ , compiler :: Compiler+ -- ^ The compiler we're building with+ , hostPlatform :: Platform+ -- ^ The platform we're building for+ , pkgDescrFile :: Maybe (SymbolicPath Pkg File)+ -- ^ the filename containing the .cabal file, if available+ , localPkgDescr :: PackageDescription+ -- ^ WARNING WARNING WARNING Be VERY careful about using+ -- this function; we haven't deprecated it but using it+ -- could introduce subtle bugs related to+ -- 'HookedBuildInfo'.+ --+ -- In principle, this is supposed to contain the+ -- resolved package description, that does not contain+ -- any conditionals. However, it MAY NOT contain+ -- the description with a 'HookedBuildInfo' applied+ -- to it; see 'HookedBuildInfo' for the whole sordid saga.+ -- As much as possible, Cabal library should avoid using+ -- this parameter.+ , installDirTemplates :: InstallDirTemplates+ -- ^ The installation directories for the various different+ -- kinds of files+ -- TODO: inplaceDirTemplates :: InstallDirs FilePath+ , withPackageDB :: PackageDBStack+ -- ^ What package database to use, global\/user+ , extraCoverageFor :: [UnitId]+ -- ^ For per-package builds-only: an extra list of libraries to be included in+ -- the hpc coverage report for testsuites run with @--enable-coverage@.+ -- Notably, this list must exclude indefinite libraries and instantiations+ -- because HPC does not support backpack (Nov. 2023).+ }+ deriving (Generic, Read, Show)++-- | Information about individual components in a package,+-- determined after the configure step.+data ComponentBuildDescr = ComponentBuildDescr+ { componentGraph :: Graph ComponentLocalBuildInfo+ -- ^ All the components to build, ordered by topological+ -- sort, and with their INTERNAL dependencies over the+ -- intrapackage dependency graph.+ -- TODO: this is assumed to be short; otherwise we want+ -- some sort of ordered map.+ , componentNameMap :: Map ComponentName [ComponentLocalBuildInfo]+ -- ^ A map from component name to all matching+ -- components. These coincide with 'componentGraph'+ -- There may be more than one matching component because of backpack instantiations+ , promisedPkgs :: Map (PackageName, ComponentName) PromisedComponent+ -- ^ The packages we were promised, but aren't already installed.+ -- MP: Perhaps this just needs to be a Set UnitId at this stage.+ , installedPkgs :: InstalledPackageIndex+ -- ^ All the info about the installed packages that the+ -- current package depends on (directly or indirectly).+ -- The copy saved on disk does NOT include internal+ -- dependencies (because we just don't have enough+ -- information at this point to have an+ -- 'InstalledPackageInfo' for an internal dep), but we+ -- will often update it with the internal dependencies;+ -- see for example 'Distribution.Simple.Build.build'.+ -- (This admonition doesn't apply for per-component builds.)+ }+ deriving (Generic, Read, Show)++-- | 'LocalBuildDescr ' contains the information Cabal determines after+-- performing package-wide and per-component configuration of a package.+--+-- This information can no longer be changed after that point.+data LocalBuildDescr = LocalBuildDescr+ { packageBuildDescr :: PackageBuildDescr+ -- ^ Information that is available after configuring the package itself,+ -- before looking at individual components.+ , componentBuildDescr :: ComponentBuildDescr+ -- ^ Information about individual components in the package+ -- determined after the configure step.+ }+ deriving (Generic, Read, Show)++-- | 'LocalBuildConfig' contains options that can be controlled+-- by the user and serve as inputs to the configuration of a package.+data LocalBuildConfig = LocalBuildConfig+ { extraConfigArgs :: [String]+ -- ^ Extra args on the command line for the configuration step.+ -- Needed to re-run configuration when .cabal is out of date+ , withPrograms :: ProgramDb+ -- ^ Location and args for all programs+ , withBuildOptions :: BuildOptions+ -- ^ Options to control the build, e.g. whether to+ -- enable profiling or to enable program coverage.+ }+ deriving (Generic, Read, Show)++-- | 'BuildOptions' contains configuration options that can be controlled+-- by the user.+data BuildOptions = BuildOptions+ { withVanillaLib :: Bool+ -- ^ Whether to build normal libs.+ , withProfLib :: Bool+ -- ^ Whether to build normal libs.+ , withProfLibShared :: Bool+ -- ^ Whether to build profiling versions of libs.+ , withSharedLib :: Bool+ -- ^ Whether to build shared versions of libs.+ , withStaticLib :: Bool+ -- ^ Whether to build static versions of libs (with all other libs rolled in)+ , withDynExe :: Bool+ -- ^ Whether to link executables dynamically+ , withFullyStaticExe :: Bool+ -- ^ Whether to link executables fully statically+ , withProfExe :: Bool+ -- ^ Whether to build executables for profiling.+ , withProfLibDetail :: ProfDetailLevel+ -- ^ Level of automatic profile detail.+ , withProfExeDetail :: ProfDetailLevel+ -- ^ Level of automatic profile detail.+ , withOptimization :: OptimisationLevel+ -- ^ Whether to build with optimization (if available).+ , withDebugInfo :: DebugInfoLevel+ -- ^ Whether to emit debug info (if available).+ , withGHCiLib :: Bool+ -- ^ Whether to build libs suitable for use with GHCi.+ , splitSections :: Bool+ -- ^ Use -split-sections with GHC, if available+ , splitObjs :: Bool+ -- ^ Use -split-objs with GHC, if available+ , stripExes :: Bool+ -- ^ Whether to strip executables during install+ , stripLibs :: Bool+ -- ^ Whether to strip libraries during install+ , exeCoverage :: Bool+ -- ^ Whether to enable executable program coverage+ , libCoverage :: Bool+ -- ^ Whether to enable library program coverage+ , relocatable :: Bool+ -- ^ Whether to build a relocatable package+ }+ deriving (Eq, Generic, Read, Show)++instance Binary PackageBuildDescr+instance Structured PackageBuildDescr+instance Binary ComponentBuildDescr+instance Structured ComponentBuildDescr+instance Binary LocalBuildDescr+instance Structured LocalBuildDescr+instance Binary LocalBuildConfig+instance Structured LocalBuildConfig+instance Binary BuildOptions+instance Structured BuildOptions++buildOptionsConfigFlags :: BuildOptions -> ConfigFlags+buildOptionsConfigFlags (BuildOptions{..}) =+ mempty+ { configVanillaLib = toFlag $ withVanillaLib+ , configSharedLib = toFlag $ withSharedLib+ , configStaticLib = toFlag $ withStaticLib+ , configDynExe = toFlag $ withDynExe+ , configFullyStaticExe = toFlag $ withFullyStaticExe+ , configGHCiLib = toFlag $ withGHCiLib+ , configProfExe = toFlag $ withProfExe+ , configProfLib = toFlag $ withProfLib+ , configProfShared = toFlag $ withProfLibShared+ , configProf = mempty+ , -- configProfDetail is for exe+lib, but overridden by configProfLibDetail+ -- so we specify both so we can specify independently+ configProfDetail = toFlag $ withProfExeDetail+ , configProfLibDetail = toFlag $ withProfLibDetail+ , configCoverage = toFlag $ exeCoverage+ , configLibCoverage = mempty+ , configRelocatable = toFlag $ relocatable+ , configOptimization = toFlag $ withOptimization+ , configSplitSections = toFlag $ splitSections+ , configSplitObjs = toFlag $ splitObjs+ , configStripExes = toFlag $ stripExes+ , configStripLibs = toFlag $ stripLibs+ , configDebugInfo = toFlag $ withDebugInfo+ }
src/Distribution/Types/LocalBuildInfo.hs view
@@ -1,170 +1,322 @@-{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} -module Distribution.Types.LocalBuildInfo (- -- * The type-- LocalBuildInfo(..),+module Distribution.Types.LocalBuildInfo+ ( -- * The types+ LocalBuildInfo+ ( LocalBuildInfo+ , configFlags+ , flagAssignment+ , componentEnabledSpec+ , extraConfigArgs+ , installDirTemplates+ , compiler+ , hostPlatform+ , pkgDescrFile+ , componentGraph+ , componentNameMap+ , promisedPkgs+ , installedPkgs+ , localPkgDescr+ , withPrograms+ , withPackageDB+ , withVanillaLib+ , withProfLib+ , withProfLibShared+ , withDynExe+ , withFullyStaticExe+ , withProfExe+ , withSharedLib+ , withStaticLib+ , withProfLibDetail+ , withProfExeDetail+ , withOptimization+ , withDebugInfo+ , withGHCiLib+ , splitSections+ , splitObjs+ , stripExes+ , stripLibs+ , exeCoverage+ , libCoverage+ , extraCoverageFor+ , relocatable+ , ..+ ) -- * Convenience accessors-- localComponentId,- localUnitId,- localCompatPackageKey,- localPackage,+ , localComponentId+ , localUnitId+ , localCompatPackageKey+ , localPackage+ , buildDir+ , buildDirPBD+ , setupFlagsBuildDir+ , distPrefLBI+ , packageRoot+ , progPrefix+ , progSuffix -- * Build targets of the 'LocalBuildInfo'.-- componentNameCLBIs,-- -- NB: the primes mean that they take a 'PackageDescription'- -- which may not match 'localPkgDescr' in 'LocalBuildInfo'.- -- More logical types would drop this argument, but- -- at the moment, this is the ONLY supported function, because- -- 'localPkgDescr' is not guaranteed to match. At some point- -- we will fix it and then we can use the (free) unprimed- -- namespace for the correct commands.- --- -- See https://github.com/haskell/cabal/issues/3606 for more- -- details.+ , componentNameCLBIs+ -- NB: the primes mean that they take a 'PackageDescription'+ -- which may not match 'localPkgDescr' in 'LocalBuildInfo'.+ -- More logical types would drop this argument, but+ -- at the moment, this is the ONLY supported function, because+ -- 'localPkgDescr' is not guaranteed to match. At some point+ -- we will fix it and then we can use the (free) unprimed+ -- namespace for the correct commands.+ --+ -- See https://github.com/haskell/cabal/issues/3606 for more+ -- details. - componentNameTargets',- unitIdTarget',- allTargetsInBuildOrder',- withAllTargetsInBuildOrder',- neededTargetsInBuildOrder',- withNeededTargetsInBuildOrder',- testCoverage,+ , componentNameTargets'+ , unitIdTarget'+ , allTargetsInBuildOrder'+ , withAllTargetsInBuildOrder'+ , neededTargetsInBuildOrder'+ , withNeededTargetsInBuildOrder'+ , testCoverage+ , buildWays -- * Functions you SHOULD NOT USE (yet), but are defined here to- -- prevent someone from accidentally defining them - componentNameTargets,- unitIdTarget,- allTargetsInBuildOrder,- withAllTargetsInBuildOrder,- neededTargetsInBuildOrder,- withNeededTargetsInBuildOrder,+ -- prevent someone from accidentally defining them++ , componentNameTargets+ , unitIdTarget+ , allTargetsInBuildOrder+ , withAllTargetsInBuildOrder+ , neededTargetsInBuildOrder+ , withNeededTargetsInBuildOrder ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -import Distribution.Types.PackageDescription+import Distribution.Types.ComponentId import Distribution.Types.ComponentLocalBuildInfo import Distribution.Types.ComponentRequestedSpec-import Distribution.Types.ComponentId+import Distribution.Types.GivenComponent+import qualified Distribution.Types.LocalBuildConfig as LBC+import Distribution.Types.PackageDescription import Distribution.Types.PackageId-import Distribution.Types.UnitId import Distribution.Types.TargetInfo+import Distribution.Types.UnitId -import Distribution.Simple.InstallDirs hiding (absoluteInstallDirs,- prefixRelativeInstallDirs,- substPathTemplate, )-import Distribution.Simple.Program+import Distribution.Utils.Path+ import Distribution.PackageDescription+import Distribution.Pretty+import Distribution.Simple.BuildWay import Distribution.Simple.Compiler+import Distribution.Simple.Flag+import Distribution.Simple.InstallDirs hiding+ ( absoluteInstallDirs+ , prefixRelativeInstallDirs+ , substPathTemplate+ ) import Distribution.Simple.PackageIndex-import Distribution.Simple.Setup+import Distribution.Simple.Program+import Distribution.Simple.Setup.Common+import Distribution.Simple.Setup.Config import Distribution.System-import Distribution.Pretty +import qualified Data.Map as Map import Distribution.Compat.Graph (Graph) import qualified Distribution.Compat.Graph as Graph-import qualified Data.Map as Map +import qualified System.FilePath as FilePath (takeDirectory)+ -- | Data cached after configuration step. See also -- 'Distribution.Simple.Setup.ConfigFlags'.-data LocalBuildInfo = LocalBuildInfo {- configFlags :: ConfigFlags,- -- ^ Options passed to the configuration step.- -- Needed to re-run configuration when .cabal is out of date- flagAssignment :: FlagAssignment,- -- ^ The final set of flags which were picked for this package- componentEnabledSpec :: ComponentRequestedSpec,- -- ^ What components were enabled during configuration, and why.- extraConfigArgs :: [String],- -- ^ Extra args on the command line for the configuration step.- -- Needed to re-run configuration when .cabal is out of date- installDirTemplates :: InstallDirTemplates,- -- ^ The installation directories for the various different- -- kinds of files- --TODO: inplaceDirTemplates :: InstallDirs FilePath- compiler :: Compiler,- -- ^ The compiler we're building with- hostPlatform :: Platform,- -- ^ The platform we're building for- buildDir :: FilePath,- -- ^ Where to build the package.- cabalFilePath :: Maybe FilePath,- -- ^ Path to the cabal file, if given during configuration.- componentGraph :: Graph ComponentLocalBuildInfo,- -- ^ All the components to build, ordered by topological- -- sort, and with their INTERNAL dependencies over the- -- intrapackage dependency graph.- -- TODO: this is assumed to be short; otherwise we want- -- some sort of ordered map.- componentNameMap :: Map ComponentName [ComponentLocalBuildInfo],- -- ^ A map from component name to all matching- -- components. These coincide with 'componentGraph'- installedPkgs :: InstalledPackageIndex,- -- ^ All the info about the installed packages that the- -- current package depends on (directly or indirectly).- -- The copy saved on disk does NOT include internal- -- dependencies (because we just don't have enough- -- information at this point to have an- -- 'InstalledPackageInfo' for an internal dep), but we- -- will often update it with the internal dependencies;- -- see for example 'Distribution.Simple.Build.build'.- -- (This admonition doesn't apply for per-component builds.)- pkgDescrFile :: Maybe FilePath,- -- ^ the filename containing the .cabal file, if available- localPkgDescr :: PackageDescription,- -- ^ WARNING WARNING WARNING Be VERY careful about using- -- this function; we haven't deprecated it but using it- -- could introduce subtle bugs related to- -- 'HookedBuildInfo'.- --- -- In principle, this is supposed to contain the- -- resolved package description, that does not contain- -- any conditionals. However, it MAY NOT contain- -- the description with a 'HookedBuildInfo' applied- -- to it; see 'HookedBuildInfo' for the whole sordid saga.- -- As much as possible, Cabal library should avoid using- -- this parameter.- withPrograms :: ProgramDb, -- ^Location and args for all programs- withPackageDB :: PackageDBStack, -- ^What package database to use, global\/user- withVanillaLib:: Bool, -- ^Whether to build normal libs.- withProfLib :: Bool, -- ^Whether to build profiling versions of libs.- withSharedLib :: Bool, -- ^Whether to build shared versions of libs.- withStaticLib :: Bool, -- ^Whether to build static versions of libs (with all other libs rolled in)- withDynExe :: Bool, -- ^Whether to link executables dynamically- withFullyStaticExe :: Bool, -- ^Whether to link executables fully statically- withProfExe :: Bool, -- ^Whether to build executables for profiling.- withProfLibDetail :: ProfDetailLevel, -- ^Level of automatic profile detail.- withProfExeDetail :: ProfDetailLevel, -- ^Level of automatic profile detail.- withOptimization :: OptimisationLevel, -- ^Whether to build with optimization (if available).- withDebugInfo :: DebugInfoLevel, -- ^Whether to emit debug info (if available).- withGHCiLib :: Bool, -- ^Whether to build libs suitable for use with GHCi.- splitSections :: Bool, -- ^Use -split-sections with GHC, if available- splitObjs :: Bool, -- ^Use -split-objs with GHC, if available- stripExes :: Bool, -- ^Whether to strip executables during install- stripLibs :: Bool, -- ^Whether to strip libraries during install- exeCoverage :: Bool, -- ^Whether to enable executable program coverage- libCoverage :: Bool, -- ^Whether to enable library program coverage- progPrefix :: PathTemplate, -- ^Prefix to be prepended to installed executables- progSuffix :: PathTemplate, -- ^Suffix to be appended to installed executables- relocatable :: Bool -- ^Whether to build a relocatable package- } deriving (Generic, Read, Show, Typeable)+data LocalBuildInfo = NewLocalBuildInfo+ { localBuildDescr :: LBC.LocalBuildDescr+ -- ^ Information about a package determined by Cabal+ -- after the configuration step.+ , localBuildConfig :: LBC.LocalBuildConfig+ -- ^ Information about a package configuration+ -- that can be modified by the user at configuration time.+ }+ deriving (Generic, Read, Show) +{-# COMPLETE LocalBuildInfo #-}++-- | This pattern synonym is for backwards compatibility, to adapt+-- to 'LocalBuildInfo' being split into 'LocalBuildDescr' and 'LocalBuildConfig'.+pattern LocalBuildInfo+ :: ConfigFlags+ -> FlagAssignment+ -> ComponentRequestedSpec+ -> [String]+ -> InstallDirTemplates+ -> Compiler+ -> Platform+ -> Maybe (SymbolicPath Pkg File)+ -> Graph ComponentLocalBuildInfo+ -> Map ComponentName [ComponentLocalBuildInfo]+ -> Map (PackageName, ComponentName) PromisedComponent+ -> InstalledPackageIndex+ -> PackageDescription+ -> ProgramDb+ -> PackageDBStack+ -> Bool+ -> Bool+ -> Bool+ -> Bool+ -> Bool+ -> Bool+ -> Bool+ -> Bool+ -> ProfDetailLevel+ -> ProfDetailLevel+ -> OptimisationLevel+ -> DebugInfoLevel+ -> Bool+ -> Bool+ -> Bool+ -> Bool+ -> Bool+ -> Bool+ -> Bool+ -> [UnitId]+ -> Bool+ -> LocalBuildInfo+pattern LocalBuildInfo+ { configFlags+ , flagAssignment+ , componentEnabledSpec+ , extraConfigArgs+ , installDirTemplates+ , compiler+ , hostPlatform+ , pkgDescrFile+ , componentGraph+ , componentNameMap+ , promisedPkgs+ , installedPkgs+ , localPkgDescr+ , withPrograms+ , withPackageDB+ , withVanillaLib+ , withProfLib+ , withProfLibShared+ , withSharedLib+ , withStaticLib+ , withDynExe+ , withFullyStaticExe+ , withProfExe+ , withProfLibDetail+ , withProfExeDetail+ , withOptimization+ , withDebugInfo+ , withGHCiLib+ , splitSections+ , splitObjs+ , stripExes+ , stripLibs+ , exeCoverage+ , libCoverage+ , extraCoverageFor+ , relocatable+ } =+ NewLocalBuildInfo+ { localBuildDescr =+ LBC.LocalBuildDescr+ { packageBuildDescr =+ LBC.PackageBuildDescr+ { configFlags+ , flagAssignment+ , componentEnabledSpec+ , compiler+ , hostPlatform+ , localPkgDescr+ , installDirTemplates+ , withPackageDB+ , pkgDescrFile+ , extraCoverageFor+ }+ , componentBuildDescr =+ LBC.ComponentBuildDescr+ { componentGraph+ , componentNameMap+ , promisedPkgs+ , installedPkgs+ }+ }+ , localBuildConfig =+ LBC.LocalBuildConfig+ { extraConfigArgs+ , withPrograms+ , withBuildOptions =+ LBC.BuildOptions+ { withVanillaLib+ , withProfLib+ , withProfLibShared+ , withSharedLib+ , withStaticLib+ , withDynExe+ , withFullyStaticExe+ , withProfExe+ , withProfLibDetail+ , withProfExeDetail+ , withOptimization+ , withDebugInfo+ , withGHCiLib+ , splitSections+ , splitObjs+ , stripExes+ , stripLibs+ , exeCoverage+ , libCoverage+ , relocatable+ }+ }+ }+ instance Binary LocalBuildInfo instance Structured LocalBuildInfo ------------------------------------------------------------------------------- -- Accessor functions +buildDir :: LocalBuildInfo -> SymbolicPath Pkg (Dir Build)+buildDir lbi =+ buildDirPBD $ LBC.packageBuildDescr $ localBuildDescr lbi++buildDirPBD :: LBC.PackageBuildDescr -> SymbolicPath Pkg (Dir Build)+buildDirPBD (LBC.PackageBuildDescr{configFlags = cfg}) =+ setupFlagsBuildDir $ configCommonFlags cfg++setupFlagsBuildDir :: CommonSetupFlags -> SymbolicPath Pkg (Dir Build)+setupFlagsBuildDir cfg = fromFlag (setupDistPref cfg) </> makeRelativePathEx "build"++distPrefLBI :: LocalBuildInfo -> SymbolicPath Pkg (Dir Dist)+distPrefLBI = fromFlag . setupDistPref . configCommonFlags . LBC.configFlags . LBC.packageBuildDescr . localBuildDescr++-- | The (relative or absolute) path to the package root, based on+--+-- - the working directory flag+-- - the @.cabal@ path+packageRoot :: CommonSetupFlags -> FilePath+packageRoot cfg =+ case flagToMaybe (setupCabalFilePath cfg) of+ Just cabalPath -> FilePath.takeDirectory $ interpretSymbolicPath mbWorkDir cabalPath+ Nothing -> maybe "." getSymbolicPath mbWorkDir+ where+ mbWorkDir = flagToMaybe $ setupWorkingDir cfg++progPrefix, progSuffix :: LocalBuildInfo -> PathTemplate+progPrefix (LocalBuildInfo{configFlags = cfg}) =+ fromFlag $ configProgPrefix cfg+progSuffix (LocalBuildInfo{configFlags = cfg}) =+ fromFlag $ configProgSuffix cfg+ -- TODO: Get rid of these functions, as much as possible. They are -- a bit useful in some cases, but you should be very careful! @@ -173,71 +325,73 @@ -- on the package ID. localComponentId :: LocalBuildInfo -> ComponentId localComponentId lbi =- case componentNameCLBIs lbi (CLibName LMainLibName) of- [LibComponentLocalBuildInfo { componentComponentId = cid }]- -> cid- _ -> mkComponentId (prettyShow (localPackage lbi))+ case componentNameCLBIs lbi (CLibName LMainLibName) of+ [LibComponentLocalBuildInfo{componentComponentId = cid}] ->+ cid+ _ -> mkComponentId (prettyShow (localPackage lbi)) -- | Extract the 'PackageIdentifier' of a 'LocalBuildInfo'. -- This is a "safe" use of 'localPkgDescr' localPackage :: LocalBuildInfo -> PackageId-localPackage lbi = package (localPkgDescr lbi)+localPackage (LocalBuildInfo{localPkgDescr = pkg}) = package pkg -- | Extract the 'UnitId' from the library component of a -- 'LocalBuildInfo' if it exists, or make a fake unit ID based on -- the package ID. localUnitId :: LocalBuildInfo -> UnitId localUnitId lbi =- case componentNameCLBIs lbi (CLibName LMainLibName) of- [LibComponentLocalBuildInfo { componentUnitId = uid }]- -> uid- _ -> mkLegacyUnitId $ localPackage lbi+ case componentNameCLBIs lbi (CLibName LMainLibName) of+ [LibComponentLocalBuildInfo{componentUnitId = uid}] ->+ uid+ _ -> mkLegacyUnitId $ localPackage lbi -- | Extract the compatibility package key from the public library component of a -- 'LocalBuildInfo' if it exists, or make a fake package key based -- on the package ID. localCompatPackageKey :: LocalBuildInfo -> String localCompatPackageKey lbi =- case componentNameCLBIs lbi (CLibName LMainLibName) of- [LibComponentLocalBuildInfo { componentCompatPackageKey = pk }]- -> pk- _ -> prettyShow (localPackage lbi)+ case componentNameCLBIs lbi (CLibName LMainLibName) of+ [LibComponentLocalBuildInfo{componentCompatPackageKey = pk}] ->+ pk+ _ -> prettyShow (localPackage lbi) -- | Convenience function to generate a default 'TargetInfo' from a -- 'ComponentLocalBuildInfo'. The idea is to call this once, and then -- use 'TargetInfo' everywhere else. Private to this module. mkTargetInfo :: PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> TargetInfo mkTargetInfo pkg_descr _lbi clbi =- TargetInfo {- targetCLBI = clbi,- -- NB: @pkg_descr@, not @localPkgDescr lbi@!- targetComponent = getComponent pkg_descr- (componentLocalName clbi)- }+ TargetInfo+ { targetCLBI = clbi+ , -- NB: @pkg_descr@, not @localPkgDescr lbi@!+ targetComponent =+ getComponent+ pkg_descr+ (componentLocalName clbi)+ } -- | Return all 'TargetInfo's associated with 'ComponentName'. -- In the presence of Backpack there may be more than one! -- Has a prime because it takes a 'PackageDescription' argument -- which may disagree with 'localPkgDescr' in 'LocalBuildInfo'. componentNameTargets' :: PackageDescription -> LocalBuildInfo -> ComponentName -> [TargetInfo]-componentNameTargets' pkg_descr lbi cname =- case Map.lookup cname (componentNameMap lbi) of- Just clbis -> map (mkTargetInfo pkg_descr lbi) clbis- Nothing -> []+componentNameTargets' pkg_descr lbi@(LocalBuildInfo{componentNameMap = comps}) cname =+ case Map.lookup cname comps of+ Just clbis -> map (mkTargetInfo pkg_descr lbi) clbis+ Nothing -> [] unitIdTarget' :: PackageDescription -> LocalBuildInfo -> UnitId -> Maybe TargetInfo-unitIdTarget' pkg_descr lbi uid =- case Graph.lookup uid (componentGraph lbi) of- Just clbi -> Just (mkTargetInfo pkg_descr lbi clbi)- Nothing -> Nothing+unitIdTarget' pkg_descr lbi@(LocalBuildInfo{componentGraph = compsGraph}) uid =+ case Graph.lookup uid compsGraph of+ Just clbi -> Just (mkTargetInfo pkg_descr lbi clbi)+ Nothing -> Nothing -- | Return all 'ComponentLocalBuildInfo's associated with 'ComponentName'. -- In the presence of Backpack there may be more than one! componentNameCLBIs :: LocalBuildInfo -> ComponentName -> [ComponentLocalBuildInfo]-componentNameCLBIs lbi cname =- case Map.lookup cname (componentNameMap lbi) of- Just clbis -> clbis- Nothing -> []+componentNameCLBIs (LocalBuildInfo{componentNameMap = comps}) cname =+ case Map.lookup cname comps of+ Just clbis -> clbis+ Nothing -> [] -- TODO: Maybe cache topsort (Graph can do this) @@ -246,24 +400,24 @@ -- Has a prime because it takes a 'PackageDescription' argument -- which may disagree with 'localPkgDescr' in 'LocalBuildInfo'. allTargetsInBuildOrder' :: PackageDescription -> LocalBuildInfo -> [TargetInfo]-allTargetsInBuildOrder' pkg_descr lbi- = map (mkTargetInfo pkg_descr lbi) (Graph.revTopSort (componentGraph lbi))+allTargetsInBuildOrder' pkg_descr lbi@(LocalBuildInfo{componentGraph = compsGraph}) =+ map (mkTargetInfo pkg_descr lbi) (Graph.revTopSort compsGraph) -- | Execute @f@ for every 'TargetInfo' in the package, respecting the -- build dependency order. (TODO: We should use Shake!) -- Has a prime because it takes a 'PackageDescription' argument -- which may disagree with 'localPkgDescr' in 'LocalBuildInfo'. withAllTargetsInBuildOrder' :: PackageDescription -> LocalBuildInfo -> (TargetInfo -> IO ()) -> IO ()-withAllTargetsInBuildOrder' pkg_descr lbi f- = sequence_ [ f target | target <- allTargetsInBuildOrder' pkg_descr lbi ]+withAllTargetsInBuildOrder' pkg_descr lbi f =+ sequence_ [f target | target <- allTargetsInBuildOrder' pkg_descr lbi] -- | Return the list of all targets needed to build the @uids@, in -- the order they need to be built. -- Has a prime because it takes a 'PackageDescription' argument -- which may disagree with 'localPkgDescr' in 'LocalBuildInfo'. neededTargetsInBuildOrder' :: PackageDescription -> LocalBuildInfo -> [UnitId] -> [TargetInfo]-neededTargetsInBuildOrder' pkg_descr lbi uids =- case Graph.closure (componentGraph lbi) uids of+neededTargetsInBuildOrder' pkg_descr lbi@(LocalBuildInfo{componentGraph = compsGraph}) uids =+ case Graph.closure compsGraph uids of Nothing -> error $ "localBuildPlan: missing uids " ++ intercalate ", " (map prettyShow uids) Just clos -> map (mkTargetInfo pkg_descr lbi) (Graph.revTopSort (Graph.fromDistinctList clos)) @@ -272,33 +426,77 @@ -- Has a prime because it takes a 'PackageDescription' argument -- which may disagree with 'localPkgDescr' in 'LocalBuildInfo'. withNeededTargetsInBuildOrder' :: PackageDescription -> LocalBuildInfo -> [UnitId] -> (TargetInfo -> IO ()) -> IO ()-withNeededTargetsInBuildOrder' pkg_descr lbi uids f- = sequence_ [ f target | target <- neededTargetsInBuildOrder' pkg_descr lbi uids ]+withNeededTargetsInBuildOrder' pkg_descr lbi uids f =+ sequence_ [f target | target <- neededTargetsInBuildOrder' pkg_descr lbi uids] -- | Is coverage enabled for test suites? In practice, this requires library -- and executable profiling to be enabled. testCoverage :: LocalBuildInfo -> Bool-testCoverage lbi = exeCoverage lbi && libCoverage lbi+testCoverage (LocalBuildInfo{exeCoverage = exes, libCoverage = libs}) =+ exes && libs +-- | Returns a list of ways, in the order which they should be built, and the+-- way we build executable and foreign library components.+--+-- Ideally all this info should be fixed at configure time and not dependent on+-- additional info but `LocalBuildInfo` is per package (not per component) so it's+-- currently not possible to configure components to be built in certain ways.+buildWays :: LocalBuildInfo -> (Bool -> [BuildWay], Bool -> BuildWay, BuildWay)+buildWays lbi =+ let+ -- enable-library-profiling (enable (static profiling way)) .p_o+ -- enable-shared (enabled dynamic way) .dyn_o+ -- enable-profiling-shared (enable dynamic profilng way) .p_dyn_o+ -- enable-library-vanilla (enable vanilla way) .o+ --+ -- enable-executable-dynamic => build dynamic executables+ -- => --enable-profiling + --enable-executable-dynamic => build dynamic profiled executables+ -- => --enable-profiling => build vanilla profiled executables++ wantedLibWays is_indef =+ [ProfDynWay | withProfLibShared lbi && not is_indef]+ <> [ProfWay | withProfLib lbi]+ -- I don't see why we shouldn't build with dynamic-- indefinite components.+ <> [DynWay | withSharedLib lbi && not is_indef]+ -- MP: Ideally we should have `BuildOptions` on a per component basis, in+ -- which case this `is_indef` check could be moved to configure time.+ <> [StaticWay | withVanillaLib lbi || withStaticLib lbi]++ wantedFLibWay is_dyn_flib =+ case (is_dyn_flib, withProfExe lbi) of+ (True, True) -> ProfDynWay+ (False, True) -> ProfWay+ (True, False) -> DynWay+ (False, False) -> StaticWay++ wantedExeWay =+ case (withDynExe lbi, withProfExe lbi) of+ (True, True) -> ProfDynWay+ (True, False) -> DynWay+ (False, True) -> ProfWay+ (False, False) -> StaticWay+ in+ (wantedLibWays, wantedFLibWay, wantedExeWay)+ ------------------------------------------------------------------------------- -- Stub functions to prevent someone from accidentally defining them -{-# WARNING componentNameTargets, unitIdTarget, allTargetsInBuildOrder, withAllTargetsInBuildOrder, neededTargetsInBuildOrder, withNeededTargetsInBuildOrder "By using this function, you may be introducing a bug where you retrieve a 'Component' which does not have 'HookedBuildInfo' applied to it. See the documentation for 'HookedBuildInfo' for an explanation of the issue. If you have a 'PakcageDescription' handy (NOT from the 'LocalBuildInfo'), try using the primed version of the function, which takes it as an extra argument." #-}-+{-# WARNING componentNameTargets, unitIdTarget, allTargetsInBuildOrder, withAllTargetsInBuildOrder, neededTargetsInBuildOrder, withNeededTargetsInBuildOrder "By using this function, you may be introducing a bug where you retrieve a 'Component' which does not have 'HookedBuildInfo' applied to it. See the documentation for 'HookedBuildInfo' for an explanation of the issue. If you have a 'PackageDescription' handy (NOT from the 'LocalBuildInfo'), try using the primed version of the function, which takes it as an extra argument." #-} componentNameTargets :: LocalBuildInfo -> ComponentName -> [TargetInfo]-componentNameTargets lbi = componentNameTargets' (localPkgDescr lbi) lbi-+componentNameTargets lbi@(LocalBuildInfo{localPkgDescr = pkg}) =+ componentNameTargets' pkg lbi unitIdTarget :: LocalBuildInfo -> UnitId -> Maybe TargetInfo-unitIdTarget lbi = unitIdTarget' (localPkgDescr lbi) lbi-+unitIdTarget lbi@(LocalBuildInfo{localPkgDescr = pkg}) =+ unitIdTarget' pkg lbi allTargetsInBuildOrder :: LocalBuildInfo -> [TargetInfo]-allTargetsInBuildOrder lbi = allTargetsInBuildOrder' (localPkgDescr lbi) lbi-+allTargetsInBuildOrder lbi@(LocalBuildInfo{localPkgDescr = pkg}) =+ allTargetsInBuildOrder' pkg lbi withAllTargetsInBuildOrder :: LocalBuildInfo -> (TargetInfo -> IO ()) -> IO ()-withAllTargetsInBuildOrder lbi = withAllTargetsInBuildOrder' (localPkgDescr lbi) lbi-+withAllTargetsInBuildOrder lbi@(LocalBuildInfo{localPkgDescr = pkg}) =+ withAllTargetsInBuildOrder' pkg lbi neededTargetsInBuildOrder :: LocalBuildInfo -> [UnitId] -> [TargetInfo]-neededTargetsInBuildOrder lbi = neededTargetsInBuildOrder' (localPkgDescr lbi) lbi-+neededTargetsInBuildOrder lbi@(LocalBuildInfo{localPkgDescr = pkg}) =+ neededTargetsInBuildOrder' pkg lbi withNeededTargetsInBuildOrder :: LocalBuildInfo -> [UnitId] -> (TargetInfo -> IO ()) -> IO ()-withNeededTargetsInBuildOrder lbi = withNeededTargetsInBuildOrder' (localPkgDescr lbi) lbi+withNeededTargetsInBuildOrder lbi@(LocalBuildInfo{localPkgDescr = pkg}) =+ withNeededTargetsInBuildOrder' pkg lbi
− src/Distribution/Types/Mixin.hs
@@ -1,93 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Distribution.Types.Mixin (- Mixin(..),- mkMixin,- normaliseMixin,-) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.CabalSpecVersion-import Distribution.Parsec-import Distribution.Pretty-import Distribution.Types.IncludeRenaming-import Distribution.Types.LibraryName-import Distribution.Types.PackageName-import Distribution.Types.UnqualComponentName--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as PP---- |------ /Invariant:/ if 'mixinLibraryName' is 'LSubLibName', it's not--- the same as 'mixinPackageName'. In other words,--- the same invariant as 'Dependency' has.----data Mixin = Mixin { mixinPackageName :: PackageName- , mixinLibraryName :: LibraryName- , mixinIncludeRenaming :: IncludeRenaming }- deriving (Show, Read, Eq, Ord, Typeable, Data, Generic)--instance Binary Mixin-instance Structured Mixin--instance NFData Mixin where rnf = genericRnf--instance Pretty Mixin where- pretty (Mixin pn LMainLibName incl) = pretty pn <+> pretty incl- pretty (Mixin pn (LSubLibName ln) incl) = pretty pn <<>> PP.colon <<>> pretty ln <+> pretty incl---- |------ >>> simpleParsec "mylib" :: Maybe Mixin--- Just (Mixin {mixinPackageName = PackageName "mylib", mixinLibraryName = LMainLibName, mixinIncludeRenaming = IncludeRenaming {includeProvidesRn = DefaultRenaming, includeRequiresRn = DefaultRenaming}})------ >>> simpleParsec "thatlib:sublib" :: Maybe Mixin--- Just (Mixin {mixinPackageName = PackageName "thatlib", mixinLibraryName = LSubLibName (UnqualComponentName "sublib"), mixinIncludeRenaming = IncludeRenaming {includeProvidesRn = DefaultRenaming, includeRequiresRn = DefaultRenaming}})------ >>> simpleParsec "thatlib:thatlib" :: Maybe Mixin--- Just (Mixin {mixinPackageName = PackageName "thatlib", mixinLibraryName = LMainLibName, mixinIncludeRenaming = IncludeRenaming {includeProvidesRn = DefaultRenaming, includeRequiresRn = DefaultRenaming}})------ Sublibrary syntax is accepted since @cabal-version: 3.4@.------ >>> map (`simpleParsec'` "mylib:sub") [CabalSpecV3_0, CabalSpecV3_4] :: [Maybe Mixin]--- [Nothing,Just (Mixin {mixinPackageName = PackageName "mylib", mixinLibraryName = LSubLibName (UnqualComponentName "sub"), mixinIncludeRenaming = IncludeRenaming {includeProvidesRn = DefaultRenaming, includeRequiresRn = DefaultRenaming}})]----instance Parsec Mixin where- parsec = do- pn <- parsec- ln <- P.option LMainLibName $ do- _ <- P.char ':'- versionGuardMultilibs- parsecWarning PWTExperimental "colon specifier is experimental feature (issue #5660)"- LSubLibName <$> parsec- P.spaces- incl <- parsec- return (mkMixin pn ln incl)- where--versionGuardMultilibs :: CabalParsing m => m ()-versionGuardMultilibs = do- csv <- askCabalSpecVersion- when (csv < CabalSpecV3_4) $ fail $ unwords- [ "Sublibrary mixin syntax used."- , "To use this syntax the package needs to specify at least 'cabal-version: 3.4'."- ]---- | Smart constructor of 'Mixin', enforces invariant.--- --- @since 3.4.0.0-mkMixin :: PackageName -> LibraryName -> IncludeRenaming -> Mixin-mkMixin pn (LSubLibName uqn) incl- | packageNameToUnqualComponentName pn == uqn- = Mixin pn LMainLibName incl-mkMixin pn ln incl- = Mixin pn ln incl---- | Restore invariant-normaliseMixin :: Mixin -> Mixin-normaliseMixin (Mixin pn ln incl) = mkMixin pn ln incl
− src/Distribution/Types/Module.hs
@@ -1,46 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}--module Distribution.Types.Module- ( Module(..)- ) where--import Prelude ()-import Distribution.Compat.Prelude--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp-import Distribution.Pretty-import Distribution.Parsec-import Distribution.Types.UnitId-import Distribution.ModuleName---- | A module identity uniquely identifies a Haskell module by--- qualifying a 'ModuleName' with the 'UnitId' which defined--- it. This type distinguishes between two packages--- which provide a module with the same name, or a module--- from the same package compiled with different dependencies.--- There are a few cases where Cabal needs to know about--- module identities, e.g., when writing out reexported modules in--- the 'InstalledPackageInfo'.-data Module =- Module DefUnitId ModuleName- deriving (Generic, Read, Show, Eq, Ord, Typeable, Data)--instance Binary Module-instance Structured Module--instance Pretty Module where- pretty (Module uid mod_name) =- pretty uid <<>> Disp.text ":" <<>> pretty mod_name--instance Parsec Module where- parsec = do- uid <- parsec- _ <- P.char ':'- mod_name <- parsec- return (Module uid mod_name)--instance NFData Module where- rnf (Module uid mod_name) = rnf uid `seq` rnf mod_name
− src/Distribution/Types/ModuleReexport.hs
@@ -1,50 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Distribution.Types.ModuleReexport (- ModuleReexport(..)-) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.ModuleName-import Distribution.Parsec-import Distribution.Pretty-import Distribution.Types.PackageName--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp---- -------------------------------------------------------------------------------- Module re-exports--data ModuleReexport = ModuleReexport {- moduleReexportOriginalPackage :: Maybe PackageName,- moduleReexportOriginalName :: ModuleName,- moduleReexportName :: ModuleName- }- deriving (Eq, Generic, Read, Show, Typeable, Data)--instance Binary ModuleReexport-instance Structured ModuleReexport-instance NFData ModuleReexport where rnf = genericRnf--instance Pretty ModuleReexport where- pretty (ModuleReexport mpkgname origname newname) =- maybe Disp.empty (\pkgname -> pretty pkgname <<>> Disp.char ':') mpkgname- <<>> pretty origname- <+> if newname == origname- then Disp.empty- else Disp.text "as" <+> pretty newname--instance Parsec ModuleReexport where- parsec = do- mpkgname <- P.optional (P.try $ parsec <* P.char ':')- origname <- parsec- newname <- P.option origname $ P.try $ do- P.spaces- _ <- P.string "as"- P.spaces- parsec- return (ModuleReexport mpkgname origname newname)
− src/Distribution/Types/ModuleRenaming.hs
@@ -1,134 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE RankNTypes #-}--module Distribution.Types.ModuleRenaming (- ModuleRenaming(..),- interpModuleRenaming,- defaultRenaming,- isDefaultRenaming,-) where--import Distribution.CabalSpecVersion-import Distribution.Compat.Prelude hiding (empty)-import Prelude ()--import Distribution.ModuleName-import Distribution.Parsec-import Distribution.Pretty--import qualified Data.Map as Map-import qualified Data.Set as Set-import qualified Distribution.Compat.CharParsing as P-import Text.PrettyPrint (hsep, parens, punctuate, text, comma)---- | Renaming applied to the modules provided by a package.--- The boolean indicates whether or not to also include all of the--- original names of modules. Thus, @ModuleRenaming False []@ is--- "don't expose any modules, and @ModuleRenaming True [("Data.Bool", "Bool")]@--- is, "expose all modules, but also expose @Data.Bool@ as @Bool@".--- If a renaming is omitted you get the 'DefaultRenaming'.------ (NB: This is a list not a map so that we can preserve order.)----data ModuleRenaming- -- | A module renaming/thinning; e.g., @(A as B, C as C)@- -- brings @B@ and @C@ into scope.- = ModuleRenaming [(ModuleName, ModuleName)]- -- | The default renaming, bringing all exported modules- -- into scope.- | DefaultRenaming- -- | Hiding renaming, e.g., @hiding (A, B)@, bringing all- -- exported modules into scope except the hidden ones.- | HidingRenaming [ModuleName]- deriving (Show, Read, Eq, Ord, Typeable, Data, Generic)---- | Interpret a 'ModuleRenaming' as a partial map from 'ModuleName'--- to 'ModuleName'. For efficiency, you should partially apply it--- with 'ModuleRenaming' and then reuse it.-interpModuleRenaming :: ModuleRenaming -> ModuleName -> Maybe ModuleName-interpModuleRenaming DefaultRenaming = Just-interpModuleRenaming (ModuleRenaming rns) =- let m = Map.fromList rns- in \k -> Map.lookup k m-interpModuleRenaming (HidingRenaming hs) =- let s = Set.fromList hs- in \k -> if k `Set.member` s then Nothing else Just k---- | The default renaming, if something is specified in @build-depends@--- only.-defaultRenaming :: ModuleRenaming-defaultRenaming = DefaultRenaming---- | Tests if its the default renaming; we can use a more compact syntax--- in 'Distribution.Types.IncludeRenaming.IncludeRenaming' in this case.-isDefaultRenaming :: ModuleRenaming -> Bool-isDefaultRenaming DefaultRenaming = True-isDefaultRenaming _ = False----instance Binary ModuleRenaming where-instance Structured ModuleRenaming where--instance NFData ModuleRenaming where rnf = genericRnf---- NB: parentheses are mandatory, because later we may extend this syntax--- to allow "hiding (A, B)" or other modifier words.-instance Pretty ModuleRenaming where- pretty DefaultRenaming = mempty- pretty (HidingRenaming hides)- = text "hiding" <+> parens (hsep (punctuate comma (map pretty hides)))- pretty (ModuleRenaming rns)- = parens . hsep $ punctuate comma (map dispEntry rns)- where dispEntry (orig, new)- | orig == new = pretty orig- | otherwise = pretty orig <+> text "as" <+> pretty new--instance Parsec ModuleRenaming where- parsec = do- csv <- askCabalSpecVersion- if csv >= CabalSpecV3_0- then moduleRenamingParsec parensLax lexemeParsec- else moduleRenamingParsec parensStrict parsec- where- -- For cabal spec versions < 3.0 white spaces were not skipped- -- after the '(' and ')' tokens in the mixin field. This- -- parser checks the cabal file version and does the correct- -- skipping of spaces.- parensLax p = P.between (P.char '(' >> P.spaces) (P.char ')' >> P.spaces) p- parensStrict p = P.between (P.char '(' >> warnSpaces) (P.char ')') p-- warnSpaces = P.optional $- P.space *> fail "space after parenthesis, use cabal-version: 3.0 or higher"--moduleRenamingParsec- :: CabalParsing m- => (forall a. m a -> m a) -- ^ between parens- -> m ModuleName -- ^ module name parser- -> m ModuleRenaming-moduleRenamingParsec bp mn =- -- NB: try not necessary as the first token is obvious- P.choice [ parseRename, parseHiding, return DefaultRenaming ]- where- cma = P.char ',' >> P.spaces- parseRename = do- rns <- bp parseList- P.spaces- return (ModuleRenaming rns)- parseHiding = do- _ <- P.string "hiding"- P.spaces -- space isn't strictly required as next is an open paren- hides <- bp (P.sepBy mn cma)- return (HidingRenaming hides)- parseList =- P.sepBy parseEntry cma- parseEntry = do- orig <- parsec- P.spaces- P.option (orig, orig) $ do- _ <- P.string "as"- P.skipSpaces1 -- require space after "as"- new <- parsec- P.spaces- return (orig, new)
− src/Distribution/Types/MungedPackageId.hs
@@ -1,83 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Types.MungedPackageId- ( MungedPackageId(..)- , computeCompatPackageId- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Parsec-import Distribution.Pretty-import Distribution.Types.LibraryName-import Distribution.Types.MungedPackageName-import Distribution.Types.PackageId-import Distribution.Version (Version, nullVersion)--import qualified Text.PrettyPrint as Disp---- | A simple pair of a 'MungedPackageName' and 'Version'. 'MungedPackageName' is to--- 'MungedPackageId' as 'PackageName' is to 'PackageId'. See 'MungedPackageName' for more--- info.-data MungedPackageId- = MungedPackageId {- -- | The combined package and component name. see documentation for- -- 'MungedPackageName'.- mungedName :: MungedPackageName,- -- | The version of this package / component, eg 1.2- mungedVersion :: Version- }- deriving (Generic, Read, Show, Eq, Ord, Typeable, Data)--instance Binary MungedPackageId-instance Structured MungedPackageId---- |------ >>> prettyShow $ MungedPackageId (MungedPackageName "servant" LMainLibName) (mkVersion [1,2,3])--- "servant-1.2.3"------ >>> prettyShow $ MungedPackageId (MungedPackageName "servant" (LSubLibName "lackey")) (mkVersion [0,1,2])--- "z-servant-z-lackey-0.1.2"----instance Pretty MungedPackageId where- pretty (MungedPackageId n v)- | v == nullVersion = pretty n -- if no version, don't show version.- | otherwise = pretty n <<>> Disp.char '-' <<>> pretty v---- |------ >>> simpleParsec "foo-bar-0" :: Maybe MungedPackageId--- Just (MungedPackageId {mungedName = MungedPackageName (PackageName "foo-bar") LMainLibName, mungedVersion = mkVersion [0]})------ >>> simpleParsec "foo-bar" :: Maybe MungedPackageId--- Just (MungedPackageId {mungedName = MungedPackageName (PackageName "foo-bar") LMainLibName, mungedVersion = mkVersion []})------ >>> simpleParsec "z-foo-bar-z-baz-0" :: Maybe MungedPackageId--- Just (MungedPackageId {mungedName = MungedPackageName (PackageName "foo-bar") (LSubLibName (UnqualComponentName "baz")), mungedVersion = mkVersion [0]})------ >>> simpleParsec "foo-bar-0-0" :: Maybe MungedPackageId--- Nothing------ >>> simpleParsec "foo-bar.0" :: Maybe MungedPackageId--- Nothing------ >>> simpleParsec "foo-bar.4-2" :: Maybe MungedPackageId--- Nothing----instance Parsec MungedPackageId where- parsec = do- PackageIdentifier pn v <- parsec- return $ MungedPackageId (decodeCompatPackageName pn) v--instance NFData MungedPackageId where- rnf (MungedPackageId name version) = rnf name `seq` rnf version--computeCompatPackageId :: PackageId -> LibraryName -> MungedPackageId-computeCompatPackageId (PackageIdentifier pn vr) ln =- MungedPackageId (MungedPackageName pn ln) vr---- $setup--- >>> :seti -XOverloadedStrings--- >>> import Distribution.Types.Version
− src/Distribution/Types/MungedPackageName.hs
@@ -1,157 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Types.MungedPackageName- ( MungedPackageName (..)- , decodeCompatPackageName- , encodeCompatPackageName- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Parsec-import Distribution.Pretty-import Distribution.Types.LibraryName-import Distribution.Types.PackageName-import Distribution.Types.UnqualComponentName--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp---- | A combination of a package and component name used in various legacy--- interfaces, chiefly bundled with a version as 'MungedPackageId'. It's generally--- better to use a 'UnitId' to opaquely refer to some compilation/packing unit,--- but that doesn't always work, e.g. where a "name" is needed, in which case--- this can be used as a fallback.------ Use 'mkMungedPackageName' and 'unMungedPackageName' to convert from/to a 'String'.------ In @3.0.0.0@ representation was changed from opaque (string) to semantic representation.------ @since 2.0.0.2----data MungedPackageName = MungedPackageName !PackageName !LibraryName- deriving (Generic, Read, Show, Eq, Ord, Typeable, Data)--instance Binary MungedPackageName-instance Structured MungedPackageName-instance NFData MungedPackageName where rnf = genericRnf---- | Computes the package name for a library. If this is the public--- library, it will just be the original package name; otherwise,--- it will be a munged package name recording the original package--- name as well as the name of the internal library.------ A lot of tooling in the Haskell ecosystem assumes that if something--- is installed to the package database with the package name 'foo',--- then it actually is an entry for the (only public) library in package--- 'foo'. With internal packages, this is not necessarily true:--- a public library as well as arbitrarily many internal libraries may--- come from the same package. To prevent tools from getting confused--- in this case, the package name of these internal libraries is munged--- so that they do not conflict the public library proper. A particular--- case where this matters is ghc-pkg: if we don't munge the package--- name, the inplace registration will OVERRIDE a different internal--- library.------ We munge into a reserved namespace, "z-", and encode both the--- component name and the package name of an internal library using the--- following format:------ compat-pkg-name ::= "z-" package-name "-z-" library-name------ where package-name and library-name have "-" ( "z" + ) "-"--- segments encoded by adding an extra "z".------ When we have the public library, the compat-pkg-name is just the--- package-name, no surprises there!------ >>> prettyShow $ MungedPackageName "servant" LMainLibName--- "servant"------ >>> prettyShow $ MungedPackageName "servant" (LSubLibName "lackey")--- "z-servant-z-lackey"----instance Pretty MungedPackageName where- -- First handle the cases where we can just use the original 'PackageName'.- -- This is for the PRIMARY library, and it is non-Backpack, or the- -- indefinite package for us.- pretty = Disp.text . encodeCompatPackageName'---- |------ >>> simpleParsec "servant" :: Maybe MungedPackageName--- Just (MungedPackageName (PackageName "servant") LMainLibName)------ >>> simpleParsec "z-servant-z-lackey" :: Maybe MungedPackageName--- Just (MungedPackageName (PackageName "servant") (LSubLibName (UnqualComponentName "lackey")))------ >>> simpleParsec "z-servant-zz" :: Maybe MungedPackageName--- Just (MungedPackageName (PackageName "z-servant-zz") LMainLibName)----instance Parsec MungedPackageName where- parsec = decodeCompatPackageName' <$> parsecUnqualComponentName------------------------------------------------------------------------------------ ZDashCode conversions------------------------------------------------------------------------------------ | Intended for internal use only------ >>> decodeCompatPackageName "z-servant-z-lackey"--- MungedPackageName (PackageName "servant") (LSubLibName (UnqualComponentName "lackey"))----decodeCompatPackageName :: PackageName -> MungedPackageName-decodeCompatPackageName = decodeCompatPackageName' . unPackageName---- | Intended for internal use only------ >>> encodeCompatPackageName $ MungedPackageName "servant" (LSubLibName "lackey")--- PackageName "z-servant-z-lackey"------ This is used in @cabal-install@ in the Solver.--- May become obsolete as solver moves to per-component solving.----encodeCompatPackageName :: MungedPackageName -> PackageName-encodeCompatPackageName = mkPackageName . encodeCompatPackageName'--decodeCompatPackageName' :: String -> MungedPackageName-decodeCompatPackageName' m =- case m of- 'z':'-':rest | Right [pn, cn] <- explicitEitherParsec parseZDashCode rest- -> MungedPackageName (mkPackageName pn) (LSubLibName (mkUnqualComponentName cn))- s -> MungedPackageName (mkPackageName s) LMainLibName--encodeCompatPackageName' :: MungedPackageName -> String-encodeCompatPackageName' (MungedPackageName pn LMainLibName) = unPackageName pn-encodeCompatPackageName' (MungedPackageName pn (LSubLibName uqn)) =- "z-" ++ zdashcode (unPackageName pn) ++- "-z-" ++ zdashcode (unUnqualComponentName uqn)--zdashcode :: String -> String-zdashcode s = go s (Nothing :: Maybe Int) []- where go [] _ r = reverse r- go ('-':z) (Just n) r | n > 0 = go z (Just 0) ('-':'z':r)- go ('-':z) _ r = go z (Just 0) ('-':r)- go ('z':z) (Just n) r = go z (Just (n+1)) ('z':r)- go (c:z) _ r = go z Nothing (c:r)--parseZDashCode :: CabalParsing m => m [String]-parseZDashCode = do- ns <- toList <$> P.sepByNonEmpty (some (P.satisfy (/= '-'))) (P.char '-')- return (go ns)- where- go ns = case break (=="z") ns of- (_, []) -> [paste ns]- (as, "z":bs) -> paste as : go bs- _ -> error "parseZDashCode: go"- unZ :: String -> String- unZ "" = error "parseZDashCode: unZ"- unZ r@('z':zs) | all (=='z') zs = zs- | otherwise = r- unZ r = r- paste :: [String] -> String- paste = intercalate "-" . map unZ---- $setup--- >>> :seti -XOverloadedStrings
− src/Distribution/Types/PackageDescription.hs
@@ -1,457 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}---------------------------------------------------------------------------------- |--- Module : Distribution.Types.PackageDescription--- Copyright : Isaac Jones 2003-2005--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ This defines the data structure for the @.cabal@ file format. There are--- several parts to this structure. It has top level info and then 'Library',--- 'Executable', 'TestSuite', and 'Benchmark' sections each of which have--- associated 'BuildInfo' data that's used to build the library, exe, test, or--- benchmark. To further complicate things there is both a 'PackageDescription'--- and a 'GenericPackageDescription'. This distinction relates to cabal--- configurations. When we initially read a @.cabal@ file we get a--- 'GenericPackageDescription' which has all the conditional sections.--- Before actually building a package we have to decide--- on each conditional. Once we've done that we get a 'PackageDescription'.--- It was done this way initially to avoid breaking too much stuff when the--- feature was introduced. It could probably do with being rationalised at some--- point to make it simpler.--module Distribution.Types.PackageDescription (- PackageDescription(..),- license,- license',- buildType,- emptyPackageDescription,- hasPublicLib,- hasLibs,- allLibraries,- withLib,- hasExes,- withExe,- hasTests,- withTest,- hasBenchmarks,- withBenchmark,- hasForeignLibs,- withForeignLib,- allBuildInfo,- enabledBuildInfos,- allBuildDepends,- enabledBuildDepends,- updatePackageDescription,- pkgComponents,- pkgBuildableComponents,- enabledComponents,- lookupComponent,- getComponent,- ) where--import Prelude ()-import Distribution.Compat.Prelude--import Control.Monad ((<=<))---- lens-import qualified Distribution.Types.BuildInfo.Lens as L-import Distribution.Types.Library-import Distribution.Types.TestSuite-import Distribution.Types.Executable-import Distribution.Types.Benchmark-import Distribution.Types.ForeignLib--import Distribution.Types.Component-import Distribution.Types.ComponentRequestedSpec-import Distribution.Types.Dependency-import Distribution.Types.PackageId-import Distribution.Types.ComponentName-import Distribution.Types.PackageName-import Distribution.Types.UnqualComponentName-import Distribution.Types.SetupBuildInfo-import Distribution.Types.BuildInfo-import Distribution.Types.BuildType-import Distribution.Types.SourceRepo-import Distribution.Types.HookedBuildInfo--import Distribution.CabalSpecVersion-import Distribution.Compiler-import Distribution.License-import Distribution.Package-import Distribution.Version-import Distribution.Utils.Path-import Distribution.Utils.ShortText--import qualified Distribution.SPDX as SPDX---- -------------------------------------------------------------------------------- The PackageDescription type---- | This data type is the internal representation of the file @pkg.cabal@.--- It contains two kinds of information about the package: information--- which is needed for all packages, such as the package name and version, and--- information which is needed for the simple build system only, such as--- the compiler options and library name.----data PackageDescription- = PackageDescription {- -- the following are required by all packages:-- -- | The version of the Cabal spec that this package description uses.- specVersion :: CabalSpecVersion,- package :: PackageIdentifier,- licenseRaw :: Either SPDX.License License,- licenseFiles :: [SymbolicPath PackageDir LicenseFile],- copyright :: !ShortText,- maintainer :: !ShortText,- author :: !ShortText,- stability :: !ShortText,- testedWith :: [(CompilerFlavor,VersionRange)],- homepage :: !ShortText,- pkgUrl :: !ShortText,- bugReports :: !ShortText,- sourceRepos :: [SourceRepo],- synopsis :: !ShortText, -- ^A one-line summary of this package- description :: !ShortText, -- ^A more verbose description of this package- category :: !ShortText,- customFieldsPD :: [(String,String)], -- ^Custom fields starting- -- with x-, stored in a- -- simple assoc-list.-- -- | The original @build-type@ value as parsed from the- -- @.cabal@ file without defaulting. See also 'buildType'.- --- -- @since 2.2- buildTypeRaw :: Maybe BuildType,- setupBuildInfo :: Maybe SetupBuildInfo,- -- components- library :: Maybe Library,- subLibraries :: [Library],- executables :: [Executable],- foreignLibs :: [ForeignLib],- testSuites :: [TestSuite],- benchmarks :: [Benchmark],- -- files- dataFiles :: [FilePath],- dataDir :: FilePath,- extraSrcFiles :: [FilePath],- extraTmpFiles :: [FilePath],- extraDocFiles :: [FilePath]- }- deriving (Generic, Show, Read, Eq, Typeable, Data)--instance Binary PackageDescription-instance Structured PackageDescription--instance NFData PackageDescription where rnf = genericRnf--instance Package PackageDescription where- packageId = package---- | The SPDX 'LicenseExpression' of the package.------ @since 2.2.0.0-license :: PackageDescription -> SPDX.License-license = license' . licenseRaw---- | See 'license'.------ @since 2.2.0.0-license' :: Either SPDX.License License -> SPDX.License-license' = either id licenseToSPDX---- | The effective @build-type@ after applying defaulting rules.------ The original @build-type@ value parsed is stored in the--- 'buildTypeRaw' field. However, the @build-type@ field is optional--- and can therefore be empty in which case we need to compute the--- /effective/ @build-type@. This function implements the following--- defaulting rules:------ * For @cabal-version:2.0@ and below, default to the @Custom@--- build-type unconditionally.------ * Otherwise, if a @custom-setup@ stanza is defined, default to--- the @Custom@ build-type; else default to @Simple@ build-type.------ @since 2.2-buildType :: PackageDescription -> BuildType-buildType pkg- | specVersion pkg >= CabalSpecV2_2- = fromMaybe newDefault (buildTypeRaw pkg)- | otherwise -- cabal-version < 2.1- = fromMaybe Custom (buildTypeRaw pkg)- where- newDefault | isNothing (setupBuildInfo pkg) = Simple- | otherwise = Custom--emptyPackageDescription :: PackageDescription-emptyPackageDescription- = PackageDescription {- package = PackageIdentifier (mkPackageName "")- nullVersion,- licenseRaw = Right UnspecifiedLicense, -- TODO:- licenseFiles = [],- specVersion = CabalSpecV1_0,- buildTypeRaw = Nothing,- copyright = mempty,- maintainer = mempty,- author = mempty,- stability = mempty,- testedWith = [],- homepage = mempty,- pkgUrl = mempty,- bugReports = mempty,- sourceRepos = [],- synopsis = mempty,- description = mempty,- category = mempty,- customFieldsPD = [],- setupBuildInfo = Nothing,- library = Nothing,- subLibraries = [],- foreignLibs = [],- executables = [],- testSuites = [],- benchmarks = [],- dataFiles = [],- dataDir = ".",- extraSrcFiles = [],- extraTmpFiles = [],- extraDocFiles = []- }---- ------------------------------------------------------------------------------ The Library type---- | Does this package have a buildable PUBLIC library?-hasPublicLib :: PackageDescription -> Bool-hasPublicLib p =- case library p of- Just lib -> buildable (libBuildInfo lib)- Nothing -> False---- | Does this package have any libraries?-hasLibs :: PackageDescription -> Bool-hasLibs p = any (buildable . libBuildInfo) (allLibraries p)--allLibraries :: PackageDescription -> [Library]-allLibraries p = maybeToList (library p) ++ subLibraries p---- | If the package description has a buildable library section,--- call the given function with the library build info as argument.--- You probably want 'withLibLBI' if you have a 'LocalBuildInfo',--- see the note in--- "Distribution.Types.ComponentRequestedSpec#buildable_vs_enabled_components"--- for more information.-withLib :: PackageDescription -> (Library -> IO ()) -> IO ()-withLib pkg_descr f =- sequence_ [f lib | lib <- allLibraries pkg_descr, buildable (libBuildInfo lib)]---- ------------------------------------------------------------------------------ The Executable type---- |does this package have any executables?-hasExes :: PackageDescription -> Bool-hasExes p = any (buildable . buildInfo) (executables p)---- | Perform the action on each buildable 'Executable' in the package--- description. You probably want 'withExeLBI' if you have a--- 'LocalBuildInfo', see the note in--- "Distribution.Types.ComponentRequestedSpec#buildable_vs_enabled_components"--- for more information.-withExe :: PackageDescription -> (Executable -> IO ()) -> IO ()-withExe pkg_descr f =- sequence_ [f exe | exe <- executables pkg_descr, buildable (buildInfo exe)]---- ------------------------------------------------------------------------------ The TestSuite type---- | Does this package have any test suites?-hasTests :: PackageDescription -> Bool-hasTests = any (buildable . testBuildInfo) . testSuites---- | Perform an action on each buildable 'TestSuite' in a package.--- You probably want 'withTestLBI' if you have a 'LocalBuildInfo', see the note in--- "Distribution.Types.ComponentRequestedSpec#buildable_vs_enabled_components"--- for more information.--withTest :: PackageDescription -> (TestSuite -> IO ()) -> IO ()-withTest pkg_descr f =- sequence_ [ f test | test <- testSuites pkg_descr, buildable (testBuildInfo test) ]---- ------------------------------------------------------------------------------ The Benchmark type---- | Does this package have any benchmarks?-hasBenchmarks :: PackageDescription -> Bool-hasBenchmarks = any (buildable . benchmarkBuildInfo) . benchmarks---- | Perform an action on each buildable 'Benchmark' in a package.--- You probably want 'withBenchLBI' if you have a 'LocalBuildInfo', see the note in--- "Distribution.Types.ComponentRequestedSpec#buildable_vs_enabled_components"--- for more information.--withBenchmark :: PackageDescription -> (Benchmark -> IO ()) -> IO ()-withBenchmark pkg_descr f =- sequence_ [f bench | bench <- benchmarks pkg_descr, buildable (benchmarkBuildInfo bench)]---- ------------------------------------------------------------------------------ The ForeignLib type---- | Does this package have any foreign libraries?-hasForeignLibs :: PackageDescription -> Bool-hasForeignLibs p = any (buildable . foreignLibBuildInfo) (foreignLibs p)---- | Perform the action on each buildable 'ForeignLib' in the package--- description.-withForeignLib :: PackageDescription -> (ForeignLib -> IO ()) -> IO ()-withForeignLib pkg_descr f =- sequence_ [ f flib- | flib <- foreignLibs pkg_descr- , buildable (foreignLibBuildInfo flib)- ]---- ------------------------------------------------------------------------------ The BuildInfo type---- | All 'BuildInfo' in the 'PackageDescription':--- libraries, executables, test-suites and benchmarks.------ Useful for implementing package checks.-allBuildInfo :: PackageDescription -> [BuildInfo]-allBuildInfo pkg_descr = [ bi | lib <- allLibraries pkg_descr- , let bi = libBuildInfo lib ]- ++ [ bi | flib <- foreignLibs pkg_descr- , let bi = foreignLibBuildInfo flib ]- ++ [ bi | exe <- executables pkg_descr- , let bi = buildInfo exe ]- ++ [ bi | tst <- testSuites pkg_descr- , let bi = testBuildInfo tst ]- ++ [ bi | tst <- benchmarks pkg_descr- , let bi = benchmarkBuildInfo tst ]---- | Return all of the 'BuildInfo's of enabled components, i.e., all of--- the ones that would be built if you run @./Setup build@.-enabledBuildInfos :: PackageDescription -> ComponentRequestedSpec -> [BuildInfo]-enabledBuildInfos pkg enabled =- [ componentBuildInfo comp- | comp <- enabledComponents pkg enabled ]----- --------------------------------------------------------------- * Utils--- ---------------------------------------------------------------- | Get the combined build-depends entries of all components.-allBuildDepends :: PackageDescription -> [Dependency]-allBuildDepends = targetBuildDepends <=< allBuildInfo---- | Get the combined build-depends entries of all enabled components, per the--- given request spec.-enabledBuildDepends :: PackageDescription -> ComponentRequestedSpec -> [Dependency]-enabledBuildDepends spec pd = targetBuildDepends =<< enabledBuildInfos spec pd---updatePackageDescription :: HookedBuildInfo -> PackageDescription -> PackageDescription-updatePackageDescription (mb_lib_bi, exe_bi) p- = p{ executables = updateExecutables exe_bi (executables p)- , library = updateLibrary mb_lib_bi (library p) }- where- updateLibrary :: Maybe BuildInfo -> Maybe Library -> Maybe Library- updateLibrary (Just bi) (Just lib) = Just (lib{libBuildInfo = bi `mappend` libBuildInfo lib})- updateLibrary Nothing mb_lib = mb_lib- updateLibrary (Just _) Nothing = Nothing-- updateExecutables :: [(UnqualComponentName, BuildInfo)] -- ^[(exeName, new buildinfo)]- -> [Executable] -- ^list of executables to update- -> [Executable] -- ^list with exeNames updated- updateExecutables exe_bi' executables' = foldr updateExecutable executables' exe_bi'-- updateExecutable :: (UnqualComponentName, BuildInfo) -- ^(exeName, new buildinfo)- -> [Executable] -- ^list of executables to update- -> [Executable] -- ^list with exeName updated- updateExecutable _ [] = []- updateExecutable exe_bi'@(name,bi) (exe:exes)- | exeName exe == name = exe{buildInfo = bi `mappend` buildInfo exe} : exes- | otherwise = exe : updateExecutable exe_bi' exes---- -------------------------------------------------------------------------------- Source-representation of buildable components---- | All the components in the package.----pkgComponents :: PackageDescription -> [Component]-pkgComponents pkg =- [ CLib lib | lib <- allLibraries pkg ]- ++ [ CFLib flib | flib <- foreignLibs pkg ]- ++ [ CExe exe | exe <- executables pkg ]- ++ [ CTest tst | tst <- testSuites pkg ]- ++ [ CBench bm | bm <- benchmarks pkg ]---- | A list of all components in the package that are buildable,--- i.e., were not marked with @buildable: False@. This does NOT--- indicate if we are actually going to build the component,--- see 'enabledComponents' instead.------ @since 2.0.0.2----pkgBuildableComponents :: PackageDescription -> [Component]-pkgBuildableComponents = filter componentBuildable . pkgComponents---- | A list of all components in the package that are enabled.------ @since 2.0.0.2----enabledComponents :: PackageDescription -> ComponentRequestedSpec -> [Component]-enabledComponents pkg enabled = filter (componentEnabled enabled) $ pkgBuildableComponents pkg--lookupComponent :: PackageDescription -> ComponentName -> Maybe Component-lookupComponent pkg (CLibName name) =- fmap CLib $ find ((name ==) . libName) (allLibraries pkg)-lookupComponent pkg (CFLibName name) =- fmap CFLib $ find ((name ==) . foreignLibName) (foreignLibs pkg)-lookupComponent pkg (CExeName name) =- fmap CExe $ find ((name ==) . exeName) (executables pkg)-lookupComponent pkg (CTestName name) =- fmap CTest $ find ((name ==) . testName) (testSuites pkg)-lookupComponent pkg (CBenchName name) =- fmap CBench $ find ((name ==) . benchmarkName) (benchmarks pkg)--getComponent :: PackageDescription -> ComponentName -> Component-getComponent pkg cname =- case lookupComponent pkg cname of- Just cpnt -> cpnt- Nothing -> missingComponent- where- missingComponent =- error $ "internal error: the package description contains no "- ++ "component corresponding to " ++ show cname---- -------------------------------------------------------------------------------- Traversal Instances--instance L.HasBuildInfos PackageDescription where- traverseBuildInfos f (PackageDescription a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16 a17 a18 a19- x1 x2 x3 x4 x5 x6- a20 a21 a22 a23 a24) =- PackageDescription a1 a2 a3 a4 a5 a6 a7 a8 a9 a10 a11 a12 a13 a14 a15 a16 a17 a18 a19- <$> (traverse . L.buildInfo) f x1 -- library- <*> (traverse . L.buildInfo) f x2 -- sub libraries- <*> (traverse . L.buildInfo) f x3 -- executables- <*> (traverse . L.buildInfo) f x4 -- foreign libs- <*> (traverse . L.buildInfo) f x5 -- test suites- <*> (traverse . L.buildInfo) f x6 -- benchmarks- <*> pure a20 -- data files- <*> pure a21 -- data dir- <*> pure a22 -- extra src files- <*> pure a23 -- extra temp files- <*> pure a24 -- extra doc files
− src/Distribution/Types/PackageDescription/Lens.hs
@@ -1,221 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RankNTypes #-}-module Distribution.Types.PackageDescription.Lens (- PackageDescription,- module Distribution.Types.PackageDescription.Lens,- ) where--import Distribution.Compat.Lens-import Distribution.Compat.Prelude-import Prelude ()--import Distribution.CabalSpecVersion (CabalSpecVersion)-import Distribution.Compiler (CompilerFlavor)-import Distribution.License (License)-import Distribution.ModuleName (ModuleName)-import Distribution.Types.Benchmark (Benchmark, benchmarkModules)-import Distribution.Types.Benchmark.Lens (benchmarkBuildInfo, benchmarkName)-import Distribution.Types.BuildInfo (BuildInfo)-import Distribution.Types.BuildType (BuildType)-import Distribution.Types.ComponentName (ComponentName (..))-import Distribution.Types.Executable (Executable, exeModules)-import Distribution.Types.Executable.Lens (exeBuildInfo, exeName)-import Distribution.Types.ForeignLib (ForeignLib, foreignLibModules)-import Distribution.Types.ForeignLib.Lens (foreignLibBuildInfo, foreignLibName)-import Distribution.Types.Library (Library, explicitLibModules)-import Distribution.Types.Library.Lens (libBuildInfo, libName)-import Distribution.Types.PackageDescription (PackageDescription)-import Distribution.Types.PackageId (PackageIdentifier)-import Distribution.Types.SetupBuildInfo (SetupBuildInfo)-import Distribution.Types.SourceRepo (SourceRepo)-import Distribution.Types.TestSuite (TestSuite, testModules)-import Distribution.Types.TestSuite.Lens (testBuildInfo, testName)-import Distribution.Utils.Path (LicenseFile, PackageDir, SymbolicPath)-import Distribution.Utils.ShortText (ShortText)-import Distribution.Version (VersionRange)--import qualified Distribution.SPDX as SPDX-import qualified Distribution.Types.PackageDescription as T--package :: Lens' PackageDescription PackageIdentifier-package f s = fmap (\x -> s { T.package = x }) (f (T.package s))-{-# INLINE package #-}--licenseRaw :: Lens' PackageDescription (Either SPDX.License License)-licenseRaw f s = fmap (\x -> s { T.licenseRaw = x }) (f (T.licenseRaw s))-{-# INLINE licenseRaw #-}--licenseFiles :: Lens' PackageDescription [SymbolicPath PackageDir LicenseFile]-licenseFiles f s = fmap (\x -> s { T.licenseFiles = x }) (f (T.licenseFiles s))-{-# INLINE licenseFiles #-}--copyright :: Lens' PackageDescription ShortText-copyright f s = fmap (\x -> s { T.copyright = x }) (f (T.copyright s))-{-# INLINE copyright #-}--maintainer :: Lens' PackageDescription ShortText-maintainer f s = fmap (\x -> s { T.maintainer = x }) (f (T.maintainer s))-{-# INLINE maintainer #-}--author :: Lens' PackageDescription ShortText-author f s = fmap (\x -> s { T.author = x }) (f (T.author s))-{-# INLINE author #-}--stability :: Lens' PackageDescription ShortText-stability f s = fmap (\x -> s { T.stability = x }) (f (T.stability s))-{-# INLINE stability #-}--testedWith :: Lens' PackageDescription [(CompilerFlavor,VersionRange)]-testedWith f s = fmap (\x -> s { T.testedWith = x }) (f (T.testedWith s))-{-# INLINE testedWith #-}--homepage :: Lens' PackageDescription ShortText-homepage f s = fmap (\x -> s { T.homepage = x }) (f (T.homepage s))-{-# INLINE homepage #-}--pkgUrl :: Lens' PackageDescription ShortText-pkgUrl f s = fmap (\x -> s { T.pkgUrl = x }) (f (T.pkgUrl s))-{-# INLINE pkgUrl #-}--bugReports :: Lens' PackageDescription ShortText-bugReports f s = fmap (\x -> s { T.bugReports = x }) (f (T.bugReports s))-{-# INLINE bugReports #-}--sourceRepos :: Lens' PackageDescription [SourceRepo]-sourceRepos f s = fmap (\x -> s { T.sourceRepos = x }) (f (T.sourceRepos s))-{-# INLINE sourceRepos #-}--synopsis :: Lens' PackageDescription ShortText-synopsis f s = fmap (\x -> s { T.synopsis = x }) (f (T.synopsis s))-{-# INLINE synopsis #-}--description :: Lens' PackageDescription ShortText-description f s = fmap (\x -> s { T.description = x }) (f (T.description s))-{-# INLINE description #-}--category :: Lens' PackageDescription ShortText-category f s = fmap (\x -> s { T.category = x }) (f (T.category s))-{-# INLINE category #-}--customFieldsPD :: Lens' PackageDescription [(String,String)]-customFieldsPD f s = fmap (\x -> s { T.customFieldsPD = x }) (f (T.customFieldsPD s))-{-# INLINE customFieldsPD #-}--specVersion :: Lens' PackageDescription CabalSpecVersion-specVersion f s = fmap (\x -> s { T.specVersion = x }) (f (T.specVersion s))-{-# INLINE specVersion #-}--buildTypeRaw :: Lens' PackageDescription (Maybe BuildType)-buildTypeRaw f s = fmap (\x -> s { T.buildTypeRaw = x }) (f (T.buildTypeRaw s))-{-# INLINE buildTypeRaw #-}--setupBuildInfo :: Lens' PackageDescription (Maybe SetupBuildInfo)-setupBuildInfo f s = fmap (\x -> s { T.setupBuildInfo = x }) (f (T.setupBuildInfo s))-{-# INLINE setupBuildInfo #-}--library :: Lens' PackageDescription (Maybe Library)-library f s = fmap (\x -> s { T.library = x }) (f (T.library s))-{-# INLINE library #-}--subLibraries :: Lens' PackageDescription [Library]-subLibraries f s = fmap (\x -> s { T.subLibraries = x }) (f (T.subLibraries s))-{-# INLINE subLibraries #-}--executables :: Lens' PackageDescription [Executable]-executables f s = fmap (\x -> s { T.executables = x }) (f (T.executables s))-{-# INLINE executables #-}--foreignLibs :: Lens' PackageDescription [ForeignLib]-foreignLibs f s = fmap (\x -> s { T.foreignLibs = x }) (f (T.foreignLibs s))-{-# INLINE foreignLibs #-}--testSuites :: Lens' PackageDescription [TestSuite]-testSuites f s = fmap (\x -> s { T.testSuites = x }) (f (T.testSuites s))-{-# INLINE testSuites #-}--benchmarks :: Lens' PackageDescription [Benchmark]-benchmarks f s = fmap (\x -> s { T.benchmarks = x }) (f (T.benchmarks s))-{-# INLINE benchmarks #-}--dataFiles :: Lens' PackageDescription [FilePath]-dataFiles f s = fmap (\x -> s { T.dataFiles = x }) (f (T.dataFiles s))-{-# INLINE dataFiles #-}--dataDir :: Lens' PackageDescription FilePath-dataDir f s = fmap (\x -> s { T.dataDir = x }) (f (T.dataDir s))-{-# INLINE dataDir #-}--extraSrcFiles :: Lens' PackageDescription [String]-extraSrcFiles f s = fmap (\x -> s { T.extraSrcFiles = x }) (f (T.extraSrcFiles s))-{-# INLINE extraSrcFiles #-}--extraTmpFiles :: Lens' PackageDescription [String]-extraTmpFiles f s = fmap (\x -> s { T.extraTmpFiles = x }) (f (T.extraTmpFiles s))-{-# INLINE extraTmpFiles #-}--extraDocFiles :: Lens' PackageDescription [String]-extraDocFiles f s = fmap (\x -> s { T.extraDocFiles = x }) (f (T.extraDocFiles s))-{-# INLINE extraDocFiles #-}---- | @since 3.0.0.0-allLibraries :: Traversal' PackageDescription Library-allLibraries f pd = mk <$> traverse f (T.library pd) <*> traverse f (T.subLibraries pd)- where- mk l ls = pd { T.library = l, T.subLibraries = ls }---- | @since 2.4-componentModules :: Monoid r => ComponentName -> Getting r PackageDescription [ModuleName]-componentModules cname = case cname of- CLibName name ->- componentModules' name allLibraries libName explicitLibModules- CFLibName name ->- componentModules' name (foreignLibs . traverse) foreignLibName foreignLibModules- CExeName name ->- componentModules' name (executables . traverse) exeName exeModules- CTestName name ->- componentModules' name (testSuites . traverse) testName testModules- CBenchName name ->- componentModules' name (benchmarks . traverse) benchmarkName benchmarkModules- where- componentModules'- :: (Eq name, Monoid r)- => name- -> Traversal' PackageDescription a- -> Lens' a name- -> (a -> [ModuleName])- -> Getting r PackageDescription [ModuleName]- componentModules' name pdL nameL modules =- pdL- . filtered ((== name) . view nameL)- . getting modules-- filtered :: (a -> Bool) -> Traversal' a a- filtered p f s = if p s then f s else pure s---- | @since 2.4-componentBuildInfo :: ComponentName -> Traversal' PackageDescription BuildInfo-componentBuildInfo cname = case cname of- CLibName name ->- componentBuildInfo' name allLibraries libName libBuildInfo- CFLibName name ->- componentBuildInfo' name (foreignLibs . traverse) foreignLibName foreignLibBuildInfo- CExeName name ->- componentBuildInfo' name (executables . traverse) exeName exeBuildInfo- CTestName name ->- componentBuildInfo' name (testSuites . traverse) testName testBuildInfo- CBenchName name ->- componentBuildInfo' name (benchmarks . traverse) benchmarkName benchmarkBuildInfo- where- componentBuildInfo' :: Eq name- => name- -> Traversal' PackageDescription a- -> Lens' a name- -> Traversal' a BuildInfo- -> Traversal' PackageDescription BuildInfo- componentBuildInfo' name pdL nameL biL =- pdL- . filtered ((== name) . view nameL)- . biL-- filtered :: (a -> Bool) -> Traversal' a a- filtered p f s = if p s then f s else pure s
− src/Distribution/Types/PackageId.hs
@@ -1,74 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Types.PackageId- ( PackageIdentifier(..)- , PackageId- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Parsec (Parsec (..), simpleParsec)-import Distribution.Pretty-import Distribution.Types.PackageName-import Distribution.Version (Version, nullVersion)--import qualified Data.List.NonEmpty as NE-import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp---- | Type alias so we can use the shorter name PackageId.-type PackageId = PackageIdentifier---- | The name and version of a package.-data PackageIdentifier- = PackageIdentifier {- pkgName :: PackageName, -- ^The name of this package, eg. foo- pkgVersion :: Version -- ^the version of this package, eg 1.2- }- deriving (Generic, Read, Show, Eq, Ord, Typeable, Data)--instance Binary PackageIdentifier-instance Structured PackageIdentifier--instance Pretty PackageIdentifier where- pretty (PackageIdentifier n v)- | v == nullVersion = pretty n -- if no version, don't show version.- | otherwise = pretty n <<>> Disp.char '-' <<>> pretty v---- |------ >>> simpleParsec "foo-bar-0" :: Maybe PackageIdentifier--- Just (PackageIdentifier {pkgName = PackageName "foo-bar", pkgVersion = mkVersion [0]})------ >>> simpleParsec "foo-bar" :: Maybe PackageIdentifier--- Just (PackageIdentifier {pkgName = PackageName "foo-bar", pkgVersion = mkVersion []})------ /Note:/ Stricter than 'Text' instance------ >>> simpleParsec "foo-bar-0-0" :: Maybe PackageIdentifier--- Nothing------ >>> simpleParsec "foo-bar.0" :: Maybe PackageIdentifier--- Nothing------ >>> simpleParsec "foo-bar.4-2" :: Maybe PackageIdentifier--- Nothing------ >>> simpleParsec "1.2.3" :: Maybe PackageIdentifier--- Nothing----instance Parsec PackageIdentifier where- parsec = do- xs' <- P.sepByNonEmpty component (P.char '-')- (v, xs) <- case simpleParsec (NE.last xs') of- Nothing -> return (nullVersion, toList xs') -- all components are version- Just v -> return (v, NE.init xs')- if not (null xs) && all (\c -> all (/= '.') c && not (all isDigit c)) xs- then return $ PackageIdentifier (mkPackageName (intercalate "-" xs)) v- else fail "all digits or a dot in a portion of package name"- where- component = P.munch1 (\c -> isAlphaNum c || c == '.')--instance NFData PackageIdentifier where- rnf (PackageIdentifier name version) = rnf name `seq` rnf version
− src/Distribution/Types/PackageId/Lens.hs
@@ -1,22 +0,0 @@-module Distribution.Types.PackageId.Lens (- PackageIdentifier,- module Distribution.Types.PackageId.Lens,- ) where--import Distribution.Compat.Lens-import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Types.PackageId (PackageIdentifier)-import Distribution.Types.PackageName (PackageName)-import Distribution.Version (Version)--import qualified Distribution.Types.PackageId as T--pkgName :: Lens' PackageIdentifier PackageName-pkgName f s = fmap (\x -> s { T.pkgName = x }) (f (T.pkgName s))-{-# INLINE pkgName #-}--pkgVersion :: Lens' PackageIdentifier Version-pkgVersion f s = fmap (\x -> s { T.pkgVersion = x }) (f (T.pkgVersion s))-{-# INLINE pkgVersion #-}
− src/Distribution/Types/PackageName.hs
@@ -1,73 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Types.PackageName- ( PackageName- , unPackageName, mkPackageName- , unPackageNameST, mkPackageNameST- ) where--import Prelude ()-import Distribution.Compat.Prelude-import Distribution.Utils.ShortText--import qualified Text.PrettyPrint as Disp-import Distribution.Pretty-import Distribution.Parsec---- | A package name.------ Use 'mkPackageName' and 'unPackageName' to convert from/to a--- 'String'.------ This type is opaque since @Cabal-2.0@------ @since 2.0.0.2-newtype PackageName = PackageName ShortText- deriving (Generic, Read, Show, Eq, Ord, Typeable, Data)---- | Convert 'PackageName' to 'String'-unPackageName :: PackageName -> String-unPackageName (PackageName s) = fromShortText s---- | @since 3.4.0.0-unPackageNameST :: PackageName -> ShortText-unPackageNameST (PackageName s) = s---- | Construct a 'PackageName' from a 'String'------ 'mkPackageName' is the inverse to 'unPackageName'------ Note: No validations are performed to ensure that the resulting--- 'PackageName' is valid------ @since 2.0.0.2-mkPackageName :: String -> PackageName-mkPackageName = PackageName . toShortText---- | Construct a 'PackageName' from a 'ShortText'------ Note: No validations are performed to ensure that the resulting--- 'PackageName' is valid------ @since 3.4.0.0-mkPackageNameST :: ShortText -> PackageName-mkPackageNameST = PackageName---- | 'mkPackageName'------ @since 2.0.0.2-instance IsString PackageName where- fromString = mkPackageName--instance Binary PackageName-instance Structured PackageName--instance Pretty PackageName where- pretty = Disp.text . unPackageName--instance Parsec PackageName where- parsec = mkPackageName <$> parsecUnqualComponentName--instance NFData PackageName where- rnf (PackageName pkg) = rnf pkg
src/Distribution/Types/PackageName/Magic.hs view
@@ -11,9 +11,13 @@ nonExistentPackageThisIsCabalBug :: PackageName nonExistentPackageThisIsCabalBug = mkPackageName "nonexistent-package-this-is-a-cabal-bug" --- | Used by @cabal new-repl@ and @cabal new-run@+-- | Used by @cabal new-repl@, @cabal new-run@ and @cabal new-build@ fakePackageName :: PackageName fakePackageName = mkPackageName "fake-package"++-- | Used by @cabal new-run@ and @cabal new-build@+fakePackageCabalFileName :: FilePath+fakePackageCabalFileName = "fake-package.cabal" -- | 'fakePackageName' with 'version0'. fakePackageId :: PackageId
− src/Distribution/Types/PackageVersionConstraint.hs
@@ -1,74 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Types.PackageVersionConstraint (- PackageVersionConstraint(..),- thisPackageVersionConstraint,- simplifyPackageVersionConstraint,-) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Parsec-import Distribution.Pretty-import Distribution.Types.PackageId-import Distribution.Types.PackageName-import Distribution.Types.Version-import Distribution.Types.VersionRange.Internal-import Distribution.Version (simplifyVersionRange)--import qualified Distribution.Compat.CharParsing as P---- | A version constraint on a package. Different from 'ExeDependency' and--- 'Dependency' since it does not specify the need for a component, not even--- the main library.--- There are a few places in the codebase where 'Dependency' was used where--- 'PackageVersionConstraint' is not used instead (#5570).-data PackageVersionConstraint = PackageVersionConstraint PackageName VersionRange- deriving (Generic, Read, Show, Eq, Typeable, Data)--instance Binary PackageVersionConstraint-instance Structured PackageVersionConstraint-instance NFData PackageVersionConstraint where rnf = genericRnf--instance Pretty PackageVersionConstraint where- -- Cannot do: PackageVersionConstraint have to be parseable- -- as Dependency, due roundtrip problems. (e.g. talking to old ./Setup).- --- -- pretty (PackageVersionConstraint name (ThisVersion ver)) =- -- pretty (PackageIdentifier name ver)- pretty (PackageVersionConstraint name ver) =- pretty name <+> pretty ver---- |------ >>> simpleParsec "foo" :: Maybe PackageVersionConstraint--- Just (PackageVersionConstraint (PackageName "foo") (OrLaterVersion (mkVersion [0])))------ >>> simpleParsec "foo >=2.0" :: Maybe PackageVersionConstraint--- Just (PackageVersionConstraint (PackageName "foo") (OrLaterVersion (mkVersion [2,0])))------ >>> simpleParsec "foo-2.0" :: Maybe PackageVersionConstraint--- Just (PackageVersionConstraint (PackageName "foo") (ThisVersion (mkVersion [2,0])))----instance Parsec PackageVersionConstraint where- parsec = do- PackageIdentifier name ver <- parsec- if ver == nullVersion- then do- P.spaces- vr <- parsec <|> return anyVersion- P.spaces- return (PackageVersionConstraint name vr)- else- pure (PackageVersionConstraint name (thisVersion ver))---- | @since 3.4.0.0-thisPackageVersionConstraint :: PackageIdentifier -> PackageVersionConstraint-thisPackageVersionConstraint (PackageIdentifier pn vr) =- PackageVersionConstraint pn (thisVersion vr)---- | @since 3.4.0.0-simplifyPackageVersionConstraint :: PackageVersionConstraint -> PackageVersionConstraint-simplifyPackageVersionConstraint (PackageVersionConstraint pn vr) =- PackageVersionConstraint pn (simplifyVersionRange vr)
+ src/Distribution/Types/ParStrat.hs view
@@ -0,0 +1,24 @@+module Distribution.Types.ParStrat where++-- | How to control parallelism, e.g. a fixed number of jobs or by using a system semaphore.+data ParStratX sem+ = -- | Compile in parallel with the given number of jobs (`-jN` or `-j`).+ NumJobs (Maybe Int)+ | -- | `--semaphore`: use a system semaphore to control parallelism.+ UseSem sem+ | -- | No parallelism (neither `-jN` nor `--semaphore`, but could be `-j1`).+ Serial+ deriving (Show)++-- | Used by Cabal to indicate that we want to use this specific semaphore (created by cabal-install)+type ParStrat = ParStratX String++-- | Used by cabal-install to say we want to create a semaphore with N slots.+type ParStratInstall = ParStratX Int++-- | Determine if the parallelism strategy enables parallel builds.+isParallelBuild :: ParStratX n -> Bool+isParallelBuild Serial = False+isParallelBuild (NumJobs (Just 1)) = False+isParallelBuild (NumJobs _) = True+isParallelBuild UseSem{} = True
− src/Distribution/Types/PkgconfigDependency.hs
@@ -1,39 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Types.PkgconfigDependency- ( PkgconfigDependency(..)- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Types.PkgconfigName-import Distribution.Types.PkgconfigVersionRange--import Distribution.Parsec-import Distribution.Pretty--import qualified Distribution.Compat.CharParsing as P---- | Describes a dependency on a pkg-config library------ @since 2.0.0.2-data PkgconfigDependency = PkgconfigDependency- PkgconfigName- PkgconfigVersionRange- deriving (Generic, Read, Show, Eq, Typeable, Data)--instance Binary PkgconfigDependency-instance Structured PkgconfigDependency-instance NFData PkgconfigDependency where rnf = genericRnf--instance Pretty PkgconfigDependency where- pretty (PkgconfigDependency name PcAnyVersion) = pretty name- pretty (PkgconfigDependency name ver) = pretty name <+> pretty ver--instance Parsec PkgconfigDependency where- parsec = do- name <- parsec- P.spaces- verRange <- parsec <|> pure anyPkgconfigVersion- pure $ PkgconfigDependency name verRange
− src/Distribution/Types/PkgconfigName.hs
@@ -1,68 +0,0 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Types.PkgconfigName- ( PkgconfigName, unPkgconfigName, mkPkgconfigName- ) where--import Prelude ()-import Distribution.Compat.Prelude-import Distribution.Utils.ShortText--import Distribution.Pretty-import Distribution.Parsec--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp---- | A pkg-config library name------ This is parsed as any valid argument to the pkg-config utility.------ @since 2.0.0.2-newtype PkgconfigName = PkgconfigName ShortText- deriving (Generic, Read, Show, Eq, Ord, Typeable, Data)---- | Convert 'PkgconfigName' to 'String'------ @since 2.0.0.2-unPkgconfigName :: PkgconfigName -> String-unPkgconfigName (PkgconfigName s) = fromShortText s---- | Construct a 'PkgconfigName' from a 'String'------ 'mkPkgconfigName' is the inverse to 'unPkgconfigName'------ Note: No validations are performed to ensure that the resulting--- 'PkgconfigName' is valid------ @since 2.0.0.2-mkPkgconfigName :: String -> PkgconfigName-mkPkgconfigName = PkgconfigName . toShortText---- | 'mkPkgconfigName'------ @since 2.0.0.2-instance IsString PkgconfigName where- fromString = mkPkgconfigName--instance Binary PkgconfigName-instance Structured PkgconfigName---- pkg-config allows versions and other letters in package names, eg--- "gtk+-2.0" is a valid pkg-config package _name_. It then has a package--- version number like 2.10.13-instance Pretty PkgconfigName where- pretty = Disp.text . unPkgconfigName--instance Parsec PkgconfigName where- parsec = mkPkgconfigName <$> P.munch1 isNameChar where- -- https://gitlab.haskell.org/ghc/ghc/issues/17752- isNameChar '-' = True- isNameChar '_' = True- isNameChar '.' = True- isNameChar '+' = True- isNameChar c = isAlphaNum c--instance NFData PkgconfigName where- rnf (PkgconfigName pkg) = rnf pkg
− src/Distribution/Types/PkgconfigVersion.hs
@@ -1,119 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--- @since 3.0-module Distribution.Types.PkgconfigVersion (- PkgconfigVersion (..),- rpmvercmp,- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Parsec-import Distribution.Pretty-import Distribution.Utils.Generic (isAsciiAlphaNum)--import qualified Data.ByteString as BS-import qualified Data.ByteString.Char8 as BS8-import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as PP---- | @pkg-config@ versions.------ In fact, this can be arbitrary 'BS.ByteString',--- but 'Parsec' instance is a little pickier.------ @since 3.0-newtype PkgconfigVersion = PkgconfigVersion BS.ByteString- deriving (Generic, Read, Show, Typeable, Data)--instance Eq PkgconfigVersion where- PkgconfigVersion a == PkgconfigVersion b = rpmvercmp a b == EQ--instance Ord PkgconfigVersion where- PkgconfigVersion a `compare` PkgconfigVersion b = rpmvercmp a b--instance Binary PkgconfigVersion-instance Structured PkgconfigVersion-instance NFData PkgconfigVersion where rnf = genericRnf--instance Pretty PkgconfigVersion where- pretty (PkgconfigVersion bs) = PP.text (BS8.unpack bs)---- |------ >>> simpleParsec "1.0.2n" :: Maybe PkgconfigVersion--- Just (PkgconfigVersion "1.0.2n")------ >>> simpleParsec "0.3.5+ds" :: Maybe PkgconfigVersion--- Nothing----instance Parsec PkgconfigVersion where- parsec = PkgconfigVersion . BS8.pack <$> P.munch1 predicate where- predicate c = isAsciiAlphaNum c || c == '.' || c == '-'------------------------------------------------------------------------------------ rmpvercmp - pure Haskell implementation------------------------------------------------------------------------------------ | Compare two version strings as @pkg-config@ would compare them.------ @since 3.0-rpmvercmp :: BS.ByteString -> BS.ByteString -> Ordering-rpmvercmp a b = go0 (BS.unpack a) (BS.unpack b)- where- go0 :: [Word8] -> [Word8] -> Ordering- -- if there is _any_ trailing "garbage", it seems to affect result- -- https://github.com/haskell/cabal/issues/6805- go0 [] [] = EQ- go0 [] _ = LT- go0 _ [] = GT- go0 xs ys = go1 (dropNonAlnum8 xs) (dropNonAlnum8 ys)-- go1 :: [Word8] -> [Word8] -> Ordering- go1 [] [] = EQ- go1 [] _ = LT- go1 _ [] = GT- go1 xs@(x:_) ys- | isDigit8 x =- let (xs1, xs2) = span isDigit8 xs- (ys1, ys2) = span isDigit8 ys- -- numeric segments are always newer than alpha segments- in if null ys1- then GT- else compareInt xs1 ys1 <> go0 xs2 ys2-- -- isAlpha- | otherwise =- let (xs1, xs2) = span isAlpha8 xs- (ys1, ys2) = span isAlpha8 ys- in if null ys1- then LT- else compareStr xs1 ys1 <> go0 xs2 ys2---- compare as numbers-compareInt :: [Word8] -> [Word8] -> Ordering-compareInt xs ys =- -- whichever number has more digits wins- compare (length xs') (length ys') <>- -- equal length: use per character compare, "strcmp"- compare xs' ys'- where- -- drop leading zeros- xs' = dropWhile (== 0x30) xs- ys' = dropWhile (== 0x30) ys---- strcmp-compareStr :: [Word8] -> [Word8] -> Ordering-compareStr = compare--dropNonAlnum8 :: [Word8] -> [Word8]-dropNonAlnum8 = dropWhile (\w -> not (isDigit8 w || isAlpha8 w))--isDigit8 :: Word8 -> Bool-isDigit8 w = 0x30 <= w && w <= 0x39--isAlpha8 :: Word8 -> Bool-isAlpha8 w = (0x41 <= w && w <= 0x5A) || (0x61 <= w && w <= 0x7A)--
− src/Distribution/Types/PkgconfigVersionRange.hs
@@ -1,161 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Types.PkgconfigVersionRange (- PkgconfigVersionRange (..),- anyPkgconfigVersion,- isAnyPkgconfigVersion,- withinPkgconfigVersionRange,- -- * Internal- versionToPkgconfigVersion,- versionRangeToPkgconfigVersionRange,- ) where--import Distribution.Compat.Prelude-import Prelude ()--import Distribution.CabalSpecVersion-import Distribution.Parsec-import Distribution.Pretty-import Distribution.Types.PkgconfigVersion-import Distribution.Types.Version-import Distribution.Types.VersionInterval-import Distribution.Types.VersionRange--import qualified Data.ByteString.Char8 as BS8-import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as PP---- | @since 3.0-data PkgconfigVersionRange- = PcAnyVersion- | PcThisVersion PkgconfigVersion -- = version- | PcLaterVersion PkgconfigVersion -- > version (NB. not >=)- | PcEarlierVersion PkgconfigVersion -- < version- | PcOrLaterVersion PkgconfigVersion -- >= version- | PcOrEarlierVersion PkgconfigVersion -- =< version- | PcUnionVersionRanges PkgconfigVersionRange PkgconfigVersionRange- | PcIntersectVersionRanges PkgconfigVersionRange PkgconfigVersionRange- deriving (Generic, Read, Show, Eq, Typeable, Data)--instance Binary PkgconfigVersionRange-instance Structured PkgconfigVersionRange-instance NFData PkgconfigVersionRange where rnf = genericRnf--instance Pretty PkgconfigVersionRange where- pretty = pp 0 where- pp :: Int -> PkgconfigVersionRange -> PP.Doc- pp _ PcAnyVersion = PP.text "-any"- pp _ (PcThisVersion v) = PP.text "==" <<>> pretty v- pp _ (PcLaterVersion v) = PP.text ">" <<>> pretty v- pp _ (PcEarlierVersion v) = PP.text "<" <<>> pretty v- pp _ (PcOrLaterVersion v) = PP.text ">=" <<>> pretty v- pp _ (PcOrEarlierVersion v) = PP.text "<=" <<>> pretty v-- pp d (PcUnionVersionRanges v u) = parens (d >= 1) $- pp 1 v PP.<+> PP.text "||" PP.<+> pp 0 u- pp d (PcIntersectVersionRanges v u) = parens (d >= 2) $- pp 2 v PP.<+> PP.text "&&" PP.<+> pp 1 u-- parens True = PP.parens- parens False = id--instance Parsec PkgconfigVersionRange where- -- note: the wildcard is used in some places, e.g- -- http://hackage.haskell.org/package/bindings-libzip-0.10.1/bindings-libzip.cabal- --- -- however, in the presence of alphanumerics etc. lax version parser,- -- wildcard is ill-specified-- parsec = do- csv <- askCabalSpecVersion- if csv >= CabalSpecV3_0- then pkgconfigParser- else versionRangeToPkgconfigVersionRange <$> versionRangeParser P.integral csv---- "modern" parser of @pkg-config@ package versions.-pkgconfigParser :: CabalParsing m => m PkgconfigVersionRange-pkgconfigParser = P.spaces >> expr where- -- every parser here eats trailing space- expr = do- ts <- term `P.sepByNonEmpty` (P.string "||" >> P.spaces)- return $ foldr1 PcUnionVersionRanges ts-- term = do- fs <- factor `P.sepByNonEmpty` (P.string "&&" >> P.spaces)- return $ foldr1 PcIntersectVersionRanges fs-- factor = parens expr <|> prim-- prim = do- op <- P.munch1 isOpChar P.<?> "operator"- case op of- "-" -> anyPkgconfigVersion <$ (P.string "any" *> P.spaces)-- "==" -> afterOp PcThisVersion- ">" -> afterOp PcLaterVersion- "<" -> afterOp PcEarlierVersion- ">=" -> afterOp PcOrLaterVersion- "<=" -> afterOp PcOrEarlierVersion-- _ -> P.unexpected $ "Unknown version operator " ++ show op-- -- https://gitlab.haskell.org/ghc/ghc/issues/17752- isOpChar '<' = True- isOpChar '=' = True- isOpChar '>' = True- isOpChar '^' = True- isOpChar '-' = True- isOpChar _ = False-- afterOp f = do- P.spaces- v <- parsec- P.spaces- return (f v)-- parens = P.between- ((P.char '(' P.<?> "opening paren") >> P.spaces)- (P.char ')' >> P.spaces)--anyPkgconfigVersion :: PkgconfigVersionRange-anyPkgconfigVersion = PcAnyVersion---- | TODO: this is not precise, but used only to prettify output.-isAnyPkgconfigVersion :: PkgconfigVersionRange -> Bool-isAnyPkgconfigVersion = (== PcAnyVersion)--withinPkgconfigVersionRange :: PkgconfigVersion -> PkgconfigVersionRange -> Bool-withinPkgconfigVersionRange v = go where- go PcAnyVersion = True- go (PcThisVersion u) = v == u- go (PcLaterVersion u) = v > u- go (PcEarlierVersion u) = v < u- go (PcOrLaterVersion u) = v >= u- go (PcOrEarlierVersion u) = v <= u- go (PcUnionVersionRanges a b) = go a || go b- go (PcIntersectVersionRanges a b) = go a && go b------------------------------------------------------------------------------------ Conversion----------------------------------------------------------------------------------versionToPkgconfigVersion :: Version -> PkgconfigVersion-versionToPkgconfigVersion = PkgconfigVersion . BS8.pack . prettyShow--versionRangeToPkgconfigVersionRange :: VersionRange -> PkgconfigVersionRange-versionRangeToPkgconfigVersionRange vr- | isAnyVersion vr- = PcAnyVersion- | otherwise- = case asVersionIntervals vr of- [] -> PcEarlierVersion (PkgconfigVersion (BS8.pack "0"))- (i:is) -> foldl (\r j -> PcUnionVersionRanges r (conv j)) (conv i) is- where- conv (VersionInterval (LowerBound v b) NoUpperBound) = convL v b- conv (VersionInterval (LowerBound v b) (UpperBound u c)) = PcIntersectVersionRanges (convL v b) (convU u c)-- convL v ExclusiveBound = PcLaterVersion (versionToPkgconfigVersion v)- convL v InclusiveBound = PcOrLaterVersion (versionToPkgconfigVersion v)-- convU v ExclusiveBound = PcEarlierVersion (versionToPkgconfigVersion v)- convU v InclusiveBound = PcOrEarlierVersion (versionToPkgconfigVersion v)
− src/Distribution/Types/SetupBuildInfo.hs
@@ -1,41 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Distribution.Types.SetupBuildInfo (- SetupBuildInfo(..)-) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Types.Dependency---- ------------------------------------------------------------------------------ The SetupBuildInfo type---- One can see this as a very cut-down version of BuildInfo below.--- To keep things simple for tools that compile Setup.hs we limit the--- options authors can specify to just Haskell package dependencies.--data SetupBuildInfo = SetupBuildInfo- { setupDepends :: [Dependency]- , defaultSetupDepends :: Bool- -- ^ Is this a default 'custom-setup' section added by the cabal-install- -- code (as opposed to user-provided)? This field is only used- -- internally, and doesn't correspond to anything in the .cabal- -- file. See #3199.- }- deriving (Generic, Show, Eq, Read, Typeable, Data)--instance Binary SetupBuildInfo-instance Structured SetupBuildInfo-instance NFData SetupBuildInfo where rnf = genericRnf--instance Monoid SetupBuildInfo where- mempty = SetupBuildInfo [] False- mappend = (<>)--instance Semigroup SetupBuildInfo where- a <> b = SetupBuildInfo- (setupDepends a <> setupDepends b)- (defaultSetupDepends a || defaultSetupDepends b)
− src/Distribution/Types/SetupBuildInfo/Lens.hs
@@ -1,21 +0,0 @@-module Distribution.Types.SetupBuildInfo.Lens (- SetupBuildInfo,- module Distribution.Types.SetupBuildInfo.Lens,- ) where--import Distribution.Compat.Lens-import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Types.Dependency (Dependency)-import Distribution.Types.SetupBuildInfo (SetupBuildInfo)--import qualified Distribution.Types.SetupBuildInfo as T--setupDepends :: Lens' SetupBuildInfo [Dependency]-setupDepends f s = fmap (\x -> s { T.setupDepends = x }) (f (T.setupDepends s))-{-# INLINE setupDepends #-}--defaultSetupDepends :: Lens' SetupBuildInfo Bool-defaultSetupDepends f s = fmap (\x -> s { T.defaultSetupDepends = x }) (f (T.defaultSetupDepends s))-{-# INLINE defaultSetupDepends #-}
− src/Distribution/Types/SourceRepo.hs
@@ -1,201 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Distribution.Types.SourceRepo (- SourceRepo(..),- RepoKind(..),- RepoType(..),- KnownRepoType (..),- knownRepoTypes,- emptySourceRepo,- classifyRepoType,- classifyRepoKind,- ) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Utils.Generic (lowercase)--import Distribution.Pretty-import Distribution.Parsec--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp-import qualified Data.Map.Strict as M---- --------------------------------------------------------------- * Source repos--- ---------------------------------------------------------------- | Information about the source revision control system for a package.------ When specifying a repo it is useful to know the meaning or intention of the--- information as doing so enables automation. There are two obvious common--- purposes: one is to find the repo for the latest development version, the--- other is to find the repo for this specific release. The 'ReopKind'--- specifies which one we mean (or another custom one).------ A package can specify one or the other kind or both. Most will specify just--- a head repo but some may want to specify a repo to reconstruct the sources--- for this package release.------ The required information is the 'RepoType' which tells us if it's using--- 'Darcs', 'Git' for example. The 'repoLocation' and other details are--- interpreted according to the repo type.----data SourceRepo = SourceRepo {- -- | The kind of repo. This field is required.- repoKind :: RepoKind,-- -- | The type of the source repository system for this repo, eg 'Darcs' or- -- 'Git'. This field is required.- repoType :: Maybe RepoType,-- -- | The location of the repository. For most 'RepoType's this is a URL.- -- This field is required.- repoLocation :: Maybe String,-- -- | 'CVS' can put multiple \"modules\" on one server and requires a- -- module name in addition to the location to identify a particular repo.- -- Logically this is part of the location but unfortunately has to be- -- specified separately. This field is required for the 'CVS' 'RepoType' and- -- should not be given otherwise.- repoModule :: Maybe String,-- -- | The name or identifier of the branch, if any. Many source control- -- systems have the notion of multiple branches in a repo that exist in the- -- same location. For example 'Git' and 'CVS' use this while systems like- -- 'Darcs' use different locations for different branches. This field is- -- optional but should be used if necessary to identify the sources,- -- especially for the 'RepoThis' repo kind.- repoBranch :: Maybe String,-- -- | The tag identify a particular state of the repository. This should be- -- given for the 'RepoThis' repo kind and not for 'RepoHead' kind.- --- repoTag :: Maybe String,-- -- | Some repositories contain multiple projects in different subdirectories- -- This field specifies the subdirectory where this packages sources can be- -- found, eg the subdirectory containing the @.cabal@ file. It is interpreted- -- relative to the root of the repository. This field is optional. If not- -- given the default is \".\" ie no subdirectory.- repoSubdir :: Maybe FilePath-}- deriving (Eq, Ord, Generic, Read, Show, Typeable, Data)--emptySourceRepo :: RepoKind -> SourceRepo-emptySourceRepo kind = SourceRepo- { repoKind = kind- , repoType = Nothing- , repoLocation = Nothing- , repoModule = Nothing- , repoBranch = Nothing- , repoTag = Nothing- , repoSubdir = Nothing- }--instance Binary SourceRepo-instance Structured SourceRepo-instance NFData SourceRepo where rnf = genericRnf---- | What this repo info is for, what it represents.----data RepoKind =- -- | The repository for the \"head\" or development version of the project.- -- This repo is where we should track the latest development activity or- -- the usual repo people should get to contribute patches.- RepoHead-- -- | The repository containing the sources for this exact package version- -- or release. For this kind of repo a tag should be given to give enough- -- information to re-create the exact sources.- | RepoThis-- | RepoKindUnknown String- deriving (Eq, Generic, Ord, Read, Show, Typeable, Data)--instance Binary RepoKind-instance Structured RepoKind-instance NFData RepoKind where rnf = genericRnf---- | An enumeration of common source control systems. The fields used in the--- 'SourceRepo' depend on the type of repo. The tools and methods used to--- obtain and track the repo depend on the repo type.----data KnownRepoType = Darcs | Git | SVN | CVS- | Mercurial | GnuArch | Bazaar | Monotone- | Pijul -- ^ @since 3.4.0.0- deriving (Eq, Generic, Ord, Read, Show, Typeable, Data, Enum, Bounded)--instance Binary KnownRepoType-instance Structured KnownRepoType-instance NFData KnownRepoType where rnf = genericRnf--instance Parsec KnownRepoType where- parsec = do- str <- P.munch1 isIdent- maybe- (P.unexpected $ "Could not parse KnownRepoType from " ++ str)- return- (M.lookup str knownRepoTypeMap)--instance Pretty KnownRepoType where- pretty = Disp.text . lowercase . show--data RepoType = KnownRepoType KnownRepoType- | OtherRepoType String- deriving (Eq, Generic, Ord, Read, Show, Typeable, Data)--instance Binary RepoType-instance Structured RepoType-instance NFData RepoType where rnf = genericRnf--knownRepoTypes :: [KnownRepoType]-knownRepoTypes = [minBound .. maxBound]--repoTypeAliases :: KnownRepoType -> [String]-repoTypeAliases Bazaar = ["bzr"]-repoTypeAliases Mercurial = ["hg"]-repoTypeAliases GnuArch = ["arch"]-repoTypeAliases _ = []--instance Pretty RepoKind where- pretty RepoHead = Disp.text "head"- pretty RepoThis = Disp.text "this"- pretty (RepoKindUnknown other) = Disp.text other--instance Parsec RepoKind where- parsec = classifyRepoKind <$> P.munch1 isIdent--classifyRepoKind :: String -> RepoKind-classifyRepoKind name = case lowercase name of- "head" -> RepoHead- "this" -> RepoThis- _ -> RepoKindUnknown name--instance Parsec RepoType where- parsec = classifyRepoType <$> P.munch1 isIdent--instance Pretty RepoType where- pretty (OtherRepoType other) = Disp.text other- pretty (KnownRepoType t) = pretty t--classifyRepoType :: String -> RepoType-classifyRepoType s =- maybe- (OtherRepoType s)- KnownRepoType- (M.lookup (lowercase s) knownRepoTypeMap)--knownRepoTypeMap :: Map String KnownRepoType-knownRepoTypeMap =- M.fromList- [ (name, repoType')- | repoType' <- knownRepoTypes- , name <- prettyShow repoType' : repoTypeAliases repoType'- ]--isIdent :: Char -> Bool-isIdent c = isAlphaNum c || c == '_' || c == '-'
− src/Distribution/Types/SourceRepo/Lens.hs
@@ -1,39 +0,0 @@-module Distribution.Types.SourceRepo.Lens (- T.SourceRepo,- module Distribution.Types.SourceRepo.Lens,- ) where--import Prelude()-import Distribution.Compat.Prelude-import Distribution.Compat.Lens--import Distribution.Types.SourceRepo (SourceRepo, RepoKind, RepoType)-import qualified Distribution.Types.SourceRepo as T--repoKind :: Lens' SourceRepo RepoKind-repoKind f s = fmap (\x -> s { T.repoKind = x }) (f (T.repoKind s))-{-# INLINE repoKind #-}--repoType :: Lens' SourceRepo (Maybe RepoType)-repoType f s = fmap (\x -> s { T.repoType = x }) (f (T.repoType s))-{-# INLINE repoType #-}--repoLocation :: Lens' SourceRepo (Maybe String)-repoLocation f s = fmap (\x -> s { T.repoLocation = x }) (f (T.repoLocation s))-{-# INLINE repoLocation #-}--repoModule :: Lens' SourceRepo (Maybe String)-repoModule f s = fmap (\x -> s { T.repoModule = x }) (f (T.repoModule s))-{-# INLINE repoModule #-}--repoBranch :: Lens' SourceRepo (Maybe String)-repoBranch f s = fmap (\x -> s { T.repoBranch = x }) (f (T.repoBranch s))-{-# INLINE repoBranch #-}--repoTag :: Lens' SourceRepo (Maybe String)-repoTag f s = fmap (\x -> s { T.repoTag = x }) (f (T.repoTag s))-{-# INLINE repoTag #-}--repoSubdir :: Lens' SourceRepo (Maybe FilePath)-repoSubdir f s = fmap (\x -> s { T.repoSubdir = x }) (f (T.repoSubdir s))-{-# INLINE repoSubdir #-}
src/Distribution/Types/TargetInfo.hs view
@@ -1,33 +1,39 @@+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TypeFamilies #-}-module Distribution.Types.TargetInfo (- TargetInfo(..)-) where -import Prelude ()+module Distribution.Types.TargetInfo+ ( TargetInfo (..)+ ) where+ import Distribution.Compat.Prelude+import Prelude () -import Distribution.Types.ComponentLocalBuildInfo import Distribution.Types.Component+import Distribution.Types.ComponentLocalBuildInfo import Distribution.Types.UnitId -import Distribution.Compat.Graph (IsNode(..))+import Distribution.Compat.Graph (IsNode (..)) -- | The 'TargetInfo' contains all the information necessary to build a -- specific target (e.g., component/module/file) in a package. In -- principle, one can get the 'Component' from a -- 'ComponentLocalBuildInfo' and 'LocalBuildInfo', but it is much more -- convenient to have the component in hand.-data TargetInfo = TargetInfo {- targetCLBI :: ComponentLocalBuildInfo,- targetComponent :: Component- -- TODO: BuildTargets supporting parsing these is dumb,- -- we don't have support for compiling single modules or- -- file paths. Accommodating it now is premature- -- generalization. Figure it out later.- -- targetSub :: Maybe (Either ModuleName FilePath)- }+data TargetInfo = TargetInfo+ { targetCLBI :: ComponentLocalBuildInfo+ , targetComponent :: Component+ -- TODO: BuildTargets supporting parsing these is dumb,+ -- we don't have support for compiling single modules or+ -- file paths. Accommodating it now is premature+ -- generalization. Figure it out later.+ -- targetSub :: Maybe (Either ModuleName FilePath)+ }+ deriving (Generic, Show) +instance Binary TargetInfo+instance Structured TargetInfo+ instance IsNode TargetInfo where- type Key TargetInfo = UnitId- nodeKey = nodeKey . targetCLBI- nodeNeighbors = nodeNeighbors . targetCLBI+ type Key TargetInfo = UnitId+ nodeKey = nodeKey . targetCLBI+ nodeNeighbors = nodeNeighbors . targetCLBI
− src/Distribution/Types/TestSuite.hs
@@ -1,83 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Distribution.Types.TestSuite (- TestSuite(..),- emptyTestSuite,- testType,- testModules,- testModulesAutogen-) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Types.BuildInfo-import Distribution.Types.TestType-import Distribution.Types.TestSuiteInterface-import Distribution.Types.UnqualComponentName--import Distribution.ModuleName--import qualified Distribution.Types.BuildInfo.Lens as L---- | A \"test-suite\" stanza in a cabal file.----data TestSuite = TestSuite {- testName :: UnqualComponentName,- testInterface :: TestSuiteInterface,- testBuildInfo :: BuildInfo- }- deriving (Generic, Show, Read, Eq, Typeable, Data)--instance L.HasBuildInfo TestSuite where- buildInfo f l = (\x -> l { testBuildInfo = x }) <$> f (testBuildInfo l)--instance Binary TestSuite-instance Structured TestSuite--instance NFData TestSuite where rnf = genericRnf--instance Monoid TestSuite where- mempty = TestSuite {- testName = mempty,- testInterface = mempty,- testBuildInfo = mempty- }- mappend = (<>)--instance Semigroup TestSuite where- a <> b = TestSuite {- testName = combine' testName,- testInterface = combine testInterface,- testBuildInfo = combine testBuildInfo- }- where combine field = field a `mappend` field b- combine' field = case ( unUnqualComponentName $ field a- , unUnqualComponentName $ field b) of- ("", _) -> field b- (_, "") -> field a- (x, y) -> error $ "Ambiguous values for test field: '"- ++ x ++ "' and '" ++ y ++ "'"--emptyTestSuite :: TestSuite-emptyTestSuite = mempty---testType :: TestSuite -> TestType-testType test = case testInterface test of- TestSuiteExeV10 ver _ -> TestTypeExe ver- TestSuiteLibV09 ver _ -> TestTypeLib ver- TestSuiteUnsupported testtype -> testtype---- | Get all the module names from a test suite.-testModules :: TestSuite -> [ModuleName]-testModules test = (case testInterface test of- TestSuiteLibV09 _ m -> [m]- _ -> [])- ++ otherModules (testBuildInfo test)---- | Get all the auto generated module names from a test suite.--- This are a subset of 'testModules'.-testModulesAutogen :: TestSuite -> [ModuleName]-testModulesAutogen test = autogenModules (testBuildInfo test)
− src/Distribution/Types/TestSuite/Lens.hs
@@ -1,27 +0,0 @@-module Distribution.Types.TestSuite.Lens (- TestSuite,- module Distribution.Types.TestSuite.Lens,- ) where--import Distribution.Compat.Lens-import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Types.BuildInfo (BuildInfo)-import Distribution.Types.TestSuite (TestSuite)-import Distribution.Types.TestSuiteInterface (TestSuiteInterface)-import Distribution.Types.UnqualComponentName (UnqualComponentName)--import qualified Distribution.Types.TestSuite as T--testName :: Lens' TestSuite UnqualComponentName-testName f s = fmap (\x -> s { T.testName = x }) (f (T.testName s))-{-# INLINE testName #-}--testInterface :: Lens' TestSuite TestSuiteInterface-testInterface f s = fmap (\x -> s { T.testInterface = x }) (f (T.testInterface s))-{-# INLINE testInterface #-}--testBuildInfo :: Lens' TestSuite BuildInfo-testBuildInfo f s = fmap (\x -> s { T.testBuildInfo = x }) (f (T.testBuildInfo s))-{-# INLINE testBuildInfo #-}
− src/Distribution/Types/TestSuiteInterface.hs
@@ -1,52 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Distribution.Types.TestSuiteInterface (- TestSuiteInterface(..),-) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Types.TestType-import Distribution.ModuleName-import Distribution.Version---- | The test suite interfaces that are currently defined. Each test suite must--- specify which interface it supports.------ More interfaces may be defined in future, either new revisions or totally--- new interfaces.----data TestSuiteInterface =-- -- | Test interface \"exitcode-stdio-1.0\". The test-suite takes the form- -- of an executable. It returns a zero exit code for success, non-zero for- -- failure. The stdout and stderr channels may be logged. It takes no- -- command line parameters and nothing on stdin.- --- TestSuiteExeV10 Version FilePath-- -- | Test interface \"detailed-0.9\". The test-suite takes the form of a- -- library containing a designated module that exports \"tests :: [Test]\".- --- | TestSuiteLibV09 Version ModuleName-- -- | A test suite that does not conform to one of the above interfaces for- -- the given reason (e.g. unknown test type).- --- | TestSuiteUnsupported TestType- deriving (Eq, Generic, Read, Show, Typeable, Data)--instance Binary TestSuiteInterface-instance Structured TestSuiteInterface--instance NFData TestSuiteInterface where rnf = genericRnf--instance Monoid TestSuiteInterface where- mempty = TestSuiteUnsupported (TestTypeUnknown mempty nullVersion)- mappend = (<>)--instance Semigroup TestSuiteInterface where- a <> (TestSuiteUnsupported _) = a- _ <> b = b
− src/Distribution/Types/TestType.hs
@@ -1,43 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}--module Distribution.Types.TestType (- TestType(..),- knownTestTypes,-) where--import Distribution.Compat.Prelude-import Distribution.Version-import Prelude ()--import Distribution.Parsec-import Distribution.Pretty-import Text.PrettyPrint (char, text)---- | The \"test-type\" field in the test suite stanza.----data TestType = TestTypeExe Version -- ^ \"type: exitcode-stdio-x.y\"- | TestTypeLib Version -- ^ \"type: detailed-x.y\"- | TestTypeUnknown String Version -- ^ Some unknown test type e.g. \"type: foo\"- deriving (Generic, Show, Read, Eq, Typeable, Data)--instance Binary TestType-instance Structured TestType--instance NFData TestType where rnf = genericRnf--knownTestTypes :: [TestType]-knownTestTypes = [ TestTypeExe (mkVersion [1,0])- , TestTypeLib (mkVersion [0,9]) ]--instance Pretty TestType where- pretty (TestTypeExe ver) = text "exitcode-stdio-" <<>> pretty ver- pretty (TestTypeLib ver) = text "detailed-" <<>> pretty ver- pretty (TestTypeUnknown name ver) = text name <<>> char '-' <<>> pretty ver--instance Parsec TestType where- parsec = parsecStandard $ \ver name -> case name of- "exitcode-stdio" -> TestTypeExe ver- "detailed" -> TestTypeLib ver- _ -> TestTypeUnknown name ver
− src/Distribution/Types/UnitId.hs
@@ -1,134 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE FlexibleContexts #-}--module Distribution.Types.UnitId- ( UnitId, unUnitId, mkUnitId- , DefUnitId- , unsafeMkDefUnitId- , unDefUnitId- , newSimpleUnitId- , mkLegacyUnitId- , getHSLibraryName- ) where--import Prelude ()-import Distribution.Compat.Prelude-import Distribution.Utils.ShortText--import qualified Distribution.Compat.CharParsing as P-import Distribution.Pretty-import Distribution.Parsec-import Distribution.Types.ComponentId-import Distribution.Types.PackageId--import Text.PrettyPrint (text)---- | A unit identifier identifies a (possibly instantiated)--- package/component that can be installed the installed package--- database. There are several types of components that can be--- installed:------ * A traditional library with no holes, so that 'unitIdHash'--- is @Nothing@. In the absence of Backpack, 'UnitId'--- is the same as a 'ComponentId'.------ * An indefinite, Backpack library with holes. In this case,--- 'unitIdHash' is still @Nothing@, but in the install,--- there are only interfaces, no compiled objects.------ * An instantiated Backpack library with all the holes--- filled in. 'unitIdHash' is a @Just@ a hash of the--- instantiating mapping.------ A unit is a component plus the additional information on how the--- holes are filled in. Thus there is a one to many relationship: for a--- particular component there are many different ways of filling in the--- holes, and each different combination is a unit (and has a separate--- 'UnitId').------ 'UnitId' is distinct from 'OpenUnitId', in that it is always--- installed, whereas 'OpenUnitId' are intermediate unit identities--- that arise during mixin linking, and don't necessarily correspond--- to any actually installed unit. Since the mapping is not actually--- recorded in a 'UnitId', you can't actually substitute over them--- (but you can substitute over 'OpenUnitId'). See also--- "Distribution.Backpack.FullUnitId" for a mechanism for expanding an--- instantiated 'UnitId' to retrieve its mapping.------ Backwards compatibility note: if you need to get the string--- representation of a UnitId to pass, e.g., as a @-package-id@--- flag, use the 'display' function, which will work on all--- versions of Cabal.----newtype UnitId = UnitId ShortText- deriving (Generic, Read, Show, Eq, Ord, Typeable, Data, NFData)--instance Binary UnitId-instance Structured UnitId---- | The textual format for 'UnitId' coincides with the format--- GHC accepts for @-package-id@.----instance Pretty UnitId where- pretty = text . unUnitId---- | The textual format for 'UnitId' coincides with the format--- GHC accepts for @-package-id@.----instance Parsec UnitId where- parsec = mkUnitId <$> P.munch1 isUnitChar where- -- https://gitlab.haskell.org/ghc/ghc/issues/17752- isUnitChar '-' = True- isUnitChar '_' = True- isUnitChar '.' = True- isUnitChar '+' = True- isUnitChar c = isAlphaNum c---- | If you need backwards compatibility, consider using 'display'--- instead, which is supported by all versions of Cabal.----unUnitId :: UnitId -> String-unUnitId (UnitId s) = fromShortText s--mkUnitId :: String -> UnitId-mkUnitId = UnitId . toShortText---- | 'mkUnitId'------ @since 2.0.0.2-instance IsString UnitId where- fromString = mkUnitId---- | Create a unit identity with no associated hash directly--- from a 'ComponentId'.-newSimpleUnitId :: ComponentId -> UnitId-newSimpleUnitId = mkUnitId . unComponentId---- | Make an old-style UnitId from a package identifier.--- Assumed to be for the public library-mkLegacyUnitId :: PackageId -> UnitId-mkLegacyUnitId = newSimpleUnitId . mkComponentId . prettyShow---- | Returns library name prefixed with HS, suitable for filenames-getHSLibraryName :: UnitId -> String-getHSLibraryName uid = "HS" ++ prettyShow uid---- | A 'UnitId' for a definite package. The 'DefUnitId' invariant says--- that a 'UnitId' identified this way is definite; i.e., it has no--- unfilled holes.-newtype DefUnitId = DefUnitId { unDefUnitId :: UnitId }- deriving (Generic, Read, Show, Eq, Ord, Typeable, Data, Binary, NFData, Pretty)--instance Structured DefUnitId---- Workaround for a GHC 8.0.1 bug, see--- https://github.com/haskell/cabal/issues/4793#issuecomment-334258288-instance Parsec DefUnitId where- parsec = DefUnitId <$> parsec---- | Unsafely create a 'DefUnitId' from a 'UnitId'. Your responsibility--- is to ensure that the 'DefUnitId' invariant holds.-unsafeMkDefUnitId :: UnitId -> DefUnitId-unsafeMkDefUnitId = DefUnitId
− src/Distribution/Types/UnqualComponentName.hs
@@ -1,93 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-module Distribution.Types.UnqualComponentName- ( UnqualComponentName, unUnqualComponentName, unUnqualComponentNameST, mkUnqualComponentName- , packageNameToUnqualComponentName, unqualComponentNameToPackageName- ) where--import Distribution.Compat.Prelude-import Distribution.Utils.ShortText-import Prelude ()--import Distribution.Parsec-import Distribution.Pretty-import Distribution.Types.PackageName---- | An unqualified component name, for any kind of component.------ This is distinguished from a 'ComponentName' and 'ComponentId'. The former--- also states which of a library, executable, etc the name refers too. The--- later uniquely identifiers a component and its closure.------ @since 2.0.0.2-newtype UnqualComponentName = UnqualComponentName ShortText- deriving (Generic, Read, Show, Eq, Ord, Typeable, Data,- Semigroup, Monoid) -- TODO: bad enabler of bad monoids---- | Convert 'UnqualComponentName' to 'String'------ @since 2.0.0.2-unUnqualComponentName :: UnqualComponentName -> String-unUnqualComponentName (UnqualComponentName s) = fromShortText s---- | @since 3.4.0.0-unUnqualComponentNameST :: UnqualComponentName -> ShortText-unUnqualComponentNameST (UnqualComponentName s) = s---- | Construct a 'UnqualComponentName' from a 'String'------ 'mkUnqualComponentName' is the inverse to 'unUnqualComponentName'------ Note: No validations are performed to ensure that the resulting--- 'UnqualComponentName' is valid------ @since 2.0.0.2-mkUnqualComponentName :: String -> UnqualComponentName-mkUnqualComponentName = UnqualComponentName . toShortText---- | 'mkUnqualComponentName'------ @since 2.0.0.2-instance IsString UnqualComponentName where- fromString = mkUnqualComponentName--instance Binary UnqualComponentName-instance Structured UnqualComponentName--instance Pretty UnqualComponentName where- pretty = showToken . unUnqualComponentName--instance Parsec UnqualComponentName where- parsec = mkUnqualComponentName <$> parsecUnqualComponentName--instance NFData UnqualComponentName where- rnf (UnqualComponentName pkg) = rnf pkg---- TODO avoid String round trip with these PackageName <->--- UnqualComponentName converters.---- | Converts a package name to an unqualified component name------ Useful in legacy situations where a package name may refer to an internal--- component, if one is defined with that name.------ 2018-12-21: These "legacy" situations are not legacy.--- We can @build-depends@ on the internal library. However--- Now dependency contains @Set LibraryName@, and we should use that.------ @since 2.0.0.2-packageNameToUnqualComponentName :: PackageName -> UnqualComponentName-packageNameToUnqualComponentName = UnqualComponentName . unPackageNameST---- | Converts an unqualified component name to a package name------ `packageNameToUnqualComponentName` is the inverse of--- `unqualComponentNameToPackageName`.------ Useful in legacy situations where a package name may refer to an internal--- component, if one is defined with that name.------ @since 2.0.0.2-unqualComponentNameToPackageName :: UnqualComponentName -> PackageName-unqualComponentNameToPackageName = mkPackageNameST . unUnqualComponentNameST
− src/Distribution/Types/Version.hs
@@ -1,242 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-module Distribution.Types.Version (- -- * Package versions- Version,- mkVersion,- mkVersion',- versionNumbers,- nullVersion,- alterVersion,- version0,-- -- * Internal- validVersion,- versionDigitParser,- ) where--import Data.Bits (shiftL, shiftR, (.&.), (.|.))-import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Parsec-import Distribution.Pretty--import qualified Data.Version as Base-import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp-import qualified Text.Read as Read---- | A 'Version' represents the version of a software entity.------ Instances of 'Eq' and 'Ord' are provided, which gives exact--- equality and lexicographic ordering of the version number--- components (i.e. 2.1 > 2.0, 1.2.3 > 1.2.2, etc.).------ This type is opaque and distinct from the 'Base.Version' type in--- "Data.Version" since @Cabal-2.0@. The difference extends to the--- 'Binary' instance using a different (and more compact) encoding.------ @since 2.0.0.2-data Version = PV0 {-# UNPACK #-} !Word64- | PV1 !Int [Int]- -- NOTE: If a version fits into the packed Word64- -- representation (i.e. at most four version components- -- which all fall into the [0..0xfffe] range), then PV0- -- MUST be used. This is essential for the 'Eq' instance- -- to work.- deriving (Data,Eq,Generic,Typeable)--instance Ord Version where- compare (PV0 x) (PV0 y) = compare x y- compare (PV1 x xs) (PV1 y ys) = case compare x y of- EQ -> compare xs ys- c -> c- compare (PV0 w) (PV1 y ys) = case compare x y of- EQ -> compare [x2,x3,x4] ys- c -> c- where- x = fromIntegral ((w `shiftR` 48) .&. 0xffff) - 1- x2 = fromIntegral ((w `shiftR` 32) .&. 0xffff) - 1- x3 = fromIntegral ((w `shiftR` 16) .&. 0xffff) - 1- x4 = fromIntegral (w .&. 0xffff) - 1- compare (PV1 x xs) (PV0 w) = case compare x y of- EQ -> compare xs [y2,y3,y4]- c -> c- where- y = fromIntegral ((w `shiftR` 48) .&. 0xffff) - 1- y2 = fromIntegral ((w `shiftR` 32) .&. 0xffff) - 1- y3 = fromIntegral ((w `shiftR` 16) .&. 0xffff) - 1- y4 = fromIntegral (w .&. 0xffff) - 1--instance Show Version where- showsPrec d v = showParen (d > 10)- $ showString "mkVersion "- . showsPrec 11 (versionNumbers v)--instance Read Version where- readPrec = Read.parens $ do- Read.Ident "mkVersion" <- Read.lexP- v <- Read.step Read.readPrec- return (mkVersion v)--instance Binary Version-instance Structured Version--instance NFData Version where- rnf (PV0 _) = ()- rnf (PV1 _ ns) = rnf ns--instance Pretty Version where- pretty ver- = Disp.hcat (Disp.punctuate (Disp.char '.')- (map Disp.int $ versionNumbers ver))--instance Parsec Version where- parsec = mkVersion <$> toList <$> P.sepByNonEmpty versionDigitParser (P.char '.') <* tags- where- tags = do- ts <- many $ P.char '-' *> some (P.satisfy isAlphaNum)- case ts of- [] -> pure ()- (_ : _) -> parsecWarning PWTVersionTag "version with tags"---- | An integral without leading zeroes.------ @since 3.0-versionDigitParser :: CabalParsing m => m Int-versionDigitParser = (some d >>= toNumber) P.<?> "version digit (integral without leading zeroes)"- where- toNumber :: CabalParsing m => [Int] -> m Int- toNumber [0] = return 0- toNumber (0:_) = P.unexpected "Version digit with leading zero"- toNumber xs- -- 10^9 = 1000000000- -- 2^30 = 1073741824- --- -- GHC Int is at least 32 bits, so 2^31-1 is the 'maxBound'.- | length xs > 9 = P.unexpected "At most 9 numbers are allowed per version number part"- | otherwise = return $ foldl' (\a b -> a * 10 + b) 0 xs-- d :: P.CharParsing m => m Int- d = f <$> P.satisfyRange '0' '9'- f c = ord c - ord '0'---- | Construct 'Version' from list of version number components.------ For instance, @mkVersion [3,2,1]@ constructs a 'Version'--- representing the version @3.2.1@.------ All version components must be non-negative. @mkVersion []@--- currently represents the special /null/ version; see also 'nullVersion'.------ @since 2.0.0.2-mkVersion :: [Int] -> Version--- TODO: add validity check; disallow 'mkVersion []' (we have--- 'nullVersion' for that)-mkVersion [] = nullVersion-mkVersion (v1:[])- | inWord16VerRep1 v1 = PV0 (mkWord64VerRep1 v1)- | otherwise = PV1 v1 []- where- inWord16VerRep1 x1 = inWord16 (x1 .|. (x1+1))- mkWord64VerRep1 y1 = mkWord64VerRep (y1+1) 0 0 0--mkVersion (v1:vs@(v2:[]))- | inWord16VerRep2 v1 v2 = PV0 (mkWord64VerRep2 v1 v2)- | otherwise = PV1 v1 vs- where- inWord16VerRep2 x1 x2 = inWord16 (x1 .|. (x1+1)- .|. x2 .|. (x2+1))- mkWord64VerRep2 y1 y2 = mkWord64VerRep (y1+1) (y2+1) 0 0--mkVersion (v1:vs@(v2:v3:[]))- | inWord16VerRep3 v1 v2 v3 = PV0 (mkWord64VerRep3 v1 v2 v3)- | otherwise = PV1 v1 vs- where- inWord16VerRep3 x1 x2 x3 = inWord16 (x1 .|. (x1+1)- .|. x2 .|. (x2+1)- .|. x3 .|. (x3+1))- mkWord64VerRep3 y1 y2 y3 = mkWord64VerRep (y1+1) (y2+1) (y3+1) 0--mkVersion (v1:vs@(v2:v3:v4:[]))- | inWord16VerRep4 v1 v2 v3 v4 = PV0 (mkWord64VerRep4 v1 v2 v3 v4)- | otherwise = PV1 v1 vs- where- inWord16VerRep4 x1 x2 x3 x4 = inWord16 (x1 .|. (x1+1)- .|. x2 .|. (x2+1)- .|. x3 .|. (x3+1)- .|. x4 .|. (x4+1))- mkWord64VerRep4 y1 y2 y3 y4 = mkWord64VerRep (y1+1) (y2+1) (y3+1) (y4+1)--mkVersion (v1:vs) = PV1 v1 vs---- | Version 0. A lower bound of 'Version'.------ @since 2.2-version0 :: Version-version0 = mkVersion [0]--{-# INLINE mkWord64VerRep #-}-mkWord64VerRep :: Int -> Int -> Int -> Int -> Word64-mkWord64VerRep v1 v2 v3 v4 =- (fromIntegral v1 `shiftL` 48)- .|. (fromIntegral v2 `shiftL` 32)- .|. (fromIntegral v3 `shiftL` 16)- .|. fromIntegral v4--{-# INLINE inWord16 #-}-inWord16 :: Int -> Bool-inWord16 x = (fromIntegral x :: Word) <= 0xffff---- | Variant of 'mkVersion' which converts a "Data.Version"--- 'Base.Version' into Cabal's 'Version' type.------ @since 2.0.0.2-mkVersion' :: Base.Version -> Version-mkVersion' = mkVersion . Base.versionBranch---- | Unpack 'Version' into list of version number components.------ This is the inverse to 'mkVersion', so the following holds:------ > (versionNumbers . mkVersion) vs == vs------ @since 2.0.0.2-versionNumbers :: Version -> [Int]-versionNumbers (PV1 n ns) = n:ns-versionNumbers (PV0 w)- | v1 < 0 = []- | v2 < 0 = [v1]- | v3 < 0 = [v1,v2]- | v4 < 0 = [v1,v2,v3]- | otherwise = [v1,v2,v3,v4]- where- v1 = fromIntegral ((w `shiftR` 48) .&. 0xffff) - 1- v2 = fromIntegral ((w `shiftR` 32) .&. 0xffff) - 1- v3 = fromIntegral ((w `shiftR` 16) .&. 0xffff) - 1- v4 = fromIntegral (w .&. 0xffff) - 1----- | Constant representing the special /null/ 'Version'------ The 'nullVersion' compares (via 'Ord') as less than every proper--- 'Version' value.------ @since 2.0.0.2-nullVersion :: Version--- TODO: at some point, 'mkVersion' may disallow creating /null/--- 'Version's-nullVersion = PV0 0---- | Apply function to list of version number components------ > alterVersion f == mkVersion . f . versionNumbers------ @since 2.0.0.2-alterVersion :: ([Int] -> [Int]) -> Version -> Version-alterVersion f = mkVersion . f . versionNumbers---- internal helper-validVersion :: Version -> Bool-validVersion v = v /= nullVersion && all (>=0) (versionNumbers v)
− src/Distribution/Types/VersionInterval.hs
@@ -1,345 +0,0 @@-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE DeriveDataTypeable #-}--- | In @Cabal-3.6@ this module have been rewritten.-------module Distribution.Types.VersionInterval (- -- * Version intervals- VersionIntervals,- unVersionIntervals,-- -- * Conversions- toVersionIntervals,- fromVersionIntervals,-- -- ** Normalisation- normaliseVersionRange2,-- -- * Relaxation- relaxLastInterval,- relaxHeadInterval,-- -- * Version intervals view- asVersionIntervals,- VersionInterval (..),- LowerBound(..),- UpperBound(..),- Bound(..),-- -- * Invariants- invariantVersionIntervals,- ) where--import Control.Applicative (liftA2)-import Control.Exception (assert)-import Distribution.Compat.Prelude-import Prelude ()--import Distribution.Types.Version-import Distribution.Types.VersionRange.Internal---- To test this module, and to run version range normalisation benchmarks:------ cabal run Cabal:unit-tests -- -p Distribution.Version--- cabal run cabal-benchmarks -- -o bench.html normaliseVersionRange------------------------------------------------------------------------------------ Data------------------------------------------------------------------------------------ | A complementary representation of a 'VersionRange'. Instead of a boolean--- version predicate it uses an increasing sequence of non-overlapping,--- non-empty intervals.------ The key point is that this representation gives a canonical representation--- for the semantics of 'VersionRange's. This makes it easier to check things--- like whether a version range is empty, covers all versions, or requires a--- certain minimum or maximum version. It also makes it easy to check equality--- or containment. It also makes it easier to identify \'simple\' version--- predicates for translation into foreign packaging systems that do not--- support complex version range expressions.----newtype VersionIntervals = VersionIntervals [VersionInterval]- deriving (Eq, Show, Typeable)---- | Inspect the list of version intervals.----unVersionIntervals :: VersionIntervals -> [VersionInterval]-unVersionIntervals (VersionIntervals is) = is--data VersionInterval = VersionInterval !LowerBound !UpperBound deriving (Eq, Show)-data LowerBound = LowerBound !Version !Bound deriving (Eq, Show)-data UpperBound = NoUpperBound | UpperBound !Version !Bound deriving (Eq, Show)-data Bound = ExclusiveBound | InclusiveBound deriving (Eq, Show)--zeroLowerBound :: LowerBound-zeroLowerBound = LowerBound version0 InclusiveBound--isVersion0 :: Version -> Bool-isVersion0 = (==) version0------------------------------------------------------------------------------------ Stage1----------------------------------------------------------------------------------stage1 :: VersionRange -> [VersionInterval]-stage1 = cataVersionRange alg where- -- version range leafs transform into singleton intervals- alg (ThisVersionF v) = [VersionInterval (LowerBound v InclusiveBound) (UpperBound v InclusiveBound)]- alg (LaterVersionF v) = [VersionInterval (LowerBound v ExclusiveBound) NoUpperBound]- alg (OrLaterVersionF v) = [VersionInterval (LowerBound v InclusiveBound) NoUpperBound]- alg (EarlierVersionF v)- | isVersion0 v = []- | otherwise = [VersionInterval zeroLowerBound (UpperBound v ExclusiveBound)]- alg (OrEarlierVersionF v) = [VersionInterval zeroLowerBound (UpperBound v InclusiveBound)]-- -- ^>= version-range's upper bound should be MajorBound- alg (MajorBoundVersionF v) = [VersionInterval (LowerBound v InclusiveBound) (UpperBound (majorUpperBound v) ExclusiveBound)]-- -- union: just merge the version intervals- alg (UnionVersionRangesF v1 v2) = v1 ++ v2-- -- intersection: pairwise intersect. Strip empty intervals. Sort to restore the invariant.- alg (IntersectVersionRangesF v1 v2) = mapMaybe nonEmptyInterval $ liftA2 intersectInterval (stage2and3 v1) (stage2and3 v2)---- | Creck that interval is non-empty-nonEmptyInterval :: VersionInterval -> Maybe VersionInterval-nonEmptyInterval i | nonEmptyVI i = Just i-nonEmptyInterval _ = Nothing------------------------------------------------------------------------------------ Stage2----------------------------------------------------------------------------------stage2 :: [VersionInterval] -> [VersionInterval]-stage2 = sortBy lowerboundCmp--lowerboundCmp :: VersionInterval -> VersionInterval -> Ordering-lowerboundCmp (VersionInterval (LowerBound v vb) _) (VersionInterval (LowerBound u ub) _) =- compare v u `mappend` compareBound vb ub- where- compareBound :: Bound -> Bound -> Ordering- compareBound InclusiveBound InclusiveBound = EQ- compareBound InclusiveBound ExclusiveBound = LT- compareBound ExclusiveBound InclusiveBound = GT- compareBound ExclusiveBound ExclusiveBound = EQ------------------------------------------------------------------------------------ Postprocess------------------------------------------------------------------------------------ | Post-processing takes a list of ordered version intervals,--- but possibly overlapping, and creates 'VersionIntervals'.----postprocess :: [VersionInterval] -> VersionIntervals-postprocess = checkInvariant . VersionIntervals . stage2and3--stage2and3 :: [VersionInterval] -> [VersionInterval]-stage2and3 = stage3 . stage2--stage3 :: [VersionInterval] -> [VersionInterval]-stage3 [] = []-stage3 (VersionInterval lb ub : rest) = stage3go lb ub rest--stage3go :: LowerBound -> UpperBound -> [VersionInterval] -> [VersionInterval]-stage3go !lb NoUpperBound _ = [VersionInterval lb NoUpperBound]-stage3go !lb !ub [] = [VersionInterval lb ub]-stage3go !lb !ub (VersionInterval lb' ub' : rest')- | doesNotTouch ub lb' = VersionInterval lb ub : stage3go lb' ub' rest'- | otherwise = stage3go lb (unionUpper ub ub') rest'------------------------------------------------------------------------------------ Intersections----------------------------------------------------------------------------------intersectInterval :: VersionInterval -> VersionInterval -> VersionInterval-intersectInterval (VersionInterval lv uv) (VersionInterval lu uu) =- VersionInterval (intersectLower lv lu) (intersectUpper uv uu)--intersectLower :: LowerBound -> LowerBound -> LowerBound-intersectLower (LowerBound v vb) (LowerBound u ub) = case compare v u of- EQ -> LowerBound v (intersectBound vb ub)- LT -> LowerBound u ub- GT -> LowerBound v vb--intersectUpper :: UpperBound -> UpperBound -> UpperBound-intersectUpper NoUpperBound b = b-intersectUpper b NoUpperBound = b-intersectUpper (UpperBound v vb) (UpperBound u ub) = case compare v u of- EQ -> UpperBound v (intersectBound vb ub)- LT -> UpperBound v vb- GT -> UpperBound u ub--intersectBound :: Bound -> Bound -> Bound-intersectBound InclusiveBound InclusiveBound = InclusiveBound-intersectBound _ _ = ExclusiveBound------------------------------------------------------------------------------------ Unions----------------------------------------------------------------------------------unionUpper :: UpperBound -> UpperBound -> UpperBound-unionUpper NoUpperBound _ = NoUpperBound-unionUpper _ NoUpperBound = NoUpperBound-unionUpper (UpperBound v vb) (UpperBound u ub) = case compare v u of- EQ -> UpperBound v (unionBound vb ub)- LT -> UpperBound u ub- GT -> UpperBound v vb--unionBound :: Bound -> Bound -> Bound-unionBound ExclusiveBound ExclusiveBound = ExclusiveBound-unionBound _ _ = InclusiveBound------------------------------------------------------------------------------------ VersionRange------------------------------------------------------------------------------------ | View a 'VersionRange' as a union of intervals.------ This provides a canonical view of the semantics of a 'VersionRange' as--- opposed to the syntax of the expression used to define it. For the syntactic--- view use 'foldVersionRange'.------ Each interval is non-empty. The sequence is in increasing order and no--- intervals overlap or touch. Therefore only the first and last can be--- unbounded. The sequence can be empty if the range is empty--- (e.g. a range expression like @< 1 && > 2@).------ Other checks are trivial to implement using this view. For example:------ > isNoVersion vr | [] <- asVersionIntervals vr = True--- > | otherwise = False------ > isSpecificVersion vr--- > | [(LowerBound v InclusiveBound--- > ,UpperBound v' InclusiveBound)] <- asVersionIntervals vr--- > , v == v' = Just v--- > | otherwise = Nothing----asVersionIntervals :: VersionRange -> [VersionInterval]-asVersionIntervals = unVersionIntervals . toVersionIntervals------------------------------------------------------------------------------------ Helpers------------------------------------------------------------------------------------ | Check an upper bound does not intersect, or even touch a lower bound:------ @--- ---| or ---) but not ---] or ---) or ---]--- |--- (--- (--- [--- [------ @----doesNotTouch :: UpperBound -> LowerBound -> Bool-doesNotTouch NoUpperBound _ = False-doesNotTouch (UpperBound u ub) (LowerBound l lb) =- (u < l) || (u == l && ub == ExclusiveBound && lb == ExclusiveBound)------------------------------------------------------------------------------------ Invariants------------------------------------------------------------------------------------ | 'VersionIntervals' invariant:------ * all intervals are valid (lower bound is less then upper bound, i.e. non-empty)--- * intervals doesn't touch each other (distinct)----invariantVersionIntervals :: VersionIntervals -> Bool-invariantVersionIntervals (VersionIntervals intervals) =- all validInterval intervals &&- all doesNotTouch' adjacentIntervals- where- doesNotTouch' :: (VersionInterval, VersionInterval) -> Bool- doesNotTouch' (VersionInterval _ u, VersionInterval l' _) = doesNotTouch u l'-- adjacentIntervals :: [(VersionInterval, VersionInterval)]- adjacentIntervals = case intervals of- [] -> []- (_:tl) -> zip intervals tl--checkInvariant :: VersionIntervals -> VersionIntervals-checkInvariant is = assert (invariantVersionIntervals is) is-{-# INLINE checkInvariant #-}--validInterval :: VersionInterval -> Bool-validInterval i@(VersionInterval l u) = validLower l && validUpper u && nonEmptyVI i- where- validLower (LowerBound v _) = validVersion v- validUpper NoUpperBound = True- validUpper (UpperBound v _) = validVersion v---- Check an interval is non-empty----nonEmptyVI :: VersionInterval -> Bool-nonEmptyVI (VersionInterval _ NoUpperBound) = True-nonEmptyVI (VersionInterval (LowerBound l lb) (UpperBound u ub)) =- (l < u) || (l == u && lb == InclusiveBound && ub == InclusiveBound)------------------------------------------------------------------------------------ Conversions------------------------------------------------------------------------------------ | Convert a 'VersionRange' to a sequence of version intervals.----toVersionIntervals :: VersionRange -> VersionIntervals-toVersionIntervals = postprocess . stage1---- | Convert a 'VersionIntervals' value back into a 'VersionRange' expression--- representing the version intervals.----fromVersionIntervals :: VersionIntervals -> VersionRange-fromVersionIntervals (VersionIntervals []) = noVersion-fromVersionIntervals (VersionIntervals (x:xs)) = foldr1 unionVersionRanges (fmap intervalToVersionRange (x:|xs))--intervalToVersionRange :: VersionInterval -> VersionRange-intervalToVersionRange (VersionInterval (LowerBound v vb) upper') = case upper' of- NoUpperBound- -> lowerBound-- UpperBound u ub- | vb == InclusiveBound- , ub == InclusiveBound- , v == u- -> thisVersion v-- UpperBound u ub -> withLowerBound (makeUpperBound u ub)- where- lowerBound :: VersionRange- lowerBound = case vb of- InclusiveBound -> orLaterVersion v- ExclusiveBound -> laterVersion v-- withLowerBound :: VersionRange -> VersionRange- withLowerBound vr- | isVersion0 v, vb == InclusiveBound = vr- | otherwise = intersectVersionRanges lowerBound vr-- makeUpperBound :: Version -> Bound -> VersionRange- makeUpperBound u InclusiveBound = orEarlierVersion u- makeUpperBound u ExclusiveBound = earlierVersion u------------------------------------------------------------------------------------ Normalisation------------------------------------------------------------------------------------ | Since @Cabal-3.6@ this function.. TODO----normaliseVersionRange2 :: VersionRange -> VersionRange-normaliseVersionRange2 = fromVersionIntervals . toVersionIntervals------------------------------------------------------------------------------------ Relaxation----------------------------------------------------------------------------------relaxLastInterval :: VersionIntervals -> VersionIntervals-relaxLastInterval (VersionIntervals xs) = VersionIntervals (relaxLastInterval' xs)- where- relaxLastInterval' [] = []- relaxLastInterval' [VersionInterval l _] = [VersionInterval l NoUpperBound]- relaxLastInterval' (i:is) = i : relaxLastInterval' is--relaxHeadInterval :: VersionIntervals -> VersionIntervals-relaxHeadInterval (VersionIntervals xs) = VersionIntervals (relaxHeadInterval' xs)- where- relaxHeadInterval' [] = []- relaxHeadInterval' (VersionInterval _ u : is) = VersionInterval zeroLowerBound u : is
− src/Distribution/Types/VersionInterval/Legacy.hs
@@ -1,368 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}--- | This is old version of "Distribution.Types.VersionInterval" module.------ It will be removed in @Cabal-3.8@.----module Distribution.Types.VersionInterval.Legacy {-# DEPRECATED "Use Distribution.Types.VersionInterval instead" #-} (- -- * Version intervals- VersionIntervals,- toVersionIntervals,- fromVersionIntervals,- withinIntervals,- versionIntervals,- mkVersionIntervals,- unionVersionIntervals,- intersectVersionIntervals,- invertVersionIntervals,- relaxLastInterval,- relaxHeadInterval,-- -- * Version intervals view- asVersionIntervals,- VersionInterval,- LowerBound(..),- UpperBound(..),- Bound(..),- ) where--import Prelude ()-import Distribution.Compat.Prelude-import Control.Exception (assert)--import Distribution.Types.Version-import Distribution.Types.VersionRange.Internal---- NonEmpty-import qualified Prelude (foldr1)------------------------------------------------------------------------------------ VersionRange------------------------------------------------------------------------------------ | View a 'VersionRange' as a union of intervals.------ This provides a canonical view of the semantics of a 'VersionRange' as--- opposed to the syntax of the expression used to define it. For the syntactic--- view use 'foldVersionRange'.------ Each interval is non-empty. The sequence is in increasing order and no--- intervals overlap or touch. Therefore only the first and last can be--- unbounded. The sequence can be empty if the range is empty--- (e.g. a range expression like @< 1 && > 2@).------ Other checks are trivial to implement using this view. For example:------ > isNoVersion vr | [] <- asVersionIntervals vr = True--- > | otherwise = False------ > isSpecificVersion vr--- > | [(LowerBound v InclusiveBound--- > ,UpperBound v' InclusiveBound)] <- asVersionIntervals vr--- > , v == v' = Just v--- > | otherwise = Nothing----asVersionIntervals :: VersionRange -> [VersionInterval]-asVersionIntervals = versionIntervals . toVersionIntervals------------------------------------------------------------------------------------- VersionInterval------------------------------------------------------------------------------------ | A complementary representation of a 'VersionRange'. Instead of a boolean--- version predicate it uses an increasing sequence of non-overlapping,--- non-empty intervals.------ The key point is that this representation gives a canonical representation--- for the semantics of 'VersionRange's. This makes it easier to check things--- like whether a version range is empty, covers all versions, or requires a--- certain minimum or maximum version. It also makes it easy to check equality--- or containment. It also makes it easier to identify \'simple\' version--- predicates for translation into foreign packaging systems that do not--- support complex version range expressions.----newtype VersionIntervals = VersionIntervals [VersionInterval]- deriving (Eq, Show, Typeable)---- | Inspect the list of version intervals.----versionIntervals :: VersionIntervals -> [VersionInterval]-versionIntervals (VersionIntervals is) = is--type VersionInterval = (LowerBound, UpperBound)-data LowerBound = LowerBound Version !Bound deriving (Eq, Show)-data UpperBound = NoUpperBound | UpperBound Version !Bound deriving (Eq, Show)-data Bound = ExclusiveBound | InclusiveBound deriving (Eq, Show)--minLowerBound :: LowerBound-minLowerBound = LowerBound (mkVersion [0]) InclusiveBound--isVersion0 :: Version -> Bool-isVersion0 = (==) version0--instance Ord LowerBound where- LowerBound ver bound <= LowerBound ver' bound' = case compare ver ver' of- LT -> True- EQ -> not (bound == ExclusiveBound && bound' == InclusiveBound)- GT -> False--instance Ord UpperBound where- _ <= NoUpperBound = True- NoUpperBound <= UpperBound _ _ = False- UpperBound ver bound <= UpperBound ver' bound' = case compare ver ver' of- LT -> True- EQ -> not (bound == InclusiveBound && bound' == ExclusiveBound)- GT -> False--invariant :: VersionIntervals -> Bool-invariant (VersionIntervals intervals) = all validInterval intervals- && all doesNotTouch' adjacentIntervals- where- doesNotTouch' :: (VersionInterval, VersionInterval) -> Bool- doesNotTouch' ((_,u), (l',_)) = doesNotTouch u l'-- adjacentIntervals :: [(VersionInterval, VersionInterval)]- adjacentIntervals = case intervals of- [] -> []- (_:tl) -> zip intervals tl--checkInvariant :: VersionIntervals -> VersionIntervals-checkInvariant is = assert (invariant is) is---- | Directly construct a 'VersionIntervals' from a list of intervals.------ In @Cabal-2.2@ the 'Maybe' is dropped from the result type.----mkVersionIntervals :: [VersionInterval] -> VersionIntervals-mkVersionIntervals intervals- | invariant (VersionIntervals intervals) = VersionIntervals intervals- | otherwise- = checkInvariant- . foldl' (flip insertInterval) (VersionIntervals [])- . filter validInterval- $ intervals--insertInterval :: VersionInterval -> VersionIntervals -> VersionIntervals-insertInterval i is = unionVersionIntervals (VersionIntervals [i]) is--validInterval :: (LowerBound, UpperBound) -> Bool-validInterval i@(l, u) = validLower l && validUpper u && nonEmptyVI i- where- validLower (LowerBound v _) = validVersion v- validUpper NoUpperBound = True- validUpper (UpperBound v _) = validVersion v---- Check an interval is non-empty----nonEmptyVI :: VersionInterval -> Bool-nonEmptyVI (_, NoUpperBound ) = True-nonEmptyVI (LowerBound l lb, UpperBound u ub) =- (l < u) || (l == u && lb == InclusiveBound && ub == InclusiveBound)---- Check an upper bound does not intersect, or even touch a lower bound:------ ---| or ---) but not ---] or ---) or ---]--- |--- (--- (--- [--- [-------doesNotTouch :: UpperBound -> LowerBound -> Bool-doesNotTouch NoUpperBound _ = False-doesNotTouch (UpperBound u ub) (LowerBound l lb) =- u < l- || (u == l && ub == ExclusiveBound && lb == ExclusiveBound)---- | Check an upper bound does not intersect a lower bound:------ ---| or ---) or ---] or ---) but not ---]--- |--- (--- (--- [--- [-------doesNotIntersect :: UpperBound -> LowerBound -> Bool-doesNotIntersect NoUpperBound _ = False-doesNotIntersect (UpperBound u ub) (LowerBound l lb) =- u < l- || (u == l && not (ub == InclusiveBound && lb == InclusiveBound))---- | Test if a version falls within the version intervals.------ It exists mostly for completeness and testing. It satisfies the following--- properties:------ > withinIntervals v (toVersionIntervals vr) = withinRange v vr--- > withinIntervals v ivs = withinRange v (fromVersionIntervals ivs)----withinIntervals :: Version -> VersionIntervals -> Bool-withinIntervals v (VersionIntervals intervals) = any withinInterval intervals- where- withinInterval (lowerBound, upperBound) = withinLower lowerBound- && withinUpper upperBound- withinLower (LowerBound v' ExclusiveBound) = v' < v- withinLower (LowerBound v' InclusiveBound) = v' <= v-- withinUpper NoUpperBound = True- withinUpper (UpperBound v' ExclusiveBound) = v' > v- withinUpper (UpperBound v' InclusiveBound) = v' >= v---- | Convert a 'VersionRange' to a sequence of version intervals.----toVersionIntervals :: VersionRange -> VersionIntervals-toVersionIntervals = cataVersionRange alg where- alg (ThisVersionF v) = chkIvl (LowerBound v InclusiveBound, UpperBound v InclusiveBound)- alg (LaterVersionF v) = chkIvl (LowerBound v ExclusiveBound, NoUpperBound)- alg (OrLaterVersionF v) = chkIvl (LowerBound v InclusiveBound, NoUpperBound)- alg (EarlierVersionF v)- | isVersion0 v = VersionIntervals []- | otherwise = chkIvl (minLowerBound, UpperBound v ExclusiveBound)- alg (OrEarlierVersionF v) = chkIvl (minLowerBound, UpperBound v InclusiveBound)- alg (MajorBoundVersionF v) = chkIvl (LowerBound v InclusiveBound, UpperBound (majorUpperBound v) ExclusiveBound)- alg (UnionVersionRangesF v1 v2) = unionVersionIntervals v1 v2- alg (IntersectVersionRangesF v1 v2) = intersectVersionIntervals v1 v2-- chkIvl interval = checkInvariant (VersionIntervals [interval])---- | Convert a 'VersionIntervals' value back into a 'VersionRange' expression--- representing the version intervals.----fromVersionIntervals :: VersionIntervals -> VersionRange-fromVersionIntervals (VersionIntervals []) = noVersion-fromVersionIntervals (VersionIntervals intervals) =- Prelude.foldr1 unionVersionRanges [ interval l u | (l, u) <- intervals ]-- where- interval (LowerBound v InclusiveBound)- (UpperBound v' InclusiveBound) | v == v'- = thisVersion v- interval l u = lowerBound l `intersectVersionRanges'` upperBound u-- lowerBound (LowerBound v InclusiveBound)- | isVersion0 v = Nothing- | otherwise = Just (orLaterVersion v)- lowerBound (LowerBound v ExclusiveBound) = Just (laterVersion v)-- upperBound NoUpperBound = Nothing- upperBound (UpperBound v InclusiveBound) = Just (orEarlierVersion v)- upperBound (UpperBound v ExclusiveBound) = Just (earlierVersion v)-- intersectVersionRanges' Nothing Nothing = anyVersion- intersectVersionRanges' (Just vr) Nothing = vr- intersectVersionRanges' Nothing (Just vr) = vr- intersectVersionRanges' (Just vr) (Just vr') = intersectVersionRanges vr vr'--unionVersionIntervals :: VersionIntervals -> VersionIntervals- -> VersionIntervals-unionVersionIntervals (VersionIntervals is0) (VersionIntervals is'0) =- checkInvariant (VersionIntervals (union is0 is'0))- where- union is [] = is- union [] is' = is'- union (i:is) (i':is') = case unionInterval i i' of- Left Nothing -> i : union is (i' :is')- Left (Just i'') -> union is (i'':is')- Right Nothing -> i' : union (i :is) is'- Right (Just i'') -> union (i'':is) is'--unionInterval :: VersionInterval -> VersionInterval- -> Either (Maybe VersionInterval) (Maybe VersionInterval)-unionInterval (lower , upper ) (lower', upper')-- -- Non-intersecting intervals with the left interval ending first- | upper `doesNotTouch` lower' = Left Nothing-- -- Non-intersecting intervals with the right interval first- | upper' `doesNotTouch` lower = Right Nothing-- -- Complete or partial overlap, with the left interval ending first- | upper <= upper' = lowerBound `seq`- Left (Just (lowerBound, upper'))-- -- Complete or partial overlap, with the left interval ending first- | otherwise = lowerBound `seq`- Right (Just (lowerBound, upper))- where- lowerBound = min lower lower'--intersectVersionIntervals :: VersionIntervals -> VersionIntervals- -> VersionIntervals-intersectVersionIntervals (VersionIntervals is0) (VersionIntervals is'0) =- checkInvariant (VersionIntervals (intersect is0 is'0))- where- intersect _ [] = []- intersect [] _ = []- intersect (i:is) (i':is') = case intersectInterval i i' of- Left Nothing -> intersect is (i':is')- Left (Just i'') -> i'' : intersect is (i':is')- Right Nothing -> intersect (i:is) is'- Right (Just i'') -> i'' : intersect (i:is) is'--intersectInterval :: VersionInterval -> VersionInterval- -> Either (Maybe VersionInterval) (Maybe VersionInterval)-intersectInterval (lower , upper ) (lower', upper')-- -- Non-intersecting intervals with the left interval ending first- | upper `doesNotIntersect` lower' = Left Nothing-- -- Non-intersecting intervals with the right interval first- | upper' `doesNotIntersect` lower = Right Nothing-- -- Complete or partial overlap, with the left interval ending first- | upper <= upper' = lowerBound `seq`- Left (Just (lowerBound, upper))-- -- Complete or partial overlap, with the right interval ending first- | otherwise = lowerBound `seq`- Right (Just (lowerBound, upper'))- where- lowerBound = max lower lower'--invertVersionIntervals :: VersionIntervals- -> VersionIntervals-invertVersionIntervals (VersionIntervals xs) =- case xs of- -- Empty interval set- [] -> VersionIntervals [(noLowerBound, NoUpperBound)]- -- Interval with no lower bound- ((lb, ub) : more) | lb == noLowerBound ->- VersionIntervals $ invertVersionIntervals' ub more- -- Interval with a lower bound- ((lb, ub) : more) ->- VersionIntervals $ (noLowerBound, invertLowerBound lb)- : invertVersionIntervals' ub more- where- -- Invert subsequent version intervals given the upper bound of- -- the intervals already inverted.- invertVersionIntervals' :: UpperBound- -> [(LowerBound, UpperBound)]- -> [(LowerBound, UpperBound)]- invertVersionIntervals' NoUpperBound [] = []- invertVersionIntervals' ub0 [] = [(invertUpperBound ub0, NoUpperBound)]- invertVersionIntervals' ub0 [(lb, NoUpperBound)] =- [(invertUpperBound ub0, invertLowerBound lb)]- invertVersionIntervals' ub0 ((lb, ub1) : more) =- (invertUpperBound ub0, invertLowerBound lb)- : invertVersionIntervals' ub1 more-- invertLowerBound :: LowerBound -> UpperBound- invertLowerBound (LowerBound v b) = UpperBound v (invertBound b)-- invertUpperBound :: UpperBound -> LowerBound- invertUpperBound (UpperBound v b) = LowerBound v (invertBound b)- invertUpperBound NoUpperBound = error "NoUpperBound: unexpected"-- invertBound :: Bound -> Bound- invertBound ExclusiveBound = InclusiveBound- invertBound InclusiveBound = ExclusiveBound-- noLowerBound :: LowerBound- noLowerBound = LowerBound (mkVersion [0]) InclusiveBound---relaxLastInterval :: VersionIntervals -> VersionIntervals-relaxLastInterval (VersionIntervals xs) = VersionIntervals (relaxLastInterval' xs)- where- relaxLastInterval' [] = []- relaxLastInterval' [(l,_)] = [(l, NoUpperBound)]- relaxLastInterval' (i:is) = i : relaxLastInterval' is--relaxHeadInterval :: VersionIntervals -> VersionIntervals-relaxHeadInterval (VersionIntervals xs) = VersionIntervals (relaxHeadInterval' xs)- where- relaxHeadInterval' [] = []- relaxHeadInterval' ((_,u):is) = (minLowerBound,u) : is
− src/Distribution/Types/VersionRange.hs
@@ -1,180 +0,0 @@-module Distribution.Types.VersionRange (- -- * Version ranges- VersionRange,-- -- ** Constructing- anyVersion, noVersion,- thisVersion, notThisVersion,- laterVersion, earlierVersion,- orLaterVersion, orEarlierVersion,- unionVersionRanges, intersectVersionRanges,- withinVersion,- majorBoundVersion,-- -- ** Inspection- --- -- See "Distribution.Version" for more utilities.- withinRange,- foldVersionRange,- normaliseVersionRange,- stripParensVersionRange,- hasUpperBound,- hasLowerBound,-- -- ** Cata & ana- VersionRangeF (..),- cataVersionRange,- anaVersionRange,- hyloVersionRange,- projectVersionRange,- embedVersionRange,-- -- ** Utilities- isAnyVersion,- isAnyVersionLight,- wildcardUpperBound,- majorUpperBound,- isWildcardRange,- versionRangeParser,- ) where--import Distribution.Compat.Prelude-import Distribution.Types.Version-import Distribution.Types.VersionInterval-import Distribution.Types.VersionRange.Internal-import Prelude ()---- | Fold over the basic syntactic structure of a 'VersionRange'.------ This provides a syntactic view of the expression defining the version range.--- The syntactic sugar @\">= v\"@, @\"<= v\"@ and @\"== v.*\"@ is presented--- in terms of the other basic syntax.------ For a semantic view use 'asVersionIntervals'.----foldVersionRange :: a -- ^ @\"-any\"@ version- -> (Version -> a) -- ^ @\"== v\"@- -> (Version -> a) -- ^ @\"> v\"@- -> (Version -> a) -- ^ @\"< v\"@- -> (a -> a -> a) -- ^ @\"_ || _\"@ union- -> (a -> a -> a) -- ^ @\"_ && _\"@ intersection- -> VersionRange -> a-foldVersionRange _any this later earlier union intersect = fold- where- fold = cataVersionRange alg-- alg (ThisVersionF v) = this v- alg (LaterVersionF v) = later v- alg (OrLaterVersionF v) = union (this v) (later v)- alg (EarlierVersionF v) = earlier v- alg (OrEarlierVersionF v) = union (this v) (earlier v)- alg (MajorBoundVersionF v) = fold (majorBound v)- alg (UnionVersionRangesF v1 v2) = union v1 v2- alg (IntersectVersionRangesF v1 v2) = intersect v1 v2-- majorBound v = intersectVersionRanges- (orLaterVersion v)- (earlierVersion (majorUpperBound v))---- | Normalise 'VersionRange'.------ In particular collapse @(== v || > v)@ into @>= v@, and so on.-normaliseVersionRange :: VersionRange -> VersionRange-normaliseVersionRange = hyloVersionRange embed projectVersionRange- where- -- == v || > v, > v || == v ==> >= v- embed (UnionVersionRangesF (ThisVersion v) (LaterVersion v')) | v == v' =- orLaterVersion v- embed (UnionVersionRangesF (LaterVersion v) (ThisVersion v')) | v == v' =- orLaterVersion v-- -- == v || < v, < v || == v ==> <= v- embed (UnionVersionRangesF (ThisVersion v) (EarlierVersion v')) | v == v' =- orEarlierVersion v- embed (UnionVersionRangesF (EarlierVersion v) (ThisVersion v')) | v == v' =- orEarlierVersion v-- -- otherwise embed normally- embed vr = embedVersionRange vr---- | Remove 'VersionRangeParens' constructors.------ Since version 3.4 this function is 'id', there aren't 'VersionRangeParens' constructor in 'VersionRange' anymore.------ @since 2.2-stripParensVersionRange :: VersionRange -> VersionRange-stripParensVersionRange = id---- | Does this version fall within the given range?------ This is the evaluation function for the 'VersionRange' type.----withinRange :: Version -> VersionRange -> Bool-withinRange v = foldVersionRange- True- (\v' -> v == v')- (\v' -> v > v')- (\v' -> v < v')- (||)- (&&)---- | Does this 'VersionRange' place any restriction on the 'Version' or is it--- in fact equivalent to 'AnyVersion'.------ Note this is a semantic check, not simply a syntactic check. So for example--- the following is @True@ (for all @v@).------ > isAnyVersion (EarlierVersion v `UnionVersionRanges` orLaterVersion v)----isAnyVersion :: VersionRange -> Bool-isAnyVersion vr = case asVersionIntervals vr of- [VersionInterval (LowerBound v InclusiveBound) NoUpperBound] -> v == version0- _ -> False---- A fast and non-precise version of 'isAnyVersion',--- returns 'True' only for @>= 0@ 'VersionRange's.------ /Do not use/. The "VersionIntervals don't destroy MajorBoundVersion"--- https://github.com/haskell/cabal/pull/6736 pull-request--- will change 'simplifyVersionRange' to properly preserve semantics.--- Then we can use it to normalise 'VersionRange's in tests.----isAnyVersionLight :: VersionRange -> Bool-isAnyVersionLight (OrLaterVersion v) = v == version0-isAnyVersionLight _vr = False--------------------------------- Wildcard range utilities------isWildcardRange :: Version -> Version -> Bool-isWildcardRange ver1 ver2 = check (versionNumbers ver1) (versionNumbers ver2)- where check (n:[]) (m:[]) | n+1 == m = True- check (n:ns) (m:ms) | n == m = check ns ms- check _ _ = False---- | Does the version range have an upper bound?------ @since 1.24.0.0-hasUpperBound :: VersionRange -> Bool-hasUpperBound = foldVersionRange- False- (const True)- (const False)- (const True)- (&&) (||)---- | Does the version range have an explicit lower bound?------ Note: this function only considers the user-specified lower bounds, but not--- the implicit >=0 lower bound.------ @since 1.24.0.0-hasLowerBound :: VersionRange -> Bool-hasLowerBound = foldVersionRange- False- (const True)- (const True)- (const False)- (&&) (||)
− src/Distribution/Types/VersionRange/Internal.hs
@@ -1,526 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveFoldable #-}-{-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE DeriveTraversable #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ScopedTypeVariables #-}---- | The only purpose of this module is to prevent the export of--- 'VersionRange' constructors from--- 'Distribution.Types.VersionRange'. To avoid creating orphan--- instances, a lot of related code had to be moved here too.--module Distribution.Types.VersionRange.Internal- ( VersionRange(..)- , anyVersion, noVersion- , thisVersion, notThisVersion- , laterVersion, earlierVersion- , orLaterVersion, orEarlierVersion- , unionVersionRanges, intersectVersionRanges- , withinVersion- , majorBoundVersion-- , VersionRangeF(..)- , projectVersionRange- , embedVersionRange- , cataVersionRange- , anaVersionRange- , hyloVersionRange- , versionRangeParser-- , majorUpperBound- , wildcardUpperBound- ) where--import Distribution.Compat.Prelude-import Distribution.Types.Version-import Prelude ()--import Distribution.CabalSpecVersion-import Distribution.Parsec-import Distribution.Pretty-import Distribution.Utils.Generic (unsnoc)--import qualified Distribution.Compat.CharParsing as P-import qualified Distribution.Compat.DList as DList-import qualified Text.PrettyPrint as Disp--data VersionRange- = ThisVersion Version -- = version- | LaterVersion Version -- > version (NB. not >=)- | OrLaterVersion Version -- >= version- | EarlierVersion Version -- < version- | OrEarlierVersion Version -- <= version- | MajorBoundVersion Version -- @^>= ver@ (same as >= ver && < MAJ(ver)+1)- | UnionVersionRanges VersionRange VersionRange- | IntersectVersionRanges VersionRange VersionRange- deriving ( Data, Eq, Generic, Read, Show, Typeable )--instance Binary VersionRange-instance Structured VersionRange-instance NFData VersionRange where rnf = genericRnf---- | The version range @-any@. That is, a version range containing all--- versions.------ > withinRange v anyVersion = True----anyVersion :: VersionRange-anyVersion = OrLaterVersion (mkVersion [0])---- | The empty version range, that is a version range containing no versions.------ This can be constructed using any unsatisfiable version range expression,--- for example @< 0@.------ > withinRange v noVersion = False----noVersion :: VersionRange-noVersion = EarlierVersion (mkVersion [0])---- | The version range @== v@------ > withinRange v' (thisVersion v) = v' == v----thisVersion :: Version -> VersionRange-thisVersion = ThisVersion---- | The version range @< v || > v@------ > withinRange v' (notThisVersion v) = v' /= v----notThisVersion :: Version -> VersionRange-notThisVersion v = UnionVersionRanges (EarlierVersion v) (LaterVersion v)---- | The version range @> v@------ > withinRange v' (laterVersion v) = v' > v----laterVersion :: Version -> VersionRange-laterVersion = LaterVersion---- | The version range @>= v@------ > withinRange v' (orLaterVersion v) = v' >= v----orLaterVersion :: Version -> VersionRange-orLaterVersion = OrLaterVersion---- | The version range @< v@------ > withinRange v' (earlierVersion v) = v' < v----earlierVersion :: Version -> VersionRange-earlierVersion = EarlierVersion---- | The version range @<= v@------ > withinRange v' (orEarlierVersion v) = v' <= v----orEarlierVersion :: Version -> VersionRange-orEarlierVersion = OrEarlierVersion---- | The version range @vr1 || vr2@------ > withinRange v' (unionVersionRanges vr1 vr2)--- > = withinRange v' vr1 || withinRange v' vr2----unionVersionRanges :: VersionRange -> VersionRange -> VersionRange-unionVersionRanges = UnionVersionRanges---- | The version range @vr1 && vr2@------ > withinRange v' (intersectVersionRanges vr1 vr2)--- > = withinRange v' vr1 && withinRange v' vr2----intersectVersionRanges :: VersionRange -> VersionRange -> VersionRange-intersectVersionRanges = IntersectVersionRanges---- | The version range @== v.*@.------ For example, for version @1.2@, the version range @== 1.2.*@ is the same as--- @>= 1.2 && < 1.3@------ > withinRange v' (laterVersion v) = v' >= v && v' < upper v--- > where--- > upper (Version lower t) = Version (init lower ++ [last lower + 1]) t----withinVersion :: Version -> VersionRange-withinVersion v = intersectVersionRanges- (orLaterVersion v)- (earlierVersion (wildcardUpperBound v))---- | The version range @^>= v@.------ For example, for version @1.2.3.4@, the version range @^>= 1.2.3.4@--- is the same as @>= 1.2.3.4 && < 1.3@.------ Note that @^>= 1@ is equivalent to @>= 1 && < 1.1@.------ @since 2.0.0.2-majorBoundVersion :: Version -> VersionRange-majorBoundVersion = MajorBoundVersion----- | F-Algebra of 'VersionRange'. See 'cataVersionRange'.------ @since 2.2-data VersionRangeF a- = ThisVersionF Version -- = version- | LaterVersionF Version -- > version (NB. not >=)- | OrLaterVersionF Version -- >= version- | EarlierVersionF Version -- < version- | OrEarlierVersionF Version -- <= version- | MajorBoundVersionF Version -- @^>= ver@ (same as >= ver && < MAJ(ver)+1)- | UnionVersionRangesF a a- | IntersectVersionRangesF a a- deriving ( Data, Eq, Generic, Read, Show, Typeable- , Functor, Foldable, Traversable )---- | @since 2.2-projectVersionRange :: VersionRange -> VersionRangeF VersionRange-projectVersionRange (ThisVersion v) = ThisVersionF v-projectVersionRange (LaterVersion v) = LaterVersionF v-projectVersionRange (OrLaterVersion v) = OrLaterVersionF v-projectVersionRange (EarlierVersion v) = EarlierVersionF v-projectVersionRange (OrEarlierVersion v) = OrEarlierVersionF v-projectVersionRange (MajorBoundVersion v) = MajorBoundVersionF v-projectVersionRange (UnionVersionRanges a b) = UnionVersionRangesF a b-projectVersionRange (IntersectVersionRanges a b) = IntersectVersionRangesF a b---- | Fold 'VersionRange'.------ @since 2.2-cataVersionRange :: (VersionRangeF a -> a) -> VersionRange -> a-cataVersionRange f = c where c = f . fmap c . projectVersionRange---- | @since 2.2-embedVersionRange :: VersionRangeF VersionRange -> VersionRange-embedVersionRange (ThisVersionF v) = ThisVersion v-embedVersionRange (LaterVersionF v) = LaterVersion v-embedVersionRange (OrLaterVersionF v) = OrLaterVersion v-embedVersionRange (EarlierVersionF v) = EarlierVersion v-embedVersionRange (OrEarlierVersionF v) = OrEarlierVersion v-embedVersionRange (MajorBoundVersionF v) = MajorBoundVersion v-embedVersionRange (UnionVersionRangesF a b) = UnionVersionRanges a b-embedVersionRange (IntersectVersionRangesF a b) = IntersectVersionRanges a b---- | Unfold 'VersionRange'.------ @since 2.2-anaVersionRange :: (a -> VersionRangeF a) -> a -> VersionRange-anaVersionRange g = a where a = embedVersionRange . fmap a . g---- | Refold 'VersionRange'------ @since 2.2-hyloVersionRange :: (VersionRangeF VersionRange -> VersionRange)- -> (VersionRange -> VersionRangeF VersionRange)- -> VersionRange -> VersionRange-hyloVersionRange f g = h where h = f . fmap h . g------------------------------------------------------------------------------------ Parsec & Pretty------------------------------------------------------------------------------------ |------ >>> fmap pretty (simpleParsec' CabalSpecV1_6 "== 3.2.*" :: Maybe VersionRange)--- Just >=3.2 && <3.3------ >>> fmap (prettyVersioned CabalSpecV1_6) (simpleParsec' CabalSpecV1_6 "== 3.2.*" :: Maybe VersionRange)--- Just ==3.2.*------ >>> fmap pretty (simpleParsec' CabalSpecV1_6 "-any" :: Maybe VersionRange)--- Just >=0------ >>> fmap (prettyVersioned CabalSpecV1_6) (simpleParsec' CabalSpecV1_6 "-any" :: Maybe VersionRange)--- Just >=0----instance Pretty VersionRange where- pretty = prettyVersioned cabalSpecLatest-- prettyVersioned csv- | csv > CabalSpecV1_6 = prettyVersionRange- | otherwise = prettyVersionRange16--prettyVersionRange :: VersionRange -> Disp.Doc-prettyVersionRange vr = cataVersionRange alg vr 0- where- alg :: VersionRangeF (Int -> Disp.Doc) -> Int -> Disp.Doc- alg (ThisVersionF v) _ = Disp.text "==" <<>> pretty v- alg (LaterVersionF v) _ = Disp.text ">" <<>> pretty v- alg (OrLaterVersionF v) _ = Disp.text ">=" <<>> pretty v- alg (EarlierVersionF v) _ = Disp.text "<" <<>> pretty v- alg (OrEarlierVersionF v) _ = Disp.text "<=" <<>> pretty v- alg (MajorBoundVersionF v) _ = Disp.text "^>=" <<>> pretty v- alg (UnionVersionRangesF r1 r2) d = parens (d > 0)- $ r1 1 <+> Disp.text "||" <+> r2 0- alg (IntersectVersionRangesF r1 r2) d = parens (d > 1)- $ r1 2 <+> Disp.text "&&" <+> r2 1-- parens True = Disp.parens- parens False = id---- | Don't use && and || operators. If possible.-prettyVersionRange16 :: VersionRange -> Disp.Doc-prettyVersionRange16 (IntersectVersionRanges (OrLaterVersion v) (EarlierVersion u))- | u == wildcardUpperBound v- = Disp.text "==" <<>> dispWild v- where- dispWild ver =- Disp.hcat (Disp.punctuate (Disp.char '.')- (map Disp.int $ versionNumbers ver))- <<>> Disp.text ".*"--prettyVersionRange16 vr = prettyVersionRange vr---- |------ >>> simpleParsec "^>= 3.4" :: Maybe VersionRange--- Just (MajorBoundVersion (mkVersion [3,4]))------ Small history:------ @-any@ and @-none@ removed in 3.4--- Use @>=0@ and @<0@ instead.------ >>> map (`simpleParsec'` "-none") [CabalSpecV3_0, CabalSpecV3_4] :: [Maybe VersionRange]--- [Just (EarlierVersion (mkVersion [0])),Nothing]------ Set operations are introduced in 3.0------ >>> map (`simpleParsec'` "^>= { 1.2 , 1.3 }") [CabalSpecV2_4, CabalSpecV3_0] :: [Maybe VersionRange]--- [Nothing,Just (UnionVersionRanges (MajorBoundVersion (mkVersion [1,2])) (MajorBoundVersion (mkVersion [1,3])))]------ @^>=@ is introduced in 2.0------ >>> map (`simpleParsec'` "^>=1.2") [CabalSpecV1_24, CabalSpecV2_0] :: [Maybe VersionRange]--- [Nothing,Just (MajorBoundVersion (mkVersion [1,2]))]------ @-none@ is introduced in 1.22------ >>> map (`simpleParsec'` "-none") [CabalSpecV1_20, CabalSpecV1_22] :: [Maybe VersionRange]--- [Nothing,Just (EarlierVersion (mkVersion [0]))]------ Operators are introduced in 1.8. Issues only a warning.------ >>> map (`simpleParsecW'` "== 1 || ==2") [CabalSpecV1_6, CabalSpecV1_8] :: [Maybe VersionRange]--- [Nothing,Just (UnionVersionRanges (ThisVersion (mkVersion [1])) (ThisVersion (mkVersion [2])))]------ Wild-version ranges are introduced in 1.6. Issues only a warning.------ >>> map (`simpleParsecW'` "== 1.2.*") [CabalSpecV1_4, CabalSpecV1_6] :: [Maybe VersionRange]--- [Nothing,Just (IntersectVersionRanges (OrLaterVersion (mkVersion [1,2])) (EarlierVersion (mkVersion [1,3])))]----instance Parsec VersionRange where- parsec = askCabalSpecVersion >>= versionRangeParser versionDigitParser---- | 'VersionRange' parser parametrised by version digit parser------ - 'versionDigitParser' is used for all 'VersionRange'.--- - 'P.integral' is used for backward-compat @pkgconfig-depends@--- versions, 'PkgConfigVersionRange'.------ @since 3.0-versionRangeParser :: forall m. CabalParsing m => m Int -> CabalSpecVersion -> m VersionRange-versionRangeParser digitParser csv = expr- where- expr = do P.spaces- t <- term- P.spaces- (do _ <- P.string "||"- checkOp- P.spaces- e <- expr- return (unionVersionRanges t e)- <|>- return t)- term = do f <- factor- P.spaces- (do _ <- P.string "&&"- checkOp- P.spaces- t <- term- return (intersectVersionRanges f t)- <|>- return f)- factor = parens expr <|> prim-- prim = do- op <- P.munch1 isOpChar P.<?> "operator"- case op of- "-" -> anyVersion <$ P.string "any" <|> P.string "none" *> noVersion'-- "==" -> do- P.spaces- (do (wild, v) <- verOrWild- checkWild wild- pure $ (if wild then withinVersion else thisVersion) v- <|>- (verSet' thisVersion =<< verSet))-- "^>=" -> do- P.spaces- (do (wild, v) <- verOrWild- when wild $ P.unexpected $- "wild-card version after ^>= operator"- majorBoundVersion' v- <|>- (verSet' majorBoundVersion =<< verSet))-- _ -> do- P.spaces- (wild, v) <- verOrWild- when wild $ P.unexpected $- "wild-card version after non-== operator: " ++ show op- case op of- ">=" -> pure $ orLaterVersion v- "<" -> pure $ earlierVersion v- "<=" -> pure $ orEarlierVersion v- ">" -> pure $ laterVersion v- _ -> fail $ "Unknown version operator " ++ show op-- -- Cannot be warning- -- On 2020-03-16 there was around 27400 files on Hackage failing to parse due this- -- For example https://hackage.haskell.org/package/haxr-3000.0.0/haxr.cabal- --- checkOp = when (csv < CabalSpecV1_8) $- parsecWarning PWTVersionOperator $ unwords- [ "version operators used."- , "To use version operators the package needs to specify at least 'cabal-version: >= 1.8'."- ]-- -- Cannot be warning- -- On 2020-03-16 there was 46 files on Hackage failing to parse due this- -- For example https://hackage.haskell.org/package/derive-0.1.2/derive.cabal- --- checkWild False = pure ()- checkWild True = when (csv < CabalSpecV1_6) $- parsecWarning PWTVersionWildcard $ unwords- [ "Wildcard syntax used."- , "To use version wildcards the package needs to specify at least 'cabal-version: >= 1.6'."- ]-- -- https://gitlab.haskell.org/ghc/ghc/issues/17752- isOpChar '<' = True- isOpChar '=' = True- isOpChar '>' = True- isOpChar '^' = True- isOpChar '-' = csv < CabalSpecV3_4- -- https://github.com/haskell/cabal/issues/6589- -- Unfortunately we have must not consume the dash,- -- as otherwise following parts may not be parsed.- --- -- i.e. we cannot fail here with good error.- isOpChar _ = False-- -- -none version range is available since 1.22- noVersion' =- if csv >= CabalSpecV1_22- then pure noVersion- else fail $ unwords- [ "-none version range used."- , "To use this syntax the package needs to specify at least 'cabal-version: 1.22'."- , "Alternatively, if broader compatibility is important then use"- , "<0 or other empty range."- ]-- -- ^>= is available since 2.0- majorBoundVersion' v =- if csv >= CabalSpecV2_0- then pure $ majorBoundVersion v- else fail $ unwords- [ "major bounded version syntax (caret, ^>=) used."- , "To use this syntax the package need to specify at least 'cabal-version: 2.0'."- , "Alternatively, if broader compatibility is important then use:"- , prettyShow $ eliminateMajorBoundSyntax $ majorBoundVersion v- ]- where- eliminateMajorBoundSyntax = hyloVersionRange embed projectVersionRange- embed (MajorBoundVersionF u) = intersectVersionRanges- (orLaterVersion u) (earlierVersion (majorUpperBound u))- embed vr = embedVersionRange vr-- -- version set notation (e.g. "== { 0.0.1.0, 0.0.2.0, 0.1.0.0 }")- verSet' op vs =- if csv >= CabalSpecV3_0- then pure $ foldr1 unionVersionRanges (fmap op vs)- else fail $ unwords- [ "version set syntax used."- , "To use this syntax the package needs to specify at least 'cabal-version: 3.0'."- , "Alternatively, if broader compatibility is important then use"- , "a series of single version constraints joined with the || operator:"- , prettyShow (foldr1 unionVersionRanges (fmap op vs))- ]-- verSet :: CabalParsing m => m (NonEmpty Version)- verSet = do- _ <- P.char '{'- P.spaces- vs <- P.sepByNonEmpty (verPlain <* P.spaces) (P.char ',' *> P.spaces)- _ <- P.char '}'- pure vs-- -- a plain version without tags or wildcards- verPlain :: CabalParsing m => m Version- verPlain = mkVersion <$> toList <$> P.sepByNonEmpty digitParser (P.char '.')-- -- either wildcard or normal version- verOrWild :: CabalParsing m => m (Bool, Version)- verOrWild = do- x <- digitParser- verLoop (DList.singleton x)-- -- trailing: wildcard (.y.*) or normal version (optional tags) (.y.z-tag)- verLoop :: CabalParsing m => DList.DList Int -> m (Bool, Version)- verLoop acc = verLoop' acc- <|> (tags *> pure (False, mkVersion (DList.toList acc)))-- verLoop' :: CabalParsing m => DList.DList Int -> m (Bool, Version)- verLoop' acc = do- _ <- P.char '.'- let digit = digitParser >>= verLoop . DList.snoc acc- let wild = (True, mkVersion (DList.toList acc)) <$ P.char '*'- digit <|> wild-- parens p = P.between- ((P.char '(' P.<?> "opening paren") >> P.spaces)- (P.char ')' >> P.spaces)- $ do- a <- p- P.spaces- return a-- tags :: CabalParsing m => m ()- tags = do- ts <- many $ P.char '-' *> some (P.satisfy isAlphaNum)- case ts of- [] -> pure ()- (_ : _) -> parsecWarning PWTVersionTag "version with tags"---------------------------------- Wildcard range utilities------- | Compute next greater major version to be used as upper bound------ Example: @0.4.1@ produces the version @0.5@ which then can be used--- to construct a range @>= 0.4.1 && < 0.5@------ @since 2.2-majorUpperBound :: Version -> Version-majorUpperBound = alterVersion $ \numbers -> case numbers of- [] -> [0,1] -- should not happen- [m1] -> [m1,1] -- e.g. version '1'- (m1:m2:_) -> [m1,m2+1]---- | @since 2.2-wildcardUpperBound :: Version -> Version-wildcardUpperBound = alterVersion $- \lowerBound -> case unsnoc lowerBound of- Nothing -> []- Just (xs, x) -> xs ++ [x + 1]
− src/Distribution/Utils/Base62.hs
@@ -1,22 +0,0 @@---- | Implementation of base-62 encoding, which we use when computing hashes--- for fully instantiated unit ids.-module Distribution.Utils.Base62 (hashToBase62) where--import GHC.Fingerprint ( Fingerprint(..), fingerprintString )-import Numeric ( showIntAtBase )-import Data.Char ( chr )---- | Hash a string using GHC's fingerprinting algorithm (a 128-bit--- MD5 hash) and then encode the resulting hash in base 62.-hashToBase62 :: String -> String-hashToBase62 s = showFingerprint $ fingerprintString s- where- showIntAtBase62 x = showIntAtBase 62 representBase62 x ""- representBase62 x- | x < 10 = chr (48 + x)- | x < 36 = chr (65 + x - 10)- | x < 62 = chr (97 + x - 36)- | otherwise = '@'- showFingerprint (Fingerprint a b) = showIntAtBase62 a ++ showIntAtBase62 b-
− src/Distribution/Utils/Generic.hs
@@ -1,576 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE BangPatterns #-}---------------------------------------------------------------------------------- |--- Module : Distribution.Simple.Utils--- Copyright : Isaac Jones, Simon Marlow 2003-2004--- License : BSD3--- portions Copyright (c) 2007, Galois Inc.------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ A large and somewhat miscellaneous collection of utility functions used--- throughout the rest of the Cabal lib and in other tools that use the Cabal--- lib like @cabal-install@. It has a very simple set of logging actions. It--- has low level functions for running programs, a bunch of wrappers for--- various directory and file functions that do extra logging.--module Distribution.Utils.Generic (- -- * reading and writing files safely- withFileContents,- writeFileAtomic,-- -- * Unicode-- -- ** Conversions- fromUTF8BS,- fromUTF8LBS,-- toUTF8BS,- toUTF8LBS,-- validateUTF8,-- -- ** File I/O- readUTF8File,- withUTF8FileContents,- writeUTF8File,-- -- ** BOM- ignoreBOM,-- -- ** Misc- normaliseLineEndings,-- -- * generic utils- dropWhileEndLE,- takeWhileEndLE,- equating,- comparing,- isInfixOf,- intercalate,- lowercase,- isAscii,- isAsciiAlpha,- isAsciiAlphaNum,- listUnion,- listUnionRight,- ordNub,- ordNubBy,- ordNubRight,- safeHead,- safeTail,- safeLast,- safeInit,- unintersperse,- wrapText,- wrapLine,- unfoldrM,- spanMaybe,- breakMaybe,- unsnoc,- unsnocNE,-- -- * Triples- fstOf3,- sndOf3,- trdOf3,-- -- * FilePath stuff- isAbsoluteOnAnyPlatform,- isRelativeOnAnyPlatform,- ) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Utils.String--import Data.Bits ((.&.), (.|.), shiftL)-import Data.List- ( isInfixOf )-import qualified Data.Set as Set-import qualified Data.ByteString as SBS-import qualified Data.ByteString.Lazy as LBS--import System.Directory- ( removeFile, renameFile )-import System.FilePath- ( (<.>), splitFileName )-import System.IO- ( withFile, withBinaryFile- , openBinaryTempFileWithDefaultPermissions- , IOMode(ReadMode), hGetContents, hClose )-import qualified Control.Exception as Exception---- -------------------------------------------------------------------------------- Helper functions---- | Wraps text to the default line width. Existing newlines are preserved.-wrapText :: String -> String-wrapText = unlines- . map (intercalate "\n"- . map unwords- . wrapLine 79- . words)- . lines---- | Wraps a list of words to a list of lines of words of a particular width.-wrapLine :: Int -> [String] -> [[String]]-wrapLine width = wrap 0 []- where wrap :: Int -> [String] -> [String] -> [[String]]- wrap 0 [] (w:ws)- | length w + 1 > width- = wrap (length w) [w] ws- wrap col line (w:ws)- | col + length w + 1 > width- = reverse line : wrap 0 [] (w:ws)- wrap col line (w:ws)- = let col' = col + length w + 1- in wrap col' (w:line) ws- wrap _ [] [] = []- wrap _ line [] = [reverse line]---------------------------------------- Safely reading and writing files---- | Gets the contents of a file, but guarantee that it gets closed.------ The file is read lazily but if it is not fully consumed by the action then--- the remaining input is truncated and the file is closed.----withFileContents :: FilePath -> (String -> IO a) -> IO a-withFileContents name action =- withFile name ReadMode- (\hnd -> hGetContents hnd >>= action)---- | Writes a file atomically.------ The file is either written successfully or an IO exception is raised and--- the original file is left unchanged.------ On windows it is not possible to delete a file that is open by a process.--- This case will give an IO exception but the atomic property is not affected.----writeFileAtomic :: FilePath -> LBS.ByteString -> IO ()-writeFileAtomic targetPath content = do- let (targetDir, targetFile) = splitFileName targetPath- Exception.bracketOnError- (openBinaryTempFileWithDefaultPermissions targetDir $ targetFile <.> "tmp")- (\(tmpPath, handle) -> hClose handle >> removeFile tmpPath)- (\(tmpPath, handle) -> do- LBS.hPut handle content- hClose handle- renameFile tmpPath targetPath)---- --------------------------------------------------------------- * Unicode stuff--- ---------------------------------------------------------------- | Decode 'String' from UTF8-encoded 'BS.ByteString'------ Invalid data in the UTF8 stream (this includes code-points @U+D800@--- through @U+DFFF@) will be decoded as the replacement character (@U+FFFD@).----fromUTF8BS :: SBS.ByteString -> String-fromUTF8BS = decodeStringUtf8 . SBS.unpack---- | Variant of 'fromUTF8BS' for lazy 'BS.ByteString's----fromUTF8LBS :: LBS.ByteString -> String-fromUTF8LBS = decodeStringUtf8 . LBS.unpack---- | Encode 'String' to UTF8-encoded 'SBS.ByteString'------ Code-points in the @U+D800@-@U+DFFF@ range will be encoded--- as the replacement character (i.e. @U+FFFD@).----toUTF8BS :: String -> SBS.ByteString-toUTF8BS = SBS.pack . encodeStringUtf8---- | Variant of 'toUTF8BS' for lazy 'BS.ByteString's----toUTF8LBS :: String -> LBS.ByteString-toUTF8LBS = LBS.pack . encodeStringUtf8---- | Check that strict 'ByteString' is valid UTF8. Returns 'Just offset' if it's not.-validateUTF8 :: SBS.ByteString -> Maybe Int-validateUTF8 = go 0 where- go off bs = case SBS.uncons bs of- Nothing -> Nothing- Just (c, bs')- | c <= 0x7F -> go (off + 1) bs'- | c <= 0xBF -> Just off- | c <= 0xDF -> twoBytes off c bs'- | c <= 0xEF -> moreBytes off 3 0x800 bs' (fromIntegral $ c .&. 0xF)- | c <= 0xF7 -> moreBytes off 4 0x10000 bs' (fromIntegral $ c .&. 0x7)- | c <= 0xFB -> moreBytes off 5 0x200000 bs' (fromIntegral $ c .&. 0x3)- | c <= 0xFD -> moreBytes off 6 0x4000000 bs' (fromIntegral $ c .&. 0x1)- | otherwise -> Just off-- twoBytes off c0 bs = case SBS.uncons bs of- Nothing -> Just off- Just (c1, bs')- | c1 .&. 0xC0 == 0x80 ->- if d >= (0x80 :: Int)- then go (off + 2) bs'- else Just off- | otherwise -> Just off- where- d = (fromIntegral (c0 .&. 0x1F) `shiftL` 6) .|. fromIntegral (c1 .&. 0x3F)-- moreBytes :: Int -> Int -> Int -> SBS.ByteString -> Int -> Maybe Int- moreBytes off 1 overlong cs' acc- | overlong <= acc, acc <= 0x10FFFF, acc < 0xD800 || 0xDFFF < acc- = go (off + 1) cs'-- | otherwise- = Just off-- moreBytes off byteCount overlong bs acc = case SBS.uncons bs of- Just (cn, bs') | cn .&. 0xC0 == 0x80 ->- moreBytes (off + 1) (byteCount-1) overlong bs' ((acc `shiftL` 6) .|. fromIntegral cn .&. 0x3F)- _ -> Just off- ---- | Ignore a Unicode byte order mark (BOM) at the beginning of the input----ignoreBOM :: String -> String-ignoreBOM ('\xFEFF':string) = string-ignoreBOM string = string---- | Reads a UTF8 encoded text file as a Unicode String------ Reads lazily using ordinary 'readFile'.----readUTF8File :: FilePath -> IO String-readUTF8File f = (ignoreBOM . fromUTF8LBS) <$> LBS.readFile f---- | Reads a UTF8 encoded text file as a Unicode String------ Same behaviour as 'withFileContents'.----withUTF8FileContents :: FilePath -> (String -> IO a) -> IO a-withUTF8FileContents name action =- withBinaryFile name ReadMode- (\hnd -> LBS.hGetContents hnd >>= action . ignoreBOM . fromUTF8LBS)---- | Writes a Unicode String as a UTF8 encoded text file.------ Uses 'writeFileAtomic', so provides the same guarantees.----writeUTF8File :: FilePath -> String -> IO ()-writeUTF8File path = writeFileAtomic path . toUTF8LBS---- | Fix different systems silly line ending conventions-normaliseLineEndings :: String -> String-normaliseLineEndings [] = []-normaliseLineEndings ('\r':'\n':s) = '\n' : normaliseLineEndings s -- windows-normaliseLineEndings ('\r':s) = '\n' : normaliseLineEndings s -- old OS X-normaliseLineEndings ( c :s) = c : normaliseLineEndings s---- --------------------------------------------------------------- * Common utils--- ---------------------------------------------------------------- | @dropWhileEndLE p@ is equivalent to @reverse . dropWhile p . reverse@, but--- quite a bit faster. The difference between "Data.List.dropWhileEnd" and this--- version is that the one in "Data.List" is strict in elements, but spine-lazy,--- while this one is spine-strict but lazy in elements. That's what @LE@ stands--- for - "lazy in elements".------ Example:------ >>> safeTail $ Data.List.dropWhileEnd (<3) [undefined, 5, 4, 3, 2, 1]--- *** Exception: Prelude.undefined--- ...------ >>> safeTail $ dropWhileEndLE (<3) [undefined, 5, 4, 3, 2, 1]--- [5,4,3]------ >>> take 3 $ Data.List.dropWhileEnd (<3) [5, 4, 3, 2, 1, undefined]--- [5,4,3]------ >>> take 3 $ dropWhileEndLE (<3) [5, 4, 3, 2, 1, undefined]--- *** Exception: Prelude.undefined--- ...----dropWhileEndLE :: (a -> Bool) -> [a] -> [a]-dropWhileEndLE p = foldr (\x r -> if null r && p x then [] else x:r) []---- | @takeWhileEndLE p@ is equivalent to @reverse . takeWhile p . reverse@, but--- is usually faster (as well as being easier to read).-takeWhileEndLE :: (a -> Bool) -> [a] -> [a]-takeWhileEndLE p = fst . foldr go ([], False)- where- go x (rest, done)- | not done && p x = (x:rest, False)- | otherwise = (rest, True)---- | Like 'Data.List.nub', but has @O(n log n)@ complexity instead of--- @O(n^2)@. Code for 'ordNub' and 'listUnion' taken from Niklas Hambüchen's--- <http://github.com/nh2/haskell-ordnub ordnub> package.-ordNub :: Ord a => [a] -> [a]-ordNub = ordNubBy id---- | Like 'ordNub' and 'Data.List.nubBy'. Selects a key for each element and--- takes the nub based on that key.-ordNubBy :: Ord b => (a -> b) -> [a] -> [a]-ordNubBy f l = go Set.empty l- where- go !_ [] = []- go !s (x:xs)- | y `Set.member` s = go s xs- | otherwise = let !s' = Set.insert y s- in x : go s' xs- where- y = f x---- | Like "Data.List.union", but has @O(n log n)@ complexity instead of--- @O(n^2)@.-listUnion :: (Ord a) => [a] -> [a] -> [a]-listUnion a b = a ++ ordNub (filter (`Set.notMember` aSet) b)- where- aSet = Set.fromList a---- | A right-biased version of 'ordNub'.------ Example:------ >>> ordNub [1,2,1] :: [Int]--- [1,2]------ >>> ordNubRight [1,2,1] :: [Int]--- [2,1]----ordNubRight :: (Ord a) => [a] -> [a]-ordNubRight = fst . foldr go ([], Set.empty)- where- go x p@(l, s) = if x `Set.member` s then p- else (x:l, Set.insert x s)---- | A right-biased version of 'listUnion'.------ Example:------ >>> listUnion [1,2,3,4,3] [2,1,1]--- [1,2,3,4,3]------ >>> listUnionRight [1,2,3,4,3] [2,1,1]--- [4,3,2,1,1]----listUnionRight :: (Ord a) => [a] -> [a] -> [a]-listUnionRight a b = ordNubRight (filter (`Set.notMember` bSet) a) ++ b- where- bSet = Set.fromList b---- | A total variant of 'head'.------ @since 3.2.0.0-safeHead :: [a] -> Maybe a-safeHead [] = Nothing-safeHead (x:_) = Just x---- | A total variant of 'tail'.------ @since 3.2.0.0-safeTail :: [a] -> [a]-safeTail [] = []-safeTail (_:xs) = xs---- | A total variant of 'last'.------ @since 3.2.0.0-safeLast :: [a] -> Maybe a-safeLast [] = Nothing-safeLast (x:xs) = Just (foldl (\_ a -> a) x xs)---- | A total variant of 'init'.------ @since 3.2.0.0-safeInit :: [a] -> [a]-safeInit [] = []-safeInit [_] = []-safeInit (x:xs) = x : safeInit xs--equating :: Eq a => (b -> a) -> b -> b -> Bool-equating p x y = p x == p y---- | Lower case string------ >>> lowercase "Foobar"--- "foobar"-lowercase :: String -> String-lowercase = map toLower---- | Ascii characters-isAscii :: Char -> Bool-isAscii c = fromEnum c < 0x80---- | Ascii letters.-isAsciiAlpha :: Char -> Bool-isAsciiAlpha c = ('a' <= c && c <= 'z')- || ('A' <= c && c <= 'Z')---- | Ascii letters and digits.------ >>> isAsciiAlphaNum 'a'--- True------ >>> isAsciiAlphaNum 'ä'--- False----isAsciiAlphaNum :: Char -> Bool-isAsciiAlphaNum c = isAscii c && isAlphaNum c--unintersperse :: Char -> String -> [String]-unintersperse mark = unfoldr unintersperse1 where- unintersperse1 str- | null str = Nothing- | otherwise =- let (this, rest) = break (== mark) str in- Just (this, safeTail rest)---- | Like 'break', but with 'Maybe' predicate------ >>> breakMaybe (readMaybe :: String -> Maybe Int) ["foo", "bar", "1", "2", "quu"]--- (["foo","bar"],Just (1,["2","quu"]))------ >>> breakMaybe (readMaybe :: String -> Maybe Int) ["foo", "bar"]--- (["foo","bar"],Nothing)------ @since 2.2----breakMaybe :: (a -> Maybe b) -> [a] -> ([a], Maybe (b, [a]))-breakMaybe f = go id where- go !acc [] = (acc [], Nothing)- go !acc (x:xs) = case f x of- Nothing -> go (acc . (x:)) xs- Just b -> (acc [], Just (b, xs))---- | Like 'span' but with 'Maybe' predicate------ >>> spanMaybe listToMaybe [[1,2],[3],[],[4,5],[6,7]]--- ([1,3],[[],[4,5],[6,7]])------ >>> spanMaybe (readMaybe :: String -> Maybe Int) ["1", "2", "foo"]--- ([1,2],["foo"])------ @since 2.2----spanMaybe :: (a -> Maybe b) -> [a] -> ([b],[a])-spanMaybe _ xs@[] = ([], xs)-spanMaybe p xs@(x:xs') = case p x of- Just y -> let (ys, zs) = spanMaybe p xs' in (y : ys, zs)- Nothing -> ([], xs)---- | 'unfoldr' with monadic action.------ >>> take 5 $ unfoldrM (\b r -> Just (r + b, b + 1)) (1 :: Int) 2--- [3,4,5,6,7]------ @since 2.2----unfoldrM :: Monad m => (b -> m (Maybe (a, b))) -> b -> m [a]-unfoldrM f = go where- go b = do- m <- f b- case m of- Nothing -> return []- Just (a, b') -> liftM (a :) (go b')---- | The opposite of 'snoc', which is the reverse of 'cons'------ Example:------ >>> unsnoc [1, 2, 3]--- Just ([1,2],3)------ >>> unsnoc []--- Nothing------ @since 3.2.0.0----unsnoc :: [a] -> Maybe ([a], a)-unsnoc [] = Nothing-unsnoc (x:xs) = Just (unsnocNE (x :| xs))---- | Like 'unsnoc', but for 'NonEmpty' so without the 'Maybe'------ Example:------ >>> unsnocNE (1 :| [2, 3])--- ([1,2],3)------ >>> unsnocNE (1 :| [])--- ([],1)------ @since 3.2.0.0----unsnocNE :: NonEmpty a -> ([a], a)-unsnocNE (x:|xs) = go x xs where- go y [] = ([], y)- go y (z:zs) = let ~(ws, w) = go z zs in (y : ws, w)------------------------------------------------------------------------------------ Triples------------------------------------------------------------------------------------ | @since 3.4.0.0-fstOf3 :: (a,b,c) -> a-fstOf3 (a,_,_) = a---- | @since 3.4.0.0-sndOf3 :: (a,b,c) -> b-sndOf3 (_,b,_) = b---- | @since 3.4.0.0-trdOf3 :: (a,b,c) -> c-trdOf3 (_,_,c) = c---- --------------------------------------------------------------- * FilePath stuff--- ---------------------------------------------------------------- | 'isAbsoluteOnAnyPlatform' and 'isRelativeOnAnyPlatform' are like--- 'System.FilePath.isAbsolute' and 'System.FilePath.isRelative' but have--- platform independent heuristics.--- The System.FilePath exists in two versions, Windows and Posix. The two--- versions don't agree on what is a relative path and we don't know if we're--- given Windows or Posix paths.--- This results in false positives when running on Posix and inspecting--- Windows paths, like the hackage server does.--- System.FilePath.Posix.isAbsolute \"C:\\hello\" == False--- System.FilePath.Windows.isAbsolute \"/hello\" == False--- This means that we would treat paths that start with \"/\" to be absolute.--- On Posix they are indeed absolute, while on Windows they are not.------ The portable versions should be used when we might deal with paths that--- are from another OS than the host OS. For example, the Hackage Server--- deals with both Windows and Posix paths while performing the--- PackageDescription checks. In contrast, when we run 'cabal configure' we--- do expect the paths to be correct for our OS and we should not have to use--- the platform independent heuristics.-isAbsoluteOnAnyPlatform :: FilePath -> Bool--- C:\\directory-isAbsoluteOnAnyPlatform (drive:':':'\\':_) = isAlpha drive-isAbsoluteOnAnyPlatform (drive:':':'/':_) = isAlpha drive--- UNC-isAbsoluteOnAnyPlatform ('\\':'\\':_) = True--- Posix root-isAbsoluteOnAnyPlatform ('/':_) = True-isAbsoluteOnAnyPlatform _ = False---- | @isRelativeOnAnyPlatform = not . 'isAbsoluteOnAnyPlatform'@-isRelativeOnAnyPlatform :: FilePath -> Bool-isRelativeOnAnyPlatform = not . isAbsoluteOnAnyPlatform---- $setup--- >>> import Data.Maybe--- >>> import Text.Read
src/Distribution/Utils/IOData.hs view
@@ -1,81 +1,86 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TypeOperators #-}+ -- | @since 2.2.0 module Distribution.Utils.IOData- ( -- * 'IOData' & 'IODataMode' type- IOData (..)- , IODataMode (..)- , KnownIODataMode (..)- , withIOData- , null- , hPutContents- ) where+ ( -- * 'IOData' & 'IODataMode' type+ IOData (..)+ , IODataMode (..)+ , KnownIODataMode (..)+ , withIOData+ , null+ , hPutContents+ ) where import qualified Data.ByteString.Lazy as LBS-import Distribution.Compat.Prelude hiding (null)-import qualified Prelude+import Distribution.Compat.Prelude hiding (null) import qualified System.IO+import qualified Prelude -- | Represents either textual or binary data passed via I/O functions -- which support binary/text mode -- -- @since 2.2 data IOData- = IODataText String- -- ^ How Text gets encoded is usually locale-dependent.- | IODataBinary LBS.ByteString- -- ^ Raw binary which gets read/written in binary mode.+ = -- | How Text gets encoded is usually locale-dependent.+ IODataText String+ | -- | Raw binary which gets read/written in binary mode.+ IODataBinary LBS.ByteString +-- | Applies a function polymorphic over 'IODataMode' to an 'IOData' value. withIOData :: IOData -> (forall mode. IODataMode mode -> mode -> r) -> r-withIOData (IODataText str) k = k IODataModeText str+withIOData (IODataText str) k = k IODataModeText str withIOData (IODataBinary lbs) k = k IODataModeBinary lbs -- | Test whether 'IOData' is empty null :: IOData -> Bool-null (IODataText s) = Prelude.null s+null (IODataText s) = Prelude.null s null (IODataBinary b) = LBS.null b instance NFData IOData where- rnf (IODataText s) = rnf s- rnf (IODataBinary lbs) = rnf lbs+ rnf (IODataText s) = rnf s+ rnf (IODataBinary lbs) = rnf lbs -- | @since 2.2 class NFData mode => KnownIODataMode mode where- -- | 'IOData' Wrapper for 'System.IO.hGetContents'- --- -- __Note__: This operation uses lazy I/O. Use 'NFData' to force all- -- data to be read and consequently the internal file handle to be- -- closed.- --- hGetIODataContents :: System.IO.Handle -> Prelude.IO mode+ -- | 'IOData' Wrapper for 'System.IO.hGetContents'+ --+ -- __Note__: This operation uses lazy I/O. Use 'NFData' to force all+ -- data to be read and consequently the internal file handle to be+ -- closed.+ hGetIODataContents :: System.IO.Handle -> Prelude.IO mode - toIOData :: mode -> IOData- iodataMode :: IODataMode mode+ toIOData :: mode -> IOData+ iodataMode :: IODataMode mode --- | @since 3.2+-- | Phantom-typed GADT representation of the mode of 'IOData', containing no+-- other data.+--+-- @since 3.2 data IODataMode mode where- IODataModeText :: IODataMode String- IODataModeBinary :: IODataMode LBS.ByteString+ IODataModeText :: IODataMode String+ IODataModeBinary :: IODataMode LBS.ByteString instance a ~ Char => KnownIODataMode [a] where- hGetIODataContents h = do- System.IO.hSetBinaryMode h False- System.IO.hGetContents h+ hGetIODataContents h = do+ System.IO.hSetBinaryMode h False+ System.IO.hGetContents h - toIOData = IODataText- iodataMode = IODataModeText+ toIOData = IODataText+ iodataMode = IODataModeText instance KnownIODataMode LBS.ByteString where- hGetIODataContents h = do- System.IO.hSetBinaryMode h True- LBS.hGetContents h+ hGetIODataContents h = do+ System.IO.hSetBinaryMode h True+ LBS.hGetContents h - toIOData = IODataBinary- iodataMode = IODataModeBinary+ toIOData = IODataBinary+ iodataMode = IODataModeBinary -- | 'IOData' Wrapper for 'System.IO.hPutStr' and 'System.IO.hClose' ----- This is the dual operation ot 'hGetIODataContents',+-- This is the dual operation to 'hGetIODataContents', -- and consequently the handle is closed with `hClose`. -- -- /Note:/ this performs lazy-IO.@@ -83,10 +88,10 @@ -- @since 2.2 hPutContents :: System.IO.Handle -> IOData -> Prelude.IO () hPutContents h (IODataText c) = do- System.IO.hSetBinaryMode h False- System.IO.hPutStr h c- System.IO.hClose h+ System.IO.hSetBinaryMode h False+ System.IO.hPutStr h c+ System.IO.hClose h hPutContents h (IODataBinary c) = do- System.IO.hSetBinaryMode h True- LBS.hPutStr h c- System.IO.hClose h+ System.IO.hSetBinaryMode h True+ LBS.hPutStr h c+ System.IO.hClose h
+ src/Distribution/Utils/Json.hs view
@@ -0,0 +1,63 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-}++-- | Extremely simple JSON helper. Don't do anything too fancy with this!+module Distribution.Utils.Json+ ( Json (..)+ , (.=)+ , renderJson+ ) where++import Data.ByteString.Builder+ ( Builder+ , intDec+ , stringUtf8+ , toLazyByteString+ )+import qualified Data.ByteString.Lazy as LBS+import Distribution.Compat.Prelude++data Json+ = JsonArray [Json]+ | JsonBool !Bool+ | JsonNull+ | JsonNumber !Int -- No support for Floats, Doubles just yet+ | JsonObject [(String, Json)]+ | JsonString !String+ deriving (Show)++-- | Convert a 'Json' into a 'ByteString'+renderJson :: Json -> LBS.ByteString+renderJson json = toLazyByteString (go json)+ where+ go (JsonArray objs) =+ surround "[" "]" $ mconcat $ intersperse "," $ map go objs+ go (JsonBool True) = stringUtf8 "true"+ go (JsonBool False) = stringUtf8 "false"+ go JsonNull = stringUtf8 "null"+ go (JsonNumber n) = intDec n+ go (JsonObject attrs) =+ surround "{" "}" $ mconcat $ intersperse "," $ map render attrs+ where+ render (k, v) = surround "\"" "\"" (stringUtf8 (escape k)) <> ":" <> go v+ go (JsonString s) = surround "\"" "\"" $ stringUtf8 (escape s)++surround :: Builder -> Builder -> Builder -> Builder+surround begin end middle = mconcat [begin, middle, end]++escape :: String -> String+escape ('\"' : xs) = "\\\"" <> escape xs+escape ('\\' : xs) = "\\\\" <> escape xs+escape ('\b' : xs) = "\\b" <> escape xs+escape ('\f' : xs) = "\\f" <> escape xs+escape ('\n' : xs) = "\\n" <> escape xs+escape ('\r' : xs) = "\\r" <> escape xs+escape ('\t' : xs) = "\\t" <> escape xs+escape (x : xs) = x : escape xs+escape [] = mempty++-- | A shorthand for building up 'JsonObject's+-- >>> JsonObject [ "a" .= JsonNumber 42, "b" .= JsonBool True ]+-- JsonObject [("a",JsonNumber 42),("b",JsonBool True)]+(.=) :: String -> Json -> (String, Json)+k .= v = (k, v)
src/Distribution/Utils/LogProgress.hs view
@@ -1,83 +1,84 @@-{-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE Rank2Types #-} {-# LANGUAGE FlexibleContexts #-}-module Distribution.Utils.LogProgress (- LogProgress,- runLogProgress,- warnProgress,- infoProgress,- dieProgress,- addProgressCtx,-) where+{-# LANGUAGE Rank2Types #-} -import Prelude ()+module Distribution.Utils.LogProgress+ ( LogProgress+ , runLogProgress+ , warnProgress+ , infoProgress+ , dieProgress+ , addProgressCtx+ ) where+ import Distribution.Compat.Prelude+import Prelude () +import Distribution.Simple.Utils import Distribution.Utils.Progress import Distribution.Verbosity-import Distribution.Simple.Utils import Text.PrettyPrint type CtxMsg = Doc type LogMsg = Doc type ErrMsg = Doc -data LogEnv = LogEnv {- le_verbosity :: Verbosity,- le_context :: [CtxMsg]- }+data LogEnv = LogEnv+ { le_verbosity :: Verbosity+ , le_context :: [CtxMsg]+ } -- | The 'Progress' monad with specialized logging and -- error messages.-newtype LogProgress a = LogProgress { unLogProgress :: LogEnv -> Progress LogMsg ErrMsg a }+newtype LogProgress a = LogProgress {unLogProgress :: LogEnv -> Progress LogMsg ErrMsg a} instance Functor LogProgress where- fmap f (LogProgress m) = LogProgress (fmap (fmap f) m)+ fmap f (LogProgress m) = LogProgress (fmap (fmap f) m) instance Applicative LogProgress where- pure x = LogProgress (pure (pure x))- LogProgress f <*> LogProgress x = LogProgress $ \r -> f r `ap` x r+ pure x = LogProgress (pure (pure x))+ LogProgress f <*> LogProgress x = LogProgress $ \r -> f r `ap` x r instance Monad LogProgress where- return = pure- LogProgress m >>= f = LogProgress $ \r -> m r >>= \x -> unLogProgress (f x) r+ return = pure+ LogProgress m >>= f = LogProgress $ \r -> m r >>= \x -> unLogProgress (f x) r -- | Run 'LogProgress', outputting traces according to 'Verbosity', -- 'die' if there is an error. runLogProgress :: Verbosity -> LogProgress a -> IO a runLogProgress verbosity (LogProgress m) =- foldProgress step_fn fail_fn return (m env)+ foldProgress step_fn fail_fn return (m env) where- env = LogEnv {- le_verbosity = verbosity,- le_context = []- }+ env =+ LogEnv+ { le_verbosity = verbosity+ , le_context = []+ } step_fn :: LogMsg -> IO a -> IO a step_fn doc go = do- putStrLn (render doc)- go+ putStrLn (render doc)+ go fail_fn :: Doc -> IO a fail_fn doc = do- dieNoWrap verbosity (render doc)+ dieNoWrap verbosity (render doc) -- | Output a warning trace message in 'LogProgress'. warnProgress :: Doc -> LogProgress () warnProgress s = LogProgress $ \env ->- when (le_verbosity env >= normal) $- stepProgress $- hang (text "Warning:") 4 (formatMsg (le_context env) s)+ when (le_verbosity env >= normal) $+ stepProgress $+ hang (text "Warning:") 4 (formatMsg (le_context env) s) -- | Output an informational trace message in 'LogProgress'. infoProgress :: Doc -> LogProgress () infoProgress s = LogProgress $ \env ->- when (le_verbosity env >= verbose) $- stepProgress s+ when (le_verbosity env >= verbose) $+ stepProgress s -- | Fail the computation with an error message. dieProgress :: Doc -> LogProgress a dieProgress s = LogProgress $ \env ->- failProgress $- hang (text "Error:") 4 (formatMsg (le_context env) s)+ failProgress $+ hang (text "Error:") 4 (formatMsg (le_context env) s) -- | Format a message with context. (Something simple for now.) formatMsg :: [CtxMsg] -> Doc -> Doc@@ -86,4 +87,4 @@ -- | Add a message to the error/warning context. addProgressCtx :: CtxMsg -> LogProgress a -> LogProgress a addProgressCtx s (LogProgress m) = LogProgress $ \env ->- m env { le_context = s : le_context env }+ m env{le_context = s : le_context env}
− src/Distribution/Utils/MD5.hs
@@ -1,79 +0,0 @@-module Distribution.Utils.MD5 (- MD5,- showMD5,- md5,- -- * Helpers- md5FromInteger,- -- * Binary- binaryPutMD5,- binaryGetMD5,- ) where--import Data.Binary (Get, Put)-import Data.Binary.Get (getWord64le)-import Data.Binary.Put (putWord64le)-import Data.Bits (complement, shiftR, (.&.))-import Foreign.Ptr (castPtr)-import GHC.Fingerprint (Fingerprint (..), fingerprintData)-import Numeric (showHex)-import System.IO.Unsafe (unsafeDupablePerformIO)--import qualified Data.ByteString as BS-import qualified Data.ByteString.Unsafe as BS--type MD5 = Fingerprint---- | Show 'MD5' in human readable form------ >>> showMD5 (Fingerprint 123 456)--- "000000000000007b00000000000001c8"------ >>> showMD5 $ md5 $ BS.pack [0..127]--- "37eff01866ba3f538421b30b7cbefcac"------ @since 3.2.0.0-showMD5 :: MD5 -> String-showMD5 (Fingerprint a b) = pad a' ++ pad b' where- a' = showHex a ""- b' = showHex b ""- pad s = replicate (16 - length s) '0' ++ s---- | @since 3.2.0.0-md5 :: BS.ByteString -> MD5-md5 bs = unsafeDupablePerformIO $ BS.unsafeUseAsCStringLen bs $ \(ptr, len) ->- fingerprintData (castPtr ptr) len---- | @since 3.2.0.0-binaryPutMD5 :: MD5 -> Put-binaryPutMD5 (Fingerprint a b) = do- putWord64le a- putWord64le b---- | @since 3.2.0.0-binaryGetMD5 :: Get MD5-binaryGetMD5 = do- a <- getWord64le- b <- getWord64le- return (Fingerprint a b)---- |------ >>> showMD5 $ md5FromInteger 0x37eff01866ba3f538421b30b7cbefcac--- "37eff01866ba3f538421b30b7cbefcac"------ Note: the input is truncated:------ >>> showMD5 $ md5FromInteger 0x1230000037eff01866ba3f538421b30b7cbefcac--- "37eff01866ba3f538421b30b7cbefcac"------ Yet, negative numbers are not a problem...------ >>> showMD5 $ md5FromInteger (-1)--- "ffffffffffffffffffffffffffffffff"------ @since 3.4.0.0-md5FromInteger :: Integer -> MD5-md5FromInteger i = Fingerprint hi lo where- mask = complement 0- lo = mask .&. fromInteger i- hi = mask .&. fromInteger (i `shiftR` 64)
src/Distribution/Utils/MapAccum.hs view
@@ -1,34 +1,26 @@-{-# LANGUAGE CPP #-} module Distribution.Utils.MapAccum (mapAccumM) where import Distribution.Compat.Prelude import Prelude () -- Like StateT but with return tuple swapped-newtype StateM s m a = StateM { runStateM :: s -> m (s, a) }+newtype StateM s m a = StateM {runStateM :: s -> m (s, a)} instance Functor m => Functor (StateM s m) where- fmap f (StateM x) = StateM $ \s -> fmap (\(s', a) -> (s', f a)) (x s)+ fmap f (StateM x) = StateM $ \s -> fmap (\(s', a) -> (s', f a)) (x s) -instance-#if __GLASGOW_HASKELL__ < 709- (Functor m, Monad m)-#else- Monad m-#endif- => Applicative (StateM s m) where- pure x = StateM $ \s -> return (s, x)- StateM f <*> StateM x = StateM $ \s -> do (s', f') <- f s- (s'', x') <- x s'- return (s'', f' x')+instance Monad m => Applicative (StateM s m) where+ pure x = StateM $ \s -> return (s, x)+ StateM f <*> StateM x = StateM $ \s -> do+ (s', f') <- f s+ (s'', x') <- x s'+ return (s'', f' x') -- | Monadic variant of 'mapAccumL'.-mapAccumM ::-#if __GLASGOW_HASKELL__ < 709- (Functor m, Monad m, Traversable t)-#else- (Monad m, Traversable t)-#endif- => (a -> b -> m (a, c)) -> a -> t b -> m (a, t c)+mapAccumM+ :: (Monad m, Traversable t)+ => (a -> b -> m (a, c))+ -> a+ -> t b+ -> m (a, t c) mapAccumM f s t = runStateM (traverse (\x -> StateM (\s' -> f s' x)) t) s-
src/Distribution/Utils/NubList.hs view
@@ -1,17 +1,16 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE ScopedTypeVariables #-}-module Distribution.Utils.NubList- ( NubList -- opaque- , toNubList -- smart construtor- , fromNubList- , overNubList - , NubListR- , toNubListR- , fromNubListR- , overNubListR- ) where+module Distribution.Utils.NubList+ ( NubList -- opaque+ , toNubList -- smart constructor+ , fromNubList+ , overNubList+ , NubListR+ , toNubListR+ , fromNubListR+ , overNubListR+ ) where import Distribution.Compat.Prelude import Prelude ()@@ -21,9 +20,8 @@ import qualified Text.Read as R -- | NubList : A de-duplicated list that maintains the original order.-newtype NubList a =- NubList { fromNubList :: [a] }- deriving (Eq, Generic, Typeable)+newtype NubList a = NubList {fromNubList :: [a]}+ deriving (Eq, Generic) -- NubList assumes that nub retains the list order while removing duplicate -- elements (keeping the first occurrence). Documentation for "Data.List.nub"@@ -39,7 +37,7 @@ overNubList f (NubList list) = toNubList . f $ list -- | Monoid operations on NubLists.--- For a valid Monoid instance we need to satistfy the required monoid laws;+-- For a valid Monoid instance we need to satisfy the required monoid laws; -- identity, associativity and closure. -- -- Identity : by inspection:@@ -51,30 +49,29 @@ -- -- Closure : appending two lists of type a and removing duplicates obviously -- does not change the type.- instance Ord a => Monoid (NubList a) where- mempty = NubList []- mappend = (<>)+ mempty = NubList []+ mappend = (<>) instance Ord a => Semigroup (NubList a) where- (NubList xs) <> (NubList ys) = NubList $ xs `listUnion` ys+ (NubList xs) <> (NubList ys) = NubList $ xs `listUnion` ys instance Show a => Show (NubList a) where- show (NubList list) = show list+ show (NubList list) = show list instance (Ord a, Read a) => Read (NubList a) where- readPrec = readNubList toNubList+ readPrec = readNubList toNubList -- | Helper used by NubList/NubListR's Read instances.-readNubList :: (Read a) => ([a] -> l a) -> R.ReadPrec (l a)+readNubList :: Read a => ([a] -> l a) -> R.ReadPrec (l a) readNubList listToL = R.parens . R.prec 10 $ fmap listToL R.readPrec -- | Binary instance for 'NubList a' is the same as for '[a]'. For 'put', we -- just pull off constructor and put the list. For 'get', we get the list and -- make a 'NubList' out of it using 'toNubList'. instance (Ord a, Binary a) => Binary (NubList a) where- put (NubList l) = put l- get = fmap toNubList get+ put (NubList l) = put l+ get = fmap toNubList get instance Structured a => Structured (NubList a) @@ -82,9 +79,8 @@ -- ["-XNoFoo", "-XFoo", "-XNoFoo"]@ will result in @["-XFoo", "-XNoFoo"]@, -- unlike the normal 'NubList', which is left-biased. Built on top of -- 'ordNubRight' and 'listUnionRight'.-newtype NubListR a =- NubListR { fromNubListR :: [a] }- deriving Eq+newtype NubListR a = NubListR {fromNubListR :: [a]}+ deriving (Eq) -- | Smart constructor for the NubListR type. toNubListR :: Ord a => [a] -> NubListR a@@ -105,4 +101,4 @@ show (NubListR list) = show list instance (Ord a, Read a) => Read (NubListR a) where- readPrec = readNubList toNubListR+ readPrec = readNubList toNubListR
− src/Distribution/Utils/Path.hs
@@ -1,103 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE StandaloneDeriving #-}-module Distribution.Utils.Path (- -- * Symbolic path- SymbolicPath,- getSymbolicPath,- sameDirectory,- unsafeMakeSymbolicPath,- -- * Path ends- PackageDir,- SourceDir,- LicenseFile,- IsDir,-) where--import Prelude ()-import Distribution.Compat.Prelude--import Distribution.Parsec-import Distribution.Pretty-import Distribution.Utils.Generic (isAbsoluteOnAnyPlatform)--import qualified Distribution.Compat.CharParsing as P--- import qualified Text.PrettyPrint as Disp------------------------------------------------------------------------------------ * SymbolicPath------------------------------------------------------------------------------------ | Symbolic paths.------ These paths are system independent and relative.--- They are *symbolic* which means we cannot perform any 'IO'--- until we interpret them.----newtype SymbolicPath from to = SymbolicPath FilePath- deriving (Generic, Show, Read, Eq, Ord, Typeable, Data)--instance Binary (SymbolicPath from to)-instance (Typeable from, Typeable to) => Structured (SymbolicPath from to)-instance NFData (SymbolicPath from to) where rnf = genericRnf---- | Extract underlying 'FilePath'.------ Avoid using this in new code.----getSymbolicPath :: SymbolicPath from to -> FilePath-getSymbolicPath (SymbolicPath p) = p--sameDirectory :: (IsDir from, IsDir to) => SymbolicPath from to-sameDirectory = SymbolicPath "."---- | Make 'SymbolicPath' without performing any checks.-unsafeMakeSymbolicPath :: FilePath -> SymbolicPath from to-unsafeMakeSymbolicPath = SymbolicPath------------------------------------------------------------------------------------ ** Parsing and pretty printing----------------------------------------------------------------------------------instance Parsec (SymbolicPath from to) where- parsec = do- token <- parsecToken- if null token then P.unexpected "empty FilePath"- else if isAbsoluteOnAnyPlatform token then P.unexpected "absolute FilePath"- else return (SymbolicPath token) -- TODO: normalise--instance Pretty (SymbolicPath from to) where- pretty = showFilePath . getSymbolicPath------------------------------------------------------------------------------------ * Composition------------------------------------------------------------------------------------ TODO--- infixr 5 <//>------ -- | Path composition--- ----- -- We don't reuse @</>@ name to not clash with "System.FilePath".--- ----- (<//>) :: path a b -> path b c -> path a c------------------------------------------------------------------------------------ * Path ends------------------------------------------------------------------------------------ | Class telling that index is for directories.-class IsDir dir--data PackageDir deriving (Typeable)-data SourceDir deriving (Typeable)--data LicenseFile deriving (Typeable)---- These instances needs to be derived standalone at least on GHC-7.6-deriving instance Data PackageDir-deriving instance Data SourceDir-deriving instance Data LicenseFile--instance IsDir PackageDir-instance IsDir SourceDir
src/Distribution/Utils/Progress.hs view
@@ -1,22 +1,21 @@-{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveFunctor #-}+ -- Note: This module was copied from cabal-install. -- | A progress monad, which we use to report failure and logging from -- otherwise pure code. module Distribution.Utils.Progress- ( Progress- , stepProgress- , failProgress- , foldProgress- ) where+ ( Progress+ , stepProgress+ , failProgress+ , foldProgress+ ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () import qualified Data.Monoid as Mon - -- | A type to represent the unfolding of an expensive long running -- calculation that may fail (or maybe not expensive, but complicated!) -- We may get intermediate steps before the final@@ -25,14 +24,13 @@ -- TODO: Apply Codensity to avoid left-associativity problem. -- See http://comonad.com/reader/2011/free-monads-for-less/ and -- http://blog.ezyang.com/2012/01/problem-set-the-codensity-transformation/----data Progress step fail done = Step step (Progress step fail done)- | Fail fail- | Done done+data Progress step fail done+ = Step step (Progress step fail done)+ | Fail fail+ | Done done deriving (Functor) -- | Emit a step and then continue.--- stepProgress :: step -> Progress step fail () stepProgress step = Step step (Done ()) @@ -46,22 +44,26 @@ -- Eg to convert into a simple 'Either' result use: -- -- > foldProgress (flip const) Left Right----foldProgress :: (step -> a -> a) -> (fail -> a) -> (done -> a)- -> Progress step fail done -> a+foldProgress+ :: (step -> a -> a)+ -> (fail -> a)+ -> (done -> a)+ -> Progress step fail done+ -> a foldProgress step err done = fold- where fold (Step s p) = step s (fold p)- fold (Fail f) = err f- fold (Done r) = done r+ where+ fold (Step s p) = step s (fold p)+ fold (Fail f) = err f+ fold (Done r) = done r instance Monad (Progress step fail) where- return = pure- p >>= f = foldProgress Step Fail f p+ return = pure+ p >>= f = foldProgress Step Fail f p instance Applicative (Progress step fail) where- pure a = Done a+ pure a = Done a p <*> x = foldProgress Step Fail (flip fmap x) p instance Monoid fail => Alternative (Progress step fail) where- empty = Fail Mon.mempty+ empty = Fail Mon.mempty p <|> q = foldProgress Step (const q) Done p
− src/Distribution/Utils/ShortText.hs
@@ -1,159 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}---- | Compact representation of short 'Strings'------ This module is designed to be import qualifeid------ @--- import Distribution.Utils.ShortText (ShortText)--- import qualified Distribution.Utils.ShortText as ShortText--- @-module Distribution.Utils.ShortText- ( -- * 'ShortText' type- ShortText- , toShortText- , fromShortText- , unsafeFromUTF8BS-- -- * Operations- , null- , length-- -- * internal utilities- , decodeStringUtf8- , encodeStringUtf8- ) where--import Distribution.Compat.Prelude hiding (length, null)-import Prelude ()--import Distribution.Utils.String (decodeStringUtf8, encodeStringUtf8)-import Distribution.Utils.Structured (Structured (..), nominalStructure)--#if defined(MIN_VERSION_bytestring)-# if MIN_VERSION_bytestring(0,10,4)-# define HAVE_SHORTBYTESTRING 1-# endif-#endif---- Hack for GHC bootstrapping------ Currently (as of GHC 8.1), GHC bootstraps Cabal by building--- binary and Cabal in one giant ghc --make command. This--- means no MIN_VERSION_binary macro is available.------ We could try to cleverly figure something out in this case,--- but there is a better plan: just use the unoptimized version--- of the Binary instance. We're not going to use it for anything--- real in any case.------ WARNING: Don't use MIN_VERSION_binary to smooth over a BC-break!----#ifndef MIN_VERSION_binary-#define MIN_VERSION_binary(x, y, z) 0-#endif--import qualified Data.ByteString as BS-import qualified Data.List as List--#if HAVE_SHORTBYTESTRING-import qualified Data.ByteString.Short as BS.Short-#else-import Distribution.Utils.Generic (fromUTF8BS)-#endif---- | Construct 'ShortText' from 'String'-toShortText :: String -> ShortText---- | Convert 'ShortText' to 'String'-fromShortText :: ShortText -> String---- | Convert from UTF-8 encoded strict 'ByteString'.------ @since 3.2.0.0-unsafeFromUTF8BS :: BS.ByteString -> ShortText---- | Text whether 'ShortText' is empty.------ @since 3.2.0.0-null :: ShortText -> Bool---- | Compact representation of short 'Strings'------ The data is stored internally as UTF8 in an--- 'BS.Short.ShortByteString' when compiled against @bytestring >=--- 0.10.4@, and otherwise the fallback is to use plain old non-compat--- '[Char]'.------ Note: This type is for internal uses (such as e.g. 'PackageName')--- and shall not be exposed in Cabal's API------ @since 2.0.0.2-#if HAVE_SHORTBYTESTRING-newtype ShortText = ST { unST :: BS.Short.ShortByteString }- deriving (Eq,Ord,Generic,Data,Typeable)--# if MIN_VERSION_binary(0,8,1)-instance Binary ShortText where- put = put . unST- get = fmap ST get-# else-instance Binary ShortText where- put = put . BS.Short.fromShort . unST- get = fmap (ST . BS.Short.toShort) get-# endif---toShortText = ST . BS.Short.pack . encodeStringUtf8--fromShortText = decodeStringUtf8 . BS.Short.unpack . unST--unsafeFromUTF8BS = ST . BS.Short.toShort--null = BS.Short.null . unST-#else-newtype ShortText = ST { unST :: String }- deriving (Eq,Ord,Generic,Data,Typeable)--instance Binary ShortText where- put = put . encodeStringUtf8 . unST- get = fmap (ST . decodeStringUtf8) get--toShortText = ST--fromShortText = unST--unsafeFromUTF8BS = ST . fromUTF8BS--null = List.null . unST-#endif--instance Structured ShortText where structure = nominalStructure--instance NFData ShortText where- rnf = rnf . unST--instance Show ShortText where- show = show . fromShortText--instance Read ShortText where- readsPrec p = map (first toShortText) . readsPrec p--instance Semigroup ShortText where- ST a <> ST b = ST (mappend a b)--instance Monoid ShortText where- mempty = ST mempty- mappend = (<>)--instance IsString ShortText where- fromString = toShortText---- | /O(n)/. Length in characters. /Slow/ as converts to string.------ @since 3.2.0.0-length :: ShortText -> Int-length = List.length . fromShortText--- Note: avoid using it, we use it @cabal check@ implementation, where it's ok.
− src/Distribution/Utils/String.hs
@@ -1,93 +0,0 @@-module Distribution.Utils.String- ( -- * Encode to/from UTF8- decodeStringUtf8- , encodeStringUtf8- ) where--import Data.Word-import Data.Bits-import Data.Char (chr,ord)---- | Decode 'String' from UTF8-encoded octets.------ Invalid data in the UTF8 stream (this includes code-points @U+D800@--- through @U+DFFF@) will be decoded as the replacement character (@U+FFFD@).------ See also 'encodeStringUtf8'-decodeStringUtf8 :: [Word8] -> String-decodeStringUtf8 = go- where- go :: [Word8] -> String- go [] = []- go (c : cs)- | c <= 0x7F = chr (fromIntegral c) : go cs- | c <= 0xBF = replacementChar : go cs- | c <= 0xDF = twoBytes c cs- | c <= 0xEF = moreBytes 3 0x800 cs (fromIntegral $ c .&. 0xF)- | c <= 0xF7 = moreBytes 4 0x10000 cs (fromIntegral $ c .&. 0x7)- | c <= 0xFB = moreBytes 5 0x200000 cs (fromIntegral $ c .&. 0x3)- | c <= 0xFD = moreBytes 6 0x4000000 cs (fromIntegral $ c .&. 0x1)- | otherwise = replacementChar : go cs-- twoBytes :: Word8 -> [Word8] -> String- twoBytes c0 (c1:cs')- | c1 .&. 0xC0 == 0x80- = let d = (fromIntegral (c0 .&. 0x1F) `shiftL` 6)- .|. fromIntegral (c1 .&. 0x3F)- in if d >= 0x80- then chr d : go cs'- else replacementChar : go cs'- twoBytes _ cs' = replacementChar : go cs'-- moreBytes :: Int -> Int -> [Word8] -> Int -> [Char]- moreBytes 1 overlong cs' acc- | overlong <= acc, acc <= 0x10FFFF, acc < 0xD800 || 0xDFFF < acc- = chr acc : go cs'-- | otherwise- = replacementChar : go cs'-- moreBytes byteCount overlong (cn:cs') acc- | cn .&. 0xC0 == 0x80- = moreBytes (byteCount-1) overlong cs'- ((acc `shiftL` 6) .|. fromIntegral cn .&. 0x3F)-- moreBytes _ _ cs' _- = replacementChar : go cs'-- replacementChar = '\xfffd'----- | Encode 'String' to a list of UTF8-encoded octets------ Code-points in the @U+D800@-@U+DFFF@ range will be encoded--- as the replacement character (i.e. @U+FFFD@).------ See also 'decodeUtf8'-encodeStringUtf8 :: String -> [Word8]-encodeStringUtf8 [] = []-encodeStringUtf8 (c:cs)- | c <= '\x07F' = w8- : encodeStringUtf8 cs- | c <= '\x7FF' = (0xC0 .|. w8ShiftR 6 )- : (0x80 .|. (w8 .&. 0x3F))- : encodeStringUtf8 cs- | c <= '\xD7FF'= (0xE0 .|. w8ShiftR 12 )- : (0x80 .|. (w8ShiftR 6 .&. 0x3F))- : (0x80 .|. (w8 .&. 0x3F))- : encodeStringUtf8 cs- | c <= '\xDFFF'= 0xEF : 0xBF : 0xBD -- U+FFFD- : encodeStringUtf8 cs- | c <= '\xFFFF'= (0xE0 .|. w8ShiftR 12 )- : (0x80 .|. (w8ShiftR 6 .&. 0x3F))- : (0x80 .|. (w8 .&. 0x3F))- : encodeStringUtf8 cs- | otherwise = (0xf0 .|. w8ShiftR 18 )- : (0x80 .|. (w8ShiftR 12 .&. 0x3F))- : (0x80 .|. (w8ShiftR 6 .&. 0x3F))- : (0x80 .|. (w8 .&. 0x3F))- : encodeStringUtf8 cs- where- w8 = fromIntegral (ord c) :: Word8- w8ShiftR :: Int -> Word8- w8ShiftR = fromIntegral . shiftR (ord c)
− src/Distribution/Utils/Structured.hs
@@ -1,477 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE DefaultSignatures #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE PolyKinds #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeFamilies #-}-{-# LANGUAGE TypeOperators #-}-#if __GLASGOW_HASKELL__ >= 711-{-# LANGUAGE PatternSynonyms #-}-#endif-#if __GLASGOW_HASKELL__ >= 800-{-# LANGUAGE TypeInType #-}-#endif--- |------ Copyright: (c) 2019 Oleg Grenrus------ Structurally tag binary serialisaton stream.--- Useful when most 'Binary' instances are 'Generic' derived.------ Say you have a data type------ @--- data Record = Record--- { _recordFields :: HM.HashMap Text (Integer, ByteString)--- , _recordEnabled :: Bool--- }--- deriving (Eq, Show, Generic)------ instance 'Binary' Record--- instance 'Structured' Record--- @------ then you can serialise and deserialise @Record@ values with a structure tag by simply------ @--- 'structuredEncode' record :: 'LBS.ByteString'--- 'structuredDecode' lbs :: IO Record--- @------ If structure of @Record@ changes in between, deserialisation will fail early.------ Technically, 'Structured' is not related to 'Binary', and may--- be useful in other uses.----module Distribution.Utils.Structured (- -- * Encoding and decoding- -- | These functions operate like @binary@'s counterparts,- -- but the serialised version has a structure hash in front.- structuredEncode,- structuredEncodeFile,- structuredDecode,- structuredDecodeOrFailIO,- structuredDecodeFileOrFail,- -- * Structured class- Structured (structure),- MD5,- structureHash,- structureBuilder,- genericStructure,- GStructured,- nominalStructure,- containerStructure,- -- * Structure type- Structure (..),- Tag (..),- TypeName,- ConstructorName,- TypeVersion,- SopStructure,- hashStructure,- typeVersion,- typeName,- ) where--import Data.Int (Int16, Int32, Int64, Int8)-import Data.List.NonEmpty (NonEmpty)-import Data.Proxy (Proxy (..))-import Data.Ratio (Ratio)-import Data.Word (Word, Word16, Word32, Word64, Word8)--import qualified Control.Monad.Trans.State.Strict as State--import Control.Exception (ErrorCall (..), catch, evaluate)--import GHC.Generics--import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as LBS-#if MIN_VERSION_bytestring(0,10,4)-import qualified Data.ByteString.Builder as Builder-#else-import qualified Data.ByteString.Lazy.Builder as Builder-#endif-import qualified Data.IntMap as IM-import qualified Data.IntSet as IS-import qualified Data.Map as Map-import qualified Data.Sequence as Seq-import qualified Data.Set as Set-import qualified Data.Text as T-import qualified Data.Text.Lazy as LT-import qualified Data.Time as Time-import qualified Distribution.Compat.Binary as Binary--#ifdef MIN_VERSION_aeson-import qualified Data.Aeson as Aeson-#endif--#if __GLASGOW_HASKELL__ >= 800-import Data.Kind (Type)-#else-#define Type *-#endif--import Distribution.Compat.Typeable (Typeable, TypeRep, typeRep)-import Distribution.Utils.MD5--import Data.Monoid (mconcat)--import qualified Data.Semigroup-import qualified Data.Foldable--#if !MIN_VERSION_base(4,8,0)-import Control.Applicative (pure)-import Data.Traversable (traverse)-#endif--#if !MIN_VERSION_base(4,7,0)-import Data.Typeable (Typeable1, typeOf1)-#endif------------------------------------------------------------------------------------- Types----------------------------------------------------------------------------------type TypeName = String-type ConstructorName = String---- | A sematic version of a data type. Usually 0.-type TypeVersion = Word32---- | Structure of a datatype.------ It can be infinite, as far as 'TypeRep's involved are finite.--- (e.g. polymorphic recursion might cause troubles).----data Structure- = Nominal !TypeRep !TypeVersion TypeName [Structure] -- ^ nominal, yet can be parametrised by other structures.- | Newtype !TypeRep !TypeVersion TypeName Structure -- ^ a newtype wrapper- | Structure !TypeRep !TypeVersion TypeName SopStructure -- ^ sum-of-products structure- deriving (Eq, Ord, Show, Generic)--type SopStructure = [(ConstructorName, [Structure])]---- | A MD5 hash digest of 'Structure'.-hashStructure :: Structure -> MD5-hashStructure = md5 . LBS.toStrict . Builder.toLazyByteString . structureBuilder---- | A van-Laarhoven lens into 'TypeVersion' of 'Structure'------ @--- 'typeVersion' :: Lens' 'Structure' 'TypeVersion'--- @-typeVersion :: Functor f => (TypeVersion -> f TypeVersion) -> Structure -> f Structure-typeVersion f (Nominal t v n s) = fmap (\v' -> Nominal t v' n s) (f v)-typeVersion f (Newtype t v n s) = fmap (\v' -> Newtype t v' n s) (f v)-typeVersion f (Structure t v n s) = fmap (\v' -> Structure t v' n s) (f v)---- | A van-Laarhoven lens into 'TypeName' of 'Structure'------ @--- 'typeName' :: Lens' 'Structure' 'TypeName'--- @-typeName :: Functor f => (TypeName -> f TypeName) -> Structure -> f Structure-typeName f (Nominal t v n s) = fmap (\n' -> Nominal t v n' s) (f n)-typeName f (Newtype t v n s) = fmap (\n' -> Newtype t v n' s) (f n)-typeName f (Structure t v n s) = fmap (\n' -> Structure t v n' s) (f n)------------------------------------------------------------------------------------ Builder------------------------------------------------------------------------------------ | Flatten 'Structure' into something we can calculate hash of.------ As 'Structure' can be potentially infinite. For mutually recursive types,--- we keep track of 'TypeRep's, and put just 'TypeRep' name when it's occurred--- another time.-structureBuilder :: Structure -> Builder.Builder-structureBuilder s0 = State.evalState (go s0) Map.empty where- go :: Structure -> State.State (Map.Map String (NonEmpty TypeRep)) Builder.Builder- go (Nominal t v n s) = withTypeRep t $ do- s' <- traverse go s- return $ mconcat $ Builder.word8 1 : Builder.word32LE v : Builder.stringUtf8 n : s'-- go (Newtype t v n s) = withTypeRep t $ do- s' <- go s- return $ mconcat [Builder.word8 2, Builder.word32LE v, Builder.stringUtf8 n, s']-- go (Structure t v n s) = withTypeRep t $ do- s' <- goSop s- return $ mconcat [Builder.word8 3, Builder.word32LE v, Builder.stringUtf8 n, s']-- withTypeRep t k = do- acc <- State.get- case insert t acc of- Nothing -> return $ mconcat [ Builder.word8 0, Builder.stringUtf8 (show t) ]- Just acc' -> do- State.put acc'- k-- goSop :: SopStructure -> State.State (Map.Map String (NonEmpty TypeRep)) Builder.Builder- goSop sop = do- parts <- traverse part sop- return $ mconcat parts-- part (cn, s) = do- s' <- traverse go s- return $ Data.Monoid.mconcat [ Builder.stringUtf8 cn, mconcat s' ]-- insert :: TypeRep -> Map.Map String (NonEmpty TypeRep) -> Maybe (Map.Map String (NonEmpty TypeRep))- insert tr m = case Map.lookup trShown m of- Nothing -> inserted- Just ne | tr `Data.Foldable.elem` ne -> Nothing- | otherwise -> inserted- where- inserted = Just (Map.insertWith (Data.Semigroup.<>) trShown (pure tr) m)- trShown = show tr------------------------------------------------------------------------------------ Classes------------------------------------------------------------------------------------ | Class of types with a known 'Structure'.------ For regular data types 'Structured' can be derived generically.------ @--- data Record = Record { a :: Int, b :: Bool, c :: [Char] } deriving ('Generic')--- instance 'Structured' Record--- @------ @since 3.2.0.0----class Typeable a => Structured a where- structure :: Proxy a -> Structure- default structure :: (Generic a, GStructured (Rep a)) => Proxy a -> Structure- structure = genericStructure-- -- This member is hidden. It's there to precalc- structureHash' :: Tagged a MD5- structureHash' = Tagged (hashStructure (structure (Proxy :: Proxy a)))---- private Tagged-newtype Tagged a b = Tagged { untag :: b }---- | Semantically @'hashStructure' . 'structure'@.-structureHash :: forall a. Structured a => Proxy a -> MD5-structureHash _ = untag (structureHash' :: Tagged a MD5)------------------------------------------------------------------------------------ Functions------------------------------------------------------------------------------------ | Structured 'Binary.encode'.--- Encode a value to using binary serialisation to a lazy 'LBS.ByteString'.--- Encoding starts with 16 byte large structure hash.-structuredEncode- :: forall a. (Binary.Binary a, Structured a)- => a -> LBS.ByteString-structuredEncode x = Binary.encode (Tag :: Tag a, x)---- | Lazily serialise a value to a file-structuredEncodeFile :: (Binary.Binary a, Structured a) => FilePath -> a -> IO ()-structuredEncodeFile f = LBS.writeFile f . structuredEncode---- | Structured 'Binary.decode'.--- Decode a value from a lazy 'LBS.ByteString', reconstructing the original structure.--- Throws pure exception on invalid inputs.-structuredDecode- :: forall a. (Binary.Binary a, Structured a)- => LBS.ByteString -> a-structuredDecode lbs = snd (Binary.decode lbs :: (Tag a, a))--structuredDecodeOrFailIO :: (Binary.Binary a, Structured a) => LBS.ByteString -> IO (Either String a)-structuredDecodeOrFailIO bs =- catch (evaluate (structuredDecode bs) >>= return . Right) handler- where-#if MIN_VERSION_base(4,9,0)- handler (ErrorCallWithLocation str _) = return $ Left str-#else- handler (ErrorCall str) = return $ Left str-#endif---- | Lazily reconstruct a value previously written to a file.-structuredDecodeFileOrFail :: (Binary.Binary a, Structured a) => FilePath -> IO (Either String a)-structuredDecodeFileOrFail f = structuredDecodeOrFailIO =<< LBS.readFile f------------------------------------------------------------------------------------ Helper data----------------------------------------------------------------------------------data Tag a = Tag--instance Structured a => Binary.Binary (Tag a) where- get = do- actual <- binaryGetMD5- if actual == expected- then return Tag- else fail $ concat- [ "Non-matching structured hashes: "- , showMD5 actual- , "; expected: "- , showMD5 expected- ]- where- expected = untag (structureHash' :: Tagged a MD5)-- put _ = binaryPutMD5 expected- where- expected = untag (structureHash' :: Tagged a MD5)------------------------------------------------------------------------------------ Smart constructors------------------------------------------------------------------------------------ | Use 'Typeable' to infer name-nominalStructure :: Typeable a => Proxy a -> Structure-nominalStructure p = Nominal tr 0 (show tr) [] where- tr = typeRep p--#if MIN_VERSION_base(4,7,0)-containerStructure :: forall f a. (Typeable f, Structured a) => Proxy (f a) -> Structure-containerStructure _ = Nominal faTypeRep 0 (show fTypeRep)- [ structure (Proxy :: Proxy a)- ]- where- fTypeRep = typeRep (Proxy :: Proxy f)- faTypeRep = typeRep (Proxy :: Proxy (f a))--#else-containerStructure :: forall f a. (Typeable1 f, Structured a) => Proxy (f a) -> Structure-containerStructure _ = Nominal faTypeRep 0 (show fTypeRep)- [ structure (Proxy :: Proxy a)- ]- where- fTypeRep = typeOf1 (undefined :: f ())- faTypeRep = typeRep (Proxy :: Proxy (f a))-#endif------------------------------------------------------------------------------------ Generic------------------------------------------------------------------------------------ | Derive 'structure' genrically.-genericStructure :: forall a. (Typeable a, Generic a, GStructured (Rep a)) => Proxy a -> Structure-genericStructure _ = gstructured (typeRep (Proxy :: Proxy a)) (Proxy :: Proxy (Rep a)) 0---- | Used to implement 'genericStructure'.-class GStructured (f :: Type -> Type) where- gstructured :: TypeRep -> Proxy f -> TypeVersion -> Structure--instance (i ~ D, Datatype c, GStructuredSum f) => GStructured (M1 i c f) where- gstructured tr _ v = case sop of-#if MIN_VERSION_base(4,7,0)- [(_, [s])] | isNewtype p -> Newtype tr v name s-#endif- _ -> Structure tr v name sop- where- p = undefined :: M1 i c f ()- name = datatypeName p- sop = gstructuredSum (Proxy :: Proxy f) []--class GStructuredSum (f :: Type -> Type) where- gstructuredSum :: Proxy f -> SopStructure -> SopStructure--instance (i ~ C, Constructor c, GStructuredProd f) => GStructuredSum (M1 i c f) where- gstructuredSum _ xs = (name, prod) : xs- where- name = conName (undefined :: M1 i c f ())- prod = gstructuredProd (Proxy :: Proxy f) []--instance (GStructuredSum f, GStructuredSum g) => GStructuredSum (f :+: g) where- gstructuredSum _ xs- = gstructuredSum (Proxy :: Proxy f)- $ gstructuredSum (Proxy :: Proxy g) xs--instance GStructuredSum V1 where- gstructuredSum _ = id--class GStructuredProd (f :: Type -> Type) where- gstructuredProd :: Proxy f -> [Structure] -> [Structure]--instance (i ~ S, GStructuredProd f) => GStructuredProd (M1 i c f) where- gstructuredProd _ = gstructuredProd (Proxy :: Proxy f)--instance Structured c => GStructuredProd (K1 i c) where- gstructuredProd _ xs = structure (Proxy :: Proxy c) : xs--instance GStructuredProd U1 where- gstructuredProd _ = id--instance (GStructuredProd f, GStructuredProd g) => GStructuredProd (f :*: g) where- gstructuredProd _ xs- = gstructuredProd (Proxy :: Proxy f)- $ gstructuredProd (Proxy :: Proxy g) xs------------------------------------------------------------------------------------ instances----------------------------------------------------------------------------------instance Structured ()-instance Structured Bool-instance Structured Ordering--instance Structured Char where structure = nominalStructure-instance Structured Int where structure = nominalStructure-instance Structured Integer where structure = nominalStructure--instance Structured Data.Word.Word where structure = nominalStructure--instance Structured Int8 where structure = nominalStructure-instance Structured Int16 where structure = nominalStructure-instance Structured Int32 where structure = nominalStructure-instance Structured Int64 where structure = nominalStructure--instance Structured Word8 where structure = nominalStructure-instance Structured Word16 where structure = nominalStructure-instance Structured Word32 where structure = nominalStructure-instance Structured Word64 where structure = nominalStructure--instance Structured Float where structure = nominalStructure-instance Structured Double where structure = nominalStructure--instance Structured a => Structured (Maybe a)-instance (Structured a, Structured b) => Structured (Either a b)-instance Structured a => Structured (Ratio a) where structure = containerStructure-instance Structured a => Structured [a] where structure = containerStructure-instance Structured a => Structured (NonEmpty a) where structure = containerStructure--instance (Structured a1, Structured a2) => Structured (a1, a2)-instance (Structured a1, Structured a2, Structured a3) => Structured (a1, a2, a3)-instance (Structured a1, Structured a2, Structured a3, Structured a4) => Structured (a1, a2, a3, a4)-instance (Structured a1, Structured a2, Structured a3, Structured a4, Structured a5) => Structured (a1, a2, a3, a4, a5)-instance (Structured a1, Structured a2, Structured a3, Structured a4, Structured a5, Structured a6) => Structured (a1, a2, a3, a4, a5, a6)-instance (Structured a1, Structured a2, Structured a3, Structured a4, Structured a5, Structured a6, Structured a7) => Structured (a1, a2, a3, a4, a5, a6, a7)--instance Structured BS.ByteString where structure = nominalStructure-instance Structured LBS.ByteString where structure = nominalStructure--instance Structured T.Text where structure = nominalStructure-instance Structured LT.Text where structure = nominalStructure--instance (Structured k, Structured v) => Structured (Map.Map k v) where structure _ = Nominal (typeRep (Proxy :: Proxy (Map.Map k v))) 0 "Map" [ structure (Proxy :: Proxy k), structure (Proxy :: Proxy v) ]-instance (Structured k) => Structured (Set.Set k) where structure = containerStructure-instance (Structured v) => Structured (IM.IntMap v) where structure = containerStructure-instance Structured IS.IntSet where structure = nominalStructure-instance (Structured v) => Structured (Seq.Seq v) where structure = containerStructure--instance Structured Time.UTCTime where structure = nominalStructure-instance Structured Time.DiffTime where structure = nominalStructure-instance Structured Time.UniversalTime where structure = nominalStructure-instance Structured Time.NominalDiffTime where structure = nominalStructure-instance Structured Time.Day where structure = nominalStructure-instance Structured Time.TimeZone where structure = nominalStructure-instance Structured Time.TimeOfDay where structure = nominalStructure-instance Structured Time.LocalTime where structure = nominalStructure---- Proxy isn't Typeable in base-4.8 / base---- #if __GLASGOW_HASKELL__ >= 800--- instance (Typeable k, Typeable (a :: k)) => Structured (Proxy a)--- #else--- instance (Typeable a) => Structured (Proxy a) where--- structure p = Structure (typeRep p) 0 "Proxy" [("Proxy",[])]--- #endif
src/Distribution/Utils/UnionFind.hs view
@@ -1,27 +1,27 @@ {-# LANGUAGE NondecreasingIndentation #-}+ -- | A simple mutable union-find data structure. -- -- It is used in a unification algorithm for backpack mix-in linking. -- -- This implementation is based off of the one in \"The Essence of ML Type -- Inference\". (N.B. the union-find package is also based off of this.)----module Distribution.Utils.UnionFind (- Point,- fresh,- find,- union,- equivalent,-) where+module Distribution.Utils.UnionFind+ ( Point+ , fresh+ , find+ , union+ , equivalent+ ) where -import Data.STRef import Control.Monad import Control.Monad.ST+import Data.STRef -- | A variable which can be unified; alternately, this can be thought -- of as an equivalence class with a distinguished representative. newtype Point s a = Point (STRef s (Link s a))- deriving (Eq)+ deriving (Eq) -- | Mutable write to a 'Point' writePoint :: Point s a -> Link s a -> ST s ()@@ -35,39 +35,41 @@ -- the representative element of an equivalence class, or a link to -- the 'Point' that actually stores the representative type. data Link s a- -- NB: it is too bad we can't say STRef Int#; the weights remain boxed- = Info {-# UNPACK #-} !(STRef s Int) {-# UNPACK #-} !(STRef s a)- | Link {-# UNPACK #-} !(Point s a)+ = -- NB: it is too bad we can't say STRef Int#; the weights remain boxed+ Info {-# UNPACK #-} !(STRef s Int) {-# UNPACK #-} !(STRef s a)+ | Link {-# UNPACK #-} !(Point s a) -- | Create a fresh equivalence class with one element. fresh :: a -> ST s (Point s a) fresh desc = do- weight <- newSTRef 1- descriptor <- newSTRef desc- Point `fmap` newSTRef (Info weight descriptor)+ weight <- newSTRef 1+ descriptor <- newSTRef desc+ Point `fmap` newSTRef (Info weight descriptor) -- | Flatten any chains of links, returning a 'Point' -- which points directly to the canonical representation. repr :: Point s a -> ST s (Point s a)-repr point = readPoint point >>= \r ->- case r of- Link point' -> do+repr point =+ readPoint point >>= \r ->+ case r of+ Link point' -> do point'' <- repr point' when (point'' /= point') $ do- writePoint point =<< readPoint point'+ writePoint point =<< readPoint point' return point''- Info _ _ -> return point+ Info _ _ -> return point -- | Return the canonical element of an equivalence -- class 'Point'. find :: Point s a -> ST s a find point =- -- Optimize length 0 and 1 case at expense of- -- general case- readPoint point >>= \r ->- case r of- Info _ d_ref -> readSTRef d_ref- Link point' -> readPoint point' >>= \r' ->+ -- Optimize length 0 and 1 case at expense of+ -- general case+ readPoint point >>= \r ->+ case r of+ Info _ d_ref -> readSTRef d_ref+ Link point' ->+ readPoint point' >>= \r' -> case r' of Info _ d_ref -> readSTRef d_ref Link _ -> repr point >>= find@@ -76,26 +78,26 @@ -- a canonical element. Keeps the descriptor of point2. union :: Point s a -> Point s a -> ST s () union refpoint1 refpoint2 = do- point1 <- repr refpoint1- point2 <- repr refpoint2- when (point1 /= point2) $ do+ point1 <- repr refpoint1+ point2 <- repr refpoint2+ when (point1 /= point2) $ do l1 <- readPoint point1 l2 <- readPoint point2 case (l1, l2) of- (Info wref1 dref1, Info wref2 dref2) -> do- weight1 <- readSTRef wref1- weight2 <- readSTRef wref2- -- Should be able to optimize the == case separately- if weight1 >= weight2- then do- writePoint point2 (Link point1)- -- The weight calculation here seems a bit dodgy- writeSTRef wref1 (weight1 + weight2)- writeSTRef dref1 =<< readSTRef dref2- else do- writePoint point1 (Link point2)- writeSTRef wref2 (weight1 + weight2)- _ -> error "UnionFind.union: repr invariant broken"+ (Info wref1 dref1, Info wref2 dref2) -> do+ weight1 <- readSTRef wref1+ weight2 <- readSTRef wref2+ -- Should be able to optimize the == case separately+ if weight1 >= weight2+ then do+ writePoint point2 (Link point1)+ -- The weight calculation here seems a bit dodgy+ writeSTRef wref1 (weight1 + weight2)+ writeSTRef dref1 =<< readSTRef dref2+ else do+ writePoint point1 (Link point2)+ writeSTRef wref2 (weight1 + weight2)+ _ -> error "UnionFind.union: repr invariant broken" -- | Test if two points are in the same equivalence class. equivalent :: Point s a -> Point s a -> ST s Bool
src/Distribution/Verbosity.hs view
@@ -1,7 +1,9 @@-{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-} -----------------------------------------------------------------------------++-- Verbosity for Cabal functions.+ -- | -- Module : Distribution.Verbosity -- Copyright : Ian Lynagh 2007@@ -21,113 +23,131 @@ -- settings (e.g., so that you can trace only particular things you -- are interested in.) It's important to note that the instances -- for 'Verbosity' assume that this does not exist.---- Verbosity for Cabal functions.+module Distribution.Verbosity+ ( -- * Verbosity+ Verbosity+ , silent+ , normal+ , verbose+ , deafening+ , moreVerbose+ , lessVerbose+ , isVerboseQuiet+ , intToVerbosity+ , flagToVerbosity+ , showForCabal+ , showForGHC+ , verboseNoFlags+ , verboseHasFlags+ , modifyVerbosity -module Distribution.Verbosity (- -- * Verbosity- Verbosity,- silent, normal, verbose, deafening,- moreVerbose, lessVerbose, isVerboseQuiet,- intToVerbosity, flagToVerbosity,- showForCabal, showForGHC,- verboseNoFlags, verboseHasFlags,- modifyVerbosity,+ -- * Call stacks+ , verboseCallSite+ , verboseCallStack+ , isVerboseCallSite+ , isVerboseCallStack - -- * Call stacks- verboseCallSite, verboseCallStack,- isVerboseCallSite, isVerboseCallStack,+ -- * Output markets+ , verboseMarkOutput+ , isVerboseMarkOutput+ , verboseUnmarkOutput - -- * Output markets- verboseMarkOutput, isVerboseMarkOutput,- verboseUnmarkOutput,+ -- * Line wrapping+ , verboseNoWrap+ , isVerboseNoWrap - -- * line-wrapping- verboseNoWrap, isVerboseNoWrap,+ -- * Time stamps+ , verboseTimestamp+ , isVerboseTimestamp+ , verboseNoTimestamp - -- * timestamps- verboseTimestamp, isVerboseTimestamp,- verboseNoTimestamp,+ -- * Stderr+ , verboseStderr+ , isVerboseStderr+ , verboseNoStderr - -- * Stderr- verboseStderr, isVerboseStderr,- verboseNoStderr,+ -- * No warnings+ , verboseNoWarn+ , isVerboseNoWarn ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () import Distribution.ReadE import Data.List (elemIndex) import Distribution.Parsec import Distribution.Pretty-import Distribution.Verbosity.Internal import Distribution.Utils.Generic (isAsciiAlpha)+import Distribution.Verbosity.Internal import qualified Data.Set as Set import qualified Distribution.Compat.CharParsing as P import qualified Text.PrettyPrint as PP -data Verbosity = Verbosity {- vLevel :: VerbosityLevel,- vFlags :: Set VerbosityFlag,- vQuiet :: Bool- } deriving (Generic, Show, Read, Typeable)+data Verbosity = Verbosity+ { vLevel :: VerbosityLevel+ , vFlags :: Set VerbosityFlag+ , vQuiet :: Bool+ }+ deriving (Generic, Show, Read) mkVerbosity :: VerbosityLevel -> Verbosity-mkVerbosity l = Verbosity { vLevel = l, vFlags = Set.empty, vQuiet = False }+mkVerbosity l = Verbosity{vLevel = l, vFlags = Set.empty, vQuiet = False} instance Eq Verbosity where- x == y = vLevel x == vLevel y+ x == y = vLevel x == vLevel y instance Ord Verbosity where- compare x y = compare (vLevel x) (vLevel y)+ compare x y = compare (vLevel x) (vLevel y) instance Enum Verbosity where- toEnum = mkVerbosity . toEnum- fromEnum = fromEnum . vLevel+ toEnum = mkVerbosity . toEnum+ fromEnum = fromEnum . vLevel instance Bounded Verbosity where- minBound = mkVerbosity minBound- maxBound = mkVerbosity maxBound+ minBound = mkVerbosity minBound+ maxBound = mkVerbosity maxBound instance Binary Verbosity instance Structured Verbosity --- We shouldn't print /anything/ unless an error occurs in silent mode+-- | In 'silent' mode, we should not print /anything/ unless an error occurs. silent :: Verbosity silent = mkVerbosity Silent --- Print stuff we want to see by default+-- | Print stuff we want to see by default. normal :: Verbosity normal = mkVerbosity Normal --- Be more verbose about what's going on+-- | Be more verbose about what's going on. verbose :: Verbosity verbose = mkVerbosity Verbose --- Not only are we verbose ourselves (perhaps even noisier than when--- being "verbose"), but we tell everything we run to be verbose too+-- | Not only are we verbose ourselves (perhaps even noisier than when+-- being 'verbose'), but we tell everything we run to be verbose too. deafening :: Verbosity deafening = mkVerbosity Deafening +-- | Increase verbosity level, but stay 'silent' if we are. moreVerbose :: Verbosity -> Verbosity moreVerbose v =- case vLevel v of- Silent -> v -- silent should stay silent- Normal -> v { vLevel = Verbose }- Verbose -> v { vLevel = Deafening }- Deafening -> v+ case vLevel v of+ Silent -> v -- silent should stay silent+ Normal -> v{vLevel = Verbose}+ Verbose -> v{vLevel = Deafening}+ Deafening -> v +-- | Decrease verbosity level, but stay 'deafening' if we are. lessVerbose :: Verbosity -> Verbosity lessVerbose v =- verboseQuiet $+ verboseQuiet $ case vLevel v of- Deafening -> v -- deafening stays deafening- Verbose -> v { vLevel = Normal }- Normal -> v { vLevel = Silent }- Silent -> v+ Deafening -> v -- deafening stays deafening+ Verbose -> v{vLevel = Normal}+ Normal -> v{vLevel = Silent}+ Silent -> v -- | Combinator for transforming verbosity level while retaining the -- original hidden state.@@ -141,8 +161,9 @@ -- -- @since 2.0.1.0 modifyVerbosity :: (Verbosity -> Verbosity) -> Verbosity -> Verbosity-modifyVerbosity f v = v { vLevel = vLevel (f v) }+modifyVerbosity f v = v{vLevel = vLevel (f v)} +-- | Numeric verbosity level @0..3@: @0@ is 'silent', @3@ is 'deafening'. intToVerbosity :: Int -> Maybe Verbosity intToVerbosity 0 = Just (mkVerbosity Silent) intToVerbosity 1 = Just (mkVerbosity Normal)@@ -171,79 +192,82 @@ -- Right (Verbosity {vLevel = Deafening, vFlags = fromList [VCallStack,VCallSite,VNoWrap,VStderr], vQuiet = False}) -- -- /Note:/ this parser will eat trailing spaces.--- instance Parsec Verbosity where- parsec = parsecVerbosity+ parsec = parsecVerbosity instance Pretty Verbosity where- pretty = PP.text . showForCabal+ pretty = PP.text . showForCabal parsecVerbosity :: CabalParsing m => m Verbosity parsecVerbosity = parseIntVerbosity <|> parseStringVerbosity where parseIntVerbosity = do- i <- P.integral- case intToVerbosity i of- Just v -> return v- Nothing -> P.unexpected $ "Bad integral verbosity: " ++ show i ++ ". Valid values are 0..3"+ i <- P.integral+ case intToVerbosity i of+ Just v -> return v+ Nothing -> P.unexpected $ "Bad integral verbosity: " ++ show i ++ ". Valid values are 0..3" parseStringVerbosity = do- level <- parseVerbosityLevel- _ <- P.spaces- flags <- many (parseFlag <* P.spaces)- return $ foldl' (flip ($)) (mkVerbosity level) flags+ level <- parseVerbosityLevel+ _ <- P.spaces+ flags <- many (parseFlag <* P.spaces)+ return $ foldl' (flip ($)) (mkVerbosity level) flags parseVerbosityLevel = do- token <- P.munch1 isAsciiAlpha- case token of- "silent" -> return Silent- "normal" -> return Normal- "verbose" -> return Verbose- "debug" -> return Deafening- "deafening" -> return Deafening- _ -> P.unexpected $ "Bad verbosity level: " ++ token+ token <- P.munch1 isAsciiAlpha+ case token of+ "silent" -> return Silent+ "normal" -> return Normal+ "verbose" -> return Verbose+ "debug" -> return Deafening+ "deafening" -> return Deafening+ _ -> P.unexpected $ "Bad verbosity level: " ++ token parseFlag = do- _ <- P.char '+'- token <- P.munch1 isAsciiAlpha- case token of- "callsite" -> return verboseCallSite- "callstack" -> return verboseCallStack- "nowrap" -> return verboseNoWrap- "markoutput" -> return verboseMarkOutput- "timestamp" -> return verboseTimestamp- "stderr" -> return verboseStderr- "stdout" -> return verboseNoStderr- _ -> P.unexpected $ "Bad verbosity flag: " ++ token+ _ <- P.char '+'+ token <- P.munch1 isAsciiAlpha+ case token of+ "callsite" -> return verboseCallSite+ "callstack" -> return verboseCallStack+ "nowrap" -> return verboseNoWrap+ "markoutput" -> return verboseMarkOutput+ "timestamp" -> return verboseTimestamp+ "stderr" -> return verboseStderr+ "stdout" -> return verboseNoStderr+ "nowarn" -> return verboseNoWarn+ _ -> P.unexpected $ "Bad verbosity flag: " ++ token flagToVerbosity :: ReadE Verbosity flagToVerbosity = parsecToReadE id parsecVerbosity showForCabal :: Verbosity -> String showForCabal v- | Set.null (vFlags v)- = maybe (error "unknown verbosity") show $- elemIndex v [silent,normal,verbose,deafening]- | otherwise- = unwords- $ showLevel (vLevel v)- : concatMap showFlag (Set.toList (vFlags v))+ | Set.null (vFlags v) =+ maybe (error "unknown verbosity") show $+ elemIndex v [silent, normal, verbose, deafening]+ | otherwise =+ unwords $+ showLevel (vLevel v)+ : concatMap showFlag (Set.toList (vFlags v)) where- showLevel Silent = "silent"- showLevel Normal = "normal"- showLevel Verbose = "verbose"+ showLevel Silent = "silent"+ showLevel Normal = "normal"+ showLevel Verbose = "verbose" showLevel Deafening = "debug" - showFlag VCallSite = ["+callsite"]- showFlag VCallStack = ["+callstack"]- showFlag VNoWrap = ["+nowrap"]+ showFlag VCallSite = ["+callsite"]+ showFlag VCallStack = ["+callstack"]+ showFlag VNoWrap = ["+nowrap"] showFlag VMarkOutput = ["+markoutput"]- showFlag VTimestamp = ["+timestamp"]- showFlag VStderr = ["+stderr"]+ showFlag VTimestamp = ["+timestamp"]+ showFlag VStderr = ["+stderr"]+ showFlag VNoWarn = ["+nowarn"] showForGHC :: Verbosity -> String-showForGHC v = maybe (error "unknown verbosity") show $- elemIndex v [silent,normal,__,verbose,deafening]- where __ = silent -- this will be always ignored by elemIndex+showForGHC v =+ maybe (error "unknown verbosity") show $+ elemIndex v [silent, normal, __, verbose, deafening]+ where+ __ = silent -- this will be always ignored by elemIndex -- | Turn on verbose call-site printing when we log. verboseCallSite :: Verbosity -> Verbosity@@ -266,9 +290,9 @@ verboseNoWrap :: Verbosity -> Verbosity verboseNoWrap = verboseFlag VNoWrap --- | Mark the verbosity as quiet+-- | Mark the verbosity as quiet. verboseQuiet :: Verbosity -> Verbosity-verboseQuiet v = v { vQuiet = True }+verboseQuiet v = v{vQuiet = True} -- | Turn on timestamps for log messages. verboseTimestamp :: Verbosity -> Verbosity@@ -278,29 +302,33 @@ verboseNoTimestamp :: Verbosity -> Verbosity verboseNoTimestamp = verboseNoFlag VTimestamp --- | Turn on timestamps for log messages.+-- | Switch logging to 'stderr'. -- -- @since 3.4.0.0 verboseStderr :: Verbosity -> Verbosity verboseStderr = verboseFlag VStderr --- | Turn off timestamps for log messages.+-- | Switch logging to 'stdout'. -- -- @since 3.4.0.0 verboseNoStderr :: Verbosity -> Verbosity verboseNoStderr = verboseNoFlag VStderr --- | Helper function for flag enabling functions+-- | Turn off warnings for log messages.+verboseNoWarn :: Verbosity -> Verbosity+verboseNoWarn = verboseFlag VNoWarn++-- | Helper function for flag enabling functions. verboseFlag :: VerbosityFlag -> (Verbosity -> Verbosity)-verboseFlag flag v = v { vFlags = Set.insert flag (vFlags v) }+verboseFlag flag v = v{vFlags = Set.insert flag (vFlags v)} --- | Helper function for flag disabling functions+-- | Helper function for flag disabling functions. verboseNoFlag :: VerbosityFlag -> (Verbosity -> Verbosity)-verboseNoFlag flag v = v { vFlags = Set.delete flag (vFlags v) }+verboseNoFlag flag v = v{vFlags = Set.delete flag (vFlags v)} --- | Turn off all flags+-- | Turn off all flags. verboseNoFlags :: Verbosity -> Verbosity-verboseNoFlags v = v { vFlags = Set.empty }+verboseNoFlags v = v{vFlags = Set.empty} verboseHasFlags :: Verbosity -> Bool verboseHasFlags = not . Set.null . vFlags@@ -321,7 +349,7 @@ isVerboseNoWrap :: Verbosity -> Bool isVerboseNoWrap = isVerboseFlag VNoWrap --- | Test if we had called 'lessVerbose' on the verbosity+-- | Test if we had called 'lessVerbose' on the verbosity. isVerboseQuiet :: Verbosity -> Bool isVerboseQuiet = vQuiet @@ -329,11 +357,15 @@ isVerboseTimestamp :: Verbosity -> Bool isVerboseTimestamp = isVerboseFlag VTimestamp --- | Test if we should output to stderr when we log.+-- | Test if we should output to 'stderr' when we log. -- -- @since 3.4.0.0 isVerboseStderr :: Verbosity -> Bool isVerboseStderr = isVerboseFlag VStderr++-- | Test if we should output warnings when we log.+isVerboseNoWarn :: Verbosity -> Bool+isVerboseNoWarn = isVerboseFlag VNoWarn -- | Helper function for flag testing functions. isVerboseFlag :: VerbosityFlag -> Verbosity -> Bool
src/Distribution/Verbosity/Internal.hs view
@@ -1,27 +1,29 @@-{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-}+ module Distribution.Verbosity.Internal- ( VerbosityLevel(..)- , VerbosityFlag(..)+ ( VerbosityLevel (..)+ , VerbosityFlag (..) ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () data VerbosityLevel = Silent | Normal | Verbose | Deafening- deriving (Generic, Show, Read, Eq, Ord, Enum, Bounded, Typeable)+ deriving (Generic, Show, Read, Eq, Ord, Enum, Bounded) instance Binary VerbosityLevel instance Structured VerbosityLevel data VerbosityFlag- = VCallStack- | VCallSite- | VNoWrap- | VMarkOutput- | VTimestamp- | VStderr -- ^ @since 3.4.0.0- deriving (Generic, Show, Read, Eq, Ord, Enum, Bounded, Typeable)+ = VCallStack+ | VCallSite+ | VNoWrap+ | VMarkOutput+ | VTimestamp+ | -- | @since 3.4.0.0+ VStderr+ | VNoWarn+ deriving (Generic, Show, Read, Eq, Ord, Enum, Bounded) instance Binary VerbosityFlag instance Structured VerbosityFlag
− src/Distribution/Version.hs
@@ -1,185 +0,0 @@--------------------------------------------------------------------------------- |--- Module : Distribution.Version--- Copyright : Isaac Jones, Simon Marlow 2003-2004--- Duncan Coutts 2008--- License : BSD3------ Maintainer : cabal-devel@haskell.org--- Portability : portable------ Exports the 'Version' type along with a parser and pretty printer. A version--- is something like @\"1.3.3\"@. It also defines the 'VersionRange' data--- types. Version ranges are like @\">= 1.2 && < 2\"@.--module Distribution.Version (- -- * Package versions- Version,- version0,- mkVersion,- mkVersion',- versionNumbers,- nullVersion,- alterVersion,-- -- * Version ranges- VersionRange,-- -- ** Constructing- anyVersion, noVersion,- thisVersion, notThisVersion,- laterVersion, earlierVersion,- orLaterVersion, orEarlierVersion,- unionVersionRanges, intersectVersionRanges,- withinVersion,- majorBoundVersion,-- -- ** Inspection- withinRange,- isAnyVersion,- isNoVersion,- isSpecificVersion,- simplifyVersionRange,- foldVersionRange,- normaliseVersionRange,- stripParensVersionRange,- hasUpperBound,- hasLowerBound,-- -- ** Cata & ana- VersionRangeF (..),- cataVersionRange,- anaVersionRange,- hyloVersionRange,- projectVersionRange,- embedVersionRange,-- -- ** Utilities- wildcardUpperBound,- majorUpperBound,-- -- ** Modification- removeUpperBound,- removeLowerBound,- transformCaret,- transformCaretUpper,- transformCaretLower,-- -- * Version intervals view- asVersionIntervals,- VersionInterval(..),- LowerBound(..),- UpperBound(..),- Bound(..),-- -- ** 'VersionIntervals' abstract type- -- | The 'VersionIntervals' type and the accompanying functions are exposed- -- primarily for completeness and testing purposes. In practice- -- 'asVersionIntervals' is the main function to use to- -- view a 'VersionRange' as a bunch of 'VersionInterval's.- --- VersionIntervals,- toVersionIntervals,- fromVersionIntervals,- unVersionIntervals,-- ) where--import Distribution.Types.Version-import Distribution.Types.VersionRange-import Distribution.Types.VersionInterval------------------------------------------------------------------------------------ Utilities on VersionRange requiring VersionInterval------------------------------------------------------------------------------------ | This is the converse of 'isAnyVersion'. It check if the version range is--- empty, if there is no possible version that satisfies the version range.------ For example this is @True@ (for all @v@):------ > isNoVersion (EarlierVersion v `IntersectVersionRanges` LaterVersion v)----isNoVersion :: VersionRange -> Bool-isNoVersion vr = case asVersionIntervals vr of- [] -> True- _ -> False---- | Is this version range in fact just a specific version?------ For example the version range @\">= 3 && <= 3\"@ contains only the version--- @3@.----isSpecificVersion :: VersionRange -> Maybe Version-isSpecificVersion vr = case asVersionIntervals vr of- [VersionInterval (LowerBound v InclusiveBound) (UpperBound v' InclusiveBound)]- | v == v' -> Just v- _ -> Nothing------------------------------------------------------------------------------------ Transformations------------------------------------------------------------------------------------ | Simplify a 'VersionRange' expression. For non-empty version ranges--- this produces a canonical form. Empty or inconsistent version ranges--- are left as-is because that provides more information.------ If you need a canonical form use--- @fromVersionIntervals . toVersionIntervals@------ It satisfies the following properties:------ > withinRange v (simplifyVersionRange r) = withinRange v r------ > withinRange v r = withinRange v r'--- > ==> simplifyVersionRange r = simplifyVersionRange r'--- > || isNoVersion r--- > || isNoVersion r'----simplifyVersionRange :: VersionRange -> VersionRange-simplifyVersionRange vr- -- If the version range is inconsistent then we just return the- -- original since that has more information than ">1 && < 1", which- -- is the canonical inconsistent version range.- | null (unVersionIntervals vi) = vr- | otherwise = fromVersionIntervals vi- where- vi = toVersionIntervals vr---- | Given a version range, remove the highest upper bound. Example: @(>= 1 && <--- 3) || (>= 4 && < 5)@ is converted to @(>= 1 && < 3) || (>= 4)@.-removeUpperBound :: VersionRange -> VersionRange-removeUpperBound = fromVersionIntervals . relaxLastInterval . toVersionIntervals---- | Given a version range, remove the lowest lower bound.--- Example: @(>= 1 && < 3) || (>= 4 && < 5)@ is converted to--- @(>= 0 && < 3) || (>= 4 && < 5)@.-removeLowerBound :: VersionRange -> VersionRange-removeLowerBound = fromVersionIntervals . relaxHeadInterval . toVersionIntervals---- | Rewrite @^>= x.y.z@ into @>= x.y.z && < x.(y+1)@------ @since 3.6.0.0----transformCaret :: VersionRange -> VersionRange-transformCaret = hyloVersionRange embed projectVersionRange where- embed (MajorBoundVersionF v) = orLaterVersion v `intersectVersionRanges` earlierVersion (majorUpperBound v)- embed vr = embedVersionRange vr---- | Rewrite @^>= x.y.z@ into @>= x.y.z@------ @since 3.6.0.0----transformCaretUpper :: VersionRange -> VersionRange-transformCaretUpper = hyloVersionRange embed projectVersionRange where- embed (MajorBoundVersionF v) = orLaterVersion v- embed vr = embedVersionRange vr---- | Rewrite @^>= x.y.z@ into @<x.(y+1)@------ @since 3.6.0.0----transformCaretLower :: VersionRange -> VersionRange-transformCaretLower = hyloVersionRange embed projectVersionRange where- embed (MajorBoundVersionF v) = earlierVersion (majorUpperBound v)- embed vr = embedVersionRange vr
src/Distribution/ZinzaPrelude.hs view
@@ -1,40 +1,41 @@ -- | A small prelude used in @zinza@ generated -- template modules.-module Distribution.ZinzaPrelude (- Writer,- execWriter,- tell,+module Distribution.ZinzaPrelude+ ( Writer+ , execWriter+ , tell+ -- * Re-exports- forM_,- Generic,- PackageName,- Version,- prettyShow- ) where+ , forM_+ , Generic+ , PackageName+ , Version+ , prettyShow+ ) where import Distribution.Compat.Prelude import Prelude () -import Control.Monad (forM_)-import Distribution.Pretty (prettyShow)+import Control.Monad (forM_)+import Distribution.Pretty (prettyShow) import Distribution.Types.PackageName (PackageName)-import Distribution.Types.Version (Version)+import Distribution.Types.Version (Version) -newtype Writer a = W { unW :: ShowS -> (ShowS, a) }+newtype Writer a = W {unW :: ShowS -> (ShowS, a)} instance Functor Writer where- fmap = liftM+ fmap = liftM instance Applicative Writer where- pure x = W $ \ss -> (ss, x)- (<*>) = ap+ pure x = W $ \ss -> (ss, x)+ (<*>) = ap instance Monad Writer where- return = pure- m >>= k = W $ \s1 ->- let (s2, x) = unW m s1- in unW (k x) s2- {-# INLINE (>>=) #-}+ return = pure+ m >>= k = W $ \s1 ->+ let (s2, x) = unW m s1+ in unW (k x) s2+ {-# INLINE (>>=) #-} execWriter :: Writer a -> String execWriter w = fst (unW w id) ""
− src/Language/Haskell/Extension.hs
@@ -1,933 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE OverloadedStrings #-}---------------------------------------------------------------------------------- |--- Module : Language.Haskell.Extension--- Copyright : Isaac Jones 2003-2004--- License : BSD3------ Maintainer : libraries@haskell.org--- Portability : portable------ Haskell language dialects and extensions--module Language.Haskell.Extension (- Language(..),- knownLanguages,- classifyLanguage,-- Extension(..),- KnownExtension(..),- deprecatedExtensions,- classifyExtension,- ) where--import qualified Prelude (head)-import Distribution.Compat.Prelude--import Data.Array (Array, accumArray, bounds, Ix(inRange), (!))--import Distribution.Parsec-import Distribution.Pretty--import qualified Distribution.Compat.CharParsing as P-import qualified Text.PrettyPrint as Disp---- --------------------------------------------------------------- * Language--- ---------------------------------------------------------------- | This represents a Haskell language dialect.------ Language 'Extension's are interpreted relative to one of these base--- languages.----data Language =-- -- | The Haskell 98 language as defined by the Haskell 98 report.- -- <http://haskell.org/onlinereport/>- Haskell98-- -- | The Haskell 2010 language as defined by the Haskell 2010 report.- -- <http://www.haskell.org/onlinereport/haskell2010>- | Haskell2010-- -- | The GHC2021 collection of language extensions.- -- <https://github.com/ghc-proposals/ghc-proposals/blob/master/proposals/0380-ghc2021.rst>- | GHC2021-- -- | An unknown language, identified by its name.- | UnknownLanguage String- deriving (Generic, Show, Read, Eq, Typeable, Data)--instance Binary Language-instance Structured Language--instance NFData Language where rnf = genericRnf---- | List of known (supported) languages for GHC-knownLanguages :: [Language]-knownLanguages = [Haskell98, Haskell2010, GHC2021]--instance Pretty Language where- pretty (UnknownLanguage other) = Disp.text other- pretty other = Disp.text (show other)--instance Parsec Language where- parsec = classifyLanguage <$> P.munch1 isAlphaNum--classifyLanguage :: String -> Language-classifyLanguage = \str -> case lookup str langTable of- Just lang -> lang- Nothing -> UnknownLanguage str- where- langTable = [ (show lang, lang)- | lang <- knownLanguages ]---- --------------------------------------------------------------- * Extension--- ---------------------------------------------------------------- Note: if you add a new 'KnownExtension':------ * also add it to the Distribution.Simple.X.languageExtensions lists--- (where X is each compiler: GHC, UHC, HaskellSuite)------ | This represents language extensions beyond a base 'Language' definition--- (such as 'Haskell98') that are supported by some implementations, usually--- in some special mode.------ Where applicable, references are given to an implementation's--- official documentation.--data Extension =- -- | Enable a known extension- EnableExtension KnownExtension-- -- | Disable a known extension- | DisableExtension KnownExtension-- -- | An unknown extension, identified by the name of its @LANGUAGE@- -- pragma.- | UnknownExtension String-- deriving (Generic, Show, Read, Eq, Ord, Typeable, Data)--instance Binary Extension-instance Structured Extension--instance NFData Extension where rnf = genericRnf--data KnownExtension =-- -- | Allow overlapping class instances, provided there is a unique- -- most specific instance for each use.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XOverlappingInstances>- OverlappingInstances-- -- | Ignore structural rules guaranteeing the termination of class- -- instance resolution. Termination is guaranteed by a fixed-depth- -- recursion stack, and compilation may fail if this depth is- -- exceeded.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XUndecidableInstances>- | UndecidableInstances-- -- | Implies 'OverlappingInstances'. Allow the implementation to- -- choose an instance even when it is possible that further- -- instantiation of types will lead to a more specific instance- -- being applicable.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XIncoherentInstances>- | IncoherentInstances-- -- | /(deprecated)/ Deprecated in favour of 'RecursiveDo'.- --- -- Old description: Allow recursive bindings in @do@ blocks, using- -- the @rec@ keyword. See also 'RecursiveDo'.- | DoRec-- -- | Allow recursive bindings in @do@ blocks, using the @rec@- -- keyword, or @mdo@, a variant of @do@.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XRecursiveDo>- | RecursiveDo-- -- | Provide syntax for writing list comprehensions which iterate- -- over several lists together, like the 'zipWith' family of- -- functions.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XParallelListComp>- | ParallelListComp-- -- | Allow multiple parameters in a type class.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XMultiParamTypeClasses>- | MultiParamTypeClasses-- -- | Enable the dreaded monomorphism restriction.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XNoMonomorphismRestriction>- | MonomorphismRestriction-- -- | Allow a specification attached to a multi-parameter type class- -- which indicates that some parameters are entirely determined by- -- others. The implementation will check that this property holds- -- for the declared instances, and will use this property to reduce- -- ambiguity in instance resolution.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XFunctionalDependencies>- | FunctionalDependencies-- -- | /(deprecated)/ A synonym for 'RankNTypes'.- --- -- Old description: Like 'RankNTypes' but does not allow a- -- higher-rank type to itself appear on the left of a function- -- arrow.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XRank2Types>- | Rank2Types-- -- | Allow a universally-quantified type to occur on the left of a- -- function arrow.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XRankNTypes>- | RankNTypes-- -- | /(deprecated)/ A synonym for 'RankNTypes'.- --- -- Old description: Allow data constructors to have polymorphic- -- arguments. Unlike 'RankNTypes', does not allow this for ordinary- -- functions.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#arbitrary-rank-polymorphism>- | PolymorphicComponents-- -- | Allow existentially-quantified data constructors.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XExistentialQuantification>- | ExistentialQuantification-- -- | Cause a type variable in a signature, which has an explicit- -- @forall@ quantifier, to scope over the definition of the- -- accompanying value declaration.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XScopedTypeVariables>- | ScopedTypeVariables-- -- | Deprecated, use 'ScopedTypeVariables' instead.- | PatternSignatures-- -- | Enable implicit function parameters with dynamic scope.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XImplicitParams>- | ImplicitParams-- -- | Relax some restrictions on the form of the context of a type- -- signature.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleContexts>- | FlexibleContexts-- -- | Relax some restrictions on the form of the context of an- -- instance declaration.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XFlexibleInstances>- | FlexibleInstances-- -- | Allow data type declarations with no constructors.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyDataDecls>- | EmptyDataDecls-- -- | Run the C preprocessor on Haskell source code.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#language-pragma>- | CPP-- -- | Allow an explicit kind signature giving the kind of types over- -- which a type variable ranges.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XKindSignatures>- | KindSignatures-- -- | Enable a form of pattern which forces evaluation before an- -- attempted match, and a form of strict @let@/@where@ binding.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XBangPatterns>- | BangPatterns-- -- | Allow type synonyms in instance heads.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XTypeSynonymInstances>- | TypeSynonymInstances-- -- | Enable Template Haskell, a system for compile-time- -- metaprogramming.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XTemplateHaskell>- | TemplateHaskell-- -- | Enable the Foreign Function Interface. In GHC, implements the- -- standard Haskell 98 Foreign Function Interface Addendum, plus- -- some GHC-specific extensions.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#language-pragma>- | ForeignFunctionInterface-- -- | Enable arrow notation.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XArrows>- | Arrows-- -- | /(deprecated)/ Enable generic type classes, with default instances defined in- -- terms of the algebraic structure of a type.- --- -- * <https://haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#generic-classes>- | Generics-- -- | Enable the implicit importing of the module "Prelude". When- -- disabled, when desugaring certain built-in syntax into ordinary- -- identifiers, use whatever is in scope rather than the "Prelude"- -- -- version.- --- -- * <https://haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#rebindable-syntax-and-the-implicit-prelude-import>- | ImplicitPrelude-- -- | Enable syntax for implicitly binding local names corresponding- -- to the field names of a record. Puns bind specific names, unlike- -- 'RecordWildCards'.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XNamedFieldPuns>- | NamedFieldPuns-- -- | Enable a form of guard which matches a pattern and binds- -- variables.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XPatternGuards>- | PatternGuards-- -- | Allow a type declared with @newtype@ to use @deriving@ for any- -- class with an instance for the underlying type.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XGeneralizedNewtypeDeriving>- | GeneralizedNewtypeDeriving-- -- Synonym for GeneralizedNewtypeDeriving added in GHC 8.6.1.- | GeneralisedNewtypeDeriving-- -- | Enable the \"Trex\" extensible records system.- --- -- * <http://haskell.org/hugs/pages/users_guide/hugs-only.html#TREX>- | ExtensibleRecords-- -- | Enable type synonyms which are transparent in some definitions- -- and opaque elsewhere, as a way of implementing abstract- -- datatypes.- --- -- * <http://haskell.org/hugs/pages/users_guide/restricted-synonyms.html>- | RestrictedTypeSynonyms-- -- | Enable an alternate syntax for string literals,- -- with string templating.- --- -- * <http://haskell.org/hugs/pages/users_guide/here-documents.html>- | HereDocuments-- -- | Allow the character @#@ as a postfix modifier on identifiers.- -- Also enables literal syntax for unboxed values.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XMagicHash>- | MagicHash-- -- | Allow data types and type synonyms which are indexed by types,- -- i.e. ad-hoc polymorphism for types.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XTypeFamilies>- | TypeFamilies-- -- | Allow a standalone declaration which invokes the type class- -- @deriving@ mechanism.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XStandaloneDeriving>- | StandaloneDeriving-- -- | Allow certain Unicode characters to stand for certain ASCII- -- character sequences, e.g. keywords and punctuation.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XUnicodeSyntax>- | UnicodeSyntax-- -- | Allow the use of unboxed types as foreign types, e.g. in- -- @foreign import@ and @foreign export@.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#language-options>- | UnliftedFFITypes-- -- | Enable interruptible FFI.- --- -- * <https://haskell.org/ghc/docs/latest/html/users_guide/ffi-chap.html#interruptible-foreign-calls>- | InterruptibleFFI-- -- | Allow use of CAPI FFI calling convention (@foreign import capi@).- --- -- * <https://haskell.org/ghc/docs/latest/html/users_guide/ffi-chap.html#the-capi-calling-convention>- | CApiFFI-- -- | Defer validity checking of types until after expanding type- -- synonyms, relaxing the constraints on how synonyms may be used.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XLiberalTypeSynonyms>- | LiberalTypeSynonyms-- -- | Allow the name of a type constructor, type class, or type- -- variable to be an infix operator.- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XTypeOperators>- | TypeOperators-- -- | Enable syntax for implicitly binding local names corresponding- -- to the field names of a record. A wildcard binds all unmentioned- -- names, unlike 'NamedFieldPuns'.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XRecordWildCards>- | RecordWildCards-- -- | Deprecated, use 'NamedFieldPuns' instead.- | RecordPuns-- -- | Allow a record field name to be disambiguated by the type of- -- the record it's in.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XDisambiguateRecordFields>- | DisambiguateRecordFields-- -- | Enable traditional record syntax (as supported by Haskell 98)- --- -- * <https://haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#traditional-record-syntax>- | TraditionalRecordSyntax-- -- | Enable overloading of string literals using a type class, much- -- like integer literals.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedStrings>- | OverloadedStrings-- -- | Enable generalized algebraic data types, in which type- -- variables may be instantiated on a per-constructor basis. Implies- -- 'GADTSyntax'.- --- -- * <https://haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#generalised-algebraic-data-types-gadts>- | GADTs-- -- | Enable GADT syntax for declaring ordinary algebraic datatypes.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XGADTSyntax>- | GADTSyntax-- -- | /(deprecated)/ Has no effect.- --- -- Old description: Make pattern bindings monomorphic.- --- -- * <https://downloads.haskell.org/~ghc/7.6.3/docs/html/users_guide/monomorphism.html>- | MonoPatBinds-- -- | Relax the requirements on mutually-recursive polymorphic- -- functions.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XRelaxedPolyRec>- | RelaxedPolyRec-- -- | Allow default instantiation of polymorphic types in more- -- situations.- --- -- * <http://downloads.haskell.org/~ghc/latest/docs/html/users_guide/ghci.html#type-defaulting-in-ghci>- | ExtendedDefaultRules-- -- | Enable unboxed tuples.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XUnboxedTuples>- | UnboxedTuples-- -- | Enable @deriving@ for classes 'Data.Typeable.Typeable' and- -- 'Data.Generics.Data'.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveDataTypeable>- | DeriveDataTypeable-- -- | Enable @deriving@ for 'GHC.Generics.Generic' and 'GHC.Generics.Generic1'.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveGeneric>- | DeriveGeneric-- -- | Enable support for default signatures.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XDefaultSignatures>- | DefaultSignatures-- -- | Allow type signatures to be specified in instance declarations.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XInstanceSigs>- | InstanceSigs-- -- | Allow a class method's type to place additional constraints on- -- a class type variable.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XConstrainedClassMethods>- | ConstrainedClassMethods-- -- | Allow imports to be qualified by the package name the module is- -- intended to be imported from, e.g.- --- -- > import "network" Network.Socket- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XPackageImports>- | PackageImports-- -- | /(deprecated)/ Allow a type variable to be instantiated at a- -- polymorphic type.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XImpredicativeTypes>- | ImpredicativeTypes-- -- | /(deprecated)/ Change the syntax for qualified infix operators.- --- -- * <http://www.haskell.org/ghc/docs/6.12.3/html/users_guide/syntax-extns.html#new-qualified-operators>- | NewQualifiedOperators-- -- | Relax the interpretation of left operator sections to allow- -- unary postfix operators.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XPostfixOperators>- | PostfixOperators-- -- | Enable quasi-quotation, a mechanism for defining new concrete- -- syntax for expressions and patterns.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XQuasiQuotes>- | QuasiQuotes-- -- | Enable generalized list comprehensions, supporting operations- -- such as sorting and grouping.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XTransformListComp>- | TransformListComp-- -- | Enable monad comprehensions, which generalise the list- -- comprehension syntax to work for any monad.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XMonadComprehensions>- | MonadComprehensions-- -- | Enable view patterns, which match a value by applying a- -- function and matching on the result.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XViewPatterns>- | ViewPatterns-- -- | Allow concrete XML syntax to be used in expressions and patterns,- -- as per the Haskell Server Pages extension language:- -- <http://www.haskell.org/haskellwiki/HSP>. The ideas behind it are- -- discussed in the paper \"Haskell Server Pages through Dynamic Loading\"- -- by Niklas Broberg, from Haskell Workshop '05.- | XmlSyntax-- -- | Allow regular pattern matching over lists, as discussed in the- -- paper \"Regular Expression Patterns\" by Niklas Broberg, Andreas Farre- -- and Josef Svenningsson, from ICFP '04.- | RegularPatterns-- -- | Enable the use of tuple sections, e.g. @(, True)@ desugars into- -- @\x -> (x, True)@.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XTupleSections>- | TupleSections-- -- | Allow GHC primops, written in C--, to be imported into a Haskell- -- file.- | GHCForeignImportPrim-- -- | Support for patterns of the form @n + k@, where @k@ is an- -- integer literal.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XNPlusKPatterns>- | NPlusKPatterns-- -- | Improve the layout rule when @if@ expressions are used in a @do@- -- block.- | DoAndIfThenElse-- -- | Enable support for multi-way @if@-expressions.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XMultiWayIf>- | MultiWayIf-- -- | Enable support lambda-@case@ expressions.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XLambdaCase>- | LambdaCase-- -- | Makes much of the Haskell sugar be desugared into calls to the- -- function with a particular name that is in scope.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XRebindableSyntax>- | RebindableSyntax-- -- | Make @forall@ a keyword in types, which can be used to give the- -- generalisation explicitly.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XExplicitForAll>- | ExplicitForAll-- -- | Allow contexts to be put on datatypes, e.g. the @Eq a@ in- -- @data Eq a => Set a = NilSet | ConsSet a (Set a)@.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XDatatypeContexts>- | DatatypeContexts-- -- | Local (@let@ and @where@) bindings are monomorphic.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XMonoLocalBinds>- | MonoLocalBinds-- -- | Enable @deriving@ for the 'Data.Functor.Functor' class.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFunctor>- | DeriveFunctor-- -- | Enable @deriving@ for the 'Data.Traversable.Traversable' class.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveTraversable>- | DeriveTraversable-- -- | Enable @deriving@ for the 'Data.Foldable.Foldable' class.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveFoldable>- | DeriveFoldable-- -- | Enable non-decreasing indentation for @do@ blocks.- --- -- * <https://haskell.org/ghc/docs/latest/html/users_guide/bugs.html#context-free-syntax>- | NondecreasingIndentation-- -- | Allow imports to be qualified with a safe keyword that requires- -- the imported module be trusted as according to the Safe Haskell- -- definition of trust.- --- -- > import safe Network.Socket- --- -- * <https://haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#safe-imports>- | SafeImports-- -- | Compile a module in the Safe, Safe Haskell mode -- a restricted- -- form of the Haskell language to ensure type safety.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/safe_haskell.html#ghc-flag--XSafe>- | Safe-- -- | Compile a module in the Trustworthy, Safe Haskell mode -- no- -- restrictions apply but the module is marked as trusted as long as- -- the package the module resides in is trusted.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/safe_haskell.html#ghc-flag--XTrustworthy>- | Trustworthy-- -- | Compile a module in the Unsafe, Safe Haskell mode so that- -- modules compiled using Safe, Safe Haskell mode can't import it.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/safe_haskell.html#ghc-flag--XUnsafe>- | Unsafe-- -- | Allow type class/implicit parameter/equality constraints to be- -- used as types with the special kind constraint. Also generalise- -- the @(ctxt => ty)@ syntax so that any type of kind constraint can- -- occur before the arrow.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XConstraintKinds>- | ConstraintKinds-- -- | Enable kind polymorphism.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XPolyKinds>- | PolyKinds-- -- | Enable datatype promotion.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XDataKinds>- | DataKinds-- -- | Enable parallel arrays syntax (@[:@, @:]@) for /Data Parallel Haskell/.- --- -- * <http://www.haskell.org/haskellwiki/GHC/Data_Parallel_Haskell>- | ParallelArrays-- -- | Enable explicit role annotations, like in (@type role Foo representational representational@).- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XRoleAnnotations>- | RoleAnnotations-- -- | Enable overloading of list literals, arithmetic sequences and- -- list patterns using the 'IsList' type class.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XOverloadedLists>- | OverloadedLists-- -- | Enable case expressions that have no alternatives. Also applies to lambda-case expressions if they are enabled.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XEmptyCase>- | EmptyCase-- -- | /(deprecated)/ Deprecated in favour of 'DeriveDataTypeable'.- --- -- Old description: Triggers the generation of derived 'Typeable'- -- instances for every datatype and type class declaration.- --- -- * <https://haskell.org/ghc/docs/7.8.4/html/users_guide/deriving.html#auto-derive-typeable>- | AutoDeriveTypeable-- -- | Desugars negative literals directly (without using negate).- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XNegativeLiterals>- | NegativeLiterals-- -- | Allow the use of binary integer literal syntax (e.g. @0b11001001@ to denote @201@).- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XBinaryLiterals>- | BinaryLiterals-- -- | Allow the use of floating literal syntax for all instances of 'Num', including 'Int' and 'Integer'.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XNumDecimals>- | NumDecimals-- -- | Enable support for type classes with no type parameter.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XNullaryTypeClasses>- | NullaryTypeClasses-- -- | Enable explicit namespaces in module import/export lists.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XExplicitNamespaces>- | ExplicitNamespaces-- -- | Allow the user to write ambiguous types, and the type inference engine to infer them.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XAllowAmbiguousTypes>- | AllowAmbiguousTypes-- -- | Enable @foreign import javascript@.- | JavaScriptFFI-- -- | Allow giving names to and abstracting over patterns.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XPatternSynonyms>- | PatternSynonyms-- -- | Allow anonymous placeholders (underscore) inside type signatures. The- -- type inference engine will generate a message describing the type inferred- -- at the hole's location.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XPartialTypeSignatures>- | PartialTypeSignatures-- -- | Allow named placeholders written with a leading underscore inside type- -- signatures. Wildcards with the same name unify to the same type.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XNamedWildCards>- | NamedWildCards-- -- | Enable @deriving@ for any class.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveAnyClass>- | DeriveAnyClass-- -- | Enable @deriving@ for the 'Language.Haskell.TH.Syntax.Lift' class.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XDeriveLift>- | DeriveLift-- -- | Enable support for 'static pointers' (and the @static@- -- keyword) to refer to globally stable names, even across- -- different programs.- --- -- * <https://www.haskell.org/ghc/docs/latest/html/users_guide/glasgow_exts.html#ghc-flag--XStaticPointers>- | StaticPointers-- -- | Switches data type declarations to be strict by default (as if- -- they had a bang using @BangPatterns@), and allow opt-in field- -- laziness using @~@.- --- -- * <https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#ghc-flag--XStrictData>- | StrictData-- -- | Switches all pattern bindings to be strict by default (as if- -- they had a bang using @BangPatterns@), ordinary patterns are- -- recovered using @~@. Implies @StrictData@.- --- -- * <https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#ghc-flag--XStrict>- | Strict-- -- | Allows @do@-notation for types that are @'Applicative'@ as well- -- as @'Monad'@. When enabled, desugaring @do@ notation tries to use- -- @(<*>)@ and @'fmap'@ and @'join'@ as far as possible.- | ApplicativeDo-- -- | Allow records to use duplicated field labels for accessors.- | DuplicateRecordFields-- -- | Enable explicit type applications with the syntax @id \@Int@.- | TypeApplications-- -- | Dissolve the distinction between types and kinds, allowing the compiler- -- to reason about kind equality and therefore enabling GADTs to be promoted- -- to the type-level.- | TypeInType-- -- | Allow recursive (and therefore undecidable) super-class relationships.- | UndecidableSuperClasses-- -- | A temporary extension to help library authors check if their- -- code will compile with the new planned desugaring of fail.- | MonadFailDesugaring-- -- | A subset of @TemplateHaskell@ including only quoting.- | TemplateHaskellQuotes-- -- | Allows use of the @#label@ syntax.- | OverloadedLabels-- -- | Allow functional dependency annotations on type families to declare them- -- as injective.- | TypeFamilyDependencies-- -- | Allow multiple @deriving@ clauses, each optionally qualified with a- -- /strategy/.- | DerivingStrategies-- -- | Enable deriving instances via types of the same runtime representation.- -- Implies 'DerivingStrategies'.- | DerivingVia-- -- | Enable the use of unboxed sum syntax.- | UnboxedSums-- -- | Allow use of hexadecimal literal notation for floating-point values.- | HexFloatLiterals-- -- | Allow @do@ blocks etc. in argument position.- | BlockArguments-- -- | Allow use of underscores in numeric literals.- | NumericUnderscores-- -- | Allow @forall@ in constraints.- | QuantifiedConstraints-- -- | Have @*@ refer to @Type@.- | StarIsType-- -- | Liberalises deriving to provide instances for empty data types.- --- -- * <https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html#deriving-instances-for-empty-data-types>- | EmptyDataDeriving-- -- | Enable detection of complete user-supplied kind signatures.- | CUSKs-- -- | Allows the syntax @import M qualified@.- | ImportQualifiedPost-- -- | Allow the use of standalone kind signatures.- | StandaloneKindSignatures-- -- | Enable unlifted newtypes.- | UnliftedNewtypes-- -- | Use whitespace to determine whether the minus sign stands for negation or subtraction.- | LexicalNegation-- -- | Enable qualified do-notation desugaring.- | QualifiedDo-- -- | Enable linear types.- | LinearTypes-- -- | Enable the generation of selector functions corresponding to record fields.- | FieldSelectors-- -- | Enable the use of record dot-accessor and updater syntax- | OverloadedRecordDot-- -- | Enable data types for which an unlifted or levity-polymorphic result kind is inferred.- | UnliftedDatatypes-- deriving (Generic, Show, Read, Eq, Ord, Enum, Bounded, Typeable, Data)--instance Binary KnownExtension-instance Structured KnownExtension--instance NFData KnownExtension where rnf = genericRnf---- | Extensions that have been deprecated, possibly paired with another--- extension that replaces it.----deprecatedExtensions :: [(Extension, Maybe Extension)]-deprecatedExtensions =- [ (EnableExtension RecordPuns, Just (EnableExtension NamedFieldPuns))- , (EnableExtension PatternSignatures, Just (EnableExtension ScopedTypeVariables))- ]--- NOTE: when adding deprecated extensions that have new alternatives--- we must be careful to make sure that the deprecation messages are--- valid. We must not recommend aliases that cannot be used with older--- compilers, perhaps by adding support in Cabal to translate the new--- name to the old one for older compilers. Otherwise we are in danger--- of the scenario in ticket #689.--instance Pretty Extension where- pretty (UnknownExtension other) = Disp.text other- pretty (EnableExtension ke) = Disp.text (show ke)- pretty (DisableExtension ke) = Disp.text ("No" ++ show ke)--instance Parsec Extension where- parsec = classifyExtension <$> P.munch1 isAlphaNum--instance Pretty KnownExtension where- pretty ke = Disp.text (show ke)--classifyExtension :: String -> Extension-classifyExtension string- = case classifyKnownExtension string of- Just ext -> EnableExtension ext- Nothing ->- case string of- 'N':'o':string' ->- case classifyKnownExtension string' of- Just ext -> DisableExtension ext- Nothing -> UnknownExtension string- _ -> UnknownExtension string---- | 'read' for 'KnownExtension's is really really slow so for the Text--- instance--- what we do is make a simple table indexed off the first letter in the--- extension name. The extension names actually cover the range @'A'-'Z'@--- pretty densely and the biggest bucket is 7 so it's not too bad. We just do--- a linear search within each bucket.------ This gives an order of magnitude improvement in parsing speed, and it'll--- also allow us to do case insensitive matches in future if we prefer.----classifyKnownExtension :: String -> Maybe KnownExtension-classifyKnownExtension "" = Nothing-classifyKnownExtension string@(c : _)- | inRange (bounds knownExtensionTable) c- = lookup string (knownExtensionTable ! c)- | otherwise = Nothing--knownExtensionTable :: Array Char [(String, KnownExtension)]-knownExtensionTable =- accumArray (flip (:)) [] ('A', 'Z')- [ (Prelude.head str, (str, extension)) -- assume KnownExtension's Show returns a non-empty string- | extension <- [toEnum 0 ..]- , let str = show extension ]