Cabal 3.10.3.0 → 3.12.0.0
raw patch · 148 files changed
+36982/−27637 lines, 148 filesdep −textdep ~Cabal-syntaxdep ~timesetup-changednew-uploaderPVP ok
version bump matches the API change (PVP)
Dependencies removed: text
Dependency ranges changed: Cabal-syntax, time
API changes (from Hackage documentation)
- Distribution.PackageDescription.Check: BadRelativePAth :: String -> FilePath -> String -> CheckExplanation
- Distribution.PackageDescription.Check: MissingField :: CEField -> CheckExplanation
- Distribution.PackageDescription.Check: OptViaC :: String -> CheckExplanation
- Distribution.PackageDescription.Check: checkPackageFileNames :: [FilePath] -> [PackageCheck]
- Distribution.PackageDescription.Check: instance GHC.Classes.Eq Distribution.PackageDescription.Check.CEField
- Distribution.PackageDescription.Check: instance GHC.Classes.Eq Distribution.PackageDescription.Check.CEType
- Distribution.PackageDescription.Check: instance GHC.Classes.Eq Distribution.PackageDescription.Check.CheckExplanation
- Distribution.PackageDescription.Check: instance GHC.Classes.Eq Distribution.PackageDescription.Check.PackageCheck
- Distribution.PackageDescription.Check: instance GHC.Classes.Eq Distribution.PackageDescription.Check.PathKind
- Distribution.PackageDescription.Check: instance GHC.Classes.Ord Distribution.PackageDescription.Check.CEField
- Distribution.PackageDescription.Check: instance GHC.Classes.Ord Distribution.PackageDescription.Check.CEType
- Distribution.PackageDescription.Check: instance GHC.Classes.Ord Distribution.PackageDescription.Check.CheckExplanation
- Distribution.PackageDescription.Check: instance GHC.Classes.Ord Distribution.PackageDescription.Check.PackageCheck
- Distribution.PackageDescription.Check: instance GHC.Show.Show Distribution.PackageDescription.Check.CEField
- Distribution.PackageDescription.Check: instance GHC.Show.Show Distribution.PackageDescription.Check.CEType
- Distribution.PackageDescription.Check: instance GHC.Show.Show Distribution.PackageDescription.Check.CheckExplanation
- Distribution.PackageDescription.Check: instance GHC.Show.Show Distribution.PackageDescription.Check.PackageCheck
- Distribution.Simple.Build: componentInitialBuildSteps :: FilePath -> PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> Verbosity -> IO ()
- Distribution.Simple.Build: initialBuildSteps :: FilePath -> PackageDescription -> LocalBuildInfo -> Verbosity -> IO ()
- Distribution.Simple.Glob: instance GHC.Base.Functor Distribution.Simple.Glob.GlobResult
- Distribution.Simple.Glob: instance GHC.Classes.Eq Distribution.Simple.Glob.GlobSyntaxError
- Distribution.Simple.Glob: instance GHC.Classes.Eq Distribution.Simple.Glob.IsRecursive
- Distribution.Simple.Glob: instance GHC.Classes.Eq a => GHC.Classes.Eq (Distribution.Simple.Glob.GlobResult a)
- Distribution.Simple.Glob: instance GHC.Classes.Ord a => GHC.Classes.Ord (Distribution.Simple.Glob.GlobResult a)
- Distribution.Simple.Glob: instance GHC.Show.Show Distribution.Simple.Glob.GlobSyntaxError
- Distribution.Simple.Glob: instance GHC.Show.Show a => GHC.Show.Show (Distribution.Simple.Glob.GlobResult a)
- Distribution.Simple.Hpc: markupTest :: Verbosity -> LocalBuildInfo -> FilePath -> String -> TestSuite -> Library -> IO ()
- Distribution.Simple.LocalBuildInfo: LocalBuildInfo :: ConfigFlags -> FlagAssignment -> ComponentRequestedSpec -> [String] -> InstallDirTemplates -> Compiler -> Platform -> FilePath -> Maybe FilePath -> Graph ComponentLocalBuildInfo -> Map ComponentName [ComponentLocalBuildInfo] -> InstalledPackageIndex -> Maybe FilePath -> PackageDescription -> ProgramDb -> PackageDBStack -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> ProfDetailLevel -> ProfDetailLevel -> OptimisationLevel -> DebugInfoLevel -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> PathTemplate -> PathTemplate -> Bool -> LocalBuildInfo
- Distribution.Simple.LocalBuildInfo: [buildDir] :: LocalBuildInfo -> FilePath
- Distribution.Simple.LocalBuildInfo: [cabalFilePath] :: LocalBuildInfo -> Maybe FilePath
- Distribution.Simple.LocalBuildInfo: [compiler] :: LocalBuildInfo -> Compiler
- Distribution.Simple.LocalBuildInfo: [componentEnabledSpec] :: LocalBuildInfo -> ComponentRequestedSpec
- Distribution.Simple.LocalBuildInfo: [componentGraph] :: LocalBuildInfo -> Graph ComponentLocalBuildInfo
- Distribution.Simple.LocalBuildInfo: [componentNameMap] :: LocalBuildInfo -> Map ComponentName [ComponentLocalBuildInfo]
- Distribution.Simple.LocalBuildInfo: [configFlags] :: LocalBuildInfo -> ConfigFlags
- Distribution.Simple.LocalBuildInfo: [exeCoverage] :: LocalBuildInfo -> Bool
- Distribution.Simple.LocalBuildInfo: [extraConfigArgs] :: LocalBuildInfo -> [String]
- Distribution.Simple.LocalBuildInfo: [flagAssignment] :: LocalBuildInfo -> FlagAssignment
- Distribution.Simple.LocalBuildInfo: [hostPlatform] :: LocalBuildInfo -> Platform
- Distribution.Simple.LocalBuildInfo: [installDirTemplates] :: LocalBuildInfo -> InstallDirTemplates
- Distribution.Simple.LocalBuildInfo: [installedPkgs] :: LocalBuildInfo -> InstalledPackageIndex
- Distribution.Simple.LocalBuildInfo: [libCoverage] :: LocalBuildInfo -> Bool
- Distribution.Simple.LocalBuildInfo: [localPkgDescr] :: LocalBuildInfo -> PackageDescription
- Distribution.Simple.LocalBuildInfo: [pkgDescrFile] :: LocalBuildInfo -> Maybe FilePath
- Distribution.Simple.LocalBuildInfo: [progPrefix] :: LocalBuildInfo -> PathTemplate
- Distribution.Simple.LocalBuildInfo: [progSuffix] :: LocalBuildInfo -> PathTemplate
- Distribution.Simple.LocalBuildInfo: [relocatable] :: LocalBuildInfo -> Bool
- Distribution.Simple.LocalBuildInfo: [splitObjs] :: LocalBuildInfo -> Bool
- Distribution.Simple.LocalBuildInfo: [splitSections] :: LocalBuildInfo -> Bool
- Distribution.Simple.LocalBuildInfo: [stripExes] :: LocalBuildInfo -> Bool
- Distribution.Simple.LocalBuildInfo: [stripLibs] :: LocalBuildInfo -> Bool
- Distribution.Simple.LocalBuildInfo: [withDebugInfo] :: LocalBuildInfo -> DebugInfoLevel
- Distribution.Simple.LocalBuildInfo: [withDynExe] :: LocalBuildInfo -> Bool
- Distribution.Simple.LocalBuildInfo: [withFullyStaticExe] :: LocalBuildInfo -> Bool
- Distribution.Simple.LocalBuildInfo: [withGHCiLib] :: LocalBuildInfo -> Bool
- Distribution.Simple.LocalBuildInfo: [withOptimization] :: LocalBuildInfo -> OptimisationLevel
- Distribution.Simple.LocalBuildInfo: [withPackageDB] :: LocalBuildInfo -> PackageDBStack
- Distribution.Simple.LocalBuildInfo: [withProfExeDetail] :: LocalBuildInfo -> ProfDetailLevel
- Distribution.Simple.LocalBuildInfo: [withProfExe] :: LocalBuildInfo -> Bool
- Distribution.Simple.LocalBuildInfo: [withProfLibDetail] :: LocalBuildInfo -> ProfDetailLevel
- Distribution.Simple.LocalBuildInfo: [withProfLib] :: LocalBuildInfo -> Bool
- Distribution.Simple.LocalBuildInfo: [withPrograms] :: LocalBuildInfo -> ProgramDb
- Distribution.Simple.LocalBuildInfo: [withSharedLib] :: LocalBuildInfo -> Bool
- Distribution.Simple.LocalBuildInfo: [withStaticLib] :: LocalBuildInfo -> Bool
- Distribution.Simple.LocalBuildInfo: [withVanillaLib] :: LocalBuildInfo -> Bool
- Distribution.Simple.Program.Db: appendProgramSearchPath :: Verbosity -> [FilePath] -> ProgramDb -> IO ProgramDb
- Distribution.Simple.Setup: [haddockProjectGenContents] :: HaddockProjectFlags -> Flag Bool
- Distribution.Simple.Setup: [haddockProjectGenIndex] :: HaddockProjectFlags -> Flag Bool
- Distribution.Simple.Setup: [haddockProjectLinkedSource] :: HaddockProjectFlags -> Flag Bool
- Distribution.Simple.Setup: [haddockProjectLocal] :: HaddockProjectFlags -> Flag Bool
- Distribution.Simple.Setup: [haddockProjectQuickJump] :: HaddockProjectFlags -> Flag Bool
- Distribution.Simple.Setup: instance Data.Binary.Class.Binary Distribution.Simple.Setup.ConfigFlags
- Distribution.Simple.Setup: instance Data.Binary.Class.Binary Distribution.Simple.Setup.HaddockTarget
- Distribution.Simple.Setup: instance Data.Binary.Class.Binary Distribution.Simple.Setup.ReplOptions
- Distribution.Simple.Setup: instance Data.Binary.Class.Binary Distribution.Simple.Setup.TestShowDetails
- Distribution.Simple.Setup: instance Distribution.Parsec.Parsec Distribution.Simple.Setup.HaddockTarget
- Distribution.Simple.Setup: instance Distribution.Parsec.Parsec Distribution.Simple.Setup.TestShowDetails
- Distribution.Simple.Setup: instance Distribution.Pretty.Pretty Distribution.Simple.Setup.HaddockTarget
- Distribution.Simple.Setup: instance Distribution.Pretty.Pretty Distribution.Simple.Setup.TestShowDetails
- Distribution.Simple.Setup: instance Distribution.Utils.Structured.Structured Distribution.Simple.Setup.ConfigFlags
- Distribution.Simple.Setup: instance Distribution.Utils.Structured.Structured Distribution.Simple.Setup.HaddockTarget
- Distribution.Simple.Setup: instance Distribution.Utils.Structured.Structured Distribution.Simple.Setup.ReplOptions
- Distribution.Simple.Setup: instance Distribution.Utils.Structured.Structured Distribution.Simple.Setup.TestShowDetails
- Distribution.Simple.Setup: instance GHC.Base.Monoid Distribution.Simple.Setup.BenchmarkFlags
- Distribution.Simple.Setup: instance GHC.Base.Monoid Distribution.Simple.Setup.BuildFlags
- Distribution.Simple.Setup: instance GHC.Base.Monoid Distribution.Simple.Setup.CleanFlags
- Distribution.Simple.Setup: instance GHC.Base.Monoid Distribution.Simple.Setup.ConfigFlags
- Distribution.Simple.Setup: instance GHC.Base.Monoid Distribution.Simple.Setup.CopyFlags
- Distribution.Simple.Setup: instance GHC.Base.Monoid Distribution.Simple.Setup.GlobalFlags
- Distribution.Simple.Setup: instance GHC.Base.Monoid Distribution.Simple.Setup.HaddockFlags
- Distribution.Simple.Setup: instance GHC.Base.Monoid Distribution.Simple.Setup.HaddockProjectFlags
- Distribution.Simple.Setup: instance GHC.Base.Monoid Distribution.Simple.Setup.HscolourFlags
- Distribution.Simple.Setup: instance GHC.Base.Monoid Distribution.Simple.Setup.InstallFlags
- Distribution.Simple.Setup: instance GHC.Base.Monoid Distribution.Simple.Setup.RegisterFlags
- Distribution.Simple.Setup: instance GHC.Base.Monoid Distribution.Simple.Setup.ReplFlags
- Distribution.Simple.Setup: instance GHC.Base.Monoid Distribution.Simple.Setup.ReplOptions
- Distribution.Simple.Setup: instance GHC.Base.Monoid Distribution.Simple.Setup.SDistFlags
- Distribution.Simple.Setup: instance GHC.Base.Monoid Distribution.Simple.Setup.TestFlags
- Distribution.Simple.Setup: instance GHC.Base.Monoid Distribution.Simple.Setup.TestShowDetails
- Distribution.Simple.Setup: instance GHC.Base.Semigroup Distribution.Simple.Setup.BenchmarkFlags
- Distribution.Simple.Setup: instance GHC.Base.Semigroup Distribution.Simple.Setup.BuildFlags
- Distribution.Simple.Setup: instance GHC.Base.Semigroup Distribution.Simple.Setup.CleanFlags
- Distribution.Simple.Setup: instance GHC.Base.Semigroup Distribution.Simple.Setup.ConfigFlags
- Distribution.Simple.Setup: instance GHC.Base.Semigroup Distribution.Simple.Setup.CopyFlags
- Distribution.Simple.Setup: instance GHC.Base.Semigroup Distribution.Simple.Setup.GlobalFlags
- Distribution.Simple.Setup: instance GHC.Base.Semigroup Distribution.Simple.Setup.HaddockFlags
- Distribution.Simple.Setup: instance GHC.Base.Semigroup Distribution.Simple.Setup.HaddockProjectFlags
- Distribution.Simple.Setup: instance GHC.Base.Semigroup Distribution.Simple.Setup.HscolourFlags
- Distribution.Simple.Setup: instance GHC.Base.Semigroup Distribution.Simple.Setup.InstallFlags
- Distribution.Simple.Setup: instance GHC.Base.Semigroup Distribution.Simple.Setup.RegisterFlags
- Distribution.Simple.Setup: instance GHC.Base.Semigroup Distribution.Simple.Setup.ReplFlags
- Distribution.Simple.Setup: instance GHC.Base.Semigroup Distribution.Simple.Setup.ReplOptions
- Distribution.Simple.Setup: instance GHC.Base.Semigroup Distribution.Simple.Setup.SDistFlags
- Distribution.Simple.Setup: instance GHC.Base.Semigroup Distribution.Simple.Setup.TestFlags
- Distribution.Simple.Setup: instance GHC.Base.Semigroup Distribution.Simple.Setup.TestShowDetails
- Distribution.Simple.Setup: instance GHC.Classes.Eq Distribution.Simple.Setup.ConfigFlags
- Distribution.Simple.Setup: instance GHC.Classes.Eq Distribution.Simple.Setup.HaddockTarget
- Distribution.Simple.Setup: instance GHC.Classes.Eq Distribution.Simple.Setup.TestShowDetails
- Distribution.Simple.Setup: instance GHC.Classes.Eq Distribution.Simple.Setup.Visibility
- Distribution.Simple.Setup: instance GHC.Classes.Ord Distribution.Simple.Setup.TestShowDetails
- Distribution.Simple.Setup: instance GHC.Enum.Bounded Distribution.Simple.Setup.TestShowDetails
- Distribution.Simple.Setup: instance GHC.Enum.Enum Distribution.Simple.Setup.TestShowDetails
- Distribution.Simple.Setup: instance GHC.Generics.Generic Distribution.Simple.Setup.BenchmarkFlags
- Distribution.Simple.Setup: instance GHC.Generics.Generic Distribution.Simple.Setup.BuildFlags
- Distribution.Simple.Setup: instance GHC.Generics.Generic Distribution.Simple.Setup.CleanFlags
- Distribution.Simple.Setup: instance GHC.Generics.Generic Distribution.Simple.Setup.ConfigFlags
- Distribution.Simple.Setup: instance GHC.Generics.Generic Distribution.Simple.Setup.CopyFlags
- Distribution.Simple.Setup: instance GHC.Generics.Generic Distribution.Simple.Setup.GlobalFlags
- Distribution.Simple.Setup: instance GHC.Generics.Generic Distribution.Simple.Setup.HaddockFlags
- Distribution.Simple.Setup: instance GHC.Generics.Generic Distribution.Simple.Setup.HaddockProjectFlags
- Distribution.Simple.Setup: instance GHC.Generics.Generic Distribution.Simple.Setup.HaddockTarget
- Distribution.Simple.Setup: instance GHC.Generics.Generic Distribution.Simple.Setup.HscolourFlags
- Distribution.Simple.Setup: instance GHC.Generics.Generic Distribution.Simple.Setup.InstallFlags
- Distribution.Simple.Setup: instance GHC.Generics.Generic Distribution.Simple.Setup.RegisterFlags
- Distribution.Simple.Setup: instance GHC.Generics.Generic Distribution.Simple.Setup.ReplFlags
- Distribution.Simple.Setup: instance GHC.Generics.Generic Distribution.Simple.Setup.ReplOptions
- Distribution.Simple.Setup: instance GHC.Generics.Generic Distribution.Simple.Setup.SDistFlags
- Distribution.Simple.Setup: instance GHC.Generics.Generic Distribution.Simple.Setup.TestFlags
- Distribution.Simple.Setup: instance GHC.Generics.Generic Distribution.Simple.Setup.TestShowDetails
- Distribution.Simple.Setup: instance GHC.Read.Read Distribution.Simple.Setup.BuildFlags
- Distribution.Simple.Setup: instance GHC.Read.Read Distribution.Simple.Setup.ConfigFlags
- Distribution.Simple.Setup: instance GHC.Show.Show Distribution.Simple.Setup.BenchmarkFlags
- Distribution.Simple.Setup: instance GHC.Show.Show Distribution.Simple.Setup.BuildFlags
- Distribution.Simple.Setup: instance GHC.Show.Show Distribution.Simple.Setup.CleanFlags
- Distribution.Simple.Setup: instance GHC.Show.Show Distribution.Simple.Setup.ConfigFlags
- Distribution.Simple.Setup: instance GHC.Show.Show Distribution.Simple.Setup.CopyFlags
- Distribution.Simple.Setup: instance GHC.Show.Show Distribution.Simple.Setup.HaddockFlags
- Distribution.Simple.Setup: instance GHC.Show.Show Distribution.Simple.Setup.HaddockProjectFlags
- Distribution.Simple.Setup: instance GHC.Show.Show Distribution.Simple.Setup.HaddockTarget
- Distribution.Simple.Setup: instance GHC.Show.Show Distribution.Simple.Setup.HscolourFlags
- Distribution.Simple.Setup: instance GHC.Show.Show Distribution.Simple.Setup.InstallFlags
- Distribution.Simple.Setup: instance GHC.Show.Show Distribution.Simple.Setup.RegisterFlags
- Distribution.Simple.Setup: instance GHC.Show.Show Distribution.Simple.Setup.ReplFlags
- Distribution.Simple.Setup: instance GHC.Show.Show Distribution.Simple.Setup.ReplOptions
- Distribution.Simple.Setup: instance GHC.Show.Show Distribution.Simple.Setup.SDistFlags
- Distribution.Simple.Setup: instance GHC.Show.Show Distribution.Simple.Setup.TestFlags
- Distribution.Simple.Setup: instance GHC.Show.Show Distribution.Simple.Setup.TestShowDetails
- Distribution.Simple.Setup: instance GHC.Show.Show Distribution.Simple.Setup.Visibility
- Distribution.Simple.Setup: optionNumJobs :: (flags -> Flag (Maybe Int)) -> (Flag (Maybe Int) -> flags -> flags) -> OptionField flags
- Distribution.Types.LocalBuildInfo: LocalBuildInfo :: ConfigFlags -> FlagAssignment -> ComponentRequestedSpec -> [String] -> InstallDirTemplates -> Compiler -> Platform -> FilePath -> Maybe FilePath -> Graph ComponentLocalBuildInfo -> Map ComponentName [ComponentLocalBuildInfo] -> InstalledPackageIndex -> Maybe FilePath -> PackageDescription -> ProgramDb -> PackageDBStack -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> ProfDetailLevel -> ProfDetailLevel -> OptimisationLevel -> DebugInfoLevel -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> PathTemplate -> PathTemplate -> Bool -> LocalBuildInfo
- Distribution.Types.LocalBuildInfo: [buildDir] :: LocalBuildInfo -> FilePath
- Distribution.Types.LocalBuildInfo: [cabalFilePath] :: LocalBuildInfo -> Maybe FilePath
- Distribution.Types.LocalBuildInfo: [compiler] :: LocalBuildInfo -> Compiler
- Distribution.Types.LocalBuildInfo: [componentEnabledSpec] :: LocalBuildInfo -> ComponentRequestedSpec
- Distribution.Types.LocalBuildInfo: [componentGraph] :: LocalBuildInfo -> Graph ComponentLocalBuildInfo
- Distribution.Types.LocalBuildInfo: [componentNameMap] :: LocalBuildInfo -> Map ComponentName [ComponentLocalBuildInfo]
- Distribution.Types.LocalBuildInfo: [configFlags] :: LocalBuildInfo -> ConfigFlags
- Distribution.Types.LocalBuildInfo: [exeCoverage] :: LocalBuildInfo -> Bool
- Distribution.Types.LocalBuildInfo: [extraConfigArgs] :: LocalBuildInfo -> [String]
- Distribution.Types.LocalBuildInfo: [flagAssignment] :: LocalBuildInfo -> FlagAssignment
- Distribution.Types.LocalBuildInfo: [hostPlatform] :: LocalBuildInfo -> Platform
- Distribution.Types.LocalBuildInfo: [installDirTemplates] :: LocalBuildInfo -> InstallDirTemplates
- Distribution.Types.LocalBuildInfo: [installedPkgs] :: LocalBuildInfo -> InstalledPackageIndex
- Distribution.Types.LocalBuildInfo: [libCoverage] :: LocalBuildInfo -> Bool
- Distribution.Types.LocalBuildInfo: [localPkgDescr] :: LocalBuildInfo -> PackageDescription
- Distribution.Types.LocalBuildInfo: [pkgDescrFile] :: LocalBuildInfo -> Maybe FilePath
- Distribution.Types.LocalBuildInfo: [progPrefix] :: LocalBuildInfo -> PathTemplate
- Distribution.Types.LocalBuildInfo: [progSuffix] :: LocalBuildInfo -> PathTemplate
- Distribution.Types.LocalBuildInfo: [relocatable] :: LocalBuildInfo -> Bool
- Distribution.Types.LocalBuildInfo: [splitObjs] :: LocalBuildInfo -> Bool
- Distribution.Types.LocalBuildInfo: [splitSections] :: LocalBuildInfo -> Bool
- Distribution.Types.LocalBuildInfo: [stripExes] :: LocalBuildInfo -> Bool
- Distribution.Types.LocalBuildInfo: [stripLibs] :: LocalBuildInfo -> Bool
- Distribution.Types.LocalBuildInfo: [withDebugInfo] :: LocalBuildInfo -> DebugInfoLevel
- Distribution.Types.LocalBuildInfo: [withDynExe] :: LocalBuildInfo -> Bool
- Distribution.Types.LocalBuildInfo: [withFullyStaticExe] :: LocalBuildInfo -> Bool
- Distribution.Types.LocalBuildInfo: [withGHCiLib] :: LocalBuildInfo -> Bool
- Distribution.Types.LocalBuildInfo: [withOptimization] :: LocalBuildInfo -> OptimisationLevel
- Distribution.Types.LocalBuildInfo: [withPackageDB] :: LocalBuildInfo -> PackageDBStack
- Distribution.Types.LocalBuildInfo: [withProfExeDetail] :: LocalBuildInfo -> ProfDetailLevel
- Distribution.Types.LocalBuildInfo: [withProfExe] :: LocalBuildInfo -> Bool
- Distribution.Types.LocalBuildInfo: [withProfLibDetail] :: LocalBuildInfo -> ProfDetailLevel
- Distribution.Types.LocalBuildInfo: [withProfLib] :: LocalBuildInfo -> Bool
- Distribution.Types.LocalBuildInfo: [withPrograms] :: LocalBuildInfo -> ProgramDb
- Distribution.Types.LocalBuildInfo: [withSharedLib] :: LocalBuildInfo -> Bool
- Distribution.Types.LocalBuildInfo: [withStaticLib] :: LocalBuildInfo -> Bool
- Distribution.Types.LocalBuildInfo: [withVanillaLib] :: LocalBuildInfo -> Bool
+ Distribution.Compat.ResponseFile: escapeArgs :: [String] -> String
+ Distribution.PackageDescription.Check: BadRelativePath :: String -> FilePath -> String -> CheckExplanation
+ Distribution.PackageDescription.Check: MissingFieldCategory :: CheckExplanation
+ Distribution.PackageDescription.Check: MissingFieldDescription :: CheckExplanation
+ Distribution.PackageDescription.Check: MissingFieldMaintainer :: CheckExplanation
+ Distribution.PackageDescription.Check: MissingFieldSynOrDesc :: CheckExplanation
+ Distribution.PackageDescription.Check: MissingFieldSynopsis :: CheckExplanation
+ Distribution.PackageDescription.Check: checkPackageFilesGPD :: Verbosity -> GenericPackageDescription -> FilePath -> IO [PackageCheck]
+ Distribution.PackageDescription.Check: data CheckExplanationID
+ Distribution.PackageDescription.Check: filterPackageChecksById :: [PackageCheck] -> [CheckExplanationID] -> [PackageCheck]
+ Distribution.PackageDescription.Check: filterPackageChecksByIdString :: [PackageCheck] -> [CheckExplanationIDString] -> ([PackageCheck], [CheckExplanationIDString])
+ Distribution.PackageDescription.Check: isHackageDistError :: PackageCheck -> Bool
+ Distribution.PackageDescription.Check: ppCheckExplanationId :: CheckExplanationID -> CheckExplanationIDString
+ Distribution.PackageDescription.Check: type CheckExplanationIDString = String
+ Distribution.Simple.Build: AutogenFile :: !ShortText -> AutogenFile
+ Distribution.Simple.Build: AutogenModule :: !ModuleName -> !Suffix -> AutogenFile
+ Distribution.Simple.Build: data AutogenFile
+ Distribution.Simple.Build: instance GHC.Classes.Eq Distribution.Simple.Build.AutogenFile
+ Distribution.Simple.Build: instance GHC.Classes.Ord Distribution.Simple.Build.AutogenFile
+ Distribution.Simple.Build: instance GHC.Show.Show Distribution.Simple.Build.AutogenFile
+ Distribution.Simple.Build: preBuildComponent :: Verbosity -> LocalBuildInfo -> TargetInfo -> IO ()
+ Distribution.Simple.Build: type AutogenFileContents = ByteString
+ Distribution.Simple.Build: writeBuiltinAutogenFiles :: Verbosity -> PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> IO ()
+ Distribution.Simple.Build.Inputs: BuildHaddock :: HaddockFlags -> BuildingWhat
+ Distribution.Simple.Build.Inputs: BuildHscolour :: HscolourFlags -> BuildingWhat
+ Distribution.Simple.Build.Inputs: BuildNormal :: BuildFlags -> BuildingWhat
+ Distribution.Simple.Build.Inputs: BuildRepl :: ReplFlags -> BuildingWhat
+ Distribution.Simple.Build.Inputs: NewLocalBuildInfo :: LocalBuildDescr -> LocalBuildConfig -> LocalBuildInfo
+ Distribution.Simple.Build.Inputs: PreBuildComponentInputs :: BuildingWhat -> LocalBuildInfo -> TargetInfo -> PreBuildComponentInputs
+ Distribution.Simple.Build.Inputs: TargetInfo :: ComponentLocalBuildInfo -> Component -> TargetInfo
+ Distribution.Simple.Build.Inputs: [$sel:localBuildConfig:NewLocalBuildInfo] :: LocalBuildInfo -> LocalBuildConfig
+ Distribution.Simple.Build.Inputs: [$sel:localBuildDescr:NewLocalBuildInfo] :: LocalBuildInfo -> LocalBuildDescr
+ Distribution.Simple.Build.Inputs: [buildingWhat] :: PreBuildComponentInputs -> BuildingWhat
+ Distribution.Simple.Build.Inputs: [localBuildInfo] :: PreBuildComponentInputs -> LocalBuildInfo
+ Distribution.Simple.Build.Inputs: [targetCLBI] :: TargetInfo -> ComponentLocalBuildInfo
+ Distribution.Simple.Build.Inputs: [targetComponent] :: TargetInfo -> Component
+ Distribution.Simple.Build.Inputs: [targetInfo] :: PreBuildComponentInputs -> TargetInfo
+ Distribution.Simple.Build.Inputs: buildBI :: PreBuildComponentInputs -> BuildInfo
+ Distribution.Simple.Build.Inputs: buildCLBI :: PreBuildComponentInputs -> ComponentLocalBuildInfo
+ Distribution.Simple.Build.Inputs: buildCompiler :: PreBuildComponentInputs -> Compiler
+ Distribution.Simple.Build.Inputs: buildComponent :: PreBuildComponentInputs -> Component
+ Distribution.Simple.Build.Inputs: buildIsLib :: PreBuildComponentInputs -> Bool
+ Distribution.Simple.Build.Inputs: buildVerbosity :: PreBuildComponentInputs -> Verbosity
+ Distribution.Simple.Build.Inputs: buildingWhatDistPref :: BuildingWhat -> FilePath
+ Distribution.Simple.Build.Inputs: buildingWhatVerbosity :: BuildingWhat -> Verbosity
+ Distribution.Simple.Build.Inputs: data BuildingWhat
+ Distribution.Simple.Build.Inputs: data LocalBuildInfo
+ Distribution.Simple.Build.Inputs: data PreBuildComponentInputs
+ Distribution.Simple.Build.Inputs: data TargetInfo
+ Distribution.Simple.Build.Inputs: pattern LocalBuildInfo :: ConfigFlags -> FlagAssignment -> ComponentRequestedSpec -> [String] -> InstallDirTemplates -> Compiler -> Platform -> Maybe FilePath -> Graph ComponentLocalBuildInfo -> Map ComponentName [ComponentLocalBuildInfo] -> Map (PackageName, ComponentName) ComponentId -> InstalledPackageIndex -> PackageDescription -> ProgramDb -> PackageDBStack -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> ProfDetailLevel -> ProfDetailLevel -> OptimisationLevel -> DebugInfoLevel -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> [UnitId] -> Bool -> LocalBuildInfo
+ Distribution.Simple.BuildToolDepends: desugarBuildToolSimple :: PackageName -> [UnqualComponentName] -> LegacyExeDependency -> Maybe ExeDependency
+ Distribution.Simple.Command: commandsRunWithFallback :: CommandUI a -> [Command action] -> ([Command action] -> String -> [String] -> IO (CommandParse action)) -> [String] -> IO (CommandParse (a, CommandParse action))
+ Distribution.Simple.Command: defaultCommandFallback :: [Command action] -> String -> [String] -> IO (CommandParse action)
+ Distribution.Simple.Command: optArgDef' :: Monoid b => ArgPlaceHolder -> (String, Maybe String -> b) -> (b -> [Maybe String]) -> MkOptDescr (a -> b) (b -> a -> a) a
+ Distribution.Simple.Compiler: jsemSupported :: Compiler -> Bool
+ Distribution.Simple.Configure: getInstalledPackagesById :: (Exception (VerboseException exception), Show exception, Typeable exception) => Verbosity -> LocalBuildInfo -> (UnitId -> exception) -> [UnitId] -> IO [InstalledPackageInfo]
+ Distribution.Simple.Errors: AmbiguousBuildTarget :: [(String, [(String, String)])] -> CabalException
+ Distribution.Simple.Errors: BadVersion :: String -> String -> PkgconfigVersion -> CabalException
+ Distribution.Simple.Errors: BadVersionDb :: String -> Version -> VersionRange -> FilePath -> CabalException
+ Distribution.Simple.Errors: BenchMarkNameDisabled :: String -> CabalException
+ Distribution.Simple.Errors: BuildingNotSupportedWithCompiler :: CabalException
+ Distribution.Simple.Errors: CannotDetermineCompilerVersion :: CabalException
+ Distribution.Simple.Errors: CantFindForeignLibraries :: [String] -> CabalException
+ Distribution.Simple.Errors: CantFindIncludeFile :: String -> CabalException
+ Distribution.Simple.Errors: CantFindSourceForPreProcessFile :: String -> CabalException
+ Distribution.Simple.Errors: CantFindSourceModule :: ModuleName -> CabalException
+ Distribution.Simple.Errors: CantInstallForeignLib :: CabalException
+ Distribution.Simple.Errors: CantParseGHCOutput :: CabalException
+ Distribution.Simple.Errors: CheckBuildTargets :: String -> CabalException
+ Distribution.Simple.Errors: CheckPackageDbStackPost76 :: CabalException
+ Distribution.Simple.Errors: CheckPackageDbStackPre76 :: CabalException
+ Distribution.Simple.Errors: CheckPackageProblems :: [String] -> CabalException
+ Distribution.Simple.Errors: CheckSemaphoreSupport :: CabalException
+ Distribution.Simple.Errors: CombinedConstraints :: Doc -> CabalException
+ Distribution.Simple.Errors: CompilerDoesn'tSupportBackpack :: CabalException
+ Distribution.Simple.Errors: CompilerDoesn'tSupportReexports :: CabalException
+ Distribution.Simple.Errors: CompilerDoesn'tSupportThinning :: CabalException
+ Distribution.Simple.Errors: CompilerNotInstalled :: CompilerFlavor -> CabalException
+ Distribution.Simple.Errors: ConfigCIDValidForPreComponent :: CabalException
+ Distribution.Simple.Errors: ConfigureEitherSingleOrAll :: CabalException
+ Distribution.Simple.Errors: ConfigureProgram :: String -> FilePath -> CabalException
+ Distribution.Simple.Errors: ConfigureScriptNotFound :: CabalException
+ Distribution.Simple.Errors: Couldn'tFindTestProgLibV09 :: FilePath -> CabalException
+ Distribution.Simple.Errors: Couldn'tFindTestProgram :: FilePath -> CabalException
+ Distribution.Simple.Errors: CreatePackageDB :: CabalException
+ Distribution.Simple.Errors: DependencyMissingInternal :: PackageName -> LibraryName -> FailedDependency
+ Distribution.Simple.Errors: DependencyNoVersion :: Dependency -> FailedDependency
+ Distribution.Simple.Errors: DependencyNotExists :: PackageName -> FailedDependency
+ Distribution.Simple.Errors: DumpFailed :: String -> String -> CabalException
+ Distribution.Simple.Errors: EnableBenchMark :: CabalException
+ Distribution.Simple.Errors: EncounteredMissingDependency :: [Dependency] -> CabalException
+ Distribution.Simple.Errors: ErrorParsingFileDoesntExist :: FilePath -> CabalException
+ Distribution.Simple.Errors: ExpectedAbsoluteDirectory :: FilePath -> CabalException
+ Distribution.Simple.Errors: ExplainErrors :: Maybe (Either [Char] [Char]) -> [String] -> CabalException
+ Distribution.Simple.Errors: FailedParsing :: String -> CabalException
+ Distribution.Simple.Errors: FailedToDetermineTarget :: CabalException
+ Distribution.Simple.Errors: FailedToParseOutput :: CabalException
+ Distribution.Simple.Errors: FailedToParseOutputDescribe :: String -> PackageId -> CabalException
+ Distribution.Simple.Errors: FailedToParseOutputDump :: String -> CabalException
+ Distribution.Simple.Errors: FailedToParseOutputList :: String -> CabalException
+ Distribution.Simple.Errors: FindFileCwd :: FilePath -> CabalException
+ Distribution.Simple.Errors: FindFileEx :: FilePath -> CabalException
+ Distribution.Simple.Errors: FindModuleFileEx :: ModuleName -> [Suffix] -> [FilePath] -> CabalException
+ Distribution.Simple.Errors: FlagsNotSpecified :: [FlagName] -> CabalException
+ Distribution.Simple.Errors: GetProgramInvocationException :: FilePath -> String -> CabalException
+ Distribution.Simple.Errors: GetProgramInvocationLBSException :: FilePath -> String -> CabalException
+ Distribution.Simple.Errors: GlobalPackageDBLimitation :: CabalException
+ Distribution.Simple.Errors: GlobalPackageDBSpecifiedFirst :: CabalException
+ Distribution.Simple.Errors: GlobalPackageDbSpecifiedFirst :: CabalException
+ Distribution.Simple.Errors: HaddockAndGHCVersionDoesntMatch :: Version -> Version -> CabalException
+ Distribution.Simple.Errors: HaddockPackageFlags :: [(InstalledPackageInfo, [UnitId])] -> CabalException
+ Distribution.Simple.Errors: HowToFindInstalledPackages :: CompilerFlavor -> CabalException
+ Distribution.Simple.Errors: IncompatibleWithCabal :: String -> String -> CabalException
+ Distribution.Simple.Errors: InstallDirsNotPrefixRelative :: InstallDirs FilePath -> CabalException
+ Distribution.Simple.Errors: LibDirDepsPrefixNotRelative :: FilePath -> FilePath -> CabalException
+ Distribution.Simple.Errors: LibraryWithinSamePackage :: [PackageId] -> CabalException
+ Distribution.Simple.Errors: ListFailed :: String -> CabalException
+ Distribution.Simple.Errors: MatchDirFileGlob :: String -> CabalException
+ Distribution.Simple.Errors: MatchDirFileGlobErrors :: [String] -> CabalException
+ Distribution.Simple.Errors: MissingCoveredInstalledLibrary :: UnitId -> CabalException
+ Distribution.Simple.Errors: MultiDesc :: [String] -> CabalException
+ Distribution.Simple.Errors: MultipleFilesWithExtension :: String -> CabalException
+ Distribution.Simple.Errors: MustHaveSharedLibraries :: CabalException
+ Distribution.Simple.Errors: NoBenchMark :: String -> CabalException
+ Distribution.Simple.Errors: NoBenchMarkProgram :: FilePath -> CabalException
+ Distribution.Simple.Errors: NoCompilerSupport :: String -> CabalException
+ Distribution.Simple.Errors: NoDesc :: CabalException
+ Distribution.Simple.Errors: NoGHCVersionFromCompiler :: CabalException
+ Distribution.Simple.Errors: NoGHCVersionFromHaddock :: CabalException
+ Distribution.Simple.Errors: NoIncludeFileFound :: String -> CabalException
+ Distribution.Simple.Errors: NoLibraryForPackage :: CabalException
+ Distribution.Simple.Errors: NoLibraryFound :: CabalException
+ Distribution.Simple.Errors: NoModuleFound :: ModuleName -> [Suffix] -> CabalException
+ Distribution.Simple.Errors: NoMultipleTargets :: CabalException
+ Distribution.Simple.Errors: NoOSSupport :: OS -> CabalException
+ Distribution.Simple.Errors: NoPackageDatabaseSpecified :: CabalException
+ Distribution.Simple.Errors: NoProgramFound :: String -> VersionRange -> CabalException
+ Distribution.Simple.Errors: NoSuchTest :: String -> CabalException
+ Distribution.Simple.Errors: NoSupportBuildingBenchMark :: BenchmarkType -> CabalException
+ Distribution.Simple.Errors: NoSupportBuildingTestSuite :: TestType -> CabalException
+ Distribution.Simple.Errors: NoSupportDirStylePackageDb :: CabalException
+ Distribution.Simple.Errors: NoSupportForHoogle :: CabalException
+ Distribution.Simple.Errors: NoSupportForPreProcessingBenchmark :: BenchmarkType -> CabalException
+ Distribution.Simple.Errors: NoSupportForPreProcessingTest :: TestType -> CabalException
+ Distribution.Simple.Errors: NoSupportForQuickJumpFlag :: CabalException
+ Distribution.Simple.Errors: NoSupportPreProcessingBenchmarkExtras :: BenchmarkType -> CabalException
+ Distribution.Simple.Errors: NoSupportPreProcessingTestExtras :: TestType -> CabalException
+ Distribution.Simple.Errors: NoTestSuitesEnabled :: CabalException
+ Distribution.Simple.Errors: NoValidComponent :: CabalException
+ Distribution.Simple.Errors: NoWorkingGcc :: CabalException
+ Distribution.Simple.Errors: NotFoundMsg :: CabalException
+ Distribution.Simple.Errors: OnlySupportSpecificPackageDb :: CabalException
+ Distribution.Simple.Errors: PkgConfigNotFound :: String -> String -> CabalException
+ Distribution.Simple.Errors: PkgDumpFailed :: CabalException
+ Distribution.Simple.Errors: ProgramNotFound :: String -> CabalException
+ Distribution.Simple.Errors: ProvideHaskellSuiteTool :: String -> CabalException
+ Distribution.Simple.Errors: REPLNotSupported :: CabalException
+ Distribution.Simple.Errors: RawSystemStdout :: String -> CabalException
+ Distribution.Simple.Errors: RegMultipleInstancePkg :: CabalException
+ Distribution.Simple.Errors: RegisMultiplePkgNotSupported :: CabalException
+ Distribution.Simple.Errors: RegisteringNotImplemented :: CabalException
+ Distribution.Simple.Errors: RelocRegistrationInfo :: CabalException
+ Distribution.Simple.Errors: ReportBuildTargetProblems :: [(String, [String], String)] -> CabalException
+ Distribution.Simple.Errors: ReportFailedDependencies :: [FailedDependency] -> String -> CabalException
+ Distribution.Simple.Errors: RequireProgram :: String -> CabalException
+ Distribution.Simple.Errors: RunProgramInvocationException :: FilePath -> String -> CabalException
+ Distribution.Simple.Errors: SanityCheckForDynamicStaticLinking :: CabalException
+ Distribution.Simple.Errors: SanityCheckForEnableComponents :: CabalException
+ Distribution.Simple.Errors: SanityCheckHookedBuildInfo :: UnqualComponentName -> CabalException
+ Distribution.Simple.Errors: SuppressingChecksOnFile :: CabalException
+ Distribution.Simple.Errors: TestCoverageSupport :: CabalException
+ Distribution.Simple.Errors: TestCoverageSupportLibV09 :: CabalException
+ Distribution.Simple.Errors: TestNameDisabled :: String -> CabalException
+ Distribution.Simple.Errors: UnknownBuildTarget :: [(String, [(String, String)])] -> CabalException
+ Distribution.Simple.Errors: UnknownCompilerException :: CabalException
+ Distribution.Simple.Errors: UnknownCompilerFlavor :: CompilerFlavor -> CabalException
+ Distribution.Simple.Errors: UnknownVersionDb :: String -> VersionRange -> FilePath -> CabalException
+ Distribution.Simple.Errors: UnlitException :: String -> CabalException
+ Distribution.Simple.Errors: UnrecognisedBuildTarget :: [String] -> CabalException
+ Distribution.Simple.Errors: UnsupportedBenchMark :: String -> CabalException
+ Distribution.Simple.Errors: UnsupportedLanguageExtension :: PackageIdentifier -> CompilerId -> [String] -> CabalException
+ Distribution.Simple.Errors: UnsupportedLanguages :: PackageIdentifier -> CompilerId -> [String] -> CabalException
+ Distribution.Simple.Errors: UnsupportedTestSuite :: String -> CabalException
+ Distribution.Simple.Errors: VersionMismatchGHC :: FilePath -> Version -> FilePath -> Version -> CabalException
+ Distribution.Simple.Errors: VersionMismatchGHCJS :: FilePath -> Version -> FilePath -> Version -> CabalException
+ Distribution.Simple.Errors: VersionMismatchJS :: FilePath -> Version -> FilePath -> Version -> CabalException
+ Distribution.Simple.Errors: WithHcPkg :: String -> CabalException
+ Distribution.Simple.Errors: data CabalException
+ Distribution.Simple.Errors: data FailedDependency
+ Distribution.Simple.Errors: exceptionCode :: CabalException -> Int
+ Distribution.Simple.Errors: exceptionMessage :: CabalException -> String
+ Distribution.Simple.Errors: instance GHC.Show.Show Distribution.Simple.Errors.CabalException
+ Distribution.Simple.Errors: instance GHC.Show.Show Distribution.Simple.Errors.FailedDependency
+ Distribution.Simple.Flag: instance Data.Foldable.Foldable Distribution.Simple.Flag.Flag
+ Distribution.Simple.Flag: instance Data.Traversable.Traversable Distribution.Simple.Flag.Flag
+ Distribution.Simple.Flag: mergeListFlag :: Flag [a] -> Flag [a] -> Flag [a]
+ Distribution.Simple.GHC: [flagHie] :: GhcImplInfo -> Bool
+ Distribution.Simple.GHC: [supportsGHC2024] :: GhcImplInfo -> Bool
+ Distribution.Simple.GHC: [unitIdForExes] :: GhcImplInfo -> Bool
+ Distribution.Simple.GHCJS: [flagHie] :: GhcImplInfo -> Bool
+ Distribution.Simple.GHCJS: [supportsGHC2024] :: GhcImplInfo -> Bool
+ Distribution.Simple.GHCJS: [unitIdForExes] :: GhcImplInfo -> Bool
+ Distribution.Simple.Glob: GlobMatchesDirectory :: a -> GlobResult a
+ Distribution.Simple.Glob: globMatches :: [GlobResult a] -> [a]
+ Distribution.Simple.Glob.Internal: EmptyGlob :: GlobSyntaxError
+ Distribution.Simple.Glob.Internal: GlobDir :: !GlobPieces -> !Glob -> Glob
+ Distribution.Simple.Glob.Internal: GlobDirRecursive :: !GlobPieces -> Glob
+ Distribution.Simple.Glob.Internal: GlobDirTrailing :: Glob
+ Distribution.Simple.Glob.Internal: GlobFile :: !GlobPieces -> Glob
+ Distribution.Simple.Glob.Internal: GlobMatch :: a -> GlobResult a
+ Distribution.Simple.Glob.Internal: GlobMatchesDirectory :: a -> GlobResult a
+ Distribution.Simple.Glob.Internal: GlobMissingDirectory :: a -> GlobResult a
+ Distribution.Simple.Glob.Internal: GlobWarnMultiDot :: a -> GlobResult a
+ Distribution.Simple.Glob.Internal: Literal :: String -> GlobPiece
+ Distribution.Simple.Glob.Internal: LiteralFileNameGlobStar :: GlobSyntaxError
+ Distribution.Simple.Glob.Internal: NoExtensionOnStar :: GlobSyntaxError
+ Distribution.Simple.Glob.Internal: StarInDirectory :: GlobSyntaxError
+ Distribution.Simple.Glob.Internal: StarInExtension :: GlobSyntaxError
+ Distribution.Simple.Glob.Internal: StarInFileName :: GlobSyntaxError
+ Distribution.Simple.Glob.Internal: Union :: [GlobPieces] -> GlobPiece
+ Distribution.Simple.Glob.Internal: VersionDoesNotSupportGlob :: GlobSyntaxError
+ Distribution.Simple.Glob.Internal: VersionDoesNotSupportGlobStar :: GlobSyntaxError
+ Distribution.Simple.Glob.Internal: WildCard :: GlobPiece
+ Distribution.Simple.Glob.Internal: checkNameMatches :: CabalSpecVersion -> GlobPieces -> String -> Maybe (GlobResult ())
+ Distribution.Simple.Glob.Internal: data Glob
+ Distribution.Simple.Glob.Internal: data GlobPiece
+ Distribution.Simple.Glob.Internal: data GlobResult a
+ Distribution.Simple.Glob.Internal: data GlobSyntaxError
+ Distribution.Simple.Glob.Internal: dispGlobPieces :: GlobPieces -> Doc
+ Distribution.Simple.Glob.Internal: enableMultidot :: CabalSpecVersion -> Bool
+ Distribution.Simple.Glob.Internal: explainGlobSyntaxError :: FilePath -> GlobSyntaxError -> String
+ Distribution.Simple.Glob.Internal: fileGlobMatches :: CabalSpecVersion -> Glob -> FilePath -> Maybe (GlobResult ())
+ Distribution.Simple.Glob.Internal: instance Data.Binary.Class.Binary Distribution.Simple.Glob.Internal.Glob
+ Distribution.Simple.Glob.Internal: instance Data.Binary.Class.Binary Distribution.Simple.Glob.Internal.GlobPiece
+ Distribution.Simple.Glob.Internal: instance Distribution.Parsec.Parsec Distribution.Simple.Glob.Internal.Glob
+ Distribution.Simple.Glob.Internal: instance Distribution.Pretty.Pretty Distribution.Simple.Glob.Internal.Glob
+ Distribution.Simple.Glob.Internal: instance Distribution.Utils.Structured.Structured Distribution.Simple.Glob.Internal.Glob
+ Distribution.Simple.Glob.Internal: instance Distribution.Utils.Structured.Structured Distribution.Simple.Glob.Internal.GlobPiece
+ Distribution.Simple.Glob.Internal: instance GHC.Base.Functor Distribution.Simple.Glob.Internal.GlobResult
+ Distribution.Simple.Glob.Internal: instance GHC.Classes.Eq Distribution.Simple.Glob.Internal.Glob
+ Distribution.Simple.Glob.Internal: instance GHC.Classes.Eq Distribution.Simple.Glob.Internal.GlobPiece
+ Distribution.Simple.Glob.Internal: instance GHC.Classes.Eq Distribution.Simple.Glob.Internal.GlobSyntaxError
+ Distribution.Simple.Glob.Internal: instance GHC.Classes.Eq a => GHC.Classes.Eq (Distribution.Simple.Glob.Internal.GlobResult a)
+ Distribution.Simple.Glob.Internal: instance GHC.Classes.Ord a => GHC.Classes.Ord (Distribution.Simple.Glob.Internal.GlobResult a)
+ Distribution.Simple.Glob.Internal: instance GHC.Generics.Generic Distribution.Simple.Glob.Internal.Glob
+ Distribution.Simple.Glob.Internal: instance GHC.Generics.Generic Distribution.Simple.Glob.Internal.GlobPiece
+ Distribution.Simple.Glob.Internal: instance GHC.Show.Show Distribution.Simple.Glob.Internal.Glob
+ Distribution.Simple.Glob.Internal: instance GHC.Show.Show Distribution.Simple.Glob.Internal.GlobPiece
+ Distribution.Simple.Glob.Internal: instance GHC.Show.Show Distribution.Simple.Glob.Internal.GlobSyntaxError
+ Distribution.Simple.Glob.Internal: instance GHC.Show.Show a => GHC.Show.Show (Distribution.Simple.Glob.Internal.GlobResult a)
+ Distribution.Simple.Glob.Internal: isGlobEscapedChar :: Char -> Bool
+ Distribution.Simple.Glob.Internal: isRecursiveInRoot :: Glob -> Bool
+ Distribution.Simple.Glob.Internal: matchGlob :: FilePath -> Glob -> IO [FilePath]
+ Distribution.Simple.Glob.Internal: matchGlobPieces :: GlobPieces -> String -> Bool
+ Distribution.Simple.Glob.Internal: parseFileGlob :: CabalSpecVersion -> FilePath -> Either GlobSyntaxError Glob
+ Distribution.Simple.Glob.Internal: runDirFileGlob :: Verbosity -> Maybe CabalSpecVersion -> FilePath -> Glob -> IO [GlobResult FilePath]
+ Distribution.Simple.Glob.Internal: type GlobPieces = [GlobPiece]
+ Distribution.Simple.Hpc: HPCMarkupInfo :: [FilePath] -> [ModuleName] -> HPCMarkupInfo
+ Distribution.Simple.Hpc: [libsModulesToInclude] :: HPCMarkupInfo -> [ModuleName]
+ Distribution.Simple.Hpc: [pathsToLibsArtifacts] :: HPCMarkupInfo -> [FilePath]
+ Distribution.Simple.Hpc: data HPCMarkupInfo
+ Distribution.Simple.InstallDirs: instance Distribution.Utils.Structured.Structured Distribution.Simple.InstallDirs.CopyDest
+ Distribution.Simple.LocalBuildInfo: NewLocalBuildInfo :: LocalBuildDescr -> LocalBuildConfig -> LocalBuildInfo
+ Distribution.Simple.LocalBuildInfo: [$sel:localBuildConfig:NewLocalBuildInfo] :: LocalBuildInfo -> LocalBuildConfig
+ Distribution.Simple.LocalBuildInfo: [$sel:localBuildDescr:NewLocalBuildInfo] :: LocalBuildInfo -> LocalBuildDescr
+ Distribution.Simple.LocalBuildInfo: buildDir :: LocalBuildInfo -> FilePath
+ Distribution.Simple.LocalBuildInfo: cabalFilePath :: LocalBuildInfo -> Maybe FilePath
+ Distribution.Simple.LocalBuildInfo: pattern LocalBuildInfo :: ConfigFlags -> FlagAssignment -> ComponentRequestedSpec -> [String] -> InstallDirTemplates -> Compiler -> Platform -> Maybe FilePath -> Graph ComponentLocalBuildInfo -> Map ComponentName [ComponentLocalBuildInfo] -> Map (PackageName, ComponentName) ComponentId -> InstalledPackageIndex -> PackageDescription -> ProgramDb -> PackageDBStack -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> ProfDetailLevel -> ProfDetailLevel -> OptimisationLevel -> DebugInfoLevel -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> [UnitId] -> Bool -> LocalBuildInfo
+ Distribution.Simple.LocalBuildInfo: progPrefix :: LocalBuildInfo -> PathTemplate
+ Distribution.Simple.LocalBuildInfo: progSuffix :: LocalBuildInfo -> PathTemplate
+ Distribution.Simple.PreProcess: Suffix :: String -> Suffix
+ Distribution.Simple.PreProcess: builtinHaskellBootSuffixes :: [Suffix]
+ Distribution.Simple.PreProcess: builtinHaskellSuffixes :: [Suffix]
+ Distribution.Simple.PreProcess: newtype Suffix
+ Distribution.Simple.PreProcess.Types: PreProcessor :: Bool -> (Verbosity -> [FilePath] -> [ModuleName] -> IO [ModuleName]) -> ((FilePath, FilePath) -> (FilePath, FilePath) -> Verbosity -> IO ()) -> PreProcessor
+ Distribution.Simple.PreProcess.Types: Suffix :: String -> Suffix
+ Distribution.Simple.PreProcess.Types: [platformIndependent] :: PreProcessor -> Bool
+ Distribution.Simple.PreProcess.Types: [ppOrdering] :: PreProcessor -> Verbosity -> [FilePath] -> [ModuleName] -> IO [ModuleName]
+ Distribution.Simple.PreProcess.Types: [runPreProcessor] :: PreProcessor -> (FilePath, FilePath) -> (FilePath, FilePath) -> Verbosity -> IO ()
+ Distribution.Simple.PreProcess.Types: builtinHaskellBootSuffixes :: [Suffix]
+ Distribution.Simple.PreProcess.Types: builtinHaskellSuffixes :: [Suffix]
+ Distribution.Simple.PreProcess.Types: data PreProcessor
+ Distribution.Simple.PreProcess.Types: instance Data.Binary.Class.Binary Distribution.Simple.PreProcess.Types.Suffix
+ Distribution.Simple.PreProcess.Types: instance Data.String.IsString Distribution.Simple.PreProcess.Types.Suffix
+ Distribution.Simple.PreProcess.Types: instance Distribution.Pretty.Pretty Distribution.Simple.PreProcess.Types.Suffix
+ Distribution.Simple.PreProcess.Types: instance Distribution.Utils.Structured.Structured Distribution.Simple.PreProcess.Types.Suffix
+ Distribution.Simple.PreProcess.Types: instance GHC.Classes.Eq Distribution.Simple.PreProcess.Types.Suffix
+ Distribution.Simple.PreProcess.Types: instance GHC.Classes.Ord Distribution.Simple.PreProcess.Types.Suffix
+ Distribution.Simple.PreProcess.Types: instance GHC.Generics.Generic Distribution.Simple.PreProcess.Types.Suffix
+ Distribution.Simple.PreProcess.Types: instance GHC.Show.Show Distribution.Simple.PreProcess.Types.Suffix
+ Distribution.Simple.PreProcess.Types: newtype Suffix
+ Distribution.Simple.Program.Db: prependProgramSearchPath :: Verbosity -> [FilePath] -> ProgramDb -> IO ProgramDb
+ Distribution.Simple.Program.GHC: [ghcOptHieDir] :: GhcOptions -> Flag FilePath
+ Distribution.Simple.Setup: BuildHaddock :: HaddockFlags -> BuildingWhat
+ Distribution.Simple.Setup: BuildHscolour :: HscolourFlags -> BuildingWhat
+ Distribution.Simple.Setup: BuildNormal :: BuildFlags -> BuildingWhat
+ Distribution.Simple.Setup: BuildRepl :: ReplFlags -> BuildingWhat
+ Distribution.Simple.Setup: [buildUseSemaphore] :: BuildFlags -> Flag String
+ Distribution.Simple.Setup: [configCoverageFor] :: ConfigFlags -> Flag [UnitId]
+ Distribution.Simple.Setup: [configPromisedDependencies] :: ConfigFlags -> [GivenComponent]
+ Distribution.Simple.Setup: [haddockOutputDir] :: HaddockFlags -> Flag FilePath
+ Distribution.Simple.Setup: [haddockProjectOutputDir] :: HaddockProjectFlags -> Flag FilePath
+ Distribution.Simple.Setup: [replOptionsFlagOutput] :: ReplOptions -> Flag FilePath
+ Distribution.Simple.Setup: buildingWhatDistPref :: BuildingWhat -> FilePath
+ Distribution.Simple.Setup: buildingWhatVerbosity :: BuildingWhat -> Verbosity
+ Distribution.Simple.Setup: data BuildingWhat
+ Distribution.Simple.Setup: instance GHC.Generics.Generic Distribution.Simple.Setup.BuildingWhat
+ Distribution.Simple.Setup: instance GHC.Show.Show Distribution.Simple.Setup.BuildingWhat
+ Distribution.Simple.Utils: VerboseException :: CallStack -> POSIXTime -> Verbosity -> a -> VerboseException a
+ Distribution.Simple.Utils: data VerboseException a
+ Distribution.Simple.Utils: dieWithException :: (HasCallStack, Show a1, Typeable a1, Exception (VerboseException a1)) => Verbosity -> a1 -> IO a
+ Distribution.Simple.Utils: exceptionWithCallStackPrefix :: CallStack -> Verbosity -> String -> String
+ Distribution.Simple.Utils: exceptionWithMetadata :: CallStack -> POSIXTime -> Verbosity -> String -> String
+ Distribution.Simple.Utils: instance GHC.Exception.Type.Exception (Distribution.Simple.Utils.VerboseException Distribution.Simple.Errors.CabalException)
+ Distribution.Simple.Utils: instance GHC.Show.Show a => GHC.Show.Show (Distribution.Simple.Utils.VerboseException a)
+ Distribution.Simple.Utils: warnError :: Verbosity -> String -> IO ()
+ Distribution.Types.LocalBuildConfig: BuildOptions :: Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> ProfDetailLevel -> ProfDetailLevel -> OptimisationLevel -> DebugInfoLevel -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> BuildOptions
+ Distribution.Types.LocalBuildConfig: ComponentBuildDescr :: Graph ComponentLocalBuildInfo -> Map ComponentName [ComponentLocalBuildInfo] -> Map (PackageName, ComponentName) ComponentId -> InstalledPackageIndex -> ComponentBuildDescr
+ Distribution.Types.LocalBuildConfig: LocalBuildConfig :: [String] -> ProgramDb -> BuildOptions -> LocalBuildConfig
+ Distribution.Types.LocalBuildConfig: LocalBuildDescr :: PackageBuildDescr -> ComponentBuildDescr -> LocalBuildDescr
+ Distribution.Types.LocalBuildConfig: PackageBuildDescr :: ConfigFlags -> FlagAssignment -> ComponentRequestedSpec -> Compiler -> Platform -> Maybe FilePath -> PackageDescription -> InstallDirTemplates -> PackageDBStack -> [UnitId] -> PackageBuildDescr
+ Distribution.Types.LocalBuildConfig: [$sel:compiler:PackageBuildDescr] :: PackageBuildDescr -> Compiler
+ Distribution.Types.LocalBuildConfig: [$sel:componentBuildDescr:LocalBuildDescr] :: LocalBuildDescr -> ComponentBuildDescr
+ Distribution.Types.LocalBuildConfig: [$sel:componentEnabledSpec:PackageBuildDescr] :: PackageBuildDescr -> ComponentRequestedSpec
+ Distribution.Types.LocalBuildConfig: [$sel:componentGraph:ComponentBuildDescr] :: ComponentBuildDescr -> Graph ComponentLocalBuildInfo
+ Distribution.Types.LocalBuildConfig: [$sel:componentNameMap:ComponentBuildDescr] :: ComponentBuildDescr -> Map ComponentName [ComponentLocalBuildInfo]
+ Distribution.Types.LocalBuildConfig: [$sel:configFlags:PackageBuildDescr] :: PackageBuildDescr -> ConfigFlags
+ Distribution.Types.LocalBuildConfig: [$sel:exeCoverage:BuildOptions] :: BuildOptions -> Bool
+ Distribution.Types.LocalBuildConfig: [$sel:extraConfigArgs:LocalBuildConfig] :: LocalBuildConfig -> [String]
+ Distribution.Types.LocalBuildConfig: [$sel:extraCoverageFor:PackageBuildDescr] :: PackageBuildDescr -> [UnitId]
+ Distribution.Types.LocalBuildConfig: [$sel:flagAssignment:PackageBuildDescr] :: PackageBuildDescr -> FlagAssignment
+ Distribution.Types.LocalBuildConfig: [$sel:hostPlatform:PackageBuildDescr] :: PackageBuildDescr -> Platform
+ Distribution.Types.LocalBuildConfig: [$sel:installDirTemplates:PackageBuildDescr] :: PackageBuildDescr -> InstallDirTemplates
+ Distribution.Types.LocalBuildConfig: [$sel:installedPkgs:ComponentBuildDescr] :: ComponentBuildDescr -> InstalledPackageIndex
+ Distribution.Types.LocalBuildConfig: [$sel:libCoverage:BuildOptions] :: BuildOptions -> Bool
+ Distribution.Types.LocalBuildConfig: [$sel:localPkgDescr:PackageBuildDescr] :: PackageBuildDescr -> PackageDescription
+ Distribution.Types.LocalBuildConfig: [$sel:packageBuildDescr:LocalBuildDescr] :: LocalBuildDescr -> PackageBuildDescr
+ Distribution.Types.LocalBuildConfig: [$sel:pkgDescrFile:PackageBuildDescr] :: PackageBuildDescr -> Maybe FilePath
+ Distribution.Types.LocalBuildConfig: [$sel:promisedPkgs:ComponentBuildDescr] :: ComponentBuildDescr -> Map (PackageName, ComponentName) ComponentId
+ Distribution.Types.LocalBuildConfig: [$sel:relocatable:BuildOptions] :: BuildOptions -> Bool
+ Distribution.Types.LocalBuildConfig: [$sel:splitObjs:BuildOptions] :: BuildOptions -> Bool
+ Distribution.Types.LocalBuildConfig: [$sel:splitSections:BuildOptions] :: BuildOptions -> Bool
+ Distribution.Types.LocalBuildConfig: [$sel:stripExes:BuildOptions] :: BuildOptions -> Bool
+ Distribution.Types.LocalBuildConfig: [$sel:stripLibs:BuildOptions] :: BuildOptions -> Bool
+ Distribution.Types.LocalBuildConfig: [$sel:withBuildOptions:LocalBuildConfig] :: LocalBuildConfig -> BuildOptions
+ Distribution.Types.LocalBuildConfig: [$sel:withDebugInfo:BuildOptions] :: BuildOptions -> DebugInfoLevel
+ Distribution.Types.LocalBuildConfig: [$sel:withDynExe:BuildOptions] :: BuildOptions -> Bool
+ Distribution.Types.LocalBuildConfig: [$sel:withFullyStaticExe:BuildOptions] :: BuildOptions -> Bool
+ Distribution.Types.LocalBuildConfig: [$sel:withGHCiLib:BuildOptions] :: BuildOptions -> Bool
+ Distribution.Types.LocalBuildConfig: [$sel:withOptimization:BuildOptions] :: BuildOptions -> OptimisationLevel
+ Distribution.Types.LocalBuildConfig: [$sel:withPackageDB:PackageBuildDescr] :: PackageBuildDescr -> PackageDBStack
+ Distribution.Types.LocalBuildConfig: [$sel:withProfExe:BuildOptions] :: BuildOptions -> Bool
+ Distribution.Types.LocalBuildConfig: [$sel:withProfExeDetail:BuildOptions] :: BuildOptions -> ProfDetailLevel
+ Distribution.Types.LocalBuildConfig: [$sel:withProfLib:BuildOptions] :: BuildOptions -> Bool
+ Distribution.Types.LocalBuildConfig: [$sel:withProfLibDetail:BuildOptions] :: BuildOptions -> ProfDetailLevel
+ Distribution.Types.LocalBuildConfig: [$sel:withPrograms:LocalBuildConfig] :: LocalBuildConfig -> ProgramDb
+ Distribution.Types.LocalBuildConfig: [$sel:withSharedLib:BuildOptions] :: BuildOptions -> Bool
+ Distribution.Types.LocalBuildConfig: [$sel:withStaticLib:BuildOptions] :: BuildOptions -> Bool
+ Distribution.Types.LocalBuildConfig: [$sel:withVanillaLib:BuildOptions] :: BuildOptions -> Bool
+ Distribution.Types.LocalBuildConfig: buildOptionsConfigFlags :: BuildOptions -> ConfigFlags
+ Distribution.Types.LocalBuildConfig: data BuildOptions
+ Distribution.Types.LocalBuildConfig: data ComponentBuildDescr
+ Distribution.Types.LocalBuildConfig: data LocalBuildConfig
+ Distribution.Types.LocalBuildConfig: data LocalBuildDescr
+ Distribution.Types.LocalBuildConfig: data PackageBuildDescr
+ Distribution.Types.LocalBuildConfig: instance Data.Binary.Class.Binary Distribution.Types.LocalBuildConfig.BuildOptions
+ Distribution.Types.LocalBuildConfig: instance Data.Binary.Class.Binary Distribution.Types.LocalBuildConfig.ComponentBuildDescr
+ Distribution.Types.LocalBuildConfig: instance Data.Binary.Class.Binary Distribution.Types.LocalBuildConfig.LocalBuildConfig
+ Distribution.Types.LocalBuildConfig: instance Data.Binary.Class.Binary Distribution.Types.LocalBuildConfig.LocalBuildDescr
+ Distribution.Types.LocalBuildConfig: instance Data.Binary.Class.Binary Distribution.Types.LocalBuildConfig.PackageBuildDescr
+ Distribution.Types.LocalBuildConfig: instance Distribution.Utils.Structured.Structured Distribution.Types.LocalBuildConfig.BuildOptions
+ Distribution.Types.LocalBuildConfig: instance Distribution.Utils.Structured.Structured Distribution.Types.LocalBuildConfig.ComponentBuildDescr
+ Distribution.Types.LocalBuildConfig: instance Distribution.Utils.Structured.Structured Distribution.Types.LocalBuildConfig.LocalBuildConfig
+ Distribution.Types.LocalBuildConfig: instance Distribution.Utils.Structured.Structured Distribution.Types.LocalBuildConfig.LocalBuildDescr
+ Distribution.Types.LocalBuildConfig: instance Distribution.Utils.Structured.Structured Distribution.Types.LocalBuildConfig.PackageBuildDescr
+ Distribution.Types.LocalBuildConfig: instance GHC.Classes.Eq Distribution.Types.LocalBuildConfig.BuildOptions
+ Distribution.Types.LocalBuildConfig: instance GHC.Generics.Generic Distribution.Types.LocalBuildConfig.BuildOptions
+ Distribution.Types.LocalBuildConfig: instance GHC.Generics.Generic Distribution.Types.LocalBuildConfig.ComponentBuildDescr
+ Distribution.Types.LocalBuildConfig: instance GHC.Generics.Generic Distribution.Types.LocalBuildConfig.LocalBuildConfig
+ Distribution.Types.LocalBuildConfig: instance GHC.Generics.Generic Distribution.Types.LocalBuildConfig.LocalBuildDescr
+ Distribution.Types.LocalBuildConfig: instance GHC.Generics.Generic Distribution.Types.LocalBuildConfig.PackageBuildDescr
+ Distribution.Types.LocalBuildConfig: instance GHC.Read.Read Distribution.Types.LocalBuildConfig.BuildOptions
+ Distribution.Types.LocalBuildConfig: instance GHC.Read.Read Distribution.Types.LocalBuildConfig.ComponentBuildDescr
+ Distribution.Types.LocalBuildConfig: instance GHC.Read.Read Distribution.Types.LocalBuildConfig.LocalBuildConfig
+ Distribution.Types.LocalBuildConfig: instance GHC.Read.Read Distribution.Types.LocalBuildConfig.LocalBuildDescr
+ Distribution.Types.LocalBuildConfig: instance GHC.Read.Read Distribution.Types.LocalBuildConfig.PackageBuildDescr
+ Distribution.Types.LocalBuildConfig: instance GHC.Show.Show Distribution.Types.LocalBuildConfig.BuildOptions
+ Distribution.Types.LocalBuildConfig: instance GHC.Show.Show Distribution.Types.LocalBuildConfig.ComponentBuildDescr
+ Distribution.Types.LocalBuildConfig: instance GHC.Show.Show Distribution.Types.LocalBuildConfig.LocalBuildConfig
+ Distribution.Types.LocalBuildConfig: instance GHC.Show.Show Distribution.Types.LocalBuildConfig.LocalBuildDescr
+ Distribution.Types.LocalBuildConfig: instance GHC.Show.Show Distribution.Types.LocalBuildConfig.PackageBuildDescr
+ Distribution.Types.LocalBuildInfo: NewLocalBuildInfo :: LocalBuildDescr -> LocalBuildConfig -> LocalBuildInfo
+ Distribution.Types.LocalBuildInfo: [$sel:localBuildConfig:NewLocalBuildInfo] :: LocalBuildInfo -> LocalBuildConfig
+ Distribution.Types.LocalBuildInfo: [$sel:localBuildDescr:NewLocalBuildInfo] :: LocalBuildInfo -> LocalBuildDescr
+ Distribution.Types.LocalBuildInfo: buildDir :: LocalBuildInfo -> FilePath
+ Distribution.Types.LocalBuildInfo: buildDirPBD :: PackageBuildDescr -> FilePath
+ Distribution.Types.LocalBuildInfo: cabalFilePath :: LocalBuildInfo -> Maybe FilePath
+ Distribution.Types.LocalBuildInfo: configFlagsBuildDir :: ConfigFlags -> FilePath
+ Distribution.Types.LocalBuildInfo: pattern LocalBuildInfo :: ConfigFlags -> FlagAssignment -> ComponentRequestedSpec -> [String] -> InstallDirTemplates -> Compiler -> Platform -> Maybe FilePath -> Graph ComponentLocalBuildInfo -> Map ComponentName [ComponentLocalBuildInfo] -> Map (PackageName, ComponentName) ComponentId -> InstalledPackageIndex -> PackageDescription -> ProgramDb -> PackageDBStack -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> ProfDetailLevel -> ProfDetailLevel -> OptimisationLevel -> DebugInfoLevel -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> [UnitId] -> Bool -> LocalBuildInfo
+ Distribution.Types.LocalBuildInfo: progPrefix :: LocalBuildInfo -> PathTemplate
+ Distribution.Types.LocalBuildInfo: progSuffix :: LocalBuildInfo -> PathTemplate
+ Distribution.Types.ParStrat: NumJobs :: Maybe Int -> ParStratX sem
+ Distribution.Types.ParStrat: Serial :: ParStratX sem
+ Distribution.Types.ParStrat: UseSem :: sem -> ParStratX sem
+ Distribution.Types.ParStrat: data ParStratX sem
+ Distribution.Types.ParStrat: instance GHC.Show.Show sem => GHC.Show.Show (Distribution.Types.ParStrat.ParStratX sem)
+ Distribution.Types.ParStrat: isParallelBuild :: ParStratX n -> Bool
+ Distribution.Types.ParStrat: type ParStrat = ParStratX String
+ Distribution.Types.ParStrat: type ParStratInstall = ParStratX Int
+ Distribution.Types.TargetInfo: instance Data.Binary.Class.Binary Distribution.Types.TargetInfo.TargetInfo
+ Distribution.Types.TargetInfo: instance Distribution.Utils.Structured.Structured Distribution.Types.TargetInfo.TargetInfo
+ Distribution.Types.TargetInfo: instance GHC.Generics.Generic Distribution.Types.TargetInfo.TargetInfo
+ Distribution.Types.TargetInfo: instance GHC.Show.Show Distribution.Types.TargetInfo.TargetInfo
- Distribution.Backpack.Configure: configureComponentLocalBuildInfos :: Verbosity -> Bool -> ComponentRequestedSpec -> Bool -> Flag String -> Flag ComponentId -> PackageDescription -> [PreExistingComponent] -> FlagAssignment -> [(ModuleName, Module)] -> InstalledPackageIndex -> Compiler -> LogProgress ([ComponentLocalBuildInfo], InstalledPackageIndex)
+ Distribution.Backpack.Configure: configureComponentLocalBuildInfos :: Verbosity -> Bool -> ComponentRequestedSpec -> Bool -> Flag String -> Flag ComponentId -> PackageDescription -> ([PreExistingComponent], [PromisedComponent]) -> FlagAssignment -> [(ModuleName, Module)] -> InstalledPackageIndex -> Compiler -> LogProgress ([ComponentLocalBuildInfo], InstalledPackageIndex)
- Distribution.Backpack.LinkedComponent: toLinkedComponent :: Verbosity -> FullDb -> PackageId -> LinkedComponentMap -> ConfiguredComponent -> LogProgress LinkedComponent
+ Distribution.Backpack.LinkedComponent: toLinkedComponent :: Verbosity -> Bool -> FullDb -> PackageId -> LinkedComponentMap -> ConfiguredComponent -> LogProgress LinkedComponent
- Distribution.Backpack.LinkedComponent: toLinkedComponents :: Verbosity -> FullDb -> PackageId -> LinkedComponentMap -> [ConfiguredComponent] -> LogProgress [LinkedComponent]
+ Distribution.Backpack.LinkedComponent: toLinkedComponents :: Verbosity -> Bool -> FullDb -> PackageId -> LinkedComponentMap -> [ConfiguredComponent] -> LogProgress [LinkedComponent]
- Distribution.PackageDescription.Check: AutogenNoOther :: CEType -> UnqualComponentName -> CheckExplanation
+ Distribution.PackageDescription.Check: AutogenNoOther :: CEType -> CheckExplanation
- Distribution.PackageDescription.Check: COptONumber :: String -> String -> CheckExplanation
+ Distribution.PackageDescription.Check: COptONumber :: String -> WarnLang -> CheckExplanation
- Distribution.PackageDescription.Check: IllegalLibraryName :: PackageDescription -> CheckExplanation
+ Distribution.PackageDescription.Check: IllegalLibraryName :: PackageName -> CheckExplanation
- Distribution.PackageDescription.Check: InvalidNameWin :: PackageDescription -> CheckExplanation
+ Distribution.PackageDescription.Check: InvalidNameWin :: PackageName -> CheckExplanation
- Distribution.PackageDescription.Check: LicenseMessParse :: PackageDescription -> CheckExplanation
+ Distribution.PackageDescription.Check: LicenseMessParse :: License -> CheckExplanation
- Distribution.PackageDescription.Check: MissingUpperBounds :: [PackageName] -> CheckExplanation
+ Distribution.PackageDescription.Check: MissingUpperBounds :: CEType -> [String] -> CheckExplanation
- Distribution.PackageDescription.Check: NoMainIs :: Executable -> CheckExplanation
+ Distribution.PackageDescription.Check: NoMainIs :: UnqualComponentName -> CheckExplanation
- Distribution.PackageDescription.Check: NoModulesExposed :: Library -> CheckExplanation
+ Distribution.PackageDescription.Check: NoModulesExposed :: LibraryName -> CheckExplanation
- Distribution.PackageDescription.Check: checkPackage :: GenericPackageDescription -> Maybe PackageDescription -> [PackageCheck]
+ Distribution.PackageDescription.Check: checkPackage :: GenericPackageDescription -> [PackageCheck]
- Distribution.PackageDescription.Check: checkPackageContent :: (Monad m, Applicative m) => CheckPackageContentOps m -> PackageDescription -> m [PackageCheck]
+ Distribution.PackageDescription.Check: checkPackageContent :: Monad m => CheckPackageContentOps m -> GenericPackageDescription -> m [PackageCheck]
- Distribution.Simple.Build: writeAutogenFiles :: Verbosity -> PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> IO ()
+ Distribution.Simple.Build: writeAutogenFiles :: Verbosity -> LocalBuildInfo -> ComponentLocalBuildInfo -> Map AutogenFile AutogenFileContents -> IO ()
- Distribution.Simple.Command: OptArg :: Description -> OptFlags -> ArgPlaceHolder -> ReadE (a -> a) -> (a -> a) -> (a -> [Maybe String]) -> OptDescr a
+ Distribution.Simple.Command: OptArg :: Description -> OptFlags -> ArgPlaceHolder -> ReadE (a -> a) -> (String, a -> a) -> (a -> [Maybe String]) -> OptDescr a
- Distribution.Simple.Command: commandsRun :: CommandUI a -> [Command action] -> [String] -> CommandParse (a, CommandParse action)
+ Distribution.Simple.Command: commandsRun :: CommandUI a -> [Command action] -> [String] -> IO (CommandParse (a, CommandParse action))
- Distribution.Simple.Command: optArg :: Monoid b => ArgPlaceHolder -> ReadE b -> b -> (b -> [Maybe String]) -> MkOptDescr (a -> b) (b -> a -> a) a
+ Distribution.Simple.Command: optArg :: Monoid b => ArgPlaceHolder -> ReadE b -> (String, b) -> (b -> [Maybe String]) -> MkOptDescr (a -> b) (b -> a -> a) a
- Distribution.Simple.GHC: GhcImplInfo :: Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> GhcImplInfo
+ Distribution.Simple.GHC: GhcImplInfo :: Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> GhcImplInfo
- Distribution.Simple.GHC: buildExe :: Verbosity -> Flag (Maybe Int) -> PackageDescription -> LocalBuildInfo -> Executable -> ComponentLocalBuildInfo -> IO ()
+ Distribution.Simple.GHC: buildExe :: Verbosity -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Executable -> ComponentLocalBuildInfo -> IO ()
- Distribution.Simple.GHC: buildFLib :: Verbosity -> Flag (Maybe Int) -> PackageDescription -> LocalBuildInfo -> ForeignLib -> ComponentLocalBuildInfo -> IO ()
+ Distribution.Simple.GHC: buildFLib :: Verbosity -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> ForeignLib -> ComponentLocalBuildInfo -> IO ()
- Distribution.Simple.GHC: buildLib :: Verbosity -> Flag (Maybe Int) -> PackageDescription -> LocalBuildInfo -> Library -> ComponentLocalBuildInfo -> IO ()
+ Distribution.Simple.GHC: buildLib :: BuildFlags -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Library -> ComponentLocalBuildInfo -> IO ()
- Distribution.Simple.GHC: replExe :: ReplOptions -> Verbosity -> Flag (Maybe Int) -> PackageDescription -> LocalBuildInfo -> Executable -> ComponentLocalBuildInfo -> IO ()
+ Distribution.Simple.GHC: replExe :: ReplFlags -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Executable -> ComponentLocalBuildInfo -> IO ()
- Distribution.Simple.GHC: replFLib :: ReplOptions -> Verbosity -> Flag (Maybe Int) -> PackageDescription -> LocalBuildInfo -> ForeignLib -> ComponentLocalBuildInfo -> IO ()
+ Distribution.Simple.GHC: replFLib :: ReplFlags -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> ForeignLib -> ComponentLocalBuildInfo -> IO ()
- Distribution.Simple.GHC: replLib :: ReplOptions -> Verbosity -> Flag (Maybe Int) -> PackageDescription -> LocalBuildInfo -> Library -> ComponentLocalBuildInfo -> IO ()
+ Distribution.Simple.GHC: replLib :: ReplFlags -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Library -> ComponentLocalBuildInfo -> IO ()
- Distribution.Simple.GHCJS: GhcImplInfo :: Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> GhcImplInfo
+ Distribution.Simple.GHCJS: GhcImplInfo :: Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> Bool -> GhcImplInfo
- Distribution.Simple.GHCJS: buildExe :: Verbosity -> Flag (Maybe Int) -> PackageDescription -> LocalBuildInfo -> Executable -> ComponentLocalBuildInfo -> IO ()
+ Distribution.Simple.GHCJS: buildExe :: Verbosity -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Executable -> ComponentLocalBuildInfo -> IO ()
- Distribution.Simple.GHCJS: buildFLib :: Verbosity -> Flag (Maybe Int) -> PackageDescription -> LocalBuildInfo -> ForeignLib -> ComponentLocalBuildInfo -> IO ()
+ Distribution.Simple.GHCJS: buildFLib :: Verbosity -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> ForeignLib -> ComponentLocalBuildInfo -> IO ()
- Distribution.Simple.GHCJS: buildLib :: Verbosity -> Flag (Maybe Int) -> PackageDescription -> LocalBuildInfo -> Library -> ComponentLocalBuildInfo -> IO ()
+ Distribution.Simple.GHCJS: buildLib :: Verbosity -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Library -> ComponentLocalBuildInfo -> IO ()
- Distribution.Simple.GHCJS: replExe :: [String] -> Verbosity -> Flag (Maybe Int) -> PackageDescription -> LocalBuildInfo -> Executable -> ComponentLocalBuildInfo -> IO ()
+ Distribution.Simple.GHCJS: replExe :: [String] -> Verbosity -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Executable -> ComponentLocalBuildInfo -> IO ()
- Distribution.Simple.GHCJS: replFLib :: [String] -> Verbosity -> Flag (Maybe Int) -> PackageDescription -> LocalBuildInfo -> ForeignLib -> ComponentLocalBuildInfo -> IO ()
+ Distribution.Simple.GHCJS: replFLib :: [String] -> Verbosity -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> ForeignLib -> ComponentLocalBuildInfo -> IO ()
- Distribution.Simple.GHCJS: replLib :: [String] -> Verbosity -> Flag (Maybe Int) -> PackageDescription -> LocalBuildInfo -> Library -> ComponentLocalBuildInfo -> IO ()
+ Distribution.Simple.GHCJS: replLib :: [String] -> Verbosity -> Flag ParStrat -> PackageDescription -> LocalBuildInfo -> Library -> ComponentLocalBuildInfo -> IO ()
- Distribution.Simple.Glob: GlobMissingDirectory :: FilePath -> GlobResult a
+ Distribution.Simple.Glob: GlobMissingDirectory :: a -> GlobResult a
- Distribution.Simple.Glob: fileGlobMatches :: Glob -> FilePath -> Maybe (GlobResult FilePath)
+ Distribution.Simple.Glob: fileGlobMatches :: CabalSpecVersion -> Glob -> FilePath -> Maybe (GlobResult ())
- Distribution.Simple.Glob: matchDirFileGlobWithDie :: Verbosity -> (Verbosity -> String -> IO [FilePath]) -> CabalSpecVersion -> FilePath -> FilePath -> IO [FilePath]
+ Distribution.Simple.Glob: matchDirFileGlobWithDie :: Verbosity -> (Verbosity -> CabalException -> IO [FilePath]) -> CabalSpecVersion -> FilePath -> FilePath -> IO [FilePath]
- Distribution.Simple.Glob: runDirFileGlob :: Verbosity -> FilePath -> Glob -> IO [GlobResult FilePath]
+ Distribution.Simple.Glob: runDirFileGlob :: Verbosity -> Maybe CabalSpecVersion -> FilePath -> Glob -> IO [GlobResult FilePath]
- Distribution.Simple.Hpc: htmlDir :: FilePath -> Way -> FilePath -> FilePath
+ Distribution.Simple.Hpc: htmlDir :: FilePath -> Way -> FilePath
- Distribution.Simple.Hpc: markupPackage :: Verbosity -> LocalBuildInfo -> FilePath -> PackageDescription -> [TestSuite] -> IO ()
+ Distribution.Simple.Hpc: markupPackage :: Verbosity -> HPCMarkupInfo -> LocalBuildInfo -> FilePath -> PackageDescription -> [TestSuite] -> IO ()
- Distribution.Simple.Hpc: mixDir :: FilePath -> Way -> FilePath -> FilePath
+ Distribution.Simple.Hpc: mixDir :: FilePath -> Way -> FilePath
- Distribution.Simple.Hpc: tixDir :: FilePath -> Way -> FilePath -> FilePath
+ Distribution.Simple.Hpc: tixDir :: FilePath -> Way -> FilePath
- Distribution.Simple.PreProcess: ppSuffixes :: [PPSuffixHandler] -> [String]
+ Distribution.Simple.PreProcess: ppSuffixes :: [PPSuffixHandler] -> [Suffix]
- Distribution.Simple.PreProcess: type PPSuffixHandler = (String, BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor)
+ Distribution.Simple.PreProcess: type PPSuffixHandler = (Suffix, BuildInfo -> LocalBuildInfo -> ComponentLocalBuildInfo -> PreProcessor)
- Distribution.Simple.PreProcess.Unlit: unlit :: FilePath -> String -> Either String String
+ Distribution.Simple.PreProcess.Unlit: unlit :: FilePath -> String -> Either String CabalException
- Distribution.Simple.Program: lookupProgramVersion :: Verbosity -> Program -> VersionRange -> ProgramDb -> IO (Either String (ConfiguredProgram, Version, ProgramDb))
+ Distribution.Simple.Program: lookupProgramVersion :: Verbosity -> Program -> VersionRange -> ProgramDb -> IO (Either CabalException (ConfiguredProgram, Version, ProgramDb))
- Distribution.Simple.Program.Db: lookupProgramVersion :: Verbosity -> Program -> VersionRange -> ProgramDb -> IO (Either String (ConfiguredProgram, Version, ProgramDb))
+ Distribution.Simple.Program.Db: lookupProgramVersion :: Verbosity -> Program -> VersionRange -> ProgramDb -> IO (Either CabalException (ConfiguredProgram, Version, ProgramDb))
- Distribution.Simple.Program.GHC: GhcOptions :: Flag GhcMode -> [String] -> [String] -> NubListR FilePath -> NubListR FilePath -> NubListR ModuleName -> Flag FilePath -> Flag FilePath -> Flag Bool -> NubListR FilePath -> Flag String -> Flag ComponentId -> [(ModuleName, OpenModule)] -> Flag Bool -> PackageDBStack -> NubListR (OpenUnitId, ModuleRenaming) -> Flag Bool -> Flag Bool -> Flag Bool -> [FilePath] -> NubListR FilePath -> [String] -> NubListR String -> NubListR String -> Flag Bool -> Flag Bool -> Flag Bool -> NubListR FilePath -> [String] -> [String] -> [String] -> [String] -> NubListR FilePath -> NubListR FilePath -> NubListR FilePath -> Flag FilePath -> Flag Language -> NubListR Extension -> Map Extension (Maybe CompilerFlag) -> Flag GhcOptimisation -> Flag DebugInfoLevel -> Flag Bool -> Flag GhcProfAuto -> Flag Bool -> Flag Bool -> Flag (Maybe Int) -> Flag FilePath -> [FilePath] -> Flag String -> Flag String -> Flag String -> Flag String -> Flag FilePath -> Flag FilePath -> Flag FilePath -> Flag FilePath -> Flag GhcDynLinkMode -> Flag Bool -> Flag Bool -> Flag Bool -> Flag String -> NubListR FilePath -> Flag Verbosity -> NubListR FilePath -> Flag Bool -> GhcOptions
+ Distribution.Simple.Program.GHC: GhcOptions :: Flag GhcMode -> [String] -> [String] -> NubListR FilePath -> NubListR FilePath -> NubListR ModuleName -> Flag FilePath -> Flag FilePath -> Flag Bool -> NubListR FilePath -> Flag String -> Flag ComponentId -> [(ModuleName, OpenModule)] -> Flag Bool -> PackageDBStack -> NubListR (OpenUnitId, ModuleRenaming) -> Flag Bool -> Flag Bool -> Flag Bool -> [FilePath] -> NubListR FilePath -> [String] -> NubListR String -> NubListR String -> Flag Bool -> Flag Bool -> Flag Bool -> NubListR FilePath -> [String] -> [String] -> [String] -> [String] -> NubListR FilePath -> NubListR FilePath -> NubListR FilePath -> Flag FilePath -> Flag Language -> NubListR Extension -> Map Extension (Maybe CompilerFlag) -> Flag GhcOptimisation -> Flag DebugInfoLevel -> Flag Bool -> Flag GhcProfAuto -> Flag Bool -> Flag Bool -> Flag ParStrat -> Flag FilePath -> [FilePath] -> Flag String -> Flag String -> Flag String -> Flag String -> Flag FilePath -> Flag FilePath -> Flag FilePath -> Flag FilePath -> Flag FilePath -> Flag GhcDynLinkMode -> Flag Bool -> Flag Bool -> Flag Bool -> Flag String -> NubListR FilePath -> Flag Verbosity -> NubListR FilePath -> Flag Bool -> GhcOptions
- Distribution.Simple.Program.GHC: [ghcOptNumJobs] :: GhcOptions -> Flag (Maybe Int)
+ Distribution.Simple.Program.GHC: [ghcOptNumJobs] :: GhcOptions -> Flag ParStrat
- Distribution.Simple.Setup: BuildFlags :: [(String, FilePath)] -> [(String, [String])] -> Flag FilePath -> Flag Verbosity -> Flag (Maybe Int) -> [String] -> Flag FilePath -> BuildFlags
+ Distribution.Simple.Setup: BuildFlags :: [(String, FilePath)] -> [(String, [String])] -> Flag FilePath -> Flag Verbosity -> Flag (Maybe Int) -> Flag String -> [String] -> Flag FilePath -> BuildFlags
- Distribution.Simple.Setup: ConfigFlags :: [String] -> Option' (Last' ProgramDb) -> [(String, FilePath)] -> [(String, [String])] -> NubList FilePath -> Flag CompilerFlavor -> Flag FilePath -> Flag FilePath -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag ProfDetailLevel -> Flag ProfDetailLevel -> [String] -> Flag OptimisationLevel -> Flag PathTemplate -> Flag PathTemplate -> InstallDirs (Flag PathTemplate) -> Flag FilePath -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> Flag String -> Flag ComponentId -> Flag Bool -> Flag FilePath -> Flag FilePath -> Flag Verbosity -> Flag Bool -> [Maybe PackageDB] -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> [PackageVersionConstraint] -> [GivenComponent] -> [(ModuleName, Module)] -> FlagAssignment -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag String -> Flag Bool -> Flag DebugInfoLevel -> Flag DumpBuildInfo -> Flag Bool -> Flag Bool -> ConfigFlags
+ Distribution.Simple.Setup: ConfigFlags :: [String] -> Option' (Last' ProgramDb) -> [(String, FilePath)] -> [(String, [String])] -> NubList FilePath -> Flag CompilerFlavor -> Flag FilePath -> Flag FilePath -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag ProfDetailLevel -> Flag ProfDetailLevel -> [String] -> Flag OptimisationLevel -> Flag PathTemplate -> Flag PathTemplate -> InstallDirs (Flag PathTemplate) -> Flag FilePath -> [FilePath] -> [FilePath] -> [FilePath] -> [FilePath] -> Flag String -> Flag ComponentId -> Flag Bool -> Flag FilePath -> Flag FilePath -> Flag Verbosity -> Flag Bool -> [Maybe PackageDB] -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> [PackageVersionConstraint] -> [GivenComponent] -> [GivenComponent] -> [(ModuleName, Module)] -> FlagAssignment -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag String -> Flag Bool -> Flag DebugInfoLevel -> Flag DumpBuildInfo -> Flag Bool -> Flag Bool -> Flag [UnitId] -> ConfigFlags
- Distribution.Simple.Setup: HaddockFlags :: [(String, FilePath)] -> [(String, [String])] -> Flag Bool -> Flag Bool -> Flag String -> Flag HaddockTarget -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag FilePath -> Flag Bool -> Flag Bool -> Flag FilePath -> Flag PathTemplate -> Flag PathTemplate -> Flag FilePath -> Flag Bool -> Flag Verbosity -> Flag FilePath -> Flag String -> Flag String -> [String] -> HaddockFlags
+ Distribution.Simple.Setup: HaddockFlags :: [(String, FilePath)] -> [(String, [String])] -> Flag Bool -> Flag Bool -> Flag String -> Flag HaddockTarget -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag FilePath -> Flag Bool -> Flag Bool -> Flag FilePath -> Flag PathTemplate -> Flag PathTemplate -> Flag FilePath -> Flag Bool -> Flag Verbosity -> Flag FilePath -> Flag String -> Flag String -> Flag FilePath -> [String] -> HaddockFlags
- Distribution.Simple.Setup: HaddockProjectFlags :: Flag Bool -> Flag Bool -> Flag String -> Flag String -> Flag Bool -> Flag Bool -> Flag [(FilePath, Maybe FilePath, Maybe FilePath, Visibility)] -> [(String, FilePath)] -> [(String, [String])] -> Flag Bool -> Flag String -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag FilePath -> Flag Bool -> Flag Bool -> Flag FilePath -> Flag Bool -> Flag Verbosity -> Flag String -> HaddockProjectFlags
+ Distribution.Simple.Setup: HaddockProjectFlags :: Flag Bool -> Flag String -> Flag String -> Flag [(FilePath, Maybe FilePath, Maybe FilePath, Visibility)] -> [(String, FilePath)] -> [(String, [String])] -> Flag Bool -> Flag String -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag Bool -> Flag FilePath -> Flag FilePath -> Flag Bool -> Flag Verbosity -> Flag String -> Flag FilePath -> HaddockProjectFlags
- Distribution.Simple.Setup: ReplOptions :: [String] -> Flag Bool -> ReplOptions
+ Distribution.Simple.Setup: ReplOptions :: [String] -> Flag Bool -> Flag FilePath -> ReplOptions
- Distribution.Simple.SrcDist: listPackageSourcesWithDie :: Verbosity -> (Verbosity -> String -> IO [FilePath]) -> FilePath -> PackageDescription -> [PPSuffixHandler] -> IO [FilePath]
+ Distribution.Simple.SrcDist: listPackageSourcesWithDie :: Verbosity -> (Verbosity -> CabalException -> IO [FilePath]) -> FilePath -> PackageDescription -> [PPSuffixHandler] -> IO [FilePath]
- Distribution.Simple.Test.ExeV10: runTest :: PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> TestFlags -> TestSuite -> IO TestSuiteLog
+ Distribution.Simple.Test.ExeV10: runTest :: PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> HPCMarkupInfo -> TestFlags -> TestSuite -> IO TestSuiteLog
- Distribution.Simple.Test.LibV09: runTest :: PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> TestFlags -> TestSuite -> IO TestSuiteLog
+ Distribution.Simple.Test.LibV09: runTest :: PackageDescription -> LocalBuildInfo -> ComponentLocalBuildInfo -> HPCMarkupInfo -> TestFlags -> TestSuite -> IO TestSuiteLog
- Distribution.Simple.Utils: findAllFilesCwdWithExtension :: FilePath -> [String] -> [FilePath] -> FilePath -> IO [FilePath]
+ Distribution.Simple.Utils: findAllFilesCwdWithExtension :: FilePath -> [Suffix] -> [FilePath] -> FilePath -> IO [FilePath]
- Distribution.Simple.Utils: findAllFilesWithExtension :: [String] -> [FilePath] -> FilePath -> IO [FilePath]
+ Distribution.Simple.Utils: findAllFilesWithExtension :: [Suffix] -> [FilePath] -> FilePath -> IO [FilePath]
- Distribution.Simple.Utils: findFileCwdWithExtension :: FilePath -> [String] -> [FilePath] -> FilePath -> IO (Maybe FilePath)
+ Distribution.Simple.Utils: findFileCwdWithExtension :: FilePath -> [Suffix] -> [FilePath] -> FilePath -> IO (Maybe FilePath)
- Distribution.Simple.Utils: findFileWithExtension :: [String] -> [FilePath] -> FilePath -> IO (Maybe FilePath)
+ Distribution.Simple.Utils: findFileWithExtension :: [Suffix] -> [FilePath] -> FilePath -> IO (Maybe FilePath)
- Distribution.Simple.Utils: findFileWithExtension' :: [String] -> [FilePath] -> FilePath -> IO (Maybe (FilePath, FilePath))
+ Distribution.Simple.Utils: findFileWithExtension' :: [Suffix] -> [FilePath] -> FilePath -> IO (Maybe (FilePath, FilePath))
- Distribution.Simple.Utils: findModuleFileEx :: Verbosity -> [FilePath] -> [String] -> ModuleName -> IO (FilePath, FilePath)
+ Distribution.Simple.Utils: findModuleFileEx :: Verbosity -> [FilePath] -> [Suffix] -> ModuleName -> IO (FilePath, FilePath)
- Distribution.Simple.Utils: findModuleFilesEx :: Verbosity -> [FilePath] -> [String] -> [ModuleName] -> IO [(FilePath, FilePath)]
+ Distribution.Simple.Utils: findModuleFilesEx :: Verbosity -> [FilePath] -> [Suffix] -> [ModuleName] -> IO [(FilePath, FilePath)]
- Distribution.Simple.Utils: findPackageDesc :: FilePath -> IO (Either String FilePath)
+ Distribution.Simple.Utils: findPackageDesc :: FilePath -> IO (Either CabalException FilePath)
- Distribution.Simple.Utils: findPackageDescCwd :: FilePath -> FilePath -> IO (Either String FilePath)
+ Distribution.Simple.Utils: findPackageDescCwd :: FilePath -> FilePath -> IO (Either CabalException FilePath)
Files
- Cabal.cabal +38/−5
- ChangeLog.md +3/−0
- LICENSE +1/−1
- README.md +2/−2
- Setup.hs +1/−0
- src/Distribution/Backpack/ComponentsGraph.hs +51/−43
- src/Distribution/Backpack/Configure.hs +358/−251
- src/Distribution/Backpack/ConfiguredComponent.hs +235/−192
- src/Distribution/Backpack/DescribeUnitId.hs +32/−21
- src/Distribution/Backpack/FullUnitId.hs +13/−12
- src/Distribution/Backpack/Id.hs +83/−72
- src/Distribution/Backpack/LinkedComponent.hs +301/−213
- src/Distribution/Backpack/MixLink.hs +168/−132
- src/Distribution/Backpack/ModSubst.hs +22/−22
- src/Distribution/Backpack/ModuleScope.hs +52/−49
- src/Distribution/Backpack/ModuleShape.hs +21/−20
- src/Distribution/Backpack/PreExistingComponent.hs +51/−39
- src/Distribution/Backpack/PreModuleShape.hs +18/−18
- src/Distribution/Backpack/ReadyComponent.hs +271/−235
- src/Distribution/Backpack/UnifyM.hs +311/−270
- src/Distribution/Compat/Async.hs +48/−30
- src/Distribution/Compat/CopyFile.hs +16/−14
- src/Distribution/Compat/Directory.hs +4/−5
- src/Distribution/Compat/Environment.hs +6/−5
- src/Distribution/Compat/FilePath.hs +4/−5
- src/Distribution/Compat/GetShortPathName.hs +6/−4
- src/Distribution/Compat/Internal/TempFile.hs +18/−13
- src/Distribution/Compat/Prelude/Internal.hs +3/−3
- src/Distribution/Compat/Process.hs +9/−7
- src/Distribution/Compat/ResponseFile.hs +27/−5
- src/Distribution/Compat/SnocList.hs +10/−9
- src/Distribution/Compat/Stack.hs +17/−15
- src/Distribution/Compat/Time.hs +27/−22
- src/Distribution/GetOpt.hs +201/−157
- src/Distribution/Lex.hs +32/−22
- src/Distribution/Make.hs +48/−43
- src/Distribution/PackageDescription/Check.hs +1084/−2939
- src/Distribution/PackageDescription/Check/Common.hs +149/−0
- src/Distribution/PackageDescription/Check/Conditional.hs +265/−0
- src/Distribution/PackageDescription/Check/Monad.hs +371/−0
- src/Distribution/PackageDescription/Check/Paths.hs +417/−0
- src/Distribution/PackageDescription/Check/Target.hs +1060/−0
- src/Distribution/PackageDescription/Check/Warning.hs +1473/−0
- src/Distribution/ReadE.hs +28/−20
- src/Distribution/Simple.hs +599/−386
- src/Distribution/Simple/Bench.hs +100/−82
- src/Distribution/Simple/Build.hs +1049/−829
- src/Distribution/Simple/Build/Inputs.hs +74/−0
- src/Distribution/Simple/Build/Macros.hs +58/−54
- src/Distribution/Simple/Build/Macros/Z.hs +1/−0
- src/Distribution/Simple/Build/PackageInfoModule.hs +20/−16
- src/Distribution/Simple/Build/PackageInfoModule/Z.hs +11/−11
- src/Distribution/Simple/Build/PathsModule.hs +96/−90
- src/Distribution/Simple/Build/PathsModule/Z.hs +1/−0
- src/Distribution/Simple/BuildPaths.hs +134/−105
- src/Distribution/Simple/BuildTarget.hs +1109/−1063
- src/Distribution/Simple/BuildToolDepends.hs +48/−27
- src/Distribution/Simple/CCompiler.hs +66/−52
- src/Distribution/Simple/Command.hs +557/−374
- src/Distribution/Simple/Compiler.hs +219/−189
- src/Distribution/Simple/Configure.hs +2694/−2138
- src/Distribution/Simple/ConfigureScript.hs +106/−77
- src/Distribution/Simple/Errors.hs +800/−0
- src/Distribution/Simple/Flag.hs +65/−40
- src/Distribution/Simple/GHC.hs +1014/−2185
- src/Distribution/Simple/GHC/Build.hs +140/−0
- src/Distribution/Simple/GHC/Build/ExtraSources.hs +242/−0
- src/Distribution/Simple/GHC/Build/Link.hs +663/−0
- src/Distribution/Simple/GHC/Build/Modules.hs +352/−0
- src/Distribution/Simple/GHC/Build/Utils.hs +217/−0
- src/Distribution/Simple/GHC/EnvironmentParser.hs +32/−22
- src/Distribution/Simple/GHC/ImplInfo.hs +105/−69
- src/Distribution/Simple/GHC/Internal.hs +875/−719
- src/Distribution/Simple/GHCJS.hs +2031/−1838
- src/Distribution/Simple/Glob.hs +73/−290
- src/Distribution/Simple/Glob/Internal.hs +497/−0
- src/Distribution/Simple/Haddock.hs +1235/−1036
- src/Distribution/Simple/HaskellSuite.hs +121/−82
- src/Distribution/Simple/Hpc.hs +126/−123
- src/Distribution/Simple/Install.hs +217/−157
- src/Distribution/Simple/InstallDirs.hs +284/−237
- src/Distribution/Simple/InstallDirs/Internal.hs +129/−96
- src/Distribution/Simple/LocalBuildInfo.hs +311/−228
- src/Distribution/Simple/PackageDescription.hs +71/−58
- src/Distribution/Simple/PackageIndex.hs +373/−311
- src/Distribution/Simple/PreProcess.hs +918/−837
- src/Distribution/Simple/PreProcess/Types.hs +114/−0
- src/Distribution/Simple/PreProcess/Unlit.hs +101/−91
- src/Distribution/Simple/Program.hs +121/−112
- src/Distribution/Simple/Program/Ar.hs +153/−120
- src/Distribution/Simple/Program/Builtin.hs +260/−230
- src/Distribution/Simple/Program/Db.hs +235/−239
- src/Distribution/Simple/Program/Find.hs +68/−59
- src/Distribution/Simple/Program/GHC.hs +911/−855
- src/Distribution/Simple/Program/HcPkg.hs +289/−265
- src/Distribution/Simple/Program/Hpc.hs +93/−64
- src/Distribution/Simple/Program/Internal.hs +21/−22
- src/Distribution/Simple/Program/Ld.hs +55/−41
- src/Distribution/Simple/Program/ResponseFile.hs +21/−15
- src/Distribution/Simple/Program/Run.hs +158/−138
- src/Distribution/Simple/Program/Script.hs +75/−75
- src/Distribution/Simple/Program/Strip.hs +42/−34
- src/Distribution/Simple/Program/Types.hs +92/−104
- src/Distribution/Simple/Register.hs +491/−359
- src/Distribution/Simple/Setup.hs +191/−2557
- src/Distribution/Simple/Setup/Benchmark.hs +131/−0
- src/Distribution/Simple/Setup/Build.hs +165/−0
- src/Distribution/Simple/Setup/Clean.hs +97/−0
- src/Distribution/Simple/Setup/Common.hs +372/−0
- src/Distribution/Simple/Setup/Config.hs +1064/−0
- src/Distribution/Simple/Setup/Copy.hs +153/−0
- src/Distribution/Simple/Setup/Global.hs +122/−0
- src/Distribution/Simple/Setup/Haddock.hs +606/−0
- src/Distribution/Simple/Setup/Hscolour.hs +157/−0
- src/Distribution/Simple/Setup/Install.hs +158/−0
- src/Distribution/Simple/Setup/Register.hs +195/−0
- src/Distribution/Simple/Setup/Repl.hs +205/−0
- src/Distribution/Simple/Setup/SDist.hs +113/−0
- src/Distribution/Simple/Setup/Test.hs +254/−0
- src/Distribution/Simple/ShowBuildInfo.hs +67/−51
- src/Distribution/Simple/SrcDist.hs +363/−310
- src/Distribution/Simple/Test.hs +146/−89
- src/Distribution/Simple/Test/ExeV10.hs +180/−131
- src/Distribution/Simple/Test/LibV09.hs +220/−173
- src/Distribution/Simple/Test/Log.hs +113/−86
- src/Distribution/Simple/UHC.hs +218/−149
- src/Distribution/Simple/UserHooks.hs +138/−149
- src/Distribution/Simple/Utils.hs +1869/−1620
- src/Distribution/TestSuite.hs +73/−66
- src/Distribution/Types/AnnotatedId.hs +15/−16
- src/Distribution/Types/ComponentInclude.hs +15/−15
- src/Distribution/Types/ComponentLocalBuildInfo.hs +96/−97
- src/Distribution/Types/DumpBuildInfo.hs +3/−2
- src/Distribution/Types/GivenComponent.hs +10/−9
- src/Distribution/Types/LocalBuildConfig.hs +226/−0
- src/Distribution/Types/LocalBuildInfo.hs +312/−180
- src/Distribution/Types/ParStrat.hs +24/−0
- src/Distribution/Types/TargetInfo.hs +24/−18
- src/Distribution/Utils/IOData.hs +51/−47
- src/Distribution/Utils/Json.hs +38/−33
- src/Distribution/Utils/LogProgress.hs +39/−37
- src/Distribution/Utils/MapAccum.hs +14/−8
- src/Distribution/Utils/NubList.hs +25/−28
- src/Distribution/Utils/Progress.hs +25/−22
- src/Distribution/Utils/UnionFind.hs +46/−44
- src/Distribution/Verbosity.hs +124/−107
- src/Distribution/Verbosity/Internal.hs +14/−12
- src/Distribution/ZinzaPrelude.hs +23/−22
Cabal.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.2 name: Cabal-version: 3.10.3.0-copyright: 2003-2023, Cabal Development Team (see AUTHORS file)+version: 3.12.0.0+copyright: 2003-2024, Cabal Development Team (see AUTHORS file) license: BSD-3-Clause license-file: LICENSE author: Cabal Development Team <cabal-devel@haskell.org>@@ -34,7 +34,7 @@ hs-source-dirs: src build-depends:- Cabal-syntax ^>= 3.10,+ Cabal-syntax ^>= 3.12, array >= 0.4.0.1 && < 0.6, base >= 4.9 && < 5, bytestring >= 0.10.0.0 && < 0.13,@@ -44,7 +44,7 @@ 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.13+ time >= 1.4.0.1 && < 1.15 if os(windows) build-depends: Win32 >= 2.3.0.0 && < 2.15@@ -59,6 +59,9 @@ if impl(ghc >= 8.0) && impl(ghc < 8.8) ghc-options: -Wnoncanonical-monadfail-instances + if impl(ghc >= 8.10)+ ghc-options: -Wunused-packages+ exposed-modules: Distribution.Backpack.Configure Distribution.Backpack.ComponentsGraph@@ -88,6 +91,7 @@ Distribution.Simple Distribution.Simple.Bench Distribution.Simple.Build+ Distribution.Simple.Build.Inputs Distribution.Simple.Build.Macros Distribution.Simple.Build.PackageInfoModule Distribution.Simple.Build.PathsModule@@ -98,11 +102,13 @@ Distribution.Simple.Command Distribution.Simple.Compiler Distribution.Simple.Configure+ Distribution.Simple.Errors Distribution.Simple.Flag Distribution.Simple.GHC Distribution.Simple.GHCJS Distribution.Simple.Haddock Distribution.Simple.Glob+ Distribution.Simple.Glob.Internal Distribution.Simple.HaskellSuite Distribution.Simple.Hpc Distribution.Simple.Install@@ -112,6 +118,7 @@ Distribution.Simple.PackageDescription Distribution.Simple.PackageIndex Distribution.Simple.PreProcess+ Distribution.Simple.PreProcess.Types Distribution.Simple.PreProcess.Unlit Distribution.Simple.Program Distribution.Simple.Program.Ar@@ -145,9 +152,11 @@ Distribution.Types.DumpBuildInfo Distribution.Types.PackageName.Magic Distribution.Types.ComponentLocalBuildInfo+ Distribution.Types.LocalBuildConfig Distribution.Types.LocalBuildInfo Distribution.Types.TargetInfo Distribution.Types.GivenComponent+ Distribution.Types.ParStrat Distribution.Utils.Json Distribution.Utils.NubList Distribution.Utils.Progress@@ -302,7 +311,6 @@ -- See also https://github.com/ekmett/transformers-compat/issues/35 transformers (>= 0.3 && < 0.4) || (>=0.4.1.0 && <0.7), mtl >= 2.1 && < 2.4,- text (>= 1.2.3.0 && < 1.3) || (>= 2.0 && < 2.2), parsec >= 3.1.13.0 && < 3.2 other-modules:@@ -319,13 +327,38 @@ Distribution.Compat.SnocList Distribution.GetOpt Distribution.Lex+ 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.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
ChangeLog.md view
@@ -1,3 +1,6 @@+# 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
LICENSE view
@@ -1,4 +1,4 @@-Copyright (c) 2003-2023, Cabal Development Team.+Copyright (c) 2003-2024, 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
@@ -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/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,60 @@ {-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE NondecreasingIndentation #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PatternGuards #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE PatternGuards #-} {-# 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+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 +63,233 @@ ------------------------------------------------------------------------------ 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], [PromisedComponent])+ -> 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+ [ ( 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+ ]+ `Map.union` Map.fromListWith+ Map.union+ [ (pkg, Map.singleton (ann_cname aid) aid)+ | PromisedComponent 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+ )+ )+ | PromisedComponent _ 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+ -> [PromisedComponent]+ -> 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,68 @@ {-# 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+-- | 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 -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 +81,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 +216,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 +305,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 +348,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,86 @@ {-# LANGUAGE FlexibleInstances #-}-{-# LANGUAGE RankNTypes #-} {-# LANGUAGE PatternGuards #-}+{-# 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 (..)) 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 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+ -- 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 +128,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,22 +226,26 @@ 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- 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- })+ 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@@ -225,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@@ -250,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] -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~@@ -336,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,31 @@+{-# LANGUAGE DeriveFoldable #-} {-# 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 +33,6 @@ import qualified Data.Map as Map import Text.PrettyPrint - ----------------------------------------------------------------------- -- Module scopes @@ -60,17 +60,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 +78,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 +132,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,22 @@ {-# 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 +27,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, Typeable) 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 +79,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 (..)+ , PromisedComponent (..)+ , 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 PromisedComponent = PromisedComponent+ { pr_pkgname :: PackageName+ , pr_cid :: AnnotatedId ComponentId+ }++instance Package PromisedComponent 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,38 @@ {-# 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 +41,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,9 +384,9 @@ 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 @@ -390,34 +394,36 @@ -- | 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+ 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 (pretty mod_name)- 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."+ 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 @@ -431,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) @@ -447,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@@ -498,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. --@@ -533,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:@@ -569,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/Compat/Async.hs view
@@ -1,5 +1,6 @@-{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveDataTypeable #-}+ -- | 'Async', yet using 'MVar's. -- -- Adopted from @async@ library@@ -7,24 +8,38 @@ -- 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)+ ( 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)@@ -33,9 +48,9 @@ -- | 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 +67,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 +83,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 +95,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,15 +137,18 @@ {-# 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+ deriving+ ( Show+ , Eq , Typeable ) +{- FOURMOLU_DISABLE -} instance Exception AsyncCancelled where #if MIN_VERSION_base(4,7,0) -- wraps in SomeAsyncException@@ -139,6 +156,7 @@ fromException = asyncExceptionFromException toException = asyncExceptionToException #endif+{- FOURMOLU_ENABLE -} -- | Cancel an asynchronous action --
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@@ -66,7 +67,7 @@ #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 ()@@ -93,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) ->@@ -239,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/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,16 +3,15 @@ {-# 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 -import qualified System.Environment as System import System.Environment (lookupEnv, unsetEnv)+import qualified System.Environment as System import Distribution.Compat.Stack @@ -61,6 +60,7 @@ where _ = callStack -- TODO: attach CallStack to exception +{- FOURMOLU_DISABLE -} # if defined(i386_HOST_ARCH) # define WINDOWS_CCONV stdcall # elif defined(x86_64_HOST_ARCH)@@ -83,3 +83,4 @@ foreign import ccall unsafe "setenv" c_setenv :: CString -> CString -> CInt -> Prelude.IO CInt #endif /* mingw32_HOST_OS */+{- FOURMOLU_ENABLE -}
src/Distribution/Compat/FilePath.hs view
@@ -1,13 +1,12 @@ {-# LANGUAGE CPP #-}- {-# OPTIONS_GHC -fno-warn-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,6 +22,7 @@ import System.Win32 (LPCTSTR, LPTSTR, DWORD) import Foreign.Marshal.Array (allocaArray) +{- FOURMOLU_DISABLE -} #ifdef x86_64_HOST_ARCH #define WINAPI ccall #else@@ -57,3 +58,4 @@ getShortPathName path = return path #endif+{- FOURMOLU_ENABLE -}
src/Distribution/Compat/Internal/TempFile.hs view
@@ -1,17 +1,18 @@ {-# 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.FilePath ((</>)) -import System.IO (Handle, openTempFile, openBinaryTempFile)+import System.IO (Handle, openBinaryTempFile, openTempFile) #if defined(__IO_MANAGER_WINIO__) import System.IO (openBinaryTempFileWithDefaultPermissions) #else@@ -23,8 +24,8 @@ o_CREAT, o_RDWR, o_NONBLOCK, o_NOCTTY) #endif +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 )@@ -33,7 +34,9 @@ #endif -- ------------------------------------------------------------+ -- * temporary files+ -- ------------------------------------------------------------ -- This is here for Haskell implementations that do not come with@@ -46,9 +49,10 @@ -- 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.++-- 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@@ -126,8 +130,9 @@ r <- tryIO $ mkPrivateDir dirpath case r of Right _ -> return dirpath- Left e | isAlreadyExistsError e -> findTempName (x+1)- | otherwise -> ioError e+ 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/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,12 +1,14 @@ {-# LANGUAGE CPP #-}-module Distribution.Compat.Process (- -- * Redefined functions- proc,++module Distribution.Compat.Process+ ( -- * Redefined functions+ proc+ -- * Additions- enableProcessJobs,- ) where+ , enableProcessJobs+ ) where -import System.Process (CreateProcess)+import System.Process (CreateProcess) import qualified System.Process as Process #if defined(mingw32_HOST_OS) && MIN_VERSION_process(1,6,9)@@ -58,4 +60,4 @@ -- | '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 }+proc path args = enableProcessJobs (Process.proc path args){Process.delegate_ctlc = True}
src/Distribution/Compat/ResponseFile.hs view
@@ -1,9 +1,11 @@-{-# LANGUAGE CPP, RankNTypes, FlexibleContexts #-}+{-# LANGUAGE CPP #-}+{-# 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 Prelude ()@@ -13,7 +15,7 @@ import System.IO.Error #if MIN_VERSION_base(4,12,0)-import GHC.ResponseFile (unescapeArgs)+import GHC.ResponseFile (unescapeArgs, escapeArgs) #else unescapeArgs :: String -> [String]@@ -47,8 +49,28 @@ | '"' == c = go cs DblQ False a as | otherwise = go cs NoneQ False (c:a) as +escapeArgs :: [String] -> String+escapeArgs = unlines . map escapeArg++escapeArg :: String -> String+escapeArg = reverse . foldl' escape []++escape :: String -> Char -> String+escape cs c+ | isSpace c+ || '\\' == c+ || '\'' == c+ || '"' == c = c:'\\':cs -- n.b., our caller must reverse the result+ | otherwise = c:cs+ #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 +78,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/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,17 +1,18 @@ {-# LANGUAGE CPP #-}-{-# LANGUAGE RankNTypes #-} {-# LANGUAGE ImplicitParams #-}-module Distribution.Compat.Stack (- WithCallStack,- CallStack,- annotateCallStackIO,- withFrozenCallStack,- withLexicalCallStack,- callStack,- prettyCallStack,- parentSrcLocPrefix-) where+{-# LANGUAGE RankNTypes #-} +module Distribution.Compat.Stack+ ( WithCallStack+ , CallStack+ , annotateCallStackIO+ , withFrozenCallStack+ , withLexicalCallStack+ , callStack+ , prettyCallStack+ , parentSrcLocPrefix+ ) where+ import System.IO.Error #ifdef MIN_VERSION_base@@ -106,8 +107,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,32 +1,33 @@ {-# 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 (withTempDirectory)+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 @@ -57,7 +58,7 @@ -- | 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, Typeable) instance Structured ModTime @@ -101,6 +102,7 @@ #endif return $! ModTime (qwTime :: Word64) +{- FOURMOLU_DISABLE -} #ifdef x86_64_HOST_ARCH #define CALLCONV ccall #else@@ -138,9 +140,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 +153,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@@ -174,15 +179,15 @@ calibrateMtimeChangeDelay = withTempDirectory silent "." "calibration-" $ \dir -> do let fileName = dir </> "probe"- mtimes <- for [1..25] $ \(i::Int) -> time $ do+ 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/GetOpt.hs view
@@ -1,4 +1,8 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE TupleSections #-}+ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.GetOpt -- Copyright : (c) Sven Panne 2002-2005@@ -20,78 +24,84 @@ -- * Parsing of option arguments is allowed to fail. -- -- * 'ReturnInOrder' argument order is removed.----{-# LANGUAGE TupleSections #-}-{-# LANGUAGE NamedFieldPuns #-}-module Distribution.GetOpt (- -- * GetOpt- getOpt, getOpt',- usageInfo,- ArgOrder(..),- OptDescr(..),- ArgDescr(..),+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 Prelude () -- | What to do with options following non-options data ArgOrder a- = RequireOrder -- ^ no option processing after first non-option- | Permute -- ^ freely intersperse options and non-options+ = -- | no option processing after first non-option+ RequireOrder+ | -- | freely intersperse options and non-options+ Permute -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+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+ 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- = NoArg a -- ^ no argument expected- | ReqArg (String -> Either String a) String -- ^ option requires argument- | OptArg (Maybe String -> Either String a) String -- ^ optional argument+ = -- | 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 g s) = OptArg (fmap f . g) s+ 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 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- }+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 description 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 } @@ -103,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]@@ -112,146 +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 dislayed 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 dislayed 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 dislayed as:+-- @--with-compiler=PATH@+-- * With optional but default arguments can be given as:+-- @--jobs or --jobs=NUM (but not --jobs NUM)@+-- This is dislayed 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 EndOfOpts RequireOrder = ([],rest,[],[])- procNextOpt EndOfOpts Permute = ([],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- fromRes = fromParseResult optStr+ 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) = (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)+ 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]- fromRes = fromParseResult optStr+ 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) = (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?+ 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")+ Right x -> Opt x+ Left err -> OptErr ("invalid argument to option `" ++ optStr ++ "': " ++ err ++ "\n") -- miscellaneous error formatting 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/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/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,28 @@ -- [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.Package+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 Distribution.Version import System.Environment (getArgs, getProgName) @@ -87,20 +88,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,20 +110,22 @@ 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 ()@@ -130,16 +134,17 @@ let verbosity = fromFlag (configVerbosity flags) rawSystemExit verbosity "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"+ NoCopyDest -> ["install"]+ CopyTo path -> ["copy", "destdir=" ++ path]+ CopyToDb _ -> error "CopyToDb not supported via Make" rawSystemExit (fromFlag $ copyVerbosity flags) "make" destArgs @@ -154,7 +159,7 @@ noExtraFlags args rawSystemExit (fromFlag $ haddockVerbosity flags) "make" ["docs"] `catchIO` \_ ->- rawSystemExit (fromFlag $ haddockVerbosity flags) "make" ["doc"]+ rawSystemExit (fromFlag $ haddockVerbosity flags) "make" ["doc"] buildAction :: BuildFlags -> [String] -> IO () buildAction flags args = do@@ -172,7 +177,7 @@ rawSystemExit (fromFlag $ sDistVerbosity flags) "make" ["dist"] registerAction :: RegisterFlags -> [String] -> IO ()-registerAction flags args = do+registerAction flags args = do noExtraFlags args rawSystemExit (fromFlag $ regVerbosity flags) "make" ["register"]
src/Distribution/PackageDescription/Check.hs view
@@ -1,2939 +1,1084 @@--------------------------------------------------------------------------------- |--- 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- CheckExplanation(..),- PackageCheck(..),- checkPackage,- checkConfiguredPackage,- wrapParseWarning,- ppPackageCheck,-- -- ** Checking package contents- checkPackageFiles,- checkPackageContent,- CheckPackageContentOps(..),- checkPackageFileNames,- ) where--import Data.Foldable (foldrM)-import Distribution.Compat.Prelude-import Prelude ()--import Data.List (delete, group)-import Distribution.CabalSpecVersion-import Distribution.Compat.Lens-import Distribution.Compiler-import Distribution.License-import Distribution.ModuleName (ModuleName)-import Distribution.Package-import Distribution.PackageDescription-import Distribution.PackageDescription.Configuration-import Distribution.Parsec.Warning (PWarning, showPWarning)-import Distribution.Pretty (prettyShow)-import Distribution.Simple.BuildPaths (autogenPackageInfoModuleName, 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.Types.PackageName.Magic-import Distribution.Utils.Generic (isAscii)-import Distribution.Verbosity-import Distribution.Version-import Distribution.Utils.Path-import Language.Haskell.Extension-import System.FilePath- ( makeRelative, normalise, splitDirectories, splitExtension, splitPath- , takeExtension, takeFileName, (<.>), (</>))--import qualified Data.ByteString.Lazy as BS-import qualified Data.Map as Map-import qualified Control.Monad as CM-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 ((&&&))---- --------------------------------------------------------------- * Warning messages--- ---------------------------------------------------------------- | Which stanza does `CheckExplanation` refer to?----data CEType = CETLibrary | CETExecutable | CETTest | CETBenchmark- deriving (Eq, Ord, Show)---- | Pretty printing `CEType`.----ppCE :: CEType -> String-ppCE CETLibrary = "library"-ppCE CETExecutable = "executable"-ppCE CETTest = "test suite"-ppCE CETBenchmark = "benchmark"---- | Which field does `CheckExplanation` refer to?----data CEField = CEFCategory | CEFMaintainer | CEFSynopsis- | CEFDescription | CEFSynOrDesc- deriving (Eq, Ord, Show)---- | Pretty printing `CEField`.----ppCEField :: CEField -> String-ppCEField CEFCategory = "category"-ppCEField CEFMaintainer = "maintainer"-ppCEField CEFSynopsis = "synopsis"-ppCEField CEFDescription = "description"-ppCEField CEFSynOrDesc = "synopsis' or 'description"---- | Explanations of 'PackageCheck`'s errors/warnings.----data CheckExplanation =- ParseWarning FilePath PWarning- | NoNameField- | NoVersionField- | NoTarget- | UnnamedInternal- | DuplicateSections [UnqualComponentName]- | IllegalLibraryName PackageDescription- | NoModulesExposed Library- | SignaturesCabal2- | AutogenNotExposed- | AutogenIncludesNotIncluded- | NoMainIs Executable- | NoHsLhsMain- | MainCCabal1_18- | AutogenNoOther CEType UnqualComponentName- | AutogenIncludesNotIncludedExe- | TestsuiteTypeNotKnown TestType- | TestsuiteNotSupported TestType- | BenchmarkTypeNotKnown BenchmarkType- | BenchmarkNotSupported BenchmarkType- | NoHsLhsMainBench- | InvalidNameWin PackageDescription- | ZPrefix- | NoBuildType- | NoCustomSetup- | UnknownCompilers [String]- | UnknownLanguages [String]- | UnknownExtensions [String]- | LanguagesAsExtension [String]- | DeprecatedExtensions [(Extension, Maybe Extension)]- | MissingField CEField- | SynopsisTooLong- | ShortDesc- | InvalidTestWith [Dependency]- | ImpossibleInternalDep [Dependency]- | ImpossibleInternalExe [ExeDependency]- | MissingInternalExe [ExeDependency]- | NONELicense- | NoLicense- | AllRightsReservedLicense- | LicenseMessParse PackageDescription- | UnrecognisedLicense String- | UncommonBSD4- | UnknownLicenseVersion License [Version]- | NoLicenseFile- | UnrecognisedSourceRepo String- | MissingType- | MissingLocation- | MissingModule- | MissingTag- | SubdirRelPath- | SubdirGoodRelPath String- | OptFasm String- | OptViaC 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 String- | COptCPP 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- | 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 [PackageName]- | SuspiciousFlagName [String]- | DeclaredUsedFlags (Set FlagName) (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 (SymbolicPath PackageDir LicenseFile)- | MissingSetupFile- | MissingConfigureScript- | UnknownDirectory String FilePath- | MissingSourceControl- | MissingExpectedDocFiles Bool [FilePath]- | WrongFieldForExpectedDocFiles Bool String [FilePath]- deriving (Eq, Ord, Show)---- | 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)---- | 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 pkg) =- "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'."-ppExplanation (NoModulesExposed lib) =- showLibraryName (libName lib) ++ " 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' or 'other-modules'."-ppExplanation AutogenIncludesNotIncluded =- "An include in 'autogen-includes' is neither in 'includes' or "- ++ "'install-includes'."-ppExplanation (NoMainIs exe) =- "No 'main-is' field found for executable " ++ prettyShow (exeName exe)-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 ucn) =- "On " ++ ppCE ct ++ " '" ++ prettyShow ucn ++ "' 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 (packageName 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 (MissingField cef) =- "No '" ++ ppCEField cef ++ "' 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 pkg) =- "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'."-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 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 (OptViaC fieldName) =- "'" ++ 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."-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 " ++ 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 (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 "- ++ "a few reserved names including \"aux\", \"nul\", \"con\", "- ++ "\"prn\", \"com1-9\", \"lpt1-9\" and \"clock$\"."- where quotes [failed] = "path " ++ quote failed ++ " is"- quotes failed = "paths " ++ intercalate ", " (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 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. This is not possible in `cabal "- ++ "3.10`. To use this feature and be able to upload your package to "- ++ "Hackage, download `cabal 3.12`!"-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 (MissingUpperBounds names) =- let separator = "\n - "- in- "These packages miss upper bounds:" ++ separator- ++ (intercalate separator (unPackageName <$> names)) ++ "\n"- ++ "Please add them, using `cabal gen-bounds` for suggestions."- ++ " For more information see: "- ++ " https://pvp.haskell.org/"-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 (SuspiciousFlagName invalidFlagNames) =- "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."-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 specific 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: "- ++ intercalate ", " 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 " ++ intercalate ", " (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 " ++ intercalate ", " (map quote ps)- targetField = if extraDocFileSupport- then "extra-doc-files"- else "extra-source-files"----- | 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 = ppExplanation (explanation e)--instance Show PackageCheck where- show notice = ppPackageCheck 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- ++ checkPackageInfoModuleExtensions pkg- ++ checkSetupVersions gpkg- ++ checkDuplicateModules gpkg- 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 NoNameField-- , check (nullVersion == packageVersion pkg) $- PackageBuildImpossible NoVersionField-- , check (all ($ pkg) [ null . executables- , null . testSuites- , null . benchmarks- , null . allLibraries- , null . foreignLibs ]) $- PackageBuildImpossible NoTarget-- , check (any (== LMainLibName) (map libName $ subLibraries pkg)) $- PackageBuildImpossible UnnamedInternal-- , check (not (null duplicateNames)) $- PackageBuildImpossible (DuplicateSections duplicateNames)-- -- 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 (IllegalLibraryName pkg)- ]- --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 [-- -- TODO: This check is bogus if a required-signature was passed through- check (null (explicitLibModules lib) && null (reexportedModules lib)) $- PackageDistSuspiciousWarn (NoModulesExposed lib)-- -- check use of signatures sections- , checkVersion CabalSpecV2_0 (not (null (signatures lib))) $- PackageDistInexcusable SignaturesCabal2-- -- check that all autogen-modules appear on other-modules or exposed-modules- , check- (not $ and $ map (flip elem (explicitLibModules lib)) (libModulesAutogen lib)) $- PackageBuildImpossible AutogenNotExposed-- -- check that all autogen-includes appear on includes or install-includes- , check- (not $ and $ map (flip elem (allExplicitIncludes lib)) (view L.autogenIncludes lib)) $- PackageBuildImpossible AutogenIncludesNotIncluded- ]-- where- checkVersion :: CabalSpecVersion -> Bool -> PackageCheck -> Maybe PackageCheck- checkVersion ver cond pc- | specVersion pkg >= ver = Nothing- | otherwise = check cond pc--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 (NoMainIs exe)-- -- This check does not apply to scripts.- , check (package pkg /= fakePackageId- && not (null (modulePath exe))- && not (fileExtensionSupportedLanguage $ modulePath exe)) $- PackageBuildImpossible NoHsLhsMain-- , checkSpecVersion pkg CabalSpecV1_18- (fileExtensionSupportedLanguage (modulePath exe)- && takeExtension (modulePath exe) `notElem` [".hs", ".lhs"]) $- PackageDistInexcusable MainCCabal1_18-- -- check that all autogen-modules appear on other-modules- , check- (not $ and $ map (flip elem (exeModules exe)) (exeModulesAutogen exe)) $- PackageBuildImpossible (AutogenNoOther CETExecutable (exeName exe))-- -- check that all autogen-includes appear on includes- , check- (not $ and $ map (flip elem (view L.includes exe)) (view L.autogenIncludes exe)) $- PackageBuildImpossible AutogenIncludesNotIncludedExe- ]--checkTestSuite :: PackageDescription -> TestSuite -> [PackageCheck]-checkTestSuite pkg test =- catMaybes [-- case testInterface test of- TestSuiteUnsupported tt@(TestTypeUnknown _ _) -> Just $- PackageBuildWarning (TestsuiteTypeNotKnown tt)-- TestSuiteUnsupported tt -> Just $- PackageBuildWarning (TestsuiteNotSupported tt)- _ -> Nothing-- , check mainIsWrongExt $- PackageBuildImpossible NoHsLhsMain-- , checkSpecVersion pkg CabalSpecV1_18 (mainIsNotHsExt && not mainIsWrongExt) $- PackageDistInexcusable MainCCabal1_18-- -- check that all autogen-modules appear on other-modules- , check- (not $ and $ map (flip elem (testModules test)) (testModulesAutogen test)) $- PackageBuildImpossible (AutogenNoOther CETTest (testName test))-- -- check that all autogen-includes appear on includes- , check- (not $ and $ map (flip elem (view L.includes test)) (view L.autogenIncludes test)) $- PackageBuildImpossible AutogenIncludesNotIncludedExe- ]- where- 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 (BenchmarkTypeNotKnown tt)-- BenchmarkUnsupported tt -> Just $- PackageBuildWarning (BenchmarkNotSupported tt)- _ -> Nothing-- , check mainIsWrongExt $- PackageBuildImpossible NoHsLhsMainBench-- -- check that all autogen-modules appear on other-modules- , check- (not $ and $ map (flip elem (benchmarkModules bm)) (benchmarkModulesAutogen bm)) $- PackageBuildImpossible (AutogenNoOther CETBenchmark (benchmarkName bm))-- -- check that all autogen-includes appear on includes- , check- (not $ and $ map (flip elem (view L.includes bm)) (view L.autogenIncludes bm)) $- PackageBuildImpossible AutogenIncludesNotIncludedExe- ]- where- 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 (InvalidNameWin pkg)-- , check (isPrefixOf "z-" . prettyShow . packageName $ pkg) $- PackageDistInexcusable ZPrefix-- , check (isNothing (buildTypeRaw pkg) && specVersion pkg < CabalSpecV2_2) $- PackageBuildWarning NoBuildType-- , check (isJust (setupBuildInfo pkg) && buildType pkg /= Custom) $- PackageBuildWarning NoCustomSetup-- , check (not (null unknownCompilers)) $- PackageBuildWarning (UnknownCompilers unknownCompilers)-- , check (not (null unknownLanguages)) $- PackageBuildWarning (UnknownLanguages unknownLanguages)-- , check (not (null unknownExtensions)) $- PackageBuildWarning (UnknownExtensions unknownExtensions)-- , check (not (null languagesUsedAsExtensions)) $- PackageBuildWarning (LanguagesAsExtension languagesUsedAsExtensions)-- , check (not (null ourDeprecatedExtensions)) $- PackageDistSuspicious (DeprecatedExtensions ourDeprecatedExtensions)-- , check (ShortText.null (category pkg)) $- PackageDistSuspicious (MissingField CEFCategory)-- , check (ShortText.null (maintainer pkg)) $- PackageDistSuspicious (MissingField CEFMaintainer)-- , check (ShortText.null (synopsis pkg) && ShortText.null (description pkg)) $- PackageDistInexcusable (MissingField CEFSynOrDesc)-- , check (ShortText.null (description pkg) && not (ShortText.null (synopsis pkg))) $- PackageDistSuspicious (MissingField CEFDescription)-- , check (ShortText.null (synopsis pkg) && not (ShortText.null (description pkg))) $- PackageDistSuspicious (MissingField CEFSynopsis)-- --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 SynopsisTooLong-- -- See also https://github.com/haskell/cabal/pull/3479- , check (not (ShortText.null (description pkg))- && ShortText.length (description pkg) <= ShortText.length (synopsis pkg)) $- PackageDistSuspicious ShortDesc-- -- check use of impossible constraints "tested-with: GHC== 6.10 && ==6.12"- , check (not (null testedWithImpossibleRanges)) $- PackageDistInexcusable (InvalidTestWith testedWithImpossibleRanges)-- -- 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- (ImpossibleInternalDep depInternalLibraryWithImpossibleVersion)-- -- , 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- (ImpossibleInternalExe depInternalExecutableWithImpossibleVersion)-- , check (not (null depMissingInternalExecutable)) $- PackageBuildImpossible (MissingInternalExe depMissingInternalExecutable)- ]- where- unknownCompilers = [ name | (OtherCompiler name, _) <- testedWith pkg ]- unknownLanguages = [ name | bi <- allBuildInfo pkg- , UnknownLanguage name <- allLanguages bi ]- unknownExtensions = filter (/= "TypeAbstractions")- [ 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 ]-- internalExecutables = map exeName $ executables pkg-- internalLibDeps =- [ dep- | bi <- allBuildInfo pkg- , dep@(Dependency name _ _) <- targetBuildDepends bi- , name == packageName pkg- ]-- 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 NONELicense ]--checkOldLicense :: PackageDescription -> License -> [PackageCheck]-checkOldLicense pkg lic = catMaybes- [ check (lic == UnspecifiedLicense) $- PackageDistInexcusable NoLicense-- , check (lic == AllRightsReserved) $- PackageDistSuspicious AllRightsReservedLicense-- , checkVersion CabalSpecV1_4 (lic `notElem` compatLicenses) $- PackageDistInexcusable (LicenseMessParse pkg)-- , case lic of- UnknownLicense l -> Just $ PackageBuildWarning (UnrecognisedLicense l)- _ -> Nothing-- , check (lic == BSD4) $- PackageDistSuspicious UncommonBSD4-- , case unknownLicenseVersion lic of- Just knownVersions -> Just $- PackageDistSuspicious (UnknownLicenseVersion lic knownVersions)- _ -> Nothing-- , check (lic `notElem` [ AllRightsReserved- , UnspecifiedLicense, PublicDomain]- -- AllRightsReserved and PublicDomain are not strictly- -- licenses so don't need license files.- && null (licenseFiles pkg)) $- PackageDistSuspicious NoLicenseFile- ]- 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 $- UnrecognisedSourceRepo kind- _ -> Nothing-- , check (isNothing (repoType repo)) $- PackageDistInexcusable MissingType-- , check (isNothing (repoLocation repo)) $- PackageDistInexcusable MissingLocation-- , check (repoType repo == Just (KnownRepoType CVS) && isNothing (repoModule repo)) $- PackageDistInexcusable MissingModule-- , check (repoKind repo == RepoThis && isNothing (repoTag repo)) $- PackageDistInexcusable MissingTag-- , check (maybe False isAbsoluteOnAnyPlatform (repoSubdir repo)) $- PackageDistInexcusable SubdirRelPath-- , do- subdir <- repoSubdir repo- err <- isGoodRelativeDirectoryPath subdir- return $ PackageDistInexcusable (SubdirGoodRelPath 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 (OptFasm fieldName)-- , checkFlags ["-fvia-C"] $- PackageDistSuspicious (OptViaC fieldName)-- , checkFlags ["-fhpc"] $- PackageDistInexcusable (OptHpc fieldName)-- , checkFlags ["-prof"] $- PackageBuildWarning (OptProf fieldName)-- , unlessScript . checkFlags ["-o"] $- PackageBuildWarning (OptO fieldName)-- , checkFlags ["-hide-package"] $- PackageBuildWarning (OptHide fieldName)-- , checkFlags ["--make"] $- PackageBuildWarning (OptMake fieldName)-- , checkNonTestAndBenchmarkFlags ["-O0", "-Onot"] $- PackageDistSuspicious (OptONot fieldName)-- , checkTestAndBenchmarkFlags ["-O0", "-Onot"] $- PackageDistSuspiciousWarn (OptONot fieldName)-- , checkFlags [ "-O", "-O1"] $- PackageDistInexcusable (OptOOne fieldName)-- , checkFlags ["-O2"] $- PackageDistSuspiciousWarn (OptOTwo fieldName)-- , checkFlags ["-split-sections"] $- PackageBuildWarning (OptSplitSections fieldName)-- , checkFlags ["-split-objs"] $- PackageBuildWarning (OptSplitObjs fieldName)-- , checkFlags ["-optl-Wl,-s", "-optl-s"] $- PackageDistInexcusable (OptWls fieldName)-- , checkFlags ["-fglasgow-exts"] $- PackageDistSuspicious (OptExts fieldName)-- , check ("-rtsopts" `elem` lib_ghc_options) $- PackageBuildWarning (OptRts fieldName)-- , check (any (\opt -> "-with-rtsopts" `isPrefixOf` opt) lib_ghc_options) $- PackageBuildWarning (OptWithRts fieldName)-- , checkAlternatives fieldName "extensions"- [ (flag, prettyShow extension) | flag <- ghc_options_no_rtsopts- , Just extension <- [ghcExtension flag] ]-- , checkAlternatives fieldName "extensions"- [ (flag, extension) | flag@('-':'X':extension) <- ghc_options_no_rtsopts ]-- , checkAlternatives fieldName "cpp-options" $- [ (flag, flag) | flag@('-':'D':_) <- ghc_options_no_rtsopts ]- ++ [ (flag, flag) | flag@('-':'U':_) <- ghc_options_no_rtsopts ]-- , checkAlternatives fieldName "include-dirs"- [ (flag, dir) | flag@('-':'I':dir) <- ghc_options_no_rtsopts ]-- , checkAlternatives fieldName "extra-libraries"- [ (flag, lib) | flag@('-':'l':lib) <- ghc_options_no_rtsopts ]-- , checkAlternatives fieldName "extra-libraries-static"- [ (flag, lib) | flag@('-':'l':lib) <- ghc_options_no_rtsopts ]-- , checkAlternatives fieldName "extra-lib-dirs"- [ (flag, dir) | flag@('-':'L':dir) <- ghc_options_no_rtsopts ]-- , checkAlternatives fieldName "extra-lib-dirs-static"- [ (flag, dir) | flag@('-':'L':dir) <- ghc_options_no_rtsopts ]-- , checkAlternatives fieldName "frameworks"- [ (flag, fmwk) | (flag@"-framework", fmwk) <-- zip ghc_options_no_rtsopts (safeTail ghc_options_no_rtsopts) ]-- , checkAlternatives fieldName "extra-framework-dirs"- [ (flag, dir) | (flag@"-framework-path", dir) <-- zip ghc_options_no_rtsopts (safeTail ghc_options_no_rtsopts) ]- ]-- where- all_ghc_options = concatMap getOptions (allBuildInfo pkg)- ghc_options_no_rtsopts = rmRtsOpts all_ghc_options- 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)-- unlessScript :: Maybe PackageCheck -> Maybe PackageCheck- unlessScript pc | packageId pkg == fakePackageId = Nothing- | otherwise = pc-- 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)-- rmRtsOpts :: [String] -> [String]- rmRtsOpts ("-with-rtsopts":_:xs) = rmRtsOpts xs- rmRtsOpts (x:xs) = x : rmRtsOpts xs- rmRtsOpts [] = []---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 (COptONumber prefix label)- ]-- 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 (COptCPP opt)- | 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 (OptAlternatives badField goodField flags)- where (badFlags, _) = unzip flags--data PathKind- = PathKindFile- | PathKindDirectory- | PathKindGlob- deriving (Eq)--checkPaths :: PackageDescription -> [PackageCheck]-checkPaths pkg =- checkPackageFileNamesWithGlob- [ (kind == PathKindGlob, path)- | (path, _, kind) <- relPaths ++ absPaths- ]- ++- [ PackageBuildWarning (RelativeOutside field path)- | (path, field, _) <- relPaths ++ absPaths- , isOutsideTree path ]- ++- [ PackageDistInexcusable (AbsolutePath field path)- | (path, field, _) <- relPaths- , isAbsoluteOnAnyPlatform path ]- ++- [ PackageDistInexcusable (BadRelativePAth field 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 $ DistPoint (Just field) path- | (path, field, _) <- relPaths ++ absPaths- , isInsideDist path ]- ++- [ PackageDistInexcusable (DistPoint Nothing path)- | bi <- allBuildInfo pkg- , (GHC, flags) <- perCompilerFlavorToList $ options bi- , path <- flags- , isInsideDist path ]- ++- [ PackageDistInexcusable $- GlobSyntaxError "data-files" (explainGlobSyntaxError pat err)- | (Left err, pat) <- zip globsDataFiles $ dataFiles pkg- ]- ++- [ PackageDistInexcusable- (GlobSyntaxError "extra-source-files" (explainGlobSyntaxError pat err))- | (Left err, pat) <- zip globsExtraSrcFiles $ extraSrcFiles pkg- ]- ++- [ PackageDistInexcusable $- GlobSyntaxError "extra-doc-files" (explainGlobSyntaxError pat err)- | (Left err, pat) <- zip globsExtraDocFiles $ extraDocFiles pkg- ]- ++- [ PackageDistSuspiciousWarn $- RecursiveGlobInRoot "data-files" pat- | (Right glob, pat) <- zip globsDataFiles $ dataFiles pkg- , isRecursiveInRoot glob- ]- ++- [ PackageDistSuspiciousWarn $- RecursiveGlobInRoot "extra-source-files" pat- | (Right glob, pat) <- zip globsExtraSrcFiles $ extraSrcFiles pkg- , isRecursiveInRoot glob- ]- ++- [ PackageDistSuspiciousWarn $- RecursiveGlobInRoot "extra-doc-files" pat- | (Right glob, pat) <- zip globsExtraDocFiles $ extraDocFiles pkg- , isRecursiveInRoot glob- ]- 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 ] ++- [ (path, "extra-lib-dirs-static", PathKindDirectory) | path <- extraLibDirsStatic bi ]- | bi <- allBuildInfo pkg- ]- globsDataFiles :: [Either GlobSyntaxError Glob]- globsDataFiles = parseFileGlob (specVersion pkg) <$> dataFiles pkg- globsExtraSrcFiles :: [Either GlobSyntaxError Glob]- globsExtraSrcFiles = parseFileGlob (specVersion pkg) <$> extraSrcFiles pkg- globsExtraDocFiles :: [Either GlobSyntaxError Glob]- globsExtraDocFiles = parseFileGlob (specVersion pkg) <$> extraDocFiles 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 CVTestSuite-- -- 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 CVDefaultLanguage-- , check (specVersion pkg >= CabalSpecV1_10 && specVersion pkg < CabalSpecV3_4- && any isNothing (buildInfoField defaultLanguage)) $- PackageBuildWarning CVDefaultLanguageComponent-- , checkVersion CabalSpecV1_18- (not . null $ extraDocFiles pkg) $- PackageDistInexcusable CVExtraDocFiles-- , checkVersion CabalSpecV2_0- (not (null (subLibraries pkg))) $- PackageDistInexcusable CVMultiLib-- -- check use of reexported-modules sections- , checkVersion CabalSpecV1_22- (any (not.null.reexportedModules) (allLibraries pkg)) $- PackageDistInexcusable CVReexported-- -- check use of thinning and renaming- , checkVersion CabalSpecV2_0 usesBackpackIncludes $- PackageDistInexcusable CVMixins-- -- 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 CVExtraFrameworkDirs-- -- 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 CVDefaultExtensions-- -- check use of extensions field- , check (specVersion pkg >= CabalSpecV1_10- && any (not . null) (buildInfoField oldExtensions)) $- PackageBuildWarning CVExtensionsDeprecated-- , checkVersion CabalSpecV3_0 (any (not . null)- (concatMap buildInfoField- [ asmSources- , cmmSources- , extraBundledLibs- , extraLibFlavours ])) $- PackageDistInexcusable CVSources-- , checkVersion CabalSpecV3_0 (any (not . null) $ buildInfoField extraDynLibFlavours) $- PackageDistInexcusable- (CVExtraDynamic $ buildInfoField extraDynLibFlavours)-- , checkVersion CabalSpecV2_2 (any (not . null)- (buildInfoField virtualModules)) $- PackageDistInexcusable CVVirtualModules-- -- check use of "source-repository" section- , checkVersion CabalSpecV1_6 (not (null (sourceRepos pkg))) $- PackageDistInexcusable CVSourceRepository-- -- check for new language extensions- , checkVersion CabalSpecV1_2 (not (null mentionedExtensionsThatNeedCabal12)) $- PackageDistInexcusable- (CVExtensions CabalSpecV1_2 mentionedExtensionsThatNeedCabal12)-- , checkVersion CabalSpecV1_4 (not (null mentionedExtensionsThatNeedCabal14)) $- PackageDistInexcusable- (CVExtensions CabalSpecV1_4 mentionedExtensionsThatNeedCabal14)-- , check (specVersion pkg >= CabalSpecV1_24- && isNothing (setupBuildInfo pkg)- && buildType pkg == Custom) $- PackageBuildWarning CVCustomSetup-- , check (specVersion pkg < CabalSpecV1_24- && isNothing (setupBuildInfo pkg)- && buildType pkg == Custom) $- PackageDistSuspiciousWarn CVExpliticDepsCustomSetup-- , check (specVersion pkg >= CabalSpecV2_0- && elem (autogenPathsModuleName pkg) allModuleNames- && not (elem (autogenPathsModuleName pkg) allModuleNamesAutogen) ) $- PackageDistInexcusable CVAutogenPaths-- , check (specVersion pkg >= CabalSpecV2_0- && elem (autogenPackageInfoModuleName pkg) allModuleNames- && not (elem (autogenPackageInfoModuleName pkg) allModuleNamesAutogen) ) $- PackageDistInexcusable CVAutogenPackageInfo-- , check- (elem (autogenPackageInfoModuleName pkg) allModuleNames)- (PackageDistInexcusable CVAutogenPackageInfoGuard)- ]- 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 =- -- if others is empty,- -- the error will still fire but listing no dependencies.- -- so we have to check- if length others > 0- then- PackageDistSuspiciousWarn (MissingUpperBounds others) : baseErrors- else- baseErrors- where- baseErrors = PackageDistInexcusable BaseNoUpperBounds <$ bases- deps = toDependencyVersionsMap allNonInternalBuildDepends pkg- -- base gets special treatment (it's more critical)- (bases, others) = partition (("base" ==) . unPackageName) $- [ name- | (name, vr) <- Map.toList deps- , not (hasUpperBound vr)- ]-- -- Get the combined build-depends entries of all components.- allNonInternalBuildDepends :: PackageDescription -> [Dependency]- allNonInternalBuildDepends = targetBuildDepends CM.<=< allNonInternalBuildInfo-- allNonInternalBuildInfo :: PackageDescription -> [BuildInfo]- allNonInternalBuildInfo 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]--checkConditionals :: GenericPackageDescription -> [PackageCheck]-checkConditionals pkg =- catMaybes [-- check (not $ null unknownOSs) $- PackageDistInexcusable (UnknownOS unknownOSs)-- , check (not $ null unknownArches) $- PackageDistInexcusable (UnknownArch unknownArches)-- , check (not $ null unknownImpls) $- PackageDistInexcusable (UnknownCompiler unknownImpls)- ]- where- unknownOSs = [ os | OS (OtherOS os) <- conditions, os /= "haiku" ]- 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 (SuspiciousFlagName invalidFlagNames) ]- 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 (DeclaredUsedFlags declared used) ]- where- 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 (NonASCIICustomField nonAsciiXFields) ]- 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 = checkAutogenModuleExtensions autogenPathsModuleName RebindableClashPaths---- | cabal-version <2.2 + PackageInfo_module + default-extensions: doesn't build.-checkPackageInfoModuleExtensions :: PackageDescription -> [PackageCheck]-checkPackageInfoModuleExtensions = checkAutogenModuleExtensions autogenPackageInfoModuleName RebindableClashPackageInfo---- | cabal-version <2.2 + *_module + default-extensions: doesn't build.-checkAutogenModuleExtensions ::- (PackageDescription -> ModuleName) ->- CheckExplanation ->- PackageDescription ->- [PackageCheck]-checkAutogenModuleExtensions autogenModuleName rebindableClashExplanation pd- | specVersion pd >= CabalSpecV2_2 = []- | any checkBI (allBuildInfo pd) || any checkLib (allLibraries pd)- = return (PackageBuildImpossible rebindableClashExplanation)- | otherwise = []- where- mn = autogenModuleName 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 distributed 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_Werror $- PackageDistInexcusable (WErrorUnneeded fieldName)-- , check has_J $- PackageDistInexcusable (JUnneeded fieldName)-- , checkFlags ["-fdefer-type-errors"] $- PackageDistInexcusable (FDeferTypeErrorsUnneeded fieldName)-- -- -dynamic is not a debug flag- , check (any (\opt -> "-d" `isPrefixOf` opt && opt /= "-dynamic")- ghcOptions) $- PackageDistInexcusable (DynamicUnneeded fieldName)-- , checkFlags ["-fprof-auto", "-fprof-auto-top", "-fprof-auto-calls",- "-fprof-cafs", "-fno-prof-count-entries",- "-auto-all", "-auto", "-caf-all"] $- PackageDistSuspicious (ProfilingUnneeded fieldName)- ]- where-- has_Werror = "-Werror" `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 (BOMStart pdfile)-- 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- (NotPackageName pdfile 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))--checkLicensesExist :: (Monad m, Applicative m)- => CheckPackageContentOps m- -> PackageDescription- -> m [PackageCheck]-checkLicensesExist ops pkg = do- exists <- traverse (doesFileExist ops . getSymbolicPath) (licenseFiles pkg)- return- [ PackageBuildWarning (UnknownFile fieldname file)- | (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 MissingSetupFile--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 MissingConfigureScript- | 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-lib-dirs-static") | dir <- extraLibDirsStatic 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 (UnknownDirectory kind dir)- | (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 MissingSourceControl ]- else return []- where- repoDirnames = [ dirname | repo <- knownRepoTypes- , dirname <- repoTypeDirname repo]--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 = checkPackageFileNamesWithGlob . zip (repeat True)--checkPackageFileNamesWithGlob :: [(Bool, FilePath)] -> [PackageCheck]-checkPackageFileNamesWithGlob files =- catMaybes $- checkWindowsPaths files- :- [ checkTarPath file- | (_, file) <- files- ]--checkWindowsPaths :: [(Bool, FilePath)] -> Maybe PackageCheck-checkWindowsPaths paths =- case filter (not . FilePath.Windows.isValid . escape) paths of- [] -> Nothing- ps -> Just $- PackageDistInexcusable (InvalidOnWin $ map snd ps)- 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 (isGlob, path) = (".\\" ++)- -- glob paths will be expanded before being dereferenced, so asterisks- -- shouldn't count against them.- $ map (\c -> if c == '*' && isGlob then 'x' else c) path---- | 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 (FilePathTooLong path)- longName = PackageDistInexcusable (FilePathNameTooLong path)- noSplit = PackageDistInexcusable (FilePathSplitTooLong path)- emptyName = PackageDistInexcusable FilePathEmpty---- ----------------------------------------------------------------- * 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 = do- -- Get the desirable doc files from package’s directory- rootContents <- System.Directory.getDirectoryContents root- docFiles0 <- filterM System.doesFileExist- [ file- | file <- rootContents- , isDesirableExtraDocFile desirableDocFiles file- ]- -- Check the globs- (warnings, unlisted) <- foldrM checkGlob ([], docFiles0) allGlobs-- return $ if null unlisted- -- No missing desirable file- then warnings- -- Some missing desirable files- else warnings ++- let unlisted' = (root </>) <$> unlisted- in [ PackageDistSuspiciousWarn- (MissingExpectedDocFiles extraDocFilesSupport unlisted')- ]- where- -- `extra-doc-files` is supported only from version 1.18- extraDocFilesSupport = specVersion pkg >= CabalSpecV1_18- adjustedDataDir = if null (dataDir pkg) then root else root </> dataDir pkg- -- Cabal fields with globs- allGlobs :: [(String, Bool, FilePath, FilePath)]- allGlobs = concat- [ (,,,) "extra-source-files" (not extraDocFilesSupport) root <$>- extraSrcFiles pkg- , (,,,) "extra-doc-files" True root <$> extraDocFiles pkg- , (,,,) "data-files" False adjustedDataDir <$> dataFiles pkg- ]-- -- For each field with globs (see allGlobs), look for:- -- • errors (missing directory, no match)- -- • omitted documentation files (changelog)- checkGlob :: (String, Bool, FilePath, FilePath)- -> ([PackageCheck], [FilePath])- -> IO ([PackageCheck], [FilePath])- checkGlob (field, isDocField, dir, glob) acc@(warnings, docFiles1) =- -- Note: we just skip over parse errors here; they're reported elsewhere.- case parseFileGlob (specVersion pkg) glob of- Left _ -> return acc- Right parsedGlob -> do- results <- runDirFileGlob verbosity (root </> dir) parsedGlob- let acc0 = (warnings, True, docFiles1, [])- return $ case foldr checkGlobResult acc0 results of- (individualWarn, noMatchesWarn, docFiles1', wrongPaths) ->- let wrongFieldWarnings = [ PackageDistSuspiciousWarn- (WrongFieldForExpectedDocFiles- extraDocFilesSupport- field wrongPaths)- | not (null wrongPaths) ]- in- ( if noMatchesWarn- then [PackageDistSuspiciousWarn (GlobNoMatch field glob)] ++- individualWarn ++- wrongFieldWarnings- else individualWarn ++ wrongFieldWarnings- , docFiles1'- )- where- checkGlobResult :: GlobResult FilePath- -> ([PackageCheck], Bool, [FilePath], [FilePath])- -> ([PackageCheck], Bool, [FilePath], [FilePath])- checkGlobResult result (ws, noMatchesWarn, docFiles2, wrongPaths) =- let noMatchesWarn' = noMatchesWarn &&- not (suppressesNoMatchesWarning result)- in case getWarning field glob result of- -- No match: add warning and do no further check- Left w ->- ( w : ws- , noMatchesWarn'- , docFiles2- , wrongPaths- )- -- Match: check doc files- Right path ->- let path' = makeRelative root (normalise path)- (docFiles2', wrongPaths') = checkDoc isDocField- path'- docFiles2- wrongPaths- in- ( ws- , noMatchesWarn'- , docFiles2'- , wrongPaths'- )-- -- Check whether a path is a desirable doc: if so, check if it is in the- -- field "extra-doc-files".- checkDoc :: Bool -- Is it "extra-doc-files" ?- -> FilePath -- Path to test- -> [FilePath] -- Pending doc files to check- -> [FilePath] -- Previous wrong paths- -> ([FilePath], [FilePath]) -- Updated paths- checkDoc isDocField path docFiles wrongFieldPaths =- if path `elem` docFiles- -- Found desirable doc file- then- ( delete path docFiles- , if isDocField then wrongFieldPaths else path : wrongFieldPaths- )- -- Not a desirable doc file- else- ( docFiles- , wrongFieldPaths- )-- -- Predicate for desirable documentation file on Hackage server- isDesirableExtraDocFile :: ([FilePath], [FilePath]) -> FilePath -> Bool- isDesirableExtraDocFile (basenames, extensions) path =- basename `elem` basenames && ext `elem` extensions- 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"]- desirableDocFiles = (desirableChangeLog, desirableChangeLogExtensions)-- -- 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- -> Either PackageCheck FilePath- getWarning _ _ (GlobMatch path) =- Right path- -- 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) =- Left (PackageDistSuspiciousWarn (GlobExactMatch field glob file))- getWarning field glob (GlobMissingDirectory dir) =- Left (PackageDistSuspiciousWarn (GlobNoDir field glob dir))---- | Check that setup dependencies, have proper bounds.--- In particular, @base@ and @Cabal@ upper bounds are mandatory.-checkSetupVersions :: GenericPackageDescription -> [PackageCheck]-checkSetupVersions pkg =- [ emitError nameStr- | (name, vr) <- Map.toList deps- , not (hasUpperBound vr)- , let nameStr = unPackageName name- , nameStr `elem` criticalPkgs- ]- where- criticalPkgs = ["Cabal", "base"]- deps = toDependencyVersionsMap (foldMap setupDepends . setupBuildInfo) pkg- emitError nm =- PackageDistInexcusable (UpperBoundSetup nm)--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 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 []---- --------------------------------------------------------------- * Utils--- --------------------------------------------------------------toDependencyVersionsMap :: (PackageDescription -> [Dependency]) -> GenericPackageDescription -> Map PackageName VersionRange-toDependencyVersionsMap selectDependencies pkg = case typicalPkg pkg of- Right (pkgs', _) ->- let- self :: PackageName- self = pkgName $ package pkgs'- in- Map.fromListWith intersectVersionRanges $- [ (pname, vr)- | Dependency pname vr _ <- selectDependencies pkgs'- , pname /= self- ]- -- Just in case finalizePD fails for any reason,- -- or if the package doesn't depend on the base package at all,- -- no deps is no checks.- _ -> Map.empty---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. 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--- @------- 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.-typicalPkg :: GenericPackageDescription- -> Either [Dependency] (PackageDescription, FlagAssignment)-typicalPkg = finalizePD- mempty defaultComponentRequestedSpec (const True)- buildPlatform- (unknownCompilerInfo- (CompilerId buildCompilerFlavor nullVersion)- NoAbiTag)- []--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."+{-# 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+ ( LicenseFile+ , PackageDir+ , SymbolicPath+ , getSymbolicPath+ )+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 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)+ -- PackageDescription checks.+ checkPackageDescription packageDescription_+ -- 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_+ ) = 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) extraSrcFiles_+ mapM_ (checkPath False "extra-tmp-files" PathKindFile) extraTmpFiles_+ mapM_ (checkPath False "extra-doc-files" PathKindGlob) extraDocFiles_+ mapM_ (checkPath False "data-files" PathKindGlob) dataFiles_+ checkPath True "data-dir" PathKindDirectory dataDir_+ let licPaths = map getSymbolicPath licenseFiles_+ mapM_ (checkPath False "license-file" PathKindFile) licPaths+ mapM_ checkLicFileExist licenseFiles_++ -- § Globs.+ dataGlobs <- mapM (checkGlob "data-files") dataFiles_+ extraGlobs <- mapM (checkGlob "extra-source-files") extraSrcFiles_+ docGlobs <- mapM (checkGlob "extra-doc-files") extraDocFiles_+ -- We collect globs to feed them to checkMissingDocs.++ -- § Missing documentation.+ checkMissingDocs+ (catMaybes dataGlobs)+ (catMaybes extraGlobs)+ (catMaybes docGlobs)++ -- § 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 /= Custom)+ (PackageBuildWarning NoCustomSetup)++ -- Contents.+ checkConfigureExists (buildType pkg)+ checkSetupExists (buildType pkg)+ checkCabalFile (packageName pkg)+ mapM_ (checkGlobFile specVersion_ "." "extra-source-files") extraSrcFiles_+ mapM_ (checkGlobFile specVersion_ "." "extra-doc-files") extraDocFiles_+ mapM_ (checkGlobFile specVersion_ dataDir_ "data-files") 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+ checkPVP ick is+ checkPVPs rck rs++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)+ 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"]++-- ------------------------------------------------------------+-- 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+ => SymbolicPath PackageDir LicenseFile+ -> 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.+ -> CheckM m ()+checkMissingDocs dgs esgs edgs = 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++ -- 3. Check if anything in 1. is missing in 2.+ let mcs = checkDoc extraDocSupport des (rgs ++ res ++ red)++ -- 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,149 @@+-- |+-- 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+ ) 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+ inNam = 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) inNam+ && 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+ => (String -> PackageCheck) -- Warn message dependend on name+ -- (e.g. "base", "Cabal").+ -> [Dependency]+ -> CheckM m ()+checkPVP ckf ds = do+ let ods = checkPVPPrim 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+ => ( [String]+ -> PackageCheck -- Grouped error message, depends on a+ -- set of names.+ )+ -> [Dependency] -- Deps to analyse.+ -> CheckM m ()+checkPVPs cf ds+ | null ns = return ()+ | otherwise = tellP (cf ns)+ where+ ods = checkPVPPrim ds+ ns = map (unPackageName . depPkgName) ods++-- Returns dependencies without upper bounds.+checkPVPPrim :: [Dependency] -> [Dependency]+checkPVPPrim ds = filter withoutUpper ds+ where+ withoutUpper :: Dependency -> Bool+ withoutUpper (Dependency _ ver _) = not . hasUpperBound $ 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 extractCheckExplantion 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,417 @@+-- |+-- 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+ , 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,1060 @@+-- |+-- 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)+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+ 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))+ (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 =+ view L.includes x+ ++ 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_+ modulePath_+ _exeScope_+ buildInfo_+ ) = do+ -- Target type/name (exe).+ let cet = CETExecutable exeName_++ -- § 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))+ (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 ()+ 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))+ (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 f)+ _ -> False++ mainIsNotHsExt =+ case testInterface_ of+ TestSuiteExeV10 _ f -> takeExtension 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_++ -- § 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))+ (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 f `notElem` [".hs", ".lhs"]+ _ -> False++-- ------------------------------------------------------------+-- 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.+ (ids, rds) <-+ partitionDeps+ ads+ [mkUnqualComponentName "base"]+ (mergeDependencies $ targetBuildDepends bi)+ let ick = const (PackageDistInexcusable BaseNoUpperBounds)+ rck = PackageDistSuspiciousWarn . MissingUpperBounds cet+ checkPVP ick ids+ unless+ (isInternalTarget cet)+ (checkPVPs rck rds)++ -- Custom fields well-formedness (ASCII).+ mapM_ checkCustomField (customFieldsBI bi)++ -- Content.+ mapM_ (checkLocalPathExist "extra-lib-dirs") (extraLibDirs bi)+ mapM_+ (checkLocalPathExist "extra-lib-dirs-static")+ (extraLibDirsStatic bi)+ mapM_+ (checkLocalPathExist "extra-framework-dirs")+ (extraFrameworkDirs bi)+ mapM_ (checkLocalPathExist "include-dirs") (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) (asmSources bi)+ mapM_ (checkPath False "cmm-sources" PathKindFile) (cmmSources bi)+ mapM_ (checkPath False "c-sources" PathKindFile) (cSources bi)+ mapM_ (checkPath False "cxx-sources" PathKindFile) (cxxSources bi)+ mapM_ (checkPath False "js-sources" PathKindFile) (jsSources bi)+ mapM_+ (checkPath False "install-includes" PathKindFile)+ (installIncludes bi)+ mapM_+ (checkPath False "hs-source-dirs" PathKindDirectory . getSymbolicPath)+ (hsSourceDirs bi)+ -- Possibly absolute paths.+ mapM_ (checkPath True "includes" PathKindFile) (includes bi)+ mapM_+ (checkPath True "include-dirs" PathKindDirectory)+ (includeDirs bi)+ mapM_+ (checkPath True "extra-lib-dirs" PathKindDirectory)+ (extraLibDirs bi)+ mapM_+ (checkPath True "extra-lib-dirs-static" PathKindDirectory)+ (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.+ checkP+ ( sv >= CabalSpecV1_10+ && sv < CabalSpecV3_4+ && isNothing (defaultLanguage bi)+ )+ (PackageBuildWarning CVDefaultLanguageComponent)+ -- 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 (asmSources bi)+ checkCVSources (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)++-- 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)++-- | 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)+ -- Does not apply to scripts.+ -- Why do we need this? See #8963.+ pid <- asksCM (pnPackageId . ccNames)+ 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+ "extensions"+ [ (flag, prettyShow extension)+ | flag <- ghcNoRts+ , Just extension <- [ghcExtension flag]+ ]+ checkAlternatives+ title+ "extensions"+ [ (flag, extension)+ | flag@('-' : 'X' : extension) <- ghcNoRts+ ]+ checkAlternatives+ title+ "cpp-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
+ src/Distribution/PackageDescription/Check/Warning.hs view
@@ -0,0 +1,1473 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# 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+ , extractCheckExplantion+ , 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+ ( LicenseFile+ , PackageDir+ , SymbolicPath+ , 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+ . extractCheckExplantion+ $ 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+ | 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+ | 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+ | 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]+ | 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 (SymbolicPath PackageDir LicenseFile)+ | 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.+extractCheckExplantion :: PackageCheck -> CheckExplanation+extractCheckExplantion (PackageBuildImpossible e) = e+extractCheckExplantion (PackageBuildWarning e) = e+extractCheckExplantion (PackageDistSuspicious e) = e+extractCheckExplantion (PackageDistSuspiciousWarn e) = e+extractCheckExplantion (PackageDistInexcusable e) = e++-- | Identifier for the speficic '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+ | CIMissingModule+ | CIMissingTag+ | CISubdirRelPath+ | CISubdirGoodRelPath+ | CIOptFasm+ | CIOptHpc+ | CIOptProf+ | CIOptO+ | CIOptHide+ | CIOptMake+ | CIOptONot+ | CIOptOOne+ | CIOptOTwo+ | CIOptSplitSections+ | CIOptSplitObjs+ | CIOptWls+ | CIOptExts+ | CIOptRts+ | CIOptWithRts+ | CICOptONumber+ | CICOptCPP+ | CIOptAlternatives+ | CIRelativeOutside+ | CIAbsolutePath+ | CIBadRelativePath+ | CIDistPoint+ | CIGlobSyntaxError+ | CIRecursiveGlobInRoot+ | CIInvalidOnWin+ | CIFilePathTooLong+ | CIFilePathNameTooLong+ | CIFilePathSplitTooLong+ | CIFilePathEmpty+ | CICVTestSuite+ | CICVDefaultLanguage+ | CICVDefaultLanguageComponent+ | 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+ | 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 (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 (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 (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 (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+--+-- ☞ N.B: if you modify anything here, remeber to change the documentation+-- in @doc/cabal-commands.rst@!+ppCheckExplanationId :: CheckExplanationID -> CheckExplanationIDString+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 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 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 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 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 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 (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 "+ ++ "a few reserved names including \"aux\", \"nul\", \"con\", "+ ++ "\"prn\", \"com1-9\", \"lpt1-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 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) =+ let separator = "\n - "+ in "On "+ ++ ppCET ct+ ++ ", "+ ++ "these packages miss upper bounds:"+ ++ separator+ ++ List.intercalate separator names+ ++ "\n"+ ++ "Please add them. There is more information at https://pvp.haskell.org/"+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++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/ReadE.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE LambdaCase #-}+ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.ReadE -- Copyright : Jose Iborra 2008@@ -9,32 +11,37 @@ -- Portability : portable -- -- Simple parsing with failure+module Distribution.ReadE+ ( -- * ReadE+ ReadE (..)+ , succeedReadE+ , failReadE -module Distribution.ReadE (- -- * ReadE- ReadE(..), succeedReadE, failReadE,- -- * Projections- parsecToReadE, parsecToReadEErr,- -- * Parse Errors- unexpectMsgString,+ -- * Projections+ , parsecToReadE+ , parsecToReadEErr++ -- * Parse Errors+ , unexpectMsgString ) where +import qualified Data.Bifunctor as Bi (first) import Distribution.Compat.Prelude import Prelude ()-import qualified Data.Bifunctor as Bi (first) import Distribution.Parsec-import qualified Text.Parsec.Error as 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)@@ -44,20 +51,21 @@ runParsecFromString :: ParsecParser a -> String -> Either Parsec.ParseError a runParsecFromString p txt =- runParsecParser p "<parsecToReadE>" (fieldLineStreamFromString txt)+ runParsecParser p "<parsecToReadE>" (fieldLineStreamFromString txt) parsecToReadE :: (String -> ErrorMsg) -> ParsecParser a -> ReadE a parsecToReadE err p = ReadE $ \txt ->- (const $ err txt) `Bi.first` runParsecFromString p txt+ const (err txt) `Bi.first` runParsecFromString p txt parsecToReadEErr :: (Parsec.ParseError -> ErrorMsg) -> ParsecParser a -> ReadE a-parsecToReadEErr err p = ReadE $+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-+unexpectMsgString =+ unlines+ . map Parsec.messageString+ . filter (\case Parsec.UnExpect _ -> True; _ -> False)+ . Parsec.errorMessages
src/Distribution/Simple.hs view
@@ -1,8 +1,17 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE RankNTypes #-} {-# LANGUAGE LambdaCase #-}+{-# 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 -fno-warn-deprecations #-}+ -- | -- Module : Distribution.Simple -- Copyright : Isaac Jones 2003-2005@@ -28,79 +37,84 @@ -- 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 -{--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 #-}+ -- * Simple interface+ , defaultMain+ , defaultMainNoRead+ , defaultMainArgs -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,+ -- * 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+import Prelude () -- local-import Distribution.Simple.Compiler-import Distribution.Simple.UserHooks+ 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.Program import Distribution.Simple.PreProcess+import Distribution.Simple.Program import Distribution.Simple.Setup-import Distribution.Simple.Command+import Distribution.Simple.UserHooks import Distribution.Simple.Build-import Distribution.Simple.SrcDist import Distribution.Simple.Register+import Distribution.Simple.SrcDist import Distribution.Simple.Configure -import Distribution.Simple.ConfigureScript-import Distribution.Simple.LocalBuildInfo+import Distribution.License+import Distribution.Pretty import Distribution.Simple.Bench import Distribution.Simple.BuildPaths-import Distribution.Simple.Test-import Distribution.Simple.Install+import Distribution.Simple.ConfigureScript+import Distribution.Simple.Errors import Distribution.Simple.Haddock+import Distribution.Simple.Install+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.Test import Distribution.Simple.Utils import Distribution.Verbosity-import Language.Haskell.Extension import Distribution.Version-import Distribution.License-import Distribution.Pretty+import Language.Haskell.Extension -- Base-import System.Environment (getArgs, getProgName)-import System.Directory (removeFile, doesFileExist- ,doesDirectoryExist, removeDirectoryRecursive)-import System.FilePath (takeDirectory, (</>))+ import Distribution.Compat.ResponseFile (expandResponse)+import System.Directory+ ( doesDirectoryExist+ , doesFileExist+ , removeDirectoryRecursive+ , removeFile+ )+import System.Environment (getArgs, getProgName)+import System.FilePath (takeDirectory, (</>)) -import Data.List (unionBy, (\\))+import Data.List (unionBy, (\\)) -- | A simple implementation of @main@ for a Cabal setup script. -- It reads the package description file using IO, and performs the@@ -130,8 +144,8 @@ -- | A customizable version of 'defaultMainNoRead'. defaultMainWithHooksNoRead :: UserHooks -> GenericPackageDescription -> IO () defaultMainWithHooksNoRead hooks pkg_descr =- getArgs >>=- defaultMainHelper hooks { readDesc = return (Just pkg_descr) }+ getArgs+ >>= defaultMainHelper hooks{readDesc = return (Just pkg_descr)} -- | A customizable version of 'defaultMainNoRead' that also takes the -- command line arguments.@@ -139,24 +153,35 @@ -- @since 2.2.0.0 defaultMainWithHooksNoReadArgs :: UserHooks -> GenericPackageDescription -> [String] -> IO () defaultMainWithHooksNoReadArgs hooks pkg_descr =- defaultMainHelper hooks { readDesc = return (Just 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- case commandsRun (globalCommand commands) commands args' of- CommandHelp help -> printHelp help- CommandList opts -> printOptionsList opts- CommandErrors errs -> printErrors errs- CommandReadyToGo (flags, commandParse) ->+ 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@@ -164,109 +189,135 @@ 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 = addKnownPrograms (hookedPrograms hooks) defaultProgramDb commands =- [configureCommand progs `commandAddAction`- \fs as -> configureAction hooks fs as >> return ()- ,buildCommand progs `commandAddAction` buildAction 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+ [ configureCommand progs+ `commandAddAction` \fs as -> configureAction hooks fs as >> return ()+ , buildCommand progs `commandAddAction` buildAction 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)+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 }+ distPref <- findDistPrefOrDefault (configDistPref flags)+ let flags' =+ flags+ { configDistPref = toFlag distPref+ , configArgs = args+ } - -- See docs for 'HookedBuildInfo'- pbi <- preConf hooks args flags'+ -- See docs for 'HookedBuildInfo'+ pbi <- preConf hooks args flags' - (mb_pd_file, pkg_descr0) <- confPkgDescr hooks verbosity- (flagToMaybe (configCabalFilePath flags))+ (mb_pd_file, pkg_descr0) <-+ confPkgDescr+ hooks+ verbosity+ (flagToMaybe (configCabalFilePath flags)) - let epkg_descr = (pkg_descr0, pbi)+ let epkg_descr = (pkg_descr0, pbi) - localbuildinfo0 <- confHook hooks epkg_descr flags'+ 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+ -- 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+ 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+ :: 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)+ 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)}+ let flags' =+ flags+ { buildDistPref = toFlag distPref+ , buildCabalFilePath = maybeToFlag (cabalFilePath lbi)+ } - progs <- reconfigurePrograms verbosity- (buildProgramPaths flags')- (buildProgramArgs flags')- (withPrograms lbi)+ progs <-+ reconfigurePrograms+ verbosity+ (buildProgramPaths flags')+ (buildProgramArgs flags')+ (withPrograms lbi) - hookedAction verbosity preBuild buildHook postBuild- (return lbi { withPrograms = progs })- hooks flags' { buildArgs = args } args+ hookedAction+ verbosity+ preBuild+ buildHook+ postBuild+ (return lbi{withPrograms = progs})+ hooks+ flags'{buildArgs = args}+ args replAction :: UserHooks -> ReplFlags -> Args -> IO () replAction hooks flags args = do distPref <- findDistPrefOrDefault (replDistPref flags) let verbosity = fromFlag $ replVerbosity flags- flags' = flags { replDistPref = toFlag distPref }+ flags' = flags{replDistPref = toFlag distPref} lbi <- getBuildConfig hooks verbosity distPref- progs <- reconfigurePrograms verbosity- (replProgramPaths flags')- (replProgramArgs flags')- (withPrograms lbi)+ 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'@@ -275,211 +326,323 @@ let pkg_descr0 = localPkgDescr lbi sanityCheckHookedBuildInfo verbosity pkg_descr0 pbi let pkg_descr = updatePackageDescription pbi pkg_descr0- lbi' = lbi { withPrograms = progs- , localPkgDescr = pkg_descr }+ 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)}+ 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+ 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)}+ let flags' =+ flags+ { haddockDistPref = toFlag distPref+ , haddockCabalFilePath = maybeToFlag (cabalFilePath lbi)+ } - progs <- reconfigurePrograms verbosity- (haddockProgramPaths flags')- (haddockProgramArgs flags')- (withPrograms lbi)+ progs <-+ reconfigurePrograms+ verbosity+ (haddockProgramPaths flags')+ (haddockProgramArgs flags')+ (withPrograms lbi) - hookedAction verbosity preHaddock haddockHook postHaddock- (return lbi { withPrograms = progs })- hooks flags' { haddockArgs = args } args+ 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)+ 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)}+ 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'+ 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+ (_, 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 ()+ 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+ 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+ 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+ (_, 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 }+ 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+ 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+ 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+ 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+ 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 ())+ -> ( UserHooks+ -> PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> flags+ -> IO ()+ )+ -> ( UserHooks+ -> Args+ -> flags+ -> PackageDescription+ -> LocalBuildInfo+ -> IO ()+ ) -> IO LocalBuildInfo- -> UserHooks -> flags -> Args -> IO ()+ -> 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+ 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 ())+ -> ( 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+ -> 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+ (PackageDescription{library = Nothing})+ (Just _, _) =+ dieWithException verbosity $ NoLibraryForPackage 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."+ | exe1 : _ <- nonExistant =+ dieWithException verbosity $ SanityCheckHookedBuildInfo exe1 where- pkgExeNames = nub (map exeName (executables pkg_descr))+ pkgExeNames = nub (map exeName (executables pkg_descr)) hookExeNames = nub (map fst hookExes)- nonExistant = hookExeNames \\ pkgExeNames-+ nonExistant = hookExeNames \\ pkgExeNames sanityCheckHookedBuildInfo _ _ _ = return () -- | Try to read the 'localBuildInfoFile'-tryGetBuildConfig :: UserHooks -> Verbosity -> FilePath- -> IO (Either ConfigStateFileError LocalBuildInfo)+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)- }+ let lbi =+ lbi_wo_programs+ { withPrograms =+ restoreProgramDb+ (builtinPrograms ++ hookedPrograms hooks)+ (withPrograms lbi_wo_programs)+ } case pkgDescrFile lbi of Nothing -> return lbi@@ -488,60 +651,64 @@ 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"+ 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- }+ 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+ let distPref = fromFlagOrDefault defaultDistPref $ cleanDistPref flags+ notice verbosity "cleaning..." - -- 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)+ maybeConfig <-+ if fromFlag (cleanSaveConf flags)+ then maybeGetPersistBuildConfig distPref+ else return Nothing - -- Any extra files the user wants to remove- traverse_ removeFileOrDirectory (extraTmpFiles pkg_descr)+ -- 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) - -- If the user wanted to save the config, write it back- traverse_ (writePersistBuildConfig distPref) maybeConfig+ -- 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)+ 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@@ -550,22 +717,22 @@ -- \"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- }+ 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)@@ -581,112 +748,158 @@ -- -- 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,- preRepl = readHookWithArgs replVerbosity replDistPref,- 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,- preTest = readHookWithArgs testVerbosity testDistPref,- preBench = readHookWithArgs benchmarkVerbosity benchmarkDistPref- }- 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- flags lbi- else die' verbosity "configure script not found."+autoconfUserHooks =+ simpleUserHooks+ { postConf = defaultPostConf+ , preBuild = readHookWithArgs buildVerbosity buildDistPref+ , preRepl = readHookWithArgs replVerbosity replDistPref+ , 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+ , preTest = readHookWithArgs testVerbosity testDistPref+ , preBench = readHookWithArgs benchmarkVerbosity benchmarkDistPref+ }+ 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+ flags+ lbi+ else dieWithException verbosity ConfigureScriptNotFound - 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'+ 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' - 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)+ 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)+ 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) getHookedBuildInfo :: Verbosity -> FilePath -> IO HookedBuildInfo getHookedBuildInfo verbosity build_dir = do maybe_infoFile <- findHookedPackageDesc verbosity build_dir case maybe_infoFile of- Nothing -> return emptyHookedBuildInfo+ Nothing -> return emptyHookedBuildInfo Just infoFile -> do info verbosity $ "Reading parameters from " ++ infoFile readHookedBuildInfo verbosity infoFile -defaultTestHook :: Args -> PackageDescription -> LocalBuildInfo- -> UserHooks -> TestFlags -> IO ()+defaultTestHook+ :: Args+ -> PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> TestFlags+ -> IO () defaultTestHook args pkg_descr localbuildinfo _ flags =- test args pkg_descr localbuildinfo flags+ test args pkg_descr localbuildinfo flags -defaultBenchHook :: Args -> PackageDescription -> LocalBuildInfo- -> UserHooks -> BenchmarkFlags -> IO ()+defaultBenchHook+ :: Args+ -> PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> BenchmarkFlags+ -> IO () defaultBenchHook args pkg_descr localbuildinfo _ flags =- bench args pkg_descr localbuildinfo flags+ bench args pkg_descr localbuildinfo flags -defaultInstallHook :: PackageDescription -> LocalBuildInfo- -> UserHooks -> InstallFlags -> IO ()+defaultInstallHook+ :: PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> InstallFlags+ -> IO () defaultInstallHook pkg_descr localbuildinfo _ flags = do- let copyFlags = defaultCopyFlags {- copyDistPref = installDistPref flags,- copyDest = installDest flags,- copyVerbosity = installVerbosity flags- }+ 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- }+ 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+ :: 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+ :: 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+ :: PackageDescription+ -> LocalBuildInfo+ -> UserHooks+ -> RegisterFlags+ -> IO () defaultRegHook pkg_descr localbuildinfo _ flags =- if hasLibs pkg_descr+ if hasLibs pkg_descr then register pkg_descr localbuildinfo flags- else setupMessage (fromFlag (regVerbosity flags))- "Package contains no library to register:" (packageId pkg_descr)+ else+ setupMessage+ (fromFlag (regVerbosity flags))+ "Package contains no library to register:"+ (packageId pkg_descr)
src/Distribution/Simple/Bench.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Bench -- Copyright : Johan Tibell 2011@@ -13,113 +14,130 @@ -- 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.BuildPaths import Distribution.Simple.Compiler+import Distribution.Simple.Flag (fromFlag) import Distribution.Simple.InstallDirs import qualified Distribution.Simple.LocalBuildInfo as LBI-import Distribution.Simple.Setup+import Distribution.Simple.Setup.Benchmark import Distribution.Simple.UserHooks import Distribution.Simple.Utils-import Distribution.Pretty -import System.Directory ( doesFileExist )-import System.FilePath ( (</>), (<.>) )+import Distribution.Types.UnqualComponentName +import Distribution.Simple.Errors+import System.Directory (doesFileExist)+import System.FilePath ((<.>), (</>))+ -- | 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)-- -- 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 $- "Could not find benchmark program \""- ++ cmd ++ "\". Did you build the package first?"+ let verbosity = fromFlag $ benchmarkVerbosity flags+ benchmarkNames = args+ pkgBenchmarks = PD.benchmarks pkg_descr+ enabledBenchmarks = map fst (LBI.enabledBenchLBIs pkg_descr lbi) - 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+ -- 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 $+ dieWithException verbosity $+ NoBenchMarkProgram cmd - _ -> do- notice verbosity $ "No support for running "- ++ "benchmark " ++ name ++ " of type: "- ++ prettyShow (PD.benchmarkType bm)- exitFailure- where name = unUnqualComponentName $ PD.benchmarkName bm+ 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+ _ -> do+ notice verbosity $+ "No support for running "+ ++ "benchmark "+ ++ name+ ++ " of type: "+ ++ prettyShow (PD.benchmarkType bm)+ exitFailure+ where+ name = unUnqualComponentName $ PD.benchmarkName bm - unless (PD.hasBenchmarks pkg_descr) $ do- notice verbosity "Package has no benchmarks."- exitSuccess+ unless (PD.hasBenchmarks pkg_descr) $ do+ notice verbosity "Package has no benchmarks."+ exitSuccess - when (PD.hasBenchmarks pkg_descr && null enabledBenchmarks) $- die' verbosity $ "No benchmarks enabled. Did you remember to configure with "- ++ "\'--enable-benchmarks\'?"+ when (PD.hasBenchmarks pkg_descr && null enabledBenchmarks) $+ dieWithException verbosity EnableBenchMark - 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+ 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 ->+ 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+ 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,829 +1,1049 @@-{-# 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, 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.PackageInfoModule (generatePackageInfoModule)-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-import Distribution.Simple.Program.Builtin (haskellSuiteProgram)-import qualified Distribution.Simple.Program.GHC as GHC-import Distribution.Simple.Program.Types-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.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.ByteString.Lazy as LBS-import System.FilePath ( (</>), (<.>), takeDirectory )-import System.Directory ( getCurrentDirectory, removeFile, doesFileExist )---- -------------------------------------------------------------------------------- |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-- -- 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-- -- Now do the actual building- (\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)----- | 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- -> FilePath -- ^ 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)- pwd <- getCurrentDirectory-- (compilerProg, _) <- case flavorToProgram (compilerFlavor (compiler lbi)) of- Nothing -> die' verbosity $ "dumpBuildInfo: Unknown compiler flavor: "- ++ show (compilerFlavor (compiler lbi))- Just program -> requireProgram verbosity program (withPrograms lbi)-- let (warns, json) = mkBuildInfo pwd 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 (buildInfoPref distPref) buildInfoText-- when (not shouldDumpBuildInfo) $ do- -- Remove existing build-info.json as it might be outdated now.- exists <- doesFileExist (buildInfoPref distPref)- when exists $ removeFile (buildInfoPref distPref)- where- 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 HaskellSuite {} = Just haskellSuiteProgram- 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 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- $ flip addExtraJsSources 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- (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 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--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 -- 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 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----generateCode- :: [String]- -> UnqualComponentName- -> PackageDescription- -> BuildInfo- -> LocalBuildInfo- -> ComponentLocalBuildInfo- -> Verbosity- -> IO (FilePath, [ModuleName.ModuleName])-generateCode codeGens nm pdesc bi lbi clbi verbosity = do- when (not . null $ codeGens) $ createDirectoryIfMissingVerbose verbosity True 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- tgtDir = buildDir lbi </> nm' </> nm' ++ "-gen"- go :: String -> IO [ModuleName.ModuleName]- go codeGenProg = fmap fromString . lines <$> getDbProgramOutput verbosity (simpleProgram codeGenProg) (withPrograms lbi)- ((tgtDir : map getSymbolicPath 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 -> [FilePath] -> 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 -> [FilePath] -> 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 -> [FilePath] -> 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 -> [FilePath] -> 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 -> [FilePath] -> 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 -> FilePath -> BuildInfo-addSrcDir bi extra = bi { hsSourceDirs = new }- where new = ordNub (unsafeMakeSymbolicPath extra : hsSourceDirs bi)--replComponent :: ReplOptions- -> 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 :: ReplOptions -> 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 (replOptionsFlags replFlags) verbosity NoFlag pkg_descr lbi lib clbi- _ -> die' verbosity "A REPL is not supported for this compiler."--replExe :: ReplOptions -> 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 (replOptionsFlags replFlags) verbosity NoFlag pkg_descr lbi exe clbi- _ -> die' verbosity "A REPL is not supported for this compiler."--replFLib :: ReplOptions -> 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, PackageInfo_<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)-- let packageInfoModulePath = autogenComponentModulesDir lbi clbi- </> ModuleName.toFilePath (autogenPackageInfoModuleName pkg) <.> "hs"- packageInfoModuleDir = takeDirectory packageInfoModulePath- -- Ensure that the directory exists!- createDirectoryIfMissingVerbose verbosity True packageInfoModuleDir- rewriteFileEx verbosity packageInfoModulePath (generatePackageInfoModule pkg lbi)-- --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 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++ -- * Repl+ , repl+ , startInterpreter++ -- * Build preparation+ , preBuildComponent+ , AutogenFile (..)+ , AutogenFileContents+ , writeBuiltinAutogenFiles+ , writeAutogenFiles++ -- * Internal package database creation+ , createInternalPackageDB+ ) 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.HaskellSuite as HaskellSuite+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)+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.Builtin (haskellSuiteProgram)+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.Config+import Distribution.Simple.Setup.Repl+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, getCurrentDirectory, 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 pkg_descr lbi flags suffixes = do+ checkSemaphoreSupport verbosity (compiler lbi) flags+ 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++ -- 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++ -- Now do the actual building+ (\f -> foldM_ f (installedPkgs lbi) componentsToBuild) $ \index target -> do+ preBuildComponent verbosity lbi target+ let comp = targetComponent target+ clbi = targetCLBI target+ bi = componentBuildInfo comp+ progs' = addInternalBuildTools pkg_descr lbi bi (withPrograms lbi)+ lbi' =+ lbi+ { withPrograms = progs'+ , withPackageDB = withPackageDB lbi ++ [internalPackageDB]+ , installedPkgs = index+ }+ par_strat <-+ toFlag <$> case buildUseSemaphore flags of+ Flag sem_name -> case buildNumJobs flags of+ Flag{} -> do+ warn verbosity $ "Ignoring -j due to --semaphore"+ return $ UseSem sem_name+ NoFlag -> return $ UseSem sem_name+ NoFlag -> return $ case buildNumJobs flags of+ Flag n -> NumJobs n+ NoFlag -> Serial+ mb_ipi <-+ buildComponent+ flags+ par_strat+ 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)++-- | 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+ -> FilePath+ -- ^ 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+ )+ pwd <- getCurrentDirectory++ (compilerProg, _) <- case flavorToProgram (compilerFlavor (compiler lbi)) of+ Nothing ->+ dieWithException verbosity $ UnknownCompilerFlavor (compilerFlavor (compiler lbi))+ Just program -> requireProgram verbosity program (withPrograms lbi)++ let (warns, json) = mkBuildInfo pwd 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 (buildInfoPref distPref) buildInfoText++ when (not shouldDumpBuildInfo) $ do+ -- Remove existing build-info.json as it might be outdated now.+ exists <- doesFileExist (buildInfoPref distPref)+ when exists $ removeFile (buildInfoPref distPref)+ where+ 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 HaskellSuite{} = Just haskellSuiteProgram+ 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 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+ [] -> 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' =+ 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+ preBuildComponent verbosity lbi subtarget+ buildComponent+ mempty{buildVerbosity = toFlag 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+ preBuildComponent verbosity lbi target+ replComponent flags 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+ _ -> dieWithException verbosity REPLNotSupported++buildComponent+ :: BuildFlags+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> [PPSuffixHandler]+ -> Component+ -> ComponentLocalBuildInfo+ -> FilePath+ -> 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+ 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+ let verbosity = fromFlag $ buildVerbosity flags+ 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 -- 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)+ (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+ suffixes+ comp+ clbi+ distPref =+ do+ let verbosity = fromFlag $ buildVerbosity flags+ preprocessComponent pkg_descr comp lbi clbi False verbosity suffixes+ 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.+ 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+ 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 (FilePath, [ModuleName.ModuleName])+generateCode codeGens nm pdesc bi lbi clbi verbosity = do+ when (not . null $ codeGens) $ createDirectoryIfMissingVerbose verbosity True 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+ tgtDir = buildDir lbi </> nm' </> nm' ++ "-gen"+ go :: String -> IO [ModuleName.ModuleName]+ go codeGenProg =+ fmap fromString . lines+ <$> getDbProgramOutput+ verbosity+ (simpleProgram codeGenProg)+ (withPrograms lbi)+ ( (tgtDir : map getSymbolicPath 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 -> [FilePath] -> 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 -> [FilePath] -> 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 -> [FilePath] -> 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 -> [FilePath] -> 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 -> [FilePath] -> 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 -> FilePath -> BuildInfo+addSrcDir bi extra = bi{hsSourceDirs = new}+ where+ new = ordNub (unsafeMakeSymbolicPath extra : hsSourceDirs bi)++replComponent+ :: ReplFlags+ -> Verbosity+ -> PackageDescription+ -> LocalBuildInfo+ -> [PPSuffixHandler]+ -> Component+ -> ComponentLocalBuildInfo+ -> FilePath+ -> 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+ 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 pkg lbi lib' libClbi+replComponent+ replFlags+ verbosity+ pkg_descr+ lbi+ suffixes+ comp+ clbi+ _ =+ do+ preprocessComponent pkg_descr comp lbi clbi False verbosity suffixes+ 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+ -> 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 =+ 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+ -> 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+ :: 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+ HaskellSuite{} -> HaskellSuite.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++-- | Pre-build steps for a component: creates the autogenerated files+-- for a particular configured component.+preBuildComponent+ :: Verbosity+ -> LocalBuildInfo+ -- ^ Configuration information+ -> TargetInfo+ -> IO ()+preBuildComponent verbosity lbi tgt = do+ let pkg_descr = localPkgDescr lbi+ clbi = targetCLBI tgt+ createDirectoryIfMissingVerbose verbosity True (componentBuildDir lbi clbi)+ writeBuiltinAutogenFiles verbosity pkg_descr lbi clbi++-- | 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 = autogenComponentModulesDir lbi clbi
+ src/Distribution/Simple/Build/Inputs.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE PatternSynonyms #-}++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 (..)+ , buildingWhatVerbosity+ , buildingWhatDistPref+ )+where++import Distribution.Simple.Compiler+import Distribution.Simple.Setup (BuildingWhat (..), buildingWhatDistPref, buildingWhatVerbosity)+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@@ -19,92 +20,95 @@ -- -- TODO Figure out what to do about backpack and internal libraries. It is very -- suspicious that this stuff works with munged package identifiers-module Distribution.Simple.Build.Macros (- generateCabalMacrosHeader,- generatePackageVersionMacros,+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 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+ } 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,3 +1,4 @@+{- FOURMOLU_DISABLE -} {-# LANGUAGE DeriveGeneric #-} module Distribution.Simple.Build.Macros.Z (render, Z(..), ZPackage (..), ZTool (..)) where import Distribution.ZinzaPrelude
src/Distribution/Simple/Build/PackageInfoModule.hs view
@@ -1,4 +1,5 @@ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Build.PackageInfoModule -- Copyright :@@ -10,9 +11,8 @@ -- -- This is a module that Cabal generates for the benefit of packages. It -- enables them to find their package informations.----module Distribution.Simple.Build.PackageInfoModule (- generatePackageInfoModule+module Distribution.Simple.Build.PackageInfoModule+ ( generatePackageInfoModule ) where import Distribution.Compat.Prelude@@ -28,29 +28,33 @@ 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- }+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])+ 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+ 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+fixchar c = c
src/Distribution/Simple/Build/PackageInfoModule/Z.hs view
@@ -5,23 +5,23 @@ import Distribution.ZinzaPrelude data Z = Z- { zPackageName :: String,- zVersionDigits :: String,- zSynopsis :: String,- zCopyright :: String,- zHomepage :: String,- zSupportsNoRebindableSyntax :: Bool+ { 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 ()+ then do+ tell "{-# LANGUAGE NoRebindableSyntax #-}\n"+ return ()+ else do+ return () tell "{-# OPTIONS_GHC -fno-warn-missing-import-lists #-}\n" tell "{-# OPTIONS_GHC -w #-}\n" tell "module PackageInfo_"
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,124 @@ 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.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 +153,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
src/Distribution/Simple/BuildPaths.hs view
@@ -1,6 +1,8 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-}+ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.BuildPaths -- Copyright : Isaac Jones 2003-2004,@@ -11,53 +13,59 @@ -- Portability : portable -- -- A bunch of dirs, paths and file names used for intermediate build steps.-----module Distribution.Simple.BuildPaths (- defaultDistPref, srcPref,- buildInfoPref, haddockDirName, hscolourPref, haddockPref,- autogenPackageModulesDir,- autogenComponentModulesDir,-- autogenPathsModuleName,- autogenPackageInfoModuleName,- cppHeaderName,- haddockName,-- mkGenericStaticLibName,- mkLibName,- mkProfLibName,- mkGenericSharedLibName,- mkSharedLibName,- mkStaticLibName,- mkGenericSharedBundledLibName,+module Distribution.Simple.BuildPaths+ ( defaultDistPref+ , srcPref+ , buildInfoPref+ , haddockDirName+ , hscolourPref+ , haddockPref+ , autogenPackageModulesDir+ , autogenComponentModulesDir+ , autogenPathsModuleName+ , autogenPackageInfoModuleName+ , cppHeaderName+ , haddockName+ , mkGenericStaticLibName+ , mkLibName+ , mkProfLibName+ , mkGenericSharedLibName+ , mkSharedLibName+ , mkStaticLibName+ , mkGenericSharedBundledLibName+ , exeExtension+ , objExtension+ , dllExtension+ , staticLibExtension - exeExtension,- objExtension,- dllExtension,- staticLibExtension, -- * Source files & build directories- getSourceFiles, getLibSourceFiles, getExeSourceFiles,- getFLibSourceFiles, exeBuildDir, flibBuildDir,+ , getSourceFiles+ , getLibSourceFiles+ , getExeSourceFiles+ , getFLibSourceFiles+ , exeBuildDir+ , flibBuildDir ) 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 (defaultDistPref)+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+import System.FilePath (normalise, (<.>), (</>)) -- --------------------------------------------------------------------------- -- Build directories and files@@ -80,8 +88,8 @@ -- | 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 distPref pkg_descr =+ distPref </> "doc" </> "html" </> haddockDirName haddockTarget pkg_descr -- | The directory in which we put auto-generated modules for EVERY -- component in the package.@@ -92,6 +100,7 @@ -- particular component. autogenComponentModulesDir :: LocalBuildInfo -> ComponentLocalBuildInfo -> String autogenComponentModulesDir lbi clbi = componentBuildDir lbi clbi </> "autogen"+ -- NB: Look at 'checkForeignDeps' for where a simplified version of this -- has been copy-pasted. @@ -103,16 +112,18 @@ autogenPathsModuleName pkg_descr = ModuleName.fromString $ "Paths_" ++ map fixchar (prettyShow (packageName pkg_descr))- where fixchar '-' = '_'- fixchar c = c+ where+ fixchar '-' = '_'+ fixchar c = c -- | 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+ where+ fixchar '-' = '_'+ fixchar c = c haddockName :: PackageDescription -> FilePath haddockName pkg_descr = prettyShow (packageName pkg_descr) <.> "haddock"@@ -120,56 +131,70 @@ -- ----------------------------------------------------------------------------- -- Source File helper -getLibSourceFiles :: Verbosity- -> LocalBuildInfo- -> Library- -> ComponentLocalBuildInfo- -> IO [(ModuleName.ModuleName, FilePath)]+getLibSourceFiles+ :: Verbosity+ -> LocalBuildInfo+ -> Library+ -> ComponentLocalBuildInfo+ -> IO [(ModuleName.ModuleName, FilePath)] getLibSourceFiles verbosity lbi lib clbi = getSourceFiles verbosity 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+ searchpaths =+ componentBuildDir lbi clbi+ : map getSymbolicPath (hsSourceDirs bi)+ ++ [ autogenComponentModulesDir lbi clbi+ , autogenPackageModulesDir lbi+ ] -getExeSourceFiles :: Verbosity- -> LocalBuildInfo- -> Executable- -> ComponentLocalBuildInfo- -> IO [(ModuleName.ModuleName, FilePath)]+getExeSourceFiles+ :: Verbosity+ -> LocalBuildInfo+ -> Executable+ -> ComponentLocalBuildInfo+ -> IO [(ModuleName.ModuleName, FilePath)] 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 searchpaths modules+ srcMainPath <- findFileEx verbosity (map getSymbolicPath $ 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)+ bi = buildInfo exe+ modules = otherModules bi+ searchpaths =+ autogenComponentModulesDir lbi clbi+ : autogenPackageModulesDir lbi+ : exeBuildDir lbi exe+ : map getSymbolicPath (hsSourceDirs bi) -getFLibSourceFiles :: Verbosity- -> LocalBuildInfo- -> ForeignLib- -> ComponentLocalBuildInfo- -> IO [(ModuleName.ModuleName, FilePath)]+getFLibSourceFiles+ :: Verbosity+ -> LocalBuildInfo+ -> ForeignLib+ -> ComponentLocalBuildInfo+ -> IO [(ModuleName.ModuleName, FilePath)] getFLibSourceFiles verbosity lbi flib clbi = getSourceFiles verbosity searchpaths modules where- bi = foreignLibBuildInfo flib- modules = otherModules bi- searchpaths = autogenComponentModulesDir lbi clbi- : autogenPackageModulesDir lbi- : flibBuildDir lbi flib : map getSymbolicPath (hsSourceDirs bi)+ bi = foreignLibBuildInfo flib+ modules = otherModules bi+ searchpaths =+ autogenComponentModulesDir lbi clbi+ : autogenPackageModulesDir lbi+ : flibBuildDir lbi flib+ : map getSymbolicPath (hsSourceDirs bi) -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)+getSourceFiles+ :: Verbosity+ -> [FilePath]+ -> [ModuleName.ModuleName]+ -> IO [(ModuleName.ModuleName, FilePath)]+getSourceFiles verbosity dirs modules = flip traverse modules $ \m ->+ fmap ((,) m) $+ findFileWithExtension builtinHaskellSuffixes dirs (ModuleName.toFilePath m) >>= maybe (notFound m) (return . normalise) where- notFound module_ = die' verbosity $ "can't find source for module " ++ prettyShow module_+ notFound module_ =+ dieWithException verbosity $ CantFindSourceModule module_ -- | The directory where we put build results for an executable exeBuildDir :: LocalBuildInfo -> Executable -> FilePath@@ -195,29 +220,31 @@ 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) -- 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@@ -230,24 +257,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 = case platform of- Platform _ Windows -> "exe"- Platform Wasm32 _ -> "wasm"- _ -> ""+ Platform _ Windows -> "exe"+ Platform Wasm32 _ -> "wasm"+ _ -> "" -- | Extension for object files. For GHC the extension is @\"o\"@. objExtension :: String@@ -256,10 +285,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 --@@ -267,5 +296,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 DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# 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 = 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@(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,15 +5,42 @@ -- 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 informations (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"+ , "greencard"+ , "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 desugared in two cases:@@ -26,31 +53,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 +99,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/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
@@ -3,6 +3,7 @@ {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Command -- Copyright : Duncan Coutts 2007@@ -18,88 +19,102 @@ -- 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 (..)+ , 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,30 +122,43 @@ -- 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)]-- | BoolOpt Description OptFlags{-True-} OptFlags{-False-}- (Bool -> a -> a) (a-> Maybe 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) -- | 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.@@ -142,23 +170,40 @@ -- * @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"+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. -- Usually called in the context of 'option' or 'multiOption'.@@ -170,296 +215,398 @@ -- * @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+ :: 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 _n aa) = concatMap optDescrToGetOpt aa where- 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 def _) =- [GetOpt.Option cs ss (GetOpt.OptArg set' arg_desc) d]- where set' Nothing = Right def- set' (Just txt) = runReadE 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 (fmap Left) (commonFlags showOrParseArgs)- ++ map (fmap Right) options+ 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.@@ -467,132 +614,168 @@ hiddenCommand (Command name synopsys f _cmdType) = Command name synopsys 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, apparently 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,8 @@-{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Compiler -- Copyright : Isaac Jones 2003-2004@@ -20,88 +21,90 @@ -- 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+ , registrationPackageDB+ , absolutePackageDBPaths+ , absolutePackageDBPath - -- * 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,- arDashLSupported,- libraryDynDirSupported,- libraryVisibilitySupported,+ -- * Support for language extensions+ , CompilerFlag+ , languageToFlags+ , unsupportedLanguages+ , extensionsToFlags+ , unsupportedExtensions+ , parmakeSupported+ , reexportedModulesSupported+ , renamingPackageFlagsSupported+ , unifiedIPIDRequired+ , packageKeySupported+ , unitIdSupported+ , coverageSupported+ , profilingSupported+ , backpackSupported+ , arResponseFilesSupported+ , arDashLSupported+ , libraryDynDirSupported+ , libraryVisibilitySupported+ , jsemSupported - -- * 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.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, Typeable, Show, Read) instance Binary Compiler instance Structured Compiler@@ -111,30 +114,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.@@ -145,35 +145,38 @@ -- > 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 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)+-- | 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) instance Binary PackageDB instance Structured PackageDB@@ -193,114 +196,126 @@ -- 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] -- | 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 dbs = case safeLast dbs of Nothing -> error "internal error: empty package db set"- Just p -> p+ Just p -> p -- | Make package paths absolute-- absolutePackageDBPaths :: PackageDBStack -> IO PackageDBStack absolutePackageDBPaths = traverse absolutePackageDBPath absolutePackageDBPath :: PackageDB -> IO PackageDB-absolutePackageDBPath GlobalPackageDB = return GlobalPackageDB-absolutePackageDBPath UserPackageDB = return UserPackageDB+absolutePackageDBPath GlobalPackageDB = return GlobalPackageDB+absolutePackageDBPath UserPackageDB = return UserPackageDB absolutePackageDBPath (SpecificPackageDB db) = SpecificPackageDB `liftM` canonicalizePath db -- ------------------------------------------------------------+ -- * 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, Typeable) 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, Typeable) 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.@@ -329,8 +344,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@@ -348,18 +364,27 @@ 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 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).@@ -376,41 +401,44 @@ 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 -- | 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@@ -419,45 +447,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- | ProfDetailTopLate- | ProfDetailOther String- deriving (Eq, Generic, Read, Show, Typeable)+data ProfDetailLevel+ = ProfDetailNone+ | ProfDetailDefault+ | ProfDetailExportedFunctions+ | ProfDetailToplevelFunctions+ | ProfDetailAllFunctions+ | ProfDetailTopLate+ | ProfDetailOther String+ deriving (Eq, Generic, Read, Show, Typeable) 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)- , ("late-toplevel", ["late"], ProfDetailTopLate)+ , ("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"- ProfDetailTopLate -> "late-toplevel"- 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,2143 +1,2699 @@ {-# 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 Prelude ()-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.Simple.Program.Db (appendProgramSearchPath, modifyProgramSearchPath, lookupProgramByName)-import Distribution.Simple.Utils-import Distribution.System-import Distribution.Types.PackageVersionConstraint-import Distribution.Types.LocalBuildInfo-import Distribution.Types.ComponentRequestedSpec-import Distribution.Types.GivenComponent-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, doesPathExist )-import Data.ByteString.Lazy ( ByteString )-import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy.Char8 as BLC8-import Data.List- ( (\\), stripPrefix, intersect)-import qualified Data.List.NonEmpty as NEL-import qualified Data.Map as Map-import System.Directory- ( canonicalizePath, createDirectoryIfMissing, doesFileExist- , getTemporaryDirectory, removeFile)-import System.FilePath- ( (</>), isAbsolute, takeDirectory )-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.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- = 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)-- 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,- programDb :: ProgramDb)- <- configCompilerEx- (flagToMaybe (configHcFlavor cfg))- (flagToMaybe (configHcPath cfg))- (flagToMaybe (configHcPkg cfg))- programDbPre- (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-- -- 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.isDynamic comp)- 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.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 = withGHCiLib_,- 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)---- | 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 ++)- <$> appendProgramSearchPath (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) $- 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 (not . null . PackageIndex.lookupInternalDependency installedPackageSet pn vr) 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- , 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- }---- | 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 overridden 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.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 = 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 = extra initialStack- 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)- 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 (drop 2) includeDirs',- extraLibs = map (drop 2) extraLibs',- extraLibDirs = map (drop 2) extraLibDirs',- extraLibsStatic = map (drop 2) extraLibsStatic',- extraLibDirsStatic = map (drop 2) extraLibDirsStatic',- ccOptions = cflags',- ldOptions = ldflags''- }---- -------------------------------------------------------------------------------- Determining the compiler details--configCompilerAuxEx :: ConfigFlags- -> IO (Compiler, Platform, ProgramDb)-configCompilerAuxEx cfg = do- programDb <- mkProgramDb cfg defaultProgramDb- configCompilerEx (flagToMaybe $ configHcFlavor cfg)- (flagToMaybe $ configHcPath cfg)- (flagToMaybe $ configHcPkg cfg)- programDb- (fromFlag (configVerbosity cfg))--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 $- 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 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 . 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])-- 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 (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 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, warnings) =- partitionEithers (M.mapMaybe classEW $ pureChecks ++ ioChecks)- if null errors- then traverse_ (warn verbosity) (map ppPackageCheck warnings)- else die' verbosity (intercalate "\n\n" $ 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---- | 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 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 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"- ]+{-# 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+ , writePersistBuildConfig+ , getConfigStateFile+ , getPersistBuildConfig+ , checkPersistBuildConfigOutdated+ , tryGetPersistBuildConfig+ , maybeGetPersistBuildConfig+ , findDistPref+ , findDistPrefOrDefault+ , getInternalLibraries+ , computeComponentId+ , computeCompatPackageKey+ , localBuildInfoFile+ , getInstalledPackages+ , getInstalledPackagesMonitorFiles+ , getInstalledPackagesById+ , getPackageDBContents+ , configCompilerEx+ , configCompilerAuxEx+ , 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.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 (lookupProgramByName, modifyProgramSearchPath, prependProgramSearchPath)+import Distribution.Simple.Setup.Common as Setup+import Distribution.Simple.Setup.Config as Setup+import Distribution.Simple.Utils+import Distribution.System+import Distribution.Types.ComponentRequestedSpec+import Distribution.Types.GivenComponent+import qualified Distribution.Types.LocalBuildConfig as LBC+import Distribution.Types.LocalBuildInfo+import Distribution.Types.PackageVersionConstraint+import Distribution.Utils.LogProgress+import Distribution.Utils.NubList+import Distribution.Verbosity+import Distribution.Version++import qualified Distribution.Simple.GHC as GHC+import qualified Distribution.Simple.GHCJS as GHCJS+import qualified Distribution.Simple.HaskellSuite as HaskellSuite+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.Utils.Structured (structuredDecodeOrFailIO, structuredEncode)+import System.Directory+ ( canonicalizePath+ , createDirectoryIfMissing+ , doesFileExist+ , getTemporaryDirectory+ , removeFile+ )+import System.FilePath+ ( isAbsolute+ , takeDirectory+ , (</>)+ )+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+import Distribution.Simple.Errors+import Distribution.Types.AnnotatedId++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+ | -- | Mismatched version.+ ConfigStateFileBadVersion+ PackageIdentifier+ PackageIdentifier+ (Either ConfigStateFileError LocalBuildInfo)+ 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 (g_pkg_descr, hookedBuildInfo) cfg = do+ -- Cabal pre-configure+ (lbc1, comp, platform, enabledComps) <- preConfigurePackage cfg g_pkg_descr++ -- Cabal package-wide configure+ (lbc2, pbd2, pkg_info) <-+ finalizeAndConfigurePackage cfg lbc1 g_pkg_descr comp platform enabledComps++ -- Cabal per-component configure+ externalPkgDeps <- finalCheckPackage g_pkg_descr pbd2 hookedBuildInfo pkg_info+ configureComponents lbc2 pbd2 pkg_info externalPkgDeps++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+ 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)+ [] -> 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 build_dir :: FilePath -- e.g. dist/build+ build_dir = configFlagsBuildDir 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 build_dir++ lbc <- computeLocalBuildConfig cfg comp programDb00+ return (lbc, comp, compPlatform, enabled)++computeLocalBuildConfig+ :: ConfigFlags+ -> Compiler+ -> ProgramDb+ -> IO LBC.LocalBuildConfig+computeLocalBuildConfig cfg comp programDb = do+ let verbosity = fromFlag (configVerbosity cfg)+ -- 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.isDynamic comp)+ 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.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."++ 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+ , 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+ }++ 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) ComponentId+ , 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 verbosity = fromFlag (configVerbosity cfg)++ -- 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 = 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 verbosity = fromFlag (configVerbosity cfg)++ 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+ 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], [PromisedComponent])+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 verbosity = fromFlag (configVerbosity cfg)+ use_external_internal_deps =+ case enabled of+ OneComponentRequestedSpec{} -> True+ ComponentRequestedSpec{} -> False++ let cabalFileDir =+ maybe "." takeDirectory $+ flagToMaybe (configCabalFilePath cfg)+ 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], [PromisedComponent])+ -> 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 verbosity = fromFlag (configVerbosity cfg)+ 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+ 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++ -- 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 :: [GivenComponent] -> Map (PackageName, ComponentName) ComponentId+mkPromisedDepsSet comps = Map.fromList [((pn, CLibName ln), cid) | GivenComponent pn ln cid <- 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) ComponentId+ -> Map (PackageName, ComponentName) InstalledPackageInfo+ -- ^ required dependencies+ -> (Dependency -> Bool)+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++ ( sublibs == mainLibSet+ && Map.member+ ( pn+ , CLibName $+ LSubLibName $+ packageNameToUnqualComponentName depName+ )+ requiredDepsMap+ )+ || all visible sublibs+ | 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 =+ 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+ -- 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 -> 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_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) ComponentId+ -> InstalledPackageIndex+ -- ^ installed packages+ -> Map (PackageName, ComponentName) InstalledPackageInfo+ -- ^ required deps+ -> PackageDescription+ -> ComponentRequestedSpec+ -> IO ([PreExistingComponent], [PromisedComponent])+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 -}, 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 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 (LBC.BuildOptions -> LBC.BuildOptions)+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 buildOptions =+ buildOptions+ { LBC.withProfLib = tryLibProfiling+ , LBC.withProfLibDetail = libLevel+ , LBC.withProfExe = tryExeProfiling+ , LBC.withProfExeDetail = exeLevel+ }+ return (tryExeProfiling && not tryLibProfiling, apply)+ else do+ let apply buildOptions =+ buildOptions+ { LBC.withProfLib = 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 (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+ | -- | 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 PromisedComponent+ | -- | 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++-- | 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) ComponentId+ -- ^ 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 cid <- Map.lookup (dep_pkgname, CLibName lib) promisedIndex =+ return $ PromisedDependency (PromisedComponent dep_pkgname (AnnotatedId currentCabalId (CLibName lib) cid))+ 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 cid <- Map.lookup (dep_pkgname, CLibName lib) promisedIndex =+ return $ PromisedDependency (PromisedComponent dep_pkgname (AnnotatedId currentCabalId (CLibName lib) cid))+ 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+ -> PackageDBStack+ -- ^ The stack of package databases.+ -> ProgramDb+ -> IO InstalledPackageIndex+getInstalledPackages verbosity comp 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 packageDBs' progdb+ GHCJS -> GHCJS.getInstalledPackages verbosity packageDBs' progdb+ UHC -> UHC.getInstalledPackages verbosity comp packageDBs' progdb+ HaskellSuite{} ->+ HaskellSuite.getInstalledPackages verbosity packageDBs' progdb+ flv ->+ dieWithException verbosity $ HowToFindInstalledPackages 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 []++-- | 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 LocalBuildInfo{compiler = comp, withPackageDB = pkgDb, withPrograms = progDb} mkException unitids = do+ ipindex <- getInstalledPackages verbosity comp 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 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]+ -- ^ 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 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` (\_ -> dieWithException verbosity $ PkgConfigNotFound pkg versionRequirement)+ `catchExit` (\_ -> dieWithException verbosity $ PkgConfigNotFound pkg versionRequirement)+ let trim = dropWhile isSpace . dropWhileEnd isSpace+ 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 (drop 2) includeDirs'+ , extraLibs = map (drop 2) extraLibs'+ , extraLibDirs = map (drop 2) extraLibDirs'+ , extraLibsStatic = map (drop 2) extraLibsStatic'+ , extraLibDirsStatic = map (drop 2) extraLibDirsStatic'+ , ccOptions = cflags'+ , ldOptions = ldflags''+ }++-- -----------------------------------------------------------------------------+-- Determining the compiler details++configCompilerAuxEx+ :: ConfigFlags+ -> IO (Compiler, Platform, ProgramDb)+configCompilerAuxEx cfg = do+ programDb <- mkProgramDb cfg defaultProgramDb+ configCompilerEx+ (flagToMaybe $ configHcFlavor cfg)+ (flagToMaybe $ configHcPath cfg)+ (flagToMaybe $ configHcPkg cfg)+ programDb+ (fromFlag (configVerbosity cfg))++configCompilerEx+ :: Maybe CompilerFlavor+ -> Maybe FilePath+ -> Maybe FilePath+ -> 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 hcPkg progdb+ HaskellSuite{} -> HaskellSuite.configure verbosity hcPath hcPkg progdb+ _ -> dieWithException verbosity UnknownCompilerException+ 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 $+ 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 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 . 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])++ 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+ ( 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 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 =+ 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++-- | 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+ 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 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 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
@@ -1,9 +1,11 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE RankNTypes #-} {-# LANGUAGE LambdaCase #-}-{-# LANGUAGE CPP #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-} -----------------------------------------------------------------------------+{-# OPTIONS_GHC -fno-warn-deprecations #-}+ -- | -- Module : Distribution.Simple.ConfigureScript -- Copyright : Isaac Jones 2003-2005@@ -11,44 +13,50 @@ -- -- Maintainer : cabal-devel@haskell.org -- Portability : portable----{-# OPTIONS_GHC -fno-warn-deprecations #-}--module Distribution.Simple.ConfigureScript (- runConfigureScript+module Distribution.Simple.ConfigureScript+ ( runConfigureScript ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -- local import Distribution.PackageDescription+import Distribution.Pretty+import Distribution.Simple.Errors+import Distribution.Simple.LocalBuildInfo import Distribution.Simple.Program import Distribution.Simple.Program.Db-import Distribution.Simple.Setup--import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.Setup.Common+import Distribution.Simple.Setup.Config import Distribution.Simple.Utils+import Distribution.System (buildPlatform) import Distribution.Utils.NubList import Distribution.Verbosity-import Distribution.Pretty-import Distribution.System (buildPlatform) -- Base-import System.FilePath (searchPathSeparator, takeDirectory, (</>),- splitDirectories, dropDrive)+import System.FilePath+ ( dropDrive+ , searchPathSeparator+ , splitDirectories+ , takeDirectory+ , (</>)+ ) #ifdef mingw32_HOST_OS import System.FilePath (normalise, splitDrive) #endif-import Distribution.Compat.Directory (makeAbsolute)-import Distribution.Compat.Environment (getEnvironment)+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 :: Verbosity -> ConfigFlags -> LocalBuildInfo- -> IO ()+runConfigureScript+ :: Verbosity+ -> ConfigFlags+ -> LocalBuildInfo+ -> IO () runConfigureScript verbosity flags lbi = do env <- getEnvironment let programDb = withPrograms lbi@@ -60,8 +68,9 @@ -- 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"+ 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@@ -78,30 +87,40 @@ 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."- ]+ 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 $ flagAssignment lbi- ]+ 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 $ flagAssignment lbi+ ] -- 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+ flip Map.traverseWithKey cabalFlagMap $ \envVar -> \case -- No conflict: no problem singleFlag :| [] -> pure singleFlag -- Conflict: warn and discard all but first@@ -109,51 +128,61 @@ 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."- ]+ 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 $ flagAssignment lbi ]- )- ]+ 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 $ flagAssignment lbi]+ )+ ] let extraPath = fromNubList $ configProgramPathExtra flags- let cflagsEnv = maybe (unwords ccFlags) (++ (" " ++ unwords ccFlags))- $ lookup "CFLAGS" env+ 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)] ++- cabalFlagEnv+ pathEnv =+ maybe+ (intercalate spSep extraPath)+ ((intercalate spSep extraPath ++ spSep) ++)+ $ lookup "PATH" env+ overEnv =+ ("CFLAGS", Just cflagsEnv)+ : [("PATH", Just pathEnv) | not (null extraPath)]+ ++ cabalFlagEnv hp = hostPlatform lbi maybeHostFlag = if hp == buildPlatform then [] else ["--host=" ++ show (pretty hp)]- args' = configureFile':args ++ ["CC=" ++ ccProgShort] ++ maybeHostFlag+ args' = configureFile' : args ++ ["CC=" ++ ccProgShort] ++ maybeHostFlag shProg = simpleProgram "sh"- progDb <- appendProgramSearchPath verbosity extraPath emptyProgramDb- shConfiguredProg <- lookupProgram shProg- `fmap` configureProgram verbosity shProg progDb+ progDb <- prependProgramSearchPath verbosity extraPath 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+ Just sh ->+ runProgramInvocation verbosity $+ (programInvocation (sh{programOverrideEnv = overEnv}) args')+ { progInvokeCwd = Just (buildDir lbi)+ }+ Nothing -> dieWithException verbosity NotFoundMsg where args = configureArgs backwardsCompatHack flags backwardsCompatHack = False-- 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
+ src/Distribution/Simple/Errors.hs view
@@ -0,0 +1,800 @@+-----------------------------------------------------------------------------++-- 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 (FlagName, UnqualComponentName)+import Distribution.Pretty+ ( Pretty (pretty)+ , prettyShow+ )+import Distribution.Simple.InstallDirs+import Distribution.Simple.PreProcess.Types (Suffix)+import Distribution.System (OS)+import Distribution.Types.BenchmarkType+import Distribution.Types.LibraryName+import Distribution.Types.PkgconfigVersion+import Distribution.Types.TestType+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+ | 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+ | ProvideHaskellSuiteTool String+ | CannotDetermineCompilerVersion+ | 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+ | NoValidComponent+ | ConfigureEitherSingleOrAll+ | ConfigCIDValidForPreComponent+ | SanityCheckForEnableComponents+ | SanityCheckForDynamicStaticLinking+ | UnsupportedLanguages PackageIdentifier CompilerId [String]+ | UnsupportedLanguageExtension PackageIdentifier CompilerId [String]+ | CantFindForeignLibraries [String]+ | ExpectedAbsoluteDirectory FilePath+ | FlagsNotSpecified [FlagName]+ | EncounteredMissingDependency [Dependency]+ | 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+ | 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+ | FindFileCwd FilePath+ | FindFileEx 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+ deriving (Show, Typeable)++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+ ProvideHaskellSuiteTool{} -> 7509+ 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+ FindFileCwd{} -> 4765+ FindFileEx{} -> 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++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 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 testType -> "Unsupported test suite type: " ++ testType+ 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 testType -> "No support for building test suite type " ++ show testType+ NoSupportBuildingBenchMark benchMarkType -> "No support for building benchmark type " ++ show benchMarkType+ BuildingNotSupportedWithCompiler -> "Building is not supported with this compiler."+ ProvideHaskellSuiteTool msg -> show msg+ CannotDetermineCompilerVersion -> "haskell-suite: couldn't determine compiler version"+ 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 "+ ++ "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 a "+ ++ "executable with that name."+ ConfigureScriptNotFound -> "configure script not found."+ NoValidComponent -> "No valid component targets found"+ ConfigureEitherSingleOrAll -> "Can only configure either 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 . simplifyDependency)+ $ 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 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 ->+ "Operating system: "+ ++ prettyShow os+ ++ ", does not support relocatable builds"+ 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 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."+ 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+ FindFileCwd fileName -> fileName ++ " doesn't exist"+ FindFileEx 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 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."
src/Distribution/Simple/Flag.hs view
@@ -1,7 +1,10 @@-{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveFoldable #-} {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE FlexibleContexts #-}+ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Flag -- Copyright : Isaac Jones 2003-2004@@ -16,55 +19,61 @@ -- 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 (..)+ , allFlags+ , toFlag+ , fromFlag+ , fromFlagOrDefault+ , flagElim+ , flagToMaybe+ , flagToList+ , maybeToFlag+ , mergeListFlag+ , BooleanFlag (..)+ ) where -import Prelude () 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)+-- Isomorphic to 'Maybe' a.+data Flag a = Flag a | NoFlag deriving (Eq, Generic, Show, Read, Typeable, Foldable, Traversable) 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+ fmap _ NoFlag = NoFlag instance Applicative Flag where (Flag x) <*> y = x <$> y- NoFlag <*> _ = NoFlag+ NoFlag <*> _ = NoFlag pure = Flag instance Monoid (Flag a) where@@ -73,7 +82,7 @@ instance Semigroup (Flag a) where _ <> f@(Flag _) = f- f <> NoFlag = f+ f <> NoFlag = f instance Bounded a => Bounded (Flag a) where minBound = toFlag minBound@@ -81,52 +90,68 @@ instance Enum a => Enum (Flag a) where fromEnum = fromEnum . fromFlag- toEnum = toFlag . toEnum+ toEnum = toFlag . toEnum enumFrom (Flag a) = map toFlag . enumFrom $ a- enumFrom _ = []+ enumFrom _ = [] enumFromThen (Flag a) (Flag b) = toFlag `map` enumFromThen a b- enumFromThen _ _ = []- enumFromTo (Flag a) (Flag b) = toFlag `map` enumFromTo a b- enumFromTo _ _ = []+ 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 _ _ _ = []+ enumFromThenTo _ _ _ = [] +-- | 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 _ (Flag x) = x+fromFlagOrDefault def NoFlag = def +-- | Converts a 'Flag' value to a 'Maybe' value. flagToMaybe :: Flag a -> Maybe a flagToMaybe (Flag x) = Just x-flagToMaybe NoFlag = Nothing+flagToMaybe NoFlag = Nothing --- | @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 n _ NoFlag = n flagElim _ f (Flag x) = f x +-- | Converts a 'Flag' value to a list. flagToList :: Flag a -> [a] flagToList (Flag x) = [x]-flagToList NoFlag = []+flagToList NoFlag = [] +-- | 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 Nothing = NoFlag maybeToFlag (Just x) = Flag x +-- | 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,2185 +1,1014 @@-{-# 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.Types.PackageName.Magic-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, doesDirectoryExist- , getAppUserDataDirectory, createDirectoryIfMissing- , canonicalizePath, removeFile, renameFile, getDirectoryContents- , makeRelativeToCurrentDirectory )-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.10- -- ... and the following odd development version- unless (ghcVersion < mkVersion [9,10]) $- warn verbosity $- "Unknown/unsupported 'ghc' version detected "- ++ "(Cabal " ++ prettyShow cabalVersion ++ " supports 'ghc' version < 9.10): "- ++ 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- 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)-- 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 :: ReplOptions -> Verbosity- -> Cabal.Flag (Maybe Int) -> PackageDescription- -> LocalBuildInfo -> Library- -> ComponentLocalBuildInfo -> IO ()-replLib = buildOrReplLib . Just--buildOrReplLib :: Maybe ReplOptions -> 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- hasJsSupport = hostArch == JavaScript- has_code = not (componentIsIndefinite clbi)-- relLibTargetDir <- makeRelativeToCurrentDirectory libTargetDir-- (ghcProg, _) <- requireProgram verbosity ghcProgram (withPrograms lbi)- let runGhcProg = runGHC verbosity ghcProg comp platform-- let libBi = libBuildInfo lib-- -- ensure extra lib dirs exist before passing to ghc- cleanedExtraLibDirs <- filterM doesDirectoryExist (extraLibDirs libBi)- cleanedExtraLibDirsStatic <- filterM doesDirectoryExist (extraLibDirsStatic libBi)--- 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)- , if hasJsSupport- -- 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.- then toNubListR (jsSources libBi)- else mempty- ]- 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 = if withFullyStaticExe lbi- then extraLibsStatic libBi- else extraLibs libBi,- ghcOptLinkLibPath = toNubListR $- if withFullyStaticExe lbi- then cleanedExtraLibDirsStatic- else cleanedExtraLibDirs,- ghcOptLinkFrameworks = toNubListR $ PD.frameworks libBi,- ghcOptLinkFrameworkDirs = toNubListR $- PD.extraFrameworkDirs libBi,- ghcOptInputFiles = toNubListR- [relLibTargetDir </> x | x <- cLikeObjs]- }- replOpts = vanillaOpts {- ghcOptExtra = Internal.filterGhciFlags- (ghcOptExtra vanillaOpts)- <> replOptionsFlags replFlags,- ghcOptNumJobs = mempty,- ghcOptInputModules = replNoLoad replFlags (ghcOptInputModules vanillaOpts)- }- `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 relLibTargetDir 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- let libraryName = case libName lib of- LMainLibName -> "the main library"- LSubLibName name -> "library " <> prettyShow name- cSrcs' <- checkCSources verbosity libraryName (cSources libBi)- unless (not has_code || null cSrcs') $ do- info verbosity "Building C Sources..."- forM_ cSrcs' $ \filename -> do- let baseCcOpts = Internal.componentCcGhcOptions verbosity implInfo- lbi libBi clbi relLibTargetDir 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)-- -- build any JS sources- unless (not has_code || not hasJsSupport || null (jsSources libBi)) $ do- info verbosity "Building JS Sources..."- sequence_- [ do let vanillaJsOpts = Internal.componentJsGhcOptions verbosity implInfo- lbi libBi clbi relLibTargetDir filename- profJsOpts = vanillaJsOpts `mappend` mempty {- ghcOptProfilingMode = toFlag True,- ghcOptObjSuffix = toFlag "p_o"- }- odir = fromFlag (ghcOptObjDir vanillaJsOpts)- createDirectoryIfMissingVerbose verbosity True odir- let runGhcProgIfNeeded jsOpts = do- needsRecomp <- checkNeedsRecompilation filename jsOpts- when needsRecomp $ runGhcProg jsOpts- runGhcProgIfNeeded vanillaJsOpts- unless forRepl $ whenProfLib (runGhcProgIfNeeded profJsOpts)- | filename <- jsSources 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 relLibTargetDir 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 relLibTargetDir 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 = relLibTargetDir </> mkLibName uid- profileLibFilePath = relLibTargetDir </> mkProfLibName uid- sharedLibFilePath = relLibTargetDir </>- mkSharedLibName (hostPlatform lbi) compiler_id uid- staticLibFilePath = relLibTargetDir </>- mkStaticLibName (hostPlatform lbi) compiler_id uid- ghciLibFilePath = relLibTargetDir </> Internal.mkGHCiLibName uid- ghciProfLibFilePath = relLibTargetDir </> 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- relLibTargetDir objExtension True- hProfObjs <-- if withProfLib lbi- then Internal.getHaskellObjects implInfo lib lbi clbi- relLibTargetDir ("p_" ++ objExtension) True- else return []- hSharedObjs <-- if withSharedLib lbi- then Internal.getHaskellObjects implInfo lib lbi clbi- relLibTargetDir ("dyn_" ++ objExtension) False- else return []-- unless (null hObjs && null cLikeObjs && null stubObjs) $ do- rpaths <- getRPaths lbi clbi-- let staticObjectFiles =- hObjs- ++ map (relLibTargetDir </>) cLikeObjs- ++ stubObjs- profObjectFiles =- hProfObjs- ++ map (relLibTargetDir </>) cLikeProfObjs- ++ stubProfObjs- dynamicObjectFiles =- hSharedObjs- ++ map (relLibTargetDir </>) 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 $ cleanedExtraLibDirs,- ghcOptLinkFrameworks = toNubListR $ PD.frameworks libBi,- ghcOptLinkFrameworkDirs =- toNubListR $ PD.extraFrameworkDirs libBi,- ghcOptRPaths = rpaths <> toNubListR (extraLibDirs libBi)- }- 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 $ cleanedExtraLibDirs- }-- 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- :: ReplOptions -> 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- :: ReplOptions -> 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 ReplOptions Executable- | GBuildFLib ForeignLib- | GReplFLib ReplOptions 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- -> PackageId- -> CabalSpecVersion- -> FilePath- -> GBuildMode- -> IO BuildSources-gbuildSources verbosity 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 <- findFileEx verbosity (tmpDir : map getSymbolicPath (hsSourceDirs bnfo)) modPath- let mainModName = fromMaybe ModuleName.main $ exeMainModuleName exe- otherModNames = exeModules exe-- -- Scripts have fakePackageId and are always Haskell but can have any extension.- if isHaskell main || 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 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"]--replNoLoad :: Ord a => ReplOptions -> NubListR a -> NubListR a-replNoLoad replFlags l- | replOptionsNoLoad replFlags == Flag True = mempty- | otherwise = l---- | 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 = gbuildInfo bm-- -- 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 (package pkg_descr) (specVersion pkg_descr) tmpDir bm-- -- ensure extra lib dirs exist before passing to ghc- cleanedExtraLibDirs <- filterM doesDirectoryExist (extraLibDirs bnfo)- cleanedExtraLibDirsStatic <- filterM doesDirectoryExist (extraLibDirsStatic bnfo)--- 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 $ if package pkg_descr == fakePackageId- then filter isHaskell inputFiles- else inputFiles,- ghcOptInputScripts = toNubListR $ if package pkg_descr == fakePackageId- then filter (not . isHaskell) inputFiles- else [],- 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 = if withFullyStaticExe lbi- then extraLibsStatic bnfo- else extraLibs bnfo,- ghcOptLinkLibPath = toNubListR $- if withFullyStaticExe lbi- then cleanedExtraLibDirsStatic- else cleanedExtraLibDirs,- ghcOptLinkFrameworks = toNubListR $- PD.frameworks bnfo,- ghcOptLinkFrameworkDirs = toNubListR $- PD.extraFrameworkDirs bnfo,- ghcOptInputFiles = toNubListR- [tmpDir </> x | x <- cLikeObjs ++ cxxObjs]- }- dynLinkerOpts = mempty {- ghcOptRPaths = rpaths <> toNubListR (extraLibDirs bnfo),- ghcOptInputFiles = toNubListR- [tmpDir </> x | x <- cLikeObjs ++ cxxObjs]- }- replOpts = baseOpts {- ghcOptExtra = Internal.filterGhciFlags- (ghcOptExtra baseOpts)- <> replOptionsFlags replFlags,- ghcOptInputModules = replNoLoad replFlags (ghcOptInputModules baseOpts),- ghcOptInputFiles = replNoLoad replFlags (ghcOptInputFiles baseOpts)- }- -- 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- cSrcs' <- checkCSources verbosity (gbuildName bm) cSrcs- unless (null cSrcs') $ do- info verbosity "Building C Sources..."- forM_ cSrcs' $ \filename -> do- let baseCcOpts = Internal.componentCcGhcOptions verbosity implInfo- lbi bnfo clbi tmpDir filename- let 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- let profCcOpts = vanillaCcOpts `mappend` mempty {- ghcOptProfilingMode = toFlag True- }- let sharedCcOpts = vanillaCcOpts `mappend` mempty {- ghcOptFPic = toFlag True,- ghcOptDynLinkMode = toFlag GhcDynamicOnly- }- let opts | needProfiling = profCcOpts- | needDynamic = sharedCcOpts- | otherwise = vanillaCcOpts- let odir = fromFlag (ghcOptObjDir opts)- createDirectoryIfMissingVerbose verbosity True odir- needsRecomp <- checkNeedsRecompilation filename opts- when needsRecomp $- runGhcProg opts-- -- 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 -- Instruct GHC to link against libHSrts.- rtsLinkOpts :: GhcOptions- rtsLinkOpts- | supportsFLinkRts =- mempty {- ghcOptLinkRts = toFlag True- }- | otherwise =- mempty {- ghcOptLinkLibs = rtsOptLinkLibs,- ghcOptLinkLibPath = toNubListR $ rtsLibPaths rtsInfo- }- where- threaded = hasThreaded (gbuildInfo bm)- supportsFLinkRts = compilerVersion comp >= mkVersion [9,0]- 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 :: GhcOptions- linkOpts = case foreignLibType flib of- ForeignLibNativeShared ->- commonOpts- `mappend` linkerOpts- `mappend` dynLinkerOpts- `mappend` rtsLinkOpts- `mappend` mempty {- ghcOptLinkNoHsMain = toFlag True,- ghcOptShared = toFlag True,- 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 Wasi = 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---- | 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---- | 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- 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 "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 verbosity 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))- && (null (jsSources (libBuildInfo lib)) || not hasJsSupport)- hasJsSupport = case hostPlatform lbi of- Platform JavaScript _ -> True- _ -> False- 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 ""--checkCSources :: Verbosity -> String -> [String] -> IO [String]-checkCSources verbosity name cSrcs = do- let (headers, cSrcs') = partition (\filepath -> ".h" `isSuffixOf` filepath) cSrcs- others = filter (\filepath -> not (".c" `isSuffixOf` filepath)) cSrcs'- unless (null headers) $ do- let files = intercalate ", " headers- warn verbosity $ unlines- [ "The following header files listed in " <> name <> "'s c-sources will not be used: " <> files <> "."- , "Header files should be in the 'include' or 'install-include' stanza."- , "See https://cabal.readthedocs.io/en/3.10/cabal-package.html#pkg-field-includes"- ]- unless (null others) $ do- let files = intercalate ", " others- warn verbosity $ unlines- [ "The following files listed in " <> name <> "'s c-sources do not have the expected '.c' extension " <> files <> "."- , "C++ files should be in the 'cxx-sources' stanza."- , "See https://cabal.readthedocs.io/en/3.10/cabal-package.html#pkg-field-cxx-sources"- ]- return cSrcs'+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TupleSections #-}++-----------------------------------------------------------------------------++-- |+-- 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+ , Internal.componentGhcOptions+ , Internal.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 Distribution.Compat.Prelude+import Prelude ()++import Control.Monad (forM_)+import Data.List (stripPrefix)+import qualified Data.Map as Map+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 Distribution.Simple.Flag (Flag (..), toFlag)+import qualified Distribution.Simple.GHC.Build as GHC+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 (extraCompilationArtifacts)+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.Verbosity+import Distribution.Version+import Language.Haskell.Extension+import System.Directory+ ( canonicalizePath+ , createDirectoryIfMissing+ , doesDirectoryExist+ , doesFileExist+ , getAppUserDataDirectory+ , getDirectoryContents+ )+import System.FilePath+ ( takeDirectory+ , (<.>)+ , (</>)+ )+import qualified System.Info+#ifndef mingw32_HOST_OS+import System.Directory (renameFile)+import System.Posix (createSymbolicLink)+#endif /* mingw32_HOST_OS */++import Distribution.Simple.Setup (BuildingWhat (..))+import Distribution.Simple.Setup.Build++-- -----------------------------------------------------------------------------+-- 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.12+ -- ... and the following odd development version+ unless (ghcVersion < mkVersion [9, 12]) $+ warn verbosity $+ "Unknown/unsupported 'ghc' version detected "+ ++ "(Cabal "+ ++ prettyShow cabalVersion+ ++ " supports 'ghc' version < 9.12): "+ ++ 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) $+ 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++ 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++ compilerAbiTag :: AbiTag+ compilerAbiTag = maybe NoAbiTag AbiTag (Map.lookup "Project Unit Id" ghcInfoMap >>= stripPrefix (prettyShow compilerId <> "-"))++ let comp =+ Compiler+ { compilerId+ , compilerAbiTag+ , 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 =+ dieWithException verbosity CheckPackageDbStackPost76+checkPackageDbStackPost76 _ _ = return ()++checkPackageDbStackPre76 :: Verbosity -> PackageDBStack -> 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+ -> [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+ :: BuildFlags+ -> Flag ParStrat+ -> PackageDescription+ -> LocalBuildInfo+ -> Library+ -> ComponentLocalBuildInfo+ -> IO ()+buildLib flags numJobs pkg lbi lib clbi =+ GHC.build numJobs pkg $+ PreBuildComponentInputs (BuildNormal flags) lbi (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 (BuildRepl flags) lbi (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+ runGHC verbosity ghcProg comp platform 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 (BuildNormal mempty{buildVerbosity = toFlag v}) lbi (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 (BuildRepl replFlags) lbi (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 (BuildNormal mempty{buildVerbosity = toFlag v}) lbi (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 (BuildRepl replFlags) lbi (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+ 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 = 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 verbosity ghcProg comp platform 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+ -- copy .hi files over:+ whenVanilla $ copyModuleFiles $ Suffix "hi"+ whenProf $ copyModuleFiles $ Suffix "p_hi"+ whenShared $ copyModuleFiles $ Suffix "dyn_hi"++ -- copy extra compilation artifacts that ghc plugins may produce+ copyDirectoryIfExists extraCompilationArtifacts++ -- 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++ copyDirectoryIfExists dirName = do+ let src = builtDir </> dirName+ dst = targetDir </> 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)+ 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)
+ src/Distribution/Simple/GHC/Build.hs view
@@ -0,0 +1,140 @@+module Distribution.Simple.GHC.Build where++import Distribution.Compat.Prelude+import Prelude ()++import Control.Monad.IO.Class+import qualified Data.Set as Set+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 (withDynFLib)+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.Program+import Distribution.Simple.Utils+import Distribution.Types.ComponentLocalBuildInfo (componentIsIndefinite)+import Distribution.Types.ParStrat+import Distribution.Utils.NubList (fromNubListR)+import System.Directory hiding (exeExtension)+import System.FilePath++{-+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+`makeRelativeToCurrentDirectory` 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+ component = buildComponent pbci+ isLib = buildIsLib pbci+ lbi = localBuildInfo pbci+ clbi = buildCLBI pbci++ -- Create a few directories for building the component+ -- See Note [Build Target Dir vs Target Dir]+ let targetDir_absolute = componentBuildDir lbi clbi+ buildTargetDir_absolute+ -- Libraries use the target dir for building (see above)+ | isLib = targetDir_absolute+ -- In other cases, use targetDir/<name-of-target-dir>-tmp+ | targetDirName : _ <- reverse $ splitDirectories targetDir_absolute =+ targetDir_absolute </> (targetDirName ++ "-tmp")+ | otherwise = error "GHC.build: targetDir is empty"++ liftIO $ do+ createDirectoryIfMissingVerbose verbosity True targetDir_absolute+ createDirectoryIfMissingVerbose verbosity True buildTargetDir_absolute++ -- See Note [Build Target Dir vs Target Dir] as well+ _targetDir <- liftIO $ makeRelativeToCurrentDirectory targetDir_absolute+ buildTargetDir <-+ -- 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.+ if isLib+ then liftIO $ makeRelativeToCurrentDirectory buildTargetDir_absolute+ else return buildTargetDir_absolute++ (ghcProg, _) <- liftIO $ requireProgram verbosity ghcProgram (withPrograms lbi)++ -- Determine in which ways we want to build the component+ let+ wantVanilla = if isLib then withVanillaLib lbi else False+ -- Arguably, wantStatic should be "withFullyStaticExe lbi" for executables,+ -- but it was not before the refactor.+ wantStatic = if isLib then withStaticLib lbi else not (wantDynamic || wantProf)+ wantDynamic = case component of+ CLib{} -> withSharedLib lbi+ CFLib flib -> withDynFLib flib+ CExe{} -> withDynExe lbi+ CTest{} -> withDynExe lbi+ CBench{} -> withDynExe lbi+ wantProf = if isLib then withProfLib lbi else withProfExe lbi++ -- See also Note [Building Haskell Modules accounting for TH] in Distribution.Simple.GHC.Build.Modules+ -- We build static by default if no other way is wanted.+ -- For executables and foreign libraries, there should only be one wanted way.+ wantedWays =+ Set.fromList $+ -- If building a library, we accumulate all the ways,+ -- otherwise, we take just one.+ (if isLib then id else take 1) $+ [ProfWay | wantProf]+ -- I don't see why we shouldn't build with dynamic+ -- indefinite components.+ <> [DynWay | wantDynamic && not (componentIsIndefinite clbi)]+ <> [StaticWay | wantStatic || wantVanilla || not (wantDynamic || wantProf)]++ liftIO $ info verbosity ("Wanted build ways: " ++ show (Set.toList wantedWays))++ -- 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).+ buildOpts <- buildHaskellModules numJobs ghcProg pkg_descr buildTargetDir_absolute wantedWays pbci+ extraSources <- buildAllExtraSources ghcProg buildTargetDir pbci+ linkOrLoadComponent ghcProg pkg_descr (fromNubListR extraSources) (buildTargetDir, targetDir_absolute) (wantedWays, buildOpts) pbci
+ src/Distribution/Simple/GHC/Build/ExtraSources.hs view
@@ -0,0 +1,242 @@+{-# LANGUAGE DisambiguateRecordFields #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE NamedFieldPuns #-}++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.GHC.Build.Utils+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.Program.Types+import Distribution.System (Arch (JavaScript), Platform (..))+import Distribution.Types.ComponentLocalBuildInfo+import Distribution.Types.Executable+import Distribution.Verbosity (Verbosity)++import Distribution.Simple.Build.Inputs++-- | An action that builds all the extra build sources of a component, i.e. C,+-- C++, Js, Asm, C-- sources.+buildAllExtraSources+ :: ConfiguredProgram+ -- ^ The GHC configured program+ -> FilePath+ -- ^ The build directory for this target+ -> PreBuildComponentInputs+ -- ^ The context and component being built in it.+ -> IO (NubListR FilePath)+ -- ^ Returns the (nubbed) list of extra sources that were built+buildAllExtraSources =+ mconcat+ [ buildCSources+ , buildCxxSources+ , buildJsSources+ , buildAsmSources+ , buildCmmSources+ ]++buildCSources+ , buildCxxSources+ , buildJsSources+ , buildAsmSources+ , buildCmmSources+ :: ConfiguredProgram+ -- ^ The GHC configured program+ -> FilePath+ -- ^ The build directory for this target+ -> PreBuildComponentInputs+ -- ^ The context and component being built in it.+ -> IO (NubListR FilePath)+ -- ^ Returns the list of extra sources that were built+buildCSources =+ buildExtraSources+ "C Sources"+ Internal.componentCcGhcOptions+ True+ ( \c ->+ cSources (componentBuildInfo c)+ ++ case c of+ CExe exe | isC (modulePath exe) -> [modulePath exe]+ _otherwise -> []+ )+buildCxxSources =+ buildExtraSources+ "C++ Sources"+ Internal.componentCxxGhcOptions+ True+ ( \c ->+ cxxSources (componentBuildInfo c)+ ++ case c of+ CExe exe | isCxx (modulePath exe) -> [modulePath exe]+ _otherwise -> []+ )+buildJsSources ghcProg buildTargetDir = do+ Platform hostArch _ <- hostPlatform <$> localBuildInfo+ let hasJsSupport = hostArch == JavaScript+ buildExtraSources+ "JS Sources"+ Internal.componentJsGhcOptions+ False+ ( \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+buildAsmSources =+ buildExtraSources+ "Assembler Sources"+ Internal.componentAsmGhcOptions+ True+ (asmSources . componentBuildInfo)+buildCmmSources =+ buildExtraSources+ "C-- Sources"+ Internal.componentCmmGhcOptions+ True+ (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 -> FilePath -> FilePath -> GhcOptions)+ -- ^ Function to determine the @'GhcOptions'@ for the+ -- invocation of GHC when compiling these extra sources (e.g.+ -- @'Internal.componentCxxGhcOptions'@,+ -- @'Internal.componentCmmGhcOptions'@)+ -> Bool+ -- ^ Some types of build sources should not be built in the dynamic way, namely, JS sources.+ -- I'm not entirely sure this remains true after we migrate to supporting GHC's JS backend rather than GHCJS.+ -- Boolean for "do we allow building these sources the dynamic way?"+ -> (Component -> [FilePath])+ -- ^ 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+ -> FilePath+ -- ^ The build directory for this target+ -> PreBuildComponentInputs+ -- ^ The context and component being built in it.+ -> IO (NubListR FilePath)+ -- ^ Returns the list of extra sources that were built+buildExtraSources description componentSourceGhcOptions wantDyn viewSources ghcProg buildTargetDir =+ \PreBuildComponentInputs{buildingWhat, localBuildInfo = lbi, targetInfo} ->+ let+ bi = componentBuildInfo (targetComponent targetInfo)+ verbosity = buildingWhatVerbosity buildingWhat+ clbi = targetCLBI targetInfo++ sources = viewSources (targetComponent targetInfo)++ comp = compiler lbi+ platform = hostPlatform lbi+ -- Instead of keeping this logic here, we really just want to+ -- receive as an input the `neededWays` from GHC/Build.build and build+ -- accordingly, since we've already determined the extra needed ways+ -- needed for e.g. template haskell. Although we'd have to account for 'wantDyn'.+ isGhcDynamic = isDynamic comp+ doingTH = usesTemplateHaskellOrQQ bi+ forceSharedLib = doingTH && isGhcDynamic+ runGhcProg = runGHC verbosity ghcProg comp platform++ buildAction sourceFile = do+ let baseSrcOpts =+ componentSourceGhcOptions+ verbosity+ lbi+ bi+ clbi+ buildTargetDir+ sourceFile+ vanillaSrcOpts+ -- Dynamic GHC requires C sources to be built+ -- with -fPIC for REPL to work. See #2207.+ | isGhcDynamic && wantDyn = baseSrcOpts{ghcOptFPic = toFlag True}+ | otherwise = baseSrcOpts+ profSrcOpts =+ vanillaSrcOpts+ `mappend` mempty+ { ghcOptProfilingMode = toFlag True+ }+ sharedSrcOpts =+ vanillaSrcOpts+ `mappend` mempty+ { 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 opts = do+ needsRecomp <- checkNeedsRecompilation sourceFile opts+ when needsRecomp $ runGhcProg opts++ -- TODO: This whole section can be streamlined to the+ -- wantedWays+neededWays logic used in Build/Modules.hs+ createDirectoryIfMissingVerbose verbosity True odir+ case targetComponent targetInfo of+ -- For libraries, we compile extra objects in the three ways: vanilla, shared, and profiled.+ -- We suffix shared objects with .dyn_o and profiled ones with .p_o.+ CLib _lib+ -- Unless for repl, in which case we only need the vanilla way+ | BuildRepl _ <- buildingWhat ->+ compileIfNeeded vanillaSrcOpts+ | otherwise ->+ do+ compileIfNeeded vanillaSrcOpts+ when (wantDyn && (forceSharedLib || withSharedLib lbi)) $+ compileIfNeeded sharedSrcOpts{ghcOptObjSuffix = toFlag "dyn_o"}+ when (withProfLib lbi) $+ compileIfNeeded profSrcOpts{ghcOptObjSuffix = toFlag "p_o"}++ -- For foreign libraries, we determine with which options to build the+ -- objects (vanilla vs shared vs profiled)+ CFLib flib+ | withProfExe lbi -> -- It doesn't sound right to query "ProfExe" for a foreign library...+ compileIfNeeded profSrcOpts+ | withDynFLib flib && wantDyn ->+ compileIfNeeded sharedSrcOpts+ | otherwise ->+ compileIfNeeded vanillaSrcOpts+ -- 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+ | withProfExe lbi ->+ compileIfNeeded profSrcOpts+ | withDynExe lbi && wantDyn ->+ compileIfNeeded sharedSrcOpts+ | otherwise ->+ compileIfNeeded vanillaSrcOpts+ in+ -- 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,663 @@+{-# LANGUAGE LambdaCase #-}++module Distribution.Simple.GHC.Build.Link where++import Distribution.Compat.Prelude+import Prelude ()++import Control.Exception (assert)+import Control.Monad (forM_)+import Control.Monad.IO.Class+import qualified Data.ByteString.Lazy.Char8 as BS+import qualified Data.Set as Set+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.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.Repl+import Distribution.Simple.Utils+import Distribution.System+import Distribution.Types.ComponentLocalBuildInfo+import Distribution.Utils.NubList+import Distribution.Verbosity+import Distribution.Version+import System.Directory+import System.FilePath++-- | 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+ -> [FilePath]+ -- ^ The full list of extra build sources (all C, C++, Js,+ -- Asm, and Cmm sources), which were compiled to object+ -- files.+ -> (FilePath, FilePath)+ -- ^ The build target dir, and the target dir.+ -- See Note [Build Target Dir vs Target Dir] in Distribution.Simple.GHC.Build+ -> (Set.Set 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) (wantedWays, 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++ -- ensure extra lib dirs exist before passing to ghc+ cleanedExtraLibDirs <- liftIO $ filterM doesDirectoryExist (extraLibDirs bi)+ cleanedExtraLibDirsStatic <- liftIO $ filterM doesDirectoryExist (extraLibDirsStatic bi)++ let+ extraSourcesObjs = map (`replaceExtension` objExtension) 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 $ PD.frameworks bi+ , ghcOptLinkFrameworkDirs = toNubListR $ PD.extraFrameworkDirs bi+ , ghcOptInputFiles = toNubListR [buildTargetDir </> x | x <- 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)+ , ghcOptInputModules = replNoLoad (replReplOptions replFlags) (ghcOptInputModules staticOpts)+ , ghcOptInputFiles = replNoLoad (replReplOptions replFlags) (ghcOptInputFiles staticOpts)+ }+ -- 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+ }++ -- 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 (pkgName (PD.package pkg_descr)) target+ _otherwise ->+ let+ runGhcProg = runGHC verbosity ghcProg comp platform+ platform = hostPlatform lbi+ comp = compiler lbi+ in+ when (not $ componentIsIndefinite clbi) $ do+ -- If not building dynamically, we don't pass any runtime paths.+ rpaths <- if DynWay `Set.member` wantedWays then getRPaths pbci else return (toNubListR [])+ liftIO $ do+ info verbosity "Linking..."+ let linkExeLike name = linkExecutable (linkerOpts rpaths) (wantedWays, buildOpts) targetDir name runGhcProg lbi+ case component of+ CLib lib -> linkLibrary buildTargetDir cleanedExtraLibDirs pkg_descr verbosity runGhcProg lib lbi clbi extraSources rpaths wantedWays+ CFLib flib -> linkFLib flib bi lbi (linkerOpts rpaths) (wantedWays, buildOpts) targetDir runGhcProg+ CExe exe -> linkExeLike (exeName exe)+ CTest test -> linkExeLike (testName test)+ CBench bench -> linkExeLike (benchmarkName bench)++-- | Link a library component+linkLibrary+ :: FilePath+ -- ^ The library target build directory+ -> [FilePath]+ -- ^ 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+ -> [FilePath]+ -- ^ Extra build sources (that were compiled to objects)+ -> NubListR FilePath+ -- ^ A list with the runtime-paths (rpaths), or empty if not linking dynamically+ -> Set.Set BuildWay+ -- ^ Wanted build ways and corresponding build options+ -> IO ()+linkLibrary buildTargetDir cleanedExtraLibDirs pkg_descr verbosity runGhcProg lib lbi clbi extraSources rpaths wantedWays = do+ let+ 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 </> mkLibName uid+ profileLibFilePath = buildTargetDir </> mkProfLibName uid+ sharedLibFilePath =+ buildTargetDir+ </> mkSharedLibName (hostPlatform lbi) compiler_id uid+ staticLibFilePath =+ buildTargetDir+ </> mkStaticLibName (hostPlatform lbi) compiler_id uid+ ghciLibFilePath = buildTargetDir </> Internal.mkGHCiLibName uid+ ghciProfLibFilePath = buildTargetDir </> Internal.mkGHCiProfLibName uid+ libInstallPath =+ libdir $+ absoluteComponentInstallDirs+ pkg_descr+ lbi+ uid+ NoCopyDest+ sharedLibInstallPath =+ libInstallPath+ </> mkSharedLibName (hostPlatform lbi) compiler_id uid++ getObjFiles way =+ mconcat+ [ Internal.getHaskellObjects+ implInfo+ lib+ lbi+ clbi+ buildTargetDir+ (buildWayPrefix way ++ objExtension)+ True+ , pure $+ map (buildTargetDir </>) $+ map ((`replaceExtension` (buildWayPrefix way ++ objExtension))) extraSources+ , catMaybes+ <$> sequenceA+ [ findFileWithExtension+ [Suffix $ buildWayPrefix way ++ objExtension]+ [buildTargetDir]+ (ModuleName.toFilePath x ++ "_stub")+ | ghcVersion < mkVersion [7, 2] -- ghc-7.2+ does not make _stub.o files+ , x <- allLibModules lib clbi+ ]+ ]++ -- 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 dynObjectFiles =+ ghcBaseLinkArgs+ { ghcOptShared = toFlag True+ , ghcOptDynLinkMode = toFlag GhcDynamicOnly+ , ghcOptInputFiles = toNubListR 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 $ PD.frameworks libBi+ , ghcOptLinkFrameworkDirs =+ toNubListR $ PD.extraFrameworkDirs libBi+ , ghcOptRPaths = rpaths+ }+ ghcStaticLinkArgs staticObjectFiles =+ ghcBaseLinkArgs+ { ghcOptStaticLib = toFlag True+ , ghcOptInputFiles = toNubListR 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++ 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+ 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+ -> (Set.Set BuildWay, BuildWay -> GhcOptions)+ -- ^ The wanted build ways and corresponding GhcOptions that were+ -- used to compile the modules in that way.+ -> FilePath+ -- ^ 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 (wantedWays, buildOpts) targetDir targetName runGhcProg lbi = do+ -- When building an executable, we should only "want" one build way.+ assert (Set.size wantedWays == 1) $+ forM_ wantedWays $ \way -> 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 </> exeTargetName (hostPlatform lbi) targetName+ when (compilerVersion comp < mkVersion [7, 7]) $ do+ e <- doesFileExist target+ when e (removeFile target)+ runGhcProg linkOpts{ghcOptOutputFile = toFlag target}++-- | Link a foreign library component+linkFLib+ :: ForeignLib+ -> BuildInfo+ -> LocalBuildInfo+ -> (GhcOptions)+ -- ^ The linker-specific GHC options+ -> (Set.Set BuildWay, BuildWay -> GhcOptions)+ -- ^ The wanted build ways and corresponding GhcOptions that were+ -- used to compile the modules in that way.+ -> FilePath+ -- ^ 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 (wantedWays, 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 $ 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 :: BuildWay -> GhcOptions+ linkOpts way = case foreignLibType flib of+ ForeignLibNativeShared ->+ (buildOpts way)+ `mappend` linkerOpts+ `mappend` rtsLinkOpts+ `mappend` mempty+ { ghcOptLinkNoHsMain = toFlag True+ , ghcOptShared = toFlag True+ , ghcOptFPic = toFlag True+ , ghcOptLinkModDefFiles = toNubListR $ 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+ -- There should not be more than one wanted way when building an flib+ assert (Set.size wantedWays == 1) $+ forM_ wantedWays $ \way -> do+ runGhcProg (linkOpts way){ghcOptOutputFile = toFlag (targetDir </> buildName)}+ renameFile (targetDir </> buildName) (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 (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+ comp = compiler lbi+ platform = hostPlatform lbi+ in case replOptionsFlagOutput (replReplOptions rflags) of+ NoFlag -> runGHC (fromFlag $ replVerbosity rflags) ghcProg comp platform ghcOpts+ Flag out_dir -> do+ src_dir <- getCurrentDirectory+ let uid = componentUnitId clbi+ this_unit = prettyShow uid+ reexported_modules = [mn | LibComponentLocalBuildInfo{} <- [clbi], IPI.ExposedModule mn (Just{}) <- componentExposedModules clbi]+ hidden_modules = otherModules bi+ extra_opts =+ concat $+ [ ["-this-package-name", prettyShow pkg_name]+ , ["-working-dir", src_dir]+ ]+ ++ [ ["-reexported-module", prettyShow m] | m <- reexported_modules+ ]+ ++ [ ["-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+ writeFileAtomic (out_dir </> this_unit) $+ BS.pack $+ escapeArgs $+ extra_opts ++ renderGhcOptions comp platform (ghcOpts{ghcOptMode = NoFlag})++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,352 @@+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE TupleSections #-}++module Distribution.Simple.GHC.Build.Modules (buildHaskellModules, BuildWay (..), buildWayPrefix) 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.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 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+ -> PD.PackageDescription+ -- ^ The package description+ -> FilePath+ -- ^ The path to the build directory for this target, which+ -- has already been created.+ -> Set.Set BuildWay+ -- ^ The set of wanted 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 pkg_descr buildTargetDir wantedWays 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++ -- 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.+ let isCoverageEnabled = if isLib then libCoverage lbi else exeCoverage lbi+ hpcdir way+ | forRepl = mempty -- HPC is not supported in ghci+ | isCoverageEnabled = Flag $ Hpc.mixDir (buildTargetDir </> extraCompilationArtifacts) way+ | otherwise = mempty++ (inputFiles, inputModules) <- componentInputs buildTargetDir pkg_descr pbci++ let+ runGhcProg = runGHC verbosity ghcProg comp platform+ platform = hostPlatform lbi++ -- See Note [Building Haskell Modules accounting for TH]+ doingTH = usesTemplateHaskellOrQQ bi++ -- 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 $+ if PD.package pkg_descr == fakePackageId+ then filter isHaskell inputFiles+ else inputFiles+ , ghcOptInputScripts =+ toNubListR $+ if PD.package pkg_descr == fakePackageId+ then filter (not . isHaskell) inputFiles+ else []+ , 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)+ }+ -- 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)+ }++ -- 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++ defaultGhcWay = if isDynamic comp then DynWay else StaticWay++ -- If there aren't modules, or if we're loading the modules in repl, don't build.+ unless (forRepl || (null inputFiles && null inputModules)) $ liftIO $ do+ -- See Note [Building Haskell Modules accounting for TH]+ let+ neededWays =+ wantedWays+ <> Set.fromList+ -- TODO: You also don't need to build the GHC way when doing TH if+ -- you are using an external interpreter!!+ [defaultGhcWay | doingTH && defaultGhcWay `Set.notMember` wantedWays]++ -- If we need both static and dynamic, use dynamic-too instead of+ -- compiling twice (if we support it)+ useDynamicToo =+ StaticWay `Set.member` neededWays+ && DynWay `Set.member` neededWays+ && supportsDynamicToo comp+ && null (hcSharedOptions GHC bi)++ -- The ways we'll build, in order+ orderedBuilds+ -- If we can use dynamic-too, do it first. The default GHC way can only+ -- be static or dynamic, so, if we build both right away, any modules+ -- possibly needed by TH later (e.g. if building profiled) are already built.+ | useDynamicToo =+ [buildStaticAndDynamicToo]+ ++ (runGhcProg . buildOpts <$> Set.toList neededWays \\ [StaticWay, DynWay])+ -- Otherwise, we need to ensure the defaultGhcWay is built first+ | otherwise =+ runGhcProg . buildOpts <$> sortOn (\w -> if w == defaultGhcWay then 0 else fromEnum w + 1) (Set.toList neededWays)++ 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 dynDir vanillaDir+ _ -> return ()+ in+ -- REVIEW:ADD? info verbosity "Building Haskell Sources..."+ sequence_ orderedBuilds+ return buildOpts++data BuildWay = StaticWay | DynWay | ProfWay+ deriving (Eq, Ord, Show, 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_"++-- | Returns the corresponding 'Hpc.Way' for a 'BuildWay'+buildWayHpcWay :: BuildWay -> Hpc.Way+buildWayHpcWay = \case+ StaticWay -> Hpc.Vanilla+ ProfWay -> Hpc.Prof+ DynWay -> Hpc.Dyn++-- | 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++-- | Returns a pair of the Haskell input files and Haskell modules of the+-- component being built.+--+-- The "input files" are either the path to the main Haskell module, or a repl+-- script (that does not necessarily have an extension).+componentInputs+ :: FilePath+ -- ^ Target build dir+ -> PD.PackageDescription+ -> PreBuildComponentInputs+ -- ^ The context and component being built in it.+ -> IO ([FilePath], [ModuleName])+ -- ^ The Haskell input files, and the Haskell modules+componentInputs buildTargetDir pkg_descr pbci = do+ let+ verbosity = buildVerbosity pbci+ component = buildComponent pbci+ clbi = buildCLBI pbci++ case component of+ CLib lib ->+ pure ([], allLibModules lib clbi)+ CFLib flib ->+ pure ([], foreignLibModules flib)+ CExe Executable{buildInfo = bi', modulePath} ->+ exeLikeInputs verbosity bi' modulePath+ CTest TestSuite{testBuildInfo = bi', testInterface = TestSuiteExeV10 _ mainFile} ->+ exeLikeInputs verbosity bi' mainFile+ CBench Benchmark{benchmarkBuildInfo = bi', benchmarkInterface = BenchmarkExeV10 _ mainFile} ->+ exeLikeInputs verbosity bi' mainFile+ CTest TestSuite{} -> error "testSuiteExeV10AsExe: wrong kind"+ CBench Benchmark{} -> error "benchmarkExeV10asExe: wrong kind"+ where+ exeLikeInputs verbosity bnfo modulePath = liftIO $ do+ main <- findExecutableMain verbosity buildTargetDir (bnfo, modulePath)+ let mainModName = exeMainModuleName bnfo+ otherModNames = otherModules bnfo++ -- Scripts have fakePackageId and are always Haskell but can have any extension.+ if isHaskell 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 ([main], filter (/= mainModName) otherModNames)+ else return ([main], otherModNames)+ else return ([], otherModNames)
+ src/Distribution/Simple/GHC/Build/Utils.hs view
@@ -0,0 +1,217 @@+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.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+import Distribution.Utils.Path (getSymbolicPath)+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@).+findExecutableMain+ :: Verbosity+ -> FilePath+ -- ^ Build directory+ -> (BuildInfo, FilePath)+ -- ^ The build info and module path of an executable-like component (Exe, Test, Bench)+ -> IO FilePath+ -- ^ The path to the main source file.+findExecutableMain verbosity bdir (bnfo, modPath) =+ findFileEx verbosity (bdir : map getSymbolicPath (hsSourceDirs bnfo)) modPath++-- | Does this compiler support the @-dynamic-too@ option+supportsDynamicToo :: Compiler -> Bool+supportsDynamicToo = Internal.ghcLookupProperty "Support dynamic-too"++-- | Is this compiler's RTS dynamically linked?+isDynamic :: Compiler -> Bool+isDynamic = Internal.ghcLookupProperty "GHC Dynamic"++-- | 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 :: 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++-- | 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
@@ -4,39 +4,49 @@ {-# 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(..) )+ ( 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 =+ 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, Typeable) instance Exception ParseErrorExc @@ -45,5 +55,5 @@ readGhcEnvironmentFile :: FilePath -> IO [GhcEnvironmentFileEntry] 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,92 +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- , flagProfLate :: Bool -- ^ fprof-late flag- , 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]- , flagProfLate = v >= [9,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- , flagProfLate = 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
@@ -2,722 +2,878 @@ {-# 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 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 = \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 = 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,- ghcOptCcProgram = maybeToFlag $ programPath <$>- lookupProgram gccProgram (withPrograms lbi),- ghcOptObjDir = toFlag odir,- ghcOptExtra = hcOptions GHC bi- }---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,- ghcOptCcProgram = maybeToFlag $ programPath <$>- lookupProgram gccProgram (withPrograms lbi),- ghcOptObjDir = toFlag odir,- ghcOptExtra = hcOptions GHC bi- }---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- }--componentJsGhcOptions :: Verbosity -> GhcImplInfo -> LocalBuildInfo- -> BuildInfo -> ComponentLocalBuildInfo- -> FilePath -> FilePath- -> GhcOptions-componentJsGhcOptions 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,- 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.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 =- 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- 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 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"++-- |+-- 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 (NoFlag), maybeToFlag, toFlag)+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.ComponentId (ComponentId)+import Distribution.Types.ComponentLocalBuildInfo+import Distribution.Types.LocalBuildInfo+import Distribution.Types.TargetInfo+import Distribution.Types.UnitId+import Distribution.Utils.NubList (toNubListR)+import Distribution.Utils.Path+import Distribution.Verbosity+import Distribution.Version (Version)+import Language.Haskell.Extension+import System.Directory (getDirectoryContents, getTemporaryDirectory)+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+ 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 = 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+ | 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++componentCcGhcOptions+ :: Verbosity+ -> LocalBuildInfo+ -> BuildInfo+ -> ComponentLocalBuildInfo+ -> FilePath+ -> FilePath+ -> 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 =+ 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 (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+ -> FilePath+ -> FilePath+ -> 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 =+ 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 (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+ -> FilePath+ -> FilePath+ -> 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 =+ 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 (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+ }++componentJsGhcOptions+ :: Verbosity+ -> LocalBuildInfo+ -> BuildInfo+ -> ComponentLocalBuildInfo+ -> FilePath+ -> FilePath+ -> 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]+ , 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 (promisedPkgs lbi) clbi+ , ghcOptObjDir = toFlag odir+ }++componentGhcOptions+ :: Verbosity+ -> LocalBuildInfo+ -> BuildInfo+ -> ComponentLocalBuildInfo+ -> FilePath+ -> 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 $+ map getSymbolicPath (hsSourceDirs bi)+ ++ [odir]+ ++ [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+ , ghcOptHieDir = bool NoFlag (toFlag $ odir </> extraCompilationArtifacts </> "hie") $ flagHie implInfo+ , 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+ -> LocalBuildInfo+ -> BuildInfo+ -> ComponentLocalBuildInfo+ -> FilePath+ -> FilePath+ -> 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 =+ 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 (promisedPkgs lbi) 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+ ]++-- | 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) ComponentId+ -> 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 (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+ = -- | @-- 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 PackageDB+ | -- | @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+ :: 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"
src/Distribution/Simple/GHCJS.hs view
@@ -1,1841 +1,2034 @@-{-# 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.Types.PackageName.Magic-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- -> PackageId- -> CabalSpecVersion- -> FilePath- -> GBuildMode- -> IO BuildSources-gbuildSources verbosity 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 <- findFileEx verbosity (tmpDir : map getSymbolicPath (hsSourceDirs bnfo)) modPath- let mainModName = fromMaybe ModuleName.main $ exeMainModuleName exe- otherModNames = exeModules exe-- -- Scripts have fakePackageId and are always Haskell but can have any extension.- if isHaskell main || 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 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 -> 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 (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 (`replaceExtension` objExtension) cSrcs- cxxObjs = map (`replaceExtension` 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 inputFiles- else inputFiles,- ghcOptInputScripts = toNubListR $ if package pkg_descr == fakePackageId- then filter (not . isHaskell) 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 = 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 Wasi = 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- 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 = 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 verbose 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 CPP #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE TupleSections #-}++module Distribution.Simple.GHCJS+ ( getGhcInfo+ , configure+ , 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.Monad (msum)+import Data.Char (isLower)+import qualified Data.Map as Map+import System.Directory+ ( canonicalizePath+ , createDirectoryIfMissing+ , doesFileExist+ , getAppUserDataDirectory+ , removeFile+ , renameFile+ )+import System.FilePath+ ( isRelative+ , replaceExtension+ , takeDirectory+ , takeExtension+ , (<.>)+ , (</>)+ )+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) $+ 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++ 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 =+ dieWithException verbosity GlobalPackageDBLimitation+checkPackageDbStack verbosity _ =+ dieWithException verbosity GlobalPackageDBSpecifiedFirst++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+ -> 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)++ (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+ hpcdir way+ | forRepl = mempty -- HPC is not supported in ghci+ | isCoverageEnabled = toFlag $ Hpc.mixDir (libTargetDir </> extraCompilationArtifacts) way+ | 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+ (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 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 mempty 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 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 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 -> [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)+ 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 :: [FilePath]+ , cxxSourceFiles :: [FilePath]+ , inputSourceFiles :: [FilePath]+ , inputSourceModules :: [ModuleName]+ }++-- | Locate and return the 'BuildSources' required to build and link.+gbuildSources+ :: Verbosity+ -> PackageId+ -> CabalSpecVersion+ -> FilePath+ -> GBuildMode+ -> IO BuildSources+gbuildSources verbosity 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 <- findFileEx verbosity (tmpDir : map getSymbolicPath (hsSourceDirs bnfo)) modPath+ let mainModName = fromMaybe ModuleName.main $ exeMainModuleName exe+ otherModNames = exeModules exe++ -- Scripts have fakePackageId and are always Haskell but can have any extension.+ if isHaskell main || 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 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+ 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+ hpcdir way+ | gbuildIsRepl bm = mempty -- HPC is not supported in ghci+ | isCoverageEnabled = toFlag $ Hpc.mixDir (tmpDir </> extraCompilationArtifacts) way+ | otherwise = mempty++ rpaths <- getRPaths lbi clbi+ buildSources <- gbuildSources verbosity (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 (`replaceExtension` objExtension) cSrcs+ cxxObjs = map (`replaceExtension` 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 inputFiles+ else inputFiles+ , ghcOptInputScripts =+ toNubListR $+ if package pkg_descr == fakePackageId+ then filter (not . isHaskell) 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 = 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+ 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 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+ 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 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+ }+ 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)++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 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+ 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 = 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 ghcArgs+ return (takeWhile (not . isSpace) hash)++componentGhcOptions+ :: Verbosity+ -> LocalBuildInfo+ -> BuildInfo+ -> ComponentLocalBuildInfo+ -> FilePath+ -> 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+ 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 $ 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+ 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]) runCmd progdb exe = ( script , programPath ghcjsProg
src/Distribution/Simple/Glob.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE DeriveFunctor #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE LambdaCase #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Glob -- Copyright : Isaac Jones, Simon Marlow 2003-2004@@ -13,206 +14,53 @@ -- Portability : portable -- -- Simple file globbing.+module Distribution.Simple.Glob+ ( -- * Globs+ Glob -module Distribution.Simple.Glob (- GlobSyntaxError(..),- GlobResult(..),- matchDirFileGlob,- matchDirFileGlobWithDie,- runDirFileGlob,- fileGlobMatches,- parseFileGlob,- explainGlobSyntaxError,- isRecursiveInRoot,- Glob,- ) where+ -- * Matching on globs+ , GlobResult (..)+ , globMatches+ , fileGlobMatches+ , matchDirFileGlob+ , matchDirFileGlobWithDie+ , runDirFileGlob -import Prelude ()-import Distribution.Compat.Prelude+ -- * Parsing globs+ , parseFileGlob+ , GlobSyntaxError (..)+ , explainGlobSyntaxError -import Distribution.CabalSpecVersion-import Distribution.Simple.Utils-import Distribution.Verbosity+ -- * Utility+ , isRecursiveInRoot+ )+where -import System.Directory (getDirectoryContents, doesDirectoryExist, doesFileExist)-import System.FilePath (joinPath, splitExtensions, splitDirectories, takeFileName, (</>), (<.>))+import Distribution.Compat.Prelude+import Prelude () -import qualified Data.List.NonEmpty as NE+import Distribution.CabalSpecVersion (CabalSpecVersion)+import Distribution.Simple.Errors+ ( CabalException (MatchDirFileGlob, MatchDirFileGlobErrors)+ )+import Distribution.Simple.Glob.Internal+import Distribution.Simple.Utils (dieWithException, warn)+import Distribution.Verbosity (Verbosity) --- 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- = 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)+-- * Matching +--------------------------------------------------------------------------------+ -- | 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)--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."--data IsRecursive = Recursive | NonRecursive deriving Eq--data MultiDot = MultiDotDisabled | MultiDotEnabled--data Glob- = GlobStem FilePath Glob- -- ^ A single subdirectory component + remainder.- | GlobFinal GlobFinal--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 IsRecursive FilePath- -- ^ Literal file name.--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 Recursive path -> "**" </> path- FinalLit NonRecursive 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)--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 isRecursive filename -> do- guard ((isRecursive == Recursive || null segs) && filename == seg)- return (GlobMatch ())--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--parseFileGlob :: CabalSpecVersion -> FilePath -> Either GlobSyntaxError Glob-parseFileGlob version filepath = case reverse (splitDirectories filepath) of- [] ->- Left EmptyGlob- (filename : "**" : segments)- | allowGlobStar -> do- finalSegment <- case splitExtensions filename of- ("*", ext) | '*' `elem` ext -> Left StarInExtension- | null ext -> Left NoExtensionOnStar- | otherwise -> Right (FinalMatch Recursive multidot ext)- _ -> if allowLiteralFilenameGlobStar- then Right (FinalLit Recursive filename)- else Left LiteralFileNameGlobStar- foldM addStem (GlobFinal 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 NonRecursive filename)- foldM addStem (GlobFinal 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- allowLiteralFilenameGlobStar = version >= CabalSpecV3_8+globMatches input = [a | GlobMatch a <- input] -- | 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.@@ -227,118 +75,53 @@ -- prefix. -- -- The second 'FilePath' is the glob itself.--- matchDirFileGlob :: Verbosity -> CabalSpecVersion -> FilePath -> FilePath -> IO [FilePath]-matchDirFileGlob v = matchDirFileGlobWithDie v die'+matchDirFileGlob v = matchDirFileGlobWithDie v dieWithException -- | Like 'matchDirFileGlob' but with customizable 'die' -- -- @since 3.6.0.0----matchDirFileGlobWithDie :: Verbosity -> (Verbosity -> String -> IO [FilePath]) -> CabalSpecVersion -> FilePath -> FilePath -> IO [FilePath]+matchDirFileGlobWithDie+ :: Verbosity+ -> (Verbosity -> CabalException -> 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+ Left err -> rip verbosity $ MatchDirFileGlob (explainGlobSyntaxError filepath err) Right glob -> do- results <- runDirFileGlob verbosity dir glob+ results <- runDirFileGlob verbosity (Just version) dir glob let missingDirectories =- [ missingDir | GlobMissingDirectory missingDir <- results ]+ [missingDir | GlobMissingDirectory missingDir <- results] matches = globMatches results+ directoryMatches = [a | GlobMatchesDirectory a <- results] 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- ]-- if null errors- then return matches- else rip verbosity $ unlines errors---- | Match files against a pre-parsed glob, starting in a directory.------ The 'Version' argument must be the spec version of the package--- description being processed, as globs behave slightly differently--- in different spec versions.------ 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- -- 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) $- 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 ++ "'."- -- 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- -- extract the constant prefix from the pattern and start walking- -- 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- 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 Recursive fn -> do- let prefix = dir </> joinedPrefix- directoryExists <- doesDirectoryExist prefix- if directoryExists- then do- candidates <- getDirectoryContentsRecursive prefix- let checkName candidate- | takeFileName candidate == fn = Just $ GlobMatch (joinedPrefix </> candidate)- | otherwise = Nothing- return $ mapMaybe checkName candidates- else- return [ GlobMissingDirectory joinedPrefix ]-- FinalLit NonRecursive fn -> do- exists <- doesFileExist (dir </> joinedPrefix </> fn)- return [ GlobMatch (joinedPrefix </> fn) | exists ]--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)---- | 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- where- step (GlobStem seg pat) = Right (seg, pat)- step (GlobFinal pat) = Left pat+ [ "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 && null directoryMatches+ -- we don't error out on directory matches, simply warn about them and ignore.+ ] + warns :: [String]+ warns =+ [ "Ignoring directory '" ++ path ++ "'" ++ " listed in a Cabal package field which should only include files (not directories)."+ | path <- directoryMatches+ ] -isRecursiveInRoot :: Glob -> Bool-isRecursiveInRoot (GlobFinal (FinalMatch Recursive _ _)) = True-isRecursiveInRoot _ = False+ if null errors+ then do+ unless (null warns) $+ warn verbosity $+ unlines warns+ return matches+ else rip verbosity $ MatchDirFileGlobErrors errors
+ src/Distribution/Simple/Glob/Internal.hs view
@@ -0,0 +1,497 @@+{-# LANGUAGE DeriveFunctor #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE LambdaCase #-}++-----------------------------------------------------------------------------++-- |+-- 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 Control.Monad (mapM)++import Distribution.Parsec+import Distribution.Pretty++import Distribution.CabalSpecVersion+import Distribution.Simple.Utils+import Distribution.Verbosity hiding (normal)++import Data.List (stripPrefix)+import System.Directory+import System.FilePath++import qualified Distribution.Compat.CharParsing as P+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++-------------------------------------------------------------------------------++-- * Matching++--------------------------------------------------------------------------------++-- | 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++-------------------------------------------------------------------------------++-- * Parsing & printing++--------------------------------------------------------------------------------+-- 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 `**`).++-- ** Parsing globs in a cabal package++parseFileGlob :: CabalSpecVersion -> FilePath -> Either GlobSyntaxError Glob+parseFileGlob version filepath = case reverse (splitDirectories filepath) of+ [] ->+ Left EmptyGlob+ (filename : "**" : segments)+ | allowGlobStar -> do+ 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 (GlobFile [WildCard, Literal ext])+ (_, ext)+ | '*' `elem` ext -> Left StarInExtension+ | '*' `elem` filename -> Left StarInFileName+ | otherwise -> Right (GlobFile [Literal filename])++ foldM addStem pat segments+ where+ addStem pat seg+ | '*' `elem` seg = Left StarInDirectory+ | otherwise = Right (GlobDir [Literal seg] pat)+ allowGlob = version >= CabalSpecV1_6+ allowGlobStar = version >= CabalSpecV2_4+ allowLiteralFilenameGlobStar = version >= CabalSpecV3_8++enableMultidot :: CabalSpecVersion -> Bool+enableMultidot version+ | version >= CabalSpecV2_4 = True+ | otherwise = False++-- ** Parsing globs otherwise++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++--------------------------------------------------------------------------------+-- Parse and printing utils+--------------------------------------------------------------------------------++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++-- ** Cabal package globbing 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+-- description being processed, as globs behave slightly differently+-- in different spec versions.+--+-- 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+ -> 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 rawRoot) $+ warn verbosity $+ "Null dir passed to runDirFileGlob; interpreting it "+ ++ "as '.'. This is probably an internal error."+ let root = if null rawRoot then "." else rawRoot+ debug verbosity $ "Expanding glob '" ++ show (pretty pat) ++ "' in directory '" ++ root ++ "'."+ -- 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+ -- extract the constant prefix from the pattern and start walking+ -- 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, variablePattern) = 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]++ 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], Glob)+ splitConstantPrefix = unfoldr' step+ where+ step (GlobDir [Literal seg] pat') = Right (seg, pat')+ step pat' = Left pat'++ 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+ 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/Haddock.hs view
@@ -4,1039 +4,1238 @@ {-# 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, createHaddockIndex, hscolour,-- haddockPackagePaths,- Visibility(..)- ) 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.- 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.- argLib :: Flag String- -- ^ haddock's static \/ auxiliary files.-} 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- 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]) $- die' verbosity "Haddock 2.0 and 2.1 do not support the --hoogle flag."-- 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 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-- return (haddockProg, version)---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')-- libdirArgs <- getGhcLibDir verbosity lbi- let commonArgs = 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'.- when (flag haddockLinkedSource && version < mkVersion [2,17]) $- hscolour' (warn verbosity) haddockTarget pkg_descr lbi suffixes- (defaultHscolourFlags `mappend` haddockToHscolour flags)-- 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 True 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 True libArgs'-- 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 True 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)----- | Execute 'Haddock' configured with 'HaddocksFlags'. It is used to build--- index and contents for documentation of multiple packages.----createHaddockIndex :: Verbosity- -> ProgramDb- -> Compiler- -> Platform- -> HaddockProjectFlags- -> IO ()-createHaddockIndex verbosity programDb comp platform flags = do- let args = fromHaddockProjectFlags flags- (haddockProg, _version) <-- getHaddockProg verbosity programDb comp args (Flag True)- runHaddock verbosity defaultTempFileOptions 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,- argLib = haddockLib 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--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- , argLib = haddockProjectLib 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- -> Bool -- ^ require targets- -> HaddockArgs- -> IO ()-runHaddock verbosity 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 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- case argPrologue args of- Flag prologueText ->- withTempFileEx tmpFileOpts outputDir "haddock-prologue.txt" $- \prologueFileName h -> do- do- when haddockSupportsUTF8 (hSetEncoding h utf8)- hPutStrLn h prologueText- 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)- _ -> do- let renderedArgs = (case argPrologueFile args of- Flag pfile -> ["--prologue="++pfile]- _ -> [])- <> 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- | fromFlagOrDefault False (argGenIndex args) ->- "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="++ unDir (argOutputDir args) </> f)- . flagToList . argInterfaceFile $ args-- , if haddockSupportsPackageName- then maybe [] (\pkg -> [ "--package-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" | isVersion 2 17- , True <- flagToList . 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 ["--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="++) . unDir . argOutputDir $ args-- , maybe []- ( (:[])- . ("--title="++)- . (bool (++" (internal documentation)")- id (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 [] ((:[]) . ("--lib="++)) . flagToMaybe . argLib $ args- ]- where- 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 ]- ])-- 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"- haddockSupportsVisibility = version >= mkVersion [2,26,1]- haddockSupportsPackageName = version > mkVersion [2,16]--------------------------------------------------------------------------------------- | 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 -> 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 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- , 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 -> 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++-- |+-- 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+ , createHaddockIndex+ , hscolour+ , 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.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.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.Haddock+import Distribution.Simple.Setup.Hscolour+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.NubList+import qualified Distribution.Utils.ShortText as ShortText+import Distribution.Version++import Distribution.Verbosity+import Language.Haskell.Extension++import Distribution.Compat.Semigroup (All (..), Any (..))++import Control.Monad+import Data.Either (rights)++import Distribution.Simple.Errors+import System.Directory (doesDirectoryExist, doesFileExist, getCurrentDirectory)+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.+ , 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.+ , argLib :: Flag String+ -- ^ haddock's static \/ auxiliary files.+ }+ 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+ 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 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')++ 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'+ (warn verbosity)+ haddockTarget+ pkg_descr+ lbi+ suffixes+ (defaultHscolourFlags `mappend` haddockToHscolour flags)++ 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++ preBuildComponent verbosity lbi target++ 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+ True+ 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 True libArgs'++ 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 True 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)++-- | Execute 'Haddock' configured with 'HaddocksFlags'. It is used to build+-- index and contents for documentation of multiple packages.+createHaddockIndex+ :: Verbosity+ -> ProgramDb+ -> Compiler+ -> Platform+ -> HaddockProjectFlags+ -> IO ()+createHaddockIndex verbosity programDb comp platform flags = do+ let args = fromHaddockProjectFlags flags+ (haddockProg, _version) <-+ getHaddockProg verbosity programDb comp args (Flag True)+ runHaddock verbosity defaultTempFileOptions 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+ , argLib = haddockLib 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++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+ , argLib = haddockProjectLib 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 dieWithException verbosity MustHaveSharedLibraries++ 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+ -> Bool+ -- ^ require targets+ -> HaddockArgs+ -> IO ()+runHaddock verbosity 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 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+ case argPrologue args of+ Flag prologueText ->+ withTempFileEx tmpFileOpts outputDir "haddock-prologue.txt" $+ \prologueFileName h -> do+ do+ when haddockSupportsUTF8 (hSetEncoding h utf8)+ hPutStrLn h prologueText+ 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)+ _ -> do+ let renderedArgs =+ ( case argPrologueFile args of+ Flag pfile -> ["--prologue=" ++ pfile]+ _ -> []+ )+ <> 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)+ 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 ->+ 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=" ++ unDir (argOutputDir args) </> f)+ . flagToList+ . argInterfaceFile+ $ args+ , if haddockSupportsPackageName+ then+ maybe+ []+ ( \pkg ->+ [ "--package-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=" ++) . unDir . argOutputDir $ args+ , maybe+ []+ ( (: [])+ . ("--title=" ++)+ . ( bool+ (++ " (internal documentation)")+ id+ (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 [] ((: []) . ("--lib=" ++)) . flagToMaybe . argLib $ args+ ]+ where+ 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]+ ]+ )++ 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"+ 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 -> 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 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+ , 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' 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 (\excep -> onNoHsColour $ exceptionMessage excep) (\(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+ let tgt = TargetInfo clbi comp+ preBuildComponent 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+ </> 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
src/Distribution/Simple/HaskellSuite.hs view
@@ -3,39 +3,42 @@ module Distribution.Simple.HaskellSuite where -import Prelude () import Distribution.Compat.Prelude+import 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.Package import Distribution.PackageDescription+import Distribution.Parsec (simpleParsec)+import Distribution.Pretty+import Distribution.Simple.BuildPaths+import Distribution.Simple.Compiler+import Distribution.Simple.Errors import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.PackageIndex as PackageIndex+import Distribution.Simple.Program+import Distribution.Simple.Program.Builtin+import Distribution.Simple.Utils import Distribution.System (Platform) import Distribution.Utils.Path+import Distribution.Verbosity+import Distribution.Version import Language.Haskell.Extension-import Distribution.Simple.Program.Builtin configure- :: Verbosity -> Maybe FilePath -> Maybe FilePath- -> ProgramDb -> IO (Compiler, Maybe Platform, ProgramDb)+ :: 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+ in maybe (dieWithException verbosity $ ProvideHaskellSuiteTool msg) return mbHcPath when (isJust hcPkgPath) $ warn verbosity "--with-hc-pkg option is ignored for haskell-suite"@@ -54,13 +57,13 @@ progdb1 return (comp, Nothing, progdb2)- where configureCompiler hcPath progdb0' = do let haskellSuiteProgram' = haskellSuiteProgram- { programFindLocation = \v p -> findProgramOnSearchPath v p hcPath }+ { 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.@@ -69,19 +72,20 @@ (confdCompiler, progdb2) <- requireProgram verbosity haskellSuiteProgram' progdb1 extensions <- getExtensions verbosity confdCompiler- languages <- getLanguages 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- }+ comp =+ Compiler+ { compilerId = CompilerId (HaskellSuite compName) compVersion+ , compilerAbiTag = NoAbiTag+ , compilerCompat = []+ , compilerLanguages = languages+ , compilerExtensions = extensions+ , compilerProperties = mempty+ } return (comp, confdCompiler, progdb2) @@ -99,58 +103,73 @@ 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+ maybe+ (dieWithException verbosity CannotDetermineCompilerVersion)+ 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"]+ lines+ `fmap` rawSystemStdout verbosity (programPath prog) ["--supported-extensions"] return- [ (ext, Just $ "-X" ++ prettyShow ext) | Just ext <- map simpleParsec extStrs ]+ [(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"]+ lines+ `fmap` rawSystemStdout verbosity (programPath prog) ["--supported-languages"] return- [ (ext, "-G" ++ prettyShow ext) | Just ext <- map simpleParsec langStrs ]+ [(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+ -> 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'"+ do+ str <-+ getDbProgramOutput+ verbosity+ haskellSuitePkgProgram+ progdb+ ["dump", packageDbOpt packagedb]+ `catchExit` \_ -> dieWithException verbosity PkgDumpFailed + case parsePackages str of+ Right ok -> return ok+ _ -> dieWithException verbosity FailedToParseOutput where parsePackages str =- case partitionEithers $ map (parseInstalledPackageInfo . toUTF8BS) (splitPkgs str) of- ([], ok) -> Right [ pkg | (_, pkg) <- ok ]- (msgss, _) -> Left (foldMap NE.toList msgss)+ 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+ splitWith p xs =+ ys : case zs of+ [] -> []+ _ : ws -> splitWith p ws+ where+ (ys, zs) = break p xs buildLib- :: Verbosity -> PackageDescription -> LocalBuildInfo- -> Library -> ComponentLocalBuildInfo -> IO ()+ :: 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,@@ -167,28 +186,37 @@ 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 ]--+ ["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+ -> FilePath+ -- ^ install location+ -> FilePath+ -- ^ install location for dynamic libraries+ -> FilePath+ -- ^ Build location -> PackageDescription -> Library -> ComponentLocalBuildInfo@@ -197,11 +225,16 @@ 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)+ , "--build-dir"+ , builtDir+ , "--target-dir"+ , targetDir+ , "--dynlib-target-dir"+ , dynlibTargetDir+ , "--package-id"+ , prettyShow $ packageId pkg+ ]+ ++ map prettyShow (allLibModules lib clbi) registerPackage :: Verbosity@@ -213,16 +246,22 @@ (hspkg, _) <- requireProgram verbosity haskellSuitePkgProgram progdb runProgramInvocation verbosity $- (programInvocation hspkg- ["update", packageDbOpt $ registrationPackageDB packageDbs])- { progInvokeInput = Just $ IODataText $ showInstalledPackageInfo installedPkgInfo }+ ( programInvocation+ hspkg+ ["update", packageDbOpt $ registrationPackageDB packageDbs]+ )+ { progInvokeInput = Just $ IODataText $ showInstalledPackageInfo installedPkgInfo+ } initPackageDB :: Verbosity -> ProgramDb -> FilePath -> IO () initPackageDB verbosity progdb dbPath =- runDbProgram verbosity haskellSuitePkgProgram progdb+ runDbProgram+ verbosity+ haskellSuitePkgProgram+ progdb ["init", dbPath] packageDbOpt :: PackageDB -> String-packageDbOpt GlobalPackageDB = "--global"-packageDbOpt UserPackageDB = "--user"+packageDbOpt GlobalPackageDB = "--global"+packageDbOpt UserPackageDB = "--user" packageDbOpt (SpecificPackageDB db) = "--package-db=" ++ db
src/Distribution/Simple/Hpc.hs view
@@ -1,7 +1,9 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Hpc -- Copyright : Thomas Tuegel 2011@@ -13,39 +15,38 @@ -- 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 (..)) 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 )+ ( hpcProgram+ , requireProgramVersion+ )+import Distribution.Simple.Program.Hpc (markup, union)+import Distribution.Simple.Utils (notice)+import Distribution.Types.UnqualComponentName+import Distribution.Verbosity (Verbosity ())+import Distribution.Version (anyVersion)+import System.Directory (createDirectoryIfMissing, doesFileExist) import System.FilePath -- -------------------------------------------------------------------------@@ -54,9 +55,12 @@ data Way = Vanilla | Prof | Dyn deriving (Bounded, Enum, Eq, Read, Show) -hpcDir :: FilePath -- ^ \"dist/\" prefix- -> Way- -> FilePath -- ^ Directory containing component's HPC .mix files+hpcDir+ :: FilePath+ -- ^ \"dist/\" prefix+ -> Way+ -> FilePath+ -- ^ Directory containing component's HPC .mix files hpcDir distPref way = distPref </> "hpc" </> wayDir where wayDir = case way of@@ -64,52 +68,40 @@ Prof -> "prof" Dyn -> "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+ :: FilePath+ -- ^ \"dist/\" prefix+ -> Way+ -> FilePath+ -- ^ Directory containing test suite's .mix files+mixDir distPref way = hpcDir distPref way </> "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+ :: FilePath+ -- ^ \"dist/\" prefix+ -> Way+ -> FilePath+ -- ^ Directory containing test suite's .tix files+tixDir distPref way = hpcDir distPref way </> "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+ :: FilePath+ -- ^ \"dist/\" prefix+ -> Way+ -> FilePath+ -- ^ Component name+ -> FilePath+ -- ^ Path to test suite's .tix file+tixFilePath distPref way name = tixDir distPref way </> 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+ :: FilePath+ -- ^ \"dist/\" prefix+ -> Way+ -> FilePath+ -- ^ Path to test suite's HTML markup directory+htmlDir distPref way = hpcDir distPref way </> "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 +111,70 @@ | 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 :: [FilePath]+ -- ^ 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+ -> FilePath+ -- ^ 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+ 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 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 $ takeDirectory summedTixFile+ union hpc verbosity tixFiles summedTixFile excluded+ return summedTixFile++ markup hpc hpcVer verbosity tixFile mixDirs htmlDir' libsModulesToInclude+ notice verbosity $+ "Package coverage report written to "+ ++ 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) pathsToLibsArtifacts
src/Distribution/Simple/Install.hs view
@@ -2,6 +2,7 @@ {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Install -- Copyright : Isaac Jones 2003-2004@@ -14,62 +15,87 @@ -- \"@.\/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 ) where -import Prelude () import Distribution.Compat.Prelude+import Prelude () -import Distribution.Types.TargetInfo-import Distribution.Types.LocalBuildInfo+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.BuildTarget+import Distribution.Simple.Compiler+ ( CompilerFlavor (..)+ , compilerFlavor+ )+import Distribution.Simple.Flag+ ( fromFlag+ ) import Distribution.Simple.Glob (matchDirFileGlob)+import Distribution.Simple.LocalBuildInfo+import Distribution.Simple.Setup.Copy+ ( CopyFlags (..)+ )+import Distribution.Simple.Setup.Haddock+ ( HaddockTarget (ForDevelopment)+ ) 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+ ( createDirectoryIfMissingVerbose+ , dieWithException+ , info+ , installDirectoryContents+ , installOrdinaryFile+ , isInSearchPath+ , noticeNoWrap+ , warn+ ) import Distribution.Utils.Path (getSymbolicPath) -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 qualified Distribution.Simple.UHC as UHC import System.Directory- ( doesDirectoryExist, doesFileExist )+ ( doesDirectoryExist+ , doesFileExist+ ) import System.FilePath- ( takeFileName, takeDirectory, (</>), isRelative )+ ( 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.--install :: PackageDescription -- ^information from the .cabal file- -> LocalBuildInfo -- ^information from the configure step- -> CopyFlags -- ^flags sent to copy or install- -> IO ()+-- 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 pkg_descr lbi flags = do checkHasLibsOrExes targets <- readTargetInfos verbosity pkg_descr lbi (copyArgs flags)@@ -80,27 +106,33 @@ 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)+ in copyComponent verbosity pkg_descr lbi comp clbi copydest+ where+ distPref = fromFlag (copyDistPref flags)+ verbosity = fromFlag (copyVerbosity flags)+ 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) $+ dieWithException verbosity NoLibraryFound -- | Copy package global files.-copyPackage :: Verbosity -> PackageDescription- -> LocalBuildInfo -> FilePath -> CopyDest -> IO ()+copyPackage+ :: Verbosity+ -> PackageDescription+ -> LocalBuildInfo+ -> FilePath+ -> 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 -- Install (package-global) data files@@ -109,30 +141,40 @@ -- 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)+ info+ verbosity+ ( "directory "+ ++ 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+ (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 let lfiles = licenseFiles pkg_descr unless (null lfiles) $ do@@ -143,118 +185,136 @@ installOrdinaryFile verbosity lfile (docPref </> takeFileName 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 = 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+ HaskellSuite _ ->+ HaskellSuite.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 = 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 = 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 ()+ _ ->+ 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+ flip traverse_ (dataFiles pkg_descr) $ \glob -> do let srcDataDirRaw = dataDir pkg_descr- srcDataDir = if null srcDataDirRaw- then "."- else srcDataDirRaw+ srcDataDir =+ if null srcDataDirRaw+ then "."+ else srcDataDirRaw files <- matchDirFileGlob verbosity (specVersion pkg_descr) srcDataDir glob- for_ files $ \ file' -> do+ for_ files $ \file' -> do let src = srcDataDir </> file' dst = destDataDir </> 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 = "." : 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+ ] 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' = fromMaybe "" (takeDirectory <$> cabalFilePath 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
@@ -6,6 +6,7 @@ {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.InstallDirs -- Copyright : Isaac Jones 2003-2004@@ -22,48 +23,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 +76,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, Typeable) instance Binary dir => Binary (InstallDirs dir) instance Structured dir => Structured (InstallDirs dir)@@ -111,38 +113,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 +170,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 +178,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 +264,111 @@ -- 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) 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,17 +376,16 @@ -- 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) @@ -362,15 +395,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 +412,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 +499,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 +517,7 @@ let m = Nothing #endif return (fromMaybe "C:\\Program Files" m)+{- FOURMOLU_ENABLE -} #ifdef mingw32_HOST_OS shGetFolderPath :: CInt -> IO (Maybe FilePath)@@ -490,6 +535,7 @@ -- csidl_PROGRAM_FILES_COMMON :: CInt -- csidl_PROGRAM_FILES_COMMON = 0x002b +{- FOURMOLU_DISABLE -} #ifdef x86_64_HOST_ARCH #define CALLCONV ccall #else@@ -504,3 +550,4 @@ -> CWString -> Prelude.IO CInt #endif+{- FOURMOLU_ENABLE -}
src/Distribution/Simple/InstallDirs/Internal.hs view
@@ -1,127 +1,160 @@ {-# 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, Typeable) 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+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, Typeable) 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,11 @@ {-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.LocalBuildInfo -- Copyright : Isaac Jones 2003-2004@@ -18,81 +21,89 @@ -- 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+ , cabalFilePath+ , progPrefix+ , progSuffix - 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.InstallDirs hiding+ ( absoluteInstallDirs+ , prefixRelativeInstallDirs+ , substPathTemplate+ )+import qualified Distribution.Simple.InstallDirs as InstallDirs import Distribution.Simple.PackageIndex import Distribution.Simple.Utils-import Distribution.Pretty-import qualified Distribution.Compat.Graph as Graph import Data.List (stripPrefix) import System.FilePath -import System.Directory (doesDirectoryExist, canonicalizePath)+import System.Directory (canonicalizePath, doesDirectoryExist) -- ----------------------------------------------------------------------------- -- Configuration information of buildable components@@ -101,83 +112,104 @@ -- 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+ </> 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 -- | 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 +218,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 = 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 +284,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 <- doesDirectoryExist p+ if exists+ then 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 +341,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 +403,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
@@ -1,4 +1,5 @@ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.PackageDescription -- Copyright : Isaac Jones 2003-2005@@ -8,33 +9,37 @@ -- Portability : portable -- -- This defines parsers for the @.cabal@ format--module Distribution.Simple.PackageDescription (- -- * Read and Parse files- readGenericPackageDescription,- readHookedBuildInfo,+module Distribution.Simple.PackageDescription+ ( -- * Read and Parse files+ readGenericPackageDescription+ , readHookedBuildInfo -- * Utility Parsing function- parseString,- ) where+ , parseString+ ) where -import Prelude () 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 )+ ( parseGenericPackageDescription+ , parseHookedBuildInfo+ )+import Distribution.Parsec.Error (showPError) import Distribution.Parsec.Warning- ( PWarning(..), PWarnType(PWTExperimental), showPWarning )-import Distribution.Simple.Utils ( equating, die', warn )-import Distribution.Verbosity ( normal, Verbosity )--import Data.List ( groupBy )-import Text.Printf ( printf )-import qualified Data.ByteString as BS+ ( PWarnType (PWTExperimental)+ , PWarning (..)+ , showPWarning+ )+import Distribution.Simple.Errors+import Distribution.Simple.Utils (dieWithException, equating, warn)+import Distribution.Verbosity (Verbosity, normal) import System.Directory (doesFileExist)+import Text.Printf (printf) readGenericPackageDescription :: Verbosity -> FilePath -> IO GenericPackageDescription readGenericPackageDescription = readAndParseFile parseGenericPackageDescription@@ -49,55 +54,63 @@ -- -- 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+ :: (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+ exists <- doesFileExist fpath+ unless exists $+ dieWithException verbosity $+ ErrorParsingFileDoesntExist fpath+ 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+ :: (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- die' verbosity $ "Failed parsing \"" ++ name ++ "\"."+ 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+ | 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+ 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))- ]+ -- 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
@@ -4,6 +4,7 @@ {-# LANGUAGE FlexibleInstances #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.PackageIndex -- Copyright : (c) David Himmelstrup 2005,@@ -43,80 +44,77 @@ -- '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,-- -- * Updates- merge,+module Distribution.Simple.PackageIndex+ ( -- * Package index data type+ InstalledPackageIndex+ , PackageIndex - insert,+ -- * Creating an index+ , fromList - deleteUnitId,- deleteSourcePackageId,- deletePackageName,--- deleteDependency,+ -- * Updates+ , merge+ , insert+ , deleteUnitId+ , deleteSourcePackageId+ , deletePackageName+ -- deleteDependency, - -- * Queries+ -- * Queries - -- ** Precise lookups- lookupUnitId,- lookupComponentId,- lookupSourcePackageId,- lookupPackageId,- lookupPackageName,- 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 ) 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 +124,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, Typeable) instance Binary a => Binary (PackageIndex a) instance Structured a => Structured (PackageIndex a)@@ -156,9 +152,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 +167,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 +256,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 +370,12 @@ -} --+ -- * Bulk queries+ -- -- | Get all the packages from the index.--- allPackages :: PackageIndex a -> [a] allPackages = Map.elems . unitIdIndex @@ -362,91 +384,99 @@ -- 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-+ Nothing -> []+ Just pvers -> Map.toList pvers -- | Does a lookup by source package name and a range of versions. --@@ -457,12 +487,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 +502,34 @@ -- 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)-+ 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) --+ -- * Case insensitive name lookups+ -- -- | Does a case-insensitive search by package name.@@ -507,42 +543,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 +593,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 +625,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 +724,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 +765,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
@@ -2,840 +2,921 @@ {-# 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,- unsorted- )- 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.Types.PackageName.Magic-import Distribution.Pretty-import Distribution.Version-import Distribution.Verbosity-import Distribution.Utils.Path--import System.Directory (doesFileExist, doesDirectoryExist)-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.-- -- | 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- ppOrdering :: Verbosity- -> [FilePath] -- Source directories- -> [ModuleName] -- Module names- -> IO [ModuleName], -- Sorted modules-- 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- }---- | 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- -> [FilePath]- -> [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 = 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 =- -- 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 = map getSymbolicPath (hsSourceDirs bi) ++- [ autogenComponentModulesDir lbi clbi ,autogenPackageModulesDir lbi]- let hndlrs = localHandlers bi- mods <- orderingFromHandlers verbosity dirs hndlrs (allLibModules lib clbi)- for_ (map ModuleName.toFilePath mods) $- pre dirs (componentBuildDir lbi clbi) hndlrs- (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]- let hndlrs = localHandlers bi- mods <- orderingFromHandlers verbosity dirs hndlrs (foreignLibModules flib)- for_ (map ModuleName.toFilePath mods) $- pre dirs flibDir hndlrs- (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]- let hndlrs = localHandlers bi- mods <- orderingFromHandlers verbosity dirs hndlrs (otherModules bi)- for_ (map ModuleName.toFilePath mods) $- pre dirs exeDir hndlrs- 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- orderingFromHandlers v d hndlrs mods =- foldM (\acc (_,pp) -> ppOrdering pp v d acc) mods hndlrs- 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 True- 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 False- | modu <- modules ]- -- XXX: what we do here (re SymbolicPath dir)- -- XXX: 2020-10-15 do we rely here on CWD being the PackageDir?- -- 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 (unsafeMakeSymbolicPath dir : hsSourceDirs bi) dir isSrcDist- (dropExtensions $ 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- :: [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- -> Bool -- ^fail on missing file- -> IO ()-preprocessFile 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 <- 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, failOnMissing) of- (Nothing, True) ->- 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,- ppOrdering = unsorted,- 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,- ppOrdering = unsorted,- 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,- ppOrdering = unsorted,- 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,- ppOrdering = unsorted,- 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,- ppOrdering = unsorted,- 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 <-- if withFullyStaticExe lbi- then PD.extraLibDirsStatic bi- else PD.extraLibDirs bi- ]- ++ [ "--lflag=-Wl,-R," ++ 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 ]- ++ [ 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 <-- if withFullyStaticExe lbi- then Installed.extraLibrariesStatic pkg- else 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,- 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)- 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"]- Wasi -> ["wasi"]- 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"]- S390X -> ["s390x"]- Alpha -> ["alpha"]- Hppa -> ["hppa"]- Rs6000 -> ["rs6000"]- M68k -> ["m68k"]- Vax -> ["vax"]- 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 ->- 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 = do- b <- doesDirectoryExist dir- if b- then (map (dir </>) . filter not_sub . concat)- <$> for knownExtrasHandlers- (withLexicalCallStack (\f -> f dir))- 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)++-- |+-- 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+ , knownSuffixHandlers+ , ppSuffixes+ , PPSuffixHandler+ , Suffix (..)+ , builtinHaskellSuffixes+ , builtinHaskellBootSuffixes+ , PreProcessor (..)+ , mkSimplePreProcessor+ , runSimplePreProcessor+ , ppCpp+ , ppCpp'+ , ppGreenCard+ , 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 qualified Distribution.ModuleName as 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+ ( dropExtensions+ , 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+ -> [FilePath]+ -> [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 = 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 '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 =+ map getSymbolicPath (hsSourceDirs bi)+ ++ [autogenComponentModulesDir lbi clbi, autogenPackageModulesDir lbi]+ let hndlrs = localHandlers bi+ mods <- orderingFromHandlers verbosity dirs hndlrs (allLibModules lib clbi)+ for_ (map ModuleName.toFilePath mods) $+ pre dirs (componentBuildDir lbi clbi) hndlrs+ (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+ ]+ let hndlrs = localHandlers bi+ mods <- orderingFromHandlers verbosity dirs hndlrs (foreignLibModules flib)+ for_ (map ModuleName.toFilePath mods) $+ pre dirs flibDir hndlrs+ (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+ ]+ let hndlrs = localHandlers bi+ mods <- orderingFromHandlers verbosity dirs hndlrs (otherModules bi)+ for_ (map ModuleName.toFilePath mods) $+ pre dirs exeDir hndlrs+ 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 ->+ dieWithException verbosity $ NoSupportForPreProcessingTest 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 ->+ 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]+ pre dirs dir lhndlrs fp =+ preprocessFile (map unsafeMakeSymbolicPath 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]+ -> 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+ False+ | modu <- modules+ ]+ -- XXX: what we do here (re SymbolicPath dir)+ -- XXX: 2020-10-15 do we rely here on CWD being the PackageDir?+ -- 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+ (unsafeMakeSymbolicPath dir : hsSourceDirs bi)+ dir+ isSrcDist+ (dropExtensions $ 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+ :: [SymbolicPath PackageDir SourceDir]+ -- ^ source directories+ -> FilePath+ -- ^ build directory+ -> Bool+ -- ^ preprocess for sdist+ -> FilePath+ -- ^ module file name+ -> Verbosity+ -- ^ verbosity+ -> [Suffix]+ -- ^ builtin suffixes+ -> [(Suffix, PreProcessor)]+ -- ^ possible preprocessors+ -> Bool+ -- ^ fail on missing file+ -> IO ()+preprocessFile 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 <- 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, failOnMissing) of+ (Nothing, True) ->+ dieWithException verbosity $+ CantFindSourceForPreProcessFile $+ "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 (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 <- 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+ , ppOrdering = unsorted+ , 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+ , 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)+ 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+ , ppOrdering = unsorted+ , 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+ , ppOrdering = unsorted+ , 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 <-+ if withFullyStaticExe lbi+ then PD.extraLibDirsStatic bi+ else PD.extraLibDirs bi+ ]+ ++ [ "--lflag=-Wl,-R," ++ 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 buildBaseDir =+ filter ("_hsc.c" `isSuffixOf`)+ `fmap` getDirectoryContentsRecursive buildBaseDir++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)+ 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"]+ 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 ->+ runDbProgram+ verbosity+ prog+ (withPrograms lbi)+ (args ++ ["-o", outFile, inFile])+ }++-- | Convenience function; get the suffixes of these preprocessors.+ppSuffixes :: [PPSuffixHandler] -> [Suffix]+ppSuffixes = map fst++-- | Standard preprocessors: GreenCard, c2hs, hsc2hs, happy, alex and cpphs.+knownSuffixHandlers :: [PPSuffixHandler]+knownSuffixHandlers =+ [ (Suffix "gc", ppGreenCard)+ , (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 [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 ->+ dieWithException verbosity $ NoSupportPreProcessingTestExtras tt+ CBench bm -> do+ let nm' = unUnqualComponentName $ benchmarkName bm+ case benchmarkInterface bm of+ BenchmarkExeV10 _ _ ->+ pp $ buildDir lbi </> nm' </> nm' ++ "-tmp"+ BenchmarkUnsupported tt ->+ dieWithException verbosity $ NoSupportPreProcessingBenchmarkExtras tt+ where+ pp :: FilePath -> IO [FilePath]+ pp dir = do+ b <- doesDirectoryExist dir+ if b+ then+ (map (dir </>) . filter not_sub . concat)+ <$> for+ knownExtrasHandlers+ (withLexicalCallStack (\f -> f dir))+ 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,114 @@+{-# 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 (..)+ , builtinHaskellSuffixes+ , builtinHaskellBootSuffixes+ )+where++import Distribution.Compat.Prelude+import Prelude ()++import Distribution.ModuleName (ModuleName)+import Distribution.Pretty+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,+-- > 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.++ ppOrdering+ :: Verbosity+ -> [FilePath] -- 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+ :: (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+ }++-- | 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
@@ -2,6 +2,7 @@ {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program -- Copyright : Isaac Jones 2006, Duncan Coutts 2007-2009@@ -34,150 +35,158 @@ -- 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+ , 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+ , getDbProgramOutput -- * 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+ , hmakeProgram+ , jhcProgram+ , uhcProgram+ , gccProgram+ , arProgram+ , stripProgram+ , happyProgram+ , alexProgram+ , hsc2hsProgram+ , c2hsProgram+ , cpphsProgram+ , hscolourProgram+ , doctestProgram+ , haddockProgram+ , greencardProgram+ , 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.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 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+ -- ^ verbosity+ -> Program+ -- ^ The program to run+ -> ProgramDb+ -- ^ look up the program here+ -> [ProgArg]+ -- ^ Any /extra/ arguments to add+ -> IO () runDbProgram verbosity prog programDb args = case lookupProgram prog programDb of- Nothing -> die' verbosity notFound+ Nothing ->+ dieWithException verbosity $ ProgramNotFound (programName prog) Just configuredProg -> runProgram verbosity configuredProg args- where- notFound = "The program '" ++ programName prog- ++ "' is required but it could not be found" -- | 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+ -- ^ 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 = case lookupProgram prog programDb of- Nothing -> die' verbosity notFound+ Nothing -> dieWithException verbosity $ ProgramNotFound (programName prog) Just configuredProg -> getProgramOutput verbosity configuredProg args- where- notFound = "The program '" ++ programName prog- ++ "' is required but it could not be found"
src/Distribution/Simple/Program/Ar.hs view
@@ -1,9 +1,10 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NondecreasingIndentation #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-}-{-# LANGUAGE NondecreasingIndentation #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Ar -- Copyright : Duncan Coutts 2009@@ -12,125 +13,155 @@ -- 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, arDashLSupported)-import Distribution.Simple.LocalBuildInfo (LocalBuildInfo(..))+import Distribution.Simple.Compiler (arDashLSupported, arResponseFilesSupported)+import Distribution.Simple.Flag+ ( fromFlagOrDefault+ )+import Distribution.Simple.LocalBuildInfo (LocalBuildInfo (..)) 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+ , programInvocation+ , runProgramInvocation+ )+import Distribution.Simple.Setup.Config+ ( configUseResponseFiles+ ) import Distribution.Simple.Utils- ( defaultTempFileOptions, dieWithLocation', withTempDirectory )+ ( defaultTempFileOptions+ , dieWithLocation'+ , withTempDirectory+ ) import Distribution.System- ( Arch(..), OS(..), Platform(..) )+ ( Arch (..)+ , OS (..)+ , Platform (..)+ ) 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+ -> FilePath+ -> [FilePath]+ -> 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+ withTempDirectory verbosity targetDir "objs" $ \tmpDir -> do+ let tmpPath = tmpDir </> targetName - -- 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.+ -- 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. - let simpleArgs = case hostOS of- OSX -> ["-r", "-s"]- _ | dashLSupported -> ["-qL"]- _ -> ["-r"]+ let simpleArgs = case hostOS of+ OSX -> ["-r", "-s"]+ _ | dashLSupported -> ["-qL"]+ _ -> ["-r"] - initialArgs = ["-q"]- finalArgs = case hostOS of- OSX -> ["-q", "-s"]- _ | dashLSupported -> ["-qL"]- _ -> ["-q"]+ initialArgs = ["-q"]+ finalArgs = case hostOS of+ OSX -> ["-q", "-s"]+ _ | dashLSupported -> ["-qL"]+ _ -> ["-q"] - extraArgs = verbosityOpts verbosity ++ [tmpPath]+ extraArgs = verbosityOpts verbosity ++ [tmpPath] - simple = programInvocation ar (simpleArgs ++ extraArgs)- initial = programInvocation ar (initialArgs ++ extraArgs)- middle = initial- final = programInvocation ar (finalArgs ++ extraArgs)+ simple = programInvocation ar (simpleArgs ++ extraArgs)+ initial = programInvocation ar (initialArgs ++ extraArgs)+ middle = initial+ final = programInvocation ar (finalArgs ++ extraArgs) - oldVersionManualOverride =- fromFlagOrDefault False $ configUseResponseFiles $ configFlags lbi- responseArgumentsNotSupported =- not (arResponseFilesSupported (compiler lbi))- dashLSupported =- arDashLSupported (compiler lbi)+ oldVersionManualOverride =+ fromFlagOrDefault False $ configUseResponseFiles $ configFlags lbi+ responseArgumentsNotSupported =+ not (arResponseFilesSupported (compiler lbi))+ dashLSupported =+ arDashLSupported (compiler lbi) - invokeWithResponesFile :: FilePath -> ProgramInvocation- invokeWithResponesFile atFile =- programInvocation ar $- simpleArgs ++ extraArgs ++ ['@' : atFile]+ invokeWithResponesFile :: FilePath -> ProgramInvocation+ invokeWithResponesFile atFile =+ programInvocation ar $+ simpleArgs ++ extraArgs ++ ['@' : atFile] - if oldVersionManualOverride || responseArgumentsNotSupported- then- sequence_- [ runProgramInvocation verbosity inv- | inv <- multiStageProgramInvocation- simple (initial, middle, final) files ]- else- withResponseFile verbosity defaultTempFileOptions tmpDir "ar.rsp" Nothing files $+ 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 - 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-+ unless+ ( hostArch == Arm -- See #1537+ || hostOS == AIX+ )+ $ wipeMetadata verbosity tmpPath -- AIX uses its own "ar" format variant+ equal <- filesEqual tmpPath targetPath+ unless equal $ renameFile tmpPath 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@@ -140,17 +171,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@@ -162,40 +194,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,45 @@ -- 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+ , hmakeProgram+ , jhcProgram+ , haskellSuiteProgram+ , haskellSuitePkgProgram+ , uhcProgram+ , gccProgram+ , arProgram+ , stripProgram+ , happyProgram+ , alexProgram+ , hsc2hsProgram+ , c2hsProgram+ , cpphsProgram+ , hscolourProgram+ , doctestProgram+ , haddockProgram+ , greencardProgram+ , 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,137 +59,153 @@ 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+ , 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+ ] ghcProgram :: Program-ghcProgram = (simpleProgram "ghc") {- programFindVersion = findProgramVersion "--numeric-version" id,-- -- 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),-- programNormaliseArgs = normaliseGhcArgs- }+ghcProgram =+ (simpleProgram "ghc")+ { programFindVersion = findProgramVersion "--numeric-version" id+ , -- 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)+ , programNormaliseArgs = normaliseGhcArgs+ } 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- _ -> ""- }+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- _ -> ""- }+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+ _ -> ""+ } 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@@ -210,118 +225,130 @@ -- 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", [])- }+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", [])- }-+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+ } arProgram :: Program arProgram = simpleProgram "ar" stripProgram :: Program-stripProgram = (simpleProgram "strip") {- programFindVersion = findProgramVersion "--version" stripExtractVersion . lessVerbose- }+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- }+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" 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.@@ -361,19 +388,22 @@ } 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"
src/Distribution/Simple/Program/Db.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Db -- Copyright : Isaac Jones 2006, Duncan Coutts 2007-2009@@ -23,64 +24,64 @@ -- 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,- appendProgramSearchPath,- lookupKnownProgram,- knownPrograms,- getProgramSearchPath,- setProgramSearchPath,- modifyProgramSearchPath,- userSpecifyPath,- userSpecifyPaths,- userMaybeSpecifyPath,- userSpecifyArgs,- userSpecifyArgss,- userSpecifiedArgs,- lookupProgram,- lookupProgramByName,- updateProgram,- configuredPrograms,+ , addKnownProgram+ , addKnownPrograms+ , prependProgramSearchPath+ , 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+ , configureAllKnownPrograms+ , unconfigureProgram+ , lookupProgramVersion+ , reconfigurePrograms+ , requireProgram+ , requireProgramVersion+ , needProgram ) 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@@ -91,17 +92,16 @@ -- 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- }+data ProgramDb = ProgramDb+ { unconfiguredProgs :: UnconfiguredProgs+ , progSearchPath :: ProgramSearchPath+ , configuredProgs :: ConfiguredProgs+ } deriving (Typeable) 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@@ -109,38 +109,38 @@ 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)@@ -148,18 +148,22 @@ get = do searchpath <- get- progs <- get- return $! emptyProgramDb {- progSearchPath = searchpath,- configuredProgs = progs- }+ progs <- get+ return $!+ emptyProgramDb+ { progSearchPath = searchpath+ , 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 ConfiguredProgs)+ ] -- | The 'Read'\/'Show' and 'Binary' instances do not preserve all the -- unconfigured 'Programs' because 'Program' is not in 'Read'\/'Show' because@@ -168,142 +172,144 @@ -- -- * 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 -- | Modify the current 'ProgramSearchPath' used by the 'ProgramDb'--- by appending the provided extra paths. Also logs the added paths+-- by prepending the provided extra paths. Also logs the added paths -- in info verbosity.-appendProgramSearchPath+prependProgramSearchPath :: Verbosity -> [FilePath] -> ProgramDb -> IO ProgramDb-appendProgramSearchPath verbosity extraPaths db =+prependProgramSearchPath verbosity extraPaths db = if not $ null extraPaths then do logExtraProgramSearchPath verbosity extraPaths pure $ modifyProgramSearchPath (map ProgramSearchPathDir extraPaths ++) db else pure 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.----userSpecifyPath :: String -- ^Program name- -> FilePath -- ^user-specified path to the program- -> ProgramDb -> ProgramDb+-- | 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)-+ flip Map.update name $+ \(prog, _, args) -> Just (prog, Just path, args) -userMaybeSpecifyPath :: String -> Maybe FilePath- -> ProgramDb -> ProgramDb-userMaybeSpecifyPath _ Nothing progdb = progdb+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@@ -317,11 +323,13 @@ 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]@@ -343,57 +351,54 @@ -- 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 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 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)]+ , programProperties = Map.empty+ , programLocation = location+ , programMonitorFiles = triedLocations+ } configuredProg' <- programPostConf prog verbosity configuredProg return (updateConfiguredProgs (Map.insert name configuredProg') progdb) - -- | 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.@@ -402,51 +407,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. --@@ -454,17 +459,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.@@ -476,50 +483,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
@@ -5,6 +5,7 @@ {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Find -- Copyright : Duncan Coutts 2013@@ -24,34 +25,39 @@ -- 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,- logExtraProgramSearchPath,- getSystemSearchPath,- getExtraPathEnv,- simpleProgram+module Distribution.Simple.Program.Find+ ( -- * Program search path+ ProgramSearchPath+ , ProgramSearchPathEntry (..)+ , defaultProgramSearchPath+ , findProgramOnSearchPath+ , programSearchPathAsPATHVar+ , logExtraProgramSearchPath+ , 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@@ -68,69 +74,74 @@ "Including the following directories in PATH:" : map ("- " ++) extraPaths -findProgramOnSearchPath :: Verbosity -> ProgramSearchPath- -> FilePath -> IO (Maybe (FilePath, [FilePath]))+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@@ -157,17 +168,16 @@ -- 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)@@ -207,19 +217,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- }+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
@@ -4,861 +4,917 @@ {-# 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.Find (getExtraPathEnv)-import Distribution.Simple.Program.Run-import Distribution.Simple.Program.Types-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 = 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"]-- 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,-- -- | Script files with irregular extensions that need -x hs.- ghcOptInputScripts :: 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,-- -- | Instruct GHC to link against @libHSrts@ when producing a shared library.- ghcOptLinkRts :: Flag Bool,-- -- | 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,-- -- | Program to use for the C and C++ compiler; the @ghc -pgmc@ flag.- ghcOptCcProgram :: Flag 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@- | GhcProfLate -- ^ @-fprof-late- deriving (Show, Eq)--runGHC :: Verbosity -> ConfiguredProgram -> Compiler -> Platform -> GhcOptions- -> IO ()-runGHC verbosity ghcProg comp platform opts = do- runProgramInvocation verbosity =<< ghcInvocation verbosity ghcProg comp platform opts--ghcInvocation :: Verbosity -> ConfiguredProgram -> Compiler -> Platform -> GhcOptions- -> IO ProgramInvocation-ghcInvocation verbosity ghcProg comp platform 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 (fromNubListR (ghcOptExtraPath opts))- let ghcProg' = ghcProg{programOverrideEnv = envOverrides ++ extraPath}-- pure $ programInvocation ghcProg' (renderGhcOptions comp platform 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 ]- , [ "-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 ]- , [ "-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]- , concat [ ["-pgmc", cc] | cc <- flag ghcOptCcProgram ]-- ------------------ -- 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- , concat [ [ "-x", "hs", script] | script <- flags ghcOptInputScripts ]- , [ 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+module Distribution.Simple.Program.GHC+ ( GhcOptions (..)+ , GhcMode (..)+ , GhcOptimisation (..)+ , GhcDynLinkMode (..)+ , GhcProfAuto (..)+ , ghcInvocation+ , renderGhcOptions+ , runGHC+ , 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.Run+import Distribution.Simple.Program.Types+import Distribution.System+import Distribution.Types.ComponentId+import Distribution.Utils.NubList+import Distribution.Verbosity+import Distribution.Version+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 Distribution.Types.ParStrat++normaliseGhcArgs :: Maybe Version -> PackageDescription -> [String] -> [String]+normaliseGhcArgs (Just ghcVersion) PackageDescription{..} ghcArgs+ | ghcVersion `withinRange` supportedGHCVersions =+ argumentFilters . filter simpleFilters . filterRtsOpts $ 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"]++ 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+ { 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 FilePath+ -- ^ The main input files; could be .hs, .hi, .c, .o, depending on mode.+ , ghcOptInputScripts :: NubListR FilePath+ -- ^ Script files with irregular extensions that need -x hs.+ , ghcOptInputModules :: NubListR ModuleName+ -- ^ The names of input Haskell modules, mainly for @--make@ mode.+ , ghcOptOutputFile :: Flag FilePath+ -- ^ 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 FilePath+ -- ^ Search path for Haskell source files; the @ghc -i@ 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 FilePath+ -- ^ 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 String+ -- ^ 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.+ , ghcOptCppIncludePath :: NubListR FilePath+ -- ^ Search path for CPP includes like header files; the @ghc -I@ flag.+ , ghcOptCppIncludes :: NubListR FilePath+ -- ^ 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 FilePath+ -- ^ 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 FilePath+ , ghcOptHieDir :: 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++ ghcOptVerbosity :: Flag Verbosity+ -- ^ Get GHC to be quiet or verbose with what it's doing; the @ghc -v@ flag.+ , ghcOptExtraPath :: NubListR FilePath+ -- ^ 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+ -> GhcOptions+ -> IO ()+runGHC verbosity ghcProg comp platform opts = do+ runProgramInvocation verbosity =<< ghcInvocation verbosity ghcProg comp platform opts++ghcInvocation+ :: Verbosity+ -> ConfiguredProgram+ -> Compiler+ -> Platform+ -> GhcOptions+ -> IO ProgramInvocation+ghcInvocation verbosity ghcProg comp platform 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 (fromNubListR (ghcOptExtraPath opts))+ let ghcProg' = ghcProg{programOverrideEnv = envOverrides ++ extraPath}++ pure $ programInvocation ghcProg' (renderGhcOptions comp platform 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]+ , ["-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]+ , ["-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 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", dir] | dir <- flag ghcOptOutputDir]+ , concat [["-odir", dir] | dir <- flag ghcOptObjDir]+ , concat [["-hidir", dir] | dir <- flag ghcOptHiDir]+ , concat [["-hiedir", dir] | dir <- flag ghcOptHieDir]+ , 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]+ , concat [["-pgmc", cc] | cc <- flag ghcOptCcProgram]+ , -----------------+ -- 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))]+ , ["-optl-Wl,-rpath," ++ dir | dir <- flags ghcOptRPaths]+ , 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+ , concat [["-x", "hs", script] | script <- flags ghcOptInputScripts]+ , [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 -- ----------------------------------------------------------------------------- -- Boilerplate Monoid instance for GhcOptions
src/Distribution/Simple/Program/HcPkg.hs view
@@ -1,9 +1,10 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.HcPkg -- Copyright : Duncan Coutts 2009, 2013@@ -13,35 +14,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,6 +51,7 @@ 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@@ -59,42 +60,46 @@ import Distribution.Types.UnitId import Distribution.Verbosity -import Data.List (stripPrefix)+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.@@ -102,194 +107,185 @@ invoke hpi verbosity dbStack extraArgs = runProgramInvocation verbosity invocation where- args = packageDbStackOpts hpi dbStack ++ extraArgs+ args = packageDbStackOpts hpi dbStack ++ extraArgs invocation = programInvocation (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+ :: HcPkgInfo+ -> Verbosity+ -> PackageDBStack+ -> InstalledPackageInfo+ -> RegisterOptions+ -> IO () register hpi verbosity 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)+ , 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+ -> 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"-+ | supportsDirDbs hpi =+ do+ let pkgfile = 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.- die' verbosity $ "HcPkg.writeRegistrationFileDirectly: only supports SpecificPackageDB for now"-+ -- 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+ runProgramInvocation+ verbosity (unregisterInvocation hpi verbosity 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+ runProgramInvocation+ verbosity (recacheInvocation hpi verbosity 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+ runProgramInvocation+ verbosity (exposeInvocation hpi verbosity 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+ output <-+ getProgramInvocationLBS+ verbosity+ (describeInvocation hpi verbosity 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+ runProgramInvocation+ verbosity (hideInvocation hpi verbosity 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+ output <-+ getProgramInvocationLBS+ verbosity+ (dumpInvocation hpi verbosity 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,26 +299,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),- libraryDirsStatic = mungePaths (libraryDirsStatic 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'@@ -332,53 +327,55 @@ 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+ :: 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"+ output <-+ getProgramInvocationOutput+ verbosity+ (listInvocation hpi verbosity 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 @@ -388,130 +385,157 @@ 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+ -> PackageDBStack -> InstalledPackageInfo -> RegisterOptions -> ProgramInvocation registerInvocation hpi verbosity packagedbs pkgInfo registerOptions =- (programInvocation (hcPkgProgram hpi) (args "-")) {- progInvokeInput = Just $ IODataText $ showInstalledPackageInfo pkgInfo,- progInvokeInputEncoding = IOEncodingUTF8+ (programInvocation (hcPkgProgram hpi) (args "-"))+ { progInvokeInput = Just $ IODataText $ showInstalledPackageInfo pkgInfo+ , progInvokeInputEncoding = IOEncodingUTF8 } where cmdname | registerAllowOverwrite registerOptions = "update"- | registerMultiInstance registerOptions = "update"- | otherwise = "register"+ | registerMultiInstance registerOptions = "update"+ | otherwise = "register" - args file = [cmdname, file]- ++ packageDbStackOpts hpi packagedbs- ++ [ "--enable-multi-instance"- | registerMultiInstance registerOptions ]- ++ [ "--force-files"- | registerSuppressFilesCheck registerOptions ]- ++ 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 -> PackageDB -> PackageId- -> ProgramInvocation+unregisterInvocation+ :: HcPkgInfo+ -> Verbosity+ -> PackageDB+ -> PackageId+ -> ProgramInvocation unregisterInvocation hpi verbosity packagedb pkgid = programInvocation (hcPkgProgram hpi) $- ["unregister", packageDbOpts hpi packagedb, prettyShow pkgid]- ++ verbosityOpts hpi verbosity-+ ["unregister", packageDbOpts hpi packagedb, prettyShow pkgid]+ ++ verbosityOpts hpi verbosity -recacheInvocation :: HcPkgInfo -> Verbosity -> PackageDB- -> ProgramInvocation+recacheInvocation+ :: HcPkgInfo+ -> Verbosity+ -> PackageDB+ -> ProgramInvocation recacheInvocation hpi verbosity packagedb = programInvocation (hcPkgProgram hpi) $- ["recache", packageDbOpts hpi packagedb]- ++ verbosityOpts hpi verbosity-+ ["recache", packageDbOpts hpi packagedb]+ ++ verbosityOpts hpi verbosity -exposeInvocation :: HcPkgInfo -> Verbosity -> PackageDB -> PackageId- -> ProgramInvocation+exposeInvocation+ :: HcPkgInfo+ -> Verbosity+ -> PackageDB+ -> PackageId+ -> ProgramInvocation exposeInvocation hpi verbosity packagedb pkgid = programInvocation (hcPkgProgram hpi) $- ["expose", packageDbOpts hpi packagedb, prettyShow pkgid]- ++ verbosityOpts hpi verbosity+ ["expose", packageDbOpts hpi packagedb, prettyShow pkgid]+ ++ verbosityOpts hpi verbosity -describeInvocation :: HcPkgInfo -> Verbosity -> PackageDBStack -> PackageId- -> ProgramInvocation+describeInvocation+ :: HcPkgInfo+ -> Verbosity+ -> PackageDBStack+ -> PackageId+ -> ProgramInvocation describeInvocation hpi verbosity packagedbs pkgid = programInvocation (hcPkgProgram hpi) $- ["describe", prettyShow pkgid]- ++ packageDbStackOpts hpi packagedbs- ++ verbosityOpts hpi verbosity+ ["describe", prettyShow pkgid]+ ++ packageDbStackOpts hpi packagedbs+ ++ verbosityOpts hpi verbosity -hideInvocation :: HcPkgInfo -> Verbosity -> PackageDB -> PackageId- -> ProgramInvocation+hideInvocation+ :: HcPkgInfo+ -> Verbosity+ -> PackageDB+ -> PackageId+ -> ProgramInvocation hideInvocation hpi verbosity packagedb pkgid = programInvocation (hcPkgProgram hpi) $- ["hide", packageDbOpts hpi packagedb, prettyShow pkgid]- ++ verbosityOpts hpi verbosity-+ ["hide", packageDbOpts hpi packagedb, prettyShow pkgid]+ ++ verbosityOpts hpi verbosity dumpInvocation :: HcPkgInfo -> Verbosity -> PackageDB -> ProgramInvocation dumpInvocation hpi _verbosity packagedb =- (programInvocation (hcPkgProgram hpi) args) {- progInvokeOutputEncoding = IOEncodingUTF8+ (programInvocation (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. +-- 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+ (programInvocation (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 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 ++ "=" ++ 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 _ GlobalPackageDB = "--global"+packageDbOpts _ UserPackageDB = "--user" packageDbOpts hpi (SpecificPackageDB db) = "--" ++ packageDbFlag hpi ++ "=" ++ 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
@@ -2,6 +2,7 @@ {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Hpc -- Copyright : Thomas Tuegel 2011@@ -10,21 +11,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 Prelude () import System.Directory (makeRelativeToCurrentDirectory) 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.Verbosity import Distribution.Version@@ -36,74 +36,103 @@ -- 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+ :: 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+ 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 makeRelativeToCurrentDirectory hpcDirs' - runProgramInvocation verbosity- (markupInvocation hpc tixFile hpcDirs'' destDir included)+ runProgramInvocation+ verbosity+ (markupInvocation 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+ :: 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+ let args =+ [ "markup"+ , tixFile+ , "--destdir=" ++ destDir+ ]+ ++ map ("--hpcdir=" ++) hpcDirs+ ++ [ "--include=" ++ prettyShow moduleName+ | moduleName <- included+ ]+ in programInvocation 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+ :: 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)+ runProgramInvocation+ verbosity+ (unionInvocation 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+ :: 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 ]+ programInvocation hpc $+ concat+ [ ["sum", "--union"]+ , tixFiles+ , ["--output=" ++ outFile]+ , [ "--exclude=" ++ prettyShow moduleName+ | moduleName <- excluded ]+ ]
src/Distribution/Simple/Program/Internal.hs view
@@ -1,4 +1,5 @@ -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Internal --@@ -6,14 +7,13 @@ -- 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'. --@@ -23,25 +23,24 @@ -- '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 -- 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 && ")" `isSuffixOf` x = 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
@@ -2,6 +2,7 @@ {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Ld -- Copyright : Duncan Coutts 2009@@ -10,58 +11,74 @@ -- 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 (..)) import Distribution.Simple.Program.ResponseFile- ( withResponseFile )+ ( withResponseFile+ ) import Distribution.Simple.Program.Run- ( ProgramInvocation, programInvocation, multiStageProgramInvocation- , runProgramInvocation )+ ( ProgramInvocation+ , multiStageProgramInvocation+ , programInvocation+ , 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.Verbosity- ( Verbosity )+ ( Verbosity+ ) import System.Directory- ( renameFile )+ ( renameFile+ ) import System.FilePath- ( (<.>), takeDirectory )+ ( takeDirectory+ , (<.>)+ ) -- | Call @ld -r@ to link a bunch of object files together.----combineObjectFiles :: Verbosity -> LocalBuildInfo -> ConfiguredProgram- -> FilePath -> [FilePath] -> IO ()+combineObjectFiles+ :: Verbosity+ -> LocalBuildInfo+ -> ConfiguredProgram+ -> FilePath+ -> [FilePath]+ -> IO () combineObjectFiles verbosity lbi ld 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 simpleArgs = ["-r", "-o", target] initialArgs = ["-r", "-o", target]- middleArgs = ["-r", "-o", target, tmpfile]- finalArgs = middleArgs+ middleArgs = ["-r", "-o", target, tmpfile]+ finalArgs = middleArgs - simple = programInvocation ld simpleArgs- initial = programInvocation ld initialArgs- middle = programInvocation ld middleArgs- final = programInvocation ld finalArgs+ simple = programInvocation ld simpleArgs+ initial = programInvocation ld initialArgs+ middle = programInvocation ld middleArgs+ final = programInvocation ld finalArgs - targetDir = takeDirectory target+ targetDir = takeDirectory target invokeWithResponesFile :: FilePath -> ProgramInvocation invokeWithResponesFile atFile =@@ -71,22 +88,19 @@ 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 =+ responseArgumentsNotSupported = not (arResponseFilesSupported (compiler lbi)) 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) files+ else withResponseFile verbosity defaultTempFileOptions targetDir "ld.rsp" Nothing files $+ \path -> runProgramInvocation verbosity $ invokeWithResponesFile path where- tmpfile = target <.> "tmp" -- perhaps should use a proper temp file-+ 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+ run [] = return ()+ run [inv] = runProgramInvocation verbosity inv+ run (inv : invs) = do+ runProgramInvocation verbosity inv+ renameFile target tmpfile+ run invs
src/Distribution/Simple/Program/ResponseFile.hs view
@@ -1,6 +1,10 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RankNTypes #-}+ ----------------------------------------------------------------------------++----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.ResponseFile -- Copyright : (c) Sergey Vinokurov 2017@@ -8,24 +12,26 @@ -- -- 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.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.+ -> 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. -> (FilePath -> IO a) -> IO a withResponseFile verbosity tmpFileOpts workDir fileNameTemplate encoding arguments f =@@ -51,9 +57,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,9 @@ {-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Run -- Copyright : Duncan Coutts 2009@@ -12,7 +13,6 @@ -- -- This module provides a data type for program invocations and functions to -- run them.- module Distribution.Simple.Program.Run ( ProgramInvocation (..) , IOEncoding (..)@@ -32,13 +32,14 @@ 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.Verbosity 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. --@@ -46,122 +47,140 @@ -- 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)],- 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+ } -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 = [],- progInvokeCwd = Nothing,- progInvokeInput = Nothing,- progInvokeInputEncoding = IOEncodingText,- progInvokeOutputEncoding = IOEncodingText- }+ ProgramInvocation+ { progInvokePath = ""+ , progInvokeArgs = []+ , progInvokeEnv = []+ , progInvokeCwd = Nothing+ , progInvokeInput = Nothing+ , progInvokeInputEncoding = IOEncodingText+ , progInvokeOutputEncoding = IOEncodingText+ } simpleProgramInvocation :: FilePath -> [String] -> ProgramInvocation simpleProgramInvocation path args =- emptyProgramInvocation {- progInvokePath = path,- progInvokeArgs = args- }+ emptyProgramInvocation+ { progInvokePath = path+ , progInvokeArgs = args+ } 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+ } runProgramInvocation :: Verbosity -> ProgramInvocation -> IO ()-runProgramInvocation verbosity- ProgramInvocation {- progInvokePath = path,- progInvokeArgs = args,- progInvokeEnv = [],- progInvokeCwd = Nothing,- progInvokeInput = Nothing- } =- rawSystemExit verbosity path args--runProgramInvocation verbosity- ProgramInvocation {- progInvokePath = path,- progInvokeArgs = args,- progInvokeEnv = envOverrides,- progInvokeCwd = mcwd,- progInvokeInput = Nothing- } = do+runProgramInvocation+ verbosity+ ProgramInvocation+ { progInvokePath = path+ , progInvokeArgs = args+ , progInvokeEnv = []+ , progInvokeCwd = Nothing+ , progInvokeInput = Nothing+ } =+ rawSystemExit verbosity 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+ 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+ (_, 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@@ -171,36 +190,39 @@ 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- , progInvokeCwd = mcwd- , progInvokeInput = minputStr+ { progInvokePath = path+ , progInvokeArgs = args+ , progInvokeEnv = envOverrides+ , progInvokeCwd = mcwd+ , progInvokeInput = minputStr , progInvokeInputEncoding = encoding } mode = do menv <- getEffectiveEnvironment envOverrides rawSystemStdInOut verbosity path args mcwd menv input mode- where- input = encodeToIOData encoding <$> minputStr+ 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+ :: [(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 the unix xargs program. Useful for when we've got very long command@@ -222,53 +244,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 -> "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]+ 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,62 @@ 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+ 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
@@ -2,9 +2,9 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-}-{-# LANGUAGE DeriveDataTypeable #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Program.Types -- Copyright : Isaac Jones 2006, Duncan Coutts 2007-2009@@ -19,28 +19,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(..),+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.Version import Distribution.Verbosity+import Distribution.Version import qualified Data.Map as Map @@ -48,38 +47,36 @@ -- -- 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 @@ -100,7 +97,7 @@ -- We also use this path to set the environment when running child processes. -- -- The @ProgramDb@ is created with a @ProgramSearchPath@ to which we--- @appendProgramSearchPath@ to add the ones that come from cli flags and from+-- @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]@@ -120,45 +117,36 @@ -- 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 {- -- | 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)],-- -- | 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,-- -- | Location of the program. eg. @\/usr\/bin\/ghc-6.4@- programLocation :: ProgramLocation,-- -- | 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]- }+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 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).+ } deriving (Eq, Generic, Read, Show, Typeable) instance Binary ConfiguredProgram@@ -167,12 +155,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, Typeable) instance Binary ProgramLocation instance Structured ProgramLocation@@ -183,20 +171,20 @@ -- | Suppress any extra arguments added by the user. suppressOverrideArgs :: ConfiguredProgram -> ConfiguredProgram-suppressOverrideArgs prog = prog { programOverrideArgs = [] }+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
@@ -2,6 +2,7 @@ {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Register -- Copyright : Isaac Jones 2003-2004@@ -25,163 +26,194 @@ -- 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.Register import Distribution.Simple.Utils-import Distribution.Utils.MapAccum import Distribution.System-import Distribution.Pretty+import Distribution.Utils.MapAccum 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+ :: 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)+ -- 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) - -- 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) <-+ (_, 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)+ 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)+ where+ verbosity = fromFlag (regVerbosity flags) -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 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+ 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)+ packageDbs =+ nub $+ withPackageDB lbi+ ++ maybeToList (flagToMaybe (regPackageDB regFlags))+ distPref = fromFlag (regDistPref regFlags) verbosity = fromFlag (regVerbosity regFlags) -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)+ packageDbs+ ipi+ HcPkg.defaultRegisterOptions where modeGenerateRegFile = isJust (flagToMaybe (regGenPkgConf regFlags))- regFile = fromMaybe (prettyShow (packageId pkg) <.> "conf")- (fromFlag (regGenPkgConf regFlags))+ regFile =+ fromMaybe+ (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))+ packageDbs =+ nub $+ withPackageDB lbi+ ++ maybeToList (flagToMaybe (regPackageDB regFlags)) verbosity = fromFlag (regVerbosity regFlags) writeRegistrationFileOrDirectory = do@@ -196,172 +228,233 @@ 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 ipis packageDbs) -generateRegistrationInfo :: Verbosity- -> PackageDescription- -> Library- -> LocalBuildInfo- -> ComponentLocalBuildInfo- -> Bool- -> Bool- -> FilePath- -> PackageDB- -> IO InstalledPackageInfo+generateRegistrationInfo+ :: Verbosity+ -> PackageDescription+ -> Library+ -> LocalBuildInfo+ -> ComponentLocalBuildInfo+ -> Bool+ -> Bool+ -> FilePath+ -> PackageDB+ -> IO InstalledPackageInfo generateRegistrationInfo verbosity pkg lib lbi clbi inplace reloc distPref packageDb = do- --TODO: eliminate pwd!+ -- TODO: eliminate pwd! pwd <- getCurrentDirectory 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+ pwd+ 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+ -> FilePath+ -> 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 ()+ HaskellSuite _ -> HaskellSuite.initPackageDB verbosity progdb dbPath+ _ -> 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+ -> Compiler+ -> ProgramDb+ -> PackageDBStack+ -> [String]+ -> IO () invokeHcPkg verbosity comp progdb dbStack extraArgs =- withHcPkg verbosity "invokeHcPkg" comp progdb+ withHcPkg+ verbosity+ "invokeHcPkg"+ comp+ progdb (\hpi -> HcPkg.invoke hpi verbosity 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+ -> Compiler+ -> ProgramDb+ -> PackageDBStack+ -> InstalledPackageInfo+ -> HcPkg.RegisterOptions+ -> IO () registerPackage verbosity comp progdb packageDbs installedPkgInfo registerOptions = case compilerFlavor comp of- GHC -> GHC.registerPackage verbosity progdb packageDbs installedPkgInfo registerOptions+ 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"+ HaskellSuite{} ->+ HaskellSuite.registerPackage verbosity progdb packageDbs installedPkgInfo+ _+ | HcPkg.registerMultiInstance registerOptions ->+ dieWithException verbosity RegisMultiplePkgNotSupported+ UHC -> UHC.registerPackage verbosity comp progdb packageDbs installedPkgInfo+ _ -> dieWithException verbosity RegisteringNotImplemented -writeHcPkgRegisterScript :: Verbosity- -> [InstalledPackageInfo]- -> PackageDBStack- -> HcPkg.HcPkgInfo- -> IO ()+writeHcPkgRegisterScript+ :: Verbosity+ -> [InstalledPackageInfo]+ -> PackageDBStack+ -> HcPkg.HcPkgInfo+ -> IO () writeHcPkgRegisterScript verbosity 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+ packageDbs+ installedPkgInfo+ HcPkg.defaultRegisterOptions+ in invocationAsSystemScript buildOS invocation scripts = map genScript ipis -- TODO: Do something more robust here regScript = unlines scripts@@ -372,19 +465,18 @@ regScriptFileName :: FilePath regScriptFileName = case buildOS of- Windows -> "register.bat"- _ -> "register.sh"-+ Windows -> "register.bat"+ _ -> "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,87 +485,94 @@ -> 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 = componentInstantiatedWith clbi+ , IPI.sourceLibName = libName lib+ , IPI.compatPackageKey = componentCompatPackageKey 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.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 = 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 =+ 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.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 = 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+ } 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) 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)) && hasJsSupport))- && 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+ _ -> False libdirsStatic | hasLibrary = libdir installDirs : extraLibDirsStaticOrFallback- | otherwise = extraLibDirsStaticOrFallback+ | otherwise = extraLibDirsStaticOrFallback where -- If no static library dirs were given, the package likely makes no -- distinction between fully static linking and otherwise.@@ -482,128 +581,161 @@ [] -> extraLibDirs bi dirs -> dirs (libdirs, dynlibdirs)- | not hasLibrary- = (extraLibDirs bi, [])+ | not hasLibrary =+ (extraLibDirs bi, []) -- 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 bi+ , dynlibdir installDirs : extraLibDirs bi+ )+ | otherwise =+ (libdir installDirs : dynlibdir installDirs : extraLibDirs bi, []) - | 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+ :: FilePath+ -- ^ top of the build tree+ -> FilePath+ -- ^ 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 adjustRelativeIncludeDirs = concatMap $ \d ->- [ inplaceDir </> d -- local include-dir- , inplaceDir </> libTargetDir </> d -- autogen include-dir+ [ inplaceDir </> d -- local include-dir+ , inplaceDir </> libTargetDir </> 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"+ (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) - -- | 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+ -> FilePath+ -> 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 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 genScript = fromFlag (regGenScript regFlags) verbosity = fromFlag (regVerbosity regFlags)- packageDb = fromFlagOrDefault (registrationPackageDB (withPackageDB lbi))- (regPackageDB regFlags)+ packageDb =+ fromFlagOrDefault+ (registrationPackageDB (withPackageDB lbi))+ (regPackageDB regFlags) 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+ 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 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 </> "package.conf.inplace"
src/Distribution/Simple/Setup.hs view
@@ -2,2563 +2,197 @@ {-# 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, 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(..),- 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.DumpBuildInfo-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- configExtraLibDirsStatic :: [FilePath], -- ^ path to search for extra- -- libraries when linking- -- fully static executables- 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.- 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.- }- 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 configExtraLibDirsStatic- && 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 configDumpBuildInfo- && 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 #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- }--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, 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 })- [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"- ]-- , 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 -> [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-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 -> [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 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 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)) = 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,- haddockIndex :: Flag PathTemplate,- haddockDistPref :: Flag FilePath,- haddockKeepTempFiles:: Flag Bool,- haddockVerbosity :: Flag Verbosity,- haddockCabalFilePath :: Flag FilePath,- haddockBaseUrl :: Flag String,- haddockLib :: Flag String,- 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,- haddockIndex = NoFlag,- haddockBaseUrl = NoFlag,- haddockLib = NoFlag,- 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))-- ,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 "" ["lib"]- "location of Haddocks static / auxiliary files"- haddockLib (\v flags -> flags { haddockLib = v})- (reqArgFlag "DIR")- ]--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 {- 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`- haddockProjectLocal :: Flag Bool,- -- ^ a shortcut option which builds self contained directory which contains- -- all the documentation, it implies:- -- * `--quickjump`- -- * `--gen-index`- -- * `--gen-contents`- -- * `--hyperlinked-source`- --- -- And it will also pass `--base-url` option to `haddock`.-- -- options passed to @haddock@ via 'createHaddockIndex'- haddockProjectDir :: Flag String,- -- ^ output directory of combined haddocks, the default is './haddocks'- haddockProjectPrologue :: Flag String,- haddockProjectGenIndex :: Flag Bool,- haddockProjectGenContents :: Flag Bool,- 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,- haddockProjectLinkedSource :: Flag Bool,- haddockProjectQuickJump :: Flag Bool,- 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- haddockProjectKeepTempFiles:: Flag Bool,- haddockProjectVerbosity :: Flag Verbosity,- -- haddockBaseUrl is not supported, a fixed value is provided- haddockProjectLib :: Flag String- }- deriving (Show, Generic, Typeable)--defaultHaddockProjectFlags :: HaddockProjectFlags-defaultHaddockProjectFlags = HaddockProjectFlags {- haddockProjectHackage = Flag False,- haddockProjectLocal = Flag False,- haddockProjectDir = Flag "./haddocks",- haddockProjectPrologue = NoFlag,- haddockProjectGenIndex = Flag False,- haddockProjectGenContents = Flag False,- 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,- haddockProjectLinkedSource = Flag False,- haddockProjectQuickJump = Flag False,- haddockProjectHscolourCss = NoFlag,- haddockProjectKeepTempFiles= Flag False,- haddockProjectVerbosity = Flag normal,- haddockProjectLib = NoFlag,- haddockProjectInterfaces = NoFlag- }--haddockProjectCommand :: CommandUI HaddockProjectFlags-haddockProjectCommand = CommandUI- { commandName = "v2-haddock-project"- , commandSynopsis = "Generate Haddocks HTML documentation for the cabal project."- , commandDescription = Just $ \_ ->- "Require the programm haddock, version 2.26.\n"- , commandNotes = Nothing- , commandUsage = usageAlternatives "haddocks" $- [ "[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 =- [option "" ["hackage"]- (concat ["A short-cut option to build documentation linked to hackage; "- ,"it implies --quickjump, --gen-index, --gen-contents, "- ,"--hyperlinked-source and --html-location"- ])- haddockProjectHackage (\v flags -> flags { haddockProjectHackage = v })- trueArg-- ,option "" ["local"]- (concat ["A short-cut option to build self contained documentation; "- ,"it implies --quickjump, --gen-index, --gen-contents "- ,"and --hyperlinked-source."- ])- haddockProjectLocal (\v flags -> flags { haddockProjectLocal = 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 "" ["gen-index"]- "Generate index"- haddockProjectGenIndex (\v flags -> flags { haddockProjectGenIndex = v})- trueArg-- ,option "" ["gen-contents"]- "Generate contents"- haddockProjectGenContents (\v flags -> flags { haddockProjectGenContents = v})- trueArg-- ,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 "" ["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 "" ["hyperlink-source","hyperlink-sources","hyperlinked-source"]- "Hyperlink the documentation to the source code"- haddockProjectLinkedSource (\v flags -> flags { haddockProjectLinkedSource = v })- trueArg-- ,option "" ["quickjump"]- "Generate an index for interactive documentation navigation"- haddockProjectQuickJump (\v flags -> flags { haddockProjectQuickJump = v })- trueArg-- ,option "" ["hscolour-css"]- "Use PATH as the HsColour stylesheet"- haddockProjectHscolourCss (\v flags -> flags { haddockProjectHscolourCss = v })- (reqArgFlag "PATH")-- ,option "" ["keep-temp-files"]- "Keep temporary files"- haddockProjectKeepTempFiles (\b flags -> flags { haddockProjectKeepTempFiles = b })- trueArg-- ,optionVerbosity haddockProjectVerbosity- (\v flags -> flags { haddockProjectVerbosity = v })-- ,option "" ["lib"]- "location of Haddocks static / auxiliary files"- haddockProjectLib (\v flags -> flags { haddockProjectLib = v})- (reqArgFlag "DIR")- ]---emptyHaddockProjectFlags :: HaddockProjectFlags-emptyHaddockProjectFlags = mempty--instance Monoid HaddockProjectFlags where- mempty = gmempty- mappend = (<>)--instance Semigroup HaddockProjectFlags 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 ReplOptions = ReplOptions {- replOptionsFlags :: [String],- replOptionsNoLoad :: Flag Bool- }- deriving (Show, Generic, Typeable)--instance Binary ReplOptions-instance Structured ReplOptions---instance Monoid ReplOptions where- mempty = ReplOptions mempty (Flag False)- mappend = (<>)--instance Semigroup ReplOptions where- (<>) = gmappend--data ReplFlags = ReplFlags {- replProgramPaths :: [(String, FilePath)],- replProgramArgs :: [(String, [String])],- replDistPref :: Flag FilePath,- replVerbosity :: Flag Verbosity,- replReload :: Flag Bool,- replReplOptions :: ReplOptions- }- deriving (Show, Generic, Typeable)--defaultReplFlags :: ReplFlags-defaultReplFlags = ReplFlags {- replProgramPaths = mempty,- replProgramArgs = [],- replDistPref = NoFlag,- replVerbosity = Flag normal,- 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; 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 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)- ]---- --------------------------------------------------------------- * 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 $- testOrBenchmarkHelpText "test"- , commandNotes = Nothing- , commandUsage = usageAlternatives "test"- [ "[FLAGS]"- , "TESTCOMPONENTS [FLAGS]"- ]- , commandDefaultFlags = defaultTestFlags- , commandOptions = testOptions'- }---- | 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." ]- ]- ]--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 $- testOrBenchmarkHelpText "benchmark"- , 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'"---- --------------------------------------------------------------- * 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 =+{-# LANGUAGE LambdaCase #-}+{-# 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+ , ConfigFlags (..)+ , emptyConfigFlags+ , defaultConfigFlags+ , configureCommand+ , configPrograms+ , configAbsolutePaths+ , 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 (..)+ , toFlag+ , fromFlag+ , fromFlagOrDefault+ , flagToMaybe+ , flagToList+ , maybeToFlag+ , BooleanFlag (..)+ , boolOpt+ , boolOpt'+ , trueArg+ , falseArg+ , optionVerbosity+ , BuildingWhat (..)+ , buildingWhatVerbosity+ , buildingWhatDistPref+ ) where++import GHC.Generics (Generic)+import Prelude (FilePath, Show, ($))++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+import Distribution.Simple.Setup.Repl+import Distribution.Simple.Setup.SDist+import Distribution.Simple.Setup.Test++import Distribution.Verbosity (Verbosity)++-- | What kind of build are we doing?+--+-- 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)++buildingWhatVerbosity :: BuildingWhat -> Verbosity+buildingWhatVerbosity = \case+ BuildNormal flags -> fromFlag $ buildVerbosity flags+ BuildRepl flags -> fromFlag $ replVerbosity flags+ BuildHaddock flags -> fromFlag $ haddockVerbosity flags+ BuildHscolour flags -> fromFlag $ hscolourVerbosity flags++buildingWhatDistPref :: BuildingWhat -> FilePath+buildingWhatDistPref = \case+ BuildNormal flags -> fromFlag $ buildDistPref flags+ BuildRepl flags -> fromFlag $ replDistPref flags+ BuildHaddock flags -> fromFlag $ haddockDistPref flags+ BuildHscolour flags -> fromFlag $ hscolourDistPref flags++-- 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"]
+ src/Distribution/Simple/Setup/Benchmark.hs view
@@ -0,0 +1,131 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}++-----------------------------------------------------------------------------++-- |+-- 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 (..)+ , emptyBenchmarkFlags+ , defaultBenchmarkFlags+ , benchmarkCommand+ , benchmarkOptions'+ ) where++import Distribution.Compat.Prelude hiding (get)+import Prelude ()++import Distribution.Simple.Command hiding (boolOpt, boolOpt')+import Distribution.Simple.Flag+import Distribution.Simple.InstallDirs+import Distribution.Simple.Utils+import Distribution.Verbosity++import Distribution.Simple.Setup.Common++-- ------------------------------------------------------------++-- * Benchmark flags++-- ------------------------------------------------------------++data BenchmarkFlags = BenchmarkFlags+ { benchmarkDistPref :: Flag FilePath+ , benchmarkVerbosity :: Flag Verbosity+ , benchmarkOptions :: [PathTemplate]+ }+ deriving (Show, Generic, Typeable)++instance Binary BenchmarkFlags+instance Structured BenchmarkFlags++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 $+ testOrBenchmarkHelpText "benchmark"+ , 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
+ src/Distribution/Simple/Setup/Build.hs view
@@ -0,0 +1,165 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}++-----------------------------------------------------------------------------++-- |+-- 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 (..)+ , 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.Utils+import Distribution.Types.DumpBuildInfo+import Distribution.Verbosity++import Distribution.Simple.Setup.Common++-- ------------------------------------------------------------++-- * Build flags++-- ------------------------------------------------------------++data BuildFlags = BuildFlags+ { buildProgramPaths :: [(String, FilePath)]+ , buildProgramArgs :: [(String, [String])]+ , buildDistPref :: Flag FilePath+ , buildVerbosity :: Flag Verbosity+ , buildNumJobs :: Flag (Maybe Int)+ , buildUseSemaphore :: Flag String+ , -- 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)++instance Binary BuildFlags+instance Structured BuildFlags++defaultBuildFlags :: BuildFlags+defaultBuildFlags =+ BuildFlags+ { buildProgramPaths = mempty+ , buildProgramArgs = []+ , buildDistPref = mempty+ , buildVerbosity = Flag normal+ , buildNumJobs = mempty+ , buildUseSemaphore = NoFlag+ , 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})+ , 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,97 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}++-----------------------------------------------------------------------------++-- |+-- 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 (..)+ , 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.Verbosity++import Distribution.Simple.Setup.Common++-- ------------------------------------------------------------++-- * Clean flags++-- ------------------------------------------------------------++data CleanFlags = CleanFlags+ { cleanSaveConf :: Flag Bool+ , cleanDistPref :: Flag FilePath+ , cleanVerbosity :: Flag Verbosity+ , cleanCabalFilePath :: Flag FilePath+ }+ deriving (Show, Generic, Typeable)++instance Binary CleanFlags+instance Structured CleanFlags++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
+ src/Distribution/Simple/Setup/Common.hs view
@@ -0,0 +1,372 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# 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+ ( CopyDest (..)+ , configureCCompiler+ , configureLinker+ , programDbOption+ , programDbOptions+ , programDbPaths+ , programDbPaths'+ , programFlagsDescription+ , splitArgs+ , testOrBenchmarkHelpText+ , defaultDistPref+ , extraCompilationArtifacts+ , optionDistPref+ , Flag (..)+ , toFlag+ , fromFlag+ , fromFlagOrDefault+ , flagToMaybe+ , flagToList+ , maybeToFlag+ , BooleanFlag (..)+ , boolOpt+ , boolOpt'+ , trueArg+ , falseArg+ , reqArgFlag+ , 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.Verbosity++-- FIXME Not sure where this should live+defaultDistPref :: FilePath+defaultDistPref = "dist"++-- | The name of the directory where optional compilation artifacts+-- go, such as ghc plugins and .hie files.+extraCompilationArtifacts :: FilePath+extraCompilationArtifacts = "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+ ++ " (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)+ (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,1064 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}++-----------------------------------------------------------------------------++-- |+-- 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 (..)+ , emptyConfigFlags+ , defaultConfigFlags+ , configureCommand+ , configPrograms+ , configAbsolutePaths+ , 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.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.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.Verbosity+import qualified Text.PrettyPrint as Disp++import Distribution.Compat.Semigroup (Last' (..), Option' (..))+import Distribution.Compat.Stack++import Distribution.Simple.Setup.Common++-- ------------------------------------------------------------++-- * 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+ , configExtraLibDirsStatic :: [FilePath]+ -- ^ path to search for extra+ -- libraries when linking+ -- fully static executables+ , 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 which already exist+ , configPromisedDependencies :: [GivenComponent]+ -- ^ 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).+ }+ 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 configExtraLibDirsStatic+ && 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 configPromisedDependencies+ && equal configConfigurationsFlags+ && equal configTests+ && equal configBenchmarks+ && equal configCoverage+ && equal configLibCoverage+ && equal configExactConfiguration+ && equal configFlagError+ && equal configRelocatable+ && equal configDebugInfo+ && equal configDumpBuildInfo+ && equal configUseResponseFiles+ && equal configCoverageFor+ 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)++{- FOURMOLU_DISABLE -}+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 #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 =+ [ 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, 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 -> [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-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 -> [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 prettyGivenComponent)+ )+ , option+ ""+ ["promised-dependency"]+ "A list of promised dependencies. E.g., --promised-dependency=\"void=void-0.5.8-177d5cdf20962d0581fe2e4932a6c309\""+ configPromisedDependencies+ (\v flags -> flags{configPromisedDependencies = v})+ ( reqArg+ "NAME[:COMPONENT_NAME]=CID"+ (parsecToReadE (const "dependency expected") ((\x -> [x]) `fmap` parsecGivenComponent))+ (map prettyGivenComponent)+ )+ , 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 [])+ )+ ]+ 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 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)) = 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++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 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+ )
+ src/Distribution/Simple/Setup/Copy.hs view
@@ -0,0 +1,153 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}++-----------------------------------------------------------------------------++-- |+-- 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 (..)+ , 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.Utils+import Distribution.Verbosity++import Distribution.Simple.Setup.Common++-- ------------------------------------------------------------++-- * 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)++instance Binary CopyFlags+instance Structured CopyFlags++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
+ src/Distribution/Simple/Setup/Global.hs view
@@ -0,0 +1,122 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# 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++-- ------------------------------------------------------------++-- * 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
+ src/Distribution/Simple/Setup/Haddock.hs view
@@ -0,0 +1,606 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}++-----------------------------------------------------------------------------++-- |+-- 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 (..)+ , 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.Verbosity+import qualified Text.PrettyPrint as Disp++import Distribution.Simple.Setup.Common++-- ------------------------------------------------------------++-- * 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+ , haddockIndex :: Flag PathTemplate+ , haddockDistPref :: Flag FilePath+ , haddockKeepTempFiles :: Flag Bool+ , haddockVerbosity :: Flag Verbosity+ , haddockCabalFilePath :: Flag FilePath+ , haddockBaseUrl :: Flag String+ , haddockLib :: Flag String+ , haddockOutputDir :: Flag FilePath+ , haddockArgs :: [String]+ }+ deriving (Show, Generic, Typeable)++instance Binary HaddockFlags+instance Structured HaddockFlags++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+ , haddockIndex = NoFlag+ , haddockBaseUrl = NoFlag+ , haddockLib = NoFlag+ , haddockOutputDir = NoFlag+ , 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)+ )+ , 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+ ""+ ["lib"]+ "location of Haddocks static / auxiliary files"+ haddockLib+ (\v flags -> flags{haddockLib = 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")+ ]++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+ { 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+ haddockProjectKeepTempFiles :: Flag Bool+ , haddockProjectVerbosity :: Flag Verbosity+ , -- haddockBaseUrl is not supported, a fixed value is provided+ haddockProjectLib :: Flag String+ , haddockProjectOutputDir :: Flag FilePath+ }+ deriving (Show, Generic, Typeable)++defaultHaddockProjectFlags :: HaddockProjectFlags+defaultHaddockProjectFlags =+ HaddockProjectFlags+ { 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+ , haddockProjectKeepTempFiles = Flag False+ , haddockProjectVerbosity = Flag normal+ , haddockProjectLib = NoFlag+ , haddockProjectOutputDir = NoFlag+ , haddockProjectInterfaces = 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 =+ [ 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+ ""+ ["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+ ""+ ["keep-temp-files"]+ "Keep temporary files"+ haddockProjectKeepTempFiles+ (\b flags -> flags{haddockProjectKeepTempFiles = b})+ trueArg+ , optionVerbosity+ haddockProjectVerbosity+ (\v flags -> flags{haddockProjectVerbosity = v})+ , option+ ""+ ["lib"]+ "location of Haddocks static / auxiliary files"+ haddockProjectLib+ (\v flags -> flags{haddockProjectLib = 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."+ haddockProjectOutputDir+ (\v flags -> flags{haddockProjectOutputDir = v})+ (reqArgFlag "DIR")+ ]++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,157 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}++-----------------------------------------------------------------------------++-- |+-- 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 (..)+ , 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.Verbosity++import Distribution.Simple.Setup.Common++-- ------------------------------------------------------------++-- * 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)++instance Binary HscolourFlags+instance Structured HscolourFlags++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")+ ]+ }
+ src/Distribution/Simple/Setup/Install.hs view
@@ -0,0 +1,158 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}++-----------------------------------------------------------------------------++-- |+-- 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 (..)+ , 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.Utils+import Distribution.Verbosity++import Distribution.Simple.Setup.Common++-- ------------------------------------------------------------++-- * 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
+ src/Distribution/Simple/Setup/Register.hs view
@@ -0,0 +1,195 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}++-----------------------------------------------------------------------------++-- |+-- 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 (..)+ , 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.Verbosity++import Distribution.Simple.Setup.Common++-- ------------------------------------------------------------++-- * 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
+ src/Distribution/Simple/Setup/Repl.hs view
@@ -0,0 +1,205 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}++-----------------------------------------------------------------------------++-- |+-- 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 (..)+ , 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.Utils+import Distribution.Verbosity++import Distribution.Simple.Setup.Common++-- ------------------------------------------------------------++-- * REPL Flags++-- ------------------------------------------------------------++data ReplOptions = ReplOptions+ { replOptionsFlags :: [String]+ , replOptionsNoLoad :: Flag Bool+ , replOptionsFlagOutput :: Flag FilePath+ }+ deriving (Show, Generic, Typeable)++instance Binary ReplOptions+instance Structured ReplOptions++instance Monoid ReplOptions where+ mempty = ReplOptions mempty (Flag False) NoFlag+ mappend = (<>)++instance Semigroup ReplOptions where+ (<>) = gmappend++data ReplFlags = ReplFlags+ { replProgramPaths :: [(String, FilePath)]+ , replProgramArgs :: [(String, [String])]+ , replDistPref :: Flag FilePath+ , replVerbosity :: Flag Verbosity+ , replReload :: Flag Bool+ , replReplOptions :: ReplOptions+ }+ deriving (Show, Generic, Typeable)++instance Binary ReplFlags+instance Structured ReplFlags++defaultReplFlags :: ReplFlags+defaultReplFlags =+ ReplFlags+ { replProgramPaths = mempty+ , replProgramArgs = []+ , replDistPref = NoFlag+ , replVerbosity = Flag normal+ , 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; 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 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)+ ]
+ src/Distribution/Simple/Setup/SDist.hs view
@@ -0,0 +1,113 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}++-----------------------------------------------------------------------------++-- |+-- 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 (..)+ , 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.Verbosity++import Distribution.Simple.Setup.Common++-- ------------------------------------------------------------++-- * 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
+ src/Distribution/Simple/Setup/Test.hs view
@@ -0,0 +1,254 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RankNTypes #-}++-----------------------------------------------------------------------------++-- |+-- 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 (..)+ , 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.Utils+import Distribution.Verbosity+import qualified Text.PrettyPrint as Disp++import Distribution.Simple.Setup.Common++-- ------------------------------------------------------------++-- * 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)++instance Binary TestFlags+instance Structured TestFlags++defaultTestFlags :: TestFlags+defaultTestFlags =+ TestFlags+ { testDistPref = NoFlag+ , testVerbosity = Flag normal+ , 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 =+ [ 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
src/Distribution/Simple/ShowBuildInfo.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE OverloadedStrings #-}+ -- | -- This module defines a simple JSON-based format for exporting basic -- information about a Cabal package and the compiler configuration Cabal@@ -55,12 +57,11 @@ -- * @src-files@: any other Haskell sources needed by the component -- -- Note: At the moment this is only supported when using the GHC compiler.-----{-# LANGUAGE OverloadedStrings #-}--module Distribution.Simple.ShowBuildInfo (- mkBuildInfo, mkBuildInfo', mkCompilerInfo, mkComponentInfo+module Distribution.Simple.ShowBuildInfo+ ( mkBuildInfo+ , mkBuildInfo'+ , mkCompilerInfo+ , mkComponentInfo ) where import System.FilePath@@ -68,37 +69,42 @@ 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 (Compiler, showCompilerId, compilerFlavor)+import Distribution.PackageDescription+import Distribution.Pretty+import Distribution.Simple.Compiler (Compiler, compilerFlavor, showCompilerId) import Distribution.Simple.Program-import Distribution.Simple.Setup+import Distribution.Simple.Setup.Build (BuildFlags) import Distribution.Simple.Utils (cabalVersion)-import Distribution.Utils.Json+import Distribution.Text import Distribution.Types.Component import Distribution.Types.ComponentLocalBuildInfo import Distribution.Types.LocalBuildInfo import Distribution.Types.TargetInfo-import Distribution.Text-import Distribution.Pretty+import Distribution.Utils.Json+import Distribution.Verbosity -- | Construct a JSON document describing the build information for a -- package. mkBuildInfo- :: FilePath -- ^ The source directory of the package- -> PackageDescription -- ^ Mostly information from the .cabal file- -> LocalBuildInfo -- ^ Configuration information- -> BuildFlags -- ^ Flags that the user passed to build+ :: FilePath+ -- ^ 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]- -> ([String], Json) -- ^ Json representation of buildinfo alongside generated warnings+ -> ([String], Json)+ -- ^ Json representation of buildinfo alongside generated warnings mkBuildInfo wdir pkg_descr lbi _flags compilerInfo targetsToBuild = (warnings, JsonObject buildInfoFields) where buildInfoFields = mkBuildInfo' (uncurry mkCompilerInfo compilerInfo) componentInfos@@ -113,33 +119,40 @@ -- 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'+ :: 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+ , "compiler" .= compilerInfo+ , "components" .= JsonArray componentInfos ] mkCompilerInfo :: ConfiguredProgram -> Compiler -> Json-mkCompilerInfo compilerProgram compilerInfo = JsonObject- [ "flavour" .= JsonString (prettyShow $ compilerFlavor compilerInfo)- , "compiler-id" .= JsonString (showCompilerId compilerInfo)- , "path" .= JsonString (programPath compilerProgram)- ]+mkCompilerInfo compilerProgram compilerInfo =+ JsonObject+ [ "flavour" .= JsonString (prettyShow $ compilerFlavor compilerInfo)+ , "compiler-id" .= JsonString (showCompilerId compilerInfo)+ , "path" .= JsonString (programPath compilerProgram)+ ] mkComponentInfo :: FilePath -> 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 sourceFiles)- , "hs-src-dirs" .= JsonArray (map (JsonString . prettyShow) $ hsSourceDirs bi)- , "src-dir" .= JsonString (addTrailingPathSeparator wdir)- ] <> cabalFile)+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 sourceFiles)+ , "hs-src-dirs" .= JsonArray (map (JsonString . prettyShow) $ hsSourceDirs bi)+ , "src-dir" .= JsonString (addTrailingPathSeparator wdir)+ ]+ <> cabalFile+ ) where (warnings, compilerArgs) = getCompilerArgs bi lbi clbi name = componentLocalName clbi@@ -147,11 +160,11 @@ -- 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"+ CLib _ -> "lib"+ CExe _ -> "exe"+ CTest _ -> "test" CBench _ -> "bench"- CFLib _ -> "flib"+ CFLib _ -> "flib" modules = case comp of CLib lib -> explicitLibModules lib CExe exe -> exeModules exe@@ -163,7 +176,7 @@ CBench bench -> benchmarkModules bench CFLib flib -> foreignLibModules flib sourceFiles = case comp of- CLib _ -> []+ CLib _ -> [] CExe exe -> [modulePath exe] CTest test -> case testInterface test of@@ -173,11 +186,10 @@ CBench bench -> case benchmarkInterface bench of BenchmarkExeV10 _ fp -> [fp] BenchmarkUnsupported _ -> []- CFLib _ -> [] cabalFile | Just fp <- pkgDescrFile lbi = [("cabal-file", JsonString fp)]- | otherwise = []+ | otherwise = [] -- | Get the command-line arguments that would be passed -- to the compiler to build the given component.@@ -188,12 +200,16 @@ -> ([String], [String]) getCompilerArgs bi lbi clbi = case compilerFlavor $ compiler lbi of- GHC -> ([], ghc)- GHCJS -> ([], ghc)- c ->- ( ["ShowBuildInfo.getCompilerArgs: Don't know how to get build "- ++ " arguments for compiler " ++ show c]- , [])+ GHC -> ([], ghc)+ GHCJS -> ([], ghc)+ 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
src/Distribution/Simple/SrcDist.hs view
@@ -2,6 +2,10 @@ {-# 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,67 +23,68 @@ -- 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.PackageDescription-import Distribution.PackageDescription.Check hiding (doesFileExist)-import Distribution.Package import Distribution.ModuleName import qualified Distribution.ModuleName as ModuleName-import Distribution.Version+import Distribution.Package+import Distribution.PackageDescription+import Distribution.PackageDescription.Check hiding (doesFileExist)+import Distribution.Pretty+import Distribution.Simple.BuildPaths import Distribution.Simple.Configure (findDistPrefOrDefault)+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.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 Distribution.Simple.Errors+import System.Directory (doesFileExist)+import System.FilePath (dropExtension, isRelative, (<.>), (</>))+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+ let targetPref = distPref tmpTargetDir = mkTmpDir distPref -- When given --list-sources, just output the list of sources to a file.@@ -88,20 +93,19 @@ ordinary <- listPackageSources verbosity "." pkg pps traverse_ (hPutStrLn outHandle) 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,7 +113,6 @@ generateSourceDir targetDir pkg' targzFile <- createArchive verbosity pkg' tmpDir targetPref notice verbosity $ "Source tarball created: " ++ targzFile- where generateSourceDir :: FilePath -> PackageDescription -> IO () generateSourceDir targetDir pkg' = do@@ -119,22 +122,26 @@ overwriteSnapshotPackageDesc verbosity pkg' targetDir verbosity = fromFlag (sDistVerbosity flags)- snapshot = fromFlag (sDistSnapshot flags)+ 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+ -> FilePath+ -- ^ directory with cabal file+ -> PackageDescription+ -- ^ info from the cabal file+ -> [PPSuffixHandler]+ -- ^ extra preprocessors (include suffixes)+ -> IO [FilePath]+ -- ^ relative paths 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,163 +151,171 @@ -- -- Since @3.4.0.0 listPackageSourcesWithDie- :: Verbosity -- ^ verbosity- -> (Verbosity -> String -> IO [FilePath])- -- ^ '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.- -> FilePath -- ^ directory with cabal file- -> PackageDescription -- ^ info from the cabal file- -> [PPSuffixHandler] -- ^ extra preprocessors (include suffixes)- -> IO [FilePath] -- ^ relative paths+ :: Verbosity+ -- ^ verbosity+ -> (Verbosity -> CabalException -> IO [FilePath])+ -- ^ '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.+ -> FilePath+ -- ^ directory with cabal file+ -> PackageDescription+ -- ^ info from the cabal file+ -> [PPSuffixHandler]+ -- ^ extra preprocessors (include suffixes)+ -> IO [FilePath]+ -- ^ relative paths 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- -> (Verbosity -> String -> IO [FilePath])- -- ^ '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.+ -- ^ verbosity+ -> (Verbosity -> CabalException -> IO [FilePath])+ -- ^ '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. -> FilePath- -- ^ directory with cabal file+ -- ^ directory with cabal file -> PackageDescription- -- ^ info from the cabal file+ -- ^ info from the cabal file -> [PPSuffixHandler]- -- ^ extra preprocessors (include suffixes)+ -- ^ extra preprocessors (include suffixes) -> IO [FilePath]- -- ^ relative paths+ -- ^ relative paths listPackageSources' verbosity rip cwd 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 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 $ 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 cwd bi pps []+ srcMainFile <- findMainExeFile verbosity cwd 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+ 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 ".")+ ] where -- 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 ()+-- | 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+ ordinary <- listPackageSources verbosity "." pkg_descr pps+ installOrdinaryFiles verbosity targetDir (zip (repeat []) ordinary)+ maybeCreateDefaultSetupScript targetDir where 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" -- | Create a default setup script in the target directory, if it doesn't exist. maybeCreateDefaultSetupScript :: FilePath -> IO ()@@ -308,22 +323,30 @@ 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+ -> FilePath+ -- ^ cwd -> BuildInfo -> [PPSuffixHandler]- -> FilePath -- ^ main-is+ -> FilePath+ -- ^ main-is -> IO FilePath findMainExeFile verbosity cwd exeBi pps mainPath = do- ppFile <- findFileCwdWithExtension cwd (ppSuffixes pps) (map getSymbolicPath (hsSourceDirs exeBi))- (dropExtension mainPath)+ ppFile <-+ findFileCwdWithExtension+ cwd+ (ppSuffixes pps)+ (map getSymbolicPath (hsSourceDirs exeBi))+ (dropExtension mainPath) case ppFile of Nothing -> findFileCwd verbosity cwd (map getSymbolicPath (hsSourceDirs exeBi)) mainPath Just pp -> return pp@@ -334,206 +357,236 @@ findModDefFile :: Verbosity -> FilePath -> BuildInfo -> [PPSuffixHandler] -> FilePath -> IO FilePath findModDefFile verbosity cwd flibBi _pps modDefPath =- findFileCwd verbosity cwd ("." : map getSymbolicPath (hsSourceDirs flibBi)) modDefPath+ findFileCwd verbosity cwd ("." : map getSymbolicPath (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+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' -- 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- && mn /= packageInfoModule- && 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+ -> 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 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 <- defaultPackageDesc 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])- -- ^ '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.- -> FilePath -- ^ cwd -- change me to 'BuildPath Absolute PackageDir'- -> BuildInfo- -> [PPSuffixHandler] -- ^ Extra preprocessors- -> [ModuleName] -- ^ Exposed modules- -> IO [FilePath]+ :: Verbosity+ -> (Verbosity -> CabalException -> IO [FilePath])+ -- ^ '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.+ -> 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+ sources <-+ fmap concat $+ sequenceA $+ [ let file = ModuleName.toFilePath 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 cwd suffixes searchDirs file+ >>= nonEmpty' (notFound module_) return+ | module_ <- modules ++ otherModules bi+ ]+ bootFiles <-+ sequenceA+ [ let file = ModuleName.toFilePath module_+ fileExts = builtinHaskellBootSuffixes+ 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 where nonEmpty' :: b -> ([a] -> b) -> [a] -> b nonEmpty' x _ [] = x nonEmpty' _ f xs = f xs - suffixes = ppSuffixes pps ++ ["hs", "lhs", "hsig", "lhsig"]+ suffixes = ppSuffixes pps ++ builtinHaskellSuffixes notFound :: ModuleName -> IO [FilePath]- notFound m = rip verbosity $ "Could not find module: " ++ prettyShow m- ++ " with any suffix: " ++ show suffixes ++ ". If the module "- ++ "is autogenerated it should be added to 'autogen-modules'."-+ 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 ppPackageCheck errors)+ notice verbosity $+ "Distribution quality errors:\n"+ ++ unlines (map ppPackageCheck errors) unless (null warnings) $- notice verbosity $ "Distribution quality warnings:\n"- ++ unlines (map ppPackageCheck 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,10 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ViewPatterns #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.Test -- Copyright : Thomas Tuegel 2010@@ -13,124 +16,178 @@ -- 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.Compiler+import Distribution.Simple.Flag (fromFlag) 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.Simple.Configure (getInstalledPackagesById)+import Distribution.Simple.Errors+import Distribution.Simple.Register+import Distribution.Simple.Setup (fromFlagOrDefault)+import Distribution.Simple.Setup.Common (extraCompilationArtifacts)+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+ )+import System.FilePath ((</>)) - 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+ 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+ -- 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 lbi 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), _) =+ case PD.testInterface suite of+ PD.TestSuiteExeV10 _ _ ->+ ExeV10.runTest pkg_descr lbi clbi hpcMarkupInfo flags suite+ PD.TestSuiteLibV09 _ _ ->+ LibV09.runTest pkg_descr lbi 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 testLogDir - -- Delete ordinary files from test log directory.- getDirectoryContents testLogDir- >>= filterM doesFileExist . map (testLogDir </>)- >>= traverse_ removeFile+ -- Delete ordinary files from test log directory.+ getDirectoryContents testLogDir+ >>= filterM doesFileExist . map (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 (</> extraCompilationArtifacts) $ 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 =+ (</>) 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,154 +2,194 @@ {-# 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.Environment import qualified Distribution.PackageDescription as PD import Distribution.Simple.Build.PathsModule import Distribution.Simple.BuildPaths import Distribution.Simple.Compiler+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.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 System.Process ( createPipe )+ ( createDirectoryIfMissing+ , doesDirectoryExist+ , doesFileExist+ , getCurrentDirectory+ , removeDirectoryRecursive+ )+import System.FilePath ((<.>), (</>))+import System.IO (stderr, stdout)+import System.Process (createPipe) 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'-- pwd <- getCurrentDirectory- existingEnv <- getEnvironment+import Distribution.Simple.Errors - let cmd = LBI.buildDir lbi </> testName'- </> testName' <.> exeExtension (LBI.hostPlatform lbi)- -- Check that the test executable exists.- exists <- doesFileExist cmd- unless exists $ die' verbosity $ "Could not find test program \"" ++ cmd- ++ "\". Did you build the package first?"+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_ = tixDir distPref way - -- Remove old .tix files if appropriate.- unless (fromFlag $ testKeepTix flags) $ do- exists' <- doesDirectoryExist tixDir_- when exists' $ removeDirectoryRecursive tixDir_+ pwd <- getCurrentDirectory+ existingEnv <- getEnvironment - -- Create directory for HPC files.- createDirectoryIfMissing True tixDir_+ let cmd =+ 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 - -- Write summary notices indicating start of test suite- notice verbosity $ summarizeSuiteStart $ testName'+ -- 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_ - -- 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+ -- Write summary notices indicating start of test suite+ notice verbosity $ summarizeSuiteStart $ testName' - -- 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+ -- 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 - -- Output logger- (wOut, wErr, getLogText) <- case details of- Direct -> return (stdout, stderr, return LBS.empty)- _ -> do- (rOut, wOut) <- createPipe+ -- 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 - return $ (,,) wOut wOut $ do- -- Read test executables' output- logText <- LBS.hGetContents rOut+ -- Output logger+ (wOut, wErr, getLogText) <- case details of+ Direct -> return (stdout, stderr, return LBS.empty)+ _ -> do+ (rOut, wOut) <- createPipe - -- '--show-details=streaming': print the log output in another thread- when (details == Streaming) $ LBS.putStr logText+ return $ (,,) wOut wOut $ do+ -- Read test executables' output+ logText <- LBS.hGetContents rOut - -- drain the output.- evaluate (force logText)+ -- '--show-details=streaming': print the log output in another thread+ when (details == Streaming) $ LBS.putStr 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)+ -- drain the output.+ evaluate (force logText) - 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)+ Nothing+ (Just shellEnv')+ getLogText+ -- these handles are automatically closed+ Nothing+ (Just wOut)+ (Just wErr)+ NoFlag ->+ rawSystemIOWithEnvAndAction+ verbosity+ cmd+ opts+ Nothing+ (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 "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 testName' = unUnqualComponentName $ PD.testName suite @@ -159,35 +199,44 @@ testLogDir = distPref </> "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 =+ 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
@@ -2,197 +2,235 @@ {-# 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.Environment import Distribution.Compat.Internal.TempFile import Distribution.ModuleName import qualified Distribution.PackageDescription as PD+import Distribution.Pretty import Distribution.Simple.Build.PathsModule import Distribution.Simple.BuildPaths import Distribution.Simple.Compiler+import Distribution.Simple.Flag (Flag (Flag, NoFlag), fromFlag) 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.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.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 Distribution.Compat.Process (proc)+import Distribution.Simple.Errors+import System.Directory+ ( canonicalizePath+ , createDirectoryIfMissing+ , doesDirectoryExist+ , doesFileExist+ , getCurrentDirectory+ , removeDirectoryRecursive+ , removeFile+ , setCurrentDirectory+ )+import System.FilePath ((<.>), (</>))+import System.IO (hClose, hPutStr) import qualified System.Process as Process -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+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 - pwd <- getCurrentDirectory- existingEnv <- getEnvironment+ pwd <- getCurrentDirectory+ existingEnv <- getEnvironment - 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 $ "Could not find test program \"" ++ cmd- ++ "\". Did you build the package first?"+ let cmd =+ LBI.buildDir lbi+ </> stubName suite+ </> stubName suite <.> exeExtension (LBI.hostPlatform lbi)+ -- Check that the test executable exists.+ exists <- doesFileExist cmd+ unless exists $+ dieWithException verbosity $+ Couldn'tFindTestProgLibV09 cmd - -- Remove old .tix files if appropriate.- unless (fromFlag $ testKeepTix flags) $ do- let tDir = tixDir distPref way testName'- exists' <- doesDirectoryExist tDir- when exists' $ removeDirectoryRecursive tDir+ -- Remove old .tix files if appropriate.+ unless (fromFlag $ testKeepTix flags) $ do+ let tDir = tixDir distPref way+ exists' <- doesDirectoryExist tDir+ when exists' $ removeDirectoryRecursive tDir - -- Create directory for HPC files.- createDirectoryIfMissing True $ tixDir distPref way testName'+ -- Create directory for HPC files.+ createDirectoryIfMissing True $ tixDir distPref way - -- Write summary notices indicating start of test suite- notice verbosity $ summarizeSuiteStart testName'+ -- Write summary notices indicating start of test suite+ notice verbosity $ summarizeSuiteStart testName' - suiteLog <- CE.bracket openCabalTemp deleteIfExists $ \tempLog -> do+ suiteLog <- CE.bracket openCabalTemp deleteIfExists $ \tempLog -> do+ -- Run 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+ -- 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+ let (cmd', opts') = case testWrapper flags of+ Flag path -> (path, cmd : opts)+ NoFlag -> (cmd, opts) - -- Run 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- -- 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- 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 - -- 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+ -- Append contents of temporary log file to the final human-+ -- readable log file+ logText <- LBS.hGetContents rOut+ -- Force the IO manager to drain the test output pipe+ _ <- evaluate (force logText)+ return logText+ unless (exitcode == ExitSuccess) $+ debug verbosity $+ cmd ++ " returned " ++ show exitcode - -- Append contents of temporary log file to the final human-- -- readable log file- logText <- LBS.hGetContents rOut- -- Force the IO manager to drain the test output pipe- _ <- evaluate (force logText)- 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 = 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 $ 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 - when isCoverageEnabled $- case PD.library pkg_descr of- Nothing ->- die' verbosity "Test coverage is only supported for packages with a library component."- 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 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" openCabalTemp = do- (f, h) <- openTempFile testLogDir $ "cabal-test-" <.> "log"- hClose h >> return f+ (f, h) <- openTempFile testLogDir $ "cabal-test-" <.> "log"+ hClose h >> return f distPref = fromFlag $ testDistPref flags verbosity = fromFlag $ testVerbosity flags -- 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 ---------- @@ -206,22 +244,26 @@ 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 )"@@ -234,18 +276,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@@ -256,35 +302,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
@@ -2,6 +2,7 @@ {-# LANGUAGE RankNTypes #-} -----------------------------------------------------------------------------+ -- | -- Module : Distribution.Simple.UHC -- Copyright : Andres Loeh 2009@@ -16,57 +17,67 @@ -- 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 -- ----------------------------------------------------------------------------- -- Configuring -configure :: Verbosity -> Maybe FilePath -> Maybe FilePath- -> ProgramDb -> IO (Compiler, Maybe Platform, ProgramDb)+configure+ :: Verbosity+ -> Maybe FilePath+ -> Maybe FilePath+ -> ProgramDb+ -> IO (Compiler, Maybe Platform, ProgramDb) configure verbosity hcPath _hcPkgPath 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,78 @@ -- | 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+ -> Compiler+ -> PackageDBStack+ -> ProgramDb+ -> IO InstalledPackageIndex getInstalledPackages verbosity comp 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) 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 = 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 = case db of- GlobalPackageDB -> [ system ]- UserPackageDB -> [ user ]- SpecificPackageDB path -> [ path ]+ GlobalPackageDB -> [system]+ UserPackageDB -> [user]+ SpecificPackageDB path -> [path] -- | Hack to add version numbers to UHC-built-in packages. This should sooner or -- later be fixed on the UHC side.@@ -143,7 +168,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,121 +178,164 @@ -- 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+ 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))+ 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+ 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]+ 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] runUhcProg uhcArgs -constructUHCCmdLine :: FilePath -> FilePath- -> LocalBuildInfo -> BuildInfo -> ComponentLocalBuildInfo- -> FilePath -> Verbosity -> [String]+constructUHCCmdLine+ :: FilePath+ -> FilePath+ -> LocalBuildInfo+ -> BuildInfo+ -> ComponentLocalBuildInfo+ -> FilePath+ -> 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" ++ 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"]+ ) 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) 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@@ -280,16 +348,17 @@ -> 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)+ 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) where- pkgid = sourcePackageId installedPkgInfo+ pkgid = sourcePackageId installedPkgInfo compilerid = compilerId comp inplacePackageDbPath :: LocalBuildInfo -> FilePath
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 Prelude () import Distribution.Compat.Prelude+import 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,1621 +1,1870 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE GADTs #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE FlexibleContexts #-}-{-# LANGUAGE RankNTypes #-}-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE LambdaCase #-}---------------------------------------------------------------------------------- |--- 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,- withOutputMarker,-- -- * exceptions- handleDoesNotExist,- ignoreSigPipe,-- -- * running programs- rawSystemExit,- rawSystemExitCode,- rawSystemProc,- rawSystemProcAction,- rawSystemExitWithEnv,- 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 (..),-- -- * 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,- ) 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 (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.Stack-import Distribution.Verbosity-import Distribution.Types.PackageId--#ifdef CURRENT_PACKAGE_KEY-#define BOOTSTRAPPED_CABAL 1-#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 (proc)-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 =- 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--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 = withFrozenCallStack $ do- when ((verbosity >= normal) && not (isVerboseNoWarn verbosity)) $ 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------ 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 -> FilePath -> [String] -> IO ()-rawSystemExit verbosity path args = withFrozenCallStack $- maybeExit $ rawSystemExitCode verbosity path args---- | Execute the given command with the given arguments, returning--- the command's exit code.----rawSystemExitCode :: Verbosity -> FilePath -> [String] -> IO ExitCode-rawSystemExitCode verbosity path args = withFrozenCallStack $- rawSystemProc verbosity $ proc path args---- | 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 path args env = withFrozenCallStack $- maybeExit $ rawSystemProc verbosity $- (proc path args) { Process.env = Just env- }---- | 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) $- die' verbosity 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 "")- `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- :: 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)----- | 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---- | 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 $- "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 (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".- (_, Windows) -> ["", "exe"]- (_, Ghcjs) -> ["", "exe"]- (Wasm32, _) -> ["", "wasm"]- _ -> [""]---- --------------------------------------------------------------- * 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+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE InstanceSigs #-}+{-# LANGUAGE LambdaCase #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}++-----------------------------------------------------------------------------++-- |+-- 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'+ , 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+ , 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++ -- * 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+ , 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.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+ , removeDirectoryRecursive+ , removeFile+ )+import System.Environment+ ( getProgName+ )+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++-- 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 =+ 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 -expections and cabal-install exceptions+data VerboseException a = VerboseException CallStack POSIXTime Verbosity a+ deriving (Show, Typeable)++-- 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 @Waring: …@ 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 -> FilePath -> [String] -> IO ()+rawSystemExit verbosity path args =+ withFrozenCallStack $+ maybeExit $+ rawSystemExitCode verbosity path args++-- | Execute the given command with the given arguments, returning+-- the command's exit code.+rawSystemExitCode :: Verbosity -> FilePath -> [String] -> IO ExitCode+rawSystemExitCode verbosity path args =+ withFrozenCallStack $+ rawSystemProc verbosity $+ proc path args++-- | 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 path args env =+ withFrozenCallStack $+ maybeExit $+ rawSystemProc verbosity $+ (proc path args)+ { Process.env = Just env+ }++-- | 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+ :: Verbosity+ -> FilePath+ -- ^ cwd+ -> [FilePath]+ -- ^ relative search location+ -> FilePath+ -- ^ File Name+ -> IO FilePath+findFileCwd verbosity cwd searchPath fileName =+ findFirstFile+ (cwd </>)+ [ path </> fileName+ | path <- ordNub searchPath+ ]+ >>= maybe (dieWithException verbosity $ FindFileCwd fileName) 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 <- ordNub searchPath+ ]+ >>= maybe (dieWithException verbosity $ FindFileEx fileName) 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+ :: [Suffix]+ -> [FilePath]+ -> FilePath+ -> IO (Maybe FilePath)+findFileWithExtension extensions searchPath baseName =+ findFirstFile+ id+ [ path </> baseName <.> ext+ | path <- ordNub searchPath+ , Suffix ext <- ordNub extensions+ ]++-- | @since 3.4.0.0+findFileCwdWithExtension+ :: FilePath+ -> [Suffix]+ -> [FilePath]+ -> FilePath+ -> IO (Maybe FilePath)+findFileCwdWithExtension cwd extensions searchPath baseName =+ findFirstFile+ (cwd </>)+ [ path </> baseName <.> ext+ | path <- ordNub searchPath+ , Suffix ext <- ordNub extensions+ ]++-- | @since 3.4.0.0+findAllFilesCwdWithExtension+ :: FilePath+ -- ^ cwd+ -> [Suffix]+ -- ^ extensions+ -> [FilePath]+ -- ^ relative search locations+ -> FilePath+ -- ^ basename+ -> IO [FilePath]+findAllFilesCwdWithExtension cwd extensions searchPath basename =+ findAllFiles+ (cwd </>)+ [ path </> basename <.> ext+ | path <- ordNub searchPath+ , Suffix ext <- ordNub extensions+ ]++findAllFilesWithExtension+ :: [Suffix]+ -> [FilePath]+ -> FilePath+ -> IO [FilePath]+findAllFilesWithExtension extensions searchPath basename =+ findAllFiles+ id+ [ path </> basename <.> ext+ | path <- ordNub searchPath+ , Suffix ext <- ordNub 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'+ :: [Suffix]+ -> [FilePath]+ -> FilePath+ -> IO (Maybe (FilePath, FilePath))+findFileWithExtension' extensions searchPath baseName =+ findFirstFile+ (uncurry (</>))+ [ (path, baseName <.> ext)+ | path <- ordNub searchPath+ , Suffix ext <- ordNub 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)++-- | 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)+ -> [Suffix]+ -- ^ search suffixes+ -> [ModuleName]+ -- ^ modules+ -> IO [(FilePath, FilePath)]+findModuleFilesEx verbosity searchPath extensions moduleNames =+ traverse (findModuleFileEx verbosity 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+ :: Verbosity+ -> [FilePath]+ -- ^ build prefix (location of objects)+ -> [Suffix]+ -- ^ 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 =+ dieWithException verbosity $ FindModuleFileEx mod_name extensions 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 </>) . 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+ :: 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 (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".+ (_, Windows) -> ["", "exe"]+ (_, Ghcjs) -> ["", "exe"]+ (Wasm32, _) -> ["", "wasm"]+ _ -> [""]++-- ------------------------------------------------------------++-- * 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 CabalException FilePath)+ -- ^ <pkgname>.cabal+findPackageDesc = findPackageDescCwd "."++-- | @since 3.4.0.0+findPackageDescCwd+ :: FilePath+ -- ^ project root+ -> FilePath+ -- ^ relative directory+ -> IO (Either CabalException 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)++-- | Like 'findPackageDesc', but calls 'die' in case of error.+tryFindPackageDesc :: Verbosity -> FilePath -> IO FilePath+tryFindPackageDesc verbosity dir =+ either (dieWithException 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 (dieWithException 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)+ _ -> dieWithException verbosity $ MultipleFilesWithExtension buildInfoExt++buildInfoExt :: String buildInfoExt = ".buildinfo"
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/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/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
@@ -2,128 +2,127 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE TypeFamilies #-} -module Distribution.Types.ComponentLocalBuildInfo (- ComponentLocalBuildInfo(..),- componentIsIndefinite,- maybeComponentInstantiatedWith,+module Distribution.Types.ComponentLocalBuildInfo+ ( ComponentLocalBuildInfo (..)+ , componentIsIndefinite+ , maybeComponentInstantiatedWith ) 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]- }+ = 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, Typeable) 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
src/Distribution/Types/DumpBuildInfo.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-}+ module Distribution.Types.DumpBuildInfo- ( DumpBuildInfo(..)- ) where+ ( DumpBuildInfo (..)+ ) where import Distribution.Compat.Prelude
src/Distribution/Types/GivenComponent.hs view
@@ -1,9 +1,10 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE DeriveGeneric #-}-module Distribution.Types.GivenComponent (- GivenComponent(..)-) where +module Distribution.Types.GivenComponent+ ( GivenComponent (..)+ ) where+ import Distribution.Compat.Prelude import Distribution.Types.ComponentId@@ -16,12 +17,12 @@ -- 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 }+data GivenComponent = GivenComponent+ { givenComponentPackage :: PackageName+ , givenComponentName :: LibraryName -- --dependency is for libraries+ -- only, not for any component+ , givenComponentId :: ComponentId+ } deriving (Generic, Read, Show, Eq, Typeable) instance Binary GivenComponent
+ src/Distribution/Types/LocalBuildConfig.hs view
@@ -0,0 +1,226 @@+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DuplicateRecordFields #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE PatternSynonyms #-}+{-# 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.ComponentId+import Distribution.Types.ComponentLocalBuildInfo+import Distribution.Types.ComponentRequestedSpec+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+import Distribution.Simple.Setup.Config+import Distribution.System++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 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.+ , 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) ComponentId+ -- ^ 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 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+ , 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,299 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# 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+ , withSharedLib+ , withStaticLib+ , withDynExe+ , withFullyStaticExe+ , withProfExe+ , 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+ , configFlagsBuildDir+ , cabalFilePath+ , 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 -- * 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 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.PackageDescription+import Distribution.Pretty 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.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 System.FilePath ((</>)) -- | 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, Typeable) +{-# 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 FilePath+ -> Graph ComponentLocalBuildInfo+ -> Map ComponentName [ComponentLocalBuildInfo]+ -> Map (PackageName, ComponentName) ComponentId+ -> InstalledPackageIndex+ -> PackageDescription+ -> ProgramDb+ -> PackageDBStack+ -> 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+ , 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+ , 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 -> FilePath+buildDir lbi =+ buildDirPBD $ LBC.packageBuildDescr $ localBuildDescr lbi++buildDirPBD :: LBC.PackageBuildDescr -> FilePath+buildDirPBD (LBC.PackageBuildDescr{configFlags = cfg}) =+ configFlagsBuildDir cfg++configFlagsBuildDir :: ConfigFlags -> FilePath+configFlagsBuildDir cfg = fromFlag (configDistPref cfg) </> "build"++cabalFilePath :: LocalBuildInfo -> Maybe FilePath+cabalFilePath (LocalBuildInfo{configFlags = cfg}) =+ flagToMaybe (configCabalFilePath 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 +302,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 +377,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 +403,34 @@ -- 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 ------------------------------------------------------------------------------- -- 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 '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/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/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/Utils/IOData.hs view
@@ -1,78 +1,82 @@ {-# LANGUAGE GADTs #-}-{-# LANGUAGE TypeOperators #-} {-# 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' --@@ -84,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
@@ -1,55 +1,60 @@-{-# LANGUAGE RankNTypes #-} {-# LANGUAGE OverloadedStrings #-}--- | Extremely simple JSON helper. Don't do anything too fancy with this!+{-# LANGUAGE RankNTypes #-} +-- | Extremely simple JSON helper. Don't do anything too fancy with this! module Distribution.Utils.Json- ( Json(..)- , (.=)- , renderJson- ) where+ ( Json (..)+ , (.=)+ , renderJson+ ) where -import Distribution.Compat.Prelude-import qualified Data.ByteString.Lazy as LBS import Data.ByteString.Builder- ( Builder, stringUtf8, intDec, toLazyByteString )+ ( 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+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) =+ 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 (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)+ 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]+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+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 ]
src/Distribution/Utils/LogProgress.hs view
@@ -1,83 +1,85 @@+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE Rank2Types #-}-{-# LANGUAGE FlexibleContexts #-}-module Distribution.Utils.LogProgress (- LogProgress,- runLogProgress,- warnProgress,- infoProgress,- dieProgress,- addProgressCtx,-) where -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 +88,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/MapAccum.hs view
@@ -1,22 +1,28 @@ {-# 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 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')+ 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 :: (Monad m, Traversable t)- => (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,17 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE ScopedTypeVariables #-}-module Distribution.Utils.NubList- ( NubList -- opaque- , toNubList -- smart constructor- , 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 +21,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, Typeable) -- NubList assumes that nub retains the list order while removing duplicate -- elements (keeping the first occurrence). Documentation for "Data.List.nub"@@ -51,30 +50,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 +80,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 +102,4 @@ show (NubListR list) = show list instance (Ord a, Read a) => Read (NubListR a) where- readPrec = readNubList toNubListR+ readPrec = readNubList toNubListR
src/Distribution/Utils/Progress.hs view
@@ -1,22 +1,22 @@ {-# 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 +25,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 +45,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/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
@@ -2,6 +2,9 @@ {-# LANGUAGE DeriveGeneric #-} -----------------------------------------------------------------------------++-- Verbosity for Cabal functions.+ -- | -- Module : Distribution.Verbosity -- Copyright : Ian Lynagh 2007@@ -21,79 +24,92 @@ -- 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,+ -- * Time stamps+ , verboseTimestamp+ , isVerboseTimestamp+ , verboseNoTimestamp - -- * Stderr- verboseStderr, isVerboseStderr,- verboseNoStderr,+ -- * Stderr+ , verboseStderr+ , isVerboseStderr+ , verboseNoStderr - -- * No warnings- verboseNoWarn, isVerboseNoWarn+ -- * 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, Typeable) 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@@ -118,21 +134,21 @@ -- | 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.@@ -146,7 +162,7 @@ -- -- @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@@ -177,81 +193,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- "nowarn" -> return verboseNoWarn- _ -> 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 VNoWarn = ["+nowarn"]+ 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@@ -276,7 +293,7 @@ -- | 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@@ -304,15 +321,15 @@ -- | 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. 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. verboseNoFlags :: Verbosity -> Verbosity-verboseNoFlags v = v { vFlags = Set.empty }+verboseNoFlags v = v{vFlags = Set.empty} verboseHasFlags :: Verbosity -> Bool verboseHasFlags = not . Set.null . vFlags
src/Distribution/Verbosity/Internal.hs view
@@ -1,28 +1,30 @@ {-# 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, Typeable) instance Binary VerbosityLevel instance Structured VerbosityLevel data VerbosityFlag- = VCallStack- | VCallSite- | VNoWrap- | VMarkOutput- | VTimestamp- | VStderr -- ^ @since 3.4.0.0- | VNoWarn- 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, Typeable) instance Binary VerbosityFlag instance Structured VerbosityFlag
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) ""