------------------------------------------------------------------------------- |-- 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 check so we can see which ones are appropriate to report-- in different situations. This code gets uses when configuring a package when-- we consider only basic problems. The higher standard is uses when 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.moduleDistribution.PackageDescription.Check(-- * Package CheckingPackageCheck(..),checkPackage,checkConfiguredPackage,-- ** Checking package contentscheckPackageFiles,checkPackageContent,CheckPackageContentOps(..),checkPackageFileNames,)whereimportDistribution.Compat.PreludeimportPrelude()importControl.Monad(mapM)importData.List(group)importDistribution.Compat.LensimportDistribution.CompilerimportDistribution.LicenseimportDistribution.PackageimportDistribution.PackageDescriptionimportDistribution.PackageDescription.ConfigurationimportDistribution.Pretty(prettyShow)importDistribution.Simple.BuildPaths(autogenPathsModuleName)importDistribution.Simple.BuildToolDependsimportDistribution.Simple.CCompilerimportDistribution.Simple.GlobimportDistribution.Simple.Utilshiding(findPackageDesc,notice)importDistribution.SystemimportDistribution.TextimportDistribution.Types.ComponentRequestedSpecimportDistribution.Types.CondTreeimportDistribution.Types.ExeDependencyimportDistribution.Types.UnqualComponentNameimportDistribution.Utils.Generic(isAscii)importDistribution.VerbosityimportDistribution.VersionimportLanguage.Haskell.ExtensionimportSystem.FilePath(splitDirectories,splitExtension,splitPath,takeExtension,takeFileName,(<.>),(</>))importqualifiedData.ByteString.LazyasBSimportqualifiedData.MapasMapimportqualifiedDistribution.Compat.DListasDListimportqualifiedDistribution.SPDXasSPDXimportqualifiedSystem.DirectoryasSystemimportqualifiedSystem.Directory(getDirectoryContents)importqualifiedSystem.FilePath.WindowsasFilePath.Windows(isValid)importqualifiedData.SetasSetimportqualifiedDistribution.Types.BuildInfo.LensasLimportqualifiedDistribution.Types.GenericPackageDescription.LensasLimportqualifiedDistribution.Types.PackageDescription.LensasL-- | 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.--dataPackageCheck=-- | This package description is no good. There's no way it's going to-- build sensibly. This should give an error at configure time.PackageBuildImpossible{explanation::String}-- | A problem that is likely to affect building the package, or an-- issue that we'd like every package author to be aware of, even if-- the package is never distributed.|PackageBuildWarning{explanation::String}-- | An issue that might not be a problem for the package author but-- might be annoying or detrimental when the package is distributed to-- users. We should encourage distributed packages to be free from these-- issues, but occasionally there are justifiable reasons so we cannot-- ban them entirely.|PackageDistSuspicious{explanation::String}-- | Like PackageDistSuspicious but will only display warnings-- rather than causing abnormal exit when you run 'cabal check'.|PackageDistSuspiciousWarn{explanation::String}-- | An issue that is OK in the author's environment but is almost-- certain to be a portability problem for other environments. We can-- quite legitimately refuse to publicly distribute packages with these-- problems.|PackageDistInexcusable{explanation::String}deriving(Eq,Ord)instanceShowPackageCheckwhereshownotice=explanationnoticecheck::Bool->PackageCheck->MaybePackageCheckcheckFalse_=NothingcheckTruepc=JustpccheckSpecVersion::PackageDescription->[Int]->Bool->PackageCheck->MaybePackageCheckcheckSpecVersionpkgspecvercondpc|specVersionpkg>=mkVersionspecver=Nothing|otherwise=checkcondpc-- -------------------------------------------------------------- * 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->MaybePackageDescription->[PackageCheck]checkPackagegpkgmpkg=checkConfiguredPackagepkg++checkConditionalsgpkg++checkPackageVersionsgpkg++checkDevelopmentOnlyFlagsgpkg++checkFlagNamesgpkg++checkUnusedFlagsgpkg++checkUnicodeXFieldsgpkg++checkPathsModuleExtensionspkgwherepkg=fromMaybe(flattenPackageDescriptiongpkg)mpkg--TODO: make this variant go away-- we should always know the GenericPackageDescriptioncheckConfiguredPackage::PackageDescription->[PackageCheck]checkConfiguredPackagepkg=checkSanitypkg++checkFieldspkg++checkLicensepkg++checkSourceRepospkg++checkGhcOptionspkg++checkCCOptionspkg++checkCxxOptionspkg++checkCPPOptionspkg++checkPathspkg++checkCabalVersionpkg-- -------------------------------------------------------------- * Basic sanity checks-- -------------------------------------------------------------- | Check that this package description is sane.--checkSanity::PackageDescription->[PackageCheck]checkSanitypkg=catMaybes[check(null.unPackageName.packageName$pkg)$PackageBuildImpossible"No 'name' field.",check(nullVersion==packageVersionpkg)$PackageBuildImpossible"No 'version' field.",check(all($pkg)[null.executables,null.testSuites,null.benchmarks,null.allLibraries,null.foreignLibs])$PackageBuildImpossible"No executables, libraries, tests, or benchmarks found. Nothing to do.",check(anyisNothing(maplibName$subLibrariespkg))$PackageBuildImpossible$"Found one or more unnamed internal libraries. "++"Only the non-internal library can have the same name as the package.",check(not(nullduplicateNames))$PackageBuildImpossible$"Duplicate sections: "++commaSep(mapunUnqualComponentNameduplicateNames)++". The name of every library, executable, test suite,"++" and benchmark section in"++" the package must be unique."-- NB: but it's OK for executables to have the same name!-- TODO shouldn't need to compare on the string level,check(any(==display(packageNamepkg))(display<$>subLibNames))$PackageBuildImpossible$"Illegal internal library name "++display(packageNamepkg)++". 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: '"++display(packageNamepkg)++"' to 'library'."]--TODO: check for name clashes case insensitively: windows file systems cannot--cope.++concatMap(checkLibrarypkg)(allLibrariespkg)++concatMap(checkExecutablepkg)(executablespkg)++concatMap(checkTestSuitepkg)(testSuitespkg)++concatMap(checkBenchmarkpkg)(benchmarkspkg)++catMaybes[check(specVersionpkg>cabalVersion)$PackageBuildImpossible$"This package description follows version "++display(specVersionpkg)++" of the Cabal specification. This "++"tool only supports up to version "++displaycabalVersion++"."]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=catMaybes.maplibName$subLibrariespkgexeNames=mapexeName$executablespkgtestNames=maptestName$testSuitespkgbmNames=mapbenchmarkName$benchmarkspkgduplicateNames=dups$subLibNames++exeNames++testNames++bmNamescheckLibrary::PackageDescription->Library->[PackageCheck]checkLibrarypkglib=catMaybes[check(not(nullmoduleDuplicates))$PackageBuildImpossible$"Duplicate modules in library: "++commaSep(mapdisplaymoduleDuplicates)-- TODO: This check is bogus if a required-signature was passed through,check(null(explicitLibModuleslib)&&null(reexportedModuleslib))$PackageDistSuspiciousWarn$"Library "++(caselibNamelibofNothing->""Justn->displayn)++"does not expose any modules"-- check use of signatures sections,checkVersion[1,25](not(null(signatureslib)))$PackageDistInexcusable$"To use the 'signatures' field the package needs to specify "++"at least 'cabal-version: 2.0'."-- check that all autogen-modules appear on other-modules or exposed-modules,check(not$and$map(flipelem(explicitLibModuleslib))(libModulesAutogenlib))$PackageBuildImpossible$"An 'autogen-module' is neither on 'exposed-modules' or "++"'other-modules'."]wherecheckVersion::[Int]->Bool->PackageCheck->MaybePackageCheckcheckVersionvercondpc|specVersionpkg>=mkVersionver=Nothing|otherwise=checkcondpc-- TODO: not sure if this check is always right in BackpackmoduleDuplicates=dups(explicitLibModuleslib++mapmoduleReexportName(reexportedModuleslib))checkExecutable::PackageDescription->Executable->[PackageCheck]checkExecutablepkgexe=catMaybes[check(null(modulePathexe))$PackageBuildImpossible$"No 'main-is' field found for executable "++display(exeNameexe),check(not(null(modulePathexe))&&(not$fileExtensionSupportedLanguage$modulePathexe))$PackageBuildImpossible$"The 'main-is' field must specify a '.hs' or '.lhs' file "++"(even if it is generated by a preprocessor), "++"or it may specify a C/C++/obj-C source file.",checkSpecVersionpkg[1,17](fileExtensionSupportedLanguage(modulePathexe)&&takeExtension(modulePathexe)`notElem`[".hs",".lhs"])$PackageDistInexcusable$"The package uses a C/C++/obj-C source file for the 'main-is' field. "++"To use this feature you must specify 'cabal-version: >= 1.18'.",check(not(nullmoduleDuplicates))$PackageBuildImpossible$"Duplicate modules in executable '"++display(exeNameexe)++"': "++commaSep(mapdisplaymoduleDuplicates)-- check that all autogen-modules appear on other-modules,check(not$and$map(flipelem(exeModulesexe))(exeModulesAutogenexe))$PackageBuildImpossible$"On executable '"++display(exeNameexe)++"' an 'autogen-module' is not "++"on 'other-modules'"]wheremoduleDuplicates=dups(exeModulesexe)checkTestSuite::PackageDescription->TestSuite->[PackageCheck]checkTestSuitepkgtest=catMaybes[casetestInterfacetestofTestSuiteUnsupportedtt@(TestTypeUnknown__)->Just$PackageBuildWarning$quote(displaytt)++" is not a known type of test suite. "++"The known test suite types are: "++commaSep(mapdisplayknownTestTypes)TestSuiteUnsupportedtt->Just$PackageBuildWarning$quote(displaytt)++" is not a supported test suite version. "++"The known test suite types are: "++commaSep(mapdisplayknownTestTypes)_->Nothing,check(not$nullmoduleDuplicates)$PackageBuildImpossible$"Duplicate modules in test suite '"++display(testNametest)++"': "++commaSep(mapdisplaymoduleDuplicates),checkmainIsWrongExt$PackageBuildImpossible$"The 'main-is' field must specify a '.hs' or '.lhs' file "++"(even if it is generated by a preprocessor), "++"or it may specify a C/C++/obj-C source file.",checkSpecVersionpkg[1,17](mainIsNotHsExt&&notmainIsWrongExt)$PackageDistInexcusable$"The package uses a C/C++/obj-C source file for the 'main-is' field. "++"To use this feature you must specify 'cabal-version: >= 1.18'."-- check that all autogen-modules appear on other-modules,check(not$and$map(flipelem(testModulestest))(testModulesAutogentest))$PackageBuildImpossible$"On test suite '"++display(testNametest)++"' an 'autogen-module' is not "++"on 'other-modules'"]wheremoduleDuplicates=dups$testModulestestmainIsWrongExt=casetestInterfacetestofTestSuiteExeV10_f->not$fileExtensionSupportedLanguagef_->FalsemainIsNotHsExt=casetestInterfacetestofTestSuiteExeV10_f->takeExtensionf`notElem`[".hs",".lhs"]_->FalsecheckBenchmark::PackageDescription->Benchmark->[PackageCheck]checkBenchmark_pkgbm=catMaybes[casebenchmarkInterfacebmofBenchmarkUnsupportedtt@(BenchmarkTypeUnknown__)->Just$PackageBuildWarning$quote(displaytt)++" is not a known type of benchmark. "++"The known benchmark types are: "++commaSep(mapdisplayknownBenchmarkTypes)BenchmarkUnsupportedtt->Just$PackageBuildWarning$quote(displaytt)++" is not a supported benchmark version. "++"The known benchmark types are: "++commaSep(mapdisplayknownBenchmarkTypes)_->Nothing,check(not$nullmoduleDuplicates)$PackageBuildImpossible$"Duplicate modules in benchmark '"++display(benchmarkNamebm)++"': "++commaSep(mapdisplaymoduleDuplicates),checkmainIsWrongExt$PackageBuildImpossible$"The 'main-is' field must specify a '.hs' or '.lhs' file "++"(even if it is generated by a preprocessor)."-- check that all autogen-modules appear on other-modules,check(not$and$map(flipelem(benchmarkModulesbm))(benchmarkModulesAutogenbm))$PackageBuildImpossible$"On benchmark '"++display(benchmarkNamebm)++"' an 'autogen-module' is "++"not on 'other-modules'"]wheremoduleDuplicates=dups$benchmarkModulesbmmainIsWrongExt=casebenchmarkInterfacebmofBenchmarkExeV10_f->takeExtensionf`notElem`[".hs",".lhs"]_->False-- -------------------------------------------------------------- * Additional pure checks-- ------------------------------------------------------------checkFields::PackageDescription->[PackageCheck]checkFieldspkg=catMaybes[check(not.FilePath.Windows.isValid.display.packageName$pkg)$PackageDistInexcusable$"Unfortunately, the package name '"++display(packageNamepkg)++"' is one of the reserved system file names on Windows. Many tools "++"need to convert package names to file names so using this name "++"would cause problems.",check((isPrefixOf"z-").display.packageName$pkg)$PackageDistInexcusable$"Package names with the prefix 'z-' are reserved by Cabal and "++"cannot be used.",check(isNothing(buildTypeRawpkg)&&specVersionpkg<mkVersion[2,1])$PackageBuildWarning$"No 'build-type' specified. If you do not need a custom Setup.hs or "++"./configure script then use 'build-type: Simple'.",check(isJust(setupBuildInfopkg)&&buildTypepkg/=Custom)$PackageBuildWarning$"Ignoring the 'custom-setup' section because the 'build-type' is "++"not 'Custom'. Use 'build-type: Custom' if you need to use a "++"custom Setup.hs script.",check(not(nullunknownCompilers))$PackageBuildWarning$"Unknown compiler "++commaSep(mapquoteunknownCompilers)++" in 'tested-with' field.",check(not(nullunknownLanguages))$PackageBuildWarning$"Unknown languages: "++commaSepunknownLanguages,check(not(nullunknownExtensions))$PackageBuildWarning$"Unknown extensions: "++commaSepunknownExtensions,check(not(nulllanguagesUsedAsExtensions))$PackageBuildWarning$"Languages listed as extensions: "++commaSeplanguagesUsedAsExtensions++". Languages must be specified in either the 'default-language' "++" or the 'other-languages' field.",check(not(nullourDeprecatedExtensions))$PackageDistSuspicious$"Deprecated extensions: "++commaSep(map(quote.display.fst)ourDeprecatedExtensions)++". "++unwords["Instead of '"++displayext++"' use '"++displayreplacement++"'."|(ext,Justreplacement)<-ourDeprecatedExtensions],check(null(categorypkg))$PackageDistSuspicious"No 'category' field.",check(null(maintainerpkg))$PackageDistSuspicious"No 'maintainer' field.",check(null(synopsispkg)&&null(descriptionpkg))$PackageDistInexcusable"No 'synopsis' or 'description' field.",check(null(descriptionpkg)&&not(null(synopsispkg)))$PackageDistSuspicious"No 'description' field.",check(null(synopsispkg)&&not(null(descriptionpkg)))$PackageDistSuspicious"No 'synopsis' field."--TODO: recommend the bug reports URL, author and homepage fields--TODO: recommend not using the stability field--TODO: recommend specifying a source repo,check(length(synopsispkg)>=80)$PackageDistSuspicious"The 'synopsis' field is rather long (max 80 chars is recommended)."-- See also https://github.com/haskell/cabal/pull/3479,check(not(null(descriptionpkg))&&length(descriptionpkg)<=length(synopsispkg))$PackageDistSuspicious$"The 'description' field should be longer than the 'synopsis' "++"field. "++"It's useful to provide an informative 'description' to allow "++"Haskell programmers who have never heard about your package to "++"understand the purpose of your package. "++"The 'description' field content is typically shown by tooling "++"(e.g. 'cabal info', Haddock, Hackage) below the 'synopsis' which "++"serves as a headline. "++"Please refer to <https://www.haskell.org/"++"cabal/users-guide/developing-packages.html#package-properties>"++" for more details."-- check use of impossible constraints "tested-with: GHC== 6.10 && ==6.12",check(not(nulltestedWithImpossibleRanges))$PackageDistInexcusable$"Invalid 'tested-with' version range: "++commaSep(mapdisplaytestedWithImpossibleRanges)++". 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'."-- Disabled due to #5119: we generate loads of spurious instances of-- this warning. Re-enabling this check should be part of the fix to-- #5119.,check(False&&not(nulldepInternalLibraryWithExtraVersion))$PackageBuildWarning$"The package has an extraneous version range for a dependency on an "++"internal library: "++commaSep(mapdisplaydepInternalLibraryWithExtraVersion)++". This version range includes the current package but isn't needed "++"as the current package's library will always be used.",check(not(nulldepInternalLibraryWithImpossibleVersion))$PackageBuildImpossible$"The package has an impossible version range for a dependency on an "++"internal library: "++commaSep(mapdisplaydepInternalLibraryWithImpossibleVersion)++". This version range does not include the current package, and must "++"be removed as the current package's library will always be used.",check(not(nulldepInternalExecutableWithExtraVersion))$PackageBuildWarning$"The package has an extraneous version range for a dependency on an "++"internal executable: "++commaSep(mapdisplaydepInternalExecutableWithExtraVersion)++". This version range includes the current package but isn't needed "++"as the current package's executable will always be used.",check(not(nulldepInternalExecutableWithImpossibleVersion))$PackageBuildImpossible$"The package has an impossible version range for a dependency on an "++"internal executable: "++commaSep(mapdisplaydepInternalExecutableWithImpossibleVersion)++". This version range does not include the current package, and must "++"be removed as the current package's executable will always be used.",check(not(nulldepMissingInternalExecutable))$PackageBuildImpossible$"The package depends on a missing internal executable: "++commaSep(mapdisplaydepInternalExecutableWithImpossibleVersion)]whereunknownCompilers=[name|(OtherCompilername,_)<-testedWithpkg]unknownLanguages=[name|bi<-allBuildInfopkg,UnknownLanguagename<-allLanguagesbi]unknownExtensions=[name|bi<-allBuildInfopkg,UnknownExtensionname<-allExtensionsbi,name`notElem`mapdisplayknownLanguages]ourDeprecatedExtensions=nub$catMaybes[find((==ext).fst)deprecatedExtensions|bi<-allBuildInfopkg,ext<-allExtensionsbi]languagesUsedAsExtensions=[name|bi<-allBuildInfopkg,UnknownExtensionname<-allExtensionsbi,name`elem`mapdisplayknownLanguages]testedWithImpossibleRanges=[Dependency(mkPackageName(displaycompiler))vr|(compiler,vr)<-testedWithpkg,isNoVersionvr]internalLibraries=map(maybe(packageNamepkg)(unqualComponentNameToPackageName).libName)(allLibrariespkg)internalExecutables=mapexeName$executablespkginternalLibDeps=[dep|bi<-allBuildInfopkg,dep@(Dependencyname_)<-targetBuildDependsbi,name`elem`internalLibraries]internalExeDeps=[dep|bi<-allBuildInfopkg,dep<-getAllToolDependenciespkgbi,isInternalpkgdep]depInternalLibraryWithExtraVersion=[dep|dep@(Dependency_versionRange)<-internalLibDeps,not$isAnyVersionversionRange,packageVersionpkg`withinRange`versionRange]depInternalLibraryWithImpossibleVersion=[dep|dep@(Dependency_versionRange)<-internalLibDeps,not$packageVersionpkg`withinRange`versionRange]depInternalExecutableWithExtraVersion=[dep|dep@(ExeDependency__versionRange)<-internalExeDeps,not$isAnyVersionversionRange,packageVersionpkg`withinRange`versionRange]depInternalExecutableWithImpossibleVersion=[dep|dep@(ExeDependency__versionRange)<-internalExeDeps,not$packageVersionpkg`withinRange`versionRange]depMissingInternalExecutable=[dep|dep@(ExeDependency_eName_)<-internalExeDeps,not$eName`elem`internalExecutables]checkLicense::PackageDescription->[PackageCheck]checkLicensepkg=caselicenseRawpkgofRightl->checkOldLicensepkglLeftl->checkNewLicensepkglcheckNewLicense::PackageDescription->SPDX.License->[PackageCheck]checkNewLicense_pkglic=catMaybes[check(lic==SPDX.NONE)$PackageDistInexcusable"The 'license' field is missing or is NONE."]checkOldLicense::PackageDescription->License->[PackageCheck]checkOldLicensepkglic=catMaybes[check(lic==UnspecifiedLicense)$PackageDistInexcusable"The 'license' field is missing.",check(lic==AllRightsReserved)$PackageDistSuspicious"The 'license' is AllRightsReserved. Is that really what you want?",checkVersion[1,4](lic`notElem`compatLicenses)$PackageDistInexcusable$"Unfortunately the license "++quote(prettyShow(licensepkg))++" 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'.",caselicofUnknownLicensel->Just$PackageBuildWarning$quote("license: "++l)++" is not a recognised license. The "++"known licenses are: "++commaSep(mapdisplayknownLicenses)_->Nothing,check(lic==BSD4)$PackageDistSuspicious$"Using 'license: BSD4' is almost always a misunderstanding. 'BSD4' "++"refers to the old 4-clause BSD license with the advertising "++"clause. 'BSD3' refers the new 3-clause BSD license.",caseunknownLicenseVersion(lic)ofJustknownVersions->Just$PackageDistSuspicious$"'license: "++display(lic)++"' is not a known "++"version of that license. The known versions are "++commaSep(mapdisplayknownVersions)++". If this is not a mistake and you think it should be a known "++"version then please file a ticket."_->Nothing,check(lic`notElem`[AllRightsReserved,UnspecifiedLicense,PublicDomain]-- AllRightsReserved and PublicDomain are not strictly-- licenses so don't need license files.&&null(licenseFilespkg))$PackageDistSuspicious"A 'license-file' is not specified."]whereunknownLicenseVersion(GPL(Justv))|v`notElem`knownVersions=JustknownVersionswhereknownVersions=[v'|GPL(Justv')<-knownLicenses]unknownLicenseVersion(LGPL(Justv))|v`notElem`knownVersions=JustknownVersionswhereknownVersions=[v'|LGPL(Justv')<-knownLicenses]unknownLicenseVersion(AGPL(Justv))|v`notElem`knownVersions=JustknownVersionswhereknownVersions=[v'|AGPL(Justv')<-knownLicenses]unknownLicenseVersion(Apache(Justv))|v`notElem`knownVersions=JustknownVersionswhereknownVersions=[v'|Apache(Justv')<-knownLicenses]unknownLicenseVersion_=NothingcheckVersion::[Int]->Bool->PackageCheck->MaybePackageCheckcheckVersionvercondpc|specVersionpkg>=mkVersionver=Nothing|otherwise=checkcondpccompatLicenses=[GPLNothing,LGPLNothing,AGPLNothing,BSD3,BSD4,PublicDomain,AllRightsReserved,UnspecifiedLicense,OtherLicense]checkSourceRepos::PackageDescription->[PackageCheck]checkSourceRepospkg=catMaybes$concat[[caserepoKindrepoofRepoKindUnknownkind->Just$PackageDistInexcusable$quotekind++" is not a recognised kind of source-repository. "++"The repo kind is usually 'head' or 'this'"_->Nothing,check(isNothing(repoTyperepo))$PackageDistInexcusable"The source-repository 'type' is a required field.",check(isNothing(repoLocationrepo))$PackageDistInexcusable"The source-repository 'location' is a required field.",check(repoTyperepo==JustCVS&&isNothing(repoModulerepo))$PackageDistInexcusable"For a CVS source-repository, the 'module' is a required field.",check(repoKindrepo==RepoThis&&isNothing(repoTagrepo))$PackageDistInexcusable$"For the 'this' kind of source-repository, the 'tag' is a required "++"field. It should specify the tag corresponding to this version "++"or release of the package.",check(maybeFalseisAbsoluteOnAnyPlatform(repoSubdirrepo))$PackageDistInexcusable"The 'subdir' field of a source-repository must be a relative path."]|repo<-sourceRepospkg]--TODO: check location looks like a URL for some repo types.checkGhcOptions::PackageDescription->[PackageCheck]checkGhcOptionspkg=catMaybes[checkFlags["-fasm"]$PackageDistInexcusable$"'ghc-options: -fasm' is unnecessary and will not work on CPU "++"architectures other than x86, x86-64, ppc or sparc.",checkFlags["-fvia-C"]$PackageDistSuspicious$"'ghc-options: -fvia-C' is usually unnecessary. If your package "++"needs -via-C for correctness rather than performance then it "++"is using the FFI incorrectly and will probably not work with GHC "++"6.10 or later.",checkFlags["-fhpc"]$PackageDistInexcusable$"'ghc-options: -fhpc' is not not necessary. Use the configure flag "++" --enable-coverage instead.",checkFlags["-prof"]$PackageBuildWarning$"'ghc-options: -prof' is not necessary and will lead to problems "++"when used on a library. Use the configure flag "++"--enable-library-profiling and/or --enable-profiling.",checkFlags["-o"]$PackageBuildWarning$"'ghc-options: -o' is not needed. "++"The output files are named automatically.",checkFlags["-hide-package"]$PackageBuildWarning$"'ghc-options: -hide-package' is never needed. "++"Cabal hides all packages.",checkFlags["--make"]$PackageBuildWarning$"'ghc-options: --make' is never needed. Cabal uses this automatically.",checkFlags["-main-is"]$PackageDistSuspicious$"'ghc-options: -main-is' is not portable.",checkNonTestAndBenchmarkFlags["-O0","-Onot"]$PackageDistSuspicious$"'ghc-options: -O0' is not needed. "++"Use the --disable-optimization configure flag.",checkTestAndBenchmarkFlags["-O0","-Onot"]$PackageDistSuspiciousWarn$"'ghc-options: -O0' is not needed. "++"Use the --disable-optimization configure flag.",checkFlags["-O","-O1"]$PackageDistInexcusable$"'ghc-options: -O' is not needed. "++"Cabal automatically adds the '-O' flag. "++"Setting it yourself interferes with the --disable-optimization flag.",checkFlags["-O2"]$PackageDistSuspiciousWarn$"'ghc-options: -O2' is rarely needed. "++"Check that it is giving a real benefit "++"and not just imposing longer compile times on your users.",checkFlags["-split-sections"]$PackageBuildWarning$"'ghc-options: -split-sections' is not needed. "++"Use the --enable-split-sections configure flag.",checkFlags["-split-objs"]$PackageBuildWarning$"'ghc-options: -split-objs' is not needed. "++"Use the --enable-split-objs configure flag.",checkFlags["-optl-Wl,-s","-optl-s"]$PackageDistInexcusable$"'ghc-options: -optl-Wl,-s' is not needed and is not portable to all"++" operating systems. Cabal 1.4 and later automatically strip"++" executables. Cabal also has a flag --disable-executable-stripping"++" which is necessary when building packages for some Linux"++" distributions and using '-optl-Wl,-s' prevents that from working.",checkFlags["-fglasgow-exts"]$PackageDistSuspicious$"Instead of 'ghc-options: -fglasgow-exts' it is preferable to use "++"the 'extensions' field.",check("-threaded"`elem`lib_ghc_options)$PackageBuildWarning$"'ghc-options: -threaded' has no effect for libraries. It should "++"only be used for executables.",check("-rtsopts"`elem`lib_ghc_options)$PackageBuildWarning$"'ghc-options: -rtsopts' has no effect for libraries. It should "++"only be used for executables.",check(any(\opt->"-with-rtsopts"`isPrefixOf`opt)lib_ghc_options)$PackageBuildWarning$"'ghc-options: -with-rtsopts' has no effect for libraries. It "++"should only be used for executables.",checkAlternatives"ghc-options""extensions"[(flag,displayextension)|flag<-all_ghc_options,Justextension<-[ghcExtensionflag]],checkAlternatives"ghc-options""extensions"[(flag,extension)|flag@('-':'X':extension)<-all_ghc_options],checkAlternatives"ghc-options""cpp-options"$[(flag,flag)|flag@('-':'D':_)<-all_ghc_options]++[(flag,flag)|flag@('-':'U':_)<-all_ghc_options],checkAlternatives"ghc-options""include-dirs"[(flag,dir)|flag@('-':'I':dir)<-all_ghc_options],checkAlternatives"ghc-options""extra-libraries"[(flag,lib)|flag@('-':'l':lib)<-all_ghc_options],checkAlternatives"ghc-options""extra-lib-dirs"[(flag,dir)|flag@('-':'L':dir)<-all_ghc_options],checkAlternatives"ghc-options""frameworks"[(flag,fmwk)|(flag@"-framework",fmwk)<-zipall_ghc_options(safeTailall_ghc_options)],checkAlternatives"ghc-options""extra-framework-dirs"[(flag,dir)|(flag@"-framework-path",dir)<-zipall_ghc_options(safeTailall_ghc_options)]]whereall_ghc_options=concatMapget_ghc_options(allBuildInfopkg)lib_ghc_options=concatMap(get_ghc_options.libBuildInfo)(allLibrariespkg)get_ghc_optionsbi=hcOptionsGHCbi++hcProfOptionsGHCbi++hcSharedOptionsGHCbitest_ghc_options=concatMap(get_ghc_options.testBuildInfo)(testSuitespkg)benchmark_ghc_options=concatMap(get_ghc_options.benchmarkBuildInfo)(benchmarkspkg)test_and_benchmark_ghc_options=test_ghc_options++benchmark_ghc_optionsnon_test_and_benchmark_ghc_options=concatMapget_ghc_options(allBuildInfo(pkg{testSuites=[],benchmarks=[]}))checkFlags::[String]->PackageCheck->MaybePackageCheckcheckFlagsflags=check(any(`elem`flags)all_ghc_options)checkTestAndBenchmarkFlags::[String]->PackageCheck->MaybePackageCheckcheckTestAndBenchmarkFlagsflags=check(any(`elem`flags)test_and_benchmark_ghc_options)checkNonTestAndBenchmarkFlags::[String]->PackageCheck->MaybePackageCheckcheckNonTestAndBenchmarkFlagsflags=check(any(`elem`flags)non_test_and_benchmark_ghc_options)ghcExtension('-':'f':name)=casenameof"allow-overlapping-instances"->enableOverlappingInstances"no-allow-overlapping-instances"->disableOverlappingInstances"th"->enableTemplateHaskell"no-th"->disableTemplateHaskell"ffi"->enableForeignFunctionInterface"no-ffi"->disableForeignFunctionInterface"fi"->enableForeignFunctionInterface"no-fi"->disableForeignFunctionInterface"monomorphism-restriction"->enableMonomorphismRestriction"no-monomorphism-restriction"->disableMonomorphismRestriction"mono-pat-binds"->enableMonoPatBinds"no-mono-pat-binds"->disableMonoPatBinds"allow-undecidable-instances"->enableUndecidableInstances"no-allow-undecidable-instances"->disableUndecidableInstances"allow-incoherent-instances"->enableIncoherentInstances"no-allow-incoherent-instances"->disableIncoherentInstances"arrows"->enableArrows"no-arrows"->disableArrows"generics"->enableGenerics"no-generics"->disableGenerics"implicit-prelude"->enableImplicitPrelude"no-implicit-prelude"->disableImplicitPrelude"implicit-params"->enableImplicitParams"no-implicit-params"->disableImplicitParams"bang-patterns"->enableBangPatterns"no-bang-patterns"->disableBangPatterns"scoped-type-variables"->enableScopedTypeVariables"no-scoped-type-variables"->disableScopedTypeVariables"extended-default-rules"->enableExtendedDefaultRules"no-extended-default-rules"->disableExtendedDefaultRules_->NothingghcExtension"-cpp"=enableCPPghcExtension_=Nothingenablee=Just(EnableExtensione)disablee=Just(DisableExtensione)checkCCOptions::PackageDescription->[PackageCheck]checkCCOptions=checkCLikeOptions"C""cc-options"ccOptionscheckCxxOptions::PackageDescription->[PackageCheck]checkCxxOptions=checkCLikeOptions"C++""cxx-options"cxxOptionscheckCLikeOptions::String->String->(BuildInfo->[String])->PackageDescription->[PackageCheck]checkCLikeOptionslabelprefixaccessorpkg=catMaybes[checkAlternativesprefix"include-dirs"[(flag,dir)|flag@('-':'I':dir)<-all_cLikeOptions],checkAlternativesprefix"extra-libraries"[(flag,lib)|flag@('-':'l':lib)<-all_cLikeOptions],checkAlternativesprefix"extra-lib-dirs"[(flag,dir)|flag@('-':'L':dir)<-all_cLikeOptions],checkAlternatives"ld-options""extra-libraries"[(flag,lib)|flag@('-':'l':lib)<-all_ldOptions],checkAlternatives"ld-options""extra-lib-dirs"[(flag,dir)|flag@('-':'L':dir)<-all_ldOptions],checkCCFlags["-O","-Os","-O0","-O1","-O2","-O3"]$PackageDistSuspicious$"'"++prefix++": -O[n]' is generally not needed. When building with "++" optimisations Cabal automatically adds '-O2' for "++label++" code. "++"Setting it yourself interferes with the --disable-optimization flag."]whereall_cLikeOptions=[opts|bi<-allBuildInfopkg,opts<-accessorbi]all_ldOptions=[opts|bi<-allBuildInfopkg,opts<-ldOptionsbi]checkCCFlags::[String]->PackageCheck->MaybePackageCheckcheckCCFlagsflags=check(any(`elem`flags)all_cLikeOptions)checkCPPOptions::PackageDescription->[PackageCheck]checkCPPOptionspkg=catMaybes[checkAlternatives"cpp-options""include-dirs"[(flag,dir)|flag@('-':'I':dir)<-all_cppOptions]]whereall_cppOptions=[opts|bi<-allBuildInfopkg,opts<-cppOptionsbi]checkAlternatives::String->String->[(String,String)]->MaybePackageCheckcheckAlternativesbadFieldgoodFieldflags=check(not(nullbadFlags))$PackageBuildWarning$"Instead of "++quote(badField++": "++unwordsbadFlags)++" use "++quote(goodField++": "++unwordsgoodFlags)where(badFlags,goodFlags)=unzipflagscheckPaths::PackageDescription->[PackageCheck]checkPathspkg=[PackageBuildWarning$quote(kind++": "++path)++" is a relative path outside of the source tree. "++"This will not work when generating a tarball with 'sdist'."|(path,kind)<-relPaths++absPaths,isOutsideTreepath]++[PackageDistInexcusable$quote(kind++": "++path)++" is an absolute path."|(path,kind)<-relPaths,isAbsoluteOnAnyPlatformpath]++[PackageDistInexcusable$quote(kind++": "++path)++" points inside the 'dist' "++"directory. This is not reliable because the location of this "++"directory is configurable by the user (or package manager). In "++"addition the layout of the 'dist' directory is subject to change "++"in future versions of Cabal."|(path,kind)<-relPaths++absPaths,isInsideDistpath]++[PackageDistInexcusable$"The 'ghc-options' contains the path '"++path++"' which points "++"inside the 'dist' directory. This is not reliable because the "++"location of this directory is configurable by the user (or package "++"manager). In addition the layout of the 'dist' directory is subject "++"to change in future versions of Cabal."|bi<-allBuildInfopkg,(GHC,flags)<-optionsbi,path<-flags,isInsideDistpath]++[PackageDistInexcusable$"In the 'data-files' field: "++explainGlobSyntaxErrorpaterr|pat<-dataFilespkg,Lefterr<-[parseFileGlob(specVersionpkg)pat]]++[PackageDistInexcusable$"In the 'extra-source-files' field: "++explainGlobSyntaxErrorpaterr|pat<-extraSrcFilespkg,Lefterr<-[parseFileGlob(specVersionpkg)pat]]++[PackageDistInexcusable$"In the 'extra-doc-files' field: "++explainGlobSyntaxErrorpaterr|pat<-extraDocFilespkg,Lefterr<-[parseFileGlob(specVersionpkg)pat]]whereisOutsideTreepath=casesplitDirectoriespathof"..":_->True".":"..":_->True_->FalseisInsideDistpath=casemaplowercase(splitDirectoriespath)of"dist":_->True".":"dist":_->True_->False-- paths that must be relativerelPaths=[(path,"extra-source-files")|path<-extraSrcFilespkg]++[(path,"extra-tmp-files")|path<-extraTmpFilespkg]++[(path,"extra-doc-files")|path<-extraDocFilespkg]++[(path,"data-files")|path<-dataFilespkg]++[(path,"data-dir")|path<-[dataDirpkg]]++[(path,"license-file")|path<-licenseFilespkg]++concat[[(path,"asm-sources")|path<-asmSourcesbi]++[(path,"cmm-sources")|path<-cmmSourcesbi]++[(path,"c-sources")|path<-cSourcesbi]++[(path,"cxx-sources")|path<-cxxSourcesbi]++[(path,"js-sources")|path<-jsSourcesbi]++[(path,"install-includes")|path<-installIncludesbi]++[(path,"hs-source-dirs")|path<-hsSourceDirsbi]|bi<-allBuildInfopkg]-- paths that are allowed to be absoluteabsPaths=concat[[(path,"includes")|path<-includesbi]++[(path,"include-dirs")|path<-includeDirsbi]++[(path,"extra-lib-dirs")|path<-extraLibDirsbi]|bi<-allBuildInfopkg]--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]checkCabalVersionpkg=catMaybes[-- check syntax of cabal-version fieldcheck(specVersionpkg>=mkVersion[1,10]&&notsimpleSpecVersionRangeSyntax)$PackageBuildWarning$"Packages relying on Cabal 1.10 or later must only specify a "++"version range of the form 'cabal-version: >= x.y'. Use "++"'cabal-version: >= "++display(specVersionpkg)++"'."-- check syntax of cabal-version field,check(specVersionpkg<mkVersion[1,9]&&notsimpleSpecVersionRangeSyntax)$PackageDistSuspicious$"It is recommended that the 'cabal-version' field only specify a "++"version range of the form '>= x.y'. Use "++"'cabal-version: >= "++display(specVersionpkg)++"'. "++"Tools based on Cabal 1.10 and later will ignore upper bounds."-- check syntax of cabal-version field,checkVersion[1,12]simpleSpecVersionSyntax$PackageBuildWarning$"With Cabal 1.10 or earlier, the 'cabal-version' field must use "++"range syntax rather than a simple version number. Use "++"'cabal-version: >= "++display(specVersionpkg)++"'."-- check syntax of cabal-version field,check(specVersionpkg>=mkVersion[1,12]&&notsimpleSpecVersionSyntax)$(ifspecVersionpkg>=mkVersion[2,0]thenPackageDistSuspiciouselsePackageDistSuspiciousWarn)$"Packages relying on Cabal 1.12 or later should specify a "++"specific version of the Cabal spec of the form "++"'cabal-version: x.y'. "++"Use 'cabal-version: "++display(specVersionpkg)++"'."-- check use of test suite sections,checkVersion[1,8](not(null$testSuitespkg))$PackageDistInexcusable$"The 'test-suite' section is new in Cabal 1.10. "++"Unfortunately it messes up the parser in older Cabal versions "++"so you must specify at least 'cabal-version: >= 1.8', but note "++"that only Cabal 1.10 and later can actually run such test suites."-- check use of default-language field-- note that we do not need to do an equivalent check for the-- other-language field since that one does not change behaviour,checkVersion[1,10](anyisJust(buildInfoFielddefaultLanguage))$PackageBuildWarning$"To use the 'default-language' field the package needs to specify "++"at least 'cabal-version: >= 1.10'.",check(specVersionpkg>=mkVersion[1,10]&&(anyisNothing(buildInfoFielddefaultLanguage)))$PackageBuildWarning$"Packages using 'cabal-version: >= 1.10' must specify the "++"'default-language' field for each component (e.g. Haskell98 or "++"Haskell2010). If a component uses different languages in "++"different modules then list the other ones in the "++"'other-languages' field.",checkVersion[1,18](not.null$extraDocFilespkg)$PackageDistInexcusable$"To use the 'extra-doc-files' field the package needs to specify "++"at least 'cabal-version: >= 1.18'.",checkVersion[2,0](not(null(subLibrariespkg)))$PackageDistInexcusable$"To use multiple 'library' sections or a named library section "++"the package needs to specify at least 'cabal-version: 2.0'."-- check use of reexported-modules sections,checkVersion[1,21](any(not.null.reexportedModules)(allLibrariespkg))$PackageDistInexcusable$"To use the 'reexported-module' field the package needs to specify "++"at least 'cabal-version: >= 1.22'."-- check use of thinning and renaming,checkVersion[1,25]usesBackpackIncludes$PackageDistInexcusable$"To use the 'mixins' field the package needs to specify "++"at least 'cabal-version: 2.0'."-- check use of 'extra-framework-dirs' field,checkVersion[1,23](any(not.null)(buildInfoFieldextraFrameworkDirs))$-- Just a warning, because this won't break on old Cabal versions.PackageDistSuspiciousWarn$"To use the 'extra-framework-dirs' field the package needs to specify"++" at least 'cabal-version: >= 1.24'."-- check use of default-extensions field-- don't need to do the equivalent check for other-extensions,checkVersion[1,10](any(not.null)(buildInfoFielddefaultExtensions))$PackageBuildWarning$"To use the 'default-extensions' field the package needs to specify "++"at least 'cabal-version: >= 1.10'."-- check use of extensions field,check(specVersionpkg>=mkVersion[1,10]&&(any(not.null)(buildInfoFieldoldExtensions)))$PackageBuildWarning$"For packages using 'cabal-version: >= 1.10' the 'extensions' "++"field is deprecated. The new 'default-extensions' field lists "++"extensions that are used in all modules in the component, while "++"the 'other-extensions' field lists extensions that are used in "++"some modules, e.g. via the {-# LANGUAGE #-} pragma."-- check use of "foo (>= 1.0 && < 1.4) || >=1.8 " version-range syntax,checkVersion[1,8](not(nullversionRangeExpressions))$PackageDistInexcusable$"The package uses full version-range expressions "++"in a 'build-depends' field: "++commaSep(mapdisplayRawDependencyversionRangeExpressions)++". To use this new syntax the package needs to specify at least "++"'cabal-version: >= 1.8'. Alternatively, if broader compatibility "++"is important, then convert to conjunctive normal form, and use "++"multiple 'build-depends:' lines, one conjunct per line."-- check use of "build-depends: foo == 1.*" syntax,checkVersion[1,6](not(nulldepsUsingWildcardSyntax))$PackageDistInexcusable$"The package uses wildcard syntax in the 'build-depends' field: "++commaSep(mapdisplaydepsUsingWildcardSyntax)++". To use this new syntax the package need to specify at least "++"'cabal-version: >= 1.6'. Alternatively, if broader compatibility "++"is important then use: "++commaSep[display(Dependencyname(eliminateWildcardSyntaxversionRange))|DependencynameversionRange<-depsUsingWildcardSyntax]-- check use of "build-depends: foo ^>= 1.2.3" syntax,checkVersion[2,0](not(nulldepsUsingMajorBoundSyntax))$PackageDistInexcusable$"The package uses major bounded version syntax in the "++"'build-depends' field: "++commaSep(mapdisplaydepsUsingMajorBoundSyntax)++". To use this new syntax the package need to specify at least "++"'cabal-version: 2.0'. Alternatively, if broader compatibility "++"is important then use: "++commaSep[display(Dependencyname(eliminateMajorBoundSyntaxversionRange))|DependencynameversionRange<-depsUsingMajorBoundSyntax],checkVersion[2,1](any(not.null)(concatMapbuildInfoField[asmSources,cmmSources,extraBundledLibs,extraLibFlavours]))$PackageDistInexcusable$"The use of 'asm-sources', 'cmm-sources', 'extra-bundled-libraries' "++" and 'extra-library-flavours' requires the package "++" to specify at least 'cabal-version: >= 2.1'.",checkVersion[2,1](any(not.null)(buildInfoFieldvirtualModules))$PackageDistInexcusable$"The use of 'virtual-modules' requires the package "++" to specify at least 'cabal-version: >= 2.1'."-- check use of "tested-with: GHC (>= 1.0 && < 1.4) || >=1.8 " syntax,checkVersion[1,8](not(nulltestedWithVersionRangeExpressions))$PackageDistInexcusable$"The package uses full version-range expressions "++"in a 'tested-with' field: "++commaSep(mapdisplayRawDependencytestedWithVersionRangeExpressions)++". To use this new syntax the package needs to specify at least "++"'cabal-version: >= 1.8'."-- check use of "tested-with: GHC == 6.12.*" syntax,checkVersion[1,6](not(nulltestedWithUsingWildcardSyntax))$PackageDistInexcusable$"The package uses wildcard syntax in the 'tested-with' field: "++commaSep(mapdisplaytestedWithUsingWildcardSyntax)++". To use this new syntax the package need to specify at least "++"'cabal-version: >= 1.6'. Alternatively, if broader compatibility "++"is important then use: "++commaSep[display(Dependencyname(eliminateWildcardSyntaxversionRange))|DependencynameversionRange<-testedWithUsingWildcardSyntax]-- check use of "source-repository" section,checkVersion[1,6](not(null(sourceRepospkg)))$PackageDistInexcusable$"The 'source-repository' section is new in Cabal 1.6. "++"Unfortunately it messes up the parser in earlier Cabal versions "++"so you need to specify 'cabal-version: >= 1.6'."-- check for new language extensions,checkVersion[1,2,3](not(nullmentionedExtensionsThatNeedCabal12))$PackageDistInexcusable$"Unfortunately the language extensions "++commaSep(map(quote.display)mentionedExtensionsThatNeedCabal12)++" break the parser in earlier Cabal versions so you need to "++"specify 'cabal-version: >= 1.2.3'. Alternatively if you require "++"compatibility with earlier Cabal versions then you may be able to "++"use an equivalent compiler-specific flag.",checkVersion[1,4](not(nullmentionedExtensionsThatNeedCabal14))$PackageDistInexcusable$"Unfortunately the language extensions "++commaSep(map(quote.display)mentionedExtensionsThatNeedCabal14)++" break the parser in earlier Cabal versions so you need to "++"specify 'cabal-version: >= 1.4'. Alternatively if you require "++"compatibility with earlier Cabal versions then you may be able to "++"use an equivalent compiler-specific flag.",check(specVersionpkg>=mkVersion[1,23]&&isNothing(setupBuildInfopkg)&&buildTypepkg==Custom)$PackageBuildWarning$"Packages using 'cabal-version: >= 1.24' with 'build-type: Custom' "++"must use a 'custom-setup' section with a 'setup-depends' field "++"that specifies the dependencies of the Setup.hs script itself. "++"The 'setup-depends' field uses the same syntax as 'build-depends', "++"so a simple example would be 'setup-depends: base, Cabal'.",check(specVersionpkg<mkVersion[1,23]&&isNothing(setupBuildInfopkg)&&buildTypepkg==Custom)$PackageDistSuspiciousWarn$"From version 1.24 cabal supports specifiying explicit dependencies "++"for Custom setup scripts. Consider using cabal-version >= 1.24 and "++"adding a 'custom-setup' section with a 'setup-depends' field "++"that specifies the dependencies of the Setup.hs script itself. "++"The 'setup-depends' field uses the same syntax as 'build-depends', "++"so a simple example would be 'setup-depends: base, Cabal'.",check(specVersionpkg>=mkVersion[1,25]&&elem(autogenPathsModuleNamepkg)allModuleNames&&not(elem(autogenPathsModuleNamepkg)allModuleNamesAutogen))$PackageDistInexcusable$"Packages using 'cabal-version: 2.0' and the autogenerated "++"module Paths_* must include it also on the 'autogen-modules' field "++"besides 'exposed-modules' and 'other-modules'. This specifies that "++"the module does not come with the package and is generated on "++"setup. Modules built with a custom Setup.hs script also go here "++"to ensure that commands like sdist don't fail."]where-- Perform a check on packages that use a version of the spec less than-- the version given. This is for cases where a new Cabal version adds-- a new feature and we want to check that it is not used prior to that-- version.checkVersion::[Int]->Bool->PackageCheck->MaybePackageCheckcheckVersionvercondpc|specVersionpkg>=mkVersionver=Nothing|otherwise=checkcondpcbuildInfoFieldfield=mapfield(allBuildInfopkg)versionRangeExpressions=[dep|dep@(Dependency_vr)<-allBuildDependspkg,usesNewVersionRangeSyntaxvr]testedWithVersionRangeExpressions=[Dependency(mkPackageName(displaycompiler))vr|(compiler,vr)<-testedWithpkg,usesNewVersionRangeSyntaxvr]simpleSpecVersionRangeSyntax=either(constTrue)(cataVersionRangealg)(specVersionRawpkg)wherealg(OrLaterVersionF_)=Truealg_=False-- is the cabal-version field a simple version number, rather than a rangesimpleSpecVersionSyntax=either(constTrue)(constFalse)(specVersionRawpkg)usesNewVersionRangeSyntax::VersionRange->BoolusesNewVersionRangeSyntax=(>2)-- uses the new syntax if depth is more than 2.cataVersionRangealgwherealg(UnionVersionRangesFab)=a+balg(IntersectVersionRangesFab)=a+balg(VersionRangeParensF_)=3alg_=1::IntdepsUsingWildcardSyntax=[dep|dep@(Dependency_vr)<-allBuildDependspkg,usesWildcardSyntaxvr]depsUsingMajorBoundSyntax=[dep|dep@(Dependency_vr)<-allBuildDependspkg,usesMajorBoundSyntaxvr]usesBackpackIncludes=any(not.null.mixins)(allBuildInfopkg)testedWithUsingWildcardSyntax=[Dependency(mkPackageName(displaycompiler))vr|(compiler,vr)<-testedWithpkg,usesWildcardSyntaxvr]usesWildcardSyntax::VersionRange->BoolusesWildcardSyntax=cataVersionRangealgwherealg(WildcardVersionF_)=Truealg(UnionVersionRangesFab)=a||balg(IntersectVersionRangesFab)=a||balg(VersionRangeParensFa)=aalg_=False-- NB: this eliminates both, WildcardVersion and MajorBoundVersion-- because when WildcardVersion is not support, neither is MajorBoundVersioneliminateWildcardSyntax=hyloVersionRangeembedprojectVersionRangewhereembed(WildcardVersionFv)=intersectVersionRanges(orLaterVersionv)(earlierVersion(wildcardUpperBoundv))embed(MajorBoundVersionFv)=intersectVersionRanges(orLaterVersionv)(earlierVersion(majorUpperBoundv))embedvr=embedVersionRangevrusesMajorBoundSyntax::VersionRange->BoolusesMajorBoundSyntax=cataVersionRangealgwherealg(MajorBoundVersionF_)=Truealg(UnionVersionRangesFab)=a||balg(IntersectVersionRangesFab)=a||balg(VersionRangeParensFa)=aalg_=FalseeliminateMajorBoundSyntax=hyloVersionRangeembedprojectVersionRangewhereembed(MajorBoundVersionFv)=intersectVersionRanges(orLaterVersionv)(earlierVersion(majorUpperBoundv))embedvr=embedVersionRangevrmentionedExtensions=[ext|bi<-allBuildInfopkg,ext<-allExtensionsbi]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.3compatExtensions=mapEnableExtension[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]++mapDisableExtension[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=mapEnableExtension[KindSignatures,MagicHash,TypeFamilies,StandaloneDeriving,UnicodeSyntax,PatternSignatures,UnliftedFFITypes,LiberalTypeSynonyms,TypeOperators,RecordWildCards,RecordPuns,DisambiguateRecordFields,OverloadedStrings,GADTs,RelaxedPolyRec,ExtendedDefaultRules,UnboxedTuples,DeriveDataTypeable,ConstrainedClassMethods]++mapDisableExtension[MonoPatBinds]allModuleNames=(caselibrarypkgofNothing->[](Justlib)->explicitLibModuleslib)++concatMapotherModules(allBuildInfopkg)allModuleNamesAutogen=concatMapautogenModules(allBuildInfopkg)displayRawDependency::Dependency->StringdisplayRawDependency(Dependencypkgvr)=displaypkg++" "++displayvr-- -------------------------------------------------------------- * Checks on the GenericPackageDescription-- -------------------------------------------------------------- | Check the build-depends fields for any weirdness or bad practise.--checkPackageVersions::GenericPackageDescription->[PackageCheck]checkPackageVersionspkg=catMaybes[-- Check that the version of base is bounded above.-- For example this bans "build-depends: base >= 3".-- It should probably be "build-depends: base >= 3 && < 4"-- which is the same as "build-depends: base == 3.*"check(not(boundedAbovebaseDependency))$PackageDistInexcusable$"The dependency 'build-depends: base' does not specify an upper "++"bound on the version number. Each major release of the 'base' "++"package changes the API in various ways and most packages will "++"need some changes to compile with it. The recommended practise "++"is to specify an upper bound on the version of the 'base' "++"package. This ensures your package will continue to build when a "++"new major version of the 'base' package is released. If you are "++"not sure what upper bound to use then use the next major "++"version. For example if you have tested your package with 'base' "++"version 4.5 and 4.6 then use 'build-depends: base >= 4.5 && < 4.7'."]where-- TODO: What we really want to do is test if there exists any-- configuration in which the base version is unbounded above.-- However that's a bit tricky because there are many possible-- configurations. As a cheap easy and safe approximation we will-- pick a single "typical" configuration and check if that has an-- open upper bound. To get a typical configuration we finalise-- using no package index and the current platform.finalised=finalizePDmemptydefaultComponentRequestedSpec(constTrue)buildPlatform(unknownCompilerInfo(CompilerIdbuildCompilerFlavornullVersion)NoAbiTag)[]pkgbaseDependency=casefinalisedofRight(pkg',_)|not(nullbaseDeps)->foldrintersectVersionRangesanyVersionbaseDepswherebaseDeps=[vr|Dependencypnamevr<-allBuildDependspkg',pname==mkPackageName"base"]-- Just in case finalizePD fails for any reason,-- or if the package doesn't depend on the base package at all,-- then we will just skip the check, since boundedAbove noVersion = True_->noVersionboundedAbove::VersionRange->BoolboundedAbovevr=caseasVersionIntervalsvrof[]->True-- this is the inconsistent version range.intervals->caselastintervalsof(_,UpperBound__)->True(_,NoUpperBound)->FalsecheckConditionals::GenericPackageDescription->[PackageCheck]checkConditionalspkg=catMaybes[check(not$nullunknownOSs)$PackageDistInexcusable$"Unknown operating system name "++commaSep(mapquoteunknownOSs),check(not$nullunknownArches)$PackageDistInexcusable$"Unknown architecture name "++commaSep(mapquoteunknownArches),check(not$nullunknownImpls)$PackageDistInexcusable$"Unknown compiler name "++commaSep(mapquoteunknownImpls)]whereunknownOSs=[os|OS(OtherOSos)<-conditions]unknownArches=[arch|Arch(OtherArcharch)<-conditions]unknownImpls=[impl|Impl(OtherCompilerimpl)_<-conditions]conditions=concatMapfvs(maybeToList(condLibrarypkg))++concatMap(fvs.snd)(condSubLibrariespkg)++concatMap(fvs.snd)(condForeignLibspkg)++concatMap(fvs.snd)(condExecutablespkg)++concatMap(fvs.snd)(condTestSuitespkg)++concatMap(fvs.snd)(condBenchmarkspkg)fvs(CondNode__ifs)=concatMapcompfvifs-- free variablescompfv(CondBranchcctmct)=condfvc++fvsct++maybe[]fvsmctcondfvc=casecofVarv->[v]Lit_->[]CNotc1->condfvc1COrc1c2->condfvc1++condfvc2CAndc1c2->condfvc1++condfvc2checkFlagNames::GenericPackageDescription->[PackageCheck]checkFlagNamesgpd|nullinvalidFlagNames=[]|otherwise=[PackageDistInexcusable$"Suspicious flag names: "++unwordsinvalidFlagNames++". "++"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."]whereinvalidFlagNames=[fn|flag<-genPackageFlagsgpd,letfn=unFlagName(flagNameflag),invalidFlagNamefn]-- starts with dashinvalidFlagName('-':_)=True-- mon ascii letterinvalidFlagNamecs=any(not.isAscii)cscheckUnusedFlags::GenericPackageDescription->[PackageCheck]checkUnusedFlagsgpd|declared==used=[]|otherwise=[PackageDistSuspicious$"Declared and used flag sets differ: "++sdeclared++" /= "++sused++". "]wheres::Set.SetFlagName->Strings=commaSep.mapunFlagName.Set.toListdeclared::Set.SetFlagNamedeclared=toSetOf(L.genPackageFlags.traverse.L.flagName)gpdused::Set.SetFlagNameused=mconcat[toSetOf(L.condLibrary.traverse.traverseCondTreeV.L._Flag)gpd,toSetOf(L.condSubLibraries.traverse._2.traverseCondTreeV.L._Flag)gpd,toSetOf(L.condForeignLibs.traverse._2.traverseCondTreeV.L._Flag)gpd,toSetOf(L.condExecutables.traverse._2.traverseCondTreeV.L._Flag)gpd,toSetOf(L.condTestSuites.traverse._2.traverseCondTreeV.L._Flag)gpd,toSetOf(L.condBenchmarks.traverse._2.traverseCondTreeV.L._Flag)gpd]checkUnicodeXFields::GenericPackageDescription->[PackageCheck]checkUnicodeXFieldsgpd|nullnonAsciiXFields=[]|otherwise=[PackageDistInexcusable$"Non ascii custom fields: "++unwordsnonAsciiXFields++". "++"For better compatibility, custom field names "++"shouldn't contain non-ascii characters."]wherenonAsciiXFields::[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]checkPathsModuleExtensionspd|specVersionpd>=mkVersion[2,1]=[]|anycheckBI(allBuildInfopd)||anycheckLib(allLibrariespd)=return$PackageBuildImpossible$unwords["The package uses RebindableSyntax with OverloadedStrings or OverloadedLists","in default-extensions, and also Paths_ autogen module.","That configuration is known to cause compile failures with Cabal < 2.2.","To use these default-extensions with Paths_ autogen module","specify at least 'cabal-version: 2.2'."]|otherwise=[]wheremn=autogenPathsModuleNamepdcheckLib::Library->BoolcheckLibl=mn`elem`exposedModulesl&&checkExts(l^.L.defaultExtensions)checkBI::BuildInfo->BoolcheckBIbi=(mn`elem`otherModulesbi||mn`elem`autogenModulesbi)&&checkExts(bi^.L.defaultExtensions)checkExtsexts=rebind`elem`exts&&(strings`elem`exts||lists`elem`exts)whererebind=EnableExtensionRebindableSyntaxstrings=EnableExtensionOverloadedStringslists=EnableExtensionOverloadedListscheckDevelopmentOnlyFlagsBuildInfo::BuildInfo->[PackageCheck]checkDevelopmentOnlyFlagsBuildInfobi=catMaybes[checkhas_WerrorWall$PackageDistInexcusable$"'ghc-options: -Wall -Werror' makes the package very easy to "++"break with future GHC versions because new GHC versions often "++"add new warnings. Use just 'ghc-options: -Wall' instead."++extraExplanation,check(nothas_WerrorWall&&has_Werror)$PackageDistInexcusable$"'ghc-options: -Werror' makes the package easy to "++"break with future GHC versions because new GHC versions often "++"add new warnings. "++extraExplanation,check(has_J)$PackageDistInexcusable$"'ghc-options: -j[N]' can make sense for specific user's setup,"++" but it is not appropriate for a distributed package."++extraExplanation,checkFlags["-fdefer-type-errors"]$PackageDistInexcusable$"'ghc-options: -fdefer-type-errors' is fine during development but "++"is not appropriate for a distributed package. "++extraExplanation-- -dynamic is not a debug flag,check(any(\opt->"-d"`isPrefixOf`opt&&opt/="-dynamic")ghc_options)$PackageDistInexcusable$"'ghc-options: -d*' debug flags are not appropriate "++"for a distributed package. "++extraExplanation,checkFlags["-fprof-auto","-fprof-auto-top","-fprof-auto-calls","-fprof-cafs","-fno-prof-count-entries","-auto-all","-auto","-caf-all"]$PackageDistSuspicious$"'ghc-options/ghc-prof-options: -fprof*' profiling flags are typically not "++"appropriate for a distributed library package. These flags are "++"useful to profile this package, but when profiling other packages "++"that use this one these flags clutter the profile output with "++"excessive detail. If you think other packages really want to see "++"cost centres from this package then use '-fprof-auto-exported' "++"which puts cost centres only on exported functions. "++extraExplanation]whereextraExplanation=" Alternatively, if you want to use this, make it conditional based "++"on a Cabal configuration flag (with 'manual: True' and 'default: "++"False') and enable that flag during development."has_WerrorWall=has_Werror&&(has_Wall||has_W)has_Werror="-Werror"`elem`ghc_optionshas_Wall="-Wall"`elem`ghc_optionshas_W="-W"`elem`ghc_optionshas_J=any(\o->caseoof"-j"->True('-':'j':d:_)->isDigitd_->False)ghc_optionsghc_options=hcOptionsGHCbi++hcProfOptionsGHCbi++hcSharedOptionsGHCbicheckFlags::[String]->PackageCheck->MaybePackageCheckcheckFlagsflags=check(any(`elem`flags)ghc_options)checkDevelopmentOnlyFlags::GenericPackageDescription->[PackageCheck]checkDevelopmentOnlyFlagspkg=concatMapcheckDevelopmentOnlyFlagsBuildInfo[bi|(conditions,bi)<-allConditionalBuildInfo,not(anyguardedByManualFlagconditions)]whereguardedByManualFlag=definitelyFalse-- We've basically got three-values logic here: True, False or unknown-- hence this pattern to propagate the unknown cases properly.definitelyFalse(Var(Flagn))=maybeFalsenot(Map.lookupnmanualFlags)definitelyFalse(Var_)=FalsedefinitelyFalse(Litb)=notbdefinitelyFalse(CNotc)=definitelyTruecdefinitelyFalse(COrc1c2)=definitelyFalsec1&&definitelyFalsec2definitelyFalse(CAndc1c2)=definitelyFalsec1||definitelyFalsec2definitelyTrue(Var(Flagn))=fromMaybeFalse(Map.lookupnmanualFlags)definitelyTrue(Var_)=FalsedefinitelyTrue(Litb)=bdefinitelyTrue(CNotc)=definitelyFalsecdefinitelyTrue(COrc1c2)=definitelyTruec1||definitelyTruec2definitelyTrue(CAndc1c2)=definitelyTruec1&&definitelyTruec2manualFlags=Map.fromList[(flagNameflag,flagDefaultflag)|flag<-genPackageFlagspkg,flagManualflag]allConditionalBuildInfo::[([ConditionConfVar],BuildInfo)]allConditionalBuildInfo=concatMap(collectCondTreePathslibBuildInfo)(maybeToList(condLibrarypkg))++concatMap(collectCondTreePathslibBuildInfo.snd)(condSubLibrariespkg)++concatMap(collectCondTreePathsbuildInfo.snd)(condExecutablespkg)++concatMap(collectCondTreePathstestBuildInfo.snd)(condTestSuitespkg)++concatMap(collectCondTreePathsbenchmarkBuildInfo.snd)(condBenchmarkspkg)-- get all the leaf BuildInfo, paired up with the path (in the tree sense)-- of if-conditions that guard itcollectCondTreePaths::(a->b)->CondTreevca->[([Conditionv],b)]collectCondTreePathsmapData=go[]wheregoconditionscondNode=-- the data at this level in the tree:(reverseconditions,mapData(condTreeDatacondNode)):concat[go(condition:conditions)ifThen|(CondBranchconditionifThen_)<-condTreeComponentscondNode]++concat[go(condition:conditions)elseThen|(CondBranchcondition_(JustelseThen))<-condTreeComponentscondNode]-- -------------------------------------------------------------- * 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->NoCallStackIO[PackageCheck]checkPackageFilesverbositypkgroot=docontentChecks<-checkPackageContentcheckFilesIOpkgpreDistributionChecks<-checkPackageFilesPreDistributionverbositypkgroot-- Sort because different platforms will provide files from-- `getDirectoryContents` in different orders, and we'd like to be-- stable for test output.return(sortcontentChecks++sortpreDistributionChecks)wherecheckFilesIO=CheckPackageContentOps{doesFileExist=System.doesFileExist.relative,doesDirectoryExist=System.doesDirectoryExist.relative,getDirectoryContents=System.Directory.getDirectoryContents.relative,getFileContents=BS.readFile.relative}relativepath=root</>path-- | A record of operations needed to check the contents of packages.-- Used by 'checkPackageContent'.--dataCheckPackageContentOpsm=CheckPackageContentOps{doesFileExist::FilePath->mBool,doesDirectoryExist::FilePath->mBool,getDirectoryContents::FilePath->m[FilePath],getFileContents::FilePath->mBS.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::Monadm=>CheckPackageContentOpsm->PackageDescription->m[PackageCheck]checkPackageContentopspkg=docabalBomError<-checkCabalFileBOMopscabalNameError<-checkCabalFileNameopspkglicenseErrors<-checkLicensesExistopspkgsetupError<-checkSetupExistsopspkgconfigureError<-checkConfigureExistsopspkglocalPathErrors<-checkLocalPathsExistopspkgvcsLocation<-checkMissingVcsInfoopspkgreturn$licenseErrors++catMaybes[cabalBomError,cabalNameError,setupError,configureError]++localPathErrors++vcsLocationcheckCabalFileBOM::Monadm=>CheckPackageContentOpsm->m(MaybePackageCheck)checkCabalFileBOMops=doepdfile<-findPackageDescopscaseepdfileof-- 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$NothingRightpdfile->(flipcheckpc.BS.isPrefixOfbomUtf8)`liftM`(getFileContentsopspdfile)wherepc=PackageDistInexcusable$pdfile++" starts with an Unicode byte order mark (BOM)."++" This may cause problems with older cabal versions."wherebomUtf8::BS.ByteStringbomUtf8=BS.pack[0xef,0xbb,0xbf]-- U+FEFF encoded as UTF8checkCabalFileName::Monadm=>CheckPackageContentOpsm->PackageDescription->m(MaybePackageCheck)checkCabalFileNameopspkg=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<-findPackageDescopscaseepdfileof-- see "MASSIVE HACK" note in 'checkCabalFileBOM'Left_->returnNothingRightpdfile|takeFileNamepdfile==expectedCabalname->returnNothing|otherwise->return$Just$PackageDistInexcusable$"The filename "++pdfile++" does not match package name "++"(expected: "++expectedCabalname++")"wherepkgname=unPackageName.packageName$pkgexpectedCabalname=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::Monadm=>CheckPackageContentOpsm->m(EitherPackageCheckFilePath)-- ^<pkgname>.cabalfindPackageDescops=doletdir="."files<-getDirectoryContentsopsdir-- 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(doesFileExistops)[dir</>file|file<-files,let(name,ext)=splitExtensionfile,not(nullname)&&ext==".cabal"]casecabalFilesof[]->return(Left$PackageBuildImpossiblenoDesc)[cabalFile]->return(RightcabalFile)multiple->return(Left$PackageBuildImpossible$multiDescmultiple)wherenoDesc::StringnoDesc="No cabal file found.\n"++"Please create a package description file <pkgname>.cabal"multiDesc::[String]->StringmultiDescl="Multiple cabal files found while checking.\n"++"Please use only one of: "++intercalate", "lcheckLicensesExist::Monadm=>CheckPackageContentOpsm->PackageDescription->m[PackageCheck]checkLicensesExistopspkg=doexists<-mapM(doesFileExistops)(licenseFilespkg)return[PackageBuildWarning$"The '"++fieldname++"' field refers to the file "++quotefile++" which does not exist."|(file,False)<-zip(licenseFilespkg)exists]wherefieldname|length(licenseFilespkg)==1="license-file"|otherwise="license-files"checkSetupExists::Monadm=>CheckPackageContentOpsm->PackageDescription->m(MaybePackageCheck)checkSetupExistsopspkg=doletsimpleBuild=buildTypepkg==Simplehsexists<-doesFileExistops"Setup.hs"lhsexists<-doesFileExistops"Setup.lhs"return$check(notsimpleBuild&&nothsexists&&notlhsexists)$PackageDistInexcusable$"The package is missing a Setup.hs or Setup.lhs script."checkConfigureExists::Monadm=>CheckPackageContentOpsm->PackageDescription->m(MaybePackageCheck)checkConfigureExistsopspd|buildTypepd==Configure=doexists<-doesFileExistops"configure"return$check(notexists)$PackageBuildWarning$"The 'build-type' is 'Configure' but there is no 'configure' script. "++"You probably need to run 'autoreconf -i' to generate it."|otherwise=returnNothingcheckLocalPathsExist::Monadm=>CheckPackageContentOpsm->PackageDescription->m[PackageCheck]checkLocalPathsExistopspkg=doletdirs=[(dir,kind)|bi<-allBuildInfopkg,(dir,kind)<-[(dir,"extra-lib-dirs")|dir<-extraLibDirsbi]++[(dir,"extra-framework-dirs")|dir<-extraFrameworkDirsbi]++[(dir,"include-dirs")|dir<-includeDirsbi]++[(dir,"hs-source-dirs")|dir<-hsSourceDirsbi],isRelativeOnAnyPlatformdir]missing<-filterM(liftMnot.doesDirectoryExistops.fst)dirsreturn[PackageBuildWarning{explanation=quote(kind++": "++dir)++" directory does not exist."}|(dir,kind)<-missing]checkMissingVcsInfo::Monadm=>CheckPackageContentOpsm->PackageDescription->m[PackageCheck]checkMissingVcsInfoopspkg|null(sourceRepospkg)=dovcsInUse<-liftMor$mapM(doesDirectoryExistops)repoDirnamesifvcsInUsethenreturn[PackageDistSuspiciousmessage]elsereturn[]whererepoDirnames=[dirname|repo<-knownRepoTypes,dirname<-repoTypeDirnamerepo]message="When distributing packages it is encouraged to specify source "++"control information in the .cabal file using one or more "++"'source-repository' sections. See the Cabal user guide for "++"details."checkMissingVcsInfo__=return[]repoTypeDirname::RepoType->[FilePath]repoTypeDirnameDarcs=["_darcs"]repoTypeDirnameGit=[".git"]repoTypeDirnameSVN=[".svn"]repoTypeDirnameCVS=["CVS"]repoTypeDirnameMercurial=[".hg"]repoTypeDirnameGnuArch=[".arch-params"]repoTypeDirnameBazaar=[".bzr"]repoTypeDirnameMonotone=["_MTN"]repoTypeDirname_=[]-- -------------------------------------------------------------- * 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]checkPackageFileNamesfiles=(take1.mapMaybecheckWindowsPath$files)++(take1.mapMaybecheckTarPath$files)-- If we get any of these checks triggering then we're likely to get-- many, and that's probably not helpful, so return at most one.checkWindowsPath::FilePath->MaybePackageCheckcheckWindowsPathpath=check(not$FilePath.Windows.isValidpath')$PackageDistInexcusable$"Unfortunately, the file "++quotepath++" is not a valid file "++"name on Windows which would cause portability problems for this "++"package. Windows file names cannot contain any of the characters "++"\":*?<>|\" and there are a few reserved names including \"aux\", "++"\"nul\", \"con\", \"prn\", \"com1-9\", \"lpt1-9\" and \"clock$\"."wherepath'=".\\"++path-- force a relative name to catch invalid file names like "f:oo" which-- otherwise parse as file "oo" in the current directory on the 'f' drive.-- | Check a file name is valid for the portable POSIX tar format.---- The POSIX tar format has a restriction on the length of file names. It is-- unfortunately not a simple restriction like a maximum length. The exact-- restriction is that either the whole path be 100 characters or less, or it-- be possible to split the path on a directory separator such that the first-- part is 155 characters or less and the second part 100 characters or less.--checkTarPath::FilePath->MaybePackageCheckcheckTarPathpath|lengthpath>255=JustlongPath|otherwise=casepacknameMax(reverse(splitPathpath))ofLefterr->JusterrRight[]->NothingRight(h:rest)->casepackprefixMaxremainderofLefterr->JusterrRight[]->NothingRight(_:_)->JustnoSplitwhere-- drop the '/' between the name and prefix:remainder=inith:restwherenameMax,prefixMax::IntnameMax=100prefixMax=155pack_[]=LeftemptyNamepackmaxLen(c:cs)|n>maxLen=LeftlongName|otherwise=Right(pack'maxLenncs)wheren=lengthcpack'maxLenn(c:cs)|n'<=maxLen=pack'maxLenn'cswheren'=n+lengthcpack'__cs=cslongPath=PackageDistInexcusable$"The following file name is too long to store in a portable POSIX "++"format tar archive. The maximum length is 255 ASCII characters.\n"++"The file in question is:\n "++pathlongName=PackageDistInexcusable$"The following file name is too long to store in a portable POSIX "++"format tar archive. The maximum length for the name part (including "++"extension) is 100 ASCII characters. The maximum length for any "++"individual directory component is 155.\n"++"The file in question is:\n "++pathnoSplit=PackageDistInexcusable$"The following file name is too long to store in a portable POSIX "++"format tar archive. While the total length is less than 255 ASCII "++"characters, there are unfortunately further restrictions. It has to "++"be possible to split the file path on a directory separator into "++"two parts such that the first part fits in 155 characters or less "++"and the second part fits in 100 characters or less. Basically you "++"have to make the file name or directory names shorter, or you could "++"split a long directory name into nested subdirectories with shorter "++"names.\nThe file in question is:\n "++pathemptyName=PackageDistInexcusable$"Encountered a file with an empty name, something is very wrong! "++"Files with an empty name cannot be stored in a tar archive or in "++"standard file systems."-- ---------------------------------------------------------------- * Checks for missing content and other pre-distribution checks-- ---------------------------------------------------------------- | Similar to 'checkPackageContent', 'checkPackageFilesPreDistribution'-- inspects the files included in the package, but is primarily looking for-- files in the working tree that may have been missed or other similar-- problems that can only be detected pre-distribution.---- Because Hackage necessarily checks the uploaded tarball, it is too late to-- check these on the server; these checks only make sense in the development-- and package-creation environment. Hence we can use IO, rather than needing-- to pass a 'CheckPackageContentOps' dictionary around.checkPackageFilesPreDistribution::Verbosity->PackageDescription->FilePath->NoCallStackIO[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->NoCallStackIO[PackageCheck]checkGlobFilesverbositypkgroot=fmapconcat$forallGlobs$\(field,dir,glob)->-- Note: we just skip over parse errors here; they're reported elsewhere.caseparseFileGlob(specVersionpkg)globofLeft_->return[]RightparsedGlob->doresults<-runDirFileGlobverbosity(root</>dir)parsedGlobletindividualWarnings=results>>=getWarningfieldglobnoMatchesWarning=[PackageDistSuspiciousWarn$"In '"++field++"': the pattern '"++glob++"' does not"++" match any files."|all(not.suppressesNoMatchesWarning)results]return(noMatchesWarning++individualWarnings)whereadjustedDataDir=ifnull(dataDirpkg)then"."elsedataDirpkgallGlobs=concat[(,,)"extra-source-files""."<$>extraSrcFilespkg,(,,)"extra-doc-files""."<$>extraDocFilespkg,(,,)"data-files"adjustedDataDir<$>dataFilespkg]-- 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_)=TruesuppressesNoMatchesWarning(GlobWarnMultiDot_)=FalsesuppressesNoMatchesWarning(GlobMissingDirectory_)=TruegetWarning::String->FilePath->GlobResultFilePath->[PackageCheck]getWarning__(GlobMatch_)=[]-- Before Cabal 2.4, the extensions of globs had to match the file-- exactly. This has been relaxed in 2.4 to allow matching only the-- suffix. This warning detects when pre-2.4 package descriptions are-- omitting files purely because of the stricter check.getWarningfieldglob(GlobWarnMultiDotfile)=[PackageDistSuspiciousWarn$"In '"++field++"': the pattern '"++glob++"' does not"++" match the file '"++file++"' because the extensions do not"++" exactly match (e.g., foo.en.html does not exactly match *.html)."++" To enable looser suffix-only matching, set 'cabal-version: 2.4' or higher."]getWarningfieldglob(GlobMissingDirectorydir)=[PackageDistSuspiciousWarn$"In '"++field++"': the pattern '"++glob++"' attempts to"++" match files in the directory '"++dir++"', but there is no"++" directory by that name."]-- -------------------------------------------------------------- * Utils-- ------------------------------------------------------------quote::String->Stringquotes="'"++s++"'"commaSep::[String]->StringcommaSep=intercalate", "dups::Orda=>[a]->[a]dupsxs=[x|(x:_:_)<-group(sortxs)]fileExtensionSupportedLanguage::FilePath->BoolfileExtensionSupportedLanguagepath=isHaskell||isCwhereextension=takeExtensionpathisHaskell=extension`elem`[".hs",".lhs"]isC=isJust(filenameCDialectextension)